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

1576
vendor/sdl-3.2.10/src/video/SDL_RLEaccel.c vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
/*
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_RLEaccel_c_h_
#define SDL_RLEaccel_c_h_
#include "SDL_internal.h"
// Useful functions and variables from SDL_RLEaccel.c
extern bool SDL_RLESurface(SDL_Surface *surface);
extern void SDL_UnRLESurface(SDL_Surface *surface, bool recode);
#endif // SDL_RLEaccel_c_h_

290
vendor/sdl-3.2.10/src/video/SDL_blit.c vendored Normal file
View file

@ -0,0 +1,290 @@
/*
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"
#include "SDL_sysvideo.h"
#include "SDL_surface_c.h"
#include "SDL_blit_auto.h"
#include "SDL_blit_copy.h"
#include "SDL_blit_slow.h"
#include "SDL_RLEaccel_c.h"
#include "SDL_pixels_c.h"
// The general purpose software blit routine
static bool SDLCALL SDL_SoftBlit(SDL_Surface *src, const SDL_Rect *srcrect,
SDL_Surface *dst, const SDL_Rect *dstrect)
{
bool okay;
int src_locked;
int dst_locked;
// Everything is okay at the beginning...
okay = true;
// Lock the destination if it's in hardware
dst_locked = 0;
if (SDL_MUSTLOCK(dst)) {
if (!SDL_LockSurface(dst)) {
okay = false;
} else {
dst_locked = 1;
}
}
// Lock the source if it's in hardware
src_locked = 0;
if (SDL_MUSTLOCK(src)) {
if (!SDL_LockSurface(src)) {
okay = false;
} else {
src_locked = 1;
}
}
// Set up source and destination buffer pointers, and BLIT!
if (okay) {
SDL_BlitFunc RunBlit;
SDL_BlitInfo *info = &src->map.info;
// Set up the blit information
info->src = (Uint8 *)src->pixels +
(Uint16)srcrect->y * src->pitch +
(Uint16)srcrect->x * info->src_fmt->bytes_per_pixel;
info->src_w = srcrect->w;
info->src_h = srcrect->h;
info->src_pitch = src->pitch;
info->src_skip =
info->src_pitch - info->src_w * info->src_fmt->bytes_per_pixel;
info->dst =
(Uint8 *)dst->pixels + (Uint16)dstrect->y * dst->pitch +
(Uint16)dstrect->x * info->dst_fmt->bytes_per_pixel;
info->dst_w = dstrect->w;
info->dst_h = dstrect->h;
info->dst_pitch = dst->pitch;
info->dst_skip =
info->dst_pitch - info->dst_w * info->dst_fmt->bytes_per_pixel;
RunBlit = (SDL_BlitFunc)src->map.data;
// Run the actual software blit
RunBlit(info);
}
// We need to unlock the surfaces if they're locked
if (dst_locked) {
SDL_UnlockSurface(dst);
}
if (src_locked) {
SDL_UnlockSurface(src);
}
// Blit is done!
return okay;
}
#ifdef SDL_HAVE_BLIT_AUTO
#ifdef SDL_PLATFORM_MACOS
#include <sys/sysctl.h>
static bool SDL_UseAltivecPrefetch(void)
{
const char key[] = "hw.l3cachesize";
u_int64_t result = 0;
size_t typeSize = sizeof(result);
if (sysctlbyname(key, &result, &typeSize, NULL, 0) == 0 && result > 0) {
return true;
} else {
return false;
}
}
#else
static bool SDL_UseAltivecPrefetch(void)
{
// Just guess G4
return true;
}
#endif // SDL_PLATFORM_MACOS
static SDL_BlitFunc SDL_ChooseBlitFunc(SDL_PixelFormat src_format, SDL_PixelFormat dst_format, int flags,
SDL_BlitFuncEntry *entries)
{
int i, flagcheck = (flags & (SDL_COPY_MODULATE_MASK | SDL_COPY_BLEND_MASK | SDL_COPY_COLORKEY | SDL_COPY_NEAREST));
static unsigned int features = 0x7fffffff;
// Get the available CPU features
if (features == 0x7fffffff) {
features = SDL_CPU_ANY;
if (SDL_HasMMX()) {
features |= SDL_CPU_MMX;
}
if (SDL_HasSSE()) {
features |= SDL_CPU_SSE;
}
if (SDL_HasSSE2()) {
features |= SDL_CPU_SSE2;
}
if (SDL_HasAltiVec()) {
if (SDL_UseAltivecPrefetch()) {
features |= SDL_CPU_ALTIVEC_PREFETCH;
} else {
features |= SDL_CPU_ALTIVEC_NOPREFETCH;
}
}
}
for (i = 0; entries[i].func; ++i) {
// Check for matching pixel formats
if (src_format != entries[i].src_format) {
continue;
}
if (dst_format != entries[i].dst_format) {
continue;
}
// Check flags
if ((flagcheck & entries[i].flags) != flagcheck) {
continue;
}
// Check CPU features
if ((entries[i].cpu & features) != entries[i].cpu) {
continue;
}
// We found the best one!
return entries[i].func;
}
return NULL;
}
#endif // SDL_HAVE_BLIT_AUTO
// Figure out which of many blit routines to set up on a surface
bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst)
{
SDL_BlitFunc blit = NULL;
SDL_BlitMap *map = &surface->map;
SDL_Colorspace src_colorspace = surface->colorspace;
SDL_Colorspace dst_colorspace = dst->colorspace;
// We don't currently support blitting to < 8 bpp surfaces
if (SDL_BITSPERPIXEL(dst->format) < 8) {
SDL_InvalidateMap(map);
return SDL_SetError("Blit combination not supported");
}
#ifdef SDL_HAVE_RLE
// Clean everything out to start
if (surface->flags & SDL_INTERNAL_SURFACE_RLEACCEL) {
SDL_UnRLESurface(surface, true);
}
#endif
map->blit = SDL_SoftBlit;
map->info.src_surface = surface;
map->info.src_fmt = surface->fmt;
map->info.src_pal = surface->palette;
map->info.dst_surface = dst;
map->info.dst_fmt = dst->fmt;
map->info.dst_pal = dst->palette;
#ifdef SDL_HAVE_RLE
// See if we can do RLE acceleration
if (map->info.flags & SDL_COPY_RLE_DESIRED) {
if (SDL_RLESurface(surface)) {
return true;
}
}
#endif
// Choose a standard blit function
if (!blit) {
if (src_colorspace != dst_colorspace ||
SDL_BYTESPERPIXEL(surface->format) > 4 ||
SDL_BYTESPERPIXEL(dst->format) > 4) {
blit = SDL_Blit_Slow_Float;
}
}
if (!blit) {
if (map->identity && !(map->info.flags & ~SDL_COPY_RLE_DESIRED)) {
blit = SDL_BlitCopy;
} else if (SDL_ISPIXELFORMAT_10BIT(surface->format) ||
SDL_ISPIXELFORMAT_10BIT(dst->format)) {
blit = SDL_Blit_Slow;
}
#ifdef SDL_HAVE_BLIT_0
else if (SDL_BITSPERPIXEL(surface->format) < 8 &&
SDL_ISPIXELFORMAT_INDEXED(surface->format)) {
blit = SDL_CalculateBlit0(surface);
}
#endif
#ifdef SDL_HAVE_BLIT_1
else if (SDL_BYTESPERPIXEL(surface->format) == 1 &&
SDL_ISPIXELFORMAT_INDEXED(surface->format)) {
blit = SDL_CalculateBlit1(surface);
}
#endif
#ifdef SDL_HAVE_BLIT_A
else if (map->info.flags & SDL_COPY_BLEND) {
blit = SDL_CalculateBlitA(surface);
}
#endif
#ifdef SDL_HAVE_BLIT_N
else {
blit = SDL_CalculateBlitN(surface);
}
#endif
}
#ifdef SDL_HAVE_BLIT_AUTO
if (!blit) {
SDL_PixelFormat src_format = surface->format;
SDL_PixelFormat dst_format = dst->format;
blit =
SDL_ChooseBlitFunc(src_format, dst_format, map->info.flags,
SDL_GeneratedBlitFuncTable);
}
#endif
#ifndef TEST_SLOW_BLIT
if (!blit)
#endif
{
SDL_PixelFormat src_format = surface->format;
SDL_PixelFormat dst_format = dst->format;
if ((!SDL_ISPIXELFORMAT_INDEXED(src_format) ||
(src_format == SDL_PIXELFORMAT_INDEX8 && surface->palette)) &&
!SDL_ISPIXELFORMAT_FOURCC(src_format) &&
(!SDL_ISPIXELFORMAT_INDEXED(dst_format) ||
(dst_format == SDL_PIXELFORMAT_INDEX8 && dst->palette)) &&
!SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
blit = SDL_Blit_Slow;
}
}
map->data = (void *)blit;
// Make sure we have a blit function
if (!blit) {
SDL_InvalidateMap(map);
return SDL_SetError("Blit combination not supported");
}
return true;
}

751
vendor/sdl-3.2.10/src/video/SDL_blit.h vendored Normal file
View file

@ -0,0 +1,751 @@
/*
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_blit_h_
#define SDL_blit_h_
// Table to do pixel byte expansion
extern const Uint8 *SDL_expand_byte[9];
extern const Uint16 SDL_expand_byte_10[];
// SDL blit copy flags
#define SDL_COPY_MODULATE_COLOR 0x00000001
#define SDL_COPY_MODULATE_ALPHA 0x00000002
#define SDL_COPY_MODULATE_MASK (SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA)
#define SDL_COPY_BLEND 0x00000010
#define SDL_COPY_BLEND_PREMULTIPLIED 0x00000020
#define SDL_COPY_ADD 0x00000040
#define SDL_COPY_ADD_PREMULTIPLIED 0x00000080
#define SDL_COPY_MOD 0x00000100
#define SDL_COPY_MUL 0x00000200
#define SDL_COPY_BLEND_MASK (SDL_COPY_BLEND | SDL_COPY_BLEND_PREMULTIPLIED | SDL_COPY_ADD | SDL_COPY_ADD_PREMULTIPLIED | SDL_COPY_MOD | SDL_COPY_MUL)
#define SDL_COPY_COLORKEY 0x00000400
#define SDL_COPY_NEAREST 0x00000800
#define SDL_COPY_RLE_DESIRED 0x00001000
#define SDL_COPY_RLE_COLORKEY 0x00002000
#define SDL_COPY_RLE_ALPHAKEY 0x00004000
#define SDL_COPY_RLE_MASK (SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)
// SDL blit CPU flags
#define SDL_CPU_ANY 0x00000000
#define SDL_CPU_MMX 0x00000001
#define SDL_CPU_SSE 0x00000002
#define SDL_CPU_SSE2 0x00000004
#define SDL_CPU_ALTIVEC_PREFETCH 0x00000008
#define SDL_CPU_ALTIVEC_NOPREFETCH 0x00000010
typedef struct
{
SDL_Surface *src_surface;
Uint8 *src;
int src_w, src_h;
int src_pitch;
int src_skip;
SDL_Surface *dst_surface;
Uint8 *dst;
int dst_w, dst_h;
int dst_pitch;
int dst_skip;
const SDL_PixelFormatDetails *src_fmt;
const SDL_Palette *src_pal;
const SDL_PixelFormatDetails *dst_fmt;
const SDL_Palette *dst_pal;
Uint8 *table;
SDL_HashTable *palette_map;
int flags;
Uint32 colorkey;
Uint8 r, g, b, a;
} SDL_BlitInfo;
typedef void (*SDL_BlitFunc)(SDL_BlitInfo *info);
typedef struct
{
SDL_PixelFormat src_format;
SDL_PixelFormat dst_format;
int flags;
unsigned int cpu;
SDL_BlitFunc func;
} SDL_BlitFuncEntry;
typedef bool (SDLCALL *SDL_Blit) (struct SDL_Surface *src, const SDL_Rect *srcrect, struct SDL_Surface *dst, const SDL_Rect *dstrect);
// Blit mapping definition
typedef struct SDL_BlitMap
{
int identity;
SDL_Blit blit;
void *data;
SDL_BlitInfo info;
/* the version count matches the destination; mismatch indicates
an invalid mapping */
Uint32 dst_palette_version;
Uint32 src_palette_version;
} SDL_BlitMap;
// Functions found in SDL_blit.c
extern bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst);
/* Functions found in SDL_blit_*.c */
extern SDL_BlitFunc SDL_CalculateBlit0(SDL_Surface *surface);
extern SDL_BlitFunc SDL_CalculateBlit1(SDL_Surface *surface);
extern SDL_BlitFunc SDL_CalculateBlitN(SDL_Surface *surface);
extern SDL_BlitFunc SDL_CalculateBlitA(SDL_Surface *surface);
/*
* Useful macros for blitting routines
*/
#ifdef __GNUC__
#define DECLARE_ALIGNED(t, v, a) t __attribute__((aligned(a))) v
#elif defined(_MSC_VER)
#define DECLARE_ALIGNED(t, v, a) __declspec(align(a)) t v
#else
#define DECLARE_ALIGNED(t, v, a) t v
#endif
// Load pixel of the specified format from a buffer and get its R-G-B values
#define RGB_FROM_PIXEL(Pixel, fmt, r, g, b) \
{ \
r = SDL_expand_byte[fmt->Rbits][((Pixel & fmt->Rmask) >> fmt->Rshift)]; \
g = SDL_expand_byte[fmt->Gbits][((Pixel & fmt->Gmask) >> fmt->Gshift)]; \
b = SDL_expand_byte[fmt->Bbits][((Pixel & fmt->Bmask) >> fmt->Bshift)]; \
}
#define RGB_FROM_RGB565(Pixel, r, g, b) \
{ \
r = SDL_expand_byte[5][((Pixel & 0xF800) >> 11)]; \
g = SDL_expand_byte[6][((Pixel & 0x07E0) >> 5)]; \
b = SDL_expand_byte[5][(Pixel & 0x001F)]; \
}
#define RGB_FROM_RGB555(Pixel, r, g, b) \
{ \
r = SDL_expand_byte[5][((Pixel & 0x7C00) >> 10)]; \
g = SDL_expand_byte[5][((Pixel & 0x03E0) >> 5)]; \
b = SDL_expand_byte[5][(Pixel & 0x001F)]; \
}
#define RGB_FROM_XRGB8888(Pixel, r, g, b) \
{ \
r = ((Pixel & 0xFF0000) >> 16); \
g = ((Pixel & 0xFF00) >> 8); \
b = (Pixel & 0xFF); \
}
#define RETRIEVE_RGB_PIXEL(buf, bpp, Pixel) \
do { \
switch (bpp) { \
case 1: \
Pixel = *((Uint8 *)(buf)); \
break; \
\
case 2: \
Pixel = *((Uint16 *)(buf)); \
break; \
\
case 3: \
{ \
Uint8 *B = (Uint8 *)(buf); \
if (SDL_BYTEORDER == SDL_LIL_ENDIAN) { \
Pixel = B[0] + (B[1] << 8) + (B[2] << 16); \
} else { \
Pixel = (B[0] << 16) + (B[1] << 8) + B[2]; \
} \
} break; \
\
case 4: \
Pixel = *((Uint32 *)(buf)); \
break; \
\
default: \
Pixel = 0; /* stop gcc complaints */ \
break; \
} \
} while (0)
#define DISEMBLE_RGB(buf, bpp, fmt, Pixel, r, g, b) \
do { \
switch (bpp) { \
case 1: \
Pixel = *((Uint8 *)(buf)); \
RGB_FROM_PIXEL(Pixel, fmt, r, g, b); \
break; \
\
case 2: \
Pixel = *((Uint16 *)(buf)); \
RGB_FROM_PIXEL(Pixel, fmt, r, g, b); \
break; \
\
case 3: \
{ \
Pixel = 0; \
if (SDL_BYTEORDER == SDL_LIL_ENDIAN) { \
r = *((buf) + fmt->Rshift / 8); \
g = *((buf) + fmt->Gshift / 8); \
b = *((buf) + fmt->Bshift / 8); \
} else { \
r = *((buf) + 2 - fmt->Rshift / 8); \
g = *((buf) + 2 - fmt->Gshift / 8); \
b = *((buf) + 2 - fmt->Bshift / 8); \
} \
} break; \
\
case 4: \
Pixel = *((Uint32 *)(buf)); \
RGB_FROM_PIXEL(Pixel, fmt, r, g, b); \
break; \
\
default: \
/* stop gcc complaints */ \
Pixel = 0; \
r = g = b = 0; \
break; \
} \
} while (0)
// Assemble R-G-B values into a specified pixel format and store them
#define PIXEL_FROM_RGB(Pixel, fmt, r, g, b) \
{ \
Pixel = ((r >> (8 - fmt->Rbits)) << fmt->Rshift) | \
((g >> (8 - fmt->Gbits)) << fmt->Gshift) | \
((b >> (8 - fmt->Bbits)) << fmt->Bshift) | \
fmt->Amask; \
}
#define RGB332_FROM_RGB(Pixel, r, g, b) \
{ \
Pixel = (Uint8)(((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6)); \
}
#define RGB565_FROM_RGB(Pixel, r, g, b) \
{ \
Pixel = (Uint16)(((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)); \
}
#define RGB555_FROM_RGB(Pixel, r, g, b) \
{ \
Pixel = (Uint16)(((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3)); \
}
#define XRGB8888_FROM_RGB(Pixel, r, g, b) \
{ \
Pixel = (r << 16) | (g << 8) | b; \
}
#define ARGB8888_FROM_RGBA(Pixel, r, g, b, a) \
{ \
Pixel = (a << 24) | (r << 16) | (g << 8) | b; \
}
#define RGBA8888_FROM_RGBA(Pixel, r, g, b, a) \
{ \
Pixel = (r << 24) | (g << 16) | (b << 8) | a; \
}
#define ABGR8888_FROM_RGBA(Pixel, r, g, b, a) \
{ \
Pixel = (a << 24) | (b << 16) | (g << 8) | r; \
}
#define BGRA8888_FROM_RGBA(Pixel, r, g, b, a) \
{ \
Pixel = (b << 24) | (g << 16) | (r << 8) | a; \
}
#define ARGB2101010_FROM_RGBA(Pixel, r, g, b, a) \
{ \
r = r ? ((r << 2) | 0x3) : 0; \
g = g ? ((g << 2) | 0x3) : 0; \
b = b ? ((b << 2) | 0x3) : 0; \
a = (a * 3) / 255; \
Pixel = (a << 30) | (r << 20) | (g << 10) | b; \
}
#define ARGB2101010_FROM_RGBAFLOAT(Pixel, r, g, b, a) \
{ \
r = SDL_clamp(r, 0.0f, 1.0f) * 1023.0f; \
g = SDL_clamp(g, 0.0f, 1.0f) * 1023.0f; \
b = SDL_clamp(b, 0.0f, 1.0f) * 1023.0f; \
a = SDL_clamp(a, 0.0f, 1.0f) * 3.0f; \
Pixel = (((Uint32)SDL_roundf(a)) << 30) | \
(((Uint32)SDL_roundf(r)) << 20) | \
(((Uint32)SDL_roundf(g)) << 10) | \
(Uint32)SDL_roundf(b); \
}
#define ABGR2101010_FROM_RGBA(Pixel, r, g, b, a) \
{ \
r = r ? ((r << 2) | 0x3) : 0; \
g = g ? ((g << 2) | 0x3) : 0; \
b = b ? ((b << 2) | 0x3) : 0; \
a = (a * 3) / 255; \
Pixel = (a << 30) | (b << 20) | (g << 10) | r; \
}
#define ABGR2101010_FROM_RGBAFLOAT(Pixel, r, g, b, a) \
{ \
r = SDL_clamp(r, 0.0f, 1.0f) * 1023.0f; \
g = SDL_clamp(g, 0.0f, 1.0f) * 1023.0f; \
b = SDL_clamp(b, 0.0f, 1.0f) * 1023.0f; \
a = SDL_clamp(a, 0.0f, 1.0f) * 3.0f; \
Pixel = (((Uint32)SDL_roundf(a)) << 30) | \
(((Uint32)SDL_roundf(b)) << 20) | \
(((Uint32)SDL_roundf(g)) << 10) | \
(Uint32)SDL_roundf(r); \
}
#define ASSEMBLE_RGB(buf, bpp, fmt, r, g, b) \
{ \
switch (bpp) { \
case 1: \
{ \
Uint8 _pixel; \
\
PIXEL_FROM_RGB(_pixel, fmt, r, g, b); \
*((Uint8 *)(buf)) = _pixel; \
} break; \
\
case 2: \
{ \
Uint16 _pixel; \
\
PIXEL_FROM_RGB(_pixel, fmt, r, g, b); \
*((Uint16 *)(buf)) = _pixel; \
} break; \
\
case 3: \
{ \
if (SDL_BYTEORDER == SDL_LIL_ENDIAN) { \
*((buf) + fmt->Rshift / 8) = r; \
*((buf) + fmt->Gshift / 8) = g; \
*((buf) + fmt->Bshift / 8) = b; \
} else { \
*((buf) + 2 - fmt->Rshift / 8) = r; \
*((buf) + 2 - fmt->Gshift / 8) = g; \
*((buf) + 2 - fmt->Bshift / 8) = b; \
} \
} break; \
\
case 4: \
{ \
Uint32 _pixel; \
\
PIXEL_FROM_RGB(_pixel, fmt, r, g, b); \
*((Uint32 *)(buf)) = _pixel; \
} break; \
} \
}
// FIXME: Should we rescale alpha into 0..255 here?
#define RGBA_FROM_PIXEL(Pixel, fmt, r, g, b, a) \
{ \
r = SDL_expand_byte[fmt->Rbits][((Pixel & fmt->Rmask) >> fmt->Rshift)]; \
g = SDL_expand_byte[fmt->Gbits][((Pixel & fmt->Gmask) >> fmt->Gshift)]; \
b = SDL_expand_byte[fmt->Bbits][((Pixel & fmt->Bmask) >> fmt->Bshift)]; \
a = SDL_expand_byte[fmt->Abits][((Pixel & fmt->Amask) >> fmt->Ashift)]; \
}
#define RGBA_FROM_8888(Pixel, fmt, r, g, b, a) \
{ \
r = (Pixel & fmt->Rmask) >> fmt->Rshift; \
g = (Pixel & fmt->Gmask) >> fmt->Gshift; \
b = (Pixel & fmt->Bmask) >> fmt->Bshift; \
a = (Pixel & fmt->Amask) >> fmt->Ashift; \
}
#define RGBA_FROM_RGBA8888(Pixel, r, g, b, a) \
{ \
r = (Pixel >> 24); \
g = ((Pixel >> 16) & 0xFF); \
b = ((Pixel >> 8) & 0xFF); \
a = (Pixel & 0xFF); \
}
#define RGBA_FROM_ARGB8888(Pixel, r, g, b, a) \
{ \
r = ((Pixel >> 16) & 0xFF); \
g = ((Pixel >> 8) & 0xFF); \
b = (Pixel & 0xFF); \
a = (Pixel >> 24); \
}
#define RGBA_FROM_ABGR8888(Pixel, r, g, b, a) \
{ \
r = (Pixel & 0xFF); \
g = ((Pixel >> 8) & 0xFF); \
b = ((Pixel >> 16) & 0xFF); \
a = (Pixel >> 24); \
}
#define RGBA_FROM_BGRA8888(Pixel, r, g, b, a) \
{ \
r = ((Pixel >> 8) & 0xFF); \
g = ((Pixel >> 16) & 0xFF); \
b = (Pixel >> 24); \
a = (Pixel & 0xFF); \
}
#define RGBA_FROM_ARGB2101010(Pixel, r, g, b, a) \
{ \
r = ((Pixel >> 22) & 0xFF); \
g = ((Pixel >> 12) & 0xFF); \
b = ((Pixel >> 2) & 0xFF); \
a = SDL_expand_byte[2][(Pixel >> 30)]; \
}
#define RGBAFLOAT_FROM_ARGB2101010(Pixel, r, g, b, a) \
{ \
r = (float)((Pixel >> 20) & 0x3FF) / 1023.0f; \
g = (float)((Pixel >> 10) & 0x3FF) / 1023.0f; \
b = (float)((Pixel >> 0) & 0x3FF) / 1023.0f; \
a = (float)(Pixel >> 30) / 3.0f; \
}
#define RGBA_FROM_ABGR2101010(Pixel, r, g, b, a) \
{ \
r = ((Pixel >> 2) & 0xFF); \
g = ((Pixel >> 12) & 0xFF); \
b = ((Pixel >> 22) & 0xFF); \
a = SDL_expand_byte[2][(Pixel >> 30)]; \
}
#define RGBAFLOAT_FROM_ABGR2101010(Pixel, r, g, b, a) \
{ \
r = (float)((Pixel >> 0) & 0x3FF) / 1023.0f; \
g = (float)((Pixel >> 10) & 0x3FF) / 1023.0f; \
b = (float)((Pixel >> 20) & 0x3FF) / 1023.0f; \
a = (float)(Pixel >> 30) / 3.0f; \
}
#define DISEMBLE_RGBA(buf, bpp, fmt, Pixel, r, g, b, a) \
do { \
switch (bpp) { \
case 1: \
Pixel = *((Uint8 *)(buf)); \
RGBA_FROM_PIXEL(Pixel, fmt, r, g, b, a); \
break; \
\
case 2: \
Pixel = *((Uint16 *)(buf)); \
RGBA_FROM_PIXEL(Pixel, fmt, r, g, b, a); \
break; \
\
case 3: \
{ \
Pixel = 0; \
if (SDL_BYTEORDER == SDL_LIL_ENDIAN) { \
r = *((buf) + fmt->Rshift / 8); \
g = *((buf) + fmt->Gshift / 8); \
b = *((buf) + fmt->Bshift / 8); \
} else { \
r = *((buf) + 2 - fmt->Rshift / 8); \
g = *((buf) + 2 - fmt->Gshift / 8); \
b = *((buf) + 2 - fmt->Bshift / 8); \
} \
a = 0xFF; \
} break; \
\
case 4: \
Pixel = *((Uint32 *)(buf)); \
RGBA_FROM_PIXEL(Pixel, fmt, r, g, b, a); \
break; \
\
default: \
/* stop gcc complaints */ \
Pixel = 0; \
r = g = b = a = 0; \
break; \
} \
} while (0)
// FIXME: this isn't correct, especially for Alpha (maximum != 255)
#define PIXEL_FROM_RGBA(Pixel, fmt, r, g, b, a) \
{ \
Pixel = ((r >> (8 - fmt->Rbits)) << fmt->Rshift) | \
((g >> (8 - fmt->Gbits)) << fmt->Gshift) | \
((b >> (8 - fmt->Bbits)) << fmt->Bshift) | \
((a >> (8 - fmt->Abits)) << fmt->Ashift); \
}
#define ASSEMBLE_RGBA(buf, bpp, fmt, r, g, b, a) \
{ \
switch (bpp) { \
case 1: \
{ \
Uint8 _pixel; \
\
PIXEL_FROM_RGBA(_pixel, fmt, r, g, b, a); \
*((Uint8 *)(buf)) = _pixel; \
} break; \
\
case 2: \
{ \
Uint16 _pixel; \
\
PIXEL_FROM_RGBA(_pixel, fmt, r, g, b, a); \
*((Uint16 *)(buf)) = _pixel; \
} break; \
\
case 3: \
{ \
if (SDL_BYTEORDER == SDL_LIL_ENDIAN) { \
*((buf) + fmt->Rshift / 8) = r; \
*((buf) + fmt->Gshift / 8) = g; \
*((buf) + fmt->Bshift / 8) = b; \
} else { \
*((buf) + 2 - fmt->Rshift / 8) = r; \
*((buf) + 2 - fmt->Gshift / 8) = g; \
*((buf) + 2 - fmt->Bshift / 8) = b; \
} \
} break; \
\
case 4: \
{ \
Uint32 _pixel; \
\
PIXEL_FROM_RGBA(_pixel, fmt, r, g, b, a); \
*((Uint32 *)(buf)) = _pixel; \
} break; \
} \
}
// Convert any 32-bit 4-bpp pixel to ARGB format
#define PIXEL_TO_ARGB_PIXEL(src, srcfmt, dst) \
do { \
Uint8 a, r, g, b; \
RGBA_FROM_PIXEL(src, srcfmt, r, g, b, a); \
dst = a << 24 | r << 16 | g << 8 | b; \
} while (0)
// Blend a single color channel or alpha value
/* dC = ((sC * sA) + (dC * (255 - sA))) / 255 */
#define ALPHA_BLEND_CHANNEL(sC, dC, sA) \
do { \
Uint16 x; \
x = ((sC - dC) * sA) + ((dC << 8) - dC); \
x += 0x1U; \
x += x >> 8; \
dC = x >> 8; \
} while (0)
// Perform a division by 255 after a multiplication of two 8-bit color channels
/* out = (sC * dC) / 255 */
#define MULT_DIV_255(sC, dC, out) \
do { \
Uint16 x = sC * dC; \
x += 0x1U; \
x += x >> 8; \
out = x >> 8; \
} while (0)
// Blend the RGB values of two pixels with an alpha value
#define ALPHA_BLEND_RGB(sR, sG, sB, A, dR, dG, dB) \
do { \
ALPHA_BLEND_CHANNEL(sR, dR, A); \
ALPHA_BLEND_CHANNEL(sG, dG, A); \
ALPHA_BLEND_CHANNEL(sB, dB, A); \
} while (0)
// Blend two 8888 pixels with the same format
/* Calculates dst = ((src * factor) + (dst * (255 - factor))) / 255 */
// FIXME: SDL_SIZE_MAX might not be an integer literal
#if defined(SIZE_MAX) && (SIZE_MAX == 0xffffffffffffffff)
#define FACTOR_BLEND_8888(src, dst, factor) \
do { \
Uint64 src64 = src; \
src64 = (src64 | (src64 << 24)) & 0x00FF00FF00FF00FF; \
\
Uint64 dst64 = dst; \
dst64 = (dst64 | (dst64 << 24)) & 0x00FF00FF00FF00FF; \
\
dst64 = ((src64 - dst64) * factor) + (dst64 << 8) - dst64; \
dst64 += 0x0001000100010001; \
dst64 += (dst64 >> 8) & 0x00FF00FF00FF00FF; \
dst64 &= 0xFF00FF00FF00FF00; \
\
dst = (Uint32)((dst64 >> 8) | (dst64 >> 32)); \
} while (0)
#else
#define FACTOR_BLEND_8888(src, dst, factor) \
do { \
Uint32 src02 = src & 0x00FF00FF; \
Uint32 dst02 = dst & 0x00FF00FF; \
\
Uint32 src13 = (src >> 8) & 0x00FF00FF; \
Uint32 dst13 = (dst >> 8) & 0x00FF00FF; \
\
Uint32 res02 = ((src02 - dst02) * factor) + (dst02 << 8) - dst02; \
res02 += 0x00010001; \
res02 += (res02 >> 8) & 0x00FF00FF; \
res02 = (res02 >> 8) & 0x00FF00FF; \
\
Uint32 res13 = ((src13 - dst13) * factor) + (dst13 << 8) - dst13; \
res13 += 0x00010001; \
res13 += (res13 >> 8) & 0x00FF00FF; \
res13 &= 0xFF00FF00; \
dst = res02 | res13; \
} while (0)
#endif
// Alpha blend two 8888 pixels with the same formats.
#define ALPHA_BLEND_8888(src, dst, fmt) \
do { \
Uint32 srcA = (src >> fmt->Ashift) & 0xFF; \
Uint32 tmp = src | fmt->Amask; \
FACTOR_BLEND_8888(tmp, dst, srcA); \
} while (0)
// Alpha blend two 8888 pixels with differing formats.
#define ALPHA_BLEND_SWIZZLE_8888(src, dst, srcfmt, dstfmt) \
do { \
Uint32 srcA = (src >> srcfmt->Ashift) & 0xFF; \
Uint32 tmp = (((src >> srcfmt->Rshift) & 0xFF) << dstfmt->Rshift) | \
(((src >> srcfmt->Gshift) & 0xFF) << dstfmt->Gshift) | \
(((src >> srcfmt->Bshift) & 0xFF) << dstfmt->Bshift) | \
dstfmt->Amask; \
FACTOR_BLEND_8888(tmp, dst, srcA); \
} while (0)
// Blend the RGBA values of two pixels
#define ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA) \
do { \
ALPHA_BLEND_CHANNEL(sR, dR, sA); \
ALPHA_BLEND_CHANNEL(sG, dG, sA); \
ALPHA_BLEND_CHANNEL(sB, dB, sA); \
ALPHA_BLEND_CHANNEL(255, dA, sA); \
} while (0)
// This is a very useful loop for optimizing blitters
#if defined(_MSC_VER) && (_MSC_VER == 1300)
// There's a bug in the Visual C++ 7 optimizer when compiling this code
#else
#define USE_DUFFS_LOOP
#endif
#define DUFFS_LOOP1(pixel_copy_increment, width) \
{ \
int n; \
for (n = width; n > 0; --n) { \
pixel_copy_increment; \
} \
}
#ifdef USE_DUFFS_LOOP
// 8-times unrolled loop
#define DUFFS_LOOP8(pixel_copy_increment, width) \
{ \
int n = (width + 7) / 8; \
switch (width & 7) { \
case 0: \
do { \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 7: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 6: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 5: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 4: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 3: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 2: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 1: \
pixel_copy_increment; \
} while (--n > 0); \
} \
}
// 4-times unrolled loop
#define DUFFS_LOOP4(pixel_copy_increment, width) \
{ \
int n = (width + 3) / 4; \
switch (width & 3) { \
case 0: \
do { \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 3: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 2: \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 1: \
pixel_copy_increment; \
} while (--n > 0); \
} \
}
// 2-times unrolled loop
#define DUFFS_LOOP2(pixel_copy_increment, width) \
{ \
int n = (width + 1) / 2; \
switch (width & 1) { \
case 0: \
do { \
pixel_copy_increment; \
SDL_FALLTHROUGH; \
case 1: \
pixel_copy_increment; \
} while (--n > 0); \
} \
}
// Use the 4-times version of the loop by default
#define DUFFS_LOOP(pixel_copy_increment, width) \
DUFFS_LOOP4(pixel_copy_increment, width)
// Use the 8-times version of the loop for simple routines
#define DUFFS_LOOP_TRIVIAL(pixel_copy_increment, width) \
DUFFS_LOOP8(pixel_copy_increment, width)
// Special version of Duff's device for even more optimization
#define DUFFS_LOOP_124(pixel_copy_increment1, \
pixel_copy_increment2, \
pixel_copy_increment4, width) \
{ \
int n = width; \
if (n & 1) { \
pixel_copy_increment1; \
n -= 1; \
} \
if (n & 2) { \
pixel_copy_increment2; \
n -= 2; \
} \
if (n & 4) { \
pixel_copy_increment4; \
n -= 4; \
} \
if (n) { \
n /= 8; \
do { \
pixel_copy_increment4; \
pixel_copy_increment4; \
} while (--n > 0); \
} \
}
#else
// Don't use Duff's device to unroll loops
#define DUFFS_LOOP(pixel_copy_increment, width) \
DUFFS_LOOP1(pixel_copy_increment, width)
#define DUFFS_LOOP_TRIVIAL(pixel_copy_increment, width) \
DUFFS_LOOP1(pixel_copy_increment, width)
#define DUFFS_LOOP8(pixel_copy_increment, width) \
DUFFS_LOOP1(pixel_copy_increment, width)
#define DUFFS_LOOP4(pixel_copy_increment, width) \
DUFFS_LOOP1(pixel_copy_increment, width)
#define DUFFS_LOOP2(pixel_copy_increment, width) \
DUFFS_LOOP1(pixel_copy_increment, width)
#define DUFFS_LOOP_124(pixel_copy_increment1, \
pixel_copy_increment2, \
pixel_copy_increment4, width) \
DUFFS_LOOP1(pixel_copy_increment1, width)
#endif // USE_DUFFS_LOOP
#if defined(_MSC_VER) && (_MSC_VER >= 600)
#pragma warning(disable : 4244) // '=': conversion from 'X' to 'Y', possible loss of data
#endif
#endif // SDL_blit_h_

972
vendor/sdl-3.2.10/src/video/SDL_blit_0.c vendored Normal file
View file

@ -0,0 +1,972 @@
/*
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_HAVE_BLIT_0
#include "SDL_surface_c.h"
// Functions to blit from bitmaps to other surfaces
SDL_FORCE_INLINE void BlitBto1(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int c;
int width, height;
Uint8 *src, *map, *dst;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = info->dst;
dstskip = info->dst_skip;
map = info->table;
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
if (map) {
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (1) {
*dst = map[bit];
}
dst++;
byte >>= srcbpp;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (1) {
*dst = map[bit];
}
dst++;
byte <<= srcbpp;
}
src += srcskip;
dst += dstskip;
}
}
} else {
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (1) {
*dst = bit;
}
dst++;
byte >>= srcbpp;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (1) {
*dst = bit;
}
dst++;
byte <<= srcbpp;
}
src += srcskip;
dst += dstskip;
}
}
}
}
SDL_FORCE_INLINE void BlitBto2(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int c;
int width, height;
Uint8 *src;
Uint16 *map, *dst;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = (Uint16 *)info->dst;
dstskip = info->dst_skip / 2;
map = (Uint16 *)info->table;
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (1) {
*dst = map[bit];
}
byte >>= srcbpp;
dst++;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (1) {
*dst = map[bit];
}
byte <<= srcbpp;
dst++;
}
src += srcskip;
dst += dstskip;
}
}
}
SDL_FORCE_INLINE void BlitBto3(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int c, o;
int width, height;
Uint8 *src, *map, *dst;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = info->dst;
dstskip = info->dst_skip;
map = info->table;
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (1) {
o = bit * 4;
dst[0] = map[o++];
dst[1] = map[o++];
dst[2] = map[o++];
}
byte >>= srcbpp;
dst += 3;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (1) {
o = bit * 4;
dst[0] = map[o++];
dst[1] = map[o++];
dst[2] = map[o++];
}
byte <<= srcbpp;
dst += 3;
}
src += srcskip;
dst += dstskip;
}
}
}
SDL_FORCE_INLINE void BlitBto4(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int width, height;
Uint8 *src;
Uint32 *map, *dst;
int srcskip, dstskip;
int c;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = (Uint32 *)info->dst;
dstskip = info->dst_skip / 4;
map = (Uint32 *)info->table;
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (1) {
*dst = map[bit];
}
byte >>= srcbpp;
dst++;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (1) {
*dst = map[bit];
}
byte <<= srcbpp;
dst++;
}
src += srcskip;
dst += dstskip;
}
}
}
SDL_FORCE_INLINE void BlitBto1Key(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
Uint8 *dst = info->dst;
int srcskip = info->src_skip;
int dstskip = info->dst_skip;
Uint32 ckey = info->colorkey;
Uint8 *palmap = info->table;
int c;
// Set up some basic variables
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
if (palmap) {
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (bit != ckey) {
*dst = palmap[bit];
}
dst++;
byte >>= srcbpp;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (bit != ckey) {
*dst = palmap[bit];
}
dst++;
byte <<= srcbpp;
}
src += srcskip;
dst += dstskip;
}
}
} else {
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (bit != ckey) {
*dst = bit;
}
dst++;
byte >>= srcbpp;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (bit != ckey) {
*dst = bit;
}
dst++;
byte <<= srcbpp;
}
src += srcskip;
dst += dstskip;
}
}
}
}
SDL_FORCE_INLINE void BlitBto2Key(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
Uint16 *dstp = (Uint16 *)info->dst;
int srcskip = info->src_skip;
int dstskip = info->dst_skip;
Uint32 ckey = info->colorkey;
Uint8 *palmap = info->table;
int c;
// Set up some basic variables
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
dstskip /= 2;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (bit != ckey) {
*dstp = ((Uint16 *)palmap)[bit];
}
byte >>= srcbpp;
dstp++;
}
src += srcskip;
dstp += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (bit != ckey) {
*dstp = ((Uint16 *)palmap)[bit];
}
byte <<= srcbpp;
dstp++;
}
src += srcskip;
dstp += dstskip;
}
}
}
SDL_FORCE_INLINE void BlitBto3Key(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
Uint8 *dst = info->dst;
int srcskip = info->src_skip;
int dstskip = info->dst_skip;
Uint32 ckey = info->colorkey;
Uint8 *palmap = info->table;
int c;
// Set up some basic variables
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (bit != ckey) {
SDL_memcpy(dst, &palmap[bit * 4], 3);
}
byte >>= srcbpp;
dst += 3;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (bit != ckey) {
SDL_memcpy(dst, &palmap[bit * 4], 3);
}
byte <<= srcbpp;
dst += 3;
}
src += srcskip;
dst += dstskip;
}
}
}
SDL_FORCE_INLINE void BlitBto4Key(SDL_BlitInfo *info, const Uint32 srcbpp)
{
const Uint32 mask = (1 << srcbpp) - 1;
const Uint32 align = (8 / srcbpp) - 1;
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
Uint32 *dstp = (Uint32 *)info->dst;
int srcskip = info->src_skip;
int dstskip = info->dst_skip;
Uint32 ckey = info->colorkey;
Uint8 *palmap = info->table;
int c;
// Set up some basic variables
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
dstskip /= 4;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (bit != ckey) {
*dstp = ((Uint32 *)palmap)[bit];
}
byte >>= srcbpp;
dstp++;
}
src += srcskip;
dstp += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (bit != ckey) {
*dstp = ((Uint32 *)palmap)[bit];
}
byte <<= srcbpp;
dstp++;
}
src += srcskip;
dstp += dstskip;
}
}
}
static void BlitBtoNAlpha(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
Uint8 *dst = info->dst;
int srcskip = info->src_skip;
int dstskip = info->dst_skip;
const SDL_Color *srcpal = info->src_pal->colors;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
int srcbpp, dstbpp;
int c;
Uint32 pixel, mask, align;
unsigned sR, sG, sB;
unsigned dR, dG, dB, dA;
const unsigned A = info->a;
// Set up some basic variables
srcbpp = srcfmt->bytes_per_pixel;
dstbpp = dstfmt->bytes_per_pixel;
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
mask = (1 << srcbpp) - 1;
align = (8 / srcbpp) - 1;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (1) {
sR = srcpal[bit].r;
sG = srcpal[bit].g;
sB = srcpal[bit].b;
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
byte >>= srcbpp;
dst += dstbpp;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (1) {
sR = srcpal[bit].r;
sG = srcpal[bit].g;
sB = srcpal[bit].b;
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
byte <<= srcbpp;
dst += dstbpp;
}
src += srcskip;
dst += dstskip;
}
}
}
static void BlitBtoNAlphaKey(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
Uint8 *dst = info->dst;
int srcskip = info->src_skip;
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
const SDL_Color *srcpal = info->src_pal->colors;
int srcbpp, dstbpp;
int c;
Uint32 pixel, mask, align;
unsigned sR, sG, sB;
unsigned dR, dG, dB, dA;
const unsigned A = info->a;
Uint32 ckey = info->colorkey;
// Set up some basic variables
srcbpp = srcfmt->bytes_per_pixel;
dstbpp = dstfmt->bytes_per_pixel;
if (srcbpp == 4)
srcskip += width - (width + 1) / 2;
else if (srcbpp == 2)
srcskip += width - (width + 3) / 4;
else if (srcbpp == 1)
srcskip += width - (width + 7) / 8;
mask = (1 << srcbpp) - 1;
align = (8 / srcbpp) - 1;
if (SDL_PIXELORDER(info->src_fmt->format) == SDL_BITMAPORDER_4321) {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte & mask);
if (bit != ckey) {
sR = srcpal[bit].r;
sG = srcpal[bit].g;
sB = srcpal[bit].b;
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
byte >>= srcbpp;
dst += dstbpp;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
Uint8 byte = 0, bit;
for (c = 0; c < width; ++c) {
if (!(c & align)) {
byte = *src++;
}
bit = (byte >> (8 - srcbpp)) & mask;
if (bit != ckey) {
sR = srcpal[bit].r;
sG = srcpal[bit].g;
sB = srcpal[bit].b;
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
byte <<= srcbpp;
dst += dstbpp;
}
src += srcskip;
dst += dstskip;
}
}
}
static void Blit1bto1(SDL_BlitInfo *info) {
BlitBto1(info, 1);
}
static void Blit1bto2(SDL_BlitInfo *info) {
BlitBto2(info, 1);
}
static void Blit1bto3(SDL_BlitInfo *info) {
BlitBto3(info, 1);
}
static void Blit1bto4(SDL_BlitInfo *info) {
BlitBto4(info, 1);
}
static const SDL_BlitFunc bitmap_blit_1b[] = {
(SDL_BlitFunc)NULL, Blit1bto1, Blit1bto2, Blit1bto3, Blit1bto4
};
static void Blit1bto1Key(SDL_BlitInfo *info) {
BlitBto1Key(info, 1);
}
static void Blit1bto2Key(SDL_BlitInfo *info) {
BlitBto2Key(info, 1);
}
static void Blit1bto3Key(SDL_BlitInfo *info) {
BlitBto3Key(info, 1);
}
static void Blit1bto4Key(SDL_BlitInfo *info) {
BlitBto4Key(info, 1);
}
static const SDL_BlitFunc colorkey_blit_1b[] = {
(SDL_BlitFunc)NULL, Blit1bto1Key, Blit1bto2Key, Blit1bto3Key, Blit1bto4Key
};
static void Blit2bto1(SDL_BlitInfo *info) {
BlitBto1(info, 2);
}
static void Blit2bto2(SDL_BlitInfo *info) {
BlitBto2(info, 2);
}
static void Blit2bto3(SDL_BlitInfo *info) {
BlitBto3(info, 2);
}
static void Blit2bto4(SDL_BlitInfo *info) {
BlitBto4(info, 2);
}
static const SDL_BlitFunc bitmap_blit_2b[] = {
(SDL_BlitFunc)NULL, Blit2bto1, Blit2bto2, Blit2bto3, Blit2bto4
};
static void Blit2bto1Key(SDL_BlitInfo *info) {
BlitBto1Key(info, 2);
}
static void Blit2bto2Key(SDL_BlitInfo *info) {
BlitBto2Key(info, 2);
}
static void Blit2bto3Key(SDL_BlitInfo *info) {
BlitBto3Key(info, 2);
}
static void Blit2bto4Key(SDL_BlitInfo *info) {
BlitBto4Key(info, 2);
}
static const SDL_BlitFunc colorkey_blit_2b[] = {
(SDL_BlitFunc)NULL, Blit2bto1Key, Blit2bto2Key, Blit2bto3Key, Blit2bto4Key
};
static void Blit4bto1(SDL_BlitInfo *info) {
BlitBto1(info, 4);
}
static void Blit4bto2(SDL_BlitInfo *info) {
BlitBto2(info, 4);
}
static void Blit4bto3(SDL_BlitInfo *info) {
BlitBto3(info, 4);
}
static void Blit4bto4(SDL_BlitInfo *info) {
BlitBto4(info, 4);
}
static const SDL_BlitFunc bitmap_blit_4b[] = {
(SDL_BlitFunc)NULL, Blit4bto1, Blit4bto2, Blit4bto3, Blit4bto4
};
static void Blit4bto1Key(SDL_BlitInfo *info) {
BlitBto1Key(info, 4);
}
static void Blit4bto2Key(SDL_BlitInfo *info) {
BlitBto2Key(info, 4);
}
static void Blit4bto3Key(SDL_BlitInfo *info) {
BlitBto3Key(info, 4);
}
static void Blit4bto4Key(SDL_BlitInfo *info) {
BlitBto4Key(info, 4);
}
static const SDL_BlitFunc colorkey_blit_4b[] = {
(SDL_BlitFunc)NULL, Blit4bto1Key, Blit4bto2Key, Blit4bto3Key, Blit4bto4Key
};
SDL_BlitFunc SDL_CalculateBlit0(SDL_Surface *surface)
{
int which;
if (SDL_BITSPERPIXEL(surface->map.info.dst_fmt->format) < 8) {
which = 0;
} else {
which = SDL_BYTESPERPIXEL(surface->map.info.dst_fmt->format);
}
if (SDL_PIXELTYPE(surface->format) == SDL_PIXELTYPE_INDEX1) {
switch (surface->map.info.flags & ~SDL_COPY_RLE_MASK) {
case 0:
if (which < SDL_arraysize(bitmap_blit_1b)) {
return bitmap_blit_1b[which];
}
break;
case SDL_COPY_COLORKEY:
if (which < SDL_arraysize(colorkey_blit_1b)) {
return colorkey_blit_1b[which];
}
break;
case SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? BlitBtoNAlpha : (SDL_BlitFunc)NULL;
case SDL_COPY_COLORKEY | SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? BlitBtoNAlphaKey : (SDL_BlitFunc)NULL;
}
return NULL;
}
if (SDL_PIXELTYPE(surface->format) == SDL_PIXELTYPE_INDEX2) {
switch (surface->map.info.flags & ~SDL_COPY_RLE_MASK) {
case 0:
if (which < SDL_arraysize(bitmap_blit_2b)) {
return bitmap_blit_2b[which];
}
break;
case SDL_COPY_COLORKEY:
if (which < SDL_arraysize(colorkey_blit_2b)) {
return colorkey_blit_2b[which];
}
break;
case SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? BlitBtoNAlpha : (SDL_BlitFunc)NULL;
case SDL_COPY_COLORKEY | SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? BlitBtoNAlphaKey : (SDL_BlitFunc)NULL;
}
return NULL;
}
if (SDL_PIXELTYPE(surface->format) == SDL_PIXELTYPE_INDEX4) {
switch (surface->map.info.flags & ~SDL_COPY_RLE_MASK) {
case 0:
if (which < SDL_arraysize(bitmap_blit_4b)) {
return bitmap_blit_4b[which];
}
break;
case SDL_COPY_COLORKEY:
if (which < SDL_arraysize(colorkey_blit_4b)) {
return colorkey_blit_4b[which];
}
break;
case SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? BlitBtoNAlpha : (SDL_BlitFunc)NULL;
case SDL_COPY_COLORKEY | SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? BlitBtoNAlphaKey : (SDL_BlitFunc)NULL;
}
return NULL;
}
return NULL;
}
#endif // SDL_HAVE_BLIT_0

563
vendor/sdl-3.2.10/src/video/SDL_blit_1.c vendored Normal file
View file

@ -0,0 +1,563 @@
/*
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_HAVE_BLIT_1
#include "SDL_surface_c.h"
#include "SDL_sysvideo.h"
// Functions to blit from 8-bit surfaces to other surfaces
static void Blit1to1(SDL_BlitInfo *info)
{
#ifndef USE_DUFFS_LOOP
int c;
#endif
int width, height;
Uint8 *src, *map, *dst;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = info->dst;
dstskip = info->dst_skip;
map = info->table;
while (height--) {
#ifdef USE_DUFFS_LOOP
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
{
*dst = map[*src];
}
dst++;
src++;
, width);
/* *INDENT-ON* */ // clang-format on
#else
for (c = width; c; --c) {
*dst = map[*src];
dst++;
src++;
}
#endif
src += srcskip;
dst += dstskip;
}
}
// This is now endian dependent
#ifndef USE_DUFFS_LOOP
#if (SDL_BYTEORDER == SDL_LIL_ENDIAN)
#define HI 1
#define LO 0
#else // ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
#define HI 0
#define LO 1
#endif
#endif
static void Blit1to2(SDL_BlitInfo *info)
{
#ifndef USE_DUFFS_LOOP
int c;
#endif
int width, height;
Uint8 *src, *dst;
Uint16 *map;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = info->dst;
dstskip = info->dst_skip;
map = (Uint16 *)info->table;
#ifdef USE_DUFFS_LOOP
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
{
*(Uint16 *)dst = map[*src++];
dst += 2;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dst += dstskip;
}
#else
// Memory align at 4-byte boundary, if necessary
if ((long)dst & 0x03) {
// Don't do anything if width is 0
if (width == 0) {
return;
}
--width;
while (height--) {
// Perform copy alignment
*(Uint16 *)dst = map[*src++];
dst += 2;
// Copy in 4 pixel chunks
for (c = width / 4; c; --c) {
*(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
src += 2;
dst += 4;
*(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
src += 2;
dst += 4;
}
// Get any leftovers
switch (width & 3) {
case 3:
*(Uint16 *)dst = map[*src++];
dst += 2;
SDL_FALLTHROUGH;
case 2:
*(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
src += 2;
dst += 4;
break;
case 1:
*(Uint16 *)dst = map[*src++];
dst += 2;
break;
}
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
// Copy in 4 pixel chunks
for (c = width / 4; c; --c) {
*(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
src += 2;
dst += 4;
*(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
src += 2;
dst += 4;
}
// Get any leftovers
switch (width & 3) {
case 3:
*(Uint16 *)dst = map[*src++];
dst += 2;
SDL_FALLTHROUGH;
case 2:
*(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
src += 2;
dst += 4;
break;
case 1:
*(Uint16 *)dst = map[*src++];
dst += 2;
break;
}
src += srcskip;
dst += dstskip;
}
}
#endif // USE_DUFFS_LOOP
}
static void Blit1to3(SDL_BlitInfo *info)
{
#ifndef USE_DUFFS_LOOP
int c;
#endif
int o;
int width, height;
Uint8 *src, *map, *dst;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = info->dst;
dstskip = info->dst_skip;
map = info->table;
while (height--) {
#ifdef USE_DUFFS_LOOP
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP(
{
o = *src * 4;
dst[0] = map[o++];
dst[1] = map[o++];
dst[2] = map[o++];
}
src++;
dst += 3;
, width);
/* *INDENT-ON* */ // clang-format on
#else
for (c = width; c; --c) {
o = *src * 4;
dst[0] = map[o++];
dst[1] = map[o++];
dst[2] = map[o++];
src++;
dst += 3;
}
#endif // USE_DUFFS_LOOP
src += srcskip;
dst += dstskip;
}
}
static void Blit1to4(SDL_BlitInfo *info)
{
#ifndef USE_DUFFS_LOOP
int c;
#endif
int width, height;
Uint8 *src;
Uint32 *map, *dst;
int srcskip, dstskip;
// Set up some basic variables
width = info->dst_w;
height = info->dst_h;
src = info->src;
srcskip = info->src_skip;
dst = (Uint32 *)info->dst;
dstskip = info->dst_skip / 4;
map = (Uint32 *)info->table;
while (height--) {
#ifdef USE_DUFFS_LOOP
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
*dst++ = map[*src++];
, width);
/* *INDENT-ON* */ // clang-format on
#else
for (c = width / 4; c; --c) {
*dst++ = map[*src++];
*dst++ = map[*src++];
*dst++ = map[*src++];
*dst++ = map[*src++];
}
switch (width & 3) {
case 3:
*dst++ = map[*src++];
SDL_FALLTHROUGH;
case 2:
*dst++ = map[*src++];
SDL_FALLTHROUGH;
case 1:
*dst++ = map[*src++];
}
#endif // USE_DUFFS_LOOP
src += srcskip;
dst += dstskip;
}
}
static void Blit1to1Key(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
Uint8 *palmap = info->table;
Uint32 ckey = info->colorkey;
if (palmap) {
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
{
if ( *src != ckey ) {
*dst = palmap[*src];
}
dst++;
src++;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dst += dstskip;
}
} else {
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
{
if ( *src != ckey ) {
*dst = *src;
}
dst++;
src++;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dst += dstskip;
}
}
}
static void Blit1to2Key(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint16 *dstp = (Uint16 *)info->dst;
int dstskip = info->dst_skip;
Uint16 *palmap = (Uint16 *)info->table;
Uint32 ckey = info->colorkey;
// Set up some basic variables
dstskip /= 2;
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
{
if ( *src != ckey ) {
*dstp=palmap[*src];
}
src++;
dstp++;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dstp += dstskip;
}
}
static void Blit1to3Key(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
Uint8 *palmap = info->table;
Uint32 ckey = info->colorkey;
int o;
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP(
{
if ( *src != ckey ) {
o = *src * 4;
dst[0] = palmap[o++];
dst[1] = palmap[o++];
dst[2] = palmap[o++];
}
src++;
dst += 3;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dst += dstskip;
}
}
static void Blit1to4Key(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint32 *dstp = (Uint32 *)info->dst;
int dstskip = info->dst_skip;
Uint32 *palmap = (Uint32 *)info->table;
Uint32 ckey = info->colorkey;
// Set up some basic variables
dstskip /= 4;
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP_TRIVIAL(
{
if ( *src != ckey ) {
*dstp = palmap[*src];
}
src++;
dstp++;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dstp += dstskip;
}
}
static void Blit1toNAlpha(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
const SDL_Color *srcpal = info->src_pal->colors;
int dstbpp;
Uint32 pixel;
unsigned sR, sG, sB, sA;
unsigned dR, dG, dB, dA;
const unsigned A = info->a;
// Set up some basic variables
dstbpp = dstfmt->bytes_per_pixel;
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP(
{
sR = srcpal[*src].r;
sG = srcpal[*src].g;
sB = srcpal[*src].b;
sA = (srcpal[*src].a * A) / 255;
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
src++;
dst += dstbpp;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dst += dstskip;
}
}
static void Blit1toNAlphaKey(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
const SDL_Color *srcpal = info->src_pal->colors;
Uint32 ckey = info->colorkey;
int dstbpp;
Uint32 pixel;
unsigned sR, sG, sB, sA;
unsigned dR, dG, dB, dA;
const unsigned A = info->a;
// Set up some basic variables
dstbpp = dstfmt->bytes_per_pixel;
while (height--) {
/* *INDENT-OFF* */ // clang-format off
DUFFS_LOOP(
{
if ( *src != ckey ) {
sR = srcpal[*src].r;
sG = srcpal[*src].g;
sB = srcpal[*src].b;
sA = (srcpal[*src].a * A) / 255;
DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
}
src++;
dst += dstbpp;
},
width);
/* *INDENT-ON* */ // clang-format on
src += srcskip;
dst += dstskip;
}
}
static const SDL_BlitFunc one_blit[] = {
(SDL_BlitFunc)NULL, Blit1to1, Blit1to2, Blit1to3, Blit1to4
};
static const SDL_BlitFunc one_blitkey[] = {
(SDL_BlitFunc)NULL, Blit1to1Key, Blit1to2Key, Blit1to3Key, Blit1to4Key
};
SDL_BlitFunc SDL_CalculateBlit1(SDL_Surface *surface)
{
int which;
if (SDL_BITSPERPIXEL(surface->map.info.dst_fmt->format) < 8) {
which = 0;
} else {
which = SDL_BYTESPERPIXEL(surface->map.info.dst_fmt->format);
}
switch (surface->map.info.flags & ~SDL_COPY_RLE_MASK) {
case 0:
if (which < SDL_arraysize(one_blit)) {
return one_blit[which];
}
break;
case SDL_COPY_COLORKEY:
if (which < SDL_arraysize(one_blitkey)) {
return one_blitkey[which];
}
break;
case SDL_COPY_COLORKEY | SDL_COPY_BLEND: // this is not super-robust but handles a specific case we found sdl12-compat.
if (surface->map.info.a == 255) {
if (which < SDL_arraysize(one_blitkey)) {
return one_blitkey[which];
}
} else {
return which >= 2 ? Blit1toNAlphaKey : (SDL_BlitFunc)NULL;
}
break;
case SDL_COPY_BLEND:
case SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
/* Supporting 8bpp->8bpp alpha is doable but requires lots of
tables which consume space and takes time to precompute,
so is better left to the user */
return which >= 2 ? Blit1toNAlpha : (SDL_BlitFunc)NULL;
case SDL_COPY_COLORKEY | SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? Blit1toNAlphaKey : (SDL_BlitFunc)NULL;
}
return (SDL_BlitFunc)NULL;
}
#endif // SDL_HAVE_BLIT_1

1503
vendor/sdl-3.2.10/src/video/SDL_blit_A.c vendored Normal file

File diff suppressed because it is too large Load diff

3221
vendor/sdl-3.2.10/src/video/SDL_blit_N.c vendored Normal file

File diff suppressed because it is too large Load diff

11542
vendor/sdl-3.2.10/src/video/SDL_blit_auto.c vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
// DO NOT EDIT! This file is generated by sdlgenblit.pl
/*
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"
#include "SDL_blit.h"
#ifdef SDL_HAVE_BLIT_AUTO
/* *INDENT-OFF* */ // clang-format off
extern SDL_BlitFuncEntry SDL_GeneratedBlitFuncTable[];
/* *INDENT-ON* */ // clang-format on
#endif // SDL_HAVE_BLIT_AUTO

View file

@ -0,0 +1,110 @@
/*
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"
#include "SDL_surface_c.h"
#include "SDL_blit_copy.h"
#ifdef SDL_SSE_INTRINSICS
// This assumes 16-byte aligned src and dst
static SDL_INLINE void SDL_TARGETING("sse") SDL_memcpySSE(Uint8 *dst, const Uint8 *src, int len)
{
int i;
__m128 values[4];
for (i = len / 64; i--;) {
_mm_prefetch((const char *)src, _MM_HINT_NTA);
values[0] = *(__m128 *)(src + 0);
values[1] = *(__m128 *)(src + 16);
values[2] = *(__m128 *)(src + 32);
values[3] = *(__m128 *)(src + 48);
_mm_stream_ps((float *)(dst + 0), values[0]);
_mm_stream_ps((float *)(dst + 16), values[1]);
_mm_stream_ps((float *)(dst + 32), values[2]);
_mm_stream_ps((float *)(dst + 48), values[3]);
src += 64;
dst += 64;
}
if (len & 63) {
SDL_memcpy(dst, src, len & 63);
}
}
#endif // SDL_SSE_INTRINSICS
void SDL_BlitCopy(SDL_BlitInfo *info)
{
bool overlap;
Uint8 *src, *dst;
int w, h;
int srcskip, dstskip;
w = info->dst_w * info->dst_fmt->bytes_per_pixel;
h = info->dst_h;
src = info->src;
dst = info->dst;
srcskip = info->src_pitch;
dstskip = info->dst_pitch;
// Properly handle overlapping blits
if (src < dst) {
overlap = (dst < (src + h * srcskip));
} else {
overlap = (src < (dst + h * dstskip));
}
if (overlap) {
if (dst < src) {
while (h--) {
SDL_memmove(dst, src, w);
src += srcskip;
dst += dstskip;
}
} else {
src += ((h - 1) * srcskip);
dst += ((h - 1) * dstskip);
while (h--) {
SDL_memmove(dst, src, w);
src -= srcskip;
dst -= dstskip;
}
}
return;
}
#ifdef SDL_SSE_INTRINSICS
if (SDL_HasSSE() &&
!((uintptr_t)src & 15) && !(srcskip & 15) &&
!((uintptr_t)dst & 15) && !(dstskip & 15)) {
while (h--) {
SDL_memcpySSE(dst, src, w);
src += srcskip;
dst += dstskip;
}
return;
}
#endif
while (h--) {
SDL_memcpy(dst, src, w);
src += srcskip;
dst += dstskip;
}
}

View file

@ -0,0 +1,27 @@
/*
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_blit_copy_h_
#define SDL_blit_copy_h_
void SDL_BlitCopy(SDL_BlitInfo *info);
#endif // SDL_blit_copy_h_

View file

@ -0,0 +1,996 @@
/*
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"
#include "SDL_surface_c.h"
#include "SDL_blit_slow.h"
#include "SDL_pixels_c.h"
typedef enum
{
SlowBlitPixelAccess_Index8,
SlowBlitPixelAccess_RGB,
SlowBlitPixelAccess_RGBA,
SlowBlitPixelAccess_10Bit,
SlowBlitPixelAccess_Large,
} SlowBlitPixelAccess;
static SlowBlitPixelAccess GetPixelAccessMethod(SDL_PixelFormat format)
{
if (SDL_BYTESPERPIXEL(format) > 4) {
return SlowBlitPixelAccess_Large;
} else if (SDL_ISPIXELFORMAT_10BIT(format)) {
return SlowBlitPixelAccess_10Bit;
} else if (format == SDL_PIXELFORMAT_INDEX8) {
return SlowBlitPixelAccess_Index8;
} else if (SDL_ISPIXELFORMAT_ALPHA(format)) {
return SlowBlitPixelAccess_RGBA;
} else {
return SlowBlitPixelAccess_RGB;
}
}
/* The ONE TRUE BLITTER
* This puppy has to handle all the unoptimized cases - yes, it's slow.
*/
void SDL_Blit_Slow(SDL_BlitInfo *info)
{
const int flags = info->flags;
const Uint32 modulateR = info->r;
const Uint32 modulateG = info->g;
const Uint32 modulateB = info->b;
const Uint32 modulateA = info->a;
Uint32 srcpixel = 0;
Uint32 srcR = 0, srcG = 0, srcB = 0, srcA = 0;
Uint32 dstpixel = 0;
Uint32 dstR = 0, dstG = 0, dstB = 0, dstA = 0;
Uint64 srcy, srcx;
Uint64 posy, posx;
Uint64 incy, incx;
const SDL_PixelFormatDetails *src_fmt = info->src_fmt;
const SDL_Palette *src_pal = info->src_pal;
const SDL_PixelFormatDetails *dst_fmt = info->dst_fmt;
const SDL_Palette *dst_pal = info->dst_pal;
SDL_HashTable *palette_map = info->palette_map;
int srcbpp = src_fmt->bytes_per_pixel;
int dstbpp = dst_fmt->bytes_per_pixel;
SlowBlitPixelAccess src_access;
SlowBlitPixelAccess dst_access;
Uint32 rgbmask = ~src_fmt->Amask;
Uint32 ckey = info->colorkey & rgbmask;
Uint32 last_pixel = 0;
Uint8 last_index = 0;
src_access = GetPixelAccessMethod(src_fmt->format);
dst_access = GetPixelAccessMethod(dst_fmt->format);
if (dst_access == SlowBlitPixelAccess_Index8) {
last_index = SDL_LookupRGBAColor(palette_map, last_pixel, dst_pal);
}
incy = ((Uint64)info->src_h << 16) / info->dst_h;
incx = ((Uint64)info->src_w << 16) / info->dst_w;
posy = incy / 2; // start at the middle of pixel
while (info->dst_h--) {
Uint8 *src = 0;
Uint8 *dst = info->dst;
int n = info->dst_w;
posx = incx / 2; // start at the middle of pixel
srcy = posy >> 16;
while (n--) {
srcx = posx >> 16;
src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp));
switch (src_access) {
case SlowBlitPixelAccess_Index8:
srcpixel = *src;
srcR = src_pal->colors[srcpixel].r;
srcG = src_pal->colors[srcpixel].g;
srcB = src_pal->colors[srcpixel].b;
srcA = src_pal->colors[srcpixel].a;
break;
case SlowBlitPixelAccess_RGB:
DISEMBLE_RGB(src, srcbpp, src_fmt, srcpixel, srcR, srcG, srcB);
srcA = 0xFF;
break;
case SlowBlitPixelAccess_RGBA:
DISEMBLE_RGBA(src, srcbpp, src_fmt, srcpixel, srcR, srcG, srcB, srcA);
break;
case SlowBlitPixelAccess_10Bit:
srcpixel = *((Uint32 *)(src));
switch (src_fmt->format) {
case SDL_PIXELFORMAT_XRGB2101010:
RGBA_FROM_ARGB2101010(srcpixel, srcR, srcG, srcB, srcA);
srcA = 0xFF;
break;
case SDL_PIXELFORMAT_XBGR2101010:
RGBA_FROM_ABGR2101010(srcpixel, srcR, srcG, srcB, srcA);
srcA = 0xFF;
break;
case SDL_PIXELFORMAT_ARGB2101010:
RGBA_FROM_ARGB2101010(srcpixel, srcR, srcG, srcB, srcA);
break;
case SDL_PIXELFORMAT_ABGR2101010:
RGBA_FROM_ABGR2101010(srcpixel, srcR, srcG, srcB, srcA);
break;
default:
break;
}
break;
case SlowBlitPixelAccess_Large:
// Handled in SDL_Blit_Slow_Float()
break;
}
if (flags & SDL_COPY_COLORKEY) {
// srcpixel isn't set for 24 bpp
if (srcbpp == 3) {
srcpixel = (srcR << src_fmt->Rshift) |
(srcG << src_fmt->Gshift) | (srcB << src_fmt->Bshift);
}
if ((srcpixel & rgbmask) == ckey) {
posx += incx;
dst += dstbpp;
continue;
}
}
if (flags & SDL_COPY_BLEND_MASK) {
switch (dst_access) {
case SlowBlitPixelAccess_Index8:
dstpixel = *dst;
dstR = dst_pal->colors[dstpixel].r;
dstG = dst_pal->colors[dstpixel].g;
dstB = dst_pal->colors[dstpixel].b;
dstA = dst_pal->colors[dstpixel].a;
break;
case SlowBlitPixelAccess_RGB:
DISEMBLE_RGB(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB);
dstA = 0xFF;
break;
case SlowBlitPixelAccess_RGBA:
DISEMBLE_RGBA(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB, dstA);
break;
case SlowBlitPixelAccess_10Bit:
dstpixel = *((Uint32 *)(dst));
switch (dst_fmt->format) {
case SDL_PIXELFORMAT_XRGB2101010:
RGBA_FROM_ARGB2101010(dstpixel, dstR, dstG, dstB, dstA);
dstA = 0xFF;
break;
case SDL_PIXELFORMAT_XBGR2101010:
RGBA_FROM_ABGR2101010(dstpixel, dstR, dstG, dstB, dstA);
dstA = 0xFF;
break;
case SDL_PIXELFORMAT_ARGB2101010:
RGBA_FROM_ARGB2101010(dstpixel, dstR, dstG, dstB, dstA);
break;
case SDL_PIXELFORMAT_ABGR2101010:
RGBA_FROM_ABGR2101010(dstpixel, dstR, dstG, dstB, dstA);
break;
default:
break;
}
break;
case SlowBlitPixelAccess_Large:
// Handled in SDL_Blit_Slow_Float()
break;
}
} else {
// don't care
}
if (flags & SDL_COPY_MODULATE_COLOR) {
srcR = (srcR * modulateR) / 255;
srcG = (srcG * modulateG) / 255;
srcB = (srcB * modulateB) / 255;
}
if (flags & SDL_COPY_MODULATE_ALPHA) {
srcA = (srcA * modulateA) / 255;
}
if (flags & (SDL_COPY_BLEND | SDL_COPY_ADD)) {
if (srcA < 255) {
srcR = (srcR * srcA) / 255;
srcG = (srcG * srcA) / 255;
srcB = (srcB * srcA) / 255;
}
}
switch (flags & SDL_COPY_BLEND_MASK) {
case 0:
dstR = srcR;
dstG = srcG;
dstB = srcB;
dstA = srcA;
break;
case SDL_COPY_BLEND:
dstR = srcR + ((255 - srcA) * dstR) / 255;
dstG = srcG + ((255 - srcA) * dstG) / 255;
dstB = srcB + ((255 - srcA) * dstB) / 255;
dstA = srcA + ((255 - srcA) * dstA) / 255;
break;
case SDL_COPY_BLEND_PREMULTIPLIED:
dstR = srcR + ((255 - srcA) * dstR) / 255;
if (dstR > 255) {
dstR = 255;
}
dstG = srcG + ((255 - srcA) * dstG) / 255;
if (dstG > 255) {
dstG = 255;
}
dstB = srcB + ((255 - srcA) * dstB) / 255;
if (dstB > 255) {
dstB = 255;
}
dstA = srcA + ((255 - srcA) * dstA) / 255;
if (dstA > 255) {
dstA = 255;
}
break;
case SDL_COPY_ADD:
case SDL_COPY_ADD_PREMULTIPLIED:
dstR = srcR + dstR;
if (dstR > 255) {
dstR = 255;
}
dstG = srcG + dstG;
if (dstG > 255) {
dstG = 255;
}
dstB = srcB + dstB;
if (dstB > 255) {
dstB = 255;
}
break;
case SDL_COPY_MOD:
dstR = (srcR * dstR) / 255;
dstG = (srcG * dstG) / 255;
dstB = (srcB * dstB) / 255;
break;
case SDL_COPY_MUL:
dstR = ((srcR * dstR) + (dstR * (255 - srcA))) / 255;
if (dstR > 255) {
dstR = 255;
}
dstG = ((srcG * dstG) + (dstG * (255 - srcA))) / 255;
if (dstG > 255) {
dstG = 255;
}
dstB = ((srcB * dstB) + (dstB * (255 - srcA))) / 255;
if (dstB > 255) {
dstB = 255;
}
break;
}
switch (dst_access) {
case SlowBlitPixelAccess_Index8:
dstpixel = ((dstR << 24) | (dstG << 16) | (dstB << 8) | dstA);
if (dstpixel != last_pixel) {
last_pixel = dstpixel;
last_index = SDL_LookupRGBAColor(palette_map, dstpixel, dst_pal);
}
*dst = last_index;
break;
case SlowBlitPixelAccess_RGB:
ASSEMBLE_RGB(dst, dstbpp, dst_fmt, dstR, dstG, dstB);
break;
case SlowBlitPixelAccess_RGBA:
ASSEMBLE_RGBA(dst, dstbpp, dst_fmt, dstR, dstG, dstB, dstA);
break;
case SlowBlitPixelAccess_10Bit:
{
Uint32 pixel;
switch (dst_fmt->format) {
case SDL_PIXELFORMAT_XRGB2101010:
dstA = 0xFF;
SDL_FALLTHROUGH;
case SDL_PIXELFORMAT_ARGB2101010:
ARGB2101010_FROM_RGBA(pixel, dstR, dstG, dstB, dstA);
break;
case SDL_PIXELFORMAT_XBGR2101010:
dstA = 0xFF;
SDL_FALLTHROUGH;
case SDL_PIXELFORMAT_ABGR2101010:
ABGR2101010_FROM_RGBA(pixel, dstR, dstG, dstB, dstA);
break;
default:
pixel = 0;
break;
}
*(Uint32 *)dst = pixel;
break;
}
case SlowBlitPixelAccess_Large:
// Handled in SDL_Blit_Slow_Float()
break;
}
posx += incx;
dst += dstbpp;
}
posy += incy;
info->dst += info->dst_pitch;
}
}
/* Convert from F16 to float
* Public domain implementation from https://gist.github.com/rygorous/2144712
*/
typedef union
{
Uint32 u;
float f;
struct
{
Uint32 Mantissa : 23;
Uint32 Exponent : 8;
Uint32 Sign : 1;
} x;
} FP32;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4214)
#endif
typedef union
{
Uint16 u;
struct
{
Uint16 Mantissa : 10;
Uint16 Exponent : 5;
Uint16 Sign : 1;
} x;
} FP16;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
static float half_to_float(Uint16 unValue)
{
static const FP32 magic = { (254 - 15) << 23 };
static const FP32 was_infnan = { (127 + 16) << 23 };
FP16 h;
FP32 o;
h.u = unValue;
o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits
o.f *= magic.f; // exponent adjust
if (o.f >= was_infnan.f) // make sure Inf/NaN survive
o.u |= 255 << 23;
o.u |= (h.u & 0x8000) << 16; // sign bit
return o.f;
}
/* Convert from float to F16
* Public domain implementation from https://stackoverflow.com/questions/76799117/how-to-convert-a-float-to-a-half-type-and-the-other-way-around-in-c
*/
static Uint16 float_to_half(float a)
{
Uint32 ia;
Uint16 ir;
SDL_memcpy(&ia, &a, sizeof(ia));
ir = (ia >> 16) & 0x8000;
if ((ia & 0x7f800000) == 0x7f800000) {
if ((ia & 0x7fffffff) == 0x7f800000) {
ir |= 0x7c00; // infinity
} else {
ir |= 0x7e00 | ((ia >> (24 - 11)) & 0x1ff); // NaN, quietened
}
} else if ((ia & 0x7f800000) >= 0x33000000) {
int shift = (int)((ia >> 23) & 0xff) - 127;
if (shift > 15) {
ir |= 0x7c00; // infinity
} else {
ia = (ia & 0x007fffff) | 0x00800000; // extract mantissa
if (shift < -14) { // denormal
ir |= ia >> (-1 - shift);
ia = ia << (32 - (-1 - shift));
} else { // normal
ir |= ia >> (24 - 11);
ia = ia << (32 - (24 - 11));
ir = ir + ((14 + shift) << 10);
}
// IEEE-754 round to nearest of even
if ((ia > 0x80000000) || ((ia == 0x80000000) && (ir & 1))) {
ir++;
}
}
}
return ir;
}
static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, const SDL_PixelFormatDetails *fmt, const SDL_Palette *pal, SDL_Colorspace colorspace, float SDR_white_point,
float *outR, float *outG, float *outB, float *outA)
{
Uint32 pixel;
Uint32 R, G, B, A;
float fR = 0.0f, fG = 0.0f, fB = 0.0f, fA = 0.0f;
float v[4];
switch (access) {
case SlowBlitPixelAccess_Index8:
pixel = *pixels;
fR = (float)pal->colors[pixel].r / 255.0f;
fG = (float)pal->colors[pixel].g / 255.0f;
fB = (float)pal->colors[pixel].b / 255.0f;
fA = (float)pal->colors[pixel].a / 255.0f;
break;
case SlowBlitPixelAccess_RGB:
DISEMBLE_RGB(pixels, fmt->bytes_per_pixel, fmt, pixel, R, G, B);
fR = (float)R / 255.0f;
fG = (float)G / 255.0f;
fB = (float)B / 255.0f;
fA = 1.0f;
break;
case SlowBlitPixelAccess_RGBA:
DISEMBLE_RGBA(pixels, fmt->bytes_per_pixel, fmt, pixel, R, G, B, A);
fR = (float)R / 255.0f;
fG = (float)G / 255.0f;
fB = (float)B / 255.0f;
fA = (float)A / 255.0f;
break;
case SlowBlitPixelAccess_10Bit:
pixel = *((Uint32 *)pixels);
switch (fmt->format) {
case SDL_PIXELFORMAT_XRGB2101010:
RGBAFLOAT_FROM_ARGB2101010(pixel, fR, fG, fB, fA);
fA = 1.0f;
break;
case SDL_PIXELFORMAT_XBGR2101010:
RGBAFLOAT_FROM_ABGR2101010(pixel, fR, fG, fB, fA);
fA = 1.0f;
break;
case SDL_PIXELFORMAT_ARGB2101010:
RGBAFLOAT_FROM_ARGB2101010(pixel, fR, fG, fB, fA);
break;
case SDL_PIXELFORMAT_ABGR2101010:
RGBAFLOAT_FROM_ABGR2101010(pixel, fR, fG, fB, fA);
break;
default:
fR = fG = fB = fA = 0.0f;
break;
}
break;
case SlowBlitPixelAccess_Large:
switch (SDL_PIXELTYPE(fmt->format)) {
case SDL_PIXELTYPE_ARRAYU16:
v[0] = (float)(((Uint16 *)pixels)[0]) / SDL_MAX_UINT16;
v[1] = (float)(((Uint16 *)pixels)[1]) / SDL_MAX_UINT16;
v[2] = (float)(((Uint16 *)pixels)[2]) / SDL_MAX_UINT16;
if (fmt->bytes_per_pixel == 8) {
v[3] = (float)(((Uint16 *)pixels)[3]) / SDL_MAX_UINT16;
} else {
v[3] = 1.0f;
}
break;
case SDL_PIXELTYPE_ARRAYF16:
v[0] = half_to_float(((Uint16 *)pixels)[0]);
v[1] = half_to_float(((Uint16 *)pixels)[1]);
v[2] = half_to_float(((Uint16 *)pixels)[2]);
if (fmt->bytes_per_pixel == 8) {
v[3] = half_to_float(((Uint16 *)pixels)[3]);
} else {
v[3] = 1.0f;
}
break;
case SDL_PIXELTYPE_ARRAYF32:
v[0] = ((float *)pixels)[0];
v[1] = ((float *)pixels)[1];
v[2] = ((float *)pixels)[2];
if (fmt->bytes_per_pixel == 16) {
v[3] = ((float *)pixels)[3];
} else {
v[3] = 1.0f;
}
break;
default:
// Unknown array type
v[0] = v[1] = v[2] = v[3] = 0.0f;
break;
}
switch (SDL_PIXELORDER(fmt->format)) {
case SDL_ARRAYORDER_RGB:
fR = v[0];
fG = v[1];
fB = v[2];
fA = 1.0f;
break;
case SDL_ARRAYORDER_RGBA:
fR = v[0];
fG = v[1];
fB = v[2];
fA = v[3];
break;
case SDL_ARRAYORDER_ARGB:
fA = v[0];
fR = v[1];
fG = v[2];
fB = v[3];
break;
case SDL_ARRAYORDER_BGR:
fB = v[0];
fG = v[1];
fR = v[2];
fA = 1.0f;
break;
case SDL_ARRAYORDER_BGRA:
fB = v[0];
fG = v[1];
fR = v[2];
fA = v[3];
break;
case SDL_ARRAYORDER_ABGR:
fA = v[0];
fB = v[1];
fG = v[2];
fR = v[3];
break;
default:
// Unknown array order
fA = fR = fG = fB = 0.0f;
break;
}
break;
}
// Convert to nits so src and dst are guaranteed to be linear and in the same units
switch (SDL_COLORSPACETRANSFER(colorspace)) {
case SDL_TRANSFER_CHARACTERISTICS_SRGB:
fR = SDL_sRGBtoLinear(fR);
fG = SDL_sRGBtoLinear(fG);
fB = SDL_sRGBtoLinear(fB);
break;
case SDL_TRANSFER_CHARACTERISTICS_PQ:
fR = SDL_PQtoNits(fR) / SDR_white_point;
fG = SDL_PQtoNits(fG) / SDR_white_point;
fB = SDL_PQtoNits(fB) / SDR_white_point;
break;
case SDL_TRANSFER_CHARACTERISTICS_LINEAR:
fR /= SDR_white_point;
fG /= SDR_white_point;
fB /= SDR_white_point;
break;
default:
// Unknown, leave it alone
break;
}
*outR = fR;
*outG = fG;
*outB = fB;
*outA = fA;
}
static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, const SDL_PixelFormatDetails *fmt, SDL_Colorspace colorspace, float SDR_white_point,
float fR, float fG, float fB, float fA)
{
Uint32 R, G, B, A;
Uint32 pixel;
float v[4];
// We converted to nits so src and dst are guaranteed to be linear and in the same units
switch (SDL_COLORSPACETRANSFER(colorspace)) {
case SDL_TRANSFER_CHARACTERISTICS_SRGB:
fR = SDL_sRGBfromLinear(fR);
fG = SDL_sRGBfromLinear(fG);
fB = SDL_sRGBfromLinear(fB);
break;
case SDL_TRANSFER_CHARACTERISTICS_PQ:
fR = SDL_PQfromNits(fR * SDR_white_point);
fG = SDL_PQfromNits(fG * SDR_white_point);
fB = SDL_PQfromNits(fB * SDR_white_point);
break;
case SDL_TRANSFER_CHARACTERISTICS_LINEAR:
fR *= SDR_white_point;
fG *= SDR_white_point;
fB *= SDR_white_point;
break;
default:
// Unknown, leave it alone
break;
}
switch (access) {
case SlowBlitPixelAccess_Index8:
// This should never happen, checked before this call
SDL_assert(0);
break;
case SlowBlitPixelAccess_RGB:
R = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f);
G = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f);
B = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f);
ASSEMBLE_RGB(pixels, fmt->bytes_per_pixel, fmt, R, G, B);
break;
case SlowBlitPixelAccess_RGBA:
R = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f);
G = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f);
B = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f);
A = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f);
ASSEMBLE_RGBA(pixels, fmt->bytes_per_pixel, fmt, R, G, B, A);
break;
case SlowBlitPixelAccess_10Bit:
{
switch (fmt->format) {
case SDL_PIXELFORMAT_XRGB2101010:
fA = 1.0f;
SDL_FALLTHROUGH;
case SDL_PIXELFORMAT_ARGB2101010:
ARGB2101010_FROM_RGBAFLOAT(pixel, fR, fG, fB, fA);
break;
case SDL_PIXELFORMAT_XBGR2101010:
fA = 1.0f;
SDL_FALLTHROUGH;
case SDL_PIXELFORMAT_ABGR2101010:
ABGR2101010_FROM_RGBAFLOAT(pixel, fR, fG, fB, fA);
break;
default:
pixel = 0;
break;
}
*(Uint32 *)pixels = pixel;
break;
}
case SlowBlitPixelAccess_Large:
switch (SDL_PIXELORDER(fmt->format)) {
case SDL_ARRAYORDER_RGB:
v[0] = fR;
v[1] = fG;
v[2] = fB;
v[3] = 1.0f;
break;
case SDL_ARRAYORDER_RGBA:
v[0] = fR;
v[1] = fG;
v[2] = fB;
v[3] = fA;
break;
case SDL_ARRAYORDER_ARGB:
v[0] = fA;
v[1] = fR;
v[2] = fG;
v[3] = fB;
break;
case SDL_ARRAYORDER_BGR:
v[0] = fB;
v[1] = fG;
v[2] = fR;
v[3] = 1.0f;
break;
case SDL_ARRAYORDER_BGRA:
v[0] = fB;
v[1] = fG;
v[2] = fR;
v[3] = fA;
break;
case SDL_ARRAYORDER_ABGR:
v[0] = fA;
v[1] = fB;
v[2] = fG;
v[3] = fR;
break;
default:
// Unknown array order
v[0] = v[1] = v[2] = v[3] = 0.0f;
break;
}
switch (SDL_PIXELTYPE(fmt->format)) {
case SDL_PIXELTYPE_ARRAYU16:
((Uint16 *)pixels)[0] = (Uint16)SDL_roundf(SDL_clamp(v[0], 0.0f, 1.0f) * SDL_MAX_UINT16);
((Uint16 *)pixels)[1] = (Uint16)SDL_roundf(SDL_clamp(v[1], 0.0f, 1.0f) * SDL_MAX_UINT16);
((Uint16 *)pixels)[2] = (Uint16)SDL_roundf(SDL_clamp(v[2], 0.0f, 1.0f) * SDL_MAX_UINT16);
if (fmt->bytes_per_pixel == 8) {
((Uint16 *)pixels)[3] = (Uint16)SDL_roundf(SDL_clamp(v[3], 0.0f, 1.0f) * SDL_MAX_UINT16);
}
break;
case SDL_PIXELTYPE_ARRAYF16:
((Uint16 *)pixels)[0] = float_to_half(v[0]);
((Uint16 *)pixels)[1] = float_to_half(v[1]);
((Uint16 *)pixels)[2] = float_to_half(v[2]);
if (fmt->bytes_per_pixel == 8) {
((Uint16 *)pixels)[3] = float_to_half(v[3]);
}
break;
case SDL_PIXELTYPE_ARRAYF32:
((float *)pixels)[0] = v[0];
((float *)pixels)[1] = v[1];
((float *)pixels)[2] = v[2];
if (fmt->bytes_per_pixel == 16) {
((float *)pixels)[3] = v[3];
}
break;
default:
// Unknown array type
break;
}
break;
}
}
typedef enum
{
SDL_TONEMAP_NONE,
SDL_TONEMAP_LINEAR,
SDL_TONEMAP_CHROME
} SDL_TonemapOperator;
typedef struct
{
SDL_TonemapOperator op;
union {
struct {
float scale;
} linear;
struct {
float a;
float b;
const float *color_primaries_matrix;
} chrome;
} data;
} SDL_TonemapContext;
static void TonemapLinear(float *r, float *g, float *b, float scale)
{
*r *= scale;
*g *= scale;
*b *= scale;
}
/* This uses the same tonemapping algorithm developed by Google for Chrome:
* https://colab.research.google.com/drive/1hI10nq6L6ru_UFvz7-f7xQaQp0qarz_K
*
* Essentially, you use the source headroom and the destination headroom
* to calculate scaling factors:
* tonemap_a = (dst_headroom / (src_headroom * src_headroom));
* tonemap_b = (1.0f / dst_headroom);
*
* Then you normalize your source color by the HDR whitepoint,
* and calculate a final scaling factor in BT.2020 colorspace.
*/
static void TonemapChrome(float *r, float *g, float *b, float tonemap_a, float tonemap_b)
{
float v1 = *r;
float v2 = *g;
float v3 = *b;
float vmax = SDL_max(v1, SDL_max(v2, v3));
if (vmax > 0.0f) {
float scale = (1.0f + tonemap_a * vmax) / (1.0f + tonemap_b * vmax);
TonemapLinear(r, g, b, scale);
}
}
static void ApplyTonemap(SDL_TonemapContext *ctx, float *r, float *g, float *b)
{
switch (ctx->op) {
case SDL_TONEMAP_LINEAR:
TonemapLinear(r, g, b, ctx->data.linear.scale);
break;
case SDL_TONEMAP_CHROME:
if (ctx->data.chrome.color_primaries_matrix) {
SDL_ConvertColorPrimaries(r, g, b, ctx->data.chrome.color_primaries_matrix);
}
TonemapChrome(r, g, b, ctx->data.chrome.a, ctx->data.chrome.b);
break;
default:
break;
}
}
/* The SECOND TRUE BLITTER
* This one is even slower than the first, but also handles large pixel formats and colorspace conversion
*/
void SDL_Blit_Slow_Float(SDL_BlitInfo *info)
{
const int flags = info->flags;
const Uint32 modulateR = info->r;
const Uint32 modulateG = info->g;
const Uint32 modulateB = info->b;
const Uint32 modulateA = info->a;
float srcR, srcG, srcB, srcA;
float dstR, dstG, dstB, dstA;
Uint64 srcy, srcx;
Uint64 posy, posx;
Uint64 incy, incx;
const SDL_PixelFormatDetails *src_fmt = info->src_fmt;
const SDL_Palette *src_pal = info->src_pal;
const SDL_PixelFormatDetails *dst_fmt = info->dst_fmt;
const SDL_Palette *dst_pal = info->dst_pal;
SDL_HashTable *palette_map = info->palette_map;
int srcbpp = src_fmt->bytes_per_pixel;
int dstbpp = dst_fmt->bytes_per_pixel;
SlowBlitPixelAccess src_access;
SlowBlitPixelAccess dst_access;
SDL_Colorspace src_colorspace;
SDL_Colorspace dst_colorspace;
SDL_ColorPrimaries src_primaries;
SDL_ColorPrimaries dst_primaries;
const float *color_primaries_matrix = NULL;
float src_white_point;
float dst_white_point;
float dst_headroom;
float src_headroom;
SDL_TonemapContext tonemap;
Uint32 last_pixel = 0;
Uint8 last_index = 0;
src_colorspace = info->src_surface->colorspace;
dst_colorspace = info->dst_surface->colorspace;
src_primaries = SDL_COLORSPACEPRIMARIES(src_colorspace);
dst_primaries = SDL_COLORSPACEPRIMARIES(dst_colorspace);
src_white_point = SDL_GetSurfaceSDRWhitePoint(info->src_surface, src_colorspace);
dst_white_point = SDL_GetSurfaceSDRWhitePoint(info->dst_surface, dst_colorspace);
src_headroom = SDL_GetSurfaceHDRHeadroom(info->src_surface, src_colorspace);
dst_headroom = SDL_GetSurfaceHDRHeadroom(info->dst_surface, dst_colorspace);
if (dst_headroom == 0.0f) {
// The destination will have the same headroom as the source
dst_headroom = src_headroom;
SDL_SetFloatProperty(SDL_GetSurfaceProperties(info->dst_surface), SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, dst_headroom);
}
SDL_zero(tonemap);
if (src_headroom > dst_headroom) {
const char *tonemap_operator = SDL_GetStringProperty(SDL_GetSurfaceProperties(info->src_surface), SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING, NULL);
if (tonemap_operator) {
if (SDL_strncmp(tonemap_operator, "*=", 2) == 0) {
tonemap.op = SDL_TONEMAP_LINEAR;
tonemap.data.linear.scale = (float)SDL_atof(tonemap_operator + 2);
} else if (SDL_strcasecmp(tonemap_operator, "chrome") == 0) {
tonemap.op = SDL_TONEMAP_CHROME;
} else if (SDL_strcasecmp(tonemap_operator, "none") == 0) {
tonemap.op = SDL_TONEMAP_NONE;
}
} else {
tonemap.op = SDL_TONEMAP_CHROME;
}
if (tonemap.op == SDL_TONEMAP_CHROME) {
tonemap.data.chrome.a = (dst_headroom / (src_headroom * src_headroom));
tonemap.data.chrome.b = (1.0f / dst_headroom);
// We'll convert to BT.2020 primaries for the tonemap operation
tonemap.data.chrome.color_primaries_matrix = SDL_GetColorPrimariesConversionMatrix(src_primaries, SDL_COLOR_PRIMARIES_BT2020);
if (tonemap.data.chrome.color_primaries_matrix) {
src_primaries = SDL_COLOR_PRIMARIES_BT2020;
}
}
}
if (src_primaries != dst_primaries) {
color_primaries_matrix = SDL_GetColorPrimariesConversionMatrix(src_primaries, dst_primaries);
}
src_access = GetPixelAccessMethod(src_fmt->format);
dst_access = GetPixelAccessMethod(dst_fmt->format);
if (dst_access == SlowBlitPixelAccess_Index8) {
last_index = SDL_LookupRGBAColor(palette_map, last_pixel, dst_pal);
}
incy = ((Uint64)info->src_h << 16) / info->dst_h;
incx = ((Uint64)info->src_w << 16) / info->dst_w;
posy = incy / 2; // start at the middle of pixel
while (info->dst_h--) {
Uint8 *src = 0;
Uint8 *dst = info->dst;
int n = info->dst_w;
posx = incx / 2; // start at the middle of pixel
srcy = posy >> 16;
while (n--) {
srcx = posx >> 16;
src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp));
ReadFloatPixel(src, src_access, src_fmt, src_pal, src_colorspace, src_white_point, &srcR, &srcG, &srcB, &srcA);
if (tonemap.op) {
ApplyTonemap(&tonemap, &srcR, &srcG, &srcB);
}
if (color_primaries_matrix) {
SDL_ConvertColorPrimaries(&srcR, &srcG, &srcB, color_primaries_matrix);
}
if (flags & SDL_COPY_COLORKEY) {
// colorkey isn't supported
}
if ((flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL))) {
ReadFloatPixel(dst, dst_access, dst_fmt, dst_pal, dst_colorspace, dst_white_point, &dstR, &dstG, &dstB, &dstA);
} else {
// don't care
dstR = dstG = dstB = dstA = 0.0f;
}
if (flags & SDL_COPY_MODULATE_COLOR) {
srcR = (srcR * modulateR) / 255;
srcG = (srcG * modulateG) / 255;
srcB = (srcB * modulateB) / 255;
}
if (flags & SDL_COPY_MODULATE_ALPHA) {
srcA = (srcA * modulateA) / 255;
}
if (flags & (SDL_COPY_BLEND | SDL_COPY_ADD)) {
if (srcA < 1.0f) {
srcR = (srcR * srcA);
srcG = (srcG * srcA);
srcB = (srcB * srcA);
}
}
switch (flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) {
case 0:
dstR = srcR;
dstG = srcG;
dstB = srcB;
dstA = srcA;
break;
case SDL_COPY_BLEND:
dstR = srcR + ((1.0f - srcA) * dstR);
dstG = srcG + ((1.0f - srcA) * dstG);
dstB = srcB + ((1.0f - srcA) * dstB);
dstA = srcA + ((1.0f - srcA) * dstA);
break;
case SDL_COPY_ADD:
dstR = srcR + dstR;
dstG = srcG + dstG;
dstB = srcB + dstB;
break;
case SDL_COPY_MOD:
dstR = (srcR * dstR);
dstG = (srcG * dstG);
dstB = (srcB * dstB);
break;
case SDL_COPY_MUL:
dstR = ((srcR * dstR) + (dstR * (1.0f - srcA)));
dstG = ((srcG * dstG) + (dstG * (1.0f - srcA)));
dstB = ((srcB * dstB) + (dstB * (1.0f - srcA)));
break;
}
if (dst_access == SlowBlitPixelAccess_Index8) {
Uint32 R = (Uint8)SDL_roundf(SDL_clamp(SDL_sRGBfromLinear(dstR), 0.0f, 1.0f) * 255.0f);
Uint32 G = (Uint8)SDL_roundf(SDL_clamp(SDL_sRGBfromLinear(dstG), 0.0f, 1.0f) * 255.0f);
Uint32 B = (Uint8)SDL_roundf(SDL_clamp(SDL_sRGBfromLinear(dstB), 0.0f, 1.0f) * 255.0f);
Uint32 A = (Uint8)SDL_roundf(SDL_clamp(dstA, 0.0f, 1.0f) * 255.0f);
Uint32 dstpixel = ((R << 24) | (G << 16) | (B << 8) | A);
if (dstpixel != last_pixel) {
last_pixel = dstpixel;
last_index = SDL_LookupRGBAColor(palette_map, dstpixel, dst_pal);
}
*dst = last_index;
} else {
WriteFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, dst_white_point, dstR, dstG, dstB, dstA);
}
posx += incx;
dst += dstbpp;
}
posy += incy;
info->dst += info->dst_pitch;
}
}

View file

@ -0,0 +1,30 @@
/*
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_blit_slow_h_
#define SDL_blit_slow_h_
#include "SDL_internal.h"
extern void SDL_Blit_Slow(SDL_BlitInfo *info);
extern void SDL_Blit_Slow_Float(SDL_BlitInfo *info);
#endif // SDL_blit_slow_h_

879
vendor/sdl-3.2.10/src/video/SDL_bmp.c vendored Normal file
View file

@ -0,0 +1,879 @@
/*
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"
/*
Code to load and save surfaces in Windows BMP format.
Why support BMP format? Well, it's a native format for Windows, and
most image processing programs can read and write it. It would be nice
to be able to have at least one image format that we can natively load
and save, and since PNG is so complex that it would bloat the library,
BMP is a good alternative.
This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
*/
#include "SDL_pixels_c.h"
#include "SDL_surface_c.h"
#define SAVE_32BIT_BMP
// Compression encodings for BMP files
#ifndef BI_RGB
#define BI_RGB 0
#define BI_RLE8 1
#define BI_RLE4 2
#define BI_BITFIELDS 3
#endif
// Logical color space values for BMP files
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/eb4bbd50-b3ce-4917-895c-be31f214797f
#ifndef LCS_WINDOWS_COLOR_SPACE
// 0x57696E20 == "Win "
#define LCS_WINDOWS_COLOR_SPACE 0x57696E20
#endif
#ifndef LCS_sRGB
// 0x73524742 == "sRGB"
#define LCS_sRGB 0x73524742
#endif
// Logical/physical color relationship
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/9fec0834-607d-427d-abd5-ab240fb0db38
#ifndef LCS_GM_GRAPHICS
#define LCS_GM_GRAPHICS 0x00000002
#endif
static bool readRlePixels(SDL_Surface *surface, SDL_IOStream *src, int isRle8)
{
/*
| Sets the surface pixels from src. A bmp image is upside down.
*/
int pitch = surface->pitch;
int height = surface->h;
Uint8 *start = (Uint8 *)surface->pixels;
Uint8 *end = start + (height * pitch);
Uint8 *bits = end - pitch, *spot;
int ofs = 0;
Uint8 ch;
Uint8 needsPad;
const int pixels_per_byte = (isRle8 ? 1 : 2);
#define COPY_PIXEL(x) \
spot = &bits[ofs++]; \
if (spot >= start && spot < end) \
*spot = (x)
for (;;) {
if (!SDL_ReadU8(src, &ch)) {
return false;
}
/*
| encoded mode starts with a run length, and then a byte
| with two colour indexes to alternate between for the run
*/
if (ch) {
Uint8 pixel;
if (!SDL_ReadU8(src, &pixel)) {
return false;
}
ch /= pixels_per_byte;
do {
COPY_PIXEL(pixel);
} while (--ch);
} else {
/*
| A leading zero is an escape; it may signal the end of the bitmap,
| a cursor move, or some absolute data.
| zero tag may be absolute mode or an escape
*/
if (!SDL_ReadU8(src, &ch)) {
return false;
}
switch (ch) {
case 0: // end of line
ofs = 0;
bits -= pitch; // go to previous
break;
case 1: // end of bitmap
return true; // success!
case 2: // delta
if (!SDL_ReadU8(src, &ch)) {
return false;
}
ofs += ch / pixels_per_byte;
if (!SDL_ReadU8(src, &ch)) {
return false;
}
bits -= ((ch / pixels_per_byte) * pitch);
break;
default: // no compression
ch /= pixels_per_byte;
needsPad = (ch & 1);
do {
Uint8 pixel;
if (!SDL_ReadU8(src, &pixel)) {
return false;
}
COPY_PIXEL(pixel);
} while (--ch);
// pad at even boundary
if (needsPad && !SDL_ReadU8(src, &ch)) {
return false;
}
break;
}
}
}
}
static void CorrectAlphaChannel(SDL_Surface *surface)
{
// Check to see if there is any alpha channel data
bool hasAlpha = false;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int alphaChannelOffset = 0;
#else
int alphaChannelOffset = 3;
#endif
Uint8 *alpha = ((Uint8 *)surface->pixels) + alphaChannelOffset;
Uint8 *end = alpha + surface->h * surface->pitch;
while (alpha < end) {
if (*alpha != 0) {
hasAlpha = true;
break;
}
alpha += 4;
}
if (!hasAlpha) {
alpha = ((Uint8 *)surface->pixels) + alphaChannelOffset;
while (alpha < end) {
*alpha = SDL_ALPHA_OPAQUE;
alpha += 4;
}
}
}
SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, bool closeio)
{
bool was_error = true;
Sint64 fp_offset = 0;
int i, pad;
SDL_Surface *surface;
Uint32 Rmask = 0;
Uint32 Gmask = 0;
Uint32 Bmask = 0;
Uint32 Amask = 0;
Uint8 *bits;
Uint8 *top, *end;
bool topDown;
bool haveRGBMasks = false;
bool haveAlphaMask = false;
bool correctAlpha = false;
// The Win32 BMP file header (14 bytes)
char magic[2];
// Uint32 bfSize;
// Uint16 bfReserved1;
// Uint16 bfReserved2;
Uint32 bfOffBits;
// The Win32 BITMAPINFOHEADER struct (40 bytes)
Uint32 biSize;
Sint32 biWidth = 0;
Sint32 biHeight = 0;
// Uint16 biPlanes;
Uint16 biBitCount = 0;
Uint32 biCompression = 0;
// Uint32 biSizeImage;
// Sint32 biXPelsPerMeter;
// Sint32 biYPelsPerMeter;
Uint32 biClrUsed = 0;
// Uint32 biClrImportant;
// Make sure we are passed a valid data source
surface = NULL;
if (!src) {
SDL_InvalidParamError("src");
goto done;
}
// Read in the BMP file header
fp_offset = SDL_TellIO(src);
if (fp_offset < 0) {
goto done;
}
SDL_ClearError();
if (SDL_ReadIO(src, magic, 2) != 2) {
goto done;
}
if (SDL_strncmp(magic, "BM", 2) != 0) {
SDL_SetError("File is not a Windows BMP file");
goto done;
}
if (!SDL_ReadU32LE(src, NULL /* bfSize */) ||
!SDL_ReadU16LE(src, NULL /* bfReserved1 */) ||
!SDL_ReadU16LE(src, NULL /* bfReserved2 */) ||
!SDL_ReadU32LE(src, &bfOffBits)) {
goto done;
}
// Read the Win32 BITMAPINFOHEADER
if (!SDL_ReadU32LE(src, &biSize)) {
goto done;
}
if (biSize == 12) { // really old BITMAPCOREHEADER
Uint16 biWidth16, biHeight16;
if (!SDL_ReadU16LE(src, &biWidth16) ||
!SDL_ReadU16LE(src, &biHeight16) ||
!SDL_ReadU16LE(src, NULL /* biPlanes */) ||
!SDL_ReadU16LE(src, &biBitCount)) {
goto done;
}
biWidth = biWidth16;
biHeight = biHeight16;
biCompression = BI_RGB;
// biSizeImage = 0;
// biXPelsPerMeter = 0;
// biYPelsPerMeter = 0;
biClrUsed = 0;
// biClrImportant = 0;
} else if (biSize >= 40) { // some version of BITMAPINFOHEADER
Uint32 headerSize;
if (!SDL_ReadS32LE(src, &biWidth) ||
!SDL_ReadS32LE(src, &biHeight) ||
!SDL_ReadU16LE(src, NULL /* biPlanes */) ||
!SDL_ReadU16LE(src, &biBitCount) ||
!SDL_ReadU32LE(src, &biCompression) ||
!SDL_ReadU32LE(src, NULL /* biSizeImage */) ||
!SDL_ReadU32LE(src, NULL /* biXPelsPerMeter */) ||
!SDL_ReadU32LE(src, NULL /* biYPelsPerMeter */) ||
!SDL_ReadU32LE(src, &biClrUsed) ||
!SDL_ReadU32LE(src, NULL /* biClrImportant */)) {
goto done;
}
// 64 == BITMAPCOREHEADER2, an incompatible OS/2 2.x extension. Skip this stuff for now.
if (biSize != 64) {
/* This is complicated. If compression is BI_BITFIELDS, then
we have 3 DWORDS that specify the RGB masks. This is either
stored here in an BITMAPV2INFOHEADER (which only differs in
that it adds these RGB masks) and biSize >= 52, or we've got
these masks stored in the exact same place, but strictly
speaking, this is the bmiColors field in BITMAPINFO immediately
following the legacy v1 info header, just past biSize. */
if (biCompression == BI_BITFIELDS) {
haveRGBMasks = true;
if (!SDL_ReadU32LE(src, &Rmask) ||
!SDL_ReadU32LE(src, &Gmask) ||
!SDL_ReadU32LE(src, &Bmask)) {
goto done;
}
// ...v3 adds an alpha mask.
if (biSize >= 56) { // BITMAPV3INFOHEADER; adds alpha mask
haveAlphaMask = true;
if (!SDL_ReadU32LE(src, &Amask)) {
goto done;
}
}
} else {
// the mask fields are ignored for v2+ headers if not BI_BITFIELD.
if (biSize >= 52) { // BITMAPV2INFOHEADER; adds RGB masks
if (!SDL_ReadU32LE(src, NULL /* Rmask */) ||
!SDL_ReadU32LE(src, NULL /* Gmask */) ||
!SDL_ReadU32LE(src, NULL /* Bmask */)) {
goto done;
}
}
if (biSize >= 56) { // BITMAPV3INFOHEADER; adds alpha mask
if (!SDL_ReadU32LE(src, NULL /* Amask */)) {
goto done;
}
}
}
/* Insert other fields here; Wikipedia and MSDN say we're up to
v5 of this header, but we ignore those for now (they add gamma,
color spaces, etc). Ignoring the weird OS/2 2.x format, we
currently parse up to v3 correctly (hopefully!). */
}
// skip any header bytes we didn't handle...
headerSize = (Uint32)(SDL_TellIO(src) - (fp_offset + 14));
if (biSize > headerSize) {
if (SDL_SeekIO(src, (biSize - headerSize), SDL_IO_SEEK_CUR) < 0) {
goto done;
}
}
}
if (biWidth <= 0 || biHeight == 0) {
SDL_SetError("BMP file with bad dimensions (%" SDL_PRIs32 "x%" SDL_PRIs32 ")", biWidth, biHeight);
goto done;
}
if (biHeight < 0) {
topDown = true;
biHeight = -biHeight;
} else {
topDown = false;
}
// Check for read error
if (SDL_strcmp(SDL_GetError(), "") != 0) {
goto done;
}
// Reject invalid bit depths
switch (biBitCount) {
case 0:
case 3:
case 5:
case 6:
case 7:
SDL_SetError("%u bpp BMP images are not supported", biBitCount);
goto done;
default:
break;
}
// RLE4 and RLE8 BMP compression is supported
switch (biCompression) {
case BI_RGB:
// If there are no masks, use the defaults
SDL_assert(!haveRGBMasks);
SDL_assert(!haveAlphaMask);
// Default values for the BMP format
switch (biBitCount) {
case 15:
case 16:
// SDL_PIXELFORMAT_XRGB1555 or SDL_PIXELFORMAT_ARGB1555 if Amask
Rmask = 0x7C00;
Gmask = 0x03E0;
Bmask = 0x001F;
break;
case 24:
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
// SDL_PIXELFORMAT_RGB24
Rmask = 0x000000FF;
Gmask = 0x0000FF00;
Bmask = 0x00FF0000;
#else
// SDL_PIXELFORMAT_BGR24
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
#endif
break;
case 32:
// We don't know if this has alpha channel or not
correctAlpha = true;
// SDL_PIXELFORMAT_RGBA8888
Amask = 0xFF000000;
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
break;
default:
break;
}
break;
case BI_BITFIELDS:
break; // we handled this in the info header.
default:
break;
}
// Create a compatible surface, note that the colors are RGB ordered
{
SDL_PixelFormat format;
// Get the pixel format
format = SDL_GetPixelFormatForMasks(biBitCount, Rmask, Gmask, Bmask, Amask);
surface = SDL_CreateSurface(biWidth, biHeight, format);
if (!surface) {
goto done;
}
}
// Load the palette, if any
if (SDL_ISPIXELFORMAT_INDEXED(surface->format)) {
SDL_Palette *palette = SDL_CreateSurfacePalette(surface);
if (!palette) {
goto done;
}
if (SDL_SeekIO(src, fp_offset + 14 + biSize, SDL_IO_SEEK_SET) < 0) {
SDL_SetError("Error seeking in datastream");
goto done;
}
if (biBitCount >= 32) { // we shift biClrUsed by this value later.
SDL_SetError("Unsupported or incorrect biBitCount field");
goto done;
}
if (biClrUsed == 0) {
biClrUsed = 1 << biBitCount;
}
if (biClrUsed > (Uint32)palette->ncolors) {
biClrUsed = 1 << biBitCount; // try forcing it?
if (biClrUsed > (Uint32)palette->ncolors) {
SDL_SetError("Unsupported or incorrect biClrUsed field");
goto done;
}
}
palette->ncolors = biClrUsed;
if (biSize == 12) {
for (i = 0; i < palette->ncolors; ++i) {
if (!SDL_ReadU8(src, &palette->colors[i].b) ||
!SDL_ReadU8(src, &palette->colors[i].g) ||
!SDL_ReadU8(src, &palette->colors[i].r)) {
goto done;
}
palette->colors[i].a = SDL_ALPHA_OPAQUE;
}
} else {
for (i = 0; i < palette->ncolors; ++i) {
if (!SDL_ReadU8(src, &palette->colors[i].b) ||
!SDL_ReadU8(src, &palette->colors[i].g) ||
!SDL_ReadU8(src, &palette->colors[i].r) ||
!SDL_ReadU8(src, &palette->colors[i].a)) {
goto done;
}
/* According to Microsoft documentation, the fourth element
is reserved and must be zero, so we shouldn't treat it as
alpha.
*/
palette->colors[i].a = SDL_ALPHA_OPAQUE;
}
}
}
// Read the surface pixels. Note that the bmp image is upside down
if (SDL_SeekIO(src, fp_offset + bfOffBits, SDL_IO_SEEK_SET) < 0) {
SDL_SetError("Error seeking in datastream");
goto done;
}
if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
if (!readRlePixels(surface, src, biCompression == BI_RLE8)) {
SDL_SetError("Error reading from datastream");
goto done;
}
// Success!
was_error = false;
goto done;
}
top = (Uint8 *)surface->pixels;
end = (Uint8 *)surface->pixels + (surface->h * surface->pitch);
pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
if (topDown) {
bits = top;
} else {
bits = end - surface->pitch;
}
while (bits >= top && bits < end) {
if (SDL_ReadIO(src, bits, surface->pitch) != (size_t)surface->pitch) {
goto done;
}
if (biBitCount == 8 && surface->palette && biClrUsed < (1u << biBitCount)) {
for (i = 0; i < surface->w; ++i) {
if (bits[i] >= biClrUsed) {
SDL_SetError("A BMP image contains a pixel with a color out of the palette");
goto done;
}
}
}
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
/* Byte-swap the pixels if needed. Note that the 24bpp
case has already been taken care of above. */
switch (biBitCount) {
case 15:
case 16:
{
Uint16 *pix = (Uint16 *)bits;
for (i = 0; i < surface->w; i++) {
pix[i] = SDL_Swap16(pix[i]);
}
break;
}
case 32:
{
Uint32 *pix = (Uint32 *)bits;
for (i = 0; i < surface->w; i++) {
pix[i] = SDL_Swap32(pix[i]);
}
break;
}
}
#endif
// Skip padding bytes, ugh
if (pad) {
Uint8 padbyte;
for (i = 0; i < pad; ++i) {
if (!SDL_ReadU8(src, &padbyte)) {
goto done;
}
}
}
if (topDown) {
bits += surface->pitch;
} else {
bits -= surface->pitch;
}
}
if (correctAlpha) {
CorrectAlphaChannel(surface);
}
was_error = false;
done:
if (was_error) {
if (src) {
SDL_SeekIO(src, fp_offset, SDL_IO_SEEK_SET);
}
SDL_DestroySurface(surface);
surface = NULL;
}
if (closeio && src) {
SDL_CloseIO(src);
}
return surface;
}
SDL_Surface *SDL_LoadBMP(const char *file)
{
SDL_IOStream *stream = SDL_IOFromFile(file, "rb");
if (!stream) {
return NULL;
}
return SDL_LoadBMP_IO(stream, true);
}
bool SDL_SaveBMP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
{
bool was_error = true;
Sint64 fp_offset, new_offset;
int i, pad;
SDL_Surface *intermediate_surface = NULL;
Uint8 *bits;
bool save32bit = false;
bool saveLegacyBMP = false;
// The Win32 BMP file header (14 bytes)
char magic[2] = { 'B', 'M' };
Uint32 bfSize;
Uint16 bfReserved1;
Uint16 bfReserved2;
Uint32 bfOffBits;
// The Win32 BITMAPINFOHEADER struct (40 bytes)
Uint32 biSize;
Sint32 biWidth;
Sint32 biHeight;
Uint16 biPlanes;
Uint16 biBitCount;
Uint32 biCompression;
Uint32 biSizeImage;
Sint32 biXPelsPerMeter;
Sint32 biYPelsPerMeter;
Uint32 biClrUsed;
Uint32 biClrImportant;
// The additional header members from the Win32 BITMAPV4HEADER struct (108 bytes in total)
Uint32 bV4RedMask = 0;
Uint32 bV4GreenMask = 0;
Uint32 bV4BlueMask = 0;
Uint32 bV4AlphaMask = 0;
Uint32 bV4CSType = 0;
Sint32 bV4Endpoints[3 * 3] = { 0 };
Uint32 bV4GammaRed = 0;
Uint32 bV4GammaGreen = 0;
Uint32 bV4GammaBlue = 0;
// The additional header members from the Win32 BITMAPV5HEADER struct (124 bytes in total)
Uint32 bV5Intent = 0;
Uint32 bV5ProfileData = 0;
Uint32 bV5ProfileSize = 0;
Uint32 bV5Reserved = 0;
// Make sure we have somewhere to save
if (!SDL_SurfaceValid(surface)) {
SDL_InvalidParamError("surface");
goto done;
}
if (!dst) {
SDL_InvalidParamError("dst");
goto done;
}
#ifdef SAVE_32BIT_BMP
// We can save alpha information in a 32-bit BMP
if (SDL_BITSPERPIXEL(surface->format) >= 8 &&
(SDL_ISPIXELFORMAT_ALPHA(surface->format) ||
surface->map.info.flags & SDL_COPY_COLORKEY)) {
save32bit = true;
}
#endif // SAVE_32BIT_BMP
if (surface->palette && !save32bit) {
if (SDL_BITSPERPIXEL(surface->format) == 8) {
intermediate_surface = surface;
} else {
SDL_SetError("%u bpp BMP files not supported",
SDL_BITSPERPIXEL(surface->format));
goto done;
}
} else if ((surface->format == SDL_PIXELFORMAT_BGR24 && !save32bit) ||
(surface->format == SDL_PIXELFORMAT_BGRA32 && save32bit)) {
intermediate_surface = surface;
} else {
SDL_PixelFormat pixel_format;
/* If the surface has a colorkey or alpha channel we'll save a
32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
if (save32bit) {
pixel_format = SDL_PIXELFORMAT_BGRA32;
} else {
pixel_format = SDL_PIXELFORMAT_BGR24;
}
intermediate_surface = SDL_ConvertSurface(surface, pixel_format);
if (!intermediate_surface) {
SDL_SetError("Couldn't convert image to %d bpp",
(int)SDL_BITSPERPIXEL(pixel_format));
goto done;
}
}
if (save32bit) {
saveLegacyBMP = SDL_GetHintBoolean(SDL_HINT_BMP_SAVE_LEGACY_FORMAT, false);
}
if (SDL_LockSurface(intermediate_surface)) {
const size_t bw = intermediate_surface->w * intermediate_surface->fmt->bytes_per_pixel;
// Set the BMP file header values
bfSize = 0; // We'll write this when we're done
bfReserved1 = 0;
bfReserved2 = 0;
bfOffBits = 0; // We'll write this when we're done
// Write the BMP file header values
fp_offset = SDL_TellIO(dst);
if (fp_offset < 0) {
goto done;
}
if (SDL_WriteIO(dst, magic, 2) != 2 ||
!SDL_WriteU32LE(dst, bfSize) ||
!SDL_WriteU16LE(dst, bfReserved1) ||
!SDL_WriteU16LE(dst, bfReserved2) ||
!SDL_WriteU32LE(dst, bfOffBits)) {
goto done;
}
// Set the BMP info values
biSize = 40;
biWidth = intermediate_surface->w;
biHeight = intermediate_surface->h;
biPlanes = 1;
biBitCount = intermediate_surface->fmt->bits_per_pixel;
biCompression = BI_RGB;
biSizeImage = intermediate_surface->h * intermediate_surface->pitch;
biXPelsPerMeter = 0;
biYPelsPerMeter = 0;
if (intermediate_surface->palette) {
biClrUsed = intermediate_surface->palette->ncolors;
} else {
biClrUsed = 0;
}
biClrImportant = 0;
// Set the BMP info values
if (save32bit && !saveLegacyBMP) {
biSize = 124;
// Version 4 values
biCompression = BI_BITFIELDS;
// The BMP format is always little endian, these masks stay the same
bV4RedMask = 0x00ff0000;
bV4GreenMask = 0x0000ff00;
bV4BlueMask = 0x000000ff;
bV4AlphaMask = 0xff000000;
bV4CSType = LCS_sRGB;
bV4GammaRed = 0;
bV4GammaGreen = 0;
bV4GammaBlue = 0;
// Version 5 values
bV5Intent = LCS_GM_GRAPHICS;
bV5ProfileData = 0;
bV5ProfileSize = 0;
bV5Reserved = 0;
}
// Write the BMP info values
if (!SDL_WriteU32LE(dst, biSize) ||
!SDL_WriteS32LE(dst, biWidth) ||
!SDL_WriteS32LE(dst, biHeight) ||
!SDL_WriteU16LE(dst, biPlanes) ||
!SDL_WriteU16LE(dst, biBitCount) ||
!SDL_WriteU32LE(dst, biCompression) ||
!SDL_WriteU32LE(dst, biSizeImage) ||
!SDL_WriteU32LE(dst, biXPelsPerMeter) ||
!SDL_WriteU32LE(dst, biYPelsPerMeter) ||
!SDL_WriteU32LE(dst, biClrUsed) ||
!SDL_WriteU32LE(dst, biClrImportant)) {
goto done;
}
// Write the BMP info values
if (save32bit && !saveLegacyBMP) {
// Version 4 values
if (!SDL_WriteU32LE(dst, bV4RedMask) ||
!SDL_WriteU32LE(dst, bV4GreenMask) ||
!SDL_WriteU32LE(dst, bV4BlueMask) ||
!SDL_WriteU32LE(dst, bV4AlphaMask) ||
!SDL_WriteU32LE(dst, bV4CSType)) {
goto done;
}
for (i = 0; i < 3 * 3; i++) {
if (!SDL_WriteU32LE(dst, bV4Endpoints[i])) {
goto done;
}
}
if (!SDL_WriteU32LE(dst, bV4GammaRed) ||
!SDL_WriteU32LE(dst, bV4GammaGreen) ||
!SDL_WriteU32LE(dst, bV4GammaBlue)) {
goto done;
}
// Version 5 values
if (!SDL_WriteU32LE(dst, bV5Intent) ||
!SDL_WriteU32LE(dst, bV5ProfileData) ||
!SDL_WriteU32LE(dst, bV5ProfileSize) ||
!SDL_WriteU32LE(dst, bV5Reserved)) {
goto done;
}
}
// Write the palette (in BGR color order)
if (intermediate_surface->palette) {
SDL_Color *colors;
int ncolors;
colors = intermediate_surface->palette->colors;
ncolors = intermediate_surface->palette->ncolors;
for (i = 0; i < ncolors; ++i) {
if (!SDL_WriteU8(dst, colors[i].b) ||
!SDL_WriteU8(dst, colors[i].g) ||
!SDL_WriteU8(dst, colors[i].r) ||
!SDL_WriteU8(dst, colors[i].a)) {
goto done;
}
}
}
// Write the bitmap offset
bfOffBits = (Uint32)(SDL_TellIO(dst) - fp_offset);
if (SDL_SeekIO(dst, fp_offset + 10, SDL_IO_SEEK_SET) < 0) {
goto done;
}
if (!SDL_WriteU32LE(dst, bfOffBits)) {
goto done;
}
if (SDL_SeekIO(dst, fp_offset + bfOffBits, SDL_IO_SEEK_SET) < 0) {
goto done;
}
// Write the bitmap image upside down
bits = (Uint8 *)intermediate_surface->pixels + (intermediate_surface->h * intermediate_surface->pitch);
pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
while (bits > (Uint8 *)intermediate_surface->pixels) {
bits -= intermediate_surface->pitch;
if (SDL_WriteIO(dst, bits, bw) != bw) {
goto done;
}
if (pad) {
const Uint8 padbyte = 0;
for (i = 0; i < pad; ++i) {
if (!SDL_WriteU8(dst, padbyte)) {
goto done;
}
}
}
}
// Write the BMP file size
new_offset = SDL_TellIO(dst);
if (new_offset < 0) {
goto done;
}
bfSize = (Uint32)(new_offset - fp_offset);
if (SDL_SeekIO(dst, fp_offset + 2, SDL_IO_SEEK_SET) < 0) {
goto done;
}
if (!SDL_WriteU32LE(dst, bfSize)) {
goto done;
}
if (SDL_SeekIO(dst, fp_offset + bfSize, SDL_IO_SEEK_SET) < 0) {
goto done;
}
// Close it up..
SDL_UnlockSurface(intermediate_surface);
was_error = false;
}
done:
if (intermediate_surface && intermediate_surface != surface) {
SDL_DestroySurface(intermediate_surface);
}
if (closeio && dst) {
if (!SDL_CloseIO(dst)) {
was_error = true;
}
}
if (was_error) {
return false;
}
return true;
}
bool SDL_SaveBMP(SDL_Surface *surface, const char *file)
{
SDL_IOStream *stream = SDL_IOFromFile(file, "wb");
if (!stream) {
return false;
}
return SDL_SaveBMP_IO(surface, stream, true);
}

View file

@ -0,0 +1,474 @@
/*
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"
#include "SDL_clipboard_c.h"
#include "SDL_sysvideo.h"
#include "../events/SDL_events_c.h"
#include "../events/SDL_clipboardevents_c.h"
void SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this)
{
if (_this->clipboard_mime_types) {
for (size_t i = 0; i < _this->num_clipboard_mime_types; ++i) {
SDL_free(_this->clipboard_mime_types[i]);
}
SDL_free(_this->clipboard_mime_types);
_this->clipboard_mime_types = NULL;
_this->num_clipboard_mime_types = 0;
}
}
void SDL_CancelClipboardData(Uint32 sequence)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (sequence && sequence != _this->clipboard_sequence) {
// This clipboard data was already canceled
return;
}
if (_this->clipboard_cleanup) {
_this->clipboard_cleanup(_this->clipboard_userdata);
}
SDL_FreeClipboardMimeTypes(_this);
_this->clipboard_callback = NULL;
_this->clipboard_cleanup = NULL;
_this->clipboard_userdata = NULL;
}
bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_FreeClipboardMimeTypes(_this);
if (mime_types && num_mime_types > 0) {
size_t num_allocated = 0;
_this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *));
if (_this->clipboard_mime_types) {
for (size_t i = 0; i < num_mime_types; ++i) {
_this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]);
if (_this->clipboard_mime_types[i]) {
++num_allocated;
}
}
}
if (num_allocated < num_mime_types) {
SDL_FreeClipboardMimeTypes(_this);
return false;
}
_this->num_clipboard_mime_types = num_mime_types;
}
return true;
}
bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
return SDL_UninitializedVideo();
}
// Parameter validation
if (!((callback && mime_types && num_mime_types > 0) ||
(!callback && !mime_types && num_mime_types == 0))) {
return SDL_SetError("Invalid parameters");
}
SDL_CancelClipboardData(0);
++_this->clipboard_sequence;
if (!_this->clipboard_sequence) {
_this->clipboard_sequence = 1;
}
_this->clipboard_callback = callback;
_this->clipboard_cleanup = cleanup;
_this->clipboard_userdata = userdata;
if (!SDL_SaveClipboardMimeTypes(mime_types, num_mime_types)) {
SDL_ClearClipboardData();
return false;
}
if (_this->SetClipboardData) {
if (!_this->SetClipboardData(_this)) {
return false;
}
} else if (_this->SetClipboardText) {
char *text = NULL;
size_t size;
for (size_t i = 0; i < num_mime_types; ++i) {
const char *mime_type = _this->clipboard_mime_types[i];
if (SDL_IsTextMimeType(mime_type)) {
const void *data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &size);
if (data) {
text = (char *)SDL_malloc(size + 1);
SDL_memcpy(text, data, size);
text[size] = '\0';
if (!_this->SetClipboardText(_this, text)) {
SDL_free(text);
return false;
}
break;
}
}
}
if (text) {
SDL_free(text);
} else {
if (!_this->SetClipboardText(_this, "")) {
return false;
}
}
}
char **mime_types_copy = SDL_CopyClipboardMimeTypes(mime_types, num_mime_types, true);
if (!mime_types_copy)
return SDL_SetError("unable to copy current mime types");
SDL_SendClipboardUpdate(true, mime_types_copy, num_mime_types);
return true;
}
bool SDL_ClearClipboardData(void)
{
return SDL_SetClipboardData(NULL, NULL, NULL, NULL, 0);
}
void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
{
void *data = NULL;
if (_this->clipboard_callback) {
const void *provided_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, size);
if (provided_data) {
// Make a copy of it for the caller and guarantee null termination
data = SDL_malloc(*size + sizeof(Uint32));
if (data) {
SDL_memcpy(data, provided_data, *size);
SDL_memset((Uint8 *)data + *size, 0, sizeof(Uint32));
}
}
}
return data;
}
void *SDL_GetClipboardData(const char *mime_type, size_t *size)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
size_t unused;
if (!_this) {
SDL_UninitializedVideo();
return NULL;
}
if (!mime_type) {
SDL_InvalidParamError("mime_type");
return NULL;
}
if (!size) {
size = &unused;
}
// Initialize size to empty, so implementations don't have to worry about it
*size = 0;
if (_this->GetClipboardData) {
return _this->GetClipboardData(_this, mime_type, size);
} else if (_this->GetClipboardText && SDL_IsTextMimeType(mime_type)) {
char *text = _this->GetClipboardText(_this);
if (text) {
if (*text == '\0') {
SDL_free(text);
text = NULL;
} else {
*size = SDL_strlen(text);
}
}
return text;
} else {
return SDL_GetInternalClipboardData(_this, mime_type, size);
}
}
bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type)
{
size_t i;
for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
if (SDL_strcmp(mime_type, _this->clipboard_mime_types[i]) == 0) {
return true;
}
}
return false;
}
bool SDL_HasClipboardData(const char *mime_type)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
SDL_UninitializedVideo();
return false;
}
if (!mime_type) {
SDL_InvalidParamError("mime_type");
return false;
}
if (_this->HasClipboardData) {
return _this->HasClipboardData(_this, mime_type);
} else if (_this->HasClipboardText && SDL_IsTextMimeType(mime_type)) {
return _this->HasClipboardText(_this);
} else {
return SDL_HasInternalClipboardData(_this, mime_type);
}
}
char **SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary)
{
size_t allocSize = sizeof(char *);
for (size_t i = 0; i < num_mime_types; i++) {
allocSize += sizeof(char *) + SDL_strlen(clipboard_mime_types[i]) + 1;
}
char *ret;
if (temporary)
ret = (char *)SDL_AllocateTemporaryMemory(allocSize);
else
ret = (char *)SDL_malloc(allocSize);
if (!ret) {
return NULL;
}
char **result = (char **)ret;
ret += sizeof(char *) * (num_mime_types + 1);
for (size_t i = 0; i < num_mime_types; i++) {
result[i] = ret;
const char *mime_type = clipboard_mime_types[i];
// Copy the whole string including the terminating null char
char c;
do {
c = *ret++ = *mime_type++;
} while (c != '\0');
}
result[num_mime_types] = NULL;
return result;
}
char **SDL_GetClipboardMimeTypes(size_t *num_mime_types)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (num_mime_types) {
*num_mime_types = 0;
}
if (!_this) {
SDL_UninitializedVideo();
return NULL;
}
if (num_mime_types) {
*num_mime_types = _this->num_clipboard_mime_types;
}
return SDL_CopyClipboardMimeTypes((const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, false);
}
// Clipboard text
bool SDL_IsTextMimeType(const char *mime_type)
{
return (SDL_strncmp(mime_type, "text", 4) == 0);
}
static const char **SDL_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types)
{
if (_this->GetTextMimeTypes) {
return _this->GetTextMimeTypes(_this, num_mime_types);
} else {
static const char *text_mime_types[] = {
"text/plain;charset=utf-8"
};
*num_mime_types = SDL_arraysize(text_mime_types);
return text_mime_types;
}
}
const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size)
{
char *text = (char *)userdata;
if (text) {
*size = SDL_strlen(text);
} else {
*size = 0;
}
return text;
}
bool SDL_SetClipboardText(const char *text)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
size_t num_mime_types;
const char **text_mime_types;
if (!_this) {
return SDL_UninitializedVideo();
}
if (text && *text) {
text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types);
return SDL_SetClipboardData(SDL_ClipboardTextCallback, SDL_free, SDL_strdup(text), text_mime_types, num_mime_types);
}
return SDL_ClearClipboardData();
}
char *SDL_GetClipboardText(void)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
size_t i, num_mime_types;
const char **text_mime_types;
size_t length;
char *text = NULL;
if (!_this) {
SDL_UninitializedVideo();
return SDL_strdup("");
}
text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types);
for (i = 0; i < num_mime_types; ++i) {
void *clipdata = SDL_GetClipboardData(text_mime_types[i], &length);
if (clipdata) {
text = (char *)clipdata;
break;
}
}
if (!text) {
text = SDL_strdup("");
}
return text;
}
bool SDL_HasClipboardText(void)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
size_t i, num_mime_types;
const char **text_mime_types;
if (!_this) {
return SDL_UninitializedVideo();
}
text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types);
for (i = 0; i < num_mime_types; ++i) {
if (SDL_HasClipboardData(text_mime_types[i])) {
return true;
}
}
return false;
}
// Primary selection text
bool SDL_SetPrimarySelectionText(const char *text)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
return SDL_UninitializedVideo();
}
if (!text) {
text = "";
}
if (_this->SetPrimarySelectionText) {
if (!_this->SetPrimarySelectionText(_this, text)) {
return false;
}
} else {
SDL_free(_this->primary_selection_text);
_this->primary_selection_text = SDL_strdup(text);
}
char **mime_types = SDL_CopyClipboardMimeTypes((const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, true);
if (!mime_types)
return SDL_SetError("unable to copy current mime types");
SDL_SendClipboardUpdate(true, mime_types, _this->num_clipboard_mime_types);
return true;
}
char *SDL_GetPrimarySelectionText(void)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
SDL_UninitializedVideo();
return SDL_strdup("");
}
if (_this->GetPrimarySelectionText) {
return _this->GetPrimarySelectionText(_this);
} else {
const char *text = _this->primary_selection_text;
if (!text) {
text = "";
}
return SDL_strdup(text);
}
}
bool SDL_HasPrimarySelectionText(void)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
return SDL_UninitializedVideo();
}
if (_this->HasPrimarySelectionText) {
return _this->HasPrimarySelectionText(_this);
} else {
if (_this->primary_selection_text && _this->primary_selection_text[0] != '\0') {
return true;
} else {
return false;
}
}
}

View file

@ -0,0 +1,46 @@
/*
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_clipboard_c_h_
#define SDL_clipboard_c_h_
#include "SDL_sysvideo.h"
// Return true if the mime type is valid clipboard text
extern bool SDL_IsTextMimeType(const char *mime_type);
// Cancel the clipboard data callback, called internally for cleanup
extern void SDL_CancelClipboardData(Uint32 sequence);
// Call the clipboard callback for application data
extern void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
extern bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type);
// General purpose clipboard text callback
const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size);
bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types);
void SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this);
char **SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary);
#endif // SDL_clipboard_c_h_

1339
vendor/sdl-3.2.10/src/video/SDL_egl.c vendored Normal file

File diff suppressed because it is too large Load diff

171
vendor/sdl-3.2.10/src/video/SDL_egl_c.h vendored Normal file
View file

@ -0,0 +1,171 @@
/*
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_egl_h_
#define SDL_egl_h_
#ifdef SDL_VIDEO_OPENGL_EGL
#include <SDL3/SDL_egl.h>
#include "SDL_sysvideo.h"
#define SDL_EGL_MAX_DEVICES 8
// For systems that don't define these
typedef intptr_t EGLAttrib;
typedef void *EGLDeviceEXT;
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETDISPLAYPROC) (EGLNativeDisplayType display_id);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLINITIALIZEPROC) (EGLDisplay dpy, EGLint *major, EGLint *minor);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLTERMINATEPROC) (EGLDisplay dpy);
typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC) (const char *procname);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLCHOOSECONFIGPROC) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
typedef EGLContext (EGLAPIENTRYP PFNEGLCREATECONTEXTPROC) (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx);
typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERSURFACEPROC) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLMAKECURRENTPROC) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPINTERVALPROC) (EGLDisplay dpy, EGLint interval);
typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGATTRIBPROC) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITNATIVEPROC) (EGLint engine);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITGLPROC) (void);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDAPIPROC) (EGLenum api);
typedef EGLint (EGLAPIENTRYP PFNEGLGETERRORPROC) (void);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICESEXTPROC) (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices);
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync);
typedef EGLint (EGLAPIENTRYP PFNEGLDUPNATIVEFENCEFDANDROIDPROC) (EGLDisplay dpy, EGLSyncKHR sync);
typedef EGLint (EGLAPIENTRYP PFNEGLWAITSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags);
typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
typedef struct SDL_EGL_VideoData
{
SDL_SharedObject *opengl_dll_handle;
SDL_SharedObject *egl_dll_handle;
EGLDisplay egl_display;
EGLConfig egl_config;
int egl_swapinterval;
int egl_surfacetype;
int egl_version_major, egl_version_minor;
EGLint egl_required_visual_id;
bool is_offscreen; // whether EGL display was offscreen
EGLenum apitype; // EGL_OPENGL_ES_API, EGL_OPENGL_API, etc
PFNEGLGETDISPLAYPROC eglGetDisplay;
PFNEGLINITIALIZEPROC eglInitialize;
PFNEGLTERMINATEPROC eglTerminate;
PFNEGLGETPROCADDRESSPROC eglGetProcAddress;
PFNEGLCHOOSECONFIGPROC eglChooseConfig;
PFNEGLCREATECONTEXTPROC eglCreateContext;
PFNEGLDESTROYCONTEXTPROC eglDestroyContext;
PFNEGLCREATEPBUFFERSURFACEPROC eglCreatePbufferSurface;
PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurface;
PFNEGLDESTROYSURFACEPROC eglDestroySurface;
PFNEGLMAKECURRENTPROC eglMakeCurrent;
PFNEGLSWAPBUFFERSPROC eglSwapBuffers;
PFNEGLSWAPINTERVALPROC eglSwapInterval;
PFNEGLQUERYSTRINGPROC eglQueryString;
PFNEGLGETCONFIGATTRIBPROC eglGetConfigAttrib;
PFNEGLWAITNATIVEPROC eglWaitNative;
PFNEGLWAITGLPROC eglWaitGL;
PFNEGLBINDAPIPROC eglBindAPI;
PFNEGLGETERRORPROC eglGetError;
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplay;
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
// Atomic functions
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID;
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR;
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
// Atomic functions end
} SDL_EGL_VideoData;
// OpenGLES functions
typedef enum SDL_EGL_ExtensionType
{
SDL_EGL_DISPLAY_EXTENSION,
SDL_EGL_CLIENT_EXTENSION
} SDL_EGL_ExtensionType;
extern bool SDL_EGL_HasExtension(SDL_VideoDevice *_this, SDL_EGL_ExtensionType type, const char *ext);
extern bool SDL_EGL_GetAttribute(SDL_VideoDevice *_this, SDL_GLAttr attrib, int *value);
/* SDL_EGL_LoadLibrary can get a display for a specific platform (EGL_PLATFORM_*)
* or, if 0 is passed, let the implementation decide.
*/
extern bool SDL_EGL_LoadLibraryOnly(SDL_VideoDevice *_this, const char *path);
extern bool SDL_EGL_LoadLibrary(SDL_VideoDevice *_this, const char *path, NativeDisplayType native_display, EGLenum platform);
extern SDL_FunctionPointer SDL_EGL_GetProcAddressInternal(SDL_VideoDevice *_this, const char *proc);
extern void SDL_EGL_UnloadLibrary(SDL_VideoDevice *_this);
extern void SDL_EGL_SetRequiredVisualId(SDL_VideoDevice *_this, int visual_id);
extern bool SDL_EGL_ChooseConfig(SDL_VideoDevice *_this);
extern bool SDL_EGL_SetSwapInterval(SDL_VideoDevice *_this, int interval);
extern bool SDL_EGL_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
extern bool SDL_EGL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
extern EGLSurface SDL_EGL_CreateSurface(SDL_VideoDevice *_this, SDL_Window *window, NativeWindowType nw);
extern void SDL_EGL_DestroySurface(SDL_VideoDevice *_this, EGLSurface egl_surface);
extern EGLSurface SDL_EGL_CreateOffscreenSurface(SDL_VideoDevice *_this, int width, int height);
// Assumes that LoadLibraryOnly() has succeeded
extern bool SDL_EGL_InitializeOffscreen(SDL_VideoDevice *_this, int device);
// These need to be wrapped to get the surface for the window by the platform GLES implementation
extern SDL_GLContext SDL_EGL_CreateContext(SDL_VideoDevice *_this, EGLSurface egl_surface);
extern bool SDL_EGL_MakeCurrent(SDL_VideoDevice *_this, EGLSurface egl_surface, SDL_GLContext context);
extern bool SDL_EGL_SwapBuffers(SDL_VideoDevice *_this, EGLSurface egl_surface);
// SDL Error-reporting
extern bool SDL_EGL_SetErrorEx(const char *message, const char *eglFunctionName, EGLint eglErrorCode);
#define SDL_EGL_SetError(message, eglFunctionName) SDL_EGL_SetErrorEx(message, eglFunctionName, _this->egl_data->eglGetError())
// A few of useful macros
#define SDL_EGL_SwapWindow_impl(BACKEND) \
bool BACKEND##_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) \
{ \
return SDL_EGL_SwapBuffers(_this, window->internal->egl_surface); \
}
#define SDL_EGL_MakeCurrent_impl(BACKEND) \
bool BACKEND##_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context) \
{ \
return SDL_EGL_MakeCurrent(_this, window ? window->internal->egl_surface : EGL_NO_SURFACE, context); \
}
#define SDL_EGL_CreateContext_impl(BACKEND) \
SDL_GLContext BACKEND##_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window) \
{ \
return SDL_EGL_CreateContext(_this, window->internal->egl_surface); \
}
#endif // SDL_VIDEO_OPENGL_EGL
#endif // SDL_egl_h_

View file

@ -0,0 +1,369 @@
/*
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"
#include "SDL_surface_c.h"
#ifdef SDL_SSE_INTRINSICS
/* *INDENT-OFF* */ // clang-format off
#if defined(_MSC_VER) && !defined(__clang__)
#define SSE_BEGIN \
__m128 c128; \
c128.m128_u32[0] = color; \
c128.m128_u32[1] = color; \
c128.m128_u32[2] = color; \
c128.m128_u32[3] = color;
#else
#define SSE_BEGIN \
__m128 c128; \
DECLARE_ALIGNED(Uint32, cccc[4], 16); \
cccc[0] = color; \
cccc[1] = color; \
cccc[2] = color; \
cccc[3] = color; \
c128 = *(__m128 *)cccc;
#endif
#define SSE_WORK \
for (i = n / 64; i--;) { \
_mm_stream_ps((float *)(p+0), c128); \
_mm_stream_ps((float *)(p+16), c128); \
_mm_stream_ps((float *)(p+32), c128); \
_mm_stream_ps((float *)(p+48), c128); \
p += 64; \
}
#define SSE_END
#define DEFINE_SSE_FILLRECT(bpp, type) \
static void SDL_TARGETING("sse") SDL_FillSurfaceRect##bpp##SSE(Uint8 *pixels, int pitch, Uint32 color, int w, int h) \
{ \
int i, n; \
Uint8 *p = NULL; \
\
/* If the number of bytes per row is equal to the pitch, treat */ \
/* all rows as one long continuous row (for better performance) */ \
if ((w) * (bpp) == pitch) { \
w = w * h; \
h = 1; \
} \
\
SSE_BEGIN; \
\
while (h--) { \
n = (w) * (bpp); \
p = pixels; \
\
if (n > 63) { \
int adjust = 16 - ((uintptr_t)p & 15); \
if (adjust < 16) { \
n -= adjust; \
adjust /= (bpp); \
while (adjust--) { \
*((type *)p) = (type)color; \
p += (bpp); \
} \
} \
SSE_WORK; \
} \
if (n & 63) { \
int remainder = (n & 63); \
remainder /= (bpp); \
while (remainder--) { \
*((type *)p) = (type)color; \
p += (bpp); \
} \
} \
pixels += pitch; \
} \
\
SSE_END; \
}
static void SDL_TARGETING("sse") SDL_FillSurfaceRect1SSE(Uint8 *pixels, int pitch, Uint32 color, int w, int h)
{
int i, n;
SSE_BEGIN;
while (h--) {
Uint8 *p = pixels;
n = w;
if (n > 63) {
int adjust = 16 - ((uintptr_t)p & 15);
if (adjust) {
n -= adjust;
SDL_memset(p, color, adjust);
p += adjust;
}
SSE_WORK;
}
if (n & 63) {
int remainder = (n & 63);
SDL_memset(p, color, remainder);
}
pixels += pitch;
}
SSE_END;
}
// DEFINE_SSE_FILLRECT(1, Uint8)
DEFINE_SSE_FILLRECT(2, Uint16)
DEFINE_SSE_FILLRECT(4, Uint32)
/* *INDENT-ON* */ // clang-format on
#endif // __SSE__
static void SDL_FillSurfaceRect1(Uint8 *pixels, int pitch, Uint32 color, int w, int h)
{
int n;
Uint8 *p = NULL;
while (h--) {
n = w;
p = pixels;
if (n > 3) {
switch ((uintptr_t)p & 3) {
case 1:
*p++ = (Uint8)color;
--n;
SDL_FALLTHROUGH;
case 2:
*p++ = (Uint8)color;
--n;
SDL_FALLTHROUGH;
case 3:
*p++ = (Uint8)color;
--n;
}
SDL_memset4(p, color, (n >> 2));
}
if (n & 3) {
p += (n & ~3);
switch (n & 3) {
case 3:
*p++ = (Uint8)color;
SDL_FALLTHROUGH;
case 2:
*p++ = (Uint8)color;
SDL_FALLTHROUGH;
case 1:
*p++ = (Uint8)color;
}
}
pixels += pitch;
}
}
static void SDL_FillSurfaceRect2(Uint8 *pixels, int pitch, Uint32 color, int w, int h)
{
int n;
Uint16 *p = NULL;
while (h--) {
n = w;
p = (Uint16 *)pixels;
if (n > 1) {
if ((uintptr_t)p & 2) {
*p++ = (Uint16)color;
--n;
}
SDL_memset4(p, color, (n >> 1));
}
if (n & 1) {
p[n - 1] = (Uint16)color;
}
pixels += pitch;
}
}
static void SDL_FillSurfaceRect3(Uint8 *pixels, int pitch, Uint32 color, int w, int h)
{
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
Uint8 b1 = (Uint8)(color & 0xFF);
Uint8 b2 = (Uint8)((color >> 8) & 0xFF);
Uint8 b3 = (Uint8)((color >> 16) & 0xFF);
#elif SDL_BYTEORDER == SDL_BIG_ENDIAN
Uint8 b1 = (Uint8)((color >> 16) & 0xFF);
Uint8 b2 = (Uint8)((color >> 8) & 0xFF);
Uint8 b3 = (Uint8)(color & 0xFF);
#endif
int n;
Uint8 *p = NULL;
while (h--) {
n = w;
p = pixels;
while (n--) {
*p++ = b1;
*p++ = b2;
*p++ = b3;
}
pixels += pitch;
}
}
static void SDL_FillSurfaceRect4(Uint8 *pixels, int pitch, Uint32 color, int w, int h)
{
while (h--) {
SDL_memset4(pixels, color, w);
pixels += pitch;
}
}
/*
* This function performs a fast fill of the given rectangle with 'color'
*/
bool SDL_FillSurfaceRect(SDL_Surface *dst, const SDL_Rect *rect, Uint32 color)
{
if (!SDL_SurfaceValid(dst)) {
return SDL_InvalidParamError("SDL_FillSurfaceRect(): dst");
}
// If 'rect' == NULL, then fill the whole surface
if (!rect) {
rect = &dst->clip_rect;
// Don't attempt to fill if the surface's clip_rect is empty
if (SDL_RectEmpty(rect)) {
return true;
}
}
return SDL_FillSurfaceRects(dst, rect, 1, color);
}
bool SDL_FillSurfaceRects(SDL_Surface *dst, const SDL_Rect *rects, int count, Uint32 color)
{
SDL_Rect clipped;
Uint8 *pixels;
const SDL_Rect *rect;
void (*fill_function)(Uint8 * pixels, int pitch, Uint32 color, int w, int h) = NULL;
int i;
if (!SDL_SurfaceValid(dst)) {
return SDL_InvalidParamError("SDL_FillSurfaceRects(): dst");
}
// Nothing to do
if (dst->w == 0 || dst->h == 0) {
return true;
}
// Perform software fill
if (!dst->pixels) {
return SDL_SetError("SDL_FillSurfaceRects(): You must lock the surface");
}
if (!rects) {
return SDL_InvalidParamError("SDL_FillSurfaceRects(): rects");
}
/* This function doesn't usually work on surfaces < 8 bpp
* Except: support for 4bits, when filling full size.
*/
if (SDL_BITSPERPIXEL(dst->format) < 8) {
if (count == 1) {
const SDL_Rect *r = &rects[0];
if (r->x == 0 && r->y == 0 && r->w == dst->w && r->h == dst->h) {
if (SDL_BITSPERPIXEL(dst->format) == 4) {
Uint8 b = (((Uint8)color << 4) | (Uint8)color);
SDL_memset(dst->pixels, b, (size_t)dst->h * dst->pitch);
return true;
}
}
}
return SDL_SetError("SDL_FillSurfaceRects(): Unsupported surface format");
}
if (fill_function == NULL) {
switch (SDL_BYTESPERPIXEL(dst->format)) {
case 1:
{
color |= (color << 8);
color |= (color << 16);
#ifdef SDL_SSE_INTRINSICS
if (SDL_HasSSE()) {
fill_function = SDL_FillSurfaceRect1SSE;
break;
}
#endif
fill_function = SDL_FillSurfaceRect1;
break;
}
case 2:
{
color |= (color << 16);
#ifdef SDL_SSE_INTRINSICS
if (SDL_HasSSE()) {
fill_function = SDL_FillSurfaceRect2SSE;
break;
}
#endif
fill_function = SDL_FillSurfaceRect2;
break;
}
case 3:
// 24-bit RGB is a slow path, at least for now.
{
fill_function = SDL_FillSurfaceRect3;
break;
}
case 4:
{
#ifdef SDL_SSE_INTRINSICS
if (SDL_HasSSE()) {
fill_function = SDL_FillSurfaceRect4SSE;
break;
}
#endif
fill_function = SDL_FillSurfaceRect4;
break;
}
default:
return SDL_SetError("Unsupported pixel format");
}
}
for (i = 0; i < count; ++i) {
rect = &rects[i];
// Perform clipping
if (!SDL_GetRectIntersection(rect, &dst->clip_rect, &clipped)) {
continue;
}
rect = &clipped;
pixels = (Uint8 *)dst->pixels + rect->y * dst->pitch +
rect->x * SDL_BYTESPERPIXEL(dst->format);
fill_function(pixels, dst->pitch, color, rect->w, rect->h);
}
// We're done!
return true;
}

1558
vendor/sdl-3.2.10/src/video/SDL_pixels.c vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,57 @@
/*
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_pixels_c_h_
#define SDL_pixels_c_h_
// Useful functions and variables from SDL_pixel.c
#include "SDL_blit.h"
// Pixel format functions
extern void SDL_Get8888AlphaMaskAndShift(const SDL_PixelFormatDetails *fmt, Uint32 *mask, Uint32 *shift);
extern SDL_Colorspace SDL_GetDefaultColorspaceForFormat(SDL_PixelFormat pixel_format);
extern void SDL_QuitPixelFormatDetails(void);
// Colorspace conversion functions
extern float SDL_sRGBtoLinear(float v);
extern float SDL_sRGBfromLinear(float v);
extern float SDL_PQtoNits(float v);
extern float SDL_PQfromNits(float v);
extern const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, int bits_per_pixel);
extern const float *SDL_GetColorPrimariesConversionMatrix(SDL_ColorPrimaries src, SDL_ColorPrimaries dst);
extern void SDL_ConvertColorPrimaries(float *fR, float *fG, float *fB, const float *matrix);
// Blit mapping functions
extern bool SDL_ValidateMap(SDL_Surface *src, SDL_Surface *dst);
extern void SDL_InvalidateMap(SDL_BlitMap *map);
extern bool SDL_MapSurface(SDL_Surface *src, SDL_Surface *dst);
// Miscellaneous functions
extern void SDL_DitherPalette(SDL_Palette *palette);
extern Uint8 SDL_FindColor(const SDL_Palette *pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
extern Uint8 SDL_LookupRGBAColor(SDL_HashTable *palette_map, Uint32 pixel, const SDL_Palette *pal);
extern void SDL_DetectPalette(const SDL_Palette *pal, bool *is_opaque, bool *has_alpha_channel);
extern SDL_Surface *SDL_DuplicatePixels(int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, void *pixels, int pitch);
#endif // SDL_pixels_c_h_

116
vendor/sdl-3.2.10/src/video/SDL_rect.c vendored Normal file
View file

@ -0,0 +1,116 @@
/*
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"
#include "SDL_rect_c.h"
/* There's no float version of this at the moment, because it's not a public API
and internally we only need the int version. */
bool SDL_GetSpanEnclosingRect(int width, int height,
int numrects, const SDL_Rect *rects, SDL_Rect *span)
{
int i;
int span_y1, span_y2;
int rect_y1, rect_y2;
if (width < 1) {
SDL_InvalidParamError("width");
return false;
} else if (height < 1) {
SDL_InvalidParamError("height");
return false;
} else if (!rects) {
SDL_InvalidParamError("rects");
return false;
} else if (!span) {
SDL_InvalidParamError("span");
return false;
} else if (numrects < 1) {
SDL_InvalidParamError("numrects");
return false;
}
// Initialize to empty rect
span_y1 = height;
span_y2 = 0;
for (i = 0; i < numrects; ++i) {
rect_y1 = rects[i].y;
rect_y2 = rect_y1 + rects[i].h;
// Clip out of bounds rectangles, and expand span rect
if (rect_y1 < 0) {
span_y1 = 0;
} else if (rect_y1 < span_y1) {
span_y1 = rect_y1;
}
if (rect_y2 > height) {
span_y2 = height;
} else if (rect_y2 > span_y2) {
span_y2 = rect_y2;
}
}
if (span_y2 > span_y1) {
span->x = 0;
span->y = span_y1;
span->w = width;
span->h = (span_y2 - span_y1);
return true;
}
return false;
}
// For use with the Cohen-Sutherland algorithm for line clipping, in SDL_rect_impl.h
#define CODE_BOTTOM 1
#define CODE_TOP 2
#define CODE_LEFT 4
#define CODE_RIGHT 8
// Same code twice, for float and int versions...
#define RECTTYPE SDL_Rect
#define POINTTYPE SDL_Point
#define SCALARTYPE int
#define BIGSCALARTYPE Sint64
#define COMPUTEOUTCODE ComputeOutCode
#define ENCLOSEPOINTS_EPSILON 1
#define SDL_RECT_CAN_OVERFLOW SDL_RectCanOverflow
#define SDL_HASINTERSECTION SDL_HasRectIntersection
#define SDL_INTERSECTRECT SDL_GetRectIntersection
#define SDL_RECTEMPTY SDL_RectEmpty
#define SDL_UNIONRECT SDL_GetRectUnion
#define SDL_ENCLOSEPOINTS SDL_GetRectEnclosingPoints
#define SDL_INTERSECTRECTANDLINE SDL_GetRectAndLineIntersection
#include "SDL_rect_impl.h"
#define RECTTYPE SDL_FRect
#define POINTTYPE SDL_FPoint
#define SCALARTYPE float
#define BIGSCALARTYPE double
#define COMPUTEOUTCODE ComputeOutCodeFloat
#define ENCLOSEPOINTS_EPSILON 0.0f
#define SDL_RECT_CAN_OVERFLOW SDL_RectCanOverflowFloat
#define SDL_HASINTERSECTION SDL_HasRectIntersectionFloat
#define SDL_INTERSECTRECT SDL_GetRectIntersectionFloat
#define SDL_RECTEMPTY SDL_RectEmptyFloat
#define SDL_UNIONRECT SDL_GetRectUnionFloat
#define SDL_ENCLOSEPOINTS SDL_GetRectEnclosingPointsFloat
#define SDL_INTERSECTRECTANDLINE SDL_GetRectAndLineIntersectionFloat
#include "SDL_rect_impl.h"

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.
*/
#ifndef SDL_rect_c_h_
#define SDL_rect_c_h_
#include "SDL_internal.h"
extern bool SDL_GetSpanEnclosingRect(int width, int height, int numrects, const SDL_Rect *rects, SDL_Rect *span);
#endif // SDL_rect_c_h_

View file

@ -0,0 +1,465 @@
/*
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.
*/
// This file is #included twice to support int and float versions with the same code.
static bool SDL_RECT_CAN_OVERFLOW(const RECTTYPE *rect)
{
if (rect->x <= (SCALARTYPE)(SDL_MIN_SINT32 / 2) ||
rect->x >= (SCALARTYPE)(SDL_MAX_SINT32 / 2) ||
rect->y <= (SCALARTYPE)(SDL_MIN_SINT32 / 2) ||
rect->y >= (SCALARTYPE)(SDL_MAX_SINT32 / 2) ||
rect->w >= (SCALARTYPE)(SDL_MAX_SINT32 / 2) ||
rect->h >= (SCALARTYPE)(SDL_MAX_SINT32 / 2)) {
return true;
}
return false;
}
bool SDL_HASINTERSECTION(const RECTTYPE *A, const RECTTYPE *B)
{
SCALARTYPE Amin, Amax, Bmin, Bmax;
if (!A) {
SDL_InvalidParamError("A");
return false;
} else if (!B) {
SDL_InvalidParamError("B");
return false;
} else if (SDL_RECT_CAN_OVERFLOW(A) ||
SDL_RECT_CAN_OVERFLOW(B)) {
SDL_SetError("Potential rect math overflow");
return false;
} else if (SDL_RECTEMPTY(A) || SDL_RECTEMPTY(B)) {
return false; // Special cases for empty rects
}
// Horizontal intersection
Amin = A->x;
Amax = Amin + A->w;
Bmin = B->x;
Bmax = Bmin + B->w;
if (Bmin > Amin) {
Amin = Bmin;
}
if (Bmax < Amax) {
Amax = Bmax;
}
if ((Amax - ENCLOSEPOINTS_EPSILON) < Amin) {
return false;
}
// Vertical intersection
Amin = A->y;
Amax = Amin + A->h;
Bmin = B->y;
Bmax = Bmin + B->h;
if (Bmin > Amin) {
Amin = Bmin;
}
if (Bmax < Amax) {
Amax = Bmax;
}
if ((Amax - ENCLOSEPOINTS_EPSILON) < Amin) {
return false;
}
return true;
}
bool SDL_INTERSECTRECT(const RECTTYPE *A, const RECTTYPE *B, RECTTYPE *result)
{
SCALARTYPE Amin, Amax, Bmin, Bmax;
if (!A) {
SDL_InvalidParamError("A");
return false;
} else if (!B) {
SDL_InvalidParamError("B");
return false;
} else if (SDL_RECT_CAN_OVERFLOW(A) ||
SDL_RECT_CAN_OVERFLOW(B)) {
SDL_SetError("Potential rect math overflow");
return false;
} else if (!result) {
SDL_InvalidParamError("result");
return false;
} else if (SDL_RECTEMPTY(A) || SDL_RECTEMPTY(B)) { // Special cases for empty rects
result->w = 0;
result->h = 0;
return false;
}
// Horizontal intersection
Amin = A->x;
Amax = Amin + A->w;
Bmin = B->x;
Bmax = Bmin + B->w;
if (Bmin > Amin) {
Amin = Bmin;
}
result->x = Amin;
if (Bmax < Amax) {
Amax = Bmax;
}
result->w = Amax - Amin;
// Vertical intersection
Amin = A->y;
Amax = Amin + A->h;
Bmin = B->y;
Bmax = Bmin + B->h;
if (Bmin > Amin) {
Amin = Bmin;
}
result->y = Amin;
if (Bmax < Amax) {
Amax = Bmax;
}
result->h = Amax - Amin;
return !SDL_RECTEMPTY(result);
}
bool SDL_UNIONRECT(const RECTTYPE *A, const RECTTYPE *B, RECTTYPE *result)
{
SCALARTYPE Amin, Amax, Bmin, Bmax;
if (!A) {
return SDL_InvalidParamError("A");
} else if (!B) {
return SDL_InvalidParamError("B");
} else if (SDL_RECT_CAN_OVERFLOW(A) ||
SDL_RECT_CAN_OVERFLOW(B)) {
return SDL_SetError("Potential rect math overflow");
} else if (!result) {
return SDL_InvalidParamError("result");
} else if (SDL_RECTEMPTY(A)) { // Special cases for empty Rects
if (SDL_RECTEMPTY(B)) { // A and B empty
SDL_zerop(result);
} else { // A empty, B not empty
*result = *B;
}
return true;
} else if (SDL_RECTEMPTY(B)) { // A not empty, B empty
*result = *A;
return true;
}
// Horizontal union
Amin = A->x;
Amax = Amin + A->w;
Bmin = B->x;
Bmax = Bmin + B->w;
if (Bmin < Amin) {
Amin = Bmin;
}
result->x = Amin;
if (Bmax > Amax) {
Amax = Bmax;
}
result->w = Amax - Amin;
// Vertical union
Amin = A->y;
Amax = Amin + A->h;
Bmin = B->y;
Bmax = Bmin + B->h;
if (Bmin < Amin) {
Amin = Bmin;
}
result->y = Amin;
if (Bmax > Amax) {
Amax = Bmax;
}
result->h = Amax - Amin;
return true;
}
bool SDL_ENCLOSEPOINTS(const POINTTYPE *points, int count, const RECTTYPE *clip, RECTTYPE *result)
{
SCALARTYPE minx = 0;
SCALARTYPE miny = 0;
SCALARTYPE maxx = 0;
SCALARTYPE maxy = 0;
SCALARTYPE x, y;
int i;
if (!points) {
SDL_InvalidParamError("points");
return false;
} else if (count < 1) {
SDL_InvalidParamError("count");
return false;
}
if (clip) {
bool added = false;
const SCALARTYPE clip_minx = clip->x;
const SCALARTYPE clip_miny = clip->y;
const SCALARTYPE clip_maxx = clip->x + clip->w - ENCLOSEPOINTS_EPSILON;
const SCALARTYPE clip_maxy = clip->y + clip->h - ENCLOSEPOINTS_EPSILON;
// Special case for empty rectangle
if (SDL_RECTEMPTY(clip)) {
return false;
}
for (i = 0; i < count; ++i) {
x = points[i].x;
y = points[i].y;
if (x < clip_minx || x > clip_maxx ||
y < clip_miny || y > clip_maxy) {
continue;
}
if (!added) {
// Special case: if no result was requested, we are done
if (!result) {
return true;
}
// First point added
minx = maxx = x;
miny = maxy = y;
added = true;
continue;
}
if (x < minx) {
minx = x;
} else if (x > maxx) {
maxx = x;
}
if (y < miny) {
miny = y;
} else if (y > maxy) {
maxy = y;
}
}
if (!added) {
return false;
}
} else {
// Special case: if no result was requested, we are done
if (!result) {
return true;
}
// No clipping, always add the first point
minx = maxx = points[0].x;
miny = maxy = points[0].y;
for (i = 1; i < count; ++i) {
x = points[i].x;
y = points[i].y;
if (x < minx) {
minx = x;
} else if (x > maxx) {
maxx = x;
}
if (y < miny) {
miny = y;
} else if (y > maxy) {
maxy = y;
}
}
}
if (result) {
result->x = minx;
result->y = miny;
result->w = (maxx - minx) + ENCLOSEPOINTS_EPSILON;
result->h = (maxy - miny) + ENCLOSEPOINTS_EPSILON;
}
return true;
}
// Use the Cohen-Sutherland algorithm for line clipping
static int COMPUTEOUTCODE(const RECTTYPE *rect, SCALARTYPE x, SCALARTYPE y)
{
int code = 0;
if (y < rect->y) {
code |= CODE_TOP;
} else if (y > (rect->y + rect->h - ENCLOSEPOINTS_EPSILON)) {
code |= CODE_BOTTOM;
}
if (x < rect->x) {
code |= CODE_LEFT;
} else if (x > (rect->x + rect->w - ENCLOSEPOINTS_EPSILON)) {
code |= CODE_RIGHT;
}
return code;
}
bool SDL_INTERSECTRECTANDLINE(const RECTTYPE *rect, SCALARTYPE *X1, SCALARTYPE *Y1, SCALARTYPE *X2, SCALARTYPE *Y2)
{
SCALARTYPE x = 0;
SCALARTYPE y = 0;
SCALARTYPE x1, y1;
SCALARTYPE x2, y2;
SCALARTYPE rectx1;
SCALARTYPE recty1;
SCALARTYPE rectx2;
SCALARTYPE recty2;
int outcode1, outcode2;
if (!rect) {
SDL_InvalidParamError("rect");
return false;
} else if (SDL_RECT_CAN_OVERFLOW(rect)) {
SDL_SetError("Potential rect math overflow");
return false;
} else if (!X1) {
SDL_InvalidParamError("X1");
return false;
} else if (!Y1) {
SDL_InvalidParamError("Y1");
return false;
} else if (!X2) {
SDL_InvalidParamError("X2");
return false;
} else if (!Y2) {
SDL_InvalidParamError("Y2");
return false;
} else if (SDL_RECTEMPTY(rect)) {
return false; // Special case for empty rect
}
x1 = *X1;
y1 = *Y1;
x2 = *X2;
y2 = *Y2;
rectx1 = rect->x;
recty1 = rect->y;
rectx2 = rect->x + rect->w - ENCLOSEPOINTS_EPSILON;
recty2 = rect->y + rect->h - ENCLOSEPOINTS_EPSILON;
// Check to see if entire line is inside rect
if (x1 >= rectx1 && x1 <= rectx2 && x2 >= rectx1 && x2 <= rectx2 &&
y1 >= recty1 && y1 <= recty2 && y2 >= recty1 && y2 <= recty2) {
return true;
}
// Check to see if entire line is to one side of rect
if ((x1 < rectx1 && x2 < rectx1) || (x1 > rectx2 && x2 > rectx2) ||
(y1 < recty1 && y2 < recty1) || (y1 > recty2 && y2 > recty2)) {
return false;
}
if (y1 == y2) { // Horizontal line, easy to clip
if (x1 < rectx1) {
*X1 = rectx1;
} else if (x1 > rectx2) {
*X1 = rectx2;
}
if (x2 < rectx1) {
*X2 = rectx1;
} else if (x2 > rectx2) {
*X2 = rectx2;
}
return true;
}
if (x1 == x2) { // Vertical line, easy to clip
if (y1 < recty1) {
*Y1 = recty1;
} else if (y1 > recty2) {
*Y1 = recty2;
}
if (y2 < recty1) {
*Y2 = recty1;
} else if (y2 > recty2) {
*Y2 = recty2;
}
return true;
}
// More complicated Cohen-Sutherland algorithm
outcode1 = COMPUTEOUTCODE(rect, x1, y1);
outcode2 = COMPUTEOUTCODE(rect, x2, y2);
while (outcode1 || outcode2) {
if (outcode1 & outcode2) {
return false;
}
if (outcode1) {
if (outcode1 & CODE_TOP) {
y = recty1;
x = (SCALARTYPE) (x1 + ((BIGSCALARTYPE)(x2 - x1) * (y - y1)) / (y2 - y1));
} else if (outcode1 & CODE_BOTTOM) {
y = recty2;
x = (SCALARTYPE) (x1 + ((BIGSCALARTYPE)(x2 - x1) * (y - y1)) / (y2 - y1));
} else if (outcode1 & CODE_LEFT) {
x = rectx1;
y = (SCALARTYPE) (y1 + ((BIGSCALARTYPE)(y2 - y1) * (x - x1)) / (x2 - x1));
} else if (outcode1 & CODE_RIGHT) {
x = rectx2;
y = (SCALARTYPE) (y1 + ((BIGSCALARTYPE)(y2 - y1) * (x - x1)) / (x2 - x1));
}
x1 = x;
y1 = y;
outcode1 = COMPUTEOUTCODE(rect, x, y);
} else {
if (outcode2 & CODE_TOP) {
SDL_assert(y2 != y1); // if equal: division by zero.
y = recty1;
x = (SCALARTYPE) (x1 + ((BIGSCALARTYPE)(x2 - x1) * (y - y1)) / (y2 - y1));
} else if (outcode2 & CODE_BOTTOM) {
SDL_assert(y2 != y1); // if equal: division by zero.
y = recty2;
x = (SCALARTYPE) (x1 + ((BIGSCALARTYPE)(x2 - x1) * (y - y1)) / (y2 - y1));
} else if (outcode2 & CODE_LEFT) {
/* If this assertion ever fires, here's the static analysis that warned about it:
http://buildbot.libsdl.org/sdl-static-analysis/sdl-macosx-static-analysis/sdl-macosx-static-analysis-1101/report-b0d01a.html#EndPath */
SDL_assert(x2 != x1); // if equal: division by zero.
x = rectx1;
y = (SCALARTYPE) (y1 + ((BIGSCALARTYPE)(y2 - y1) * (x - x1)) / (x2 - x1));
} else if (outcode2 & CODE_RIGHT) {
/* If this assertion ever fires, here's the static analysis that warned about it:
http://buildbot.libsdl.org/sdl-static-analysis/sdl-macosx-static-analysis/sdl-macosx-static-analysis-1101/report-39b114.html#EndPath */
SDL_assert(x2 != x1); // if equal: division by zero.
x = rectx2;
y = (SCALARTYPE) (y1 + ((BIGSCALARTYPE)(y2 - y1) * (x - x1)) / (x2 - x1));
}
x2 = x;
y2 = y;
outcode2 = COMPUTEOUTCODE(rect, x, y);
}
}
*X1 = x1;
*Y1 = y1;
*X2 = x2;
*Y2 = y2;
return true;
}
#undef RECTTYPE
#undef POINTTYPE
#undef SCALARTYPE
#undef BIGSCALARTYPE
#undef COMPUTEOUTCODE
#undef ENCLOSEPOINTS_EPSILON
#undef SDL_RECT_CAN_OVERFLOW
#undef SDL_HASINTERSECTION
#undef SDL_INTERSECTRECT
#undef SDL_RECTEMPTY
#undef SDL_UNIONRECT
#undef SDL_ENCLOSEPOINTS
#undef SDL_INTERSECTRECTANDLINE

121
vendor/sdl-3.2.10/src/video/SDL_stb.c vendored Normal file
View file

@ -0,0 +1,121 @@
/*
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"
#include "SDL_stb_c.h"
// We currently only support JPEG, but we could add other image formats if we wanted
#ifdef SDL_HAVE_STB
#define malloc SDL_malloc
#define realloc SDL_realloc
#define free SDL_free
#undef memcpy
#define memcpy SDL_memcpy
#undef memset
#define memset SDL_memset
#undef strcmp
#define strcmp SDL_strcmp
#undef strncmp
#define strncmp SDL_strncmp
#define strtol SDL_strtol
#define abs SDL_abs
#define pow SDL_pow
#define ldexp SDL_scalbn
#define STB_IMAGE_STATIC
#define STBI_NO_THREAD_LOCALS
#define STBI_FAILURE_USERMSG
#if defined(SDL_NEON_INTRINSICS)
#define STBI_NEON
#endif
#define STBI_ONLY_JPEG
#define STBI_NO_GIF
#define STBI_NO_PNG
#define STBI_NO_HDR
#define STBI_NO_LINEAR
#define STBI_NO_ZLIB
#define STBI_NO_STDIO
#define STBI_ASSERT SDL_assert
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#undef memset
#endif
#ifdef SDL_HAVE_STB
static bool SDL_ConvertPixels_MJPG_to_NV12(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
{
int w = 0, h = 0, format = 0;
stbi__context s;
stbi__start_mem(&s, src, src_pitch);
stbi__result_info ri;
SDL_zero(ri);
ri.bits_per_channel = 8;
ri.channel_order = STBI_ORDER_RGB;
ri.num_channels = 0;
stbi__nv12 nv12;
nv12.w = width;
nv12.h = height;
nv12.pitch = dst_pitch;
nv12.y = (stbi_uc *)dst;
nv12.uv = nv12.y + (nv12.h * nv12.pitch);
void *pixels = stbi__jpeg_load(&s, &w, &h, &format, 4, &nv12, &ri);
if (!pixels) {
return false;
}
return true;
}
#endif // SDL_HAVE_STB
bool SDL_ConvertPixels_STB(int width, int height,
SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
{
#ifdef SDL_HAVE_STB
if (src_format == SDL_PIXELFORMAT_MJPG && dst_format == SDL_PIXELFORMAT_NV12) {
return SDL_ConvertPixels_MJPG_to_NV12(width, height, src, src_pitch, dst, dst_pitch);
}
bool result;
int w = 0, h = 0, format = 0;
int len = (src_format == SDL_PIXELFORMAT_MJPG) ? src_pitch : (height * src_pitch);
void *pixels = stbi_load_from_memory(src, len, &w, &h, &format, 4);
if (!pixels) {
return false;
}
if (w == width && h == height) {
result = SDL_ConvertPixelsAndColorspace(w, h, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, pixels, width * 4, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
} else {
result = SDL_SetError("Expected image size %dx%d, actual size %dx%d", width, height, w, h);
}
stbi_image_free(pixels);
return result;
#else
return SDL_SetError("SDL not built with STB image support");
#endif
}

31
vendor/sdl-3.2.10/src/video/SDL_stb_c.h vendored Normal file
View file

@ -0,0 +1,31 @@
/*
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_stb_c_h_
#define SDL_stb_c_h_
#include "SDL_internal.h"
// Image conversion functions
extern bool SDL_ConvertPixels_STB(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
#endif // SDL_stb_c_h_

View file

@ -0,0 +1,978 @@
/*
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"
#include "SDL_surface_c.h"
static bool SDL_StretchSurfaceUncheckedNearest(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect);
static bool SDL_StretchSurfaceUncheckedLinear(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect);
bool SDL_StretchSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)
{
bool result;
int src_locked;
int dst_locked;
SDL_Rect full_src;
SDL_Rect full_dst;
if (!src) {
return SDL_InvalidParamError("src");
}
if (!dst) {
return SDL_InvalidParamError("dst");
}
if (src->format != dst->format) {
// Slow!
SDL_Surface *src_tmp = SDL_ConvertSurfaceAndColorspace(src, dst->format, dst->palette, dst->colorspace, dst->props);
if (!src_tmp) {
return false;
}
result = SDL_StretchSurface(src_tmp, srcrect, dst, dstrect, scaleMode);
SDL_DestroySurface(src_tmp);
return result;
}
if (SDL_ISPIXELFORMAT_FOURCC(src->format)) {
// Slow!
if (!dstrect) {
full_dst.x = 0;
full_dst.y = 0;
full_dst.w = dst->w;
full_dst.h = dst->h;
dstrect = &full_dst;
}
SDL_Surface *src_tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_XRGB8888);
SDL_Surface *dst_tmp = SDL_CreateSurface(dstrect->w, dstrect->h, SDL_PIXELFORMAT_XRGB8888);
if (src_tmp && dst_tmp) {
result = SDL_StretchSurface(src_tmp, srcrect, dst_tmp, NULL, scaleMode);
if (result) {
result = SDL_ConvertPixelsAndColorspace(dstrect->w, dstrect->h,
dst_tmp->format, SDL_COLORSPACE_SRGB, 0,
dst_tmp->pixels, dst_tmp->pitch,
dst->format, dst->colorspace, SDL_GetSurfaceProperties(dst),
(Uint8 *)dst->pixels + dstrect->y * dst->pitch + dstrect->x * SDL_BYTESPERPIXEL(dst->format), dst->pitch);
}
} else {
result = false;
}
SDL_DestroySurface(src_tmp);
SDL_DestroySurface(dst_tmp);
return result;
}
if (scaleMode != SDL_SCALEMODE_NEAREST && scaleMode != SDL_SCALEMODE_LINEAR) {
return SDL_InvalidParamError("scaleMode");
}
if (scaleMode != SDL_SCALEMODE_NEAREST) {
scaleMode = SDL_SCALEMODE_LINEAR;
}
if (scaleMode == SDL_SCALEMODE_LINEAR) {
if (SDL_BYTESPERPIXEL(src->format) != 4 || src->format == SDL_PIXELFORMAT_ARGB2101010) {
return SDL_SetError("Wrong format");
}
}
// Verify the blit rectangles
if (srcrect) {
if ((srcrect->x < 0) || (srcrect->y < 0) ||
((srcrect->x + srcrect->w) > src->w) ||
((srcrect->y + srcrect->h) > src->h)) {
return SDL_SetError("Invalid source blit rectangle");
}
} else {
full_src.x = 0;
full_src.y = 0;
full_src.w = src->w;
full_src.h = src->h;
srcrect = &full_src;
}
if (dstrect) {
if ((dstrect->x < 0) || (dstrect->y < 0) ||
((dstrect->x + dstrect->w) > dst->w) ||
((dstrect->y + dstrect->h) > dst->h)) {
return SDL_SetError("Invalid destination blit rectangle");
}
} else {
full_dst.x = 0;
full_dst.y = 0;
full_dst.w = dst->w;
full_dst.h = dst->h;
dstrect = &full_dst;
}
if (dstrect->w <= 0 || dstrect->h <= 0) {
return true;
}
if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 ||
dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) {
return SDL_SetError("Size too large for scaling");
}
// Lock the destination if it's in hardware
dst_locked = 0;
if (SDL_MUSTLOCK(dst)) {
if (!SDL_LockSurface(dst)) {
return SDL_SetError("Unable to lock destination surface");
}
dst_locked = 1;
}
// Lock the source if it's in hardware
src_locked = 0;
if (SDL_MUSTLOCK(src)) {
if (!SDL_LockSurface(src)) {
if (dst_locked) {
SDL_UnlockSurface(dst);
}
return SDL_SetError("Unable to lock source surface");
}
src_locked = 1;
}
if (scaleMode == SDL_SCALEMODE_NEAREST) {
result = SDL_StretchSurfaceUncheckedNearest(src, srcrect, dst, dstrect);
} else {
result = SDL_StretchSurfaceUncheckedLinear(src, srcrect, dst, dstrect);
}
// We need to unlock the surfaces if they're locked
if (dst_locked) {
SDL_UnlockSurface(dst);
}
if (src_locked) {
SDL_UnlockSurface(src);
}
return result;
}
/* bilinear interpolation precision must be < 8
Because with SSE: add-multiply: _mm_madd_epi16 works with signed int
so pixels 0xb1...... are negatives and false the result
same in NEON probably */
#define PRECISION 7
#define FIXED_POINT(i) ((Uint32)(i) << 16)
#define SRC_INDEX(fp) ((Uint32)(fp) >> 16)
#define INTEGER(fp) ((Uint32)(fp) >> PRECISION)
#define FRAC(fp) ((Uint32)((fp) >> (16 - PRECISION)) & ((1 << PRECISION) - 1))
#define FRAC_ZERO 0
#define FRAC_ONE (1 << PRECISION)
#define FP_ONE FIXED_POINT(1)
#define BILINEAR___START \
int i; \
Sint64 fp_sum_h; \
int fp_step_h, left_pad_h, right_pad_h; \
Sint64 fp_sum_w; \
int fp_step_w, left_pad_w, right_pad_w; \
Sint64 fp_sum_w_init; \
int left_pad_w_init, right_pad_w_init, dst_gap, middle_init; \
get_scaler_datas(src_h, dst_h, &fp_sum_h, &fp_step_h, &left_pad_h, &right_pad_h); \
get_scaler_datas(src_w, dst_w, &fp_sum_w, &fp_step_w, &left_pad_w, &right_pad_w); \
fp_sum_w_init = fp_sum_w + left_pad_w * fp_step_w; \
left_pad_w_init = left_pad_w; \
right_pad_w_init = right_pad_w; \
dst_gap = dst_pitch - 4 * dst_w; \
middle_init = dst_w - left_pad_w - right_pad_w;
#define BILINEAR___HEIGHT \
int index_h, frac_h0, frac_h1, middle; \
const Uint32 *src_h0, *src_h1; \
int no_padding; \
Uint64 incr_h0, incr_h1; \
\
no_padding = !(i < left_pad_h || i > dst_h - 1 - right_pad_h); \
index_h = SRC_INDEX(fp_sum_h); \
frac_h0 = FRAC(fp_sum_h); \
\
index_h = no_padding ? index_h : (i < left_pad_h ? 0 : src_h - 1); \
frac_h0 = no_padding ? frac_h0 : 0; \
incr_h1 = no_padding ? src_pitch : 0; \
incr_h0 = (Uint64)index_h * src_pitch; \
\
src_h0 = (const Uint32 *)((const Uint8 *)src + incr_h0); \
src_h1 = (const Uint32 *)((const Uint8 *)src_h0 + incr_h1); \
\
fp_sum_h += fp_step_h; \
\
frac_h1 = FRAC_ONE - frac_h0; \
fp_sum_w = fp_sum_w_init; \
right_pad_w = right_pad_w_init; \
left_pad_w = left_pad_w_init; \
middle = middle_init;
#ifdef __clang__
// Remove inlining of this function
// Compiler crash with clang 9.0.8 / android-ndk-r21d
// Compiler crash with clang 11.0.3 / Xcode
// OK with clang 11.0.5 / android-ndk-22
// OK with clang 12.0.0 / Xcode
__attribute__((noinline))
#endif
static void get_scaler_datas(int src_nb, int dst_nb, Sint64 *fp_start, int *fp_step, int *left_pad, int *right_pad)
{
int step = FIXED_POINT(src_nb) / (dst_nb); // source step in fixed point
int x0 = FP_ONE / 2; // dst first pixel center at 0.5 in fixed point
Sint64 fp_sum;
int i;
#if 0
// scale to source coordinates
x0 *= src_nb;
x0 /= dst_nb; // x0 == step / 2
#else
// Use this code for perfect match with pixman
Sint64 tmp[2];
tmp[0] = (Sint64)step * (x0 >> 16);
tmp[1] = (Sint64)step * (x0 & 0xFFFF);
x0 = (int)(tmp[0] + ((tmp[1] + 0x8000) >> 16)); // x0 == (step + 1) / 2
#endif
// -= 0.5, get back the pixel origin, in source coordinates
x0 -= FP_ONE / 2;
*fp_start = x0;
*fp_step = step;
*left_pad = 0;
*right_pad = 0;
fp_sum = x0;
for (i = 0; i < dst_nb; i++) {
if (fp_sum < 0) {
*left_pad += 1;
} else {
int index = SRC_INDEX(fp_sum);
if (index > src_nb - 2) {
*right_pad += 1;
}
}
fp_sum += step;
}
// SDL_Log("%d -> %d x0=%d step=%d left_pad=%d right_pad=%d", src_nb, dst_nb, *fp_start, *fp_step, *left_pad, *right_pad);
}
typedef struct color_t
{
Uint8 a;
Uint8 b;
Uint8 c;
Uint8 d;
} color_t;
#if 0
static void printf_64(const char *str, void *var)
{
uint8_t *val = (uint8_t*) var;
printf(" * %s: %02x %02x %02x %02x _ %02x %02x %02x %02x\n",
str, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
}
#endif
/* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
static SDL_INLINE void INTERPOL(const Uint32 *src_x0, const Uint32 *src_x1, int frac0, int frac1, Uint32 *dst)
{
const color_t *c0 = (const color_t *)src_x0;
const color_t *c1 = (const color_t *)src_x1;
color_t *cx = (color_t *)dst;
#if 0
cx->a = c0->a + INTEGER(frac0 * (c1->a - c0->a));
cx->b = c0->b + INTEGER(frac0 * (c1->b - c0->b));
cx->c = c0->c + INTEGER(frac0 * (c1->c - c0->c));
cx->d = c0->d + INTEGER(frac0 * (c1->d - c0->d));
#else
cx->a = (Uint8)INTEGER(frac1 * c0->a + frac0 * c1->a);
cx->b = (Uint8)INTEGER(frac1 * c0->b + frac0 * c1->b);
cx->c = (Uint8)INTEGER(frac1 * c0->c + frac0 * c1->c);
cx->d = (Uint8)INTEGER(frac1 * c0->d + frac0 * c1->d);
#endif
}
static SDL_INLINE void INTERPOL_BILINEAR(const Uint32 *s0, const Uint32 *s1, int frac_w0, int frac_h0, int frac_h1, Uint32 *dst)
{
Uint32 tmp[2];
unsigned int frac_w1 = FRAC_ONE - frac_w0;
// Vertical first, store to 'tmp'
INTERPOL(s0, s1, frac_h0, frac_h1, tmp);
INTERPOL(s0 + 1, s1 + 1, frac_h0, frac_h1, tmp + 1);
// Horizontal, store to 'dst'
INTERPOL(tmp, tmp + 1, frac_w0, frac_w1, dst);
}
static bool scale_mat(const Uint32 *src, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
BILINEAR___START
for (i = 0; i < dst_h; i++) {
BILINEAR___HEIGHT
while (left_pad_w--) {
INTERPOL_BILINEAR(src_h0, src_h1, FRAC_ZERO, frac_h0, frac_h1, dst);
dst += 1;
}
while (middle--) {
const Uint32 *s_00_01;
const Uint32 *s_10_11;
int index_w = 4 * SRC_INDEX(fp_sum_w);
int frac_w = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
/*
x00 ... x0_ ..... x01
. . .
. x .
. . .
. . .
x10 ... x1_ ..... x11
*/
s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
INTERPOL_BILINEAR(s_00_01, s_10_11, frac_w, frac_h0, frac_h1, dst);
dst += 1;
}
while (right_pad_w--) {
int index_w = 4 * (src_w - 2);
const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
INTERPOL_BILINEAR(s_00_01, s_10_11, FRAC_ONE, frac_h0, frac_h1, dst);
dst += 1;
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
#ifdef SDL_NEON_INTRINSICS
#define CAST_uint8x8_t (uint8x8_t)
#define CAST_uint32x2_t (uint32x2_t)
#endif
#if defined(_MSC_VER)
#ifdef SDL_NEON_INTRINSICS
#undef CAST_uint8x8_t
#undef CAST_uint32x2_t
#define CAST_uint8x8_t
#define CAST_uint32x2_t
#endif
#endif
#ifdef SDL_SSE2_INTRINSICS
#if 0
static void SDL_TARGETING("sse2") printf_128(const char *str, __m128i var)
{
uint16_t *val = (uint16_t*) &var;
printf(" * %s: %04x %04x %04x %04x _ %04x %04x %04x %04x\n",
str, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
}
#endif
static SDL_INLINE int hasSSE2(void)
{
static int val = -1;
if (val != -1) {
return val;
}
val = SDL_HasSSE2();
return val;
}
static SDL_INLINE void SDL_TARGETING("sse2") INTERPOL_BILINEAR_SSE(const Uint32 *s0, const Uint32 *s1, int frac_w, __m128i v_frac_h0, __m128i v_frac_h1, Uint32 *dst, __m128i zero)
{
__m128i x_00_01, x_10_11; /* Pixels in 4*uint8 in row */
__m128i v_frac_w0, k0, l0, d0, e0;
int f, f2;
f = frac_w;
f2 = FRAC_ONE - frac_w;
v_frac_w0 = _mm_set_epi16((short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2);
x_00_01 = _mm_loadl_epi64((const __m128i *)s0); // Load x00 and x01
x_10_11 = _mm_loadl_epi64((const __m128i *)s1);
/* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
// Interpolation vertical
k0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_00_01, zero), v_frac_h1);
l0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_10_11, zero), v_frac_h0);
k0 = _mm_add_epi16(k0, l0);
// For perfect match, clear the factionnal part eventually.
/*
k0 = _mm_srli_epi16(k0, PRECISION);
k0 = _mm_slli_epi16(k0, PRECISION);
*/
// Interpolation horizontal
l0 = _mm_unpacklo_epi64(/* unused */ l0, k0);
k0 = _mm_madd_epi16(_mm_unpackhi_epi16(l0, k0), v_frac_w0);
// Store 1 pixel
d0 = _mm_srli_epi32(k0, PRECISION * 2);
e0 = _mm_packs_epi32(d0, d0);
e0 = _mm_packus_epi16(e0, e0);
*dst = _mm_cvtsi128_si32(e0);
}
static bool SDL_TARGETING("sse2") scale_mat_SSE(const Uint32 *src, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
BILINEAR___START
for (i = 0; i < dst_h; i++) {
int nb_block2;
__m128i v_frac_h0;
__m128i v_frac_h1;
__m128i zero;
BILINEAR___HEIGHT
nb_block2 = middle / 2;
v_frac_h0 = _mm_set_epi16((short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0);
v_frac_h1 = _mm_set_epi16((short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1);
zero = _mm_setzero_si128();
while (left_pad_w--) {
INTERPOL_BILINEAR_SSE(src_h0, src_h1, FRAC_ZERO, v_frac_h0, v_frac_h1, dst, zero);
dst += 1;
}
while (nb_block2--) {
int index_w_0, frac_w_0;
int index_w_1, frac_w_1;
const Uint32 *s_00_01, *s_02_03, *s_10_11, *s_12_13;
__m128i x_00_01, x_10_11, x_02_03, x_12_13; /* Pixels in 4*uint8 in row */
__m128i v_frac_w0, k0, l0, d0, e0;
__m128i v_frac_w1, k1, l1, d1, e1;
int f, f2;
index_w_0 = 4 * SRC_INDEX(fp_sum_w);
frac_w_0 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
index_w_1 = 4 * SRC_INDEX(fp_sum_w);
frac_w_1 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
/*
x00............ x01 x02...........x03
. . . . . .
j0 f0 j1 j2 f1 j3
. . . . . .
. . . . . .
. . . . . .
x10............ x11 x12...........x13
*/
s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_0);
s_02_03 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_1);
s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_0);
s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1);
f = frac_w_0;
f2 = FRAC_ONE - frac_w_0;
v_frac_w0 = _mm_set_epi16((short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2);
f = frac_w_1;
f2 = FRAC_ONE - frac_w_1;
v_frac_w1 = _mm_set_epi16((short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2);
x_00_01 = _mm_loadl_epi64((const __m128i *)s_00_01); // Load x00 and x01
x_02_03 = _mm_loadl_epi64((const __m128i *)s_02_03);
x_10_11 = _mm_loadl_epi64((const __m128i *)s_10_11);
x_12_13 = _mm_loadl_epi64((const __m128i *)s_12_13);
// Interpolation vertical
k0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_00_01, zero), v_frac_h1);
l0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_10_11, zero), v_frac_h0);
k0 = _mm_add_epi16(k0, l0);
k1 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_02_03, zero), v_frac_h1);
l1 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_12_13, zero), v_frac_h0);
k1 = _mm_add_epi16(k1, l1);
// Interpolation horizontal
l0 = _mm_unpacklo_epi64(/* unused */ l0, k0);
k0 = _mm_madd_epi16(_mm_unpackhi_epi16(l0, k0), v_frac_w0);
l1 = _mm_unpacklo_epi64(/* unused */ l1, k1);
k1 = _mm_madd_epi16(_mm_unpackhi_epi16(l1, k1), v_frac_w1);
// Store 1 pixel
d0 = _mm_srli_epi32(k0, PRECISION * 2);
e0 = _mm_packs_epi32(d0, d0);
e0 = _mm_packus_epi16(e0, e0);
*dst++ = _mm_cvtsi128_si32(e0);
// Store 1 pixel
d1 = _mm_srli_epi32(k1, PRECISION * 2);
e1 = _mm_packs_epi32(d1, d1);
e1 = _mm_packus_epi16(e1, e1);
*dst++ = _mm_cvtsi128_si32(e1);
}
// Last point
if (middle & 0x1) {
const Uint32 *s_00_01;
const Uint32 *s_10_11;
int index_w = 4 * SRC_INDEX(fp_sum_w);
int frac_w = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
INTERPOL_BILINEAR_SSE(s_00_01, s_10_11, frac_w, v_frac_h0, v_frac_h1, dst, zero);
dst += 1;
}
while (right_pad_w--) {
int index_w = 4 * (src_w - 2);
const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
INTERPOL_BILINEAR_SSE(s_00_01, s_10_11, FRAC_ONE, v_frac_h0, v_frac_h1, dst, zero);
dst += 1;
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
#endif
#ifdef SDL_NEON_INTRINSICS
static SDL_INLINE int hasNEON(void)
{
static int val = -1;
if (val != -1) {
return val;
}
val = SDL_HasNEON();
return val;
}
static SDL_INLINE void INTERPOL_BILINEAR_NEON(const Uint32 *s0, const Uint32 *s1, int frac_w, uint8x8_t v_frac_h0, uint8x8_t v_frac_h1, Uint32 *dst)
{
uint8x8_t x_00_01, x_10_11; /* Pixels in 4*uint8 in row */
uint16x8_t k0;
uint32x4_t l0;
uint16x8_t d0;
uint8x8_t e0;
x_00_01 = CAST_uint8x8_t vld1_u32(s0); // Load 2 pixels
x_10_11 = CAST_uint8x8_t vld1_u32(s1);
/* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */
k0 = vmlal_u8(k0, x_10_11, v_frac_h0); /* k0 += x1 * frac */
// k0 now contains 2 interpolated pixels { j0, j1 }
l0 = vshll_n_u16(vget_low_u16(k0), PRECISION);
l0 = vmlsl_n_u16(l0, vget_low_u16(k0), frac_w);
l0 = vmlal_n_u16(l0, vget_high_u16(k0), frac_w);
// Shift and narrow
d0 = vcombine_u16(
/* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION),
/* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION));
// Narrow again
e0 = vmovn_u16(d0);
// Store 1 pixel
*dst = vget_lane_u32(CAST_uint32x2_t e0, 0);
}
static bool scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
BILINEAR___START
for (i = 0; i < dst_h; i++) {
int nb_block4;
uint8x8_t v_frac_h0, v_frac_h1;
BILINEAR___HEIGHT
nb_block4 = middle / 4;
v_frac_h0 = vmov_n_u8(frac_h0);
v_frac_h1 = vmov_n_u8(frac_h1);
while (left_pad_w--) {
INTERPOL_BILINEAR_NEON(src_h0, src_h1, FRAC_ZERO, v_frac_h0, v_frac_h1, dst);
dst += 1;
}
while (nb_block4--) {
int index_w_0, frac_w_0;
int index_w_1, frac_w_1;
int index_w_2, frac_w_2;
int index_w_3, frac_w_3;
const Uint32 *s_00_01, *s_02_03, *s_04_05, *s_06_07;
const Uint32 *s_10_11, *s_12_13, *s_14_15, *s_16_17;
uint8x8_t x_00_01, x_10_11, x_02_03, x_12_13; /* Pixels in 4*uint8 in row */
uint8x8_t x_04_05, x_14_15, x_06_07, x_16_17;
uint16x8_t k0, k1, k2, k3;
uint32x4_t l0, l1, l2, l3;
uint16x8_t d0, d1;
uint8x8_t e0, e1;
uint32x4_t f0;
index_w_0 = 4 * SRC_INDEX(fp_sum_w);
frac_w_0 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
index_w_1 = 4 * SRC_INDEX(fp_sum_w);
frac_w_1 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
index_w_2 = 4 * SRC_INDEX(fp_sum_w);
frac_w_2 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
index_w_3 = 4 * SRC_INDEX(fp_sum_w);
frac_w_3 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_0);
s_02_03 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_1);
s_04_05 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_2);
s_06_07 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_3);
s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_0);
s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1);
s_14_15 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_2);
s_16_17 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_3);
// Interpolation vertical
x_00_01 = CAST_uint8x8_t vld1_u32(s_00_01); // Load 2 pixels
x_02_03 = CAST_uint8x8_t vld1_u32(s_02_03);
x_04_05 = CAST_uint8x8_t vld1_u32(s_04_05);
x_06_07 = CAST_uint8x8_t vld1_u32(s_06_07);
x_10_11 = CAST_uint8x8_t vld1_u32(s_10_11);
x_12_13 = CAST_uint8x8_t vld1_u32(s_12_13);
x_14_15 = CAST_uint8x8_t vld1_u32(s_14_15);
x_16_17 = CAST_uint8x8_t vld1_u32(s_16_17);
/* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */
k0 = vmlal_u8(k0, x_10_11, v_frac_h0); /* k0 += x1 * frac */
k1 = vmull_u8(x_02_03, v_frac_h1);
k1 = vmlal_u8(k1, x_12_13, v_frac_h0);
k2 = vmull_u8(x_04_05, v_frac_h1);
k2 = vmlal_u8(k2, x_14_15, v_frac_h0);
k3 = vmull_u8(x_06_07, v_frac_h1);
k3 = vmlal_u8(k3, x_16_17, v_frac_h0);
// k0 now contains 2 interpolated pixels { j0, j1 }
// k1 now contains 2 interpolated pixels { j2, j3 }
// k2 now contains 2 interpolated pixels { j4, j5 }
// k3 now contains 2 interpolated pixels { j6, j7 }
l0 = vshll_n_u16(vget_low_u16(k0), PRECISION);
l0 = vmlsl_n_u16(l0, vget_low_u16(k0), frac_w_0);
l0 = vmlal_n_u16(l0, vget_high_u16(k0), frac_w_0);
l1 = vshll_n_u16(vget_low_u16(k1), PRECISION);
l1 = vmlsl_n_u16(l1, vget_low_u16(k1), frac_w_1);
l1 = vmlal_n_u16(l1, vget_high_u16(k1), frac_w_1);
l2 = vshll_n_u16(vget_low_u16(k2), PRECISION);
l2 = vmlsl_n_u16(l2, vget_low_u16(k2), frac_w_2);
l2 = vmlal_n_u16(l2, vget_high_u16(k2), frac_w_2);
l3 = vshll_n_u16(vget_low_u16(k3), PRECISION);
l3 = vmlsl_n_u16(l3, vget_low_u16(k3), frac_w_3);
l3 = vmlal_n_u16(l3, vget_high_u16(k3), frac_w_3);
// shift and narrow
d0 = vcombine_u16(
/* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION),
/* uint16x4_t */ vshrn_n_u32(l1, 2 * PRECISION));
// narrow again
e0 = vmovn_u16(d0);
// Shift and narrow
d1 = vcombine_u16(
/* uint16x4_t */ vshrn_n_u32(l2, 2 * PRECISION),
/* uint16x4_t */ vshrn_n_u32(l3, 2 * PRECISION));
// Narrow again
e1 = vmovn_u16(d1);
f0 = vcombine_u32(CAST_uint32x2_t e0, CAST_uint32x2_t e1);
// Store 4 pixels
vst1q_u32(dst, f0);
dst += 4;
}
if (middle & 0x2) {
int index_w_0, frac_w_0;
int index_w_1, frac_w_1;
const Uint32 *s_00_01, *s_02_03;
const Uint32 *s_10_11, *s_12_13;
uint8x8_t x_00_01, x_10_11, x_02_03, x_12_13; /* Pixels in 4*uint8 in row */
uint16x8_t k0, k1;
uint32x4_t l0, l1;
uint16x8_t d0;
uint8x8_t e0;
index_w_0 = 4 * SRC_INDEX(fp_sum_w);
frac_w_0 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
index_w_1 = 4 * SRC_INDEX(fp_sum_w);
frac_w_1 = FRAC(fp_sum_w);
fp_sum_w += fp_step_w;
/*
x00............ x01 x02...........x03
. . . . . .
j0 dest0 j1 j2 dest1 j3
. . . . . .
. . . . . .
. . . . . .
x10............ x11 x12...........x13
*/
s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_0);
s_02_03 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_1);
s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_0);
s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1);
// Interpolation vertical
x_00_01 = CAST_uint8x8_t vld1_u32(s_00_01); // Load 2 pixels
x_02_03 = CAST_uint8x8_t vld1_u32(s_02_03);
x_10_11 = CAST_uint8x8_t vld1_u32(s_10_11);
x_12_13 = CAST_uint8x8_t vld1_u32(s_12_13);
/* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */
k0 = vmlal_u8(k0, x_10_11, v_frac_h0); /* k0 += x1 * frac */
k1 = vmull_u8(x_02_03, v_frac_h1);
k1 = vmlal_u8(k1, x_12_13, v_frac_h0);
// k0 now contains 2 interpolated pixels { j0, j1 }
// k1 now contains 2 interpolated pixels { j2, j3 }
l0 = vshll_n_u16(vget_low_u16(k0), PRECISION);
l0 = vmlsl_n_u16(l0, vget_low_u16(k0), frac_w_0);
l0 = vmlal_n_u16(l0, vget_high_u16(k0), frac_w_0);
l1 = vshll_n_u16(vget_low_u16(k1), PRECISION);
l1 = vmlsl_n_u16(l1, vget_low_u16(k1), frac_w_1);
l1 = vmlal_n_u16(l1, vget_high_u16(k1), frac_w_1);
// Shift and narrow
d0 = vcombine_u16(
/* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION),
/* uint16x4_t */ vshrn_n_u32(l1, 2 * PRECISION));
// Narrow again
e0 = vmovn_u16(d0);
// Store 2 pixels
vst1_u32(dst, CAST_uint32x2_t e0);
dst += 2;
}
// Last point
if (middle & 0x1) {
int index_w = 4 * SRC_INDEX(fp_sum_w);
int frac_w = FRAC(fp_sum_w);
const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
INTERPOL_BILINEAR_NEON(s_00_01, s_10_11, frac_w, v_frac_h0, v_frac_h1, dst);
dst += 1;
}
while (right_pad_w--) {
int index_w = 4 * (src_w - 2);
const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
INTERPOL_BILINEAR_NEON(s_00_01, s_10_11, FRAC_ONE, v_frac_h0, v_frac_h1, dst);
dst += 1;
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
#endif
bool SDL_StretchSurfaceUncheckedLinear(SDL_Surface *s, const SDL_Rect *srcrect, SDL_Surface *d, const SDL_Rect *dstrect)
{
bool result = false;
int src_w = srcrect->w;
int src_h = srcrect->h;
int dst_w = dstrect->w;
int dst_h = dstrect->h;
int src_pitch = s->pitch;
int dst_pitch = d->pitch;
Uint32 *src = (Uint32 *)((Uint8 *)s->pixels + srcrect->x * 4 + srcrect->y * src_pitch);
Uint32 *dst = (Uint32 *)((Uint8 *)d->pixels + dstrect->x * 4 + dstrect->y * dst_pitch);
#ifdef SDL_NEON_INTRINSICS
if (!result && hasNEON()) {
result = scale_mat_NEON(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
}
#endif
#ifdef SDL_SSE2_INTRINSICS
if (!result && hasSSE2()) {
result = scale_mat_SSE(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
}
#endif
if (!result) {
result = scale_mat(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
}
return result;
}
#define SDL_SCALE_NEAREST__START \
int i; \
Uint64 posy, incy; \
Uint64 posx, incx; \
Uint64 srcy, srcx; \
int dst_gap, n; \
const Uint32 *src_h0; \
incy = ((Uint64)src_h << 16) / dst_h; \
incx = ((Uint64)src_w << 16) / dst_w; \
dst_gap = dst_pitch - bpp * dst_w; \
posy = incy / 2;
#define SDL_SCALE_NEAREST__HEIGHT \
srcy = (posy >> 16); \
src_h0 = (const Uint32 *)((const Uint8 *)src_ptr + srcy * src_pitch); \
posy += incy; \
posx = incx / 2; \
n = dst_w;
static bool scale_mat_nearest_1(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
Uint32 bpp = 1;
SDL_SCALE_NEAREST__START
for (i = 0; i < dst_h; i++) {
SDL_SCALE_NEAREST__HEIGHT
while (n--) {
const Uint8 *src;
srcx = bpp * (posx >> 16);
posx += incx;
src = (const Uint8 *)src_h0 + srcx;
*(Uint8 *)dst = *src;
dst = (Uint32 *)((Uint8 *)dst + bpp);
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
static bool scale_mat_nearest_2(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
Uint32 bpp = 2;
SDL_SCALE_NEAREST__START
for (i = 0; i < dst_h; i++) {
SDL_SCALE_NEAREST__HEIGHT
while (n--) {
const Uint16 *src;
srcx = bpp * (posx >> 16);
posx += incx;
src = (const Uint16 *)((const Uint8 *)src_h0 + srcx);
*(Uint16 *)dst = *src;
dst = (Uint32 *)((Uint8 *)dst + bpp);
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
static bool scale_mat_nearest_3(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
Uint32 bpp = 3;
SDL_SCALE_NEAREST__START
for (i = 0; i < dst_h; i++) {
SDL_SCALE_NEAREST__HEIGHT
while (n--) {
const Uint8 *src;
srcx = bpp * (posx >> 16);
posx += incx;
src = (const Uint8 *)src_h0 + srcx;
((Uint8 *)dst)[0] = src[0];
((Uint8 *)dst)[1] = src[1];
((Uint8 *)dst)[2] = src[2];
dst = (Uint32 *)((Uint8 *)dst + bpp);
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
static bool scale_mat_nearest_4(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
{
Uint32 bpp = 4;
SDL_SCALE_NEAREST__START
for (i = 0; i < dst_h; i++) {
SDL_SCALE_NEAREST__HEIGHT
while (n--) {
const Uint32 *src;
srcx = bpp * (posx >> 16);
posx += incx;
src = (const Uint32 *)((const Uint8 *)src_h0 + srcx);
*dst = *src;
dst = (Uint32 *)((Uint8 *)dst + bpp);
}
dst = (Uint32 *)((Uint8 *)dst + dst_gap);
}
return true;
}
bool SDL_StretchSurfaceUncheckedNearest(SDL_Surface *s, const SDL_Rect *srcrect, SDL_Surface *d, const SDL_Rect *dstrect)
{
int src_w = srcrect->w;
int src_h = srcrect->h;
int dst_w = dstrect->w;
int dst_h = dstrect->h;
int src_pitch = s->pitch;
int dst_pitch = d->pitch;
int bpp = SDL_BYTESPERPIXEL(d->format);
Uint32 *src = (Uint32 *)((Uint8 *)s->pixels + srcrect->x * bpp + srcrect->y * src_pitch);
Uint32 *dst = (Uint32 *)((Uint8 *)d->pixels + dstrect->x * bpp + dstrect->y * dst_pitch);
if (bpp == 4) {
return scale_mat_nearest_4(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
} else if (bpp == 3) {
return scale_mat_nearest_3(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
} else if (bpp == 2) {
return scale_mat_nearest_2(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
} else {
return scale_mat_nearest_1(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
}
}

3021
vendor/sdl-3.2.10/src/video/SDL_surface.c vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,93 @@
/*
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_surface_c_h_
#define SDL_surface_c_h_
// Useful functions and variables from SDL_surface.c
#include "SDL_blit.h"
// Surface internal flags
typedef Uint32 SDL_SurfaceDataFlags;
#define SDL_INTERNAL_SURFACE_DONTFREE 0x00000001u /**< Surface is referenced internally */
#define SDL_INTERNAL_SURFACE_STACK 0x00000002u /**< Surface is allocated on the stack */
#define SDL_INTERNAL_SURFACE_RLEACCEL 0x00000004u /**< Surface is RLE encoded */
// Surface internal data definition
struct SDL_Surface
{
// Public API definition
SDL_SurfaceFlags flags; /**< The flags of the surface, read-only */
SDL_PixelFormat format; /**< The format of the surface, read-only */
int w; /**< The width of the surface, read-only. */
int h; /**< The height of the surface, read-only. */
int pitch; /**< The distance in bytes between rows of pixels, read-only */
void *pixels; /**< A pointer to the pixels of the surface, the pixels are writeable if non-NULL */
int refcount; /**< Application reference count, used when freeing surface */
void *reserved; /**< Reserved for internal use */
// Private API definition
/** flags for this surface */
SDL_SurfaceDataFlags internal_flags;
/** properties for this surface */
SDL_PropertiesID props;
/** detailed format for this surface */
const SDL_PixelFormatDetails *fmt;
/** Pixel colorspace */
SDL_Colorspace colorspace;
/** palette for indexed surfaces */
SDL_Palette *palette;
/** Alternate representation of images */
int num_images;
SDL_Surface **images;
/** information needed for surfaces requiring locks */
int locked;
/** clipping information */
SDL_Rect clip_rect;
/** info for fast blit mapping to other surfaces */
SDL_BlitMap map;
};
// Surface functions
extern bool SDL_SurfaceValid(SDL_Surface *surface);
extern void SDL_UpdateSurfaceLockFlag(SDL_Surface *surface);
extern bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, size_t *size, size_t *pitch, bool minimalPitch);
extern float SDL_GetDefaultSDRWhitePoint(SDL_Colorspace colorspace);
extern float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace);
extern float SDL_GetDefaultHDRHeadroom(SDL_Colorspace colorspace);
extern float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace);
extern SDL_Surface *SDL_GetSurfaceImage(SDL_Surface *surface, float display_scale);
#endif // SDL_surface_c_h_

View file

@ -0,0 +1,602 @@
/*
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_sysvideo_h_
#define SDL_sysvideo_h_
#include <SDL3/SDL_vulkan.h>
#include "SDL_surface_c.h"
// The SDL video driver
typedef struct SDL_VideoDisplay SDL_VideoDisplay;
typedef struct SDL_VideoDevice SDL_VideoDevice;
typedef struct SDL_VideoData SDL_VideoData;
typedef struct SDL_DisplayData SDL_DisplayData;
typedef struct SDL_WindowData SDL_WindowData;
typedef struct
{
float SDR_white_level;
float HDR_headroom;
} SDL_HDROutputProperties;
// Define the SDL window structure, corresponding to toplevel windows
struct SDL_Window
{
SDL_WindowID id;
char *title;
SDL_Surface *icon;
int x, y;
int w, h;
int min_w, min_h;
int max_w, max_h;
float min_aspect;
float max_aspect;
int last_pixel_w, last_pixel_h;
SDL_WindowFlags flags;
SDL_WindowFlags pending_flags;
float display_scale;
bool external_graphics_context;
bool fullscreen_exclusive; // The window is currently fullscreen exclusive
SDL_DisplayID last_fullscreen_exclusive_display; // The last fullscreen_exclusive display
SDL_DisplayID last_displayID;
/* Stored position and size for the window in the non-fullscreen state,
* including when the window is maximized or tiled.
*
* This is the size and position to which the window should return when
* leaving the fullscreen state.
*/
SDL_Rect windowed;
/* Stored position and size for the window in the base 'floating' state;
* when not fullscreen, nor in a state such as maximized or tiled.
*
* This is the size and position to which the window should return when
* it's maximized and SDL_RestoreWindow() is called.
*/
SDL_Rect floating;
// The last client requested size and position for the window.
SDL_Rect pending;
/* Toggle for drivers to indicate that the current window state is tiled,
* and sizes set non-programmatically shouldn't be cached.
*/
bool tiled;
// Whether or not the initial position was defined
bool undefined_x;
bool undefined_y;
SDL_DisplayMode requested_fullscreen_mode;
SDL_DisplayMode current_fullscreen_mode;
SDL_HDROutputProperties HDR;
float opacity;
SDL_Surface *surface;
bool surface_valid;
bool is_hiding;
bool restore_on_show; // Child was hidden recursively by the parent, restore when shown.
bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning.
bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied.
bool is_destroying;
bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete().
int safe_inset_left;
int safe_inset_right;
int safe_inset_top;
int safe_inset_bottom;
SDL_Rect safe_rect;
SDL_PropertiesID text_input_props;
bool text_input_active;
SDL_Rect text_input_rect;
int text_input_cursor;
SDL_Rect mouse_rect;
SDL_HitTest hit_test;
void *hit_test_data;
SDL_PropertiesID props;
int num_renderers;
SDL_Renderer **renderers;
SDL_WindowData *internal;
SDL_Window *prev;
SDL_Window *next;
SDL_Window *parent;
SDL_Window *first_child;
SDL_Window *prev_sibling;
SDL_Window *next_sibling;
};
#define SDL_WINDOW_FULLSCREEN_VISIBLE(W) \
((((W)->flags & SDL_WINDOW_FULLSCREEN) != 0) && \
(((W)->flags & SDL_WINDOW_HIDDEN) == 0) && \
(((W)->flags & SDL_WINDOW_MINIMIZED) == 0))
#define SDL_WINDOW_IS_POPUP(W) \
(((W)->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0)
/*
* Define the SDL display structure.
* This corresponds to physical monitors attached to the system.
*/
struct SDL_VideoDisplay
{
SDL_DisplayID id;
char *name;
int max_fullscreen_modes;
int num_fullscreen_modes;
SDL_DisplayMode *fullscreen_modes;
SDL_DisplayMode desktop_mode;
const SDL_DisplayMode *current_mode;
SDL_DisplayOrientation natural_orientation;
SDL_DisplayOrientation current_orientation;
float content_scale;
SDL_HDROutputProperties HDR;
// This is true if we are fullscreen or fullscreen is pending
bool fullscreen_active;
SDL_Window *fullscreen_window;
SDL_VideoDevice *device;
SDL_PropertiesID props;
SDL_DisplayData *internal;
};
// Video device flags
typedef enum
{
VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED = 0x01,
VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT = 0x02,
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS = 0x04,
VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY = 0x08,
VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES = 0x10,
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS = 0x20,
VIDEO_DEVICE_CAPS_SENDS_HDR_CHANGES = 0x40
} DeviceCaps;
// Fullscreen operations
typedef enum
{
SDL_FULLSCREEN_OP_LEAVE = 0,
SDL_FULLSCREEN_OP_ENTER,
SDL_FULLSCREEN_OP_UPDATE
} SDL_FullscreenOp;
typedef enum
{
SDL_FULLSCREEN_FAILED,
SDL_FULLSCREEN_SUCCEEDED,
SDL_FULLSCREEN_PENDING
} SDL_FullscreenResult;
struct SDL_VideoDevice
{
/* * * */
// The name of this video driver
const char *name;
/* * * */
// Initialization/Query functions
/*
* Initialize the native video subsystem, filling in the list of
* displays for this driver, returning 0 or -1 if there's an error.
*/
bool (*VideoInit)(SDL_VideoDevice *_this);
/*
* Reverse the effects VideoInit() -- called if VideoInit() fails or
* if the application is shutting down the video subsystem.
*/
void (*VideoQuit)(SDL_VideoDevice *_this);
/*
* Reinitialize the touch devices -- called if an unknown touch ID occurs.
*/
void (*ResetTouch)(SDL_VideoDevice *_this);
/* * * */
/*
* Display functions
*/
/*
* Refresh the display list
*/
void (*RefreshDisplays)(SDL_VideoDevice *_this);
/*
* Get the bounds of a display
*/
bool (*GetDisplayBounds)(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
/*
* Get the usable bounds of a display (bounds minus menubar or whatever)
*/
bool (*GetDisplayUsableBounds)(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
/*
* Get a list of the available display modes for a display.
*/
bool (*GetDisplayModes)(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
/*
* Setting the display mode is independent of creating windows, so
* when the display mode is changed, all existing windows should have
* their data updated accordingly, including the display surfaces
* associated with them.
*/
bool (*SetDisplayMode)(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
/* * * */
/*
* Window functions
*/
bool (*CreateSDLWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
void (*SetWindowTitle)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*SetWindowIcon)(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
bool (*SetWindowPosition)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowSize)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowMinimumSize)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowMaximumSize)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowAspectRatio)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*GetWindowBordersSize)(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right);
float (*GetWindowContentScale)(SDL_VideoDevice *_this, SDL_Window *window);
void (*GetWindowSizeInPixels)(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
bool (*SetWindowOpacity)(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
bool (*SetWindowParent)(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
bool (*SetWindowModal)(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
void (*ShowWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*HideWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*RaiseWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*MaximizeWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*MinimizeWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*RestoreWindow)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowBordered)(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
void (*SetWindowResizable)(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
void (*SetWindowAlwaysOnTop)(SDL_VideoDevice *_this, SDL_Window *window, bool on_top);
SDL_FullscreenResult (*SetWindowFullscreen)(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
void *(*GetWindowICCProfile)(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
SDL_DisplayID (*GetDisplayForWindow)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*SetWindowMouseRect)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*SetWindowMouseGrab)(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
bool (*SetWindowKeyboardGrab)(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
void (*DestroyWindow)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*CreateWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch);
bool (*SetWindowFramebufferVSync)(SDL_VideoDevice *_this, SDL_Window *window, int vsync);
bool (*GetWindowFramebufferVSync)(SDL_VideoDevice *_this, SDL_Window *window, int *vsync);
bool (*UpdateWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects);
void (*DestroyWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window);
void (*OnWindowEnter)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*UpdateWindowShape)(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
bool (*FlashWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
bool (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
bool (*SyncWindow)(SDL_VideoDevice *_this, SDL_Window *window);
/* * * */
/*
* OpenGL support
*/
bool (*GL_LoadLibrary)(SDL_VideoDevice *_this, const char *path);
SDL_FunctionPointer (*GL_GetProcAddress)(SDL_VideoDevice *_this, const char *proc);
void (*GL_UnloadLibrary)(SDL_VideoDevice *_this);
SDL_GLContext (*GL_CreateContext)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*GL_MakeCurrent)(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
SDL_EGLSurface (*GL_GetEGLSurface)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*GL_SetSwapInterval)(SDL_VideoDevice *_this, int interval);
bool (*GL_GetSwapInterval)(SDL_VideoDevice *_this, int *interval);
bool (*GL_SwapWindow)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*GL_DestroyContext)(SDL_VideoDevice *_this, SDL_GLContext context);
void (*GL_DefaultProfileConfig)(SDL_VideoDevice *_this, int *mask, int *major, int *minor);
/* * * */
/*
* Vulkan support
*/
bool (*Vulkan_LoadLibrary)(SDL_VideoDevice *_this, const char *path);
void (*Vulkan_UnloadLibrary)(SDL_VideoDevice *_this);
char const* const* (*Vulkan_GetInstanceExtensions)(SDL_VideoDevice *_this, Uint32 *count);
bool (*Vulkan_CreateSurface)(SDL_VideoDevice *_this, SDL_Window *window, VkInstance instance, const struct VkAllocationCallbacks *allocator, VkSurfaceKHR *surface);
void (*Vulkan_DestroySurface)(SDL_VideoDevice *_this, VkInstance instance, VkSurfaceKHR surface, const struct VkAllocationCallbacks *allocator);
bool (*Vulkan_GetPresentationSupport)(SDL_VideoDevice *_this, VkInstance instance, VkPhysicalDevice physicalDevice, Uint32 queueFamilyIndex);
/* * * */
/*
* Metal support
*/
SDL_MetalView (*Metal_CreateView)(SDL_VideoDevice *_this, SDL_Window *window);
void (*Metal_DestroyView)(SDL_VideoDevice *_this, SDL_MetalView view);
void *(*Metal_GetLayer)(SDL_VideoDevice *_this, SDL_MetalView view);
/* * * */
/*
* Event manager functions
*/
int (*WaitEventTimeout)(SDL_VideoDevice *_this, Sint64 timeoutNS);
void (*SendWakeupEvent)(SDL_VideoDevice *_this, SDL_Window *window);
void (*PumpEvents)(SDL_VideoDevice *_this);
// Suspend/resume the screensaver
bool (*SuspendScreenSaver)(SDL_VideoDevice *_this);
// Text input
bool (*StartTextInput)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
bool (*StopTextInput)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*UpdateTextInputArea)(SDL_VideoDevice *_this, SDL_Window *window);
bool (*ClearComposition)(SDL_VideoDevice *_this, SDL_Window *window);
// Screen keyboard
bool (*HasScreenKeyboardSupport)(SDL_VideoDevice *_this);
void (*ShowScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
void (*HideScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetTextInputProperties)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
bool (*IsScreenKeyboardShown)(SDL_VideoDevice *_this, SDL_Window *window);
// Clipboard
const char **(*GetTextMimeTypes)(SDL_VideoDevice *_this, size_t *num_mime_types);
bool (*SetClipboardData)(SDL_VideoDevice *_this);
void *(*GetClipboardData)(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
bool (*HasClipboardData)(SDL_VideoDevice *_this, const char *mime_type);
/* If you implement *ClipboardData, you don't need to implement *ClipboardText */
bool (*SetClipboardText)(SDL_VideoDevice *_this, const char *text);
char *(*GetClipboardText)(SDL_VideoDevice *_this);
bool (*HasClipboardText)(SDL_VideoDevice *_this);
// These functions are only needed if the platform has a separate primary selection buffer
bool (*SetPrimarySelectionText)(SDL_VideoDevice *_this, const char *text);
char *(*GetPrimarySelectionText)(SDL_VideoDevice *_this);
bool (*HasPrimarySelectionText)(SDL_VideoDevice *_this);
// MessageBox
bool (*ShowMessageBox)(SDL_VideoDevice *_this, const SDL_MessageBoxData *messageboxdata, int *buttonID);
// Hit-testing
bool (*SetWindowHitTest)(SDL_Window *window, bool enabled);
// Tell window that app enabled drag'n'drop events
void (*AcceptDragAndDrop)(SDL_Window *window, bool accept);
// Display the system-level window menu
void (*ShowWindowSystemMenu)(SDL_Window *window, int x, int y);
/* * * */
// Data common to all drivers
SDL_ThreadID thread;
bool checked_texture_framebuffer;
bool is_dummy;
bool suspend_screensaver;
SDL_Window *wakeup_window;
SDL_Mutex *wakeup_lock; // Initialized only if WaitEventTimeout/SendWakeupEvent are supported
int num_displays;
SDL_VideoDisplay **displays;
SDL_Rect desktop_bounds;
SDL_Window *windows;
SDL_Window *grabbed_window;
Uint32 clipboard_sequence;
SDL_ClipboardDataCallback clipboard_callback;
SDL_ClipboardCleanupCallback clipboard_cleanup;
void *clipboard_userdata;
char **clipboard_mime_types;
size_t num_clipboard_mime_types;
char *primary_selection_text;
bool setting_display_mode;
Uint32 device_caps;
SDL_SystemTheme system_theme;
/* * * */
// Data used by the GL drivers
struct
{
int red_size;
int green_size;
int blue_size;
int alpha_size;
int depth_size;
int buffer_size;
int stencil_size;
int double_buffer;
int accum_red_size;
int accum_green_size;
int accum_blue_size;
int accum_alpha_size;
int stereo;
int multisamplebuffers;
int multisamplesamples;
int floatbuffers;
int accelerated;
int major_version;
int minor_version;
int flags;
int profile_mask;
int share_with_current_context;
int release_behavior;
int reset_notification;
int framebuffer_srgb_capable;
int no_error;
int retained_backing;
int egl_platform;
int driver_loaded;
char driver_path[256];
SDL_SharedObject *dll_handle;
} gl_config;
SDL_EGLAttribArrayCallback egl_platformattrib_callback;
SDL_EGLIntArrayCallback egl_surfaceattrib_callback;
SDL_EGLIntArrayCallback egl_contextattrib_callback;
void *egl_attrib_callback_userdata;
/* * * */
// Cache current GL context; don't call the OS when it hasn't changed.
/* We have the global pointers here so Cocoa continues to work the way
it always has, and the thread-local storage for the general case.
*/
SDL_Window *current_glwin;
SDL_GLContext current_glctx;
SDL_TLSID current_glwin_tls;
SDL_TLSID current_glctx_tls;
/* Flag that stores whether it's allowed to call SDL_GL_MakeCurrent()
* with a NULL window, but a non-NULL context. (Not allowed in most cases,
* except on EGL under some circumstances.) */
bool gl_allow_no_surface;
/* * * */
// Data used by the Vulkan drivers
struct
{
SDL_FunctionPointer vkGetInstanceProcAddr;
SDL_FunctionPointer vkEnumerateInstanceExtensionProperties;
int loader_loaded;
char loader_path[256];
SDL_SharedObject *loader_handle;
} vulkan_config;
/* * * */
// Data private to this driver
SDL_VideoData *internal;
struct SDL_GLDriverData *gl_data;
#ifdef SDL_VIDEO_OPENGL_EGL
struct SDL_EGL_VideoData *egl_data;
#endif
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
struct SDL_PrivateGLESData *gles_data;
#endif
/* * * */
// The function used to dispose of this structure
void (*free)(SDL_VideoDevice *_this);
};
typedef struct VideoBootStrap
{
const char *name;
const char *desc;
SDL_VideoDevice *(*create)(void);
bool (*ShowMessageBox)(const SDL_MessageBoxData *messageboxdata, int *buttonID); // can be done without initializing backend!
bool is_preferred;
} VideoBootStrap;
// Not all of these are available in a given build. Use #ifdefs, etc.
extern VideoBootStrap PRIVATE_bootstrap;
extern VideoBootStrap COCOA_bootstrap;
extern VideoBootStrap X11_bootstrap;
extern VideoBootStrap WINDOWS_bootstrap;
extern VideoBootStrap HAIKU_bootstrap;
extern VideoBootStrap UIKIT_bootstrap;
extern VideoBootStrap Android_bootstrap;
extern VideoBootStrap PS2_bootstrap;
extern VideoBootStrap PSP_bootstrap;
extern VideoBootStrap VITA_bootstrap;
extern VideoBootStrap RISCOS_bootstrap;
extern VideoBootStrap N3DS_bootstrap;
extern VideoBootStrap RPI_bootstrap;
extern VideoBootStrap KMSDRM_bootstrap;
extern VideoBootStrap DUMMY_bootstrap;
extern VideoBootStrap DUMMY_evdev_bootstrap;
extern VideoBootStrap Wayland_preferred_bootstrap;
extern VideoBootStrap Wayland_bootstrap;
extern VideoBootStrap VIVANTE_bootstrap;
extern VideoBootStrap Emscripten_bootstrap;
extern VideoBootStrap OFFSCREEN_bootstrap;
extern VideoBootStrap QNX_bootstrap;
extern VideoBootStrap OPENVR_bootstrap;
extern bool SDL_UninitializedVideo(void);
// Use SDL_OnVideoThread() sparingly, to avoid regressions in use cases that currently happen to work
extern bool SDL_OnVideoThread(void);
extern SDL_VideoDevice *SDL_GetVideoDevice(void);
extern void SDL_SetSystemTheme(SDL_SystemTheme theme);
extern SDL_DisplayID SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode);
extern SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, bool send_event);
extern void SDL_DelVideoDisplay(SDL_DisplayID display, bool send_event);
extern bool SDL_AddFullscreenDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_ResetFullscreenDisplayModes(SDL_VideoDisplay *display);
extern void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_SetCurrentDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode);
extern void SDL_SetDisplayContentScale(SDL_VideoDisplay *display, float scale);
extern void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDROutputProperties *HDR);
extern bool SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern SDL_VideoDisplay *SDL_GetVideoDisplay(SDL_DisplayID display);
extern SDL_DisplayID SDL_GetDisplayForWindowPosition(SDL_Window *window);
extern SDL_VideoDisplay *SDL_GetVideoDisplayForWindow(SDL_Window *window);
extern SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window);
extern int SDL_GetDisplayIndex(SDL_DisplayID displayID);
extern SDL_DisplayData *SDL_GetDisplayDriverData(SDL_DisplayID display);
extern SDL_DisplayData *SDL_GetDisplayDriverDataForWindow(SDL_Window *window);
extern int SDL_GetMessageBoxCount(void);
extern void SDL_SetWindowHDRProperties(SDL_Window *window, const SDL_HDROutputProperties *HDR, bool send_event);
extern void SDL_SetWindowSafeAreaInsets(SDL_Window *window, int left, int right, int top, int bottom);
extern void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor);
extern bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags);
extern bool SDL_HasWindows(void);
extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y);
extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y);
extern void SDL_OnDisplayAdded(SDL_VideoDisplay *display);
extern void SDL_OnDisplayMoved(SDL_VideoDisplay *display);
extern void SDL_OnWindowShown(SDL_Window *window);
extern void SDL_OnWindowHidden(SDL_Window *window);
extern void SDL_OnWindowMoved(SDL_Window *window);
extern void SDL_OnWindowResized(SDL_Window *window);
extern void SDL_CheckWindowPixelSizeChanged(SDL_Window *window);
extern void SDL_OnWindowPixelSizeChanged(SDL_Window *window);
extern void SDL_OnWindowLiveResizeUpdate(SDL_Window *window);
extern void SDL_OnWindowMinimized(SDL_Window *window);
extern void SDL_OnWindowMaximized(SDL_Window *window);
extern void SDL_OnWindowRestored(SDL_Window *window);
extern void SDL_OnWindowEnter(SDL_Window *window);
extern void SDL_OnWindowLeave(SDL_Window *window);
extern void SDL_OnWindowFocusGained(SDL_Window *window);
extern void SDL_OnWindowFocusLost(SDL_Window *window);
extern void SDL_OnWindowDisplayChanged(SDL_Window *window);
extern void SDL_UpdateWindowGrab(SDL_Window *window);
extern bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, bool commit);
extern SDL_Window *SDL_GetToplevelForKeyboardFocus(void);
extern bool SDL_ShouldAllowTopmost(void);
extern void SDL_ToggleDragAndDropSupport(void);
extern void SDL_UpdateDesktopBounds(void);
extern SDL_TextInputType SDL_GetTextInputType(SDL_PropertiesID props);
extern SDL_Capitalization SDL_GetTextInputCapitalization(SDL_PropertiesID props);
extern bool SDL_GetTextInputAutocorrect(SDL_PropertiesID props);
extern bool SDL_GetTextInputMultiline(SDL_PropertiesID props);
#endif // SDL_sysvideo_h_

6135
vendor/sdl-3.2.10/src/video/SDL_video.c vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,69 @@
/*
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_video_c_h_
#define SDL_video_c_h_
#include "SDL_internal.h"
struct SDL_VideoDevice;
/**
* Initialize the video subsystem, optionally specifying a video driver.
*
* This function initializes the video subsystem, setting up a connection to
* the window manager, etc, and determines the available display modes and
* pixel formats, but does not initialize a window or graphics mode.
*
* If you use this function and you haven't used the SDL_INIT_VIDEO flag with
* either SDL_Init() or SDL_InitSubSystem(), you should call SDL_VideoQuit()
* before calling SDL_Quit().
*
* It is safe to call this function multiple times. SDL_VideoInit() will call
* SDL_VideoQuit() itself if the video subsystem has already been initialized.
*
* You can use SDL_GetNumVideoDrivers() and SDL_GetVideoDriver() to find a
* specific `driver_name`.
*
* \param driver_name the name of a video driver to initialize, or NULL for
* the default driver
* \returns true on success or false on failure; call
* SDL_GetError() for more information.
*/
extern bool SDL_VideoInit(const char *driver_name);
/**
* Shut down the video subsystem, if initialized with SDL_VideoInit().
*
* This function closes all windows, and restores the original video mode.
*/
extern void SDL_VideoQuit(void);
extern bool SDL_SetWindowTextureVSync(struct SDL_VideoDevice *_this, SDL_Window *window, int vsync);
#if defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_EMSCRIPTEN)
const char *SDL_GetCSSCursorName(SDL_SystemCursor id, const char **fallback_name);
#endif
extern bool SDL_AddWindowRenderer(SDL_Window *window, SDL_Renderer *renderer);
extern void SDL_RemoveWindowRenderer(SDL_Window *window, SDL_Renderer *renderer);
#endif // SDL_video_c_h_

View file

@ -0,0 +1,115 @@
/*
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_VIDEO_DRIVER_WINDOWS
#if defined(SDL_PLATFORM_WINDOWS)
bool SDL_RegisterApp(const char *name, Uint32 style, void *hInst)
{
(void)name;
(void)style;
(void)hInst;
return true;
}
void SDL_UnregisterApp(void)
{
}
void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata)
{
}
#endif // defined(SDL_PLATFORM_WINDOWS)
SDL_DECLSPEC bool SDLCALL SDL_GetDXGIOutputInfo(SDL_DisplayID displayID, int *adapterIndex, int *outputIndex);
bool SDL_GetDXGIOutputInfo(SDL_DisplayID displayID, int *adapterIndex, int *outputIndex)
{
(void)displayID;
(void)adapterIndex;
(void)outputIndex;
return SDL_Unsupported();
}
SDL_DECLSPEC int SDLCALL SDL_GetDirect3D9AdapterIndex(SDL_DisplayID displayID);
int SDL_GetDirect3D9AdapterIndex(SDL_DisplayID displayID)
{
(void)displayID;
SDL_Unsupported();
return -1;
}
#elif defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
SDL_DECLSPEC int SDLCALL SDL_GetDirect3D9AdapterIndex(SDL_DisplayID displayID);
int SDL_GetDirect3D9AdapterIndex(SDL_DisplayID displayID)
{
(void)displayID;
SDL_Unsupported();
return -1;
}
#endif // !SDL_VIDEO_DRIVER_WINDOWS
#ifndef SDL_PLATFORM_GDK
SDL_DECLSPEC bool SDLCALL SDL_GetGDKTaskQueue(void *outTaskQueue);
bool SDL_GetGDKTaskQueue(void *outTaskQueue)
{
(void)outTaskQueue;
return SDL_Unsupported();
}
#endif
#ifndef SDL_VIDEO_DRIVER_UIKIT
SDL_DECLSPEC void SDLCALL SDL_OnApplicationDidChangeStatusBarOrientation(void);
void SDL_OnApplicationDidChangeStatusBarOrientation(void)
{
SDL_Unsupported();
}
#endif
#ifndef SDL_VIDEO_DRIVER_UIKIT
typedef void (SDLCALL *SDL_iOSAnimationCallback)(void *userdata);
SDL_DECLSPEC bool SDLCALL SDL_SetiOSAnimationCallback(SDL_Window *window, int interval, SDL_iOSAnimationCallback callback, void *callbackParam);
bool SDL_SetiOSAnimationCallback(SDL_Window *window, int interval, SDL_iOSAnimationCallback callback, void *callbackParam)
{
(void)window;
(void)interval;
(void)callback;
(void)callbackParam;
return SDL_Unsupported();
}
SDL_DECLSPEC void SDLCALL SDL_SetiOSEventPump(bool enabled);
void SDL_SetiOSEventPump(bool enabled)
{
(void)enabled;
SDL_Unsupported();
}
#endif

View file

@ -0,0 +1,91 @@
/*
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_vulkan_internal_h_
#define SDL_vulkan_internal_h_
#include "SDL_internal.h"
#ifdef SDL_VIDEO_VULKAN
#ifdef SDL_VIDEO_DRIVER_ANDROID
#define VK_USE_PLATFORM_ANDROID_KHR
#endif
#ifdef SDL_VIDEO_DRIVER_COCOA
#define VK_USE_PLATFORM_METAL_EXT
#define VK_USE_PLATFORM_MACOS_MVK
#endif
#ifdef SDL_VIDEO_DRIVER_UIKIT
#define VK_USE_PLATFORM_METAL_EXT
#define VK_USE_PLATFORM_IOS_MVK
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#define VK_USE_PLATFORM_WAYLAND_KHR
#include "wayland/SDL_waylanddyn.h"
#endif
#ifdef SDL_VIDEO_DRIVER_WINDOWS
#define VK_USE_PLATFORM_WIN32_KHR
#include "../core/windows/SDL_windows.h"
#endif
#ifdef SDL_VIDEO_DRIVER_X11
#define VK_USE_PLATFORM_XLIB_KHR
#define VK_USE_PLATFORM_XCB_KHR
#endif
#define VK_NO_PROTOTYPES
#include "./khronos/vulkan/vulkan.h"
#include <SDL3/SDL_vulkan.h>
extern const char *SDL_Vulkan_GetResultString(VkResult result);
extern VkExtensionProperties *SDL_Vulkan_CreateInstanceExtensionsList(
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties,
Uint32 *extensionCount); // free returned list with SDL_free
/* Create a surface directly from a display connected to a physical device
* using the DisplayKHR extension.
* This needs to be passed an instance that was created with the VK_KHR_DISPLAY_EXTENSION_NAME
* extension. */
extern bool SDL_Vulkan_Display_CreateSurface(void *vkGetInstanceProcAddr,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
/* Platform independent base function for destroying the Vulkan surface. Unlike surface
* creation, surface destruction doesn't require platform specific extensions like
* VK_KHR_wayland_surface, VK_KHR_android_surface or VK_EXT_metal_surface. The only
* necessary extension is cross platform VK_KHR_surface, which is a dependency to all
* WSI platform extensions, so we can handle surface destruction in an platform-independent
* manner. */
extern void SDL_Vulkan_DestroySurface_Internal(void *vkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
#else
// No SDL Vulkan support, just include the header for typedefs
#include <SDL3/SDL_vulkan.h>
typedef void (*PFN_vkGetInstanceProcAddr)(void);
typedef int (*PFN_vkEnumerateInstanceExtensionProperties)(void);
#endif // SDL_VIDEO_VULKAN
#endif // SDL_vulkan_internal_h_

View file

@ -0,0 +1,487 @@
/*
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"
#include "SDL_vulkan_internal.h"
#ifdef SDL_VIDEO_VULKAN
const char *SDL_Vulkan_GetResultString(VkResult result)
{
switch ((int)result) {
case VK_SUCCESS:
return "VK_SUCCESS";
case VK_NOT_READY:
return "VK_NOT_READY";
case VK_TIMEOUT:
return "VK_TIMEOUT";
case VK_EVENT_SET:
return "VK_EVENT_SET";
case VK_EVENT_RESET:
return "VK_EVENT_RESET";
case VK_INCOMPLETE:
return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY:
return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED:
return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST:
return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED:
return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT:
return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT:
return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT:
return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER:
return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS:
return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED:
return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_FRAGMENTED_POOL:
return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_UNKNOWN:
return "VK_ERROR_UNKNOWN";
case VK_ERROR_OUT_OF_POOL_MEMORY:
return "VK_ERROR_OUT_OF_POOL_MEMORY";
case VK_ERROR_INVALID_EXTERNAL_HANDLE:
return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
case VK_ERROR_FRAGMENTATION:
return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS:
return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
case VK_ERROR_SURFACE_LOST_KHR:
return "VK_ERROR_SURFACE_LOST_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_SUBOPTIMAL_KHR:
return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR:
return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_VALIDATION_FAILED_EXT:
return "VK_ERROR_VALIDATION_FAILED_EXT";
case VK_ERROR_INVALID_SHADER_NV:
return "VK_ERROR_INVALID_SHADER_NV";
#if VK_HEADER_VERSION >= 135 && VK_HEADER_VERSION < 162
case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
#endif
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VK_ERROR_NOT_PERMITTED_EXT:
return "VK_ERROR_NOT_PERMITTED_EXT";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
case VK_THREAD_IDLE_KHR:
return "VK_THREAD_IDLE_KHR";
case VK_THREAD_DONE_KHR:
return "VK_THREAD_DONE_KHR";
case VK_OPERATION_DEFERRED_KHR:
return "VK_OPERATION_DEFERRED_KHR";
case VK_OPERATION_NOT_DEFERRED_KHR:
return "VK_OPERATION_NOT_DEFERRED_KHR";
case VK_PIPELINE_COMPILE_REQUIRED_EXT:
return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
default:
break;
}
if (result < 0) {
return "VK_ERROR_<Unknown>";
}
return "VK_<Unknown>";
}
VkExtensionProperties *SDL_Vulkan_CreateInstanceExtensionsList(
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties,
Uint32 *extensionCount)
{
Uint32 count = 0;
VkResult rc = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
VkExtensionProperties *result;
if (rc == VK_ERROR_INCOMPATIBLE_DRIVER) {
// Avoid the ERR_MAX_STRLEN limit by passing part of the message as a string argument.
SDL_SetError(
"You probably don't have a working Vulkan driver installed. %s %s %s(%d)",
"Getting Vulkan extensions failed:",
"vkEnumerateInstanceExtensionProperties returned",
SDL_Vulkan_GetResultString(rc),
(int)rc);
return NULL;
} else if (rc != VK_SUCCESS) {
SDL_SetError(
"Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
"%s(%d)",
SDL_Vulkan_GetResultString(rc),
(int)rc);
return NULL;
}
if (count == 0) {
result = (VkExtensionProperties *)SDL_calloc(1, sizeof(VkExtensionProperties)); // so we can return non-null
} else {
result = (VkExtensionProperties *)SDL_calloc(count, sizeof(VkExtensionProperties));
}
if (!result) {
return NULL;
}
rc = vkEnumerateInstanceExtensionProperties(NULL, &count, result);
if (rc != VK_SUCCESS) {
SDL_SetError(
"Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
"%s(%d)",
SDL_Vulkan_GetResultString(rc),
(int)rc);
SDL_free(result);
return NULL;
}
*extensionCount = count;
return result;
}
// Alpha modes, in order of preference
static const VkDisplayPlaneAlphaFlagBitsKHR alphaModes[4] = {
VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR,
VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR,
VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR,
VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR,
};
bool SDL_Vulkan_Display_CreateSurface(void *vkGetInstanceProcAddr_,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr_;
#define VULKAN_INSTANCE_FUNCTION(name) \
PFN_##name name = (PFN_##name)vkGetInstanceProcAddr((VkInstance)instance, #name)
VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices);
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPropertiesKHR);
VULKAN_INSTANCE_FUNCTION(vkGetDisplayModePropertiesKHR);
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPlanePropertiesKHR);
VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneCapabilitiesKHR);
VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneSupportedDisplaysKHR);
VULKAN_INSTANCE_FUNCTION(vkCreateDisplayPlaneSurfaceKHR);
#undef VULKAN_INSTANCE_FUNCTION
VkDisplaySurfaceCreateInfoKHR createInfo;
VkResult rc;
uint32_t physicalDeviceCount = 0;
VkPhysicalDevice *physicalDevices = NULL;
uint32_t physicalDeviceIndex;
const char *chosenDisplayId;
int displayId = 0; // Counting from physical device 0, display 0
if (!vkEnumeratePhysicalDevices ||
!vkGetPhysicalDeviceDisplayPropertiesKHR ||
!vkGetDisplayModePropertiesKHR ||
!vkGetPhysicalDeviceDisplayPlanePropertiesKHR ||
!vkGetDisplayPlaneCapabilitiesKHR ||
!vkGetDisplayPlaneSupportedDisplaysKHR ||
!vkCreateDisplayPlaneSurfaceKHR) {
SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
goto error;
}
chosenDisplayId = SDL_GetHint(SDL_HINT_VULKAN_DISPLAY);
if (chosenDisplayId) {
displayId = SDL_atoi(chosenDisplayId);
}
// Enumerate physical devices
rc = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, NULL);
if (rc != VK_SUCCESS) {
SDL_SetError("Could not enumerate Vulkan physical devices");
goto error;
}
if (physicalDeviceCount == 0) {
SDL_SetError("No Vulkan physical devices");
goto error;
}
physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
if (!physicalDevices) {
goto error;
}
rc = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices);
if (rc != VK_SUCCESS) {
SDL_SetError("Error enumerating physical devices");
goto error;
}
for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
uint32_t displayPropertiesCount = 0;
VkDisplayPropertiesKHR *displayProperties = NULL;
uint32_t displayModePropertiesCount = 0;
VkDisplayModePropertiesKHR *displayModeProperties = NULL;
int bestMatchIndex = -1;
uint32_t refreshRate = 0;
uint32_t i;
uint32_t displayPlanePropertiesCount = 0;
int planeIndex = -1;
VkDisplayKHR display;
VkDisplayPlanePropertiesKHR *displayPlaneProperties = NULL;
VkExtent2D extent;
VkDisplayPlaneCapabilitiesKHR planeCaps = { 0 };
// Get information about the physical displays
rc = vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, NULL);
if (rc != VK_SUCCESS || displayPropertiesCount == 0) {
// This device has no physical device display properties, move on to next.
continue;
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display properties for device %u: %u",
physicalDeviceIndex, displayPropertiesCount);
if (displayId < 0 || (uint32_t)displayId >= displayPropertiesCount) {
// Display id specified was higher than number of available displays, move to next physical device.
displayId -= displayPropertiesCount;
continue;
}
displayProperties = (VkDisplayPropertiesKHR *)SDL_malloc(sizeof(VkDisplayPropertiesKHR) * displayPropertiesCount);
if (!displayProperties) {
goto error;
}
rc = vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, displayProperties);
if (rc != VK_SUCCESS || displayPropertiesCount == 0) {
SDL_free(displayProperties);
SDL_SetError("Error enumerating physical device displays");
goto error;
}
display = displayProperties[displayId].display;
extent = displayProperties[displayId].physicalResolution;
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Display: %s Native resolution: %ux%u",
displayProperties[displayId].displayName, extent.width, extent.height);
SDL_free(displayProperties);
displayProperties = NULL;
// Get display mode properties for the chosen display
rc = vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, NULL);
if (rc != VK_SUCCESS || displayModePropertiesCount == 0) {
SDL_SetError("Error enumerating display modes");
goto error;
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display modes: %u", displayModePropertiesCount);
displayModeProperties = (VkDisplayModePropertiesKHR *)SDL_malloc(sizeof(VkDisplayModePropertiesKHR) * displayModePropertiesCount);
if (!displayModeProperties) {
goto error;
}
rc = vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, displayModeProperties);
if (rc != VK_SUCCESS || displayModePropertiesCount == 0) {
SDL_SetError("Error enumerating display modes");
SDL_free(displayModeProperties);
goto error;
}
// Try to find a display mode that matches the native resolution
for (i = 0; i < displayModePropertiesCount; ++i) {
if (displayModeProperties[i].parameters.visibleRegion.width == extent.width &&
displayModeProperties[i].parameters.visibleRegion.height == extent.height &&
displayModeProperties[i].parameters.refreshRate > refreshRate) {
bestMatchIndex = i;
refreshRate = displayModeProperties[i].parameters.refreshRate;
}
}
if (bestMatchIndex < 0) {
SDL_SetError("Found no matching display mode");
SDL_free(displayModeProperties);
goto error;
}
SDL_zero(createInfo);
createInfo.displayMode = displayModeProperties[bestMatchIndex].displayMode;
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Matching mode %ux%u with refresh rate %u",
displayModeProperties[bestMatchIndex].parameters.visibleRegion.width,
displayModeProperties[bestMatchIndex].parameters.visibleRegion.height,
refreshRate);
SDL_free(displayModeProperties);
displayModeProperties = NULL;
// Try to find a plane index that supports our display
rc = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, NULL);
if (rc != VK_SUCCESS || displayPlanePropertiesCount == 0) {
SDL_SetError("Error enumerating display planes");
goto error;
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display planes: %u", displayPlanePropertiesCount);
displayPlaneProperties = (VkDisplayPlanePropertiesKHR *)SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * displayPlanePropertiesCount);
if (!displayPlaneProperties) {
goto error;
}
rc = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, displayPlaneProperties);
if (rc != VK_SUCCESS || displayPlanePropertiesCount == 0) {
SDL_SetError("Error enumerating display plane properties");
SDL_free(displayPlaneProperties);
goto error;
}
for (i = 0; i < displayPlanePropertiesCount; ++i) {
uint32_t planeSupportedDisplaysCount = 0;
VkDisplayKHR *planeSupportedDisplays = NULL;
uint32_t j;
// Check if plane is attached to a display, if not, continue.
if (displayPlaneProperties[i].currentDisplay == VK_NULL_HANDLE) {
continue;
}
// Check supported displays for this plane.
rc = vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, NULL);
if (rc != VK_SUCCESS || planeSupportedDisplaysCount == 0) {
continue; // No supported displays, on to next plane.
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of supported displays for plane %u: %u", i, planeSupportedDisplaysCount);
planeSupportedDisplays = (VkDisplayKHR *)SDL_malloc(sizeof(VkDisplayKHR) * planeSupportedDisplaysCount);
if (!planeSupportedDisplays) {
SDL_free(displayPlaneProperties);
goto error;
}
rc = vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, planeSupportedDisplays);
if (rc != VK_SUCCESS || planeSupportedDisplaysCount == 0) {
SDL_SetError("Error enumerating supported displays, or no supported displays");
SDL_free(planeSupportedDisplays);
SDL_free(displayPlaneProperties);
goto error;
}
for (j = 0; j < planeSupportedDisplaysCount && planeSupportedDisplays[j] != display; ++j) {
}
SDL_free(planeSupportedDisplays);
planeSupportedDisplays = NULL;
if (j == planeSupportedDisplaysCount) {
// This display is not supported for this plane, move on.
continue;
}
rc = vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, createInfo.displayMode, i, &planeCaps);
if (rc != VK_SUCCESS) {
SDL_SetError("Error getting display plane capabilities");
SDL_free(displayPlaneProperties);
goto error;
}
// Check if plane fulfills extent requirements.
if (extent.width >= planeCaps.minDstExtent.width && extent.height >= planeCaps.minDstExtent.height &&
extent.width <= planeCaps.maxDstExtent.width && extent.height <= planeCaps.maxDstExtent.height) {
// If it does, choose this plane.
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Choosing plane %u, minimum extent %ux%u maximum extent %ux%u", i,
planeCaps.minDstExtent.width, planeCaps.minDstExtent.height,
planeCaps.maxDstExtent.width, planeCaps.maxDstExtent.height);
planeIndex = i;
break;
}
}
if (planeIndex < 0) {
SDL_SetError("No plane supports the selected resolution");
SDL_free(displayPlaneProperties);
goto error;
}
createInfo.planeIndex = planeIndex;
createInfo.planeStackIndex = displayPlaneProperties[planeIndex].currentStackIndex;
SDL_free(displayPlaneProperties);
displayPlaneProperties = NULL;
// Find a supported alpha mode. Not all planes support OPAQUE
createInfo.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
for (i = 0; i < SDL_arraysize(alphaModes); i++) {
if (planeCaps.supportedAlpha & alphaModes[i]) {
createInfo.alphaMode = alphaModes[i];
break;
}
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Chose alpha mode 0x%x", createInfo.alphaMode);
// Found a match, finally! Fill in extent, and break from loop
createInfo.imageExtent = extent;
break;
}
SDL_free(physicalDevices);
physicalDevices = NULL;
if (physicalDeviceIndex == physicalDeviceCount) {
SDL_SetError("No usable displays found or requested display out of range");
goto error;
}
createInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
createInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.globalAlpha = 1.0f;
rc = vkCreateDisplayPlaneSurfaceKHR(instance, &createInfo, allocator, surface);
if (rc != VK_SUCCESS) {
SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(rc));
goto error;
}
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Created surface");
return true;
error:
SDL_free(physicalDevices);
return false;
}
void SDL_Vulkan_DestroySurface_Internal(void *vkGetInstanceProcAddr_,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator)
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr_;
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR =
(PFN_vkDestroySurfaceKHR)vkGetInstanceProcAddr(
instance,
"vkDestroySurfaceKHR");
if (vkDestroySurfaceKHR) {
vkDestroySurfaceKHR(instance, surface, allocator);
}
}
#endif

2610
vendor/sdl-3.2.10/src/video/SDL_yuv.c vendored Normal file

File diff suppressed because it is too large Load diff

36
vendor/sdl-3.2.10/src/video/SDL_yuv_c.h vendored Normal file
View file

@ -0,0 +1,36 @@
/*
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_yuv_c_h_
#define SDL_yuv_c_h_
#include "SDL_internal.h"
// YUV conversion functions
extern bool SDL_ConvertPixels_YUV_to_RGB(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
extern bool SDL_ConvertPixels_RGB_to_YUV(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
extern bool SDL_ConvertPixels_YUV_to_YUV(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
extern bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, size_t *pitch);
#endif // SDL_yuv_c_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_ANDROID
#include "SDL_androidvideo.h"
#include "SDL_androidclipboard.h"
#include "../../core/android/SDL_android.h"
bool Android_SetClipboardText(SDL_VideoDevice *_this, const char *text)
{
return Android_JNI_SetClipboardText(text);
}
char *Android_GetClipboardText(SDL_VideoDevice *_this)
{
return Android_JNI_GetClipboardText();
}
bool Android_HasClipboardText(SDL_VideoDevice *_this)
{
return Android_JNI_HasClipboardText();
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,30 @@
/*
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_androidclipboard_h_
#define SDL_androidclipboard_h_
extern bool Android_SetClipboardText(SDL_VideoDevice *_this, const char *text);
extern char *Android_GetClipboardText(SDL_VideoDevice *_this);
extern bool Android_HasClipboardText(SDL_VideoDevice *_this);
#endif // SDL_androidclipboard_h_

View file

@ -0,0 +1,269 @@
/*
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_ANDROID
#include "SDL_androidevents.h"
#include "SDL_androidkeyboard.h"
#include "SDL_androidwindow.h"
#include "../SDL_sysvideo.h"
#include "../../events/SDL_events_c.h"
#include "../../audio/aaudio/SDL_aaudio.h"
#include "../../audio/openslES/SDL_openslES.h"
#ifdef SDL_VIDEO_OPENGL_EGL
static void android_egl_context_restore(SDL_Window *window)
{
if (window) {
SDL_WindowData *data = window->internal;
SDL_GL_MakeCurrent(window, NULL);
if (!SDL_GL_MakeCurrent(window, (SDL_GLContext)data->egl_context)) {
// The context is no longer valid, create a new one
data->egl_context = (EGLContext)SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, (SDL_GLContext)data->egl_context);
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_RENDER_DEVICE_RESET;
event.render.windowID = SDL_GetWindowID(window);
SDL_PushEvent(&event);
}
data->backup_done = false;
if (data->has_swap_interval) {
SDL_GL_SetSwapInterval(data->swap_interval);
}
}
}
static void android_egl_context_backup(SDL_Window *window)
{
if (window) {
int interval = 0;
// Keep a copy of the EGL Context so we can try to restore it when we resume
SDL_WindowData *data = window->internal;
data->egl_context = SDL_GL_GetCurrentContext();
// Save/Restore the swap interval / vsync
if (SDL_GL_GetSwapInterval(&interval)) {
data->has_swap_interval = 1;
data->swap_interval = interval;
}
// We need to do this so the EGLSurface can be freed
SDL_GL_MakeCurrent(window, NULL);
data->backup_done = true;
}
}
#endif
/*
* Android_ResumeSem and Android_PauseSem are signaled from Java_org_libsdl_app_SDLActivity_nativePause and Java_org_libsdl_app_SDLActivity_nativeResume
*/
static bool Android_EventsInitialized;
static bool Android_BlockOnPause = true;
static bool Android_Paused;
static bool Android_PausedAudio;
static bool Android_Destroyed;
void Android_InitEvents(void)
{
if (!Android_EventsInitialized) {
Android_BlockOnPause = SDL_GetHintBoolean(SDL_HINT_ANDROID_BLOCK_ON_PAUSE, true);
Android_Paused = false;
Android_Destroyed = false;
Android_EventsInitialized = true;
}
}
static void Android_PauseAudio(void)
{
OPENSLES_PauseDevices();
AAUDIO_PauseDevices();
Android_PausedAudio = true;
}
static void Android_ResumeAudio(void)
{
if (Android_PausedAudio) {
OPENSLES_ResumeDevices();
AAUDIO_ResumeDevices();
Android_PausedAudio = false;
}
}
static void Android_OnPause(void)
{
SDL_OnApplicationWillEnterBackground();
SDL_OnApplicationDidEnterBackground();
/* The semantics are that as soon as the enter background event
* has been queued, the app will block. The application should
* do any life cycle handling in an event filter while the event
* was being queued.
*/
#ifdef SDL_VIDEO_OPENGL_EGL
if (Android_Window && !Android_Window->external_graphics_context) {
Android_LockActivityMutex();
android_egl_context_backup(Android_Window);
Android_UnlockActivityMutex();
}
#endif
if (Android_BlockOnPause) {
// We're blocking, also pause audio
Android_PauseAudio();
}
Android_Paused = true;
}
static void Android_OnResume(void)
{
Android_Paused = false;
SDL_OnApplicationWillEnterForeground();
Android_ResumeAudio();
#ifdef SDL_VIDEO_OPENGL_EGL
// Restore the GL Context from here, as this operation is thread dependent
if (Android_Window && !Android_Window->external_graphics_context && !SDL_HasEvent(SDL_EVENT_QUIT)) {
Android_LockActivityMutex();
android_egl_context_restore(Android_Window);
Android_UnlockActivityMutex();
}
#endif
// Make sure SW Keyboard is restored when an app becomes foreground
if (Android_Window) {
Android_RestoreScreenKeyboardOnResume(SDL_GetVideoDevice(), Android_Window);
}
SDL_OnApplicationDidEnterForeground();
}
static void Android_OnLowMemory(void)
{
SDL_SendAppEvent(SDL_EVENT_LOW_MEMORY);
}
static void Android_OnDestroy(void)
{
// Make sure we unblock any audio processing before we quit
Android_ResumeAudio();
/* Discard previous events. The user should have handled state storage
* in SDL_EVENT_WILL_ENTER_BACKGROUND. After nativeSendQuit() is called, no
* events other than SDL_EVENT_QUIT and SDL_EVENT_TERMINATING should fire */
SDL_FlushEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST);
SDL_SendQuit();
SDL_SendAppEvent(SDL_EVENT_TERMINATING);
Android_Destroyed = true;
}
static void Android_HandleLifecycleEvent(SDL_AndroidLifecycleEvent event)
{
switch (event) {
case SDL_ANDROID_LIFECYCLE_WAKE:
// Nothing to do, just return
break;
case SDL_ANDROID_LIFECYCLE_PAUSE:
Android_OnPause();
break;
case SDL_ANDROID_LIFECYCLE_RESUME:
Android_OnResume();
break;
case SDL_ANDROID_LIFECYCLE_LOWMEMORY:
Android_OnLowMemory();
break;
case SDL_ANDROID_LIFECYCLE_DESTROY:
Android_OnDestroy();
break;
default:
break;
}
}
static Sint64 GetLifecycleEventTimeout(bool paused, Sint64 timeoutNS)
{
if (Android_Paused) {
if (Android_BlockOnPause) {
timeoutNS = -1;
} else if (timeoutNS == 0) {
timeoutNS = SDL_MS_TO_NS(100);
}
}
return timeoutNS;
}
void Android_PumpEvents(Sint64 timeoutNS)
{
SDL_AndroidLifecycleEvent event;
bool paused = Android_Paused;
while (!Android_Destroyed &&
Android_WaitLifecycleEvent(&event, GetLifecycleEventTimeout(paused, timeoutNS))) {
Android_HandleLifecycleEvent(event);
switch (event) {
case SDL_ANDROID_LIFECYCLE_WAKE:
// Finish handling events quickly if we're not paused
timeoutNS = 0;
break;
case SDL_ANDROID_LIFECYCLE_PAUSE:
// Finish handling events at the current timeout and return to process events one more time before blocking.
break;
case SDL_ANDROID_LIFECYCLE_RESUME:
// Finish handling events at the resume state timeout
paused = false;
break;
default:
break;
}
}
}
bool Android_WaitActiveAndLockActivity(void)
{
while (Android_Paused && !Android_Destroyed) {
Android_PumpEvents(-1);
}
if (Android_Destroyed) {
SDL_SetError("Android activity has been destroyed");
return false;
}
Android_LockActivityMutex();
return true;
}
void Android_QuitEvents(void)
{
Android_EventsInitialized = false;
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,26 @@
/*
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"
extern void Android_InitEvents(void);
extern void Android_PumpEvents(Sint64 timeoutNS);
extern bool Android_WaitActiveAndLockActivity(void);
extern void Android_QuitEvents(void);

View file

@ -0,0 +1,88 @@
/*
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"
#if defined(SDL_VIDEO_DRIVER_ANDROID) && defined(SDL_VIDEO_OPENGL_EGL)
// Android SDL video driver implementation
#include "../SDL_egl_c.h"
#include "SDL_androidwindow.h"
#include "SDL_androidvideo.h"
#include "SDL_androidevents.h"
#include "SDL_androidgl.h"
#include "../../core/android/SDL_android.h"
#include <android/log.h>
#include <dlfcn.h>
bool Android_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
{
if (window && context) {
return SDL_EGL_MakeCurrent(_this, window->internal->egl_surface, context);
} else {
return SDL_EGL_MakeCurrent(_this, NULL, NULL);
}
}
SDL_GLContext Android_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_GLContext result;
if (!Android_WaitActiveAndLockActivity()) {
return NULL;
}
result = SDL_EGL_CreateContext(_this, window->internal->egl_surface);
Android_UnlockActivityMutex();
return result;
}
bool Android_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
bool result;
Android_LockActivityMutex();
/* The following two calls existed in the original Java code
* If you happen to have a device that's affected by their removal,
* please report to our bug tracker. -- Gabriel
*/
/*_this->egl_data->eglWaitNative(EGL_CORE_NATIVE_ENGINE);
_this->egl_data->eglWaitGL();*/
result = SDL_EGL_SwapBuffers(_this, window->internal->egl_surface);
Android_UnlockActivityMutex();
return result;
}
bool Android_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
return SDL_EGL_LoadLibrary(_this, path, (NativeDisplayType)0, 0);
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,31 @@
/*
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_androidgl_h_
#define SDL_androidgl_h_
extern SDL_GLContext Android_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Android_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
extern bool Android_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Android_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
#endif // SDL_androidgl_h_

View file

@ -0,0 +1,468 @@
/*
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_ANDROID
#include <android/log.h>
#include "../../events/SDL_events_c.h"
#include "SDL_androidkeyboard.h"
#include "../../core/android/SDL_android.h"
#define TYPE_CLASS_TEXT 0x00000001
#define TYPE_CLASS_NUMBER 0x00000002
#define TYPE_CLASS_PHONE 0x00000003
#define TYPE_CLASS_DATETIME 0x00000004
#define TYPE_DATETIME_VARIATION_NORMAL 0x00000000
#define TYPE_DATETIME_VARIATION_DATE 0x00000010
#define TYPE_DATETIME_VARIATION_TIME 0x00000020
#define TYPE_NUMBER_VARIATION_NORMAL 0x00000000
#define TYPE_NUMBER_VARIATION_PASSWORD 0x00000010
#define TYPE_NUMBER_FLAG_SIGNED 0x00001000
#define TYPE_NUMBER_FLAG_DECIMAL 0x00002000
#define TYPE_TEXT_FLAG_CAP_CHARACTERS 0x00001000
#define TYPE_TEXT_FLAG_CAP_WORDS 0x00002000
#define TYPE_TEXT_FLAG_CAP_SENTENCES 0x00004000
#define TYPE_TEXT_FLAG_AUTO_CORRECT 0x00008000
#define TYPE_TEXT_FLAG_AUTO_COMPLETE 0x00010000
#define TYPE_TEXT_FLAG_MULTI_LINE 0x00020000
#define TYPE_TEXT_FLAG_IME_MULTI_LINE 0x00040000
#define TYPE_TEXT_FLAG_NO_SUGGESTIONS 0x00080000
#define TYPE_TEXT_VARIATION_NORMAL 0x00000000
#define TYPE_TEXT_VARIATION_URI 0x00000010
#define TYPE_TEXT_VARIATION_EMAIL_ADDRESS 0x00000020
#define TYPE_TEXT_VARIATION_EMAIL_SUBJECT 0x00000030
#define TYPE_TEXT_VARIATION_SHORT_MESSAGE 0x00000040
#define TYPE_TEXT_VARIATION_LONG_MESSAGE 0x00000050
#define TYPE_TEXT_VARIATION_PERSON_NAME 0x00000060
#define TYPE_TEXT_VARIATION_POSTAL_ADDRESS 0x00000070
#define TYPE_TEXT_VARIATION_PASSWORD 0x00000080
#define TYPE_TEXT_VARIATION_VISIBLE_PASSWORD 0x00000090
#define TYPE_TEXT_VARIATION_WEB_EDIT_TEXT 0x000000a0
#define TYPE_TEXT_VARIATION_FILTER 0x000000b0
#define TYPE_TEXT_VARIATION_PHONETIC 0x000000c0
#define TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS 0x000000d0
#define TYPE_TEXT_VARIATION_WEB_PASSWORD 0x000000e0
static SDL_Scancode Android_Keycodes[] = {
SDL_SCANCODE_UNKNOWN, // AKEYCODE_UNKNOWN
SDL_SCANCODE_SOFTLEFT, // AKEYCODE_SOFT_LEFT
SDL_SCANCODE_SOFTRIGHT, // AKEYCODE_SOFT_RIGHT
SDL_SCANCODE_AC_HOME, // AKEYCODE_HOME
SDL_SCANCODE_AC_BACK, // AKEYCODE_BACK
SDL_SCANCODE_CALL, // AKEYCODE_CALL
SDL_SCANCODE_ENDCALL, // AKEYCODE_ENDCALL
SDL_SCANCODE_0, // AKEYCODE_0
SDL_SCANCODE_1, // AKEYCODE_1
SDL_SCANCODE_2, // AKEYCODE_2
SDL_SCANCODE_3, // AKEYCODE_3
SDL_SCANCODE_4, // AKEYCODE_4
SDL_SCANCODE_5, // AKEYCODE_5
SDL_SCANCODE_6, // AKEYCODE_6
SDL_SCANCODE_7, // AKEYCODE_7
SDL_SCANCODE_8, // AKEYCODE_8
SDL_SCANCODE_9, // AKEYCODE_9
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STAR
SDL_SCANCODE_UNKNOWN, // AKEYCODE_POUND
SDL_SCANCODE_UP, // AKEYCODE_DPAD_UP
SDL_SCANCODE_DOWN, // AKEYCODE_DPAD_DOWN
SDL_SCANCODE_LEFT, // AKEYCODE_DPAD_LEFT
SDL_SCANCODE_RIGHT, // AKEYCODE_DPAD_RIGHT
SDL_SCANCODE_SELECT, // AKEYCODE_DPAD_CENTER
SDL_SCANCODE_VOLUMEUP, // AKEYCODE_VOLUME_UP
SDL_SCANCODE_VOLUMEDOWN, // AKEYCODE_VOLUME_DOWN
SDL_SCANCODE_POWER, // AKEYCODE_POWER
SDL_SCANCODE_UNKNOWN, // AKEYCODE_CAMERA
SDL_SCANCODE_CLEAR, // AKEYCODE_CLEAR
SDL_SCANCODE_A, // AKEYCODE_A
SDL_SCANCODE_B, // AKEYCODE_B
SDL_SCANCODE_C, // AKEYCODE_C
SDL_SCANCODE_D, // AKEYCODE_D
SDL_SCANCODE_E, // AKEYCODE_E
SDL_SCANCODE_F, // AKEYCODE_F
SDL_SCANCODE_G, // AKEYCODE_G
SDL_SCANCODE_H, // AKEYCODE_H
SDL_SCANCODE_I, // AKEYCODE_I
SDL_SCANCODE_J, // AKEYCODE_J
SDL_SCANCODE_K, // AKEYCODE_K
SDL_SCANCODE_L, // AKEYCODE_L
SDL_SCANCODE_M, // AKEYCODE_M
SDL_SCANCODE_N, // AKEYCODE_N
SDL_SCANCODE_O, // AKEYCODE_O
SDL_SCANCODE_P, // AKEYCODE_P
SDL_SCANCODE_Q, // AKEYCODE_Q
SDL_SCANCODE_R, // AKEYCODE_R
SDL_SCANCODE_S, // AKEYCODE_S
SDL_SCANCODE_T, // AKEYCODE_T
SDL_SCANCODE_U, // AKEYCODE_U
SDL_SCANCODE_V, // AKEYCODE_V
SDL_SCANCODE_W, // AKEYCODE_W
SDL_SCANCODE_X, // AKEYCODE_X
SDL_SCANCODE_Y, // AKEYCODE_Y
SDL_SCANCODE_Z, // AKEYCODE_Z
SDL_SCANCODE_COMMA, // AKEYCODE_COMMA
SDL_SCANCODE_PERIOD, // AKEYCODE_PERIOD
SDL_SCANCODE_LALT, // AKEYCODE_ALT_LEFT
SDL_SCANCODE_RALT, // AKEYCODE_ALT_RIGHT
SDL_SCANCODE_LSHIFT, // AKEYCODE_SHIFT_LEFT
SDL_SCANCODE_RSHIFT, // AKEYCODE_SHIFT_RIGHT
SDL_SCANCODE_TAB, // AKEYCODE_TAB
SDL_SCANCODE_SPACE, // AKEYCODE_SPACE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_SYM
SDL_SCANCODE_UNKNOWN, // AKEYCODE_EXPLORER
SDL_SCANCODE_UNKNOWN, // AKEYCODE_ENVELOPE
SDL_SCANCODE_RETURN, // AKEYCODE_ENTER
SDL_SCANCODE_BACKSPACE, // AKEYCODE_DEL
SDL_SCANCODE_GRAVE, // AKEYCODE_GRAVE
SDL_SCANCODE_MINUS, // AKEYCODE_MINUS
SDL_SCANCODE_EQUALS, // AKEYCODE_EQUALS
SDL_SCANCODE_LEFTBRACKET, // AKEYCODE_LEFT_BRACKET
SDL_SCANCODE_RIGHTBRACKET, // AKEYCODE_RIGHT_BRACKET
SDL_SCANCODE_BACKSLASH, // AKEYCODE_BACKSLASH
SDL_SCANCODE_SEMICOLON, // AKEYCODE_SEMICOLON
SDL_SCANCODE_APOSTROPHE, // AKEYCODE_APOSTROPHE
SDL_SCANCODE_SLASH, // AKEYCODE_SLASH
SDL_SCANCODE_UNKNOWN, // AKEYCODE_AT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_NUM
SDL_SCANCODE_UNKNOWN, // AKEYCODE_HEADSETHOOK
SDL_SCANCODE_UNKNOWN, // AKEYCODE_FOCUS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PLUS
SDL_SCANCODE_MENU, // AKEYCODE_MENU
SDL_SCANCODE_UNKNOWN, // AKEYCODE_NOTIFICATION
SDL_SCANCODE_AC_SEARCH, // AKEYCODE_SEARCH
SDL_SCANCODE_MEDIA_PLAY_PAUSE, // AKEYCODE_MEDIA_PLAY_PAUSE
SDL_SCANCODE_MEDIA_STOP, // AKEYCODE_MEDIA_STOP
SDL_SCANCODE_MEDIA_NEXT_TRACK, // AKEYCODE_MEDIA_NEXT
SDL_SCANCODE_MEDIA_PREVIOUS_TRACK, // AKEYCODE_MEDIA_PREVIOUS
SDL_SCANCODE_MEDIA_REWIND, // AKEYCODE_MEDIA_REWIND
SDL_SCANCODE_MEDIA_FAST_FORWARD, // AKEYCODE_MEDIA_FAST_FORWARD
SDL_SCANCODE_MUTE, // AKEYCODE_MUTE
SDL_SCANCODE_PAGEUP, // AKEYCODE_PAGE_UP
SDL_SCANCODE_PAGEDOWN, // AKEYCODE_PAGE_DOWN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PICTSYMBOLS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_SWITCH_CHARSET
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_A
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_B
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_C
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_X
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_Y
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_Z
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_L1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_R1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_L2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_R2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_THUMBL
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_THUMBR
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_START
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_SELECT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_MODE
SDL_SCANCODE_ESCAPE, // AKEYCODE_ESCAPE
SDL_SCANCODE_DELETE, // AKEYCODE_FORWARD_DEL
SDL_SCANCODE_LCTRL, // AKEYCODE_CTRL_LEFT
SDL_SCANCODE_RCTRL, // AKEYCODE_CTRL_RIGHT
SDL_SCANCODE_CAPSLOCK, // AKEYCODE_CAPS_LOCK
SDL_SCANCODE_SCROLLLOCK, // AKEYCODE_SCROLL_LOCK
SDL_SCANCODE_LGUI, // AKEYCODE_META_LEFT
SDL_SCANCODE_RGUI, // AKEYCODE_META_RIGHT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_FUNCTION
SDL_SCANCODE_PRINTSCREEN, // AKEYCODE_SYSRQ
SDL_SCANCODE_PAUSE, // AKEYCODE_BREAK
SDL_SCANCODE_HOME, // AKEYCODE_MOVE_HOME
SDL_SCANCODE_END, // AKEYCODE_MOVE_END
SDL_SCANCODE_INSERT, // AKEYCODE_INSERT
SDL_SCANCODE_AC_FORWARD, // AKEYCODE_FORWARD
SDL_SCANCODE_MEDIA_PLAY, // AKEYCODE_MEDIA_PLAY
SDL_SCANCODE_MEDIA_PAUSE, // AKEYCODE_MEDIA_PAUSE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_CLOSE
SDL_SCANCODE_MEDIA_EJECT, // AKEYCODE_MEDIA_EJECT
SDL_SCANCODE_MEDIA_RECORD, // AKEYCODE_MEDIA_RECORD
SDL_SCANCODE_F1, // AKEYCODE_F1
SDL_SCANCODE_F2, // AKEYCODE_F2
SDL_SCANCODE_F3, // AKEYCODE_F3
SDL_SCANCODE_F4, // AKEYCODE_F4
SDL_SCANCODE_F5, // AKEYCODE_F5
SDL_SCANCODE_F6, // AKEYCODE_F6
SDL_SCANCODE_F7, // AKEYCODE_F7
SDL_SCANCODE_F8, // AKEYCODE_F8
SDL_SCANCODE_F9, // AKEYCODE_F9
SDL_SCANCODE_F10, // AKEYCODE_F10
SDL_SCANCODE_F11, // AKEYCODE_F11
SDL_SCANCODE_F12, // AKEYCODE_F12
SDL_SCANCODE_NUMLOCKCLEAR, // AKEYCODE_NUM_LOCK
SDL_SCANCODE_KP_0, // AKEYCODE_NUMPAD_0
SDL_SCANCODE_KP_1, // AKEYCODE_NUMPAD_1
SDL_SCANCODE_KP_2, // AKEYCODE_NUMPAD_2
SDL_SCANCODE_KP_3, // AKEYCODE_NUMPAD_3
SDL_SCANCODE_KP_4, // AKEYCODE_NUMPAD_4
SDL_SCANCODE_KP_5, // AKEYCODE_NUMPAD_5
SDL_SCANCODE_KP_6, // AKEYCODE_NUMPAD_6
SDL_SCANCODE_KP_7, // AKEYCODE_NUMPAD_7
SDL_SCANCODE_KP_8, // AKEYCODE_NUMPAD_8
SDL_SCANCODE_KP_9, // AKEYCODE_NUMPAD_9
SDL_SCANCODE_KP_DIVIDE, // AKEYCODE_NUMPAD_DIVIDE
SDL_SCANCODE_KP_MULTIPLY, // AKEYCODE_NUMPAD_MULTIPLY
SDL_SCANCODE_KP_MINUS, // AKEYCODE_NUMPAD_SUBTRACT
SDL_SCANCODE_KP_PLUS, // AKEYCODE_NUMPAD_ADD
SDL_SCANCODE_KP_PERIOD, // AKEYCODE_NUMPAD_DOT
SDL_SCANCODE_KP_COMMA, // AKEYCODE_NUMPAD_COMMA
SDL_SCANCODE_KP_ENTER, // AKEYCODE_NUMPAD_ENTER
SDL_SCANCODE_KP_EQUALS, // AKEYCODE_NUMPAD_EQUALS
SDL_SCANCODE_KP_LEFTPAREN, // AKEYCODE_NUMPAD_LEFT_PAREN
SDL_SCANCODE_KP_RIGHTPAREN, // AKEYCODE_NUMPAD_RIGHT_PAREN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_VOLUME_MUTE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_INFO
SDL_SCANCODE_CHANNEL_INCREMENT, // AKEYCODE_CHANNEL_UP
SDL_SCANCODE_CHANNEL_INCREMENT, // AKEYCODE_CHANNEL_DOWN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_ZOOM_IN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_ZOOM_OUT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV
SDL_SCANCODE_UNKNOWN, // AKEYCODE_WINDOW
SDL_SCANCODE_UNKNOWN, // AKEYCODE_GUIDE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_DVR
SDL_SCANCODE_AC_BOOKMARKS, // AKEYCODE_BOOKMARK
SDL_SCANCODE_UNKNOWN, // AKEYCODE_CAPTIONS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_SETTINGS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_POWER
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STB_POWER
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STB_INPUT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_AVR_POWER
SDL_SCANCODE_UNKNOWN, // AKEYCODE_AVR_INPUT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PROG_RED
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PROG_GREEN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PROG_YELLOW
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PROG_BLUE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_APP_SWITCH
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_3
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_4
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_5
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_6
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_7
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_8
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_9
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_10
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_11
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_12
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_13
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_14
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_15
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BUTTON_16
SDL_SCANCODE_UNKNOWN, // AKEYCODE_LANGUAGE_SWITCH
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MANNER_MODE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_3D_MODE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_CONTACTS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_CALENDAR
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MUSIC
SDL_SCANCODE_UNKNOWN, // AKEYCODE_CALCULATOR
SDL_SCANCODE_LANG5, // AKEYCODE_ZENKAKU_HANKAKU
SDL_SCANCODE_UNKNOWN, // AKEYCODE_EISU
SDL_SCANCODE_INTERNATIONAL5, // AKEYCODE_MUHENKAN
SDL_SCANCODE_INTERNATIONAL4, // AKEYCODE_HENKAN
SDL_SCANCODE_LANG3, // AKEYCODE_KATAKANA_HIRAGANA
SDL_SCANCODE_INTERNATIONAL3, // AKEYCODE_YEN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_RO
SDL_SCANCODE_UNKNOWN, // AKEYCODE_KANA
SDL_SCANCODE_UNKNOWN, // AKEYCODE_ASSIST
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BRIGHTNESS_DOWN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_BRIGHTNESS_UP
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_AUDIO_TRACK
SDL_SCANCODE_SLEEP, // AKEYCODE_SLEEP
SDL_SCANCODE_UNKNOWN, // AKEYCODE_WAKEUP
SDL_SCANCODE_UNKNOWN, // AKEYCODE_PAIRING
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_TOP_MENU
SDL_SCANCODE_UNKNOWN, // AKEYCODE_11
SDL_SCANCODE_UNKNOWN, // AKEYCODE_12
SDL_SCANCODE_UNKNOWN, // AKEYCODE_LAST_CHANNEL
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_DATA_SERVICE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_VOICE_ASSIST
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_RADIO_SERVICE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_TELETEXT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_NUMBER_ENTRY
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_TERRESTRIAL_ANALOG
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_TERRESTRIAL_DIGITAL
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_SATELLITE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_SATELLITE_BS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_SATELLITE_CS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_SATELLITE_SERVICE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_NETWORK
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_ANTENNA_CABLE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_HDMI_1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_HDMI_2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_HDMI_3
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_HDMI_4
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_COMPOSITE_1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_COMPOSITE_2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_COMPONENT_1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_COMPONENT_2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_INPUT_VGA_1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_AUDIO_DESCRIPTION
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_ZOOM_MODE
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_CONTENTS_MENU
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_MEDIA_CONTEXT_MENU
SDL_SCANCODE_UNKNOWN, // AKEYCODE_TV_TIMER_PROGRAMMING
SDL_SCANCODE_HELP, // AKEYCODE_HELP
SDL_SCANCODE_UNKNOWN, // AKEYCODE_NAVIGATE_PREVIOUS
SDL_SCANCODE_UNKNOWN, // AKEYCODE_NAVIGATE_NEXT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_NAVIGATE_IN
SDL_SCANCODE_UNKNOWN, // AKEYCODE_NAVIGATE_OUT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STEM_PRIMARY
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STEM_1
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STEM_2
SDL_SCANCODE_UNKNOWN, // AKEYCODE_STEM_3
SDL_SCANCODE_UNKNOWN, // AKEYCODE_DPAD_UP_LEFT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_DPAD_DOWN_LEFT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_DPAD_UP_RIGHT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_DPAD_DOWN_RIGHT
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_SKIP_FORWARD
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_SKIP_BACKWARD
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_STEP_FORWARD
SDL_SCANCODE_UNKNOWN, // AKEYCODE_MEDIA_STEP_BACKWARD
SDL_SCANCODE_UNKNOWN, // AKEYCODE_SOFT_SLEEP
SDL_SCANCODE_CUT, // AKEYCODE_CUT
SDL_SCANCODE_COPY, // AKEYCODE_COPY
SDL_SCANCODE_PASTE, // AKEYCODE_PASTE
};
static bool SDL_screen_keyboard_shown;
static SDL_Scancode TranslateKeycode(int keycode)
{
SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
if (keycode < SDL_arraysize(Android_Keycodes)) {
scancode = Android_Keycodes[keycode];
}
if (scancode == SDL_SCANCODE_UNKNOWN) {
__android_log_print(ANDROID_LOG_INFO, "SDL", "Unknown keycode %d", keycode);
}
return scancode;
}
void Android_OnKeyDown(int keycode)
{
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, keycode, TranslateKeycode(keycode), true);
}
void Android_OnKeyUp(int keycode)
{
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, keycode, TranslateKeycode(keycode), false);
}
bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
{
return true;
}
void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
{
int input_type = 0;
if (SDL_HasProperty(props, SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER)) {
input_type = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER, 0);
} else {
switch (SDL_GetTextInputType(props)) {
default:
case SDL_TEXTINPUT_TYPE_TEXT:
input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_NORMAL);
break;
case SDL_TEXTINPUT_TYPE_TEXT_NAME:
input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PERSON_NAME);
break;
case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
break;
case SDL_TEXTINPUT_TYPE_TEXT_USERNAME:
input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_NORMAL);
break;
case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN:
input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
break;
case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE:
input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
break;
case SDL_TEXTINPUT_TYPE_NUMBER:
input_type = (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_NORMAL);
break;
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
input_type = (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD);
break;
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
input_type = (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_NORMAL);
break;
}
switch (SDL_GetTextInputCapitalization(props)) {
default:
case SDL_CAPITALIZE_NONE:
break;
case SDL_CAPITALIZE_LETTERS:
input_type |= TYPE_TEXT_FLAG_CAP_CHARACTERS;
break;
case SDL_CAPITALIZE_WORDS:
input_type |= TYPE_TEXT_FLAG_CAP_WORDS;
break;
case SDL_CAPITALIZE_SENTENCES:
input_type |= TYPE_TEXT_FLAG_CAP_SENTENCES;
break;
}
if (SDL_GetTextInputAutocorrect(props)) {
input_type |= (TYPE_TEXT_FLAG_AUTO_CORRECT | TYPE_TEXT_FLAG_AUTO_COMPLETE);
}
if (SDL_GetTextInputMultiline(props)) {
input_type |= TYPE_TEXT_FLAG_MULTI_LINE;
}
}
Android_JNI_ShowScreenKeyboard(input_type, &window->text_input_rect);
SDL_screen_keyboard_shown = true;
}
void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
{
Android_JNI_HideScreenKeyboard();
SDL_screen_keyboard_shown = false;
}
void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window)
{
if (SDL_screen_keyboard_shown) {
Android_ShowScreenKeyboard(_this, window, window->text_input_props);
}
}
bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
{
return Android_JNI_IsScreenKeyboardShown();
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,32 @@
/*
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"
#include "SDL_androidvideo.h"
extern void Android_OnKeyDown(int keycode);
extern void Android_OnKeyUp(int keycode);
extern bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
extern void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
extern void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);

View file

@ -0,0 +1,33 @@
/*
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_ANDROID
#include "SDL_androidmessagebox.h"
#include "../../core/android/SDL_android.h"
bool Android_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
return Android_JNI_ShowMessageBox(messageboxdata, buttonID);
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,27 @@
/*
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_ANDROID
extern bool Android_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,250 @@
/*
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_ANDROID
#include "SDL_androidmouse.h"
#include "../../events/SDL_mouse_c.h"
#include "../../core/android/SDL_android.h"
// See Android's MotionEvent class for constants
#define ACTION_DOWN 0
#define ACTION_UP 1
#define ACTION_MOVE 2
#define ACTION_HOVER_MOVE 7
#define ACTION_SCROLL 8
#define BUTTON_PRIMARY 1
#define BUTTON_SECONDARY 2
#define BUTTON_TERTIARY 4
#define BUTTON_BACK 8
#define BUTTON_FORWARD 16
struct SDL_CursorData
{
int custom_cursor;
int system_cursor;
};
// Last known Android mouse button state (includes all buttons)
static int last_state;
// Blank cursor
static SDL_Cursor *empty_cursor;
static SDL_Cursor *Android_WrapCursor(int custom_cursor, int system_cursor)
{
SDL_Cursor *cursor;
cursor = SDL_calloc(1, sizeof(*cursor));
if (cursor) {
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
if (data) {
data->custom_cursor = custom_cursor;
data->system_cursor = system_cursor;
cursor->internal = data;
} else {
SDL_free(cursor);
cursor = NULL;
}
}
return cursor;
}
static SDL_Cursor *Android_CreateDefaultCursor(void)
{
SDL_SystemCursor id = SDL_GetDefaultSystemCursor();
return Android_WrapCursor(0, id);
}
static SDL_Cursor *Android_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
int custom_cursor;
SDL_Surface *converted;
converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
if (!converted) {
return NULL;
}
custom_cursor = Android_JNI_CreateCustomCursor(converted, hot_x, hot_y);
SDL_DestroySurface(converted);
if (!custom_cursor) {
SDL_Unsupported();
return NULL;
}
return Android_WrapCursor(custom_cursor, 0);
}
static SDL_Cursor *Android_CreateSystemCursor(SDL_SystemCursor id)
{
return Android_WrapCursor(0, id);
}
static void Android_FreeCursor(SDL_Cursor *cursor)
{
SDL_CursorData *data = cursor->internal;
if (data->custom_cursor != 0) {
Android_JNI_DestroyCustomCursor(data->custom_cursor);
}
SDL_free(cursor->internal);
SDL_free(cursor);
}
static SDL_Cursor *Android_CreateEmptyCursor(void)
{
if (!empty_cursor) {
SDL_Surface *empty_surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ARGB8888);
if (empty_surface) {
SDL_memset(empty_surface->pixels, 0, (size_t)empty_surface->h * empty_surface->pitch);
empty_cursor = Android_CreateCursor(empty_surface, 0, 0);
SDL_DestroySurface(empty_surface);
}
}
return empty_cursor;
}
static void Android_DestroyEmptyCursor(void)
{
if (empty_cursor) {
Android_FreeCursor(empty_cursor);
empty_cursor = NULL;
}
}
static bool Android_ShowCursor(SDL_Cursor *cursor)
{
if (!cursor) {
cursor = Android_CreateEmptyCursor();
}
if (cursor) {
SDL_CursorData *data = cursor->internal;
if (data->custom_cursor) {
if (!Android_JNI_SetCustomCursor(data->custom_cursor)) {
return SDL_Unsupported();
}
} else {
if (!Android_JNI_SetSystemCursor(data->system_cursor)) {
return SDL_Unsupported();
}
}
return true;
} else {
// SDL error set inside Android_CreateEmptyCursor()
return false;
}
}
static bool Android_SetRelativeMouseMode(bool enabled)
{
if (!Android_JNI_SupportsRelativeMouse()) {
return SDL_Unsupported();
}
if (!Android_JNI_SetRelativeMouseEnabled(enabled)) {
return SDL_Unsupported();
}
return true;
}
void Android_InitMouse(void)
{
SDL_Mouse *mouse = SDL_GetMouse();
mouse->CreateCursor = Android_CreateCursor;
mouse->CreateSystemCursor = Android_CreateSystemCursor;
mouse->ShowCursor = Android_ShowCursor;
mouse->FreeCursor = Android_FreeCursor;
mouse->SetRelativeMouseMode = Android_SetRelativeMouseMode;
SDL_SetDefaultCursor(Android_CreateDefaultCursor());
last_state = 0;
}
void Android_QuitMouse(void)
{
Android_DestroyEmptyCursor();
}
// Translate Android mouse button state to SDL mouse button
static Uint8 TranslateButton(int state)
{
if (state & BUTTON_PRIMARY) {
return SDL_BUTTON_LEFT;
} else if (state & BUTTON_SECONDARY) {
return SDL_BUTTON_RIGHT;
} else if (state & BUTTON_TERTIARY) {
return SDL_BUTTON_MIDDLE;
} else if (state & BUTTON_FORWARD) {
return SDL_BUTTON_X1;
} else if (state & BUTTON_BACK) {
return SDL_BUTTON_X2;
} else {
return 0;
}
}
void Android_OnMouse(SDL_Window *window, int state, int action, float x, float y, bool relative)
{
int changes;
Uint8 button;
if (!window) {
return;
}
switch (action) {
case ACTION_DOWN:
changes = state & ~last_state;
button = TranslateButton(changes);
last_state = state;
SDL_SendMouseMotion(0, window, SDL_DEFAULT_MOUSE_ID, relative, x, y);
SDL_SendMouseButton(0, window, SDL_DEFAULT_MOUSE_ID, button, true);
break;
case ACTION_UP:
changes = last_state & ~state;
button = TranslateButton(changes);
last_state = state;
SDL_SendMouseMotion(0, window, SDL_DEFAULT_MOUSE_ID, relative, x, y);
SDL_SendMouseButton(0, window, SDL_DEFAULT_MOUSE_ID, button, false);
break;
case ACTION_MOVE:
case ACTION_HOVER_MOVE:
SDL_SendMouseMotion(0, window, SDL_DEFAULT_MOUSE_ID, relative, x, y);
break;
case ACTION_SCROLL:
SDL_SendMouseWheel(0, window, SDL_DEFAULT_MOUSE_ID, x, y, SDL_MOUSEWHEEL_NORMAL);
break;
default:
break;
}
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,31 @@
/*
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_androidmouse_h_
#define SDL_androidmouse_h_
#include "SDL_androidvideo.h"
extern void Android_InitMouse(void);
extern void Android_OnMouse(SDL_Window *window, int button, int action, float x, float y, bool relative);
extern void Android_QuitMouse(void);
#endif // SDL_androidmouse_h_

View file

@ -0,0 +1,98 @@
/*
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_ANDROID
#include "SDL_androidpen.h"
#include "../../events/SDL_pen_c.h"
#include "../../core/android/SDL_android.h"
#define ACTION_DOWN 0
#define ACTION_UP 1
#define ACTION_CANCEL 3
#define ACTION_POINTER_DOWN 5
#define ACTION_POINTER_UP 6
#define ACTION_HOVER_EXIT 10
void Android_OnPen(SDL_Window *window, int pen_id_in, int button, int action, float x, float y, float p)
{
if (!window) {
return;
}
// pointer index starts from zero.
pen_id_in++;
SDL_PenID pen = SDL_FindPenByHandle((void *) (size_t) pen_id_in);
if (!pen) {
// TODO: Query JNI for pen device info
SDL_PenInfo peninfo;
SDL_zero(peninfo);
peninfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ERASER;
peninfo.num_buttons = 2;
peninfo.subtype = SDL_PEN_TYPE_PEN;
pen = SDL_AddPenDevice(0, NULL, &peninfo, (void *) (size_t) pen_id_in);
if (!pen) {
SDL_Log("error: can't add a pen device %d", pen_id_in);
return;
}
}
SDL_SendPenMotion(0, pen, window, x, y);
SDL_SendPenAxis(0, pen, window, SDL_PEN_AXIS_PRESSURE, p);
// TODO: add more axis
SDL_PenInputFlags current = SDL_GetPenStatus(pen, NULL, 0);
int diff = current ^ button;
if (diff != 0) {
// Android only exposes BUTTON_STYLUS_PRIMARY and BUTTON_STYLUS_SECONDARY
if (diff & SDL_PEN_INPUT_BUTTON_1)
SDL_SendPenButton(0, pen, window, 1, (button & SDL_PEN_INPUT_BUTTON_1) != 0);
if (diff & SDL_PEN_INPUT_BUTTON_2)
SDL_SendPenButton(0, pen, window, 2, (button & SDL_PEN_INPUT_BUTTON_2) != 0);
}
// button contains DOWN/ERASER_TIP on DOWN/UP regardless of pressed state, use action to distinguish
// we don't compare tip flags above because MotionEvent.getButtonState doesn't return stylus tip/eraser state.
switch (action) {
case ACTION_CANCEL:
case ACTION_HOVER_EXIT:
SDL_RemovePenDevice(0, pen);
break;
case ACTION_DOWN:
case ACTION_POINTER_DOWN:
SDL_SendPenTouch(0, pen, window, (button & SDL_PEN_INPUT_ERASER_TIP) != 0, true);
break;
case ACTION_UP:
case ACTION_POINTER_UP:
SDL_SendPenTouch(0, pen, window, (button & SDL_PEN_INPUT_ERASER_TIP) != 0, false);
break;
default:
break;
}
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,25 @@
/*
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"
#include "SDL_androidvideo.h"
extern void Android_OnPen(SDL_Window *window, int pen_id_in, int button, int action, float x, float y, float p);

View file

@ -0,0 +1,96 @@
/*
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_ANDROID
#include <android/log.h>
#include "SDL_androidtouch.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h"
#include "../../core/android/SDL_android.h"
#define ACTION_DOWN 0
#define ACTION_UP 1
#define ACTION_MOVE 2
#define ACTION_CANCEL 3
// #define ACTION_OUTSIDE 4
#define ACTION_POINTER_DOWN 5
#define ACTION_POINTER_UP 6
void Android_InitTouch(void)
{
// Add all touch devices
Android_JNI_InitTouch();
}
void Android_QuitTouch(void)
{
}
void Android_OnTouch(SDL_Window *window, int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
{
SDL_TouchID touchDeviceId = 0;
SDL_FingerID fingerId = 0;
if (!window) {
return;
}
/* Touch device -1 appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
touchDeviceId = (SDL_TouchID)(touch_device_id_in + 2);
// Finger ID should be greater than 0
fingerId = (SDL_FingerID)(pointer_finger_id_in + 1);
if (SDL_AddTouch(touchDeviceId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) {
SDL_Log("error: can't add touch %s, %d", __FILE__, __LINE__);
}
switch (action) {
case ACTION_DOWN:
case ACTION_POINTER_DOWN:
SDL_SendTouch(0, touchDeviceId, fingerId, window, SDL_EVENT_FINGER_DOWN, x, y, p);
break;
case ACTION_MOVE:
SDL_SendTouchMotion(0, touchDeviceId, fingerId, window, x, y, p);
break;
case ACTION_UP:
case ACTION_POINTER_UP:
SDL_SendTouch(0, touchDeviceId, fingerId, window, SDL_EVENT_FINGER_UP, x, y, p);
break;
case ACTION_CANCEL:
SDL_SendTouch(0, touchDeviceId, fingerId, window, SDL_EVENT_FINGER_CANCELED, x, y, p);
break;
default:
break;
}
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,27 @@
/*
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"
#include "SDL_androidvideo.h"
extern void Android_InitTouch(void);
extern void Android_QuitTouch(void);
extern void Android_OnTouch(SDL_Window *window, int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p);

View file

@ -0,0 +1,331 @@
/*
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_ANDROID
// Android SDL video driver implementation
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_windowevents_c.h"
#include "SDL_androidvideo.h"
#include "SDL_androidgl.h"
#include "SDL_androidclipboard.h"
#include "SDL_androidevents.h"
#include "SDL_androidkeyboard.h"
#include "SDL_androidmouse.h"
#include "SDL_androidtouch.h"
#include "SDL_androidwindow.h"
#include "SDL_androidvulkan.h"
#include "SDL_androidmessagebox.h"
#define ANDROID_VID_DRIVER_NAME "android"
// Initialization/Query functions
static bool Android_VideoInit(SDL_VideoDevice *_this);
static void Android_VideoQuit(SDL_VideoDevice *_this);
#include "../SDL_egl_c.h"
#define Android_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
#define Android_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
#define Android_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
#define Android_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
#define Android_GLES_DestroyContext SDL_EGL_DestroyContext
// Android driver bootstrap functions
// These are filled in with real values in Android_SetScreenResolution on init (before SDL_main())
int Android_SurfaceWidth = 0;
int Android_SurfaceHeight = 0;
static int Android_DeviceWidth = 0;
static int Android_DeviceHeight = 0;
static Uint32 Android_ScreenFormat = SDL_PIXELFORMAT_RGB565; // Default SurfaceView format, in case this is queried before being filled
float Android_ScreenDensity = 1.0f;
static float Android_ScreenRate = 0.0f;
static SDL_DisplayOrientation Android_ScreenOrientation = SDL_ORIENTATION_UNKNOWN;
int Android_SafeInsetLeft = 0;
int Android_SafeInsetRight = 0;
int Android_SafeInsetTop = 0;
int Android_SafeInsetBottom = 0;
static SDL_SystemTheme Android_SystemTheme;
static bool Android_SuspendScreenSaver(SDL_VideoDevice *_this)
{
return Android_JNI_SuspendScreenSaver(_this->suspend_screensaver);
}
static void Android_DeleteDevice(SDL_VideoDevice *device)
{
SDL_free(device->internal);
SDL_free(device);
}
static SDL_VideoDevice *Android_CreateDevice(void)
{
SDL_VideoDevice *device;
SDL_VideoData *data;
// Initialize all variables that we clean on shutdown
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (!device) {
return NULL;
}
data = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData));
if (!data) {
SDL_free(device);
return NULL;
}
device->internal = data;
device->system_theme = Android_SystemTheme;
// Set the function pointers
device->VideoInit = Android_VideoInit;
device->VideoQuit = Android_VideoQuit;
device->CreateSDLWindow = Android_CreateWindow;
device->SetWindowTitle = Android_SetWindowTitle;
device->SetWindowFullscreen = Android_SetWindowFullscreen;
device->MinimizeWindow = Android_MinimizeWindow;
device->SetWindowResizable = Android_SetWindowResizable;
device->DestroyWindow = Android_DestroyWindow;
device->free = Android_DeleteDevice;
// GL pointers
#ifdef SDL_VIDEO_OPENGL_EGL
device->GL_LoadLibrary = Android_GLES_LoadLibrary;
device->GL_GetProcAddress = Android_GLES_GetProcAddress;
device->GL_UnloadLibrary = Android_GLES_UnloadLibrary;
device->GL_CreateContext = Android_GLES_CreateContext;
device->GL_MakeCurrent = Android_GLES_MakeCurrent;
device->GL_SetSwapInterval = Android_GLES_SetSwapInterval;
device->GL_GetSwapInterval = Android_GLES_GetSwapInterval;
device->GL_SwapWindow = Android_GLES_SwapWindow;
device->GL_DestroyContext = Android_GLES_DestroyContext;
#endif
#ifdef SDL_VIDEO_VULKAN
device->Vulkan_LoadLibrary = Android_Vulkan_LoadLibrary;
device->Vulkan_UnloadLibrary = Android_Vulkan_UnloadLibrary;
device->Vulkan_GetInstanceExtensions = Android_Vulkan_GetInstanceExtensions;
device->Vulkan_CreateSurface = Android_Vulkan_CreateSurface;
device->Vulkan_DestroySurface = Android_Vulkan_DestroySurface;
#endif
// Screensaver
device->SuspendScreenSaver = Android_SuspendScreenSaver;
// Screen keyboard
device->HasScreenKeyboardSupport = Android_HasScreenKeyboardSupport;
device->ShowScreenKeyboard = Android_ShowScreenKeyboard;
device->HideScreenKeyboard = Android_HideScreenKeyboard;
device->IsScreenKeyboardShown = Android_IsScreenKeyboardShown;
// Clipboard
device->SetClipboardText = Android_SetClipboardText;
device->GetClipboardText = Android_GetClipboardText;
device->HasClipboardText = Android_HasClipboardText;
device->device_caps = VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}
VideoBootStrap Android_bootstrap = {
ANDROID_VID_DRIVER_NAME, "SDL Android video driver",
Android_CreateDevice,
Android_ShowMessageBox,
false
};
bool Android_VideoInit(SDL_VideoDevice *_this)
{
SDL_VideoData *videodata = _this->internal;
SDL_DisplayID displayID;
SDL_VideoDisplay *display;
SDL_DisplayMode mode;
videodata->isPaused = false;
videodata->isPausing = false;
SDL_zero(mode);
mode.format = Android_ScreenFormat;
mode.w = Android_DeviceWidth;
mode.h = Android_DeviceHeight;
mode.refresh_rate = Android_ScreenRate;
displayID = SDL_AddBasicVideoDisplay(&mode);
if (displayID == 0) {
return false;
}
display = SDL_GetVideoDisplay(displayID);
display->natural_orientation = Android_JNI_GetDisplayNaturalOrientation();
display->current_orientation = Android_JNI_GetDisplayCurrentOrientation();
display->content_scale = Android_ScreenDensity;
Android_InitTouch();
Android_InitMouse();
// We're done!
return true;
}
void Android_VideoQuit(SDL_VideoDevice *_this)
{
Android_QuitMouse();
Android_QuitTouch();
}
void Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float density, float rate)
{
Android_SurfaceWidth = surfaceWidth;
Android_SurfaceHeight = surfaceHeight;
Android_DeviceWidth = deviceWidth;
Android_DeviceHeight = deviceHeight;
Android_ScreenDensity = (density > 0.0f) ? density : 1.0f;
Android_ScreenRate = rate;
}
static Uint32 format_to_pixelFormat(int format)
{
Uint32 pf;
if (format == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM) { // 1
pf = SDL_PIXELFORMAT_RGBA8888;
} else if (format == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM) { // 2
pf = SDL_PIXELFORMAT_RGBX8888;
} else if (format == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM) { // 3
pf = SDL_PIXELFORMAT_RGB24;
} else if (format == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM) { // 4
pf = SDL_PIXELFORMAT_RGB565;
} else if (format == 5) {
pf = SDL_PIXELFORMAT_BGRA8888;
} else if (format == 6) {
pf = SDL_PIXELFORMAT_RGBA5551;
} else if (format == 7) {
pf = SDL_PIXELFORMAT_RGBA4444;
} else if (format == 0x115) {
// HAL_PIXEL_FORMAT_BGR_565
pf = SDL_PIXELFORMAT_RGB565;
} else {
pf = SDL_PIXELFORMAT_UNKNOWN;
}
return pf;
}
void Android_SetFormat(int format_wanted, int format_got)
{
Uint32 pf_wanted;
Uint32 pf_got;
pf_wanted = format_to_pixelFormat(format_wanted);
pf_got = format_to_pixelFormat(format_got);
Android_ScreenFormat = pf_got;
SDL_Log("pixel format wanted %s (%d), got %s (%d)",
SDL_GetPixelFormatName(pf_wanted), format_wanted,
SDL_GetPixelFormatName(pf_got), format_got);
}
static void Android_SendOrientationUpdate(void)
{
/* If we've received a compatible resize event, update the
* orientation immediately, otherwise wait for the display
* resize event.
*/
SDL_VideoDevice *device = SDL_GetVideoDevice();
if (device && device->num_displays > 0) {
SDL_VideoDisplay *display = device->displays[0];
bool mode_landscape = (display->desktop_mode.w > display->desktop_mode.h);
bool sensor_landscape = (Android_ScreenOrientation == SDL_ORIENTATION_LANDSCAPE || Android_ScreenOrientation == SDL_ORIENTATION_LANDSCAPE_FLIPPED);
if (sensor_landscape == mode_landscape) {
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_ORIENTATION, Android_ScreenOrientation, 0);
}
}
}
void Android_SetOrientation(SDL_DisplayOrientation orientation)
{
Android_ScreenOrientation = orientation;
Android_SendOrientationUpdate();
}
void Android_SendResize(SDL_Window *window)
{
/*
Update the resolution of the desktop mode, so that the window
can be properly resized. The screen resolution change can for
example happen when the Activity enters or exits immersive mode,
which can happen after VideoInit().
*/
SDL_VideoDevice *device = SDL_GetVideoDevice();
if (device && device->num_displays > 0) {
SDL_VideoDisplay *display = device->displays[0];
SDL_DisplayMode desktop_mode;
SDL_zero(desktop_mode);
desktop_mode.format = Android_ScreenFormat;
desktop_mode.w = Android_DeviceWidth;
desktop_mode.h = Android_DeviceHeight;
desktop_mode.refresh_rate = Android_ScreenRate;
SDL_SetDesktopDisplayMode(display, &desktop_mode);
Android_SendOrientationUpdate();
}
if (window) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, Android_SurfaceWidth, Android_SurfaceHeight);
}
}
void Android_SetWindowSafeAreaInsets(int left, int right, int top, int bottom)
{
Android_SafeInsetLeft = left;
Android_SafeInsetRight = right;
Android_SafeInsetTop = top;
Android_SafeInsetBottom = bottom;
if (Android_Window) {
SDL_SetWindowSafeAreaInsets(Android_Window, left, right, top, bottom);
}
}
void Android_SetDarkMode(bool enabled)
{
SDL_VideoDevice *device = SDL_GetVideoDevice();
if (enabled) {
Android_SystemTheme = SDL_SYSTEM_THEME_DARK;
} else {
Android_SystemTheme = SDL_SYSTEM_THEME_LIGHT;
}
if (device) {
SDL_SetSystemTheme(Android_SystemTheme);
}
}
#endif // SDL_VIDEO_DRIVER_ANDROID

View file

@ -0,0 +1,52 @@
/*
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_androidvideo_h_
#define SDL_androidvideo_h_
#include "../SDL_sysvideo.h"
// Called by the JNI layer when the screen changes size or format
extern void Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float density, float rate);
extern void Android_SetFormat(int format_wanted, int format_got);
extern void Android_SetOrientation(SDL_DisplayOrientation orientation);
extern void Android_SendResize(SDL_Window *window);
extern void Android_SetWindowSafeAreaInsets(int left, int right, int top, int bottom);
extern void Android_SetDarkMode(bool enabled);
// Private display data
struct SDL_VideoData
{
int isPaused;
int isPausing;
};
extern int Android_SurfaceWidth;
extern int Android_SurfaceHeight;
extern float Android_ScreenDensity;
extern int Android_SafeInsetLeft;
extern int Android_SafeInsetRight;
extern int Android_SafeInsetTop;
extern int Android_SafeInsetBottom;
#endif // SDL_androidvideo_h_

View file

@ -0,0 +1,171 @@
/*
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 Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.c.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_ANDROID)
#include "../SDL_vulkan_internal.h"
#include "SDL_androidvideo.h"
#include "SDL_androidwindow.h"
#include "SDL_androidvulkan.h"
bool Android_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 i, extensionCount = 0;
bool hasSurfaceExtension = false;
bool hasAndroidSurfaceExtension = false;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan already loaded");
}
// Load the Vulkan loader library
if (!path) {
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
}
if (!path) {
path = "libvulkan.so";
}
_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_ANDROID_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasAndroidSurfaceExtension = true;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
} else if (!hasAndroidSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "extension");
goto fail;
}
return true;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return false;
}
void Android_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
{
if (_this->vulkan_config.loader_handle) {
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
}
}
char const* const* Android_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
Uint32 *count)
{
static const char *const extensionsForAndroid[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
};
if (count) {
*count = SDL_arraysize(extensionsForAndroid);
}
return extensionsForAndroid;
}
bool Android_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
SDL_WindowData *windowData = window->internal;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR =
(PFN_vkCreateAndroidSurfaceKHR)vkGetInstanceProcAddr(
instance,
"vkCreateAndroidSurfaceKHR");
VkAndroidSurfaceCreateInfoKHR createInfo;
VkResult result;
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
if (!vkCreateAndroidSurfaceKHR) {
return SDL_SetError(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
" extension is not enabled in the Vulkan instance.");
}
SDL_zero(createInfo);
createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.window = windowData->native_window;
result = vkCreateAndroidSurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
return SDL_SetError("vkCreateAndroidSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
}
return true;
}
void Android_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,52 @@
/*
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 Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.h.
*/
#include "SDL_internal.h"
#ifndef SDL_androidvulkan_h_
#define SDL_androidvulkan_h_
#include "../SDL_vulkan_internal.h"
#include "../SDL_sysvideo.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_ANDROID)
extern bool Android_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void Android_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
extern char const* const* Android_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
extern bool Android_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
extern void Android_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
#endif
#endif // SDL_androidvulkan_h_

View file

@ -0,0 +1,204 @@
/*
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_ANDROID
#include "../SDL_sysvideo.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_windowevents_c.h"
#include "../../core/android/SDL_android.h"
#include "SDL_androidvideo.h"
#include "SDL_androidevents.h"
#include "SDL_androidwindow.h"
// Currently only one window
SDL_Window *Android_Window = NULL;
bool Android_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
{
SDL_WindowData *data;
bool result = true;
if (!Android_WaitActiveAndLockActivity()) {
return false;
}
if (Android_Window) {
result = SDL_SetError("Android only supports one window");
goto endfunction;
}
// Set orientation
Android_JNI_SetOrientation(window->w, window->h, window->flags & SDL_WINDOW_RESIZABLE, SDL_GetHint(SDL_HINT_ORIENTATIONS));
// Adjust the window data to match the screen
window->x = 0;
window->y = 0;
window->w = Android_SurfaceWidth;
window->h = Android_SurfaceHeight;
// One window, it always has focus
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data));
if (!data) {
result = false;
goto endfunction;
}
data->native_window = Android_JNI_GetNativeWindow();
if (!data->native_window) {
SDL_free(data);
result = SDL_SetError("Could not fetch native window");
goto endfunction;
}
SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, data->native_window);
/* Do not create EGLSurface for Vulkan window since it will then make the window
incompatible with vkCreateAndroidSurfaceKHR */
#ifdef SDL_VIDEO_OPENGL_EGL
if (window->flags & SDL_WINDOW_OPENGL) {
data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)data->native_window);
if (data->egl_surface == EGL_NO_SURFACE) {
ANativeWindow_release(data->native_window);
SDL_free(data);
result = false;
goto endfunction;
}
}
SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER, data->egl_surface);
#endif
SDL_SetWindowSafeAreaInsets(window, Android_SafeInsetLeft, Android_SafeInsetRight, Android_SafeInsetTop, Android_SafeInsetBottom);
window->internal = data;
Android_Window = window;
endfunction:
Android_UnlockActivityMutex();
return result;
}
void Android_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
{
Android_JNI_SetActivityTitle(window->title);
}
SDL_FullscreenResult Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen)
{
Android_LockActivityMutex();
if (window == Android_Window) {
SDL_WindowData *data;
int old_w, old_h, new_w, new_h;
// If the window is being destroyed don't change visible state
if (!window->is_destroying) {
Android_JNI_SetWindowStyle(fullscreen);
}
/* Ensure our size matches reality after we've executed the window style change.
*
* It is possible that we've set width and height to the full-size display, but on
* Samsung DeX or Chromebooks or other windowed Android environemtns, our window may
* still not be the full display size.
*/
if (!SDL_IsDeXMode() && !SDL_IsChromebook()) {
goto endfunction;
}
data = window->internal;
if (!data || !data->native_window) {
if (data && !data->native_window) {
SDL_SetError("Missing native window");
}
goto endfunction;
}
old_w = window->w;
old_h = window->h;
new_w = ANativeWindow_getWidth(data->native_window);
new_h = ANativeWindow_getHeight(data->native_window);
if (new_w < 0 || new_h < 0) {
SDL_SetError("ANativeWindow_getWidth/Height() fails");
}
if (old_w != new_w || old_h != new_h) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, new_w, new_h);
}
}
endfunction:
Android_UnlockActivityMutex();
return SDL_FULLSCREEN_SUCCEEDED;
}
void Android_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
Android_JNI_MinizeWindow();
}
void Android_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable)
{
// Set orientation
Android_JNI_SetOrientation(window->w, window->h, window->flags & SDL_WINDOW_RESIZABLE, SDL_GetHint(SDL_HINT_ORIENTATIONS));
}
void Android_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
Android_LockActivityMutex();
if (window == Android_Window) {
Android_Window = NULL;
if (window->internal) {
SDL_WindowData *data = window->internal;
#ifdef SDL_VIDEO_OPENGL_EGL
if (data->egl_surface != EGL_NO_SURFACE) {
SDL_EGL_DestroySurface(_this, data->egl_surface);
}
#endif
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
SDL_free(window->internal);
window->internal = NULL;
}
}
Android_UnlockActivityMutex();
}
#endif // SDL_VIDEO_DRIVER_ANDROID

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.
*/
#include "SDL_internal.h"
#ifndef SDL_androidwindow_h_
#define SDL_androidwindow_h_
#include "../../core/android/SDL_android.h"
#include "../SDL_egl_c.h"
extern bool Android_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void Android_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_FullscreenResult Android_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
extern void Android_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Android_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
extern void Android_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_Window *Android_Window;
struct SDL_WindowData
{
#ifdef SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface;
EGLContext egl_context; // We use this to preserve the context when losing focus
int has_swap_interval; // Save/Restore the swap interval / vsync
int swap_interval;
#endif
bool backup_done;
ANativeWindow *native_window;
};
#endif // SDL_androidwindow_h_

View file

@ -0,0 +1,34 @@
/*
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_cocoaclipboard_h_
#define SDL_cocoaclipboard_h_
// Forward declaration
@class SDL_CocoaVideoData;
extern void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data);
extern bool Cocoa_SetClipboardData(SDL_VideoDevice *_this);
extern void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
extern bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
#endif // SDL_cocoaclipboard_h_

View file

@ -0,0 +1,262 @@
/*
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_COCOA
#include "SDL_cocoavideo.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_clipboardevents_c.h"
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
@interface Cocoa_PasteboardDataProvider : NSObject<NSPasteboardItemDataProvider>
{
SDL_ClipboardDataCallback m_callback;
void *m_userdata;
}
@end
@implementation Cocoa_PasteboardDataProvider
- (nullable instancetype)initWith:(SDL_ClipboardDataCallback)callback
userData:(void *)userdata
{
self = [super init];
if (!self) {
return self;
}
m_callback = callback;
m_userdata = userdata;
return self;
}
- (void)pasteboard:(NSPasteboard *)pasteboard
item:(NSPasteboardItem *)item
provideDataForType:(NSPasteboardType)type
{
@autoreleasepool {
size_t size = 0;
CFStringRef mimeType;
const void *callbackData;
NSData *data;
mimeType = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)type, kUTTagClassMIMEType);
callbackData = m_callback(m_userdata, [(__bridge NSString *)mimeType UTF8String], &size);
CFRelease(mimeType);
if (callbackData == NULL || size == 0) {
return;
}
data = [NSData dataWithBytes: callbackData length: size];
[item setData: data forType: type];
}
}
@end
static char **GetMimeTypes(int *pnformats)
{
char **new_mime_types = NULL;
*pnformats = 0;
int nformats = 0;
int formatsSz = 0;
NSArray<NSPasteboardItem *> *items = [[NSPasteboard generalPasteboard] pasteboardItems];
NSUInteger nitems = [items count];
if (nitems > 0) {
for (NSPasteboardItem *item in items) {
NSArray<NSString *> *types = [item types];
for (NSString *type in types) {
if (@available(macOS 11.0, *)) {
UTType *uttype = [UTType typeWithIdentifier:type];
NSString *mime_type = [uttype preferredMIMEType];
if (mime_type) {
NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
formatsSz += len;
++nformats;
}
}
NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
formatsSz += len;
++nformats;
}
}
new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz);
if (new_mime_types) {
int i = 0;
char *strPtr = (char *)(new_mime_types + nformats + 1);
for (NSPasteboardItem *item in items) {
NSArray<NSString *> *types = [item types];
for (NSString *type in types) {
if (@available(macOS 11.0, *)) {
UTType *uttype = [UTType typeWithIdentifier:type];
NSString *mime_type = [uttype preferredMIMEType];
if (mime_type) {
NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
SDL_memcpy(strPtr, [mime_type UTF8String], len);
new_mime_types[i++] = strPtr;
strPtr += len;
}
}
NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
SDL_memcpy(strPtr, [type UTF8String], len);
new_mime_types[i++] = strPtr;
strPtr += len;
}
}
new_mime_types[nformats] = NULL;
*pnformats = nformats;
}
}
return new_mime_types;
}
void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
{
@autoreleasepool {
NSPasteboard *pasteboard;
NSInteger count;
pasteboard = [NSPasteboard generalPasteboard];
count = [pasteboard changeCount];
if (count != data.clipboard_count) {
if (count) {
int nformats = 0;
char **new_mime_types = GetMimeTypes(&nformats);
if (new_mime_types) {
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
}
}
data.clipboard_count = count;
}
}
}
bool Cocoa_SetClipboardData(SDL_VideoDevice *_this)
{
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSPasteboardItem *newItem = [NSPasteboardItem new];
NSMutableArray *utiTypes = [NSMutableArray new];
Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata];
BOOL itemResult = FALSE;
BOOL writeResult = FALSE;
if (_this->clipboard_callback) {
for (int i = 0; i < _this->num_clipboard_mime_types; i++) {
CFStringRef mimeType = CFStringCreateWithCString(NULL, _this->clipboard_mime_types[i], kCFStringEncodingUTF8);
CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
CFRelease(mimeType);
[utiTypes addObject: (__bridge NSString *)utiType];
CFRelease(utiType);
}
itemResult = [newItem setDataProvider: provider forTypes: utiTypes];
if (itemResult == FALSE) {
return SDL_SetError("Unable to set clipboard item data");
}
[pasteboard clearContents];
writeResult = [pasteboard writeObjects: @[newItem]];
if (writeResult == FALSE) {
return SDL_SetError("Unable to set clipboard data");
}
} else {
[pasteboard clearContents];
}
data.clipboard_count = [pasteboard changeCount];
}
return true;
}
static bool IsMimeType(const char *tag)
{
if (SDL_strchr(tag, '/')) {
// MIME types have slashes
return true;
} else if (SDL_strchr(tag, '.')) {
// UTI identifiers have periods
return false;
} else {
// Not sure, but it's not a UTI identifier
return true;
}
}
static CFStringRef GetUTIType(const char *tag)
{
CFStringRef utiType;
if (IsMimeType(tag)) {
CFStringRef mimeType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
CFRelease(mimeType);
} else {
utiType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
}
return utiType;
}
void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
{
@autoreleasepool {
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
void *data = NULL;
*size = 0;
for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
NSData *itemData;
CFStringRef utiType = GetUTIType(mime_type);
itemData = [item dataForType: (__bridge NSString *)utiType];
CFRelease(utiType);
if (itemData != nil) {
NSUInteger length = [itemData length];
*size = (size_t)length;
data = SDL_malloc(*size + sizeof(Uint32));
if (data) {
[itemData getBytes: data length: length];
SDL_memset((Uint8 *)data + length, 0, sizeof(Uint32));
}
break;
}
}
return data;
}
}
bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
{
bool result = false;
@autoreleasepool {
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
CFStringRef utiType = GetUTIType(mime_type);
if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) {
result = true;
}
CFRelease(utiType);
}
return result;
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,33 @@
/*
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_cocoaevents_h_
#define SDL_cocoaevents_h_
extern void Cocoa_RegisterApp(void);
extern Uint64 Cocoa_GetEventTimestamp(NSTimeInterval nsTimestamp);
extern void Cocoa_PumpEvents(SDL_VideoDevice *_this);
extern int Cocoa_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
extern void Cocoa_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_SuspendScreenSaver(SDL_VideoDevice *_this);
#endif // SDL_cocoaevents_h_

View file

@ -0,0 +1,680 @@
/*
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_COCOA
#include "SDL_cocoavideo.h"
#include "../../events/SDL_events_c.h"
static SDL_Window *FindSDLWindowForNSWindow(NSWindow *win)
{
SDL_Window *sdlwindow = NULL;
SDL_VideoDevice *device = SDL_GetVideoDevice();
if (device && device->windows) {
for (sdlwindow = device->windows; sdlwindow; sdlwindow = sdlwindow->next) {
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)sdlwindow->internal).nswindow;
if (win == nswindow) {
return sdlwindow;
}
}
}
return sdlwindow;
}
@interface SDL3Application : NSApplication
- (void)terminate:(id)sender;
- (void)sendEvent:(NSEvent *)theEvent;
+ (void)registerUserDefaults;
@end
@implementation SDL3Application
// Override terminate to handle Quit and System Shutdown smoothly.
- (void)terminate:(id)sender
{
SDL_SendQuit();
}
static bool s_bShouldHandleEventsInSDLApplication = false;
static void Cocoa_DispatchEvent(NSEvent *theEvent)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
switch ([theEvent type]) {
case NSEventTypeLeftMouseDown:
case NSEventTypeOtherMouseDown:
case NSEventTypeRightMouseDown:
case NSEventTypeLeftMouseUp:
case NSEventTypeOtherMouseUp:
case NSEventTypeRightMouseUp:
case NSEventTypeLeftMouseDragged:
case NSEventTypeRightMouseDragged:
case NSEventTypeOtherMouseDragged: // usually middle mouse dragged
case NSEventTypeMouseMoved:
case NSEventTypeScrollWheel:
case NSEventTypeMouseEntered:
case NSEventTypeMouseExited:
Cocoa_HandleMouseEvent(_this, theEvent);
break;
case NSEventTypeKeyDown:
case NSEventTypeKeyUp:
case NSEventTypeFlagsChanged:
Cocoa_HandleKeyEvent(_this, theEvent);
break;
default:
break;
}
}
// Dispatch events here so that we can handle events caught by
// nextEventMatchingMask in SDL, as well as events caught by other
// processes (such as CEF) that are passed down to NSApp.
- (void)sendEvent:(NSEvent *)theEvent
{
if (s_bShouldHandleEventsInSDLApplication) {
Cocoa_DispatchEvent(theEvent);
}
[super sendEvent:theEvent];
}
+ (void)registerUserDefaults
{
BOOL momentumScrollSupported = (BOOL)SDL_GetHintBoolean(SDL_HINT_MAC_SCROLL_MOMENTUM, false);
NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithBool:momentumScrollSupported], @"AppleMomentumScrollSupported",
[NSNumber numberWithBool:YES], @"ApplePressAndHoldEnabled",
[NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
}
@end // SDL3Application
// setAppleMenu disappeared from the headers in 10.4
@interface NSApplication (NSAppleMenu)
- (void)setAppleMenu:(NSMenu *)menu;
@end
@interface SDL3AppDelegate : NSObject <NSApplicationDelegate>
{
@public
BOOL seenFirstActivate;
}
- (id)init;
- (void)localeDidChange:(NSNotification *)notification;
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app;
- (IBAction)menu:(id)sender;
@end
@implementation SDL3AppDelegate : NSObject
- (id)init
{
self = [super init];
if (self) {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
bool registerActivationHandlers = SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true);
seenFirstActivate = NO;
if (registerActivationHandlers) {
[center addObserver:self
selector:@selector(windowWillClose:)
name:NSWindowWillCloseNotification
object:nil];
[center addObserver:self
selector:@selector(focusSomeWindow:)
name:NSApplicationDidBecomeActiveNotification
object:nil];
[center addObserver:self
selector:@selector(screenParametersChanged:)
name:NSApplicationDidChangeScreenParametersNotification
object:nil];
}
[center addObserver:self
selector:@selector(localeDidChange:)
name:NSCurrentLocaleDidChangeNotification
object:nil];
[NSApp addObserver:self
forKeyPath:@"effectiveAppearance"
options:NSKeyValueObservingOptionInitial
context:nil];
}
return self;
}
- (void)dealloc
{
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:NSWindowWillCloseNotification object:nil];
[center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
[center removeObserver:self name:NSApplicationDidChangeScreenParametersNotification object:nil];
[center removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil];
[NSApp removeObserver:self forKeyPath:@"effectiveAppearance"];
// Remove our URL event handler only if we set it
if ([NSApp delegate] == self) {
[[NSAppleEventManager sharedAppleEventManager]
removeEventHandlerForEventClass:kInternetEventClass
andEventID:kAEGetURL];
}
}
- (void)windowWillClose:(NSNotification *)notification
{
NSWindow *win = (NSWindow *)[notification object];
if (![win isKeyWindow]) {
return;
}
// Don't do anything if this was not an SDL window that was closed
if (FindSDLWindowForNSWindow(win) == NULL) {
return;
}
/* HACK: Make the next window in the z-order key when the key window is
* closed. The custom event loop and/or windowing code we have seems to
* prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825
*/
/* +[NSApp orderedWindows] never includes the 'About' window, but we still
* want to try its list first since the behavior in other apps is to only
* make the 'About' window key if no other windows are on-screen.
*/
for (NSWindow *window in [NSApp orderedWindows]) {
if (window != win && [window canBecomeKeyWindow]) {
if (![window isOnActiveSpace]) {
continue;
}
[window makeKeyAndOrderFront:self];
return;
}
}
/* If a window wasn't found above, iterate through all visible windows in
* the active Space in z-order (including the 'About' window, if it's shown)
* and make the first one key.
*/
for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) {
NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]];
if (window && window != win && [window canBecomeKeyWindow]) {
[window makeKeyAndOrderFront:self];
return;
}
}
}
- (void)focusSomeWindow:(NSNotification *)aNotification
{
SDL_VideoDevice *device;
/* HACK: Ignore the first call. The application gets a
* applicationDidBecomeActive: a little bit after the first window is
* created, and if we don't ignore it, a window that has been created with
* SDL_WINDOW_MINIMIZED will ~immediately be restored.
*/
if (!seenFirstActivate) {
seenFirstActivate = YES;
return;
}
/* Don't do anything if the application already has a key window
* that is not an SDL window.
*/
if ([NSApp keyWindow] && FindSDLWindowForNSWindow([NSApp keyWindow]) == NULL) {
return;
}
device = SDL_GetVideoDevice();
if (device && device->windows) {
SDL_Window *window = device->windows;
int i;
for (i = 0; i < device->num_displays; ++i) {
SDL_Window *fullscreen_window = device->displays[i]->fullscreen_window;
if (fullscreen_window) {
if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) {
SDL_RestoreWindow(fullscreen_window);
}
return;
}
}
if (window->flags & SDL_WINDOW_MINIMIZED) {
SDL_RestoreWindow(window);
} else {
SDL_RaiseWindow(window);
}
}
}
- (void)screenParametersChanged:(NSNotification *)aNotification
{
SDL_VideoDevice *device = SDL_GetVideoDevice();
if (device) {
Cocoa_UpdateDisplays(device);
}
}
- (void)localeDidChange:(NSNotification *)notification
{
SDL_SendLocaleChangedEvent();
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
SDL_SetSystemTheme(Cocoa_GetSystemTheme());
}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
return (BOOL)SDL_SendDropFile(NULL, NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL);
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true))
return;
/* The menu bar of SDL apps which don't have the typical .app bundle
* structure fails to work the first time a window is created (until it's
* de-focused and re-focused), if this call is in Cocoa_RegisterApp instead
* of here. https://bugzilla.libsdl.org/show_bug.cgi?id=3051
*/
if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) {
// Get more aggressive for Catalina: activate the Dock first so we definitely reset all activation state.
for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
[i activateWithOptions:NSApplicationActivateIgnoringOtherApps];
break;
}
SDL_Delay(300); // !!! FIXME: this isn't right.
[NSApp activateIgnoringOtherApps:YES];
}
/* If we call this before NSApp activation, macOS might print a complaint
* about ApplePersistenceIgnoreState. */
[SDL3Application registerUserDefaults];
}
- (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSString *path = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
SDL_SendDropFile(NULL, NULL, [path UTF8String]);
SDL_SendDropComplete(NULL);
}
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app
{
// This just tells Cocoa that we didn't do any custom save state magic for the app,
// so the system is safe to use NSSecureCoding internally, instead of using unencrypted
// save states for backwards compatibility. If we don't return YES here, we'll get a
// warning on the console at startup:
//
// ```
// WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES.
// ```
//
// More-detailed explanation:
// https://stackoverflow.com/questions/77283578/sonoma-and-nsapplicationdelegate-applicationsupportssecurerestorablestate/77320845#77320845
return YES;
}
- (IBAction)menu:(id)sender
{
SDL_TrayEntry *entry = [[sender representedObject] pointerValue];
SDL_ClickTrayEntry(entry);
}
@end
static SDL3AppDelegate *appDelegate = nil;
static NSString *GetApplicationName(void)
{
NSString *appName = nil;
const char *metaname = SDL_GetStringProperty(SDL_GetGlobalProperties(), SDL_PROP_APP_METADATA_NAME_STRING, NULL);
if (metaname && *metaname) {
appName = [NSString stringWithUTF8String:metaname];
}
// Determine the application name
if (!appName) {
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
if (!appName) {
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
}
}
if (![appName length]) {
appName = [[NSProcessInfo processInfo] processName];
}
return appName;
}
static bool LoadMainMenuNibIfAvailable(void)
{
NSDictionary *infoDict;
NSString *mainNibFileName;
bool success = false;
infoDict = [[NSBundle mainBundle] infoDictionary];
if (infoDict) {
mainNibFileName = [infoDict valueForKey:@"NSMainNibFile"];
if (mainNibFileName) {
success = [[NSBundle mainBundle] loadNibNamed:mainNibFileName owner:[NSApplication sharedApplication] topLevelObjects:nil];
}
}
return success;
}
static void CreateApplicationMenus(void)
{
NSString *appName;
NSString *title;
NSMenu *appleMenu;
NSMenu *serviceMenu;
NSMenu *windowMenu;
NSMenuItem *menuItem;
NSMenu *mainMenu;
if (NSApp == nil) {
return;
}
mainMenu = [[NSMenu alloc] init];
// Create the main menu bar
[NSApp setMainMenu:mainMenu];
// Create the application menu
appName = GetApplicationName();
appleMenu = [[NSMenu alloc] initWithTitle:@""];
// Add menu items
title = [@"About " stringByAppendingString:appName];
// !!! FIXME: Menu items can't take parameters, just a basic selector, so this should instead call a selector
// !!! FIXME: that itself calls -[NSApplication orderFrontStandardAboutPanelWithOptions:optionsDictionary],
// !!! FIXME: filling in that NSDictionary with SDL_GetAppMetadataProperty()
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
[appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","];
[appleMenu addItem:[NSMenuItem separatorItem]];
serviceMenu = [[NSMenu alloc] initWithTitle:@""];
menuItem = [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""];
[menuItem setSubmenu:serviceMenu];
[NSApp setServicesMenu:serviceMenu];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Hide " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
menuItem = [appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Quit " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
// Put menu into the menubar
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
[[NSApp mainMenu] addItem:menuItem];
// Tell the application object that this is now the application menu
[NSApp setAppleMenu:appleMenu];
// Create the window menu
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
// Add menu items
[windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
[windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
[windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
// Add the fullscreen toggle menu option.
/* Cocoa should update the title to Enter or Exit Full Screen automatically.
* But if not, then just fallback to Toggle Full Screen.
*/
menuItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
[windowMenu addItem:menuItem];
// Put menu into the menubar
menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
[menuItem setSubmenu:windowMenu];
[[NSApp mainMenu] addItem:menuItem];
// Tell the application object that this is now the window menu
[NSApp setWindowsMenu:windowMenu];
}
void Cocoa_RegisterApp(void)
{
@autoreleasepool {
// This can get called more than once! Be careful what you initialize!
if (NSApp == nil) {
[SDL3Application sharedApplication];
SDL_assert(NSApp != nil);
s_bShouldHandleEventsInSDLApplication = true;
if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
}
/* If there aren't already menus in place, look to see if there's
* a nib we should use. If not, then manually create the basic
* menus we meed.
*/
if ([NSApp mainMenu] == nil) {
bool nibLoaded;
nibLoaded = LoadMainMenuNibIfAvailable();
if (!nibLoaded) {
CreateApplicationMenus();
}
}
[NSApp finishLaunching];
if ([NSApp delegate]) {
/* The SDL app delegate calls this in didFinishLaunching if it's
* attached to the NSApp, otherwise we need to call it manually.
*/
[SDL3Application registerUserDefaults];
}
}
if (NSApp && !appDelegate) {
appDelegate = [[SDL3AppDelegate alloc] init];
/* If someone else has an app delegate, it means we can't turn a
* termination into SDL_Quit, and we can't handle application:openFile:
*/
if (![NSApp delegate]) {
/* Only register the URL event handler if we are being set as the
* app delegate to avoid replacing any existing event handler.
*/
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:appDelegate
andSelector:@selector(handleURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
[(NSApplication *)NSApp setDelegate:appDelegate];
} else {
appDelegate->seenFirstActivate = YES;
}
}
}
}
Uint64 Cocoa_GetEventTimestamp(NSTimeInterval nsTimestamp)
{
static Uint64 timestamp_offset;
Uint64 timestamp = (Uint64)(nsTimestamp * SDL_NS_PER_SECOND);
Uint64 now = SDL_GetTicksNS();
if (!timestamp_offset) {
timestamp_offset = (now - timestamp);
}
timestamp += timestamp_offset;
if (timestamp > now) {
timestamp_offset -= (timestamp - now);
timestamp = now;
}
return timestamp;
}
int Cocoa_PumpEventsUntilDate(SDL_VideoDevice *_this, NSDate *expiration, bool accumulate)
{
// Run any existing modal sessions.
for (SDL_Window *w = _this->windows; w; w = w->next) {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)w->internal;
if (data.modal_session) {
[NSApp runModalSession:data.modal_session];
}
}
for (;;) {
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:expiration inMode:NSDefaultRunLoopMode dequeue:YES];
if (event == nil) {
return 0;
}
if (!s_bShouldHandleEventsInSDLApplication) {
Cocoa_DispatchEvent(event);
}
// Pass events down to SDL3Application to be handled in sendEvent:
[NSApp sendEvent:event];
if (!accumulate) {
break;
}
}
return 1;
}
int Cocoa_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
{
@autoreleasepool {
if (timeoutNS > 0) {
NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow:(double)timeoutNS / SDL_NS_PER_SECOND];
return Cocoa_PumpEventsUntilDate(_this, limitDate, false);
} else if (timeoutNS == 0) {
return Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], false);
} else {
while (Cocoa_PumpEventsUntilDate(_this, [NSDate distantFuture], false) == 0) {
}
}
return 1;
}
}
void Cocoa_PumpEvents(SDL_VideoDevice *_this)
{
@autoreleasepool {
Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], true);
}
}
void Cocoa_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:0.0
windowNumber:((__bridge SDL_CocoaWindowData *)window->internal).window_number
context:nil
subtype:0
data1:0
data2:0];
[NSApp postEvent:event atStart:YES];
}
}
bool Cocoa_SuspendScreenSaver(SDL_VideoDevice *_this)
{
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
if (data.screensaver_assertion) {
IOPMAssertionRelease(data.screensaver_assertion);
data.screensaver_assertion = kIOPMNullAssertionID;
}
if (_this->suspend_screensaver) {
/* FIXME: this should ideally describe the real reason why the game
* called SDL_DisableScreenSaver. Note that the name is only meant to be
* seen by macOS power users. there's an additional optional human-readable
* (localized) reason parameter which we don't set.
*/
IOPMAssertionID assertion = kIOPMNullAssertionID;
NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"];
IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep,
(__bridge CFStringRef)name,
NULL, NULL, NULL, 0, NULL,
&assertion);
data.screensaver_assertion = assertion;
}
}
return true;
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,36 @@
/*
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_cocoakeyboard_h_
#define SDL_cocoakeyboard_h_
extern void Cocoa_InitKeyboard(SDL_VideoDevice *_this);
extern void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event);
extern void Cocoa_QuitKeyboard(SDL_VideoDevice *_this);
extern bool Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern bool Cocoa_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
#endif // SDL_cocoakeyboard_h_

View file

@ -0,0 +1,604 @@
/*
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_COCOA
#include "SDL_cocoavideo.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/scancodes_darwin.h"
#include <Carbon/Carbon.h>
#if 0
#define DEBUG_IME NSLog
#else
#define DEBUG_IME(...)
#endif
@interface SDL3TranslatorResponder : NSView <NSTextInputClient>
{
NSString *_markedText;
NSRange _markedRange;
NSRange _selectedRange;
SDL_Rect _inputRect;
int _pendingRawCode;
SDL_Scancode _pendingScancode;
Uint64 _pendingTimestamp;
}
- (void)doCommandBySelector:(SEL)myselector;
- (void)setInputRect:(const SDL_Rect *)rect;
- (void)setPendingKey:(int)rawcode scancode:(SDL_Scancode)scancode timestamp:(Uint64)timestamp;
- (void)sendPendingKey;
- (void)clearPendingKey;
@end
@implementation SDL3TranslatorResponder
- (void)setInputRect:(const SDL_Rect *)rect
{
SDL_copyp(&_inputRect, rect);
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{
const char *str;
DEBUG_IME(@"insertText: %@ replacementRange: (%d, %d)", aString,
(int)replacementRange.location, (int)replacementRange.length);
/* Could be NSString or NSAttributedString, so we have
* to test and convert it before return as SDL event */
if ([aString isKindOfClass:[NSAttributedString class]]) {
str = [[aString string] UTF8String];
} else {
str = [aString UTF8String];
}
// We're likely sending the composed text, so we reset the IME status.
if ([self hasMarkedText]) {
[self unmarkText];
}
// Deliver the raw key event that generated this text
[self sendPendingKey];
if ((int)replacementRange.location != -1) {
// We're replacing the last character
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, true);
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, false);
}
SDL_SendKeyboardText(str);
}
- (void)doCommandBySelector:(SEL)myselector
{
/* No need to do anything since we are not using Cocoa
selectors to handle special keys, instead we use SDL
key events to do the same job.
*/
}
- (BOOL)hasMarkedText
{
return _markedText != nil;
}
- (NSRange)markedRange
{
return _markedRange;
}
- (NSRange)selectedRange
{
return _selectedRange;
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
if ([aString isKindOfClass:[NSAttributedString class]]) {
aString = [aString string];
}
if ([aString length] == 0) {
[self unmarkText];
return;
}
if (_markedText != aString) {
_markedText = aString;
}
_selectedRange = selectedRange;
_markedRange = NSMakeRange(0, [aString length]);
// This key event was consumed by the IME
[self clearPendingKey];
NSUInteger utf32SelectedRangeLocation = [[aString substringToIndex:selectedRange.location] lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
NSUInteger utf32SelectionRangeEnd = [[aString substringToIndex:(selectedRange.location + selectedRange.length)] lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
NSUInteger utf32SelectionRangeLength = utf32SelectionRangeEnd - utf32SelectedRangeLocation;
SDL_SendEditingText([aString UTF8String],
(int)utf32SelectedRangeLocation, (int)utf32SelectionRangeLength);
DEBUG_IME(@"setMarkedText: %@, (%d, %d) replacement range (%d, %d)", _markedText,
(int)selectedRange.location, (int)selectedRange.length,
(int)replacementRange.location, (int)replacementRange.length);
}
- (void)unmarkText
{
_markedText = nil;
// This key event was consumed by the IME
[self clearPendingKey];
SDL_SendEditingText("", 0, 0);
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
NSWindow *window = [self window];
NSRect contentRect = [window contentRectForFrameRect:[window frame]];
float windowHeight = contentRect.size.height;
NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h,
_inputRect.w, _inputRect.h);
if (actualRange) {
*actualRange = aRange;
}
DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@",
(int)aRange.location, (int)aRange.length, windowHeight,
NSStringFromRect(rect));
rect = [window convertRectToScreen:rect];
return rect;
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", (int)aRange.location, (int)aRange.length);
return nil;
}
- (NSInteger)conversationIdentifier
{
return (NSInteger)self;
}
/* This method returns the index for character that is
* nearest to thePoint. thPoint is in screen coordinate system.
*/
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y);
return 0;
}
/* This method is the key to attribute extension.
* We could add new attributes through this method.
* NSInputServer examines the return value of this
* method & constructs appropriate attributed string.
*/
- (NSArray *)validAttributesForMarkedText
{
return [NSArray array];
}
- (void)setPendingKey:(int)rawcode scancode:(SDL_Scancode)scancode timestamp:(Uint64)timestamp
{
_pendingRawCode = rawcode;
_pendingScancode = scancode;
_pendingTimestamp = timestamp;
}
- (void)sendPendingKey
{
if (_pendingRawCode < 0) {
return;
}
SDL_SendKeyboardKey(_pendingTimestamp, SDL_DEFAULT_KEYBOARD_ID, _pendingRawCode, _pendingScancode, true);
[self clearPendingKey];
}
- (void)clearPendingKey
{
_pendingRawCode = -1;
}
@end
static bool IsModifierKeyPressed(unsigned int flags,
unsigned int target_mask,
unsigned int other_mask,
unsigned int either_mask)
{
bool target_pressed = (flags & target_mask) != 0;
bool other_pressed = (flags & other_mask) != 0;
bool either_pressed = (flags & either_mask) != 0;
if (either_pressed != (target_pressed || other_pressed))
return either_pressed;
return target_pressed;
}
static void HandleModifiers(SDL_VideoDevice *_this, SDL_Scancode code, unsigned int modifierFlags)
{
bool pressed = false;
if (code == SDL_SCANCODE_LSHIFT) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELSHIFTKEYMASK,
NX_DEVICERSHIFTKEYMASK, NX_SHIFTMASK);
} else if (code == SDL_SCANCODE_LCTRL) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELCTLKEYMASK,
NX_DEVICERCTLKEYMASK, NX_CONTROLMASK);
} else if (code == SDL_SCANCODE_LALT) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELALTKEYMASK,
NX_DEVICERALTKEYMASK, NX_ALTERNATEMASK);
} else if (code == SDL_SCANCODE_LGUI) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELCMDKEYMASK,
NX_DEVICERCMDKEYMASK, NX_COMMANDMASK);
} else if (code == SDL_SCANCODE_RSHIFT) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERSHIFTKEYMASK,
NX_DEVICELSHIFTKEYMASK, NX_SHIFTMASK);
} else if (code == SDL_SCANCODE_RCTRL) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERCTLKEYMASK,
NX_DEVICELCTLKEYMASK, NX_CONTROLMASK);
} else if (code == SDL_SCANCODE_RALT) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERALTKEYMASK,
NX_DEVICELALTKEYMASK, NX_ALTERNATEMASK);
} else if (code == SDL_SCANCODE_RGUI) {
pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERCMDKEYMASK,
NX_DEVICELCMDKEYMASK, NX_COMMANDMASK);
} else {
return;
}
if (pressed) {
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, code, true);
} else {
SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, code, false);
}
}
static void UpdateKeymap(SDL_CocoaVideoData *data, bool send_event)
{
TISInputSourceRef key_layout;
UCKeyboardLayout *keyLayoutPtr = NULL;
CFDataRef uchrDataRef;
// See if the keymap needs to be updated
key_layout = TISCopyCurrentKeyboardLayoutInputSource();
if (key_layout == data.key_layout) {
return;
}
data.key_layout = key_layout;
// Try Unicode data first
uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData);
if (uchrDataRef) {
keyLayoutPtr = (UCKeyboardLayout *)CFDataGetBytePtr(uchrDataRef);
}
if (!keyLayoutPtr) {
CFRelease(key_layout);
return;
}
static struct {
int flags;
SDL_Keymod modstate;
} mods[] = {
{ 0, SDL_KMOD_NONE },
{ shiftKey, SDL_KMOD_SHIFT },
{ alphaLock, SDL_KMOD_CAPS },
{ (shiftKey | alphaLock), (SDL_KMOD_SHIFT | SDL_KMOD_CAPS) },
{ optionKey, SDL_KMOD_ALT },
{ (optionKey | shiftKey), (SDL_KMOD_ALT | SDL_KMOD_SHIFT) },
{ (optionKey | alphaLock), (SDL_KMOD_ALT | SDL_KMOD_CAPS) },
{ (optionKey | shiftKey | alphaLock), (SDL_KMOD_ALT | SDL_KMOD_SHIFT | SDL_KMOD_CAPS) }
};
UInt32 keyboard_type = LMGetKbdType();
SDL_Keymap *keymap = SDL_CreateKeymap();
for (int m = 0; m < SDL_arraysize(mods); ++m) {
for (int i = 0; i < SDL_arraysize(darwin_scancode_table); i++) {
OSStatus err;
UniChar s[8];
UniCharCount len;
UInt32 dead_key_state;
// Make sure this scancode is a valid character scancode
SDL_Scancode scancode = darwin_scancode_table[i];
if (scancode == SDL_SCANCODE_UNKNOWN ||
scancode == SDL_SCANCODE_DELETE ||
(SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & SDLK_SCANCODE_MASK)) {
continue;
}
/*
* Swap the scancode for these two wrongly translated keys
* UCKeyTranslate() function does not do its job properly for ISO layout keyboards, where the key '@',
* which is located in the top left corner of the keyboard right under the Escape key, and the additional
* key '<', which is on the right of the Shift key, are inverted
*/
if ((scancode == SDL_SCANCODE_NONUSBACKSLASH || scancode == SDL_SCANCODE_GRAVE) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
// see comments in scancodes_darwin.h
scancode = (SDL_Scancode)((SDL_SCANCODE_NONUSBACKSLASH + SDL_SCANCODE_GRAVE) - scancode);
}
dead_key_state = 0;
err = UCKeyTranslate(keyLayoutPtr, i, kUCKeyActionDown,
((mods[m].flags >> 8) & 0xFF), keyboard_type,
kUCKeyTranslateNoDeadKeysMask,
&dead_key_state, 8, &len, s);
if (err != noErr) {
continue;
}
if (len > 0 && s[0] != 0x10) {
SDL_SetKeymapEntry(keymap, scancode, mods[m].modstate, s[0]);
} else {
// The default keymap doesn't have any SDL_KMOD_ALT entries, so we don't need to override them
if (!(mods[m].modstate & SDL_KMOD_ALT)) {
SDL_SetKeymapEntry(keymap, scancode, mods[m].modstate, SDLK_UNKNOWN);
}
}
}
}
SDL_SetKeymap(keymap, send_event);
}
static void SDLCALL SDL_MacOptionAsAltChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata;
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
if (hint && *hint) {
if (SDL_strcmp(hint, "none") == 0) {
data.option_as_alt = OptionAsAltNone;
} else if (SDL_strcmp(hint, "only_left") == 0) {
data.option_as_alt = OptionAsAltOnlyLeft;
} else if (SDL_strcmp(hint, "only_right") == 0) {
data.option_as_alt = OptionAsAltOnlyRight;
} else if (SDL_strcmp(hint, "both") == 0) {
data.option_as_alt = OptionAsAltBoth;
}
} else {
data.option_as_alt = OptionAsAltNone;
}
}
void Cocoa_InitKeyboard(SDL_VideoDevice *_this)
{
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
UpdateKeymap(data, false);
// Set our own names for the platform-dependent but layout-independent keys
// This key is NumLock on the MacBook keyboard. :)
// SDL_SetScancodeName(SDL_SCANCODE_NUMLOCKCLEAR, "Clear");
SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option");
SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command");
SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option");
SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command");
data.modifierFlags = (unsigned int)[NSEvent modifierFlags];
SDL_ToggleModState(SDL_KMOD_CAPS, (data.modifierFlags & NSEventModifierFlagCapsLock) ? true : false);
SDL_AddHintCallback(SDL_HINT_MAC_OPTION_AS_ALT, SDL_MacOptionAsAltChanged, _this);
}
bool Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
{
@autoreleasepool {
NSView *parentView;
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->internal).nswindow;
parentView = [nswindow contentView];
/* We only keep one field editor per process, since only the front most
* window can receive text input events, so it make no sense to keep more
* than one copy. When we switched to another window and requesting for
* text input, simply remove the field editor from its superview then add
* it to the front most window's content view */
if (!data.fieldEdit) {
data.fieldEdit = [[SDL3TranslatorResponder alloc] initWithFrame:NSMakeRect(0.0, 0.0, 0.0, 0.0)];
}
if (![[data.fieldEdit superview] isEqual:parentView]) {
// DEBUG_IME(@"add fieldEdit to window contentView");
[data.fieldEdit removeFromSuperview];
[parentView addSubview:data.fieldEdit];
[nswindow makeFirstResponder:data.fieldEdit];
}
}
return Cocoa_UpdateTextInputArea(_this, window);
}
bool Cocoa_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
if (data && data.fieldEdit) {
[data.fieldEdit removeFromSuperview];
data.fieldEdit = nil;
}
}
return true;
}
bool Cocoa_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
if (data.fieldEdit) {
[data.fieldEdit setInputRect:&window->text_input_rect];
}
return true;
}
static NSEvent *ReplaceEvent(NSEvent *event, OptionAsAlt option_as_alt)
{
if (option_as_alt == OptionAsAltNone) {
return event;
}
const unsigned int modflags = (unsigned int)[event modifierFlags];
bool ignore_alt_characters = false;
bool lalt_pressed = IsModifierKeyPressed(modflags, NX_DEVICELALTKEYMASK,
NX_DEVICERALTKEYMASK, NX_ALTERNATEMASK);
bool ralt_pressed = IsModifierKeyPressed(modflags, NX_DEVICERALTKEYMASK,
NX_DEVICELALTKEYMASK, NX_ALTERNATEMASK);
if (option_as_alt == OptionAsAltOnlyLeft && lalt_pressed) {
ignore_alt_characters = true;
} else if (option_as_alt == OptionAsAltOnlyRight && ralt_pressed) {
ignore_alt_characters = true;
} else if (option_as_alt == OptionAsAltBoth && (lalt_pressed || ralt_pressed)) {
ignore_alt_characters = true;
}
bool cmd_pressed = modflags & NX_COMMANDMASK;
bool ctrl_pressed = modflags & NX_CONTROLMASK;
ignore_alt_characters = ignore_alt_characters && !cmd_pressed && !ctrl_pressed;
if (ignore_alt_characters) {
NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
return [NSEvent keyEventWithType:[event type]
location:[event locationInWindow]
modifierFlags:modflags
timestamp:[event timestamp]
windowNumber:[event windowNumber]
context:nil
characters:charactersIgnoringModifiers
charactersIgnoringModifiers:charactersIgnoringModifiers
isARepeat:[event isARepeat]
keyCode:[event keyCode]];
}
return event;
}
void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event)
{
unsigned short scancode;
SDL_Scancode code;
SDL_CocoaVideoData *data = _this ? ((__bridge SDL_CocoaVideoData *)_this->internal) : nil;
if (!data) {
return; // can happen when returning from fullscreen Space on shutdown
}
if ([event type] == NSEventTypeKeyDown || [event type] == NSEventTypeKeyUp) {
event = ReplaceEvent(event, data.option_as_alt);
}
scancode = [event keyCode];
if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) {
// see comments in scancodes_darwin.h
scancode = 60 - scancode;
}
if (scancode < SDL_arraysize(darwin_scancode_table)) {
code = darwin_scancode_table[scancode];
} else {
// Hmm, does this ever happen? If so, need to extend the keymap...
code = SDL_SCANCODE_UNKNOWN;
}
switch ([event type]) {
case NSEventTypeKeyDown:
if (![event isARepeat]) {
// See if we need to rebuild the keyboard layout
UpdateKeymap(data, true);
}
#ifdef DEBUG_SCANCODES
if (code == SDL_SCANCODE_UNKNOWN) {
SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list <https://discourse.libsdl.org/> or to Christian Walther <cwalther@gmx.ch>. Mac virtual key code is %d.", scancode);
}
#endif
if (SDL_TextInputActive(SDL_GetKeyboardFocus())) {
[data.fieldEdit setPendingKey:scancode scancode:code timestamp:Cocoa_GetEventTimestamp([event timestamp])];
[data.fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
[data.fieldEdit sendPendingKey];
} else if (SDL_GetKeyboardFocus()) {
SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, scancode, code, true);
}
break;
case NSEventTypeKeyUp:
SDL_SendKeyboardKey(Cocoa_GetEventTimestamp([event timestamp]), SDL_DEFAULT_KEYBOARD_ID, scancode, code, false);
break;
case NSEventTypeFlagsChanged: {
// see if the new modifierFlags mean any existing keys should be pressed/released...
const unsigned int modflags = (unsigned int)[event modifierFlags];
HandleModifiers(_this, SDL_SCANCODE_LSHIFT, modflags);
HandleModifiers(_this, SDL_SCANCODE_LCTRL, modflags);
HandleModifiers(_this, SDL_SCANCODE_LALT, modflags);
HandleModifiers(_this, SDL_SCANCODE_LGUI, modflags);
HandleModifiers(_this, SDL_SCANCODE_RSHIFT, modflags);
HandleModifiers(_this, SDL_SCANCODE_RCTRL, modflags);
HandleModifiers(_this, SDL_SCANCODE_RALT, modflags);
HandleModifiers(_this, SDL_SCANCODE_RGUI, modflags);
break;
}
default: // just to avoid compiler warnings
break;
}
}
void Cocoa_QuitKeyboard(SDL_VideoDevice *_this)
{
}
typedef int CGSConnection;
typedef enum
{
CGSGlobalHotKeyEnable = 0,
CGSGlobalHotKeyDisable = 1,
} CGSGlobalHotKeyOperatingMode;
extern CGSConnection _CGSDefaultConnection(void);
extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode);
bool Cocoa_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed)
{
#ifdef SDL_MAC_NO_SANDBOX
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), grabbed ? CGSGlobalHotKeyDisable : CGSGlobalHotKeyEnable);
#endif
return true;
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,27 @@
/*
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_COCOA
extern bool Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
#endif // SDL_VIDEO_DRIVER_COCOA

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.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_COCOA
#include "SDL_cocoavideo.h"
@interface SDL3MessageBoxPresenter : NSObject
{
@public
NSInteger clicked;
NSWindow *nswindow;
}
- (id)initWithParentWindow:(SDL_Window *)window;
@end
@implementation SDL3MessageBoxPresenter
- (id)initWithParentWindow:(SDL_Window *)window
{
self = [super init];
if (self) {
clicked = -1;
// Retain the NSWindow because we'll show the alert later on the main thread
if (window) {
nswindow = ((__bridge SDL_CocoaWindowData *)window->internal).nswindow;
} else {
nswindow = nil;
}
}
return self;
}
- (void)showAlert:(NSAlert *)alert
{
if (nswindow) {
[alert beginSheetModalForWindow:nswindow
completionHandler:^(NSModalResponse returnCode) {
[NSApp stopModalWithCode:returnCode];
}];
clicked = [NSApp runModalForWindow:nswindow];
nswindow = nil;
} else {
clicked = [alert runModal];
}
}
@end
static void Cocoa_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID, bool *result)
{
NSAlert *alert;
const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
SDL3MessageBoxPresenter *presenter;
NSInteger clicked;
int i;
Cocoa_RegisterApp();
alert = [[NSAlert alloc] init];
if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
[alert setAlertStyle:NSAlertStyleCritical];
} else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
[alert setAlertStyle:NSAlertStyleWarning];
} else {
[alert setAlertStyle:NSAlertStyleInformational];
}
[alert setMessageText:[NSString stringWithUTF8String:messageboxdata->title]];
[alert setInformativeText:[NSString stringWithUTF8String:messageboxdata->message]];
for (i = 0; i < messageboxdata->numbuttons; ++i) {
const SDL_MessageBoxButtonData *sdlButton;
NSButton *button;
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
} else {
sdlButton = &messageboxdata->buttons[i];
}
button = [alert addButtonWithTitle:[NSString stringWithUTF8String:sdlButton->text]];
if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
[button setKeyEquivalent:@"\r"];
} else if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
[button setKeyEquivalent:@"\033"];
} else {
[button setKeyEquivalent:@""];
}
}
presenter = [[SDL3MessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window];
[presenter showAlert:alert];
clicked = presenter->clicked;
if (clicked >= NSAlertFirstButtonReturn) {
clicked -= NSAlertFirstButtonReturn;
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
clicked = messageboxdata->numbuttons - 1 - clicked;
}
*buttonID = buttons[clicked].buttonID;
*result = true;
} else {
*result = SDL_SetError("Did not get a valid `clicked button' id: %ld", (long)clicked);
}
}
// Display a Cocoa message box
bool Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
@autoreleasepool {
__block bool result = 0;
if ([NSThread isMainThread]) {
Cocoa_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
Cocoa_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
});
}
return result;
}
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,66 @@
/*
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 Mark Callow, www.edgewise-consulting.com.
*
* Thanks to @slime73 on GitHub for their gist showing how to add a CAMetalLayer
* backed view.
*/
#include "SDL_internal.h"
#ifndef SDL_cocoametalview_h_
#define SDL_cocoametalview_h_
#if defined(SDL_VIDEO_DRIVER_COCOA) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
#import "../SDL_sysvideo.h"
#import "SDL_cocoawindow.h"
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
@interface SDL3_cocoametalview : NSView
- (instancetype)initWithFrame:(NSRect)frame
highDPI:(BOOL)highDPI
windowID:(Uint32)windowID
opaque:(BOOL)opaque;
- (void)updateDrawableSize;
- (NSView *)hitTest:(NSPoint)point;
// Override superclass tag so this class can set it.
@property(assign, readonly) NSInteger tag;
@property(nonatomic) BOOL highDPI;
@property(nonatomic) Uint32 sdlWindowID;
@end
SDL_MetalView Cocoa_Metal_CreateView(SDL_VideoDevice *_this, SDL_Window *window);
void Cocoa_Metal_DestroyView(SDL_VideoDevice *_this, SDL_MetalView view);
void *Cocoa_Metal_GetLayer(SDL_VideoDevice *_this, SDL_MetalView view);
#endif // SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL)
#endif // SDL_cocoametalview_h_

View file

@ -0,0 +1,182 @@
/*
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 Mark Callow, www.edgewise-consulting.com.
*
* Thanks to @slime73 on GitHub for their gist showing how to add a CAMetalLayer
* backed view.
*/
#include "SDL_internal.h"
#include "../../events/SDL_windowevents_c.h"
#import "SDL_cocoametalview.h"
#if defined(SDL_VIDEO_DRIVER_COCOA) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
static bool SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
{
/* Update the drawable size when SDL receives a size changed event for
* the window that contains the metal view. It would be nice to use
* - (void)resizeWithOldSuperviewSize:(NSSize)oldSize and
* - (void)viewDidChangeBackingProperties instead, but SDL's size change
* events don't always happen in the same frame (for example when a
* resizable window exits a fullscreen Space via the user pressing the OS
* exit-space button). */
if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
@autoreleasepool {
SDL3_cocoametalview *view = (__bridge SDL3_cocoametalview *)userdata;
if (view.sdlWindowID == event->window.windowID) {
[view updateDrawableSize];
}
}
}
return false;
}
@implementation SDL3_cocoametalview
// Return a Metal-compatible layer.
+ (Class)layerClass
{
return NSClassFromString(@"CAMetalLayer");
}
// Indicate the view wants to draw using a backing layer instead of drawRect.
- (BOOL)wantsUpdateLayer
{
return YES;
}
/* When the wantsLayer property is set to YES, this method will be invoked to
* return a layer instance.
*/
- (CALayer *)makeBackingLayer
{
return [self.class.layerClass layer];
}
- (instancetype)initWithFrame:(NSRect)frame
highDPI:(BOOL)highDPI
windowID:(Uint32)windowID
opaque:(BOOL)opaque
{
self = [super initWithFrame:frame];
if (self != nil) {
self.highDPI = highDPI;
self.sdlWindowID = windowID;
self.wantsLayer = YES;
// Allow resize.
self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
self.layer.opaque = opaque;
SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
[self updateDrawableSize];
}
return self;
}
- (void)dealloc
{
SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
}
- (NSInteger)tag
{
return SDL_METALVIEW_TAG;
}
- (void)updateDrawableSize
{
CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
NSSize size = self.bounds.size;
NSSize backingSize = size;
if (self.highDPI) {
/* Note: NSHighResolutionCapable must be set to true in the app's
* Info.plist in order for the backing size to be high res.
*/
backingSize = [self convertSizeToBacking:size];
}
metalLayer.contentsScale = backingSize.height / size.height;
metalLayer.drawableSize = NSSizeToCGSize(backingSize);
}
- (NSView *)hitTest:(NSPoint)point
{
return nil;
}
@end
SDL_MetalView Cocoa_Metal_CreateView(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
NSView *view = data.nswindow.contentView;
BOOL highDPI = (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) != 0;
BOOL opaque = (window->flags & SDL_WINDOW_TRANSPARENT) == 0;
Uint32 windowID = SDL_GetWindowID(window);
SDL3_cocoametalview *newview;
SDL_MetalView metalview;
newview = [[SDL3_cocoametalview alloc] initWithFrame:view.frame
highDPI:highDPI
windowID:windowID
opaque:opaque];
if (newview == nil) {
SDL_OutOfMemory();
return NULL;
}
[view addSubview:newview];
// Make sure the drawable size is up to date after attaching the view.
[newview updateDrawableSize];
metalview = (SDL_MetalView)CFBridgingRetain(newview);
return metalview;
}
}
void Cocoa_Metal_DestroyView(SDL_VideoDevice *_this, SDL_MetalView view)
{
@autoreleasepool {
SDL3_cocoametalview *metalview = CFBridgingRelease(view);
[metalview removeFromSuperview];
}
}
void *Cocoa_Metal_GetLayer(SDL_VideoDevice *_this, SDL_MetalView view)
{
@autoreleasepool {
SDL3_cocoametalview *cocoaview = (__bridge SDL3_cocoametalview *)view;
return (__bridge void *)cocoaview.layer;
}
}
#endif // SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL)

View file

@ -0,0 +1,45 @@
/*
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_cocoamodes_h_
#define SDL_cocoamodes_h_
struct SDL_DisplayData
{
CGDirectDisplayID display;
};
struct SDL_DisplayModeData
{
CFMutableArrayRef modes;
};
extern void Cocoa_InitModes(SDL_VideoDevice *_this);
extern void Cocoa_UpdateDisplays(SDL_VideoDevice *_this);
extern bool Cocoa_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
extern bool Cocoa_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
extern bool Cocoa_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern bool Cocoa_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern void Cocoa_QuitModes(SDL_VideoDevice *_this);
extern SDL_VideoDisplay *Cocoa_FindSDLDisplayByCGDirectDisplayID(SDL_VideoDevice *_this, CGDirectDisplayID displayid);
#endif // SDL_cocoamodes_h_

View file

@ -0,0 +1,718 @@
/*
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_COCOA
#include "SDL_cocoavideo.h"
#include "../../events/SDL_events_c.h"
// We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName
#include <IOKit/graphics/IOGraphicsLib.h>
// We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod
#include <CoreVideo/CVBase.h>
#include <CoreVideo/CVDisplayLink.h>
#if (IOGRAPHICSTYPES_REV < 40)
#define kDisplayModeNativeFlag 0x02000000
#endif
static bool CG_SetError(const char *prefix, CGDisplayErr result)
{
const char *error;
switch (result) {
case kCGErrorFailure:
error = "kCGErrorFailure";
break;
case kCGErrorIllegalArgument:
error = "kCGErrorIllegalArgument";
break;
case kCGErrorInvalidConnection:
error = "kCGErrorInvalidConnection";
break;
case kCGErrorInvalidContext:
error = "kCGErrorInvalidContext";
break;
case kCGErrorCannotComplete:
error = "kCGErrorCannotComplete";
break;
case kCGErrorNotImplemented:
error = "kCGErrorNotImplemented";
break;
case kCGErrorRangeCheck:
error = "kCGErrorRangeCheck";
break;
case kCGErrorTypeCheck:
error = "kCGErrorTypeCheck";
break;
case kCGErrorInvalidOperation:
error = "kCGErrorInvalidOperation";
break;
case kCGErrorNoneAvailable:
error = "kCGErrorNoneAvailable";
break;
default:
error = "Unknown Error";
break;
}
return SDL_SetError("%s: %s", prefix, error);
}
static NSScreen *GetNSScreenForDisplayID(CGDirectDisplayID displayID)
{
NSArray *screens = [NSScreen screens];
// !!! FIXME: maybe track the NSScreen in SDL_DisplayData?
for (NSScreen *screen in screens) {
const CGDirectDisplayID thisDisplay = (CGDirectDisplayID)[[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
if (thisDisplay == displayID) {
return screen;
}
}
return nil;
}
SDL_VideoDisplay *Cocoa_FindSDLDisplayByCGDirectDisplayID(SDL_VideoDevice *_this, CGDirectDisplayID displayid)
{
for (int i = 0; i < _this->num_displays; i++) {
const SDL_DisplayData *displaydata = _this->displays[i]->internal;
if (displaydata && (displaydata->display == displayid)) {
return _this->displays[i];
}
}
return NULL;
}
static float GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link)
{
float refreshRate = (float)CGDisplayModeGetRefreshRate(vidmode);
// CGDisplayModeGetRefreshRate can return 0 (eg for built-in displays).
if (refreshRate == 0 && link != NULL) {
CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
refreshRate = (float)time.timeScale / time.timeValue;
}
}
return refreshRate;
}
static bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode)
{
uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
// Filter out modes which have flags that we don't want.
if (ioflags & (kDisplayModeNeverShowFlag | kDisplayModeNotGraphicsQualityFlag)) {
return false;
}
// Filter out modes which don't have flags that we want.
if (!(ioflags & kDisplayModeValidFlag) || !(ioflags & kDisplayModeSafeFlag)) {
return false;
}
return true;
}
static Uint32 GetDisplayModePixelFormat(CGDisplayModeRef vidmode)
{
// This API is deprecated in 10.11 with no good replacement (as of 10.15).
CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
Uint32 pixelformat = SDL_PIXELFORMAT_UNKNOWN;
if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
pixelformat = SDL_PIXELFORMAT_ARGB8888;
} else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
pixelformat = SDL_PIXELFORMAT_ARGB1555;
} else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
pixelformat = SDL_PIXELFORMAT_ARGB2101010;
} else {
// ignore 8-bit and such for now.
}
CFRelease(fmt);
return pixelformat;
}
static bool GetDisplayMode(CGDisplayModeRef vidmode, bool vidmodeCurrent, CFArrayRef modelist, CVDisplayLinkRef link, SDL_DisplayMode *mode)
{
SDL_DisplayModeData *data;
bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode);
size_t width = CGDisplayModeGetWidth(vidmode);
size_t height = CGDisplayModeGetHeight(vidmode);
size_t pixelW = width;
size_t pixelH = height;
uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
float refreshrate = GetDisplayModeRefreshRate(vidmode, link);
Uint32 format = GetDisplayModePixelFormat(vidmode);
bool interlaced = (ioflags & kDisplayModeInterlacedFlag) != 0;
CFMutableArrayRef modes;
if (format == SDL_PIXELFORMAT_UNKNOWN) {
return false;
}
/* Don't fail the current mode based on flags because this could prevent Cocoa_InitModes from
* succeeding if the current mode lacks certain flags (esp kDisplayModeSafeFlag). */
if (!vidmodeCurrent && !HasValidDisplayModeFlags(vidmode)) {
return false;
}
modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(modes, vidmode);
/* If a list of possible display modes is passed in, use it to filter out
* modes that have duplicate sizes. We don't just rely on SDL's higher level
* duplicate filtering because this code can choose what properties are
* preferred, and it can add CGDisplayModes to the DisplayModeData's list of
* modes to try (see comment below for why that's necessary). */
pixelW = CGDisplayModeGetPixelWidth(vidmode);
pixelH = CGDisplayModeGetPixelHeight(vidmode);
if (modelist != NULL) {
CFIndex modescount = CFArrayGetCount(modelist);
int i;
for (i = 0; i < modescount; i++) {
size_t otherW, otherH, otherpixelW, otherpixelH;
float otherrefresh;
Uint32 otherformat;
bool otherGUI;
CGDisplayModeRef othermode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modelist, i);
uint32_t otherioflags = CGDisplayModeGetIOFlags(othermode);
if (CFEqual(vidmode, othermode)) {
continue;
}
if (!HasValidDisplayModeFlags(othermode)) {
continue;
}
otherW = CGDisplayModeGetWidth(othermode);
otherH = CGDisplayModeGetHeight(othermode);
otherpixelW = CGDisplayModeGetPixelWidth(othermode);
otherpixelH = CGDisplayModeGetPixelHeight(othermode);
otherrefresh = GetDisplayModeRefreshRate(othermode, link);
otherformat = GetDisplayModePixelFormat(othermode);
otherGUI = CGDisplayModeIsUsableForDesktopGUI(othermode);
/* Ignore this mode if it's interlaced and there's a non-interlaced
* mode in the list with the same properties.
*/
if (interlaced && ((otherioflags & kDisplayModeInterlacedFlag) == 0) && width == otherW && height == otherH && pixelW == otherpixelW && pixelH == otherpixelH && refreshrate == otherrefresh && format == otherformat && usableForGUI == otherGUI) {
CFRelease(modes);
return false;
}
/* Ignore this mode if it's not usable for desktop UI and its
* properties are equal to another GUI-capable mode in the list.
*/
if (width == otherW && height == otherH && pixelW == otherpixelW && pixelH == otherpixelH && !usableForGUI && otherGUI && refreshrate == otherrefresh && format == otherformat) {
CFRelease(modes);
return false;
}
/* If multiple modes have the exact same properties, they'll all
* go in the list of modes to try when SetDisplayMode is called.
* This is needed because kCGDisplayShowDuplicateLowResolutionModes
* (which is used to expose highdpi display modes) can make the
* list of modes contain duplicates (according to their properties
* obtained via public APIs) which don't work with SetDisplayMode.
* Those duplicate non-functional modes *do* have different pixel
* formats according to their internal data structure viewed with
* NSLog, but currently no public API can detect that.
* https://bugzilla.libsdl.org/show_bug.cgi?id=4822
*
* As of macOS 10.15.0, those duplicates have the exact same
* properties via public APIs in every way (even their IO flags and
* CGDisplayModeGetIODisplayModeID is the same), so we could test
* those for equality here too, but I'm intentionally not doing that
* in case there are duplicate modes with different IO flags or IO
* display mode IDs in the future. In that case I think it's better
* to try them all in SetDisplayMode than to risk one of them being
* correct but it being filtered out by SDL_AddFullscreenDisplayMode
* as being a duplicate.
*/
if (width == otherW && height == otherH && pixelW == otherpixelW && pixelH == otherpixelH && usableForGUI == otherGUI && refreshrate == otherrefresh && format == otherformat) {
CFArrayAppendValue(modes, othermode);
}
}
}
SDL_zerop(mode);
data = (SDL_DisplayModeData *)SDL_malloc(sizeof(*data));
if (!data) {
CFRelease(modes);
return false;
}
data->modes = modes;
mode->format = format;
mode->w = (int)width;
mode->h = (int)height;
mode->pixel_density = (float)pixelW / width;
mode->refresh_rate = refreshrate;
mode->internal = data;
return true;
}
static char *Cocoa_GetDisplayName(CGDirectDisplayID displayID)
{
if (@available(macOS 10.15, *)) {
NSScreen *screen = GetNSScreenForDisplayID(displayID);
if (screen) {
const char *name = [screen.localizedName UTF8String];
if (name) {
return SDL_strdup(name);
}
}
}
// This API is deprecated in 10.9 with no good replacement (as of 10.15).
io_service_t servicePort = CGDisplayIOServicePort(displayID);
CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName);
NSDictionary *localizedNames = [(__bridge NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
char *displayName = NULL;
if ([localizedNames count] > 0) {
displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
}
CFRelease(deviceInfo);
return displayName;
}
static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDROutputProperties *HDR)
{
HDR->SDR_white_level = 1.0f;
HDR->HDR_headroom = 1.0f;
if (@available(macOS 10.15, *)) {
NSScreen *screen = GetNSScreenForDisplayID(displayID);
if (screen) {
if (screen.maximumExtendedDynamicRangeColorComponentValue > 1.0f) {
HDR->HDR_headroom = screen.maximumExtendedDynamicRangeColorComponentValue;
} else {
HDR->HDR_headroom = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
}
}
}
}
bool Cocoa_AddDisplay(CGDirectDisplayID display, bool send_event)
{
CGDisplayModeRef moderef = CGDisplayCopyDisplayMode(display);
if (!moderef) {
return false;
}
SDL_DisplayData *displaydata = (SDL_DisplayData *)SDL_malloc(sizeof(*displaydata));
if (!displaydata) {
CGDisplayModeRelease(moderef);
return false;
}
displaydata->display = display;
CVDisplayLinkRef link = NULL;
CVDisplayLinkCreateWithCGDisplay(display, &link);
SDL_VideoDisplay viddisplay;
SDL_zero(viddisplay);
viddisplay.name = Cocoa_GetDisplayName(display); // this returns a strdup'ed string
SDL_DisplayMode mode;
if (!GetDisplayMode(moderef, true, NULL, link, &mode)) {
CVDisplayLinkRelease(link);
CGDisplayModeRelease(moderef);
SDL_free(viddisplay.name);
SDL_free(displaydata);
return false;
}
CVDisplayLinkRelease(link);
CGDisplayModeRelease(moderef);
Cocoa_GetHDRProperties(displaydata->display, &viddisplay.HDR);
viddisplay.desktop_mode = mode;
viddisplay.internal = displaydata;
const bool retval = SDL_AddVideoDisplay(&viddisplay, send_event);
SDL_free(viddisplay.name);
return retval;
}
static void Cocoa_DisplayReconfigurationCallback(CGDirectDisplayID displayid, CGDisplayChangeSummaryFlags flags, void *userInfo)
{
#if 0
SDL_Log("COCOA DISPLAY RECONFIG CALLBACK! display=%u", (unsigned int) displayid);
#define CHECK_DISPLAY_RECONFIG_FLAG(x) if (flags & x) { SDL_Log(" - " #x); }
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayBeginConfigurationFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayMovedFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplaySetMainFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplaySetModeFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayAddFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayRemoveFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayEnabledFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayDisabledFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayMirrorFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayUnMirrorFlag);
CHECK_DISPLAY_RECONFIG_FLAG(kCGDisplayDesktopShapeChangedFlag);
#undef CHECK_DISPLAY_RECONFIG_FLAG
#endif
SDL_VideoDevice *_this = (SDL_VideoDevice *) userInfo;
SDL_VideoDisplay *display = Cocoa_FindSDLDisplayByCGDirectDisplayID(_this, displayid); // will be NULL for newly-added (or newly-unmirrored) displays!
if (flags & kCGDisplayDisabledFlag) {
flags |= kCGDisplayRemoveFlag; // treat this like a display leaving, even though it's still plugged in.
}
if (flags & kCGDisplayEnabledFlag) {
flags |= kCGDisplayAddFlag; // treat this like a display leaving, even though it's still plugged in.
}
if (flags & kCGDisplayMirrorFlag) {
flags |= kCGDisplayRemoveFlag; // treat this like a display leaving, even though it's still actually here.
}
if (flags & kCGDisplayUnMirrorFlag) {
flags |= kCGDisplayAddFlag; // treat this like a new display arriving, even though it was here all along.
}
if ((flags & kCGDisplayAddFlag) && (flags & kCGDisplayRemoveFlag)) {
// sometimes you get a removed device that gets Add and Remove flags at the same time but the display dimensions are 0x0 or 1x1, hence the `> 1` test.
// Mirrored things are always removed, since they don't represent a discrete display in this state.
if (((flags & kCGDisplayMirrorFlag) == 0) && (CGDisplayPixelsWide(displayid) > 1)) {
// Final state is connected
flags &= ~kCGDisplayRemoveFlag;
} else {
// Final state is disconnected
flags &= ~kCGDisplayAddFlag;
}
}
if (flags & kCGDisplayAddFlag) {
if (!display) {
if (!Cocoa_AddDisplay(displayid, true)) {
return; // oh well.
}
display = Cocoa_FindSDLDisplayByCGDirectDisplayID(_this, displayid);
SDL_assert(display != NULL);
}
}
if (flags & kCGDisplayRemoveFlag) {
if (display) {
SDL_DelVideoDisplay(display->id, true);
display = NULL;
}
}
if (flags & kCGDisplaySetModeFlag) {
if (display) {
CGDisplayModeRef moderef = CGDisplayCopyDisplayMode(displayid);
if (moderef) {
CVDisplayLinkRef link = NULL;
CVDisplayLinkCreateWithCGDisplay(displayid, &link);
if (link) {
SDL_DisplayMode mode;
if (GetDisplayMode(moderef, true, NULL, link, &mode)) {
SDL_SetDesktopDisplayMode(display, &mode);
}
CVDisplayLinkRelease(link);
}
CGDisplayModeRelease(moderef);
}
}
}
if (flags & kCGDisplaySetMainFlag) {
if (display) {
for (int i = 0; i < _this->num_displays; i++) {
if (_this->displays[i] == display) {
if (i > 0) {
// move this display to the front of _this->displays so it's treated as primary.
SDL_memmove(&_this->displays[1], &_this->displays[0], sizeof (*_this->displays) * i);
_this->displays[0] = display;
}
flags |= kCGDisplayMovedFlag; // we don't have an SDL event atm for "this display became primary," so at least let everyone know it "moved".
break;
}
}
}
}
if (flags & kCGDisplayMovedFlag) {
if (display) {
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_MOVED, 0, 0);
}
}
if (flags & kCGDisplayDesktopShapeChangedFlag) {
SDL_UpdateDesktopBounds();
}
}
void Cocoa_InitModes(SDL_VideoDevice *_this)
{
@autoreleasepool {
CGDisplayErr result;
CGDisplayCount numDisplays = 0;
result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
if (result != kCGErrorSuccess) {
CG_SetError("CGGetOnlineDisplayList()", result);
return;
}
bool isstack;
CGDirectDisplayID *displays = SDL_small_alloc(CGDirectDisplayID, numDisplays, &isstack);
result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
if (result != kCGErrorSuccess) {
CG_SetError("CGGetOnlineDisplayList()", result);
SDL_small_free(displays, isstack);
return;
}
// future updates to the display graph will come through this callback.
CGDisplayRegisterReconfigurationCallback(Cocoa_DisplayReconfigurationCallback, _this);
// Pick up the primary display in the first pass, then get the rest
for (int pass = 0; pass < 2; ++pass) {
for (int i = 0; i < numDisplays; ++i) {
if (pass == 0) {
if (!CGDisplayIsMain(displays[i])) {
continue;
}
} else {
if (CGDisplayIsMain(displays[i])) {
continue;
}
}
if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
continue;
}
Cocoa_AddDisplay(displays[i], false);
}
}
SDL_small_free(displays, isstack);
}
}
void Cocoa_UpdateDisplays(SDL_VideoDevice *_this)
{
SDL_HDROutputProperties HDR;
int i;
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *display = _this->displays[i];
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
Cocoa_GetHDRProperties(displaydata->display, &HDR);
SDL_SetDisplayHDRProperties(display, &HDR);
}
}
bool Cocoa_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
{
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
CGRect cgrect;
cgrect = CGDisplayBounds(displaydata->display);
rect->x = (int)cgrect.origin.x;
rect->y = (int)cgrect.origin.y;
rect->w = (int)cgrect.size.width;
rect->h = (int)cgrect.size.height;
return true;
}
bool Cocoa_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
{
@autoreleasepool {
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
NSScreen *screen = GetNSScreenForDisplayID(displaydata->display);
if (screen == nil) {
return SDL_SetError("Couldn't get NSScreen for display");
}
{
const NSRect frame = [screen visibleFrame];
rect->x = (int)frame.origin.x;
rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height);
rect->w = (int)frame.size.width;
rect->h = (int)frame.size.height;
}
return true;
}
}
bool Cocoa_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
{
SDL_DisplayData *data = (SDL_DisplayData *)display->internal;
CVDisplayLinkRef link = NULL;
CFArrayRef modes;
CFDictionaryRef dict = NULL;
const CFStringRef dictkeys[] = { kCGDisplayShowDuplicateLowResolutionModes };
const CFBooleanRef dictvalues[] = { kCFBooleanTrue };
CVDisplayLinkCreateWithCGDisplay(data->display, &link);
/* By default, CGDisplayCopyAllDisplayModes will only get a subset of the
* system's available modes. For example on a 15" 2016 MBP, users can
* choose 1920x1080@2x in System Preferences but it won't show up here,
* unless we specify the option below.
* The display modes returned by CGDisplayCopyAllDisplayModes are also not
* high dpi-capable unless this option is set.
* macOS 10.15 also seems to have a bug where entering, exiting, and
* re-entering exclusive fullscreen with a low dpi display mode can cause
* the content of the screen to move up, which this setting avoids:
* https://bugzilla.libsdl.org/show_bug.cgi?id=4822
*/
dict = CFDictionaryCreate(NULL,
(const void **)dictkeys,
(const void **)dictvalues,
1,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
modes = CGDisplayCopyAllDisplayModes(data->display, dict);
if (dict) {
CFRelease(dict);
}
if (modes) {
CFIndex i;
const CFIndex count = CFArrayGetCount(modes);
for (i = 0; i < count; i++) {
CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i);
SDL_DisplayMode mode;
if (GetDisplayMode(moderef, false, modes, link, &mode)) {
if (!SDL_AddFullscreenDisplayMode(display, &mode)) {
CFRelease(mode.internal->modes);
SDL_free(mode.internal);
}
}
}
CFRelease(modes);
}
CVDisplayLinkRelease(link);
return true;
}
static CGError SetDisplayModeForDisplay(CGDirectDisplayID display, SDL_DisplayModeData *data)
{
/* SDL_DisplayModeData can contain multiple CGDisplayModes to try (with
* identical properties), some of which might not work. See GetDisplayMode.
*/
CGError result = kCGErrorFailure;
for (CFIndex i = 0; i < CFArrayGetCount(data->modes); i++) {
CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(data->modes, i);
result = CGDisplaySetDisplayMode(display, moderef, NULL);
if (result == kCGErrorSuccess) {
// If this mode works, try it first next time.
if (i > 0) {
CFArrayExchangeValuesAtIndices(data->modes, i, 0);
}
break;
}
}
return result;
}
bool Cocoa_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
{
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
SDL_DisplayModeData *data = mode->internal;
CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
CGError result = kCGErrorSuccess;
b_inModeTransition = true;
// Fade to black to hide resolution-switching flicker
if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
}
if (data == display->desktop_mode.internal) {
// Restoring desktop mode
SetDisplayModeForDisplay(displaydata->display, data);
} else {
// Do the physical switch
result = SetDisplayModeForDisplay(displaydata->display, data);
}
// Fade in again (asynchronously)
if (fade_token != kCGDisplayFadeReservationInvalidToken) {
CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation(fade_token);
}
b_inModeTransition = false;
if (result != kCGErrorSuccess) {
return CG_SetError("CGDisplaySwitchToMode()", result);
}
return true;
}
void Cocoa_QuitModes(SDL_VideoDevice *_this)
{
int i, j;
CGDisplayRemoveReconfigurationCallback(Cocoa_DisplayReconfigurationCallback, _this);
for (i = 0; i < _this->num_displays; ++i) {
SDL_VideoDisplay *display = _this->displays[i];
SDL_DisplayModeData *mode;
if (display->current_mode->internal != display->desktop_mode.internal) {
Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
}
mode = display->desktop_mode.internal;
CFRelease(mode->modes);
for (j = 0; j < display->num_fullscreen_modes; j++) {
mode = display->fullscreen_modes[j].internal;
CFRelease(mode->modes);
}
}
}
#endif // SDL_VIDEO_DRIVER_COCOA

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.
*/
#include "SDL_internal.h"
#ifndef SDL_cocoamouse_h_
#define SDL_cocoamouse_h_
#include "SDL_cocoavideo.h"
extern bool Cocoa_InitMouse(SDL_VideoDevice *_this);
extern NSWindow *Cocoa_GetMouseFocus();
extern void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event);
extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event);
extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y);
extern void Cocoa_QuitMouse(SDL_VideoDevice *_this);
typedef struct
{
// Whether we've seen a cursor warp since the last move event.
bool seenWarp;
// What location our last cursor warp was to.
CGFloat lastWarpX;
CGFloat lastWarpY;
// What location we last saw the cursor move to.
CGFloat lastMoveX;
CGFloat lastMoveY;
} SDL_MouseData;
@interface NSCursor (InvisibleCursor)
+ (NSCursor *)invisibleCursor;
@end
#endif // SDL_cocoamouse_h_

View file

@ -0,0 +1,591 @@
/*
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_COCOA
#include "SDL_cocoamouse.h"
#include "SDL_cocoavideo.h"
#include "../../events/SDL_mouse_c.h"
#if 0
#define DEBUG_COCOAMOUSE
#endif
#ifdef DEBUG_COCOAMOUSE
#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
#else
#define DLog(...) \
do { \
} while (0)
#endif
@implementation NSCursor (InvisibleCursor)
+ (NSCursor *)invisibleCursor
{
static NSCursor *invisibleCursor = NULL;
if (!invisibleCursor) {
// RAW 16x16 transparent GIF
static unsigned char cursorBytes[] = {
0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
};
NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
length:sizeof(cursorBytes)
freeWhenDone:NO];
NSImage *cursorImage = [[NSImage alloc] initWithData:cursorData];
invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
hotSpot:NSZeroPoint];
}
return invisibleCursor;
}
@end
static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
@autoreleasepool {
NSImage *nsimage;
NSCursor *nscursor = NULL;
SDL_Cursor *cursor = NULL;
nsimage = Cocoa_CreateImage(surface);
if (nsimage) {
nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(hot_x, hot_y)];
}
if (nscursor) {
cursor = SDL_calloc(1, sizeof(*cursor));
if (cursor) {
cursor->internal = (void *)CFBridgingRetain(nscursor);
}
}
return cursor;
}
}
/* there are .pdf files of some of the cursors we need, installed by default on macOS, but not available through NSCursor.
If we can load them ourselves, use them, otherwise fallback to something standard but not super-great.
Since these are under /System, they should be available even to sandboxed apps. */
static NSCursor *LoadHiddenSystemCursor(NSString *cursorName, SEL fallback)
{
NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName];
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];
// we can't do animation atm. :/
const int frames = (int)[[info valueForKey:@"frames"] integerValue];
NSCursor *cursor;
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];
if ((image == nil) || (image.isValid == NO)) {
return [NSCursor performSelector:fallback];
}
if (frames > 1) {
#ifdef MAC_OS_VERSION_12_0 // same value as deprecated symbol.
const NSCompositingOperation operation = NSCompositingOperationCopy;
#else
const NSCompositingOperation operation = NSCompositeCopy;
#endif
const NSSize cropped_size = NSMakeSize(image.size.width, (int)(image.size.height / frames));
NSImage *cropped = [[NSImage alloc] initWithSize:cropped_size];
if (cropped == nil) {
return [NSCursor performSelector:fallback];
}
[cropped lockFocus];
{
const NSRect cropped_rect = NSMakeRect(0, 0, cropped_size.width, cropped_size.height);
[image drawInRect:cropped_rect fromRect:cropped_rect operation:operation fraction:1];
}
[cropped unlockFocus];
image = cropped;
}
cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])];
return cursor;
}
static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id)
{
@autoreleasepool {
NSCursor *nscursor = NULL;
SDL_Cursor *cursor = NULL;
switch (id) {
case SDL_SYSTEM_CURSOR_DEFAULT:
nscursor = [NSCursor arrowCursor];
break;
case SDL_SYSTEM_CURSOR_TEXT:
nscursor = [NSCursor IBeamCursor];
break;
case SDL_SYSTEM_CURSOR_CROSSHAIR:
nscursor = [NSCursor crosshairCursor];
break;
case SDL_SYSTEM_CURSOR_WAIT: // !!! FIXME: this is more like WAITARROW
nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor));
break;
case SDL_SYSTEM_CURSOR_PROGRESS: // !!! FIXME: this is meant to be animated
nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor));
break;
case SDL_SYSTEM_CURSOR_NWSE_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_NESW_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_EW_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
break;
case SDL_SYSTEM_CURSOR_NS_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
break;
case SDL_SYSTEM_CURSOR_MOVE:
nscursor = LoadHiddenSystemCursor(@"move", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_NOT_ALLOWED:
nscursor = [NSCursor operationNotAllowedCursor];
break;
case SDL_SYSTEM_CURSOR_POINTER:
nscursor = [NSCursor pointingHandCursor];
break;
case SDL_SYSTEM_CURSOR_NW_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_N_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
break;
case SDL_SYSTEM_CURSOR_NE_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_E_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
break;
case SDL_SYSTEM_CURSOR_SE_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_S_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor));
break;
case SDL_SYSTEM_CURSOR_SW_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor));
break;
case SDL_SYSTEM_CURSOR_W_RESIZE:
nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor));
break;
default:
SDL_assert(!"Unknown system cursor");
return NULL;
}
if (nscursor) {
cursor = SDL_calloc(1, sizeof(*cursor));
if (cursor) {
// We'll free it later, so retain it here
cursor->internal = (void *)CFBridgingRetain(nscursor);
}
}
return cursor;
}
}
static SDL_Cursor *Cocoa_CreateDefaultCursor(void)
{
SDL_SystemCursor id = SDL_GetDefaultSystemCursor();
return Cocoa_CreateSystemCursor(id);
}
static void Cocoa_FreeCursor(SDL_Cursor *cursor)
{
@autoreleasepool {
CFBridgingRelease((void *)cursor->internal);
SDL_free(cursor);
}
}
static bool Cocoa_ShowCursor(SDL_Cursor *cursor)
{
@autoreleasepool {
SDL_VideoDevice *device = SDL_GetVideoDevice();
SDL_Window *window = (device ? device->windows : NULL);
for (; window != NULL; window = window->next) {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
if (data) {
[data.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
withObject:[data.nswindow contentView]
waitUntilDone:NO];
}
}
return true;
}
}
static SDL_Window *SDL_FindWindowAtPoint(const float x, const float y)
{
const SDL_FPoint pt = { x, y };
SDL_Window *i;
for (i = SDL_GetVideoDevice()->windows; i; i = i->next) {
const SDL_FRect r = { (float)i->x, (float)i->y, (float)i->w, (float)i->h };
if (SDL_PointInRectFloat(&pt, &r)) {
return i;
}
}
return NULL;
}
static bool Cocoa_WarpMouseGlobal(float x, float y)
{
CGPoint point;
SDL_Mouse *mouse = SDL_GetMouse();
if (mouse->focus) {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)mouse->focus->internal;
if ([data.listener isMovingOrFocusClickPending]) {
DLog("Postponing warp, window being moved or focused.");
[data.listener setPendingMoveX:x Y:y];
return true;
}
}
point = CGPointMake(x, y);
Cocoa_HandleMouseWarp(point.x, point.y);
CGWarpMouseCursorPosition(point);
/* CGWarpMouse causes a short delay by default, which is preventable by
* Calling this directly after. CGSetLocalEventsSuppressionInterval can also
* prevent it, but it's deprecated as macOS 10.6.
*/
if (!mouse->relative_mode) {
CGAssociateMouseAndMouseCursorPosition(YES);
}
/* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
* other implementations' APIs. Send what's appropriate.
*/
if (!mouse->relative_mode) {
SDL_Window *win = SDL_FindWindowAtPoint(x, y);
SDL_SetMouseFocus(win);
if (win) {
SDL_assert(win == mouse->focus);
SDL_SendMouseMotion(0, win, SDL_GLOBAL_MOUSE_ID, false, x - win->x, y - win->y);
}
}
return true;
}
static bool Cocoa_WarpMouse(SDL_Window *window, float x, float y)
{
return Cocoa_WarpMouseGlobal(window->x + x, window->y + y);
}
static bool Cocoa_SetRelativeMouseMode(bool enabled)
{
CGError result;
if (enabled) {
SDL_Window *window = SDL_GetKeyboardFocus();
if (window) {
/* We will re-apply the relative mode when the window finishes being moved,
* if it is being moved right now.
*/
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
if ([data.listener isMovingOrFocusClickPending]) {
return true;
}
// make sure the mouse isn't at the corner of the window, as this can confuse things if macOS thinks a window resize is happening on the first click.
const CGPoint point = CGPointMake((float)(window->x + (window->w / 2)), (float)(window->y + (window->h / 2)));
Cocoa_HandleMouseWarp(point.x, point.y);
CGWarpMouseCursorPosition(point);
}
DLog("Turning on.");
result = CGAssociateMouseAndMouseCursorPosition(NO);
} else {
DLog("Turning off.");
result = CGAssociateMouseAndMouseCursorPosition(YES);
}
if (result != kCGErrorSuccess) {
return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
}
/* The hide/unhide calls are redundant most of the time, but they fix
* https://bugzilla.libsdl.org/show_bug.cgi?id=2550
*/
if (enabled) {
[NSCursor hide];
} else {
[NSCursor unhide];
}
return true;
}
static bool Cocoa_CaptureMouse(SDL_Window *window)
{
/* our Cocoa event code already tracks the mouse outside the window,
so all we have to do here is say "okay" and do what we always do. */
return true;
}
static SDL_MouseButtonFlags Cocoa_GetGlobalMouseState(float *x, float *y)
{
const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
const NSPoint cocoaLocation = [NSEvent mouseLocation];
SDL_MouseButtonFlags result = 0;
*x = cocoaLocation.x;
*y = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - cocoaLocation.y);
result |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
result |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
result |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
result |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
result |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
return result;
}
bool Cocoa_InitMouse(SDL_VideoDevice *_this)
{
NSPoint location;
SDL_Mouse *mouse = SDL_GetMouse();
SDL_MouseData *internal = (SDL_MouseData *)SDL_calloc(1, sizeof(SDL_MouseData));
if (internal == NULL) {
return false;
}
mouse->internal = internal;
mouse->CreateCursor = Cocoa_CreateCursor;
mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
mouse->ShowCursor = Cocoa_ShowCursor;
mouse->FreeCursor = Cocoa_FreeCursor;
mouse->WarpMouse = Cocoa_WarpMouse;
mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
mouse->CaptureMouse = Cocoa_CaptureMouse;
mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
location = [NSEvent mouseLocation];
internal->lastMoveX = location.x;
internal->lastMoveY = location.y;
return true;
}
static void Cocoa_HandleTitleButtonEvent(SDL_VideoDevice *_this, NSEvent *event)
{
SDL_Window *window;
NSWindow *nswindow = [event window];
/* You might land in this function before SDL_Init if showing a message box.
Don't dereference a NULL pointer if that happens. */
if (_this == NULL) {
return;
}
for (window = _this->windows; window; window = window->next) {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
if (data && data.nswindow == nswindow) {
switch ([event type]) {
case NSEventTypeLeftMouseDown:
case NSEventTypeRightMouseDown:
case NSEventTypeOtherMouseDown:
[data.listener setFocusClickPending:[event buttonNumber]];
break;
case NSEventTypeLeftMouseUp:
case NSEventTypeRightMouseUp:
case NSEventTypeOtherMouseUp:
[data.listener clearFocusClickPending:[event buttonNumber]];
break;
default:
break;
}
break;
}
}
}
static NSWindow *Cocoa_MouseFocus;
NSWindow *Cocoa_GetMouseFocus()
{
return Cocoa_MouseFocus;
}
void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
{
SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID;
SDL_Mouse *mouse;
SDL_MouseData *data;
NSPoint location;
CGFloat lastMoveX, lastMoveY;
float deltaX, deltaY;
bool seenWarp;
// All events except NSEventTypeMouseExited can only happen if the window
// has mouse focus, so we'll always set the focus even if we happen to miss
// NSEventTypeMouseEntered, which apparently happens if the window is
// created under the mouse on macOS 12.7
NSEventType event_type = [event type];
if (event_type == NSEventTypeMouseExited) {
Cocoa_MouseFocus = NULL;
} else {
Cocoa_MouseFocus = [event window];
}
switch (event_type) {
case NSEventTypeMouseEntered:
case NSEventTypeMouseExited:
// Focus is handled above
return;
case NSEventTypeMouseMoved:
case NSEventTypeLeftMouseDragged:
case NSEventTypeRightMouseDragged:
case NSEventTypeOtherMouseDragged:
break;
case NSEventTypeLeftMouseDown:
case NSEventTypeLeftMouseUp:
case NSEventTypeRightMouseDown:
case NSEventTypeRightMouseUp:
case NSEventTypeOtherMouseDown:
case NSEventTypeOtherMouseUp:
if ([event window]) {
NSRect windowRect = [[[event window] contentView] frame];
if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
Cocoa_HandleTitleButtonEvent(_this, event);
return;
}
}
return;
default:
// Ignore any other events.
return;
}
mouse = SDL_GetMouse();
data = (SDL_MouseData *)mouse->internal;
if (!data) {
return; // can happen when returning from fullscreen Space on shutdown
}
seenWarp = data->seenWarp;
data->seenWarp = NO;
location = [NSEvent mouseLocation];
lastMoveX = data->lastMoveX;
lastMoveY = data->lastMoveY;
data->lastMoveX = location.x;
data->lastMoveY = location.y;
DLog("Last seen mouse: (%g, %g)", location.x, location.y);
// Non-relative movement is handled in -[SDL3Cocoa_WindowListener mouseMoved:]
if (!mouse->relative_mode) {
return;
}
// Ignore events that aren't inside the client area (i.e. title bar.)
if ([event window]) {
NSRect windowRect = [[[event window] contentView] frame];
if (!NSMouseInRect([event locationInWindow], windowRect, NO)) {
return;
}
}
deltaX = [event deltaX];
deltaY = [event deltaY];
if (seenWarp) {
deltaX += (lastMoveX - data->lastWarpX);
deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - data->lastWarpY);
DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
}
SDL_SendMouseMotion(Cocoa_GetEventTimestamp([event timestamp]), mouse->focus, mouseID, true, deltaX, deltaY);
}
void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
{
SDL_MouseID mouseID = SDL_DEFAULT_MOUSE_ID;
SDL_MouseWheelDirection direction;
CGFloat x, y;
x = -[event deltaX];
y = [event deltaY];
direction = SDL_MOUSEWHEEL_NORMAL;
if ([event isDirectionInvertedFromDevice] == YES) {
direction = SDL_MOUSEWHEEL_FLIPPED;
}
/* For discrete scroll events from conventional mice, always send a full tick.
For continuous scroll events from trackpads, send fractional deltas for smoother scrolling. */
if (![event hasPreciseScrollingDeltas]) {
if (x > 0) {
x = SDL_ceil(x);
} else if (x < 0) {
x = SDL_floor(x);
}
if (y > 0) {
y = SDL_ceil(y);
} else if (y < 0) {
y = SDL_floor(y);
}
}
SDL_SendMouseWheel(Cocoa_GetEventTimestamp([event timestamp]), window, mouseID, x, y, direction);
}
void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
{
/* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
* since it gets included in the next movement event.
*/
SDL_MouseData *data = (SDL_MouseData *)SDL_GetMouse()->internal;
data->lastWarpX = x;
data->lastWarpY = y;
data->seenWarp = true;
DLog("(%g, %g)", x, y);
}
void Cocoa_QuitMouse(SDL_VideoDevice *_this)
{
SDL_Mouse *mouse = SDL_GetMouse();
if (mouse) {
if (mouse->internal) {
SDL_free(mouse->internal);
mouse->internal = NULL;
}
}
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,88 @@
/*
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_cocoaopengl_h_
#define SDL_cocoaopengl_h_
#ifdef SDL_VIDEO_OPENGL_CGL
#import <Cocoa/Cocoa.h>
#import <QuartzCore/CVDisplayLink.h>
// We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
struct SDL_GLDriverData
{
int initialized;
};
@interface SDL3OpenGLContext : NSOpenGLContext
{
SDL_AtomicInt dirty;
SDL_Window *window;
CVDisplayLinkRef displayLink;
@public
SDL_Mutex *swapIntervalMutex;
@public
SDL_Condition *swapIntervalCond;
@public
SDL_AtomicInt swapIntervalSetting;
@public
SDL_AtomicInt swapIntervalsPassed;
}
- (id)initWithFormat:(NSOpenGLPixelFormat *)format
shareContext:(NSOpenGLContext *)share;
- (void)scheduleUpdate;
- (void)updateIfNeeded;
- (void)movedToNewScreen;
- (void)setWindow:(SDL_Window *)window;
- (SDL_Window *)window;
- (void)explicitUpdate;
- (void)cleanup;
@property(retain, nonatomic) NSOpenGLPixelFormat *openglPixelFormat; // macOS 10.10 has -[NSOpenGLContext pixelFormat] but this handles older OS releases.
@end
// OpenGL functions
extern bool Cocoa_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern SDL_FunctionPointer Cocoa_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc);
extern void Cocoa_GL_UnloadLibrary(SDL_VideoDevice *_this);
extern SDL_GLContext Cocoa_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
extern bool Cocoa_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval);
extern bool Cocoa_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
extern bool Cocoa_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // SDL_VIDEO_OPENGL_CGL
#endif // SDL_cocoaopengl_h_

View file

@ -0,0 +1,559 @@
/*
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"
// NSOpenGL implementation of SDL OpenGL support
#ifdef SDL_VIDEO_OPENGL_CGL
#include "SDL_cocoavideo.h"
#include "SDL_cocoaopengl.h"
#include "SDL_cocoaopengles.h"
#include <OpenGL/CGLTypes.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLRenderers.h>
#include <SDL3/SDL_opengl.h>
#include "../../SDL_hints_c.h"
#define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"
// We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
// _Nullable is available starting Xcode 7
#ifdef __has_feature
#if __has_feature(nullability)
#define HAS_FEATURE_NULLABLE
#endif
#endif
#ifndef HAS_FEATURE_NULLABLE
#define _Nullable
#endif
static bool SDL_opengl_async_dispatch = false;
static void SDLCALL SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_opengl_async_dispatch = SDL_GetStringBoolean(hint, false);
}
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)displayLinkContext;
// printf("DISPLAY LINK! %u\n", (unsigned int) SDL_GetTicks());
const int setting = SDL_GetAtomicInt(&nscontext->swapIntervalSetting);
if (setting != 0) { // nothing to do if vsync is disabled, don't even lock
SDL_LockMutex(nscontext->swapIntervalMutex);
SDL_AddAtomicInt(&nscontext->swapIntervalsPassed, 1);
SDL_SignalCondition(nscontext->swapIntervalCond);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
}
return kCVReturnSuccess;
}
@implementation SDL3OpenGLContext : NSOpenGLContext
- (id)initWithFormat:(NSOpenGLPixelFormat *)format
shareContext:(NSOpenGLContext *)share
{
self = [super initWithFormat:format shareContext:share];
if (self) {
self.openglPixelFormat = format;
SDL_SetAtomicInt(&self->dirty, 0);
self->window = NULL;
SDL_SetAtomicInt(&self->swapIntervalSetting, 0);
SDL_SetAtomicInt(&self->swapIntervalsPassed, 0);
self->swapIntervalCond = SDL_CreateCondition();
self->swapIntervalMutex = SDL_CreateMutex();
if (!self->swapIntervalCond || !self->swapIntervalMutex) {
return nil;
}
// !!! FIXME: check return values.
CVDisplayLinkCreateWithActiveCGDisplays(&self->displayLink);
CVDisplayLinkSetOutputCallback(self->displayLink, &DisplayLinkCallback, (__bridge void *_Nullable)self);
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [format CGLPixelFormatObj]);
CVDisplayLinkStart(displayLink);
}
SDL_AddHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL);
return self;
}
- (void)movedToNewScreen
{
if (self->displayLink) {
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [[self openglPixelFormat] CGLPixelFormatObj]);
}
}
- (void)scheduleUpdate
{
SDL_AddAtomicInt(&self->dirty, 1);
}
// This should only be called on the thread on which a user is using the context.
- (void)updateIfNeeded
{
const int value = SDL_SetAtomicInt(&self->dirty, 0);
if (value > 0) {
// We call the real underlying update here, since -[SDL3OpenGLContext update] just calls us.
[self explicitUpdate];
}
}
// This should only be called on the thread on which a user is using the context.
- (void)update
{
// This ensures that regular 'update' calls clear the atomic dirty flag.
[self scheduleUpdate];
[self updateIfNeeded];
}
// Updates the drawable for the contexts and manages related state.
- (void)setWindow:(SDL_Window *)newWindow
{
if (self->window) {
SDL_CocoaWindowData *oldwindowdata = (__bridge SDL_CocoaWindowData *)self->window->internal;
// Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too.
NSMutableArray *contexts = oldwindowdata.nscontexts;
@synchronized(contexts) {
[contexts removeObject:self];
}
}
self->window = newWindow;
if (newWindow) {
SDL_CocoaWindowData *windowdata = (__bridge SDL_CocoaWindowData *)newWindow->internal;
NSView *contentview = windowdata.sdlContentView;
// Now sign up for scheduled updates for the new window.
NSMutableArray *contexts = windowdata.nscontexts;
@synchronized(contexts) {
[contexts addObject:self];
}
if ([self view] != contentview) {
if ([NSThread isMainThread]) {
[self setView:contentview];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[self setView:contentview];
});
}
if (self == [NSOpenGLContext currentContext]) {
[self explicitUpdate];
} else {
[self scheduleUpdate];
}
}
} else {
if ([NSThread isMainThread]) {
[self setView:nil];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:nil]; });
}
}
}
- (SDL_Window *)window
{
return self->window;
}
- (void)explicitUpdate
{
if ([NSThread isMainThread]) {
[super update];
} else {
if (SDL_opengl_async_dispatch) {
dispatch_async(dispatch_get_main_queue(), ^{
[super update];
});
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[super update];
});
}
}
}
- (void)cleanup
{
[self setWindow:NULL];
SDL_RemoveHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL);
if (self->displayLink) {
CVDisplayLinkRelease(self->displayLink);
self->displayLink = nil;
}
if (self->swapIntervalCond) {
SDL_DestroyCondition(self->swapIntervalCond);
self->swapIntervalCond = NULL;
}
if (self->swapIntervalMutex) {
SDL_DestroyMutex(self->swapIntervalMutex);
self->swapIntervalMutex = NULL;
}
}
@end
bool Cocoa_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
// Load the OpenGL library
if (path == NULL) {
path = SDL_GetHint(SDL_HINT_OPENGL_LIBRARY);
}
if (path == NULL) {
path = DEFAULT_OPENGL;
}
_this->gl_config.dll_handle = SDL_LoadObject(path);
if (!_this->gl_config.dll_handle) {
return false;
}
SDL_strlcpy(_this->gl_config.driver_path, path,
SDL_arraysize(_this->gl_config.driver_path));
return true;
}
SDL_FunctionPointer Cocoa_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc)
{
return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
}
void Cocoa_GL_UnloadLibrary(SDL_VideoDevice *_this)
{
SDL_UnloadObject(_this->gl_config.dll_handle);
_this->gl_config.dll_handle = NULL;
}
SDL_GLContext Cocoa_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
NSOpenGLPixelFormatAttribute attr[32];
NSOpenGLPixelFormat *fmt;
SDL3OpenGLContext *context;
SDL_GLContext sdlcontext;
NSOpenGLContext *share_context = nil;
int i = 0;
const char *glversion;
int glversion_major;
int glversion_minor;
NSOpenGLPixelFormatAttribute profile;
int interval;
int opaque;
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
#ifdef SDL_VIDEO_OPENGL_EGL
// Switch to EGL based functions
Cocoa_GL_UnloadLibrary(_this);
_this->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
_this->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
_this->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
_this->GL_CreateContext = Cocoa_GLES_CreateContext;
_this->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
_this->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
_this->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
_this->GL_SwapWindow = Cocoa_GLES_SwapWindow;
_this->GL_DestroyContext = Cocoa_GLES_DestroyContext;
if (!Cocoa_GLES_LoadLibrary(_this, NULL)) {
return NULL;
}
return Cocoa_GLES_CreateContext(_this, window);
#else
SDL_SetError("SDL not configured with EGL support");
return NULL;
#endif
}
attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
profile = NSOpenGLProfileVersionLegacy;
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
profile = NSOpenGLProfileVersion3_2Core;
}
attr[i++] = NSOpenGLPFAOpenGLProfile;
attr[i++] = profile;
attr[i++] = NSOpenGLPFAColorSize;
attr[i++] = SDL_BYTESPERPIXEL(display->current_mode->format) * 8;
attr[i++] = NSOpenGLPFADepthSize;
attr[i++] = _this->gl_config.depth_size;
if (_this->gl_config.double_buffer) {
attr[i++] = NSOpenGLPFADoubleBuffer;
}
if (_this->gl_config.stereo) {
attr[i++] = NSOpenGLPFAStereo;
}
if (_this->gl_config.stencil_size) {
attr[i++] = NSOpenGLPFAStencilSize;
attr[i++] = _this->gl_config.stencil_size;
}
if ((_this->gl_config.accum_red_size +
_this->gl_config.accum_green_size +
_this->gl_config.accum_blue_size +
_this->gl_config.accum_alpha_size) > 0) {
attr[i++] = NSOpenGLPFAAccumSize;
attr[i++] = _this->gl_config.accum_red_size + _this->gl_config.accum_green_size + _this->gl_config.accum_blue_size + _this->gl_config.accum_alpha_size;
}
if (_this->gl_config.multisamplebuffers) {
attr[i++] = NSOpenGLPFASampleBuffers;
attr[i++] = _this->gl_config.multisamplebuffers;
}
if (_this->gl_config.multisamplesamples) {
attr[i++] = NSOpenGLPFASamples;
attr[i++] = _this->gl_config.multisamplesamples;
attr[i++] = NSOpenGLPFANoRecovery;
}
if (_this->gl_config.floatbuffers) {
attr[i++] = NSOpenGLPFAColorFloat;
}
if (_this->gl_config.accelerated >= 0) {
if (_this->gl_config.accelerated) {
attr[i++] = NSOpenGLPFAAccelerated;
} else {
attr[i++] = NSOpenGLPFARendererID;
attr[i++] = kCGLRendererGenericFloatID;
}
}
attr[i++] = NSOpenGLPFAScreenMask;
attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
attr[i] = 0;
fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
if (fmt == nil) {
SDL_SetError("Failed creating OpenGL pixel format");
return NULL;
}
if (_this->gl_config.share_with_current_context) {
share_context = (__bridge NSOpenGLContext *)SDL_GL_GetCurrentContext();
}
context = [[SDL3OpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
if (context == nil) {
SDL_SetError("Failed creating OpenGL context");
return NULL;
}
sdlcontext = (SDL_GLContext)CFBridgingRetain(context);
// vsync is handled separately by synchronizing with a display link.
interval = 0;
[context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
opaque = (window->flags & SDL_WINDOW_TRANSPARENT) ? 0 : 1;
[context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
if (!Cocoa_GL_MakeCurrent(_this, window, sdlcontext)) {
SDL_GL_DestroyContext(sdlcontext);
SDL_SetError("Failed making OpenGL context current");
return NULL;
}
if (_this->gl_config.major_version < 3 &&
_this->gl_config.profile_mask == 0 &&
_this->gl_config.flags == 0) {
// This is a legacy profile, so to match other backends, we're done.
} else {
const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum))SDL_GL_GetProcAddress("glGetString");
if (!glGetStringFunc) {
SDL_GL_DestroyContext(sdlcontext);
SDL_SetError("Failed getting OpenGL glGetString entry point");
return NULL;
}
glversion = (const char *)glGetStringFunc(GL_VERSION);
if (glversion == NULL) {
SDL_GL_DestroyContext(sdlcontext);
SDL_SetError("Failed getting OpenGL context version");
return NULL;
}
if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
SDL_GL_DestroyContext(sdlcontext);
SDL_SetError("Failed parsing OpenGL context version");
return NULL;
}
if ((glversion_major < _this->gl_config.major_version) ||
((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
SDL_GL_DestroyContext(sdlcontext);
SDL_SetError("Failed creating OpenGL context at version requested");
return NULL;
}
/* In the future we'll want to do this, but to match other platforms
we'll leave the OpenGL version the way it is for now
*/
// _this->gl_config.major_version = glversion_major;
// _this->gl_config.minor_version = glversion_minor;
}
return sdlcontext;
}
}
bool Cocoa_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
{
@autoreleasepool {
if (context) {
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)context;
if ([nscontext window] != window) {
[nscontext setWindow:window];
[nscontext updateIfNeeded];
}
[nscontext makeCurrentContext];
} else {
[NSOpenGLContext clearCurrentContext];
}
return true;
}
}
bool Cocoa_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
{
@autoreleasepool {
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)SDL_GL_GetCurrentContext();
bool result;
if (nscontext == nil) {
result = SDL_SetError("No current OpenGL context");
} else {
SDL_LockMutex(nscontext->swapIntervalMutex);
SDL_SetAtomicInt(&nscontext->swapIntervalsPassed, 0);
SDL_SetAtomicInt(&nscontext->swapIntervalSetting, interval);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
result = true;
}
return result;
}
}
bool Cocoa_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
{
@autoreleasepool {
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)SDL_GL_GetCurrentContext();
if (nscontext) {
*interval = SDL_GetAtomicInt(&nscontext->swapIntervalSetting);
return true;
} else {
return SDL_SetError("no OpenGL context");
}
}
}
bool Cocoa_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)SDL_GL_GetCurrentContext();
SDL_CocoaVideoData *videodata = (__bridge SDL_CocoaVideoData *)_this->internal;
const int setting = SDL_GetAtomicInt(&nscontext->swapIntervalSetting);
if (setting == 0) {
// nothing to do if vsync is disabled, don't even lock
} else if (setting < 0) { // late swap tearing
SDL_LockMutex(nscontext->swapIntervalMutex);
while (SDL_GetAtomicInt(&nscontext->swapIntervalsPassed) == 0) {
SDL_WaitCondition(nscontext->swapIntervalCond, nscontext->swapIntervalMutex);
}
SDL_SetAtomicInt(&nscontext->swapIntervalsPassed, 0);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
} else {
SDL_LockMutex(nscontext->swapIntervalMutex);
do { // always wait here so we know we just hit a swap interval.
SDL_WaitCondition(nscontext->swapIntervalCond, nscontext->swapIntervalMutex);
} while ((SDL_GetAtomicInt(&nscontext->swapIntervalsPassed) % setting) != 0);
SDL_SetAtomicInt(&nscontext->swapIntervalsPassed, 0);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
}
// { static Uint64 prev = 0; const Uint64 now = SDL_GetTicks(); const unsigned int diff = (unsigned int) (now - prev); prev = now; printf("GLSWAPBUFFERS TICKS %u\n", diff); }
/* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
threads try to swap at the same time, so put a mutex around it. */
SDL_LockMutex(videodata.swaplock);
[nscontext flushBuffer];
[nscontext updateIfNeeded];
SDL_UnlockMutex(videodata.swaplock);
return true;
}
}
static void DispatchedDestroyContext(SDL_GLContext context)
{
@autoreleasepool {
SDL3OpenGLContext *nscontext = (__bridge SDL3OpenGLContext *)context;
[nscontext cleanup];
CFRelease(context);
}
}
bool Cocoa_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
{
if ([NSThread isMainThread]) {
DispatchedDestroyContext(context);
} else {
if (SDL_opengl_async_dispatch) {
dispatch_async(dispatch_get_main_queue(), ^{
DispatchedDestroyContext(context);
});
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
DispatchedDestroyContext(context);
});
}
}
return true;
}
// We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it.
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // SDL_VIDEO_OPENGL_CGL

View file

@ -0,0 +1,48 @@
/*
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_cocoaopengles_h_
#define SDL_cocoaopengles_h_
#ifdef SDL_VIDEO_OPENGL_EGL
#include "../SDL_sysvideo.h"
#include "../SDL_egl_c.h"
// OpenGLES functions
#define Cocoa_GLES_GetAttribute SDL_EGL_GetAttribute
#define Cocoa_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
#define Cocoa_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
#define Cocoa_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
#define Cocoa_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
extern bool Cocoa_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern SDL_GLContext Cocoa_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
extern bool Cocoa_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
extern bool Cocoa_GLES_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern SDL_EGLSurface Cocoa_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_VIDEO_OPENGL_EGL
#endif // SDL_cocoaopengles_h_

View file

@ -0,0 +1,156 @@
/*
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"
#if defined(SDL_VIDEO_DRIVER_COCOA) && defined(SDL_VIDEO_OPENGL_EGL)
#include "SDL_cocoavideo.h"
#include "SDL_cocoaopengles.h"
#include "SDL_cocoaopengl.h"
// EGL implementation of SDL OpenGL support
bool Cocoa_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
// If the profile requested is not GL ES, switch over to WIN_GL functions
if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) {
#ifdef SDL_VIDEO_OPENGL_CGL
Cocoa_GLES_UnloadLibrary(_this);
_this->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
_this->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
_this->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
_this->GL_CreateContext = Cocoa_GL_CreateContext;
_this->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
_this->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
_this->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
_this->GL_SwapWindow = Cocoa_GL_SwapWindow;
_this->GL_DestroyContext = Cocoa_GL_DestroyContext;
_this->GL_GetEGLSurface = NULL;
return Cocoa_GL_LoadLibrary(_this, path);
#else
return SDL_SetError("SDL not configured with OpenGL/CGL support");
#endif
}
if (_this->egl_data == NULL) {
return SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, _this->gl_config.egl_platform);
}
return true;
}
SDL_GLContext Cocoa_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
SDL_GLContext context;
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
#ifdef SDL_VIDEO_OPENGL_CGL
if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) {
// Switch to CGL based functions
Cocoa_GLES_UnloadLibrary(_this);
_this->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
_this->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
_this->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
_this->GL_CreateContext = Cocoa_GL_CreateContext;
_this->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
_this->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
_this->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
_this->GL_SwapWindow = Cocoa_GL_SwapWindow;
_this->GL_DestroyContext = Cocoa_GL_DestroyContext;
_this->GL_GetEGLSurface = NULL;
if (!Cocoa_GL_LoadLibrary(_this, NULL)) {
return NULL;
}
return Cocoa_GL_CreateContext(_this, window);
}
#endif
context = SDL_EGL_CreateContext(_this, data.egl_surface);
return context;
}
}
bool Cocoa_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
{
@autoreleasepool {
SDL_EGL_DestroyContext(_this, context);
}
return true;
}
bool Cocoa_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
return SDL_EGL_SwapBuffers(_this, ((__bridge SDL_CocoaWindowData *)window->internal).egl_surface);
}
}
bool Cocoa_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
{
@autoreleasepool {
return SDL_EGL_MakeCurrent(_this, window ? ((__bridge SDL_CocoaWindowData *)window->internal).egl_surface : EGL_NO_SURFACE, context);
}
}
bool Cocoa_GLES_SetupWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
NSView *v;
// The current context is lost in here; save it and reset it.
SDL_CocoaWindowData *windowdata = (__bridge SDL_CocoaWindowData *)window->internal;
SDL_Window *current_win = SDL_GL_GetCurrentWindow();
SDL_GLContext current_ctx = SDL_GL_GetCurrentContext();
if (_this->egl_data == NULL) {
// !!! FIXME: commenting out this assertion is (I think) incorrect; figure out why driver_loaded is wrong for ANGLE instead. --ryan.
#if 0 // When hint SDL_HINT_OPENGL_ES_DRIVER is set to "1" (e.g. for ANGLE support), _this->gl_config.driver_loaded can be 1, while the below lines function.
SDL_assert(!_this->gl_config.driver_loaded);
#endif
if (!SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, _this->gl_config.egl_platform)) {
SDL_EGL_UnloadLibrary(_this);
return false;
}
_this->gl_config.driver_loaded = 1;
}
// Create the GLES window surface
v = windowdata.nswindow.contentView;
windowdata.egl_surface = SDL_EGL_CreateSurface(_this, window, (__bridge NativeWindowType)[v layer]);
if (windowdata.egl_surface == EGL_NO_SURFACE) {
return SDL_SetError("Could not create GLES window surface");
}
return Cocoa_GLES_MakeCurrent(_this, current_win, current_ctx);
}
}
SDL_EGLSurface Cocoa_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window)
{
@autoreleasepool {
return ((__bridge SDL_CocoaWindowData *)window->internal).egl_surface;
}
}
#endif // SDL_VIDEO_DRIVER_COCOA && SDL_VIDEO_OPENGL_EGL

View file

@ -0,0 +1,32 @@
/*
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_cocoapen_h_
#define SDL_cocoapenm_h_
#include "SDL_cocoavideo.h"
extern bool Cocoa_InitPen(SDL_VideoDevice *_this);
extern bool Cocoa_HandlePenEvent(SDL_CocoaWindowData *_data, NSEvent *event); // return false if we didn't handle this event.
extern void Cocoa_QuitPen(SDL_VideoDevice *_this);
#endif // SDL_cocoapen_h_

View file

@ -0,0 +1,178 @@
/*
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_COCOA
#include "SDL_cocoapen.h"
#include "SDL_cocoavideo.h"
#include "../../events/SDL_pen_c.h"
bool Cocoa_InitPen(SDL_VideoDevice *_this)
{
return true;
}
typedef struct Cocoa_PenHandle
{
NSUInteger deviceid;
NSUInteger toolid;
SDL_PenID pen;
bool is_eraser;
} Cocoa_PenHandle;
typedef struct FindPenByDeviceAndToolIDData
{
NSUInteger deviceid;
NSUInteger toolid;
void *handle;
} FindPenByDeviceAndToolIDData;
static bool FindPenByDeviceAndToolID(void *handle, void *userdata)
{
const Cocoa_PenHandle *cocoa_handle = (const Cocoa_PenHandle *) handle;
FindPenByDeviceAndToolIDData *data = (FindPenByDeviceAndToolIDData *) userdata;
if (cocoa_handle->deviceid != data->deviceid) {
return false;
} else if (cocoa_handle->toolid != data->toolid) {
return false;
}
data->handle = handle;
return true;
}
static Cocoa_PenHandle *Cocoa_FindPenByDeviceID(NSUInteger deviceid, NSUInteger toolid)
{
FindPenByDeviceAndToolIDData data;
data.deviceid = deviceid;
data.toolid = toolid;
data.handle = NULL;
SDL_FindPenByCallback(FindPenByDeviceAndToolID, &data);
return (Cocoa_PenHandle *) data.handle;
}
static void Cocoa_HandlePenProximityEvent(SDL_CocoaWindowData *_data, NSEvent *event)
{
const NSUInteger devid = [event deviceID];
const NSUInteger toolid = [event pointingDeviceID];
if (event.enteringProximity) { // new pen coming!
const NSPointingDeviceType devtype = [event pointingDeviceType];
const bool is_eraser = (devtype == NSPointingDeviceTypeEraser);
const bool is_pen = (devtype == NSPointingDeviceTypePen);
if (!is_eraser && !is_pen) {
return; // we ignore other things, which hopefully is right.
}
Cocoa_PenHandle *handle = (Cocoa_PenHandle *) SDL_calloc(1, sizeof (*handle));
if (!handle) {
return; // oh well.
}
// Cocoa offers almost none of this information as specifics, but can without warning offer any of these specific things.
SDL_PenInfo peninfo;
SDL_zero(peninfo);
peninfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ROTATION | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT | SDL_PEN_CAPABILITY_TANGENTIAL_PRESSURE | (is_eraser ? SDL_PEN_CAPABILITY_ERASER : 0);
peninfo.max_tilt = 90.0f;
peninfo.num_buttons = 2;
peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN;
handle->deviceid = devid;
handle->toolid = toolid;
handle->is_eraser = is_eraser;
handle->pen = SDL_AddPenDevice(Cocoa_GetEventTimestamp([event timestamp]), NULL, &peninfo, handle);
if (!handle->pen) {
SDL_free(handle); // oh well.
}
} else { // old pen leaving!
Cocoa_PenHandle *handle = Cocoa_FindPenByDeviceID(devid, toolid);
if (handle) {
SDL_RemovePenDevice(Cocoa_GetEventTimestamp([event timestamp]), handle->pen);
SDL_free(handle);
}
}
}
static void Cocoa_HandlePenPointEvent(SDL_CocoaWindowData *_data, NSEvent *event)
{
const Uint64 timestamp = Cocoa_GetEventTimestamp([event timestamp]);
Cocoa_PenHandle *handle = Cocoa_FindPenByDeviceID([event deviceID], [event pointingDeviceID]);
if (!handle) {
return;
}
const SDL_PenID pen = handle->pen;
const NSEventButtonMask buttons = [event buttonMask];
const NSPoint tilt = [event tilt];
const NSPoint point = [event locationInWindow];
const bool is_touching = (buttons & NSEventButtonMaskPenTip) != 0;
SDL_Window *window = _data.window;
SDL_SendPenTouch(timestamp, pen, window, handle->is_eraser, is_touching);
SDL_SendPenMotion(timestamp, pen, window, (float) point.x, (float) (window->h - point.y));
SDL_SendPenButton(timestamp, pen, window, 1, ((buttons & NSEventButtonMaskPenLowerSide) != 0));
SDL_SendPenButton(timestamp, pen, window, 2, ((buttons & NSEventButtonMaskPenUpperSide) != 0));
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_PRESSURE, [event pressure]);
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_ROTATION, [event rotation]);
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_XTILT, ((float) tilt.x) * 90.0f);
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_YTILT, ((float) -tilt.y) * 90.0f);
SDL_SendPenAxis(timestamp, pen, window, SDL_PEN_AXIS_TANGENTIAL_PRESSURE, event.tangentialPressure);
}
bool Cocoa_HandlePenEvent(SDL_CocoaWindowData *_data, NSEvent *event)
{
NSEventType type = [event type];
if ((type != NSEventTypeTabletPoint) && (type != NSEventTypeTabletProximity)) {
const NSEventSubtype subtype = [event subtype];
if (subtype == NSEventSubtypeTabletPoint) {
type = NSEventTypeTabletPoint;
} else if (subtype == NSEventSubtypeTabletProximity) {
type = NSEventTypeTabletProximity;
} else {
return false; // not a tablet event.
}
}
if (type == NSEventTypeTabletPoint) {
Cocoa_HandlePenPointEvent(_data, event);
} else if (type == NSEventTypeTabletProximity) {
Cocoa_HandlePenProximityEvent(_data, event);
} else {
return false; // not a tablet event.
}
return true;
}
static void Cocoa_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata)
{
SDL_free(handle);
}
void Cocoa_QuitPen(SDL_VideoDevice *_this)
{
SDL_RemoveAllPenDevices(Cocoa_FreePenHandle, NULL);
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,28 @@
/*
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_cocoashape_h_
#define SDL_cocoashape_h_
extern bool Cocoa_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
#endif // SDL_cocoashape_h_

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.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_COCOA
#include "SDL_cocoavideo.h"
#include "SDL_cocoashape.h"
bool Cocoa_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape)
{
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
BOOL ignoresMouseEvents = NO;
if (shape) {
SDL_FPoint point;
SDL_GetGlobalMouseState(&point.x, &point.y);
point.x -= window->x;
point.y -= window->y;
if (point.x >= 0.0f && point.x < window->w &&
point.y >= 0.0f && point.y < window->h) {
int x = (int)SDL_roundf((point.x / (window->w - 1)) * (shape->w - 1));
int y = (int)SDL_roundf((point.y / (window->h - 1)) * (shape->h - 1));
Uint8 a;
if (!SDL_ReadSurfacePixel(shape, x, y, NULL, NULL, NULL, &a) || a == SDL_ALPHA_TRANSPARENT) {
ignoresMouseEvents = YES;
}
}
}
data.nswindow.ignoresMouseEvents = ignoresMouseEvents;
return true;
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,71 @@
/*
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_cocoavideo_h_
#define SDL_cocoavideo_h_
#include <SDL3/SDL_opengl.h>
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <Cocoa/Cocoa.h>
#include "../SDL_sysvideo.h"
#include "SDL_cocoaclipboard.h"
#include "SDL_cocoaevents.h"
#include "SDL_cocoakeyboard.h"
#include "SDL_cocoamodes.h"
#include "SDL_cocoamouse.h"
#include "SDL_cocoaopengl.h"
#include "SDL_cocoawindow.h"
#include "SDL_cocoapen.h"
// Private display data
@class SDL3TranslatorResponder;
typedef enum
{
OptionAsAltNone,
OptionAsAltOnlyLeft,
OptionAsAltOnlyRight,
OptionAsAltBoth,
} OptionAsAlt;
@interface SDL_CocoaVideoData : NSObject
@property(nonatomic) int allow_spaces;
@property(nonatomic) int trackpad_is_touch_only;
@property(nonatomic) unsigned int modifierFlags;
@property(nonatomic) void *key_layout;
@property(nonatomic) SDL3TranslatorResponder *fieldEdit;
@property(nonatomic) NSInteger clipboard_count;
@property(nonatomic) IOPMAssertionID screensaver_assertion;
@property(nonatomic) SDL_Mutex *swaplock;
@property(nonatomic) OptionAsAlt option_as_alt;
@end
// Utility functions
extern SDL_SystemTheme Cocoa_GetSystemTheme(void);
extern NSImage *Cocoa_CreateImage(SDL_Surface *surface);
#endif // SDL_cocoavideo_h_

View file

@ -0,0 +1,337 @@
/*
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_COCOA
#if !__has_feature(objc_arc)
#error SDL must be built with Objective-C ARC (automatic reference counting) enabled
#endif
#include "SDL_cocoavideo.h"
#include "SDL_cocoavulkan.h"
#include "SDL_cocoametalview.h"
#include "SDL_cocoaopengles.h"
#include "SDL_cocoamessagebox.h"
#include "SDL_cocoashape.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
@implementation SDL_CocoaVideoData
@end
// Initialization/Query functions
static bool Cocoa_VideoInit(SDL_VideoDevice *_this);
static void Cocoa_VideoQuit(SDL_VideoDevice *_this);
// Cocoa driver bootstrap functions
static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
{
@autoreleasepool {
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
CFBridgingRelease(device->internal);
SDL_free(device);
}
}
static SDL_VideoDevice *Cocoa_CreateDevice(void)
{
@autoreleasepool {
SDL_VideoDevice *device;
SDL_CocoaVideoData *data;
if (![NSThread isMainThread]) {
return NULL; // this doesn't SDL_SetError() because SDL_VideoInit is just going to overwrite it.
}
Cocoa_RegisterApp();
// Initialize all variables that we clean on shutdown
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (device) {
data = [[SDL_CocoaVideoData alloc] init];
} else {
data = nil;
}
if (!data) {
SDL_free(device);
return NULL;
}
device->internal = (SDL_VideoData *)CFBridgingRetain(data);
device->wakeup_lock = SDL_CreateMutex();
device->system_theme = Cocoa_GetSystemTheme();
// Set the function pointers
device->VideoInit = Cocoa_VideoInit;
device->VideoQuit = Cocoa_VideoQuit;
device->GetDisplayBounds = Cocoa_GetDisplayBounds;
device->GetDisplayUsableBounds = Cocoa_GetDisplayUsableBounds;
device->GetDisplayModes = Cocoa_GetDisplayModes;
device->SetDisplayMode = Cocoa_SetDisplayMode;
device->PumpEvents = Cocoa_PumpEvents;
device->WaitEventTimeout = Cocoa_WaitEventTimeout;
device->SendWakeupEvent = Cocoa_SendWakeupEvent;
device->SuspendScreenSaver = Cocoa_SuspendScreenSaver;
device->CreateSDLWindow = Cocoa_CreateWindow;
device->SetWindowTitle = Cocoa_SetWindowTitle;
device->SetWindowIcon = Cocoa_SetWindowIcon;
device->SetWindowPosition = Cocoa_SetWindowPosition;
device->SetWindowSize = Cocoa_SetWindowSize;
device->SetWindowMinimumSize = Cocoa_SetWindowMinimumSize;
device->SetWindowMaximumSize = Cocoa_SetWindowMaximumSize;
device->SetWindowAspectRatio = Cocoa_SetWindowAspectRatio;
device->SetWindowOpacity = Cocoa_SetWindowOpacity;
device->GetWindowSizeInPixels = Cocoa_GetWindowSizeInPixels;
device->ShowWindow = Cocoa_ShowWindow;
device->HideWindow = Cocoa_HideWindow;
device->RaiseWindow = Cocoa_RaiseWindow;
device->MaximizeWindow = Cocoa_MaximizeWindow;
device->MinimizeWindow = Cocoa_MinimizeWindow;
device->RestoreWindow = Cocoa_RestoreWindow;
device->SetWindowBordered = Cocoa_SetWindowBordered;
device->SetWindowResizable = Cocoa_SetWindowResizable;
device->SetWindowAlwaysOnTop = Cocoa_SetWindowAlwaysOnTop;
device->SetWindowFullscreen = Cocoa_SetWindowFullscreen;
device->GetWindowICCProfile = Cocoa_GetWindowICCProfile;
device->GetDisplayForWindow = Cocoa_GetDisplayForWindow;
device->SetWindowMouseRect = Cocoa_SetWindowMouseRect;
device->SetWindowMouseGrab = Cocoa_SetWindowMouseGrab;
device->SetWindowKeyboardGrab = Cocoa_SetWindowKeyboardGrab;
device->DestroyWindow = Cocoa_DestroyWindow;
device->SetWindowHitTest = Cocoa_SetWindowHitTest;
device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
device->UpdateWindowShape = Cocoa_UpdateWindowShape;
device->FlashWindow = Cocoa_FlashWindow;
device->SetWindowFocusable = Cocoa_SetWindowFocusable;
device->SetWindowParent = Cocoa_SetWindowParent;
device->SetWindowModal = Cocoa_SetWindowModal;
device->SyncWindow = Cocoa_SyncWindow;
#ifdef SDL_VIDEO_OPENGL_CGL
device->GL_LoadLibrary = Cocoa_GL_LoadLibrary;
device->GL_GetProcAddress = Cocoa_GL_GetProcAddress;
device->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
device->GL_CreateContext = Cocoa_GL_CreateContext;
device->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
device->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
device->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
device->GL_SwapWindow = Cocoa_GL_SwapWindow;
device->GL_DestroyContext = Cocoa_GL_DestroyContext;
device->GL_GetEGLSurface = NULL;
#endif
#ifdef SDL_VIDEO_OPENGL_EGL
#ifdef SDL_VIDEO_OPENGL_CGL
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
#endif
device->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
device->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
device->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
device->GL_CreateContext = Cocoa_GLES_CreateContext;
device->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
device->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
device->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
device->GL_SwapWindow = Cocoa_GLES_SwapWindow;
device->GL_DestroyContext = Cocoa_GLES_DestroyContext;
device->GL_GetEGLSurface = Cocoa_GLES_GetEGLSurface;
#ifdef SDL_VIDEO_OPENGL_CGL
}
#endif
#endif
#ifdef SDL_VIDEO_VULKAN
device->Vulkan_LoadLibrary = Cocoa_Vulkan_LoadLibrary;
device->Vulkan_UnloadLibrary = Cocoa_Vulkan_UnloadLibrary;
device->Vulkan_GetInstanceExtensions = Cocoa_Vulkan_GetInstanceExtensions;
device->Vulkan_CreateSurface = Cocoa_Vulkan_CreateSurface;
device->Vulkan_DestroySurface = Cocoa_Vulkan_DestroySurface;
#endif
#ifdef SDL_VIDEO_METAL
device->Metal_CreateView = Cocoa_Metal_CreateView;
device->Metal_DestroyView = Cocoa_Metal_DestroyView;
device->Metal_GetLayer = Cocoa_Metal_GetLayer;
#endif
device->StartTextInput = Cocoa_StartTextInput;
device->StopTextInput = Cocoa_StopTextInput;
device->UpdateTextInputArea = Cocoa_UpdateTextInputArea;
device->SetClipboardData = Cocoa_SetClipboardData;
device->GetClipboardData = Cocoa_GetClipboardData;
device->HasClipboardData = Cocoa_HasClipboardData;
device->free = Cocoa_DeleteDevice;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
return device;
}
}
VideoBootStrap COCOA_bootstrap = {
"cocoa", "SDL Cocoa video driver",
Cocoa_CreateDevice,
Cocoa_ShowMessageBox,
false
};
static bool Cocoa_VideoInit(SDL_VideoDevice *_this)
{
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
Cocoa_InitModes(_this);
Cocoa_InitKeyboard(_this);
if (!Cocoa_InitMouse(_this)) {
return false;
}
if (!Cocoa_InitPen(_this)) {
return false;
}
// Assume we have a mouse and keyboard
// We could use GCMouse and GCKeyboard if we needed to, as is done in SDL_uikitevents.m
SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL, false);
SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL, false);
data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, true);
data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, false);
SDL_AddHintCallback(SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY, Cocoa_MenuVisibilityCallback, NULL);
data.swaplock = SDL_CreateMutex();
if (!data.swaplock) {
return false;
}
return true;
}
}
void Cocoa_VideoQuit(SDL_VideoDevice *_this)
{
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
Cocoa_QuitModes(_this);
Cocoa_QuitKeyboard(_this);
Cocoa_QuitMouse(_this);
Cocoa_QuitPen(_this);
SDL_DestroyMutex(data.swaplock);
data.swaplock = NULL;
}
}
// This function assumes that it's called from within an autorelease pool
SDL_SystemTheme Cocoa_GetSystemTheme(void)
{
if (@available(macOS 10.14, *)) {
NSAppearance* appearance = [[NSApplication sharedApplication] effectiveAppearance];
if ([appearance.name containsString: @"Dark"]) {
return SDL_SYSTEM_THEME_DARK;
}
}
return SDL_SYSTEM_THEME_LIGHT;
}
// This function assumes that it's called from within an autorelease pool
NSImage *Cocoa_CreateImage(SDL_Surface *surface)
{
NSImage *img;
img = [[NSImage alloc] initWithSize:NSMakeSize(surface->w, surface->h)];
if (img == nil) {
return nil;
}
SDL_Surface **images = SDL_GetSurfaceImages(surface, NULL);
if (!images) {
return nil;
}
for (int i = 0; images[i]; ++i) {
SDL_Surface *converted = SDL_ConvertSurface(images[i], SDL_PIXELFORMAT_RGBA32);
if (!converted) {
SDL_free(images);
return nil;
}
// Premultiply the alpha channel
SDL_PremultiplySurfaceAlpha(converted, false);
NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:converted->w
pixelsHigh:converted->h
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:converted->pitch
bitsPerPixel:SDL_BITSPERPIXEL(converted->format)];
if (imgrep == nil) {
SDL_free(images);
SDL_DestroySurface(converted);
return nil;
}
// Copy the pixels
Uint8 *pixels = [imgrep bitmapData];
SDL_memcpy(pixels, converted->pixels, (size_t)converted->h * converted->pitch);
SDL_DestroySurface(converted);
// Add the image representation
[img addRepresentation:imgrep];
}
SDL_free(images);
return img;
}
/*
* macOS log support.
*
* This doesn't really have anything to do with the interfaces of the SDL video
* subsystem, but we need to stuff this into an Objective-C source code file.
*
* NOTE: This is copypasted in src/video/uikit/SDL_uikitvideo.m! Be sure both
* versions remain identical!
*/
void SDL_NSLog(const char *prefix, const char *text)
{
@autoreleasepool {
NSString *nsText = [NSString stringWithUTF8String:text];
if (prefix && *prefix) {
NSString *nsPrefix = [NSString stringWithUTF8String:prefix];
NSLog(@"%@%@", nsPrefix, nsText);
} else {
NSLog(@"%@", nsText);
}
}
}
#endif // SDL_VIDEO_DRIVER_COCOA

View file

@ -0,0 +1,52 @@
/*
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 Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.h.
*/
#include "SDL_internal.h"
#ifndef SDL_cocoavulkan_h_
#define SDL_cocoavulkan_h_
#include "../SDL_vulkan_internal.h"
#include "../SDL_sysvideo.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_COCOA)
extern bool Cocoa_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
extern char const* const* Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
extern bool Cocoa_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
extern void Cocoa_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
#endif
#endif // SDL_cocoavulkan_h_

View file

@ -0,0 +1,304 @@
/*
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 Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
* SDL_x11vulkan.c.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_COCOA)
#include "SDL_cocoavideo.h"
#include "SDL_cocoawindow.h"
#include "SDL_cocoametalview.h"
#include "SDL_cocoavulkan.h"
#include <dlfcn.h>
const char *defaultPaths[] = {
"vulkan.framework/vulkan",
"libvulkan.1.dylib",
"libvulkan.dylib",
"MoltenVK.framework/MoltenVK",
"libMoltenVK.dylib"
};
// Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF.
#define DEFAULT_HANDLE RTLD_DEFAULT
bool Cocoa_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
VkExtensionProperties *extensions = NULL;
Uint32 extensionCount = 0;
bool hasSurfaceExtension = false;
bool hasMetalSurfaceExtension = false;
bool hasMacOSSurfaceExtension = false;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan Portability library is already loaded.");
}
// Load the Vulkan loader library
if (!path) {
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
}
if (!path) {
// Handle the case where Vulkan Portability is linked statically.
vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE,
"vkGetInstanceProcAddr");
}
if (vkGetInstanceProcAddr) {
_this->vulkan_config.loader_handle = DEFAULT_HANDLE;
} else {
const char **paths;
const char *foundPath = NULL;
int numPaths;
int i;
if (path) {
paths = &path;
numPaths = 1;
} else {
/* Look for framework or .dylib packaged with the application
* instead. */
paths = defaultPaths;
numPaths = SDL_arraysize(defaultPaths);
}
for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) {
foundPath = paths[i];
_this->vulkan_config.loader_handle = SDL_LoadObject(foundPath);
}
if (_this->vulkan_config.loader_handle == NULL) {
return SDL_SetError("Failed to load Vulkan Portability library");
}
SDL_strlcpy(_this->vulkan_config.loader_path, foundPath,
SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
}
if (!vkGetInstanceProcAddr) {
SDL_SetError("Failed to find %s in either executable or %s: %s",
"vkGetInstanceProcAddr",
_this->vulkan_config.loader_path,
(const char *)dlerror());
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 (Uint32 i = 0; i < extensionCount; i++) {
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasSurfaceExtension = true;
} else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasMetalSurfaceExtension = true;
} else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasMacOSSurfaceExtension = true;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
} else if (!hasMetalSurfaceExtension && !hasMacOSSurfaceExtension) {
SDL_SetError("Installed Vulkan Portability library doesn't implement the " VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME " extensions");
goto fail;
}
return true;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return false;
}
void Cocoa_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
{
if (_this->vulkan_config.loader_handle) {
if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) {
SDL_UnloadObject(_this->vulkan_config.loader_handle);
}
_this->vulkan_config.loader_handle = NULL;
}
}
char const* const* Cocoa_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
Uint32 *count)
{
static const char *const extensionsForCocoa[] = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
};
if(count) {
*count = SDL_arraysize(extensionsForCocoa);
}
return extensionsForCocoa;
}
static bool Cocoa_Vulkan_CreateSurfaceViaMetalView(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface,
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT,
PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK)
{
VkResult rc;
SDL_MetalView metalview = Cocoa_Metal_CreateView(_this, window);
if (metalview == NULL) {
return false;
}
if (vkCreateMetalSurfaceEXT) {
VkMetalSurfaceCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pLayer = (__bridge const CAMetalLayer *)
Cocoa_Metal_GetLayer(_this, metalview);
rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
if (rc != VK_SUCCESS) {
Cocoa_Metal_DestroyView(_this, metalview);
return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc));
}
} else {
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = (const void *)metalview;
rc = vkCreateMacOSSurfaceMVK(instance, &createInfo,
NULL, surface);
if (rc != VK_SUCCESS) {
Cocoa_Metal_DestroyView(_this, metalview);
return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc));
}
}
/* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call
* Metal_DestroyView from. Right now the metal view's ref count is +2 (one
* from returning a new view object in CreateView, and one because it's
* a subview of the window.) If we release the view here to make it +1, it
* will be destroyed when the window is destroyed.
*
* TODO: Now that we have SDL_Vulkan_DestroySurface someone with enough
* knowledge of Metal can proceed. */
CFBridgingRelease(metalview);
return true; // success!
}
bool Cocoa_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT =
(PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr(
instance,
"vkCreateMetalSurfaceEXT");
PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK =
(PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr(
instance,
"vkCreateMacOSSurfaceMVK");
VkResult rc;
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
if (!vkCreateMetalSurfaceEXT && !vkCreateMacOSSurfaceMVK) {
return SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_MACOS_SURFACE_EXTENSION_NAME
" extensions are not enabled in the Vulkan instance.");
}
if (window->flags & SDL_WINDOW_EXTERNAL) {
@autoreleasepool {
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
if (![data.sdlContentView.layer isKindOfClass:[CAMetalLayer class]]) {
[data.sdlContentView setLayer:[CAMetalLayer layer]];
}
if (vkCreateMetalSurfaceEXT) {
VkMetalSurfaceCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pLayer = (CAMetalLayer *)data.sdlContentView.layer;
rc = vkCreateMetalSurfaceEXT(instance, &createInfo, allocator, surface);
if (rc != VK_SUCCESS) {
return SDL_SetError("vkCreateMetalSurfaceEXT failed: %s", SDL_Vulkan_GetResultString(rc));
}
} else {
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = (__bridge const void *)data.sdlContentView;
rc = vkCreateMacOSSurfaceMVK(instance, &createInfo,
allocator, surface);
if (rc != VK_SUCCESS) {
return SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", SDL_Vulkan_GetResultString(rc));
}
}
}
} else {
return Cocoa_Vulkan_CreateSurfaceViaMetalView(_this, window, instance, allocator, surface, vkCreateMetalSurfaceEXT, vkCreateMacOSSurfaceMVK);
}
return true;
}
void Cocoa_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);
// TODO: Add CFBridgingRelease(metalview) here perhaps?
}
}
#endif

View file

@ -0,0 +1,199 @@
/*
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_cocoawindow_h_
#define SDL_cocoawindow_h_
#import <Cocoa/Cocoa.h>
#ifdef SDL_VIDEO_OPENGL_EGL
#include "../SDL_egl_c.h"
#endif
#define SDL_METALVIEW_TAG 255
@class SDL_CocoaWindowData;
typedef enum
{
PENDING_OPERATION_NONE = 0x00,
PENDING_OPERATION_ENTER_FULLSCREEN = 0x01,
PENDING_OPERATION_LEAVE_FULLSCREEN = 0x02,
PENDING_OPERATION_MINIMIZE = 0x04,
PENDING_OPERATION_ZOOM = 0x08
} PendingWindowOperation;
@interface SDL3Cocoa_WindowListener : NSResponder <NSWindowDelegate>
{
/* SDL_CocoaWindowData owns this Listener and has a strong reference to it.
* To avoid reference cycles, we could have either a weak or an
* unretained ref to the WindowData. */
__weak SDL_CocoaWindowData *_data;
BOOL observingVisible;
BOOL wasCtrlLeft;
BOOL wasVisible;
BOOL isFullscreenSpace;
BOOL inFullscreenTransition;
PendingWindowOperation pendingWindowOperation;
BOOL isMoving;
BOOL isMiniaturizing;
NSInteger focusClickPending;
float pendingWindowWarpX, pendingWindowWarpY;
BOOL isDragAreaRunning;
NSTimer *liveResizeTimer;
}
- (BOOL)isTouchFromTrackpad:(NSEvent *)theEvent;
- (void)listen:(SDL_CocoaWindowData *)data;
- (void)pauseVisibleObservation;
- (void)resumeVisibleObservation;
- (BOOL)setFullscreenSpace:(BOOL)state;
- (BOOL)isInFullscreenSpace;
- (BOOL)isInFullscreenSpaceTransition;
- (void)addPendingWindowOperation:(PendingWindowOperation)operation;
- (void)close;
- (BOOL)isMoving;
- (BOOL)isMovingOrFocusClickPending;
- (void)setFocusClickPending:(NSInteger)button;
- (void)clearFocusClickPending:(NSInteger)button;
- (void)updateIgnoreMouseState:(NSEvent *)theEvent;
- (void)setPendingMoveX:(float)x Y:(float)y;
- (void)windowDidFinishMoving;
- (void)onMovingOrFocusClickPendingStateCleared;
// Window delegate functionality
- (BOOL)windowShouldClose:(id)sender;
- (void)windowDidExpose:(NSNotification *)aNotification;
- (void)windowDidChangeOcclusionState:(NSNotification *)aNotification;
- (void)windowWillStartLiveResize:(NSNotification *)aNotification;
- (void)windowDidEndLiveResize:(NSNotification *)aNotification;
- (void)windowDidMove:(NSNotification *)aNotification;
- (void)windowDidResize:(NSNotification *)aNotification;
- (void)windowDidMiniaturize:(NSNotification *)aNotification;
- (void)windowDidDeminiaturize:(NSNotification *)aNotification;
- (void)windowDidBecomeKey:(NSNotification *)aNotification;
- (void)windowDidResignKey:(NSNotification *)aNotification;
- (void)windowDidChangeBackingProperties:(NSNotification *)aNotification;
- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification;
- (void)windowDidChangeScreen:(NSNotification *)aNotification;
- (void)windowWillEnterFullScreen:(NSNotification *)aNotification;
- (void)windowDidEnterFullScreen:(NSNotification *)aNotification;
- (void)windowWillExitFullScreen:(NSNotification *)aNotification;
- (void)windowDidExitFullScreen:(NSNotification *)aNotification;
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
// See if event is in a drag area, toggle on window dragging.
- (void)updateHitTest;
- (BOOL)processHitTest:(NSEvent *)theEvent;
// Window event handling
- (void)mouseDown:(NSEvent *)theEvent;
- (void)rightMouseDown:(NSEvent *)theEvent;
- (void)otherMouseDown:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)rightMouseUp:(NSEvent *)theEvent;
- (void)otherMouseUp:(NSEvent *)theEvent;
- (void)mouseMoved:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)rightMouseDragged:(NSEvent *)theEvent;
- (void)otherMouseDragged:(NSEvent *)theEvent;
- (void)scrollWheel:(NSEvent *)theEvent;
- (void)touchesBeganWithEvent:(NSEvent *)theEvent;
- (void)touchesMovedWithEvent:(NSEvent *)theEvent;
- (void)touchesEndedWithEvent:(NSEvent *)theEvent;
- (void)touchesCancelledWithEvent:(NSEvent *)theEvent;
// Touch event handling
- (void)handleTouches:(NSTouchPhase)phase withEvent:(NSEvent *)theEvent;
// Tablet event handling (but these also come through on mouse events sometimes!)
- (void)tabletProximity:(NSEvent *)theEvent;
- (void)tabletPoint:(NSEvent *)theEvent;
@end
/* *INDENT-ON* */
@class SDL3OpenGLContext;
@class SDL_CocoaVideoData;
@interface SDL_CocoaWindowData : NSObject
@property(nonatomic) SDL_Window *window;
@property(nonatomic) NSWindow *nswindow;
@property(nonatomic) NSView *sdlContentView;
@property(nonatomic) NSMutableArray *nscontexts;
@property(nonatomic) BOOL in_blocking_transition;
@property(nonatomic) BOOL fullscreen_space_requested;
@property(nonatomic) BOOL was_zoomed;
@property(nonatomic) NSInteger window_number;
@property(nonatomic) NSInteger flash_request;
@property(nonatomic) SDL_Window *keyboard_focus;
@property(nonatomic) SDL3Cocoa_WindowListener *listener;
@property(nonatomic) NSModalSession modal_session;
@property(nonatomic) SDL_CocoaVideoData *videodata;
@property(nonatomic) bool pending_size;
@property(nonatomic) bool pending_position;
@property(nonatomic) bool border_toggled;
#ifdef SDL_VIDEO_OPENGL_EGL
@property(nonatomic) EGLSurface egl_surface;
#endif
@end
extern bool b_inModeTransition;
extern bool Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void Cocoa_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
extern bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
extern bool Cocoa_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
extern void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
extern void Cocoa_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
extern void Cocoa_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top);
extern SDL_FullscreenResult Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
extern void *Cocoa_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern SDL_DisplayID Cocoa_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Cocoa_SetWindowHitTest(SDL_Window *window, bool enabled);
extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, bool accept);
extern bool Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
extern bool Cocoa_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
extern bool Cocoa_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
extern bool Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void Cocoa_MenuVisibilityCallback(void *userdata, const char *name, const char *oldValue, const char *newValue);
#endif // SDL_cocoawindow_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
/*
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_D3D12_H
#define SDL_D3D12_H
#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
/* From the DirectX-Headers build system:
* "MinGW has RPC headers which define old versions, and complain if D3D
* headers are included before the RPC headers, since D3D headers were
* generated with new MIDL and "require" new RPC headers."
*/
#define __REQUIRED_RPCNDR_H_VERSION__ 475
// May not be defined in winapifamily.h, can safely be ignored
#ifndef WINAPI_PARTITION_GAMES
#define WINAPI_PARTITION_GAMES 0
#endif // WINAPI_PARTITION_GAMES
#define COBJMACROS
#include "d3d12.h"
#include <dxgi1_6.h>
#include <dxgidebug.h>
#define D3D_GUID(X) &(X)
#define D3D_SAFE_RELEASE(X) \
if (X) { \
(X)->lpVtbl->Release(X); \
X = NULL; \
}
/* Some D3D12 calls are mismatched between Windows/Xbox, so we need to wrap the
* C function ourselves :(
*/
#define D3D_CALL_RET(THIS, FUNC, ...) (THIS)->lpVtbl->FUNC((THIS), ##__VA_ARGS__)
#else // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
#if defined(SDL_PLATFORM_XBOXONE)
#include <d3d12_x.h>
#else // SDL_PLATFORM_XBOXSERIES
#include <d3d12_xs.h>
#endif
#define D3D_GUID(X) (X)
#define D3D_SAFE_RELEASE(X) \
if (X) { \
(X)->Release(); \
X = NULL; \
}
// Older versions of the Xbox GDK may not have this defined
#ifndef D3D12_TEXTURE_DATA_PITCH_ALIGNMENT
#define D3D12_TEXTURE_DATA_PITCH_ALIGNMENT 256
#endif
#ifndef D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE
#define D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE ((D3D12_RESOURCE_STATES) (0x40 | 0x80))
#endif
#ifndef D3D12_HEAP_TYPE_GPU_UPLOAD
#define D3D12_HEAP_TYPE_GPU_UPLOAD ((D3D12_HEAP_TYPE) 5)
#endif
// DXGI_PRESENT flags are removed on Xbox
#define DXGI_PRESENT_ALLOW_TEARING 0
// Xbox D3D12 does not define the COBJMACROS, so we need to define them ourselves
#include "SDL_d3d12_xbox_cmacros.h"
// They don't even define the CMACROS for ID3DBlob, come on man
#define ID3D10Blob_GetBufferPointer(blob) blob->GetBufferPointer()
#define ID3D10Blob_GetBufferSize(blob) blob->GetBufferSize()
#define ID3D10Blob_Release(blob) blob->Release()
/* Xbox's D3D12 ABI actually varies from Windows, if a function does not exist
* in the above header then you need to use this instead :(
*/
#define D3D_CALL_RET(THIS, FUNC, RETVAL, ...) *(RETVAL) = (THIS)->FUNC(__VA_ARGS__)
#endif // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
#endif // SDL_D3D12_H

File diff suppressed because it is too large Load diff

34949
vendor/sdl-3.2.10/src/video/directx/d3d12.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
/*
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.
*/
// Build and run this any time you update d3d12.h/d3d12sdklayers.h!
using System.IO;
class Program
{
static void GenMacros(string[] input, StreamWriter output)
{
for (int i = 0; i < input.Length; i += 1)
{
if (input[i].StartsWith("#define I"))
{
// Strip out the bad ABI calls, use D3D_CALL_RET instead!
if (input[i].Contains("_GetDesc(") ||
input[i].Contains("_GetDesc1(") ||
input[i].Contains("_GetCPUDescriptorHandleForHeapStart(") ||
input[i].Contains("_GetGPUDescriptorHandleForHeapStart(") ||
input[i].Contains("_GetResourceAllocationInfo(") ||
input[i].Contains("_GetResourceAllocationInfo1(") ||
input[i].Contains("_GetResourceAllocationInfo2(") ||
input[i].Contains("_GetResourceAllocationInfo3(") ||
input[i].Contains("_GetCustomHeapProperties(") ||
input[i].Contains("_GetAdapterLuid(") ||
input[i].Contains("_GetLUID(") ||
input[i].Contains("_GetProgramIdentifier(") ||
input[i].Contains("_GetNodeID(") ||
input[i].Contains("_GetEntrypointID("))
{
// May as well skip the next line...
i += 1;
continue;
}
// The first line is fine as-is.
output.WriteLine(input[i]);
// The second line, however...
i += 1;
string notThis;
if (input[i].LastIndexOf("This,") > -1)
{
// Function with arguments
notThis = "This,";
}
else
{
// Function with no arguments
notThis = "This";
}
int lastNotThis = input[i].LastIndexOf(notThis);
string alias = input[i].Substring(0, lastNotThis).Replace("lpVtbl -> ", "");
string definition = input[i].Substring(lastNotThis).Replace(notThis, "");
output.WriteLine(alias + definition);
}
}
}
static void Main(string[] args)
{
using (FileStream SDL_d3d12_xbox_cmacros_h = File.OpenWrite("SDL_d3d12_xbox_cmacros.h"))
using (StreamWriter output = new StreamWriter(SDL_d3d12_xbox_cmacros_h))
{
output.WriteLine("/* This file is autogenerated, DO NOT MODIFY */");
GenMacros(File.ReadAllLines("d3d12.h"), output);
GenMacros(File.ReadAllLines("d3d12sdklayers.h"), output);
}
}
}

View file

@ -0,0 +1,38 @@
/*
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_DUMMY
/* Being a null driver, there's no event stream. We just define stubs for
most of the API. */
#include "../../events/SDL_events_c.h"
#include "SDL_nullvideo.h"
#include "SDL_nullevents_c.h"
void DUMMY_PumpEvents(SDL_VideoDevice *_this)
{
// do nothing.
}
#endif // SDL_VIDEO_DRIVER_DUMMY

View file

@ -0,0 +1,31 @@
/*
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_nullevents_c_h_
#define SDL_nullevents_c_h_
#include "SDL_internal.h"
#include "SDL_nullvideo.h"
extern void DUMMY_PumpEvents(SDL_VideoDevice *_this);
#endif // SDL_nullevents_c_h_

View file

@ -0,0 +1,78 @@
/*
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_DUMMY
#include "../SDL_sysvideo.h"
#include "../../SDL_properties_c.h"
#include "SDL_nullframebuffer_c.h"
#define DUMMY_SURFACE "SDL.internal.window.surface"
bool SDL_DUMMY_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch)
{
SDL_Surface *surface;
const SDL_PixelFormat surface_format = SDL_PIXELFORMAT_XRGB8888;
int w, h;
// Create a new framebuffer
SDL_GetWindowSizeInPixels(window, &w, &h);
surface = SDL_CreateSurface(w, h, surface_format);
if (!surface) {
return false;
}
// Save the info and return!
SDL_SetSurfaceProperty(SDL_GetWindowProperties(window), DUMMY_SURFACE, surface);
*format = surface_format;
*pixels = surface->pixels;
*pitch = surface->pitch;
return true;
}
bool SDL_DUMMY_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects)
{
static int frame_number;
SDL_Surface *surface;
surface = (SDL_Surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), DUMMY_SURFACE, NULL);
if (!surface) {
return SDL_SetError("Couldn't find dummy surface for window");
}
// Send the data to the display
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_DUMMY_SAVE_FRAMES, false)) {
char file[128];
(void)SDL_snprintf(file, sizeof(file), "SDL_window%" SDL_PRIu32 "-%8.8d.bmp",
SDL_GetWindowID(window), ++frame_number);
SDL_SaveBMP(surface, file);
}
return true;
}
void SDL_DUMMY_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_ClearProperty(SDL_GetWindowProperties(window), DUMMY_SURFACE);
}
#endif // SDL_VIDEO_DRIVER_DUMMY

View file

@ -0,0 +1,31 @@
/*
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_nullframebuffer_c_h_
#define SDL_nullframebuffer_c_h_
#include "SDL_internal.h"
extern bool SDL_DUMMY_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, void **pixels, int *pitch);
extern bool SDL_DUMMY_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects);
extern void SDL_DUMMY_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_nullframebuffer_c_h_

View file

@ -0,0 +1,182 @@
/*
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_DUMMY
/* Dummy SDL video driver implementation; this is just enough to make an
* SDL-based application THINK it's got a working video driver, for
* applications that call SDL_Init(SDL_INIT_VIDEO) when they don't need it,
* and also for use as a collection of stubs when porting SDL to a new
* platform for which you haven't yet written a valid video driver.
*
* This is also a great way to determine bottlenecks: if you think that SDL
* is a performance problem for a given platform, enable this driver, and
* then see if your application runs faster without video overhead.
*
* Initial work by Ryan C. Gordon (icculus@icculus.org). A good portion
* of this was cut-and-pasted from Stephane Peter's work in the AAlib
* SDL video driver. Renamed to "DUMMY" by Sam Lantinga.
*/
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../events/SDL_events_c.h"
#ifdef SDL_INPUT_LINUXEV
#include "../../core/linux/SDL_evdev.h"
#endif
#include "SDL_nullvideo.h"
#include "SDL_nullevents_c.h"
#include "SDL_nullframebuffer_c.h"
#define DUMMYVID_DRIVER_NAME "dummy"
#define DUMMYVID_DRIVER_EVDEV_NAME "evdev"
// Initialization/Query functions
static bool DUMMY_VideoInit(SDL_VideoDevice *_this);
static void DUMMY_VideoQuit(SDL_VideoDevice *_this);
static bool DUMMY_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->pending.x, window->pending.y);
return true;
}
static void DUMMY_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h);
}
// DUMMY driver bootstrap functions
static bool DUMMY_Available(const char *enable_hint)
{
const char *hint = SDL_GetHint(SDL_HINT_VIDEO_DRIVER);
if (hint) {
if (SDL_strcmp(hint, enable_hint) == 0) {
return true;
}
}
return false;
}
static void DUMMY_DeleteDevice(SDL_VideoDevice *device)
{
SDL_free(device);
}
static SDL_VideoDevice *DUMMY_InternalCreateDevice(const char *enable_hint)
{
SDL_VideoDevice *device;
if (!DUMMY_Available(enable_hint)) {
return NULL;
}
// Initialize all variables that we clean on shutdown
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (!device) {
return NULL;
}
device->is_dummy = true;
// Set the function pointers
device->VideoInit = DUMMY_VideoInit;
device->VideoQuit = DUMMY_VideoQuit;
device->PumpEvents = DUMMY_PumpEvents;
device->SetWindowSize = DUMMY_SetWindowSize;
device->SetWindowPosition = DUMMY_SetWindowPosition;
device->CreateWindowFramebuffer = SDL_DUMMY_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = SDL_DUMMY_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = SDL_DUMMY_DestroyWindowFramebuffer;
device->free = DUMMY_DeleteDevice;
return device;
}
static SDL_VideoDevice *DUMMY_CreateDevice(void)
{
return DUMMY_InternalCreateDevice(DUMMYVID_DRIVER_NAME);
}
VideoBootStrap DUMMY_bootstrap = {
DUMMYVID_DRIVER_NAME, "SDL dummy video driver",
DUMMY_CreateDevice,
NULL, // no ShowMessageBox implementation
false
};
#ifdef SDL_INPUT_LINUXEV
static void DUMMY_EVDEV_Poll(SDL_VideoDevice *_this)
{
(void)_this;
SDL_EVDEV_Poll();
}
static SDL_VideoDevice *DUMMY_EVDEV_CreateDevice(void)
{
SDL_VideoDevice *device = DUMMY_InternalCreateDevice(DUMMYVID_DRIVER_EVDEV_NAME);
if (device) {
device->PumpEvents = DUMMY_EVDEV_Poll;
}
return device;
}
VideoBootStrap DUMMY_evdev_bootstrap = {
DUMMYVID_DRIVER_EVDEV_NAME, "SDL dummy video driver with evdev",
DUMMY_EVDEV_CreateDevice,
NULL, // no ShowMessageBox implementation
false
};
#endif // SDL_INPUT_LINUXEV
bool DUMMY_VideoInit(SDL_VideoDevice *_this)
{
SDL_DisplayMode mode;
// Use a fake 32-bpp desktop mode
SDL_zero(mode);
mode.format = SDL_PIXELFORMAT_XRGB8888;
mode.w = 1024;
mode.h = 768;
if (SDL_AddBasicVideoDisplay(&mode) == 0) {
return false;
}
#ifdef SDL_INPUT_LINUXEV
SDL_EVDEV_Init();
#endif
// We're done!
return true;
}
void DUMMY_VideoQuit(SDL_VideoDevice *_this)
{
#ifdef SDL_INPUT_LINUXEV
SDL_EVDEV_Quit();
#endif
}
#endif // SDL_VIDEO_DRIVER_DUMMY

View file

@ -0,0 +1,28 @@
/*
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_nullvideo_h_
#define SDL_nullvideo_h_
#include "../SDL_sysvideo.h"
#endif // SDL_nullvideo_h_

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more