upgrading to sdl 3.16

This commit is contained in:
peterino2 2025-06-07 14:05:12 -07:00
parent 826c057bea
commit 6f2396c814
108 changed files with 2380 additions and 9270 deletions

View File

@ -9,7 +9,7 @@ This is a port of [SDL](https://libsdl.org/) to the Zig build system, packaged f
## Usage
Requires Zig 0.14.0 or 0.15.0-dev (master).
Requires Zig 0.14.1 or 0.15.0-dev (master).
```sh
zig fetch --save git+https://github.com/castholm/SDL.git
@ -21,6 +21,7 @@ const sdl_dep = b.dependency("sdl", .{
.optimize = optimize,
//.preferred_linkage = .static,
//.strip = null,
//.sanitize_c = null,
//.pic = null,
//.lto = null,
//.emscripten_pthreads = false,
@ -79,7 +80,7 @@ When building for non-native macOS targets (for example for x86-64 from an AArch
```sh
sysroot_path=$(xcrun --sdk macosx --show-sdk-path)
zig build -Dtarget=x86_64-macos-none --sysroot "$sysroot_path"
zig build -Dtarget=x86_64-macos --sysroot "$sysroot_path"
```
### Emscripten (web)
@ -103,7 +104,7 @@ When building for Emscripten, you need to provide a path to the Emscripten sysro
```sh
cache_path=$(em-config CACHE)
sysroot_path="$cache_path/sysroot"
zig build -Dtarget=wasm32-emscripten-musl --sysroot "$sysroot_path"
zig build -Dtarget=wasm32-emscripten --sysroot "$sysroot_path"
```
Depending on the state of your Emscripten cache, you might need to run `embuilder build sysroot` to ensure that the Emscripten sysroot is built before you run `zig build`.

112
lib/sdl3/SDL/build.zig vendored
View File

@ -3,15 +3,15 @@
const std = @import("std");
pub const version: std.SemanticVersion = .{ .major = 3, .minor = 2, .patch = 10 };
pub const version: std.SemanticVersion = .{ .major = 3, .minor = 2, .patch = 16 };
const formatted_version = std.fmt.comptimePrint("SDL3-{}", .{version});
pub const vendor_info = "https://github.com/castholm/SDL 0.2.1";
pub const vendor_info = "https://github.com/castholm/SDL 0.2.4";
pub const revision = formatted_version ++ " (" ++ vendor_info ++ ")";
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
var preferred_linkage: std.builtin.LinkMode = b.option(
const preferred_linkage = b.option(
std.builtin.LinkMode,
"preferred_linkage",
"Prefer building statically or dynamically linked libraries (default: static)",
@ -20,23 +20,50 @@ pub fn build(b: *std.Build) void {
"preferred_link_mode",
"Deprecated; use 'preferred_linkage' instead",
) orelse .static;
preferred_linkage = .dynamic;
const strip = b.option(
bool,
"strip",
"Strip debug symbols (default: varies)",
);
const sanitize_c = b.option(
enum { off, trap, full }, // TODO: Change to std.zig.SanitizeC after 0.15
"sanitize_c",
"Detect C undefined behavior (default: varies)",
);
const legacy_sanitize_c_field = @FieldType(std.Build.Module.CreateOptions, "sanitize_c") == ?bool;
const resolved_sanitize_c = if (sanitize_c) |x| switch (legacy_sanitize_c_field) {
true => switch (x) {
.off => false,
.trap, .full => true,
},
false => @as(std.zig.SanitizeC, switch (x) {
.off => .off,
.trap => .trap,
.full => .full,
}),
} else null;
const pic = b.option(
bool,
"pic",
"Produce position-independent code (default: varies)",
);
const lto = b.option(
bool,
enum { true, false, none, full, thin }, // TODO: Change to std.zig.LtoMode after 0.15
"lto",
"Perform link time optimization (default: varies)",
);
const legacy_lto_field = !@hasField(std.Build.Step.Compile, "lto");
const resolved_lto = if (lto) |x| switch (legacy_lto_field) {
true => switch (x) {
.false, .none => false,
.true, .full, .thin => true,
},
false => @as(std.zig.LtoMode, switch (x) {
.false, .none => .none,
.true, .full => .full,
.thin => .thin,
}),
} else null;
const emscripten_pthreads = b.option(
bool,
"emscripten_pthreads",
@ -52,11 +79,12 @@ pub fn build(b: *std.Build) void {
var linux = false;
var linux_deps_values: ?LinuxDepsValues = null;
var macos = false;
var macos_system_include_path: ?std.Build.LazyPath = null;
var macos_system_framework_path: ?std.Build.LazyPath = null;
var macos_library_path: ?std.Build.LazyPath = null;
var emscripten = false;
var emscripten_system_include_path: ?std.Build.LazyPath = null;
var system_include_path: ?std.Build.LazyPath = null;
var system_framework_path: ?std.Build.LazyPath = null;
var library_path: ?std.Build.LazyPath = null;
var glibc = false;
var musl = false;
switch (target.result.os.tag) {
.windows => {
windows = true;
@ -66,13 +94,15 @@ pub fn build(b: *std.Build) void {
if (b.lazyImport(@This(), "sdl_linux_deps")) |build_zig| {
linux_deps_values = LinuxDepsValues.fromBuildZig(b, build_zig);
}
glibc = target.result.abi.isGnu();
musl = target.result.abi.isMusl();
},
.macos => {
macos = true;
if (b.sysroot) |sysroot| {
macos_system_include_path = .{ .cwd_relative = b.pathJoin(&.{ sysroot, "usr/include" }) };
macos_system_framework_path = .{ .cwd_relative = b.pathJoin(&.{ sysroot, "System/Library/Frameworks" }) };
macos_library_path = .{ .cwd_relative = "/usr/lib" }; // ???
system_include_path = .{ .cwd_relative = b.pathJoin(&.{ sysroot, "usr/include" }) };
system_framework_path = .{ .cwd_relative = b.pathJoin(&.{ sysroot, "System/Library/Frameworks" }) };
library_path = .{ .cwd_relative = "/usr/lib" }; // ???
} else if (!target.query.isNative()) {
std.log.err("'--sysroot' is required when building SDL for non-native macOS targets", .{});
std.process.exit(1);
@ -81,7 +111,7 @@ pub fn build(b: *std.Build) void {
.emscripten => {
emscripten = true;
if (b.sysroot) |sysroot| {
emscripten_system_include_path = .{ .cwd_relative = b.pathJoin(&.{ sysroot, "include" }) };
system_include_path = .{ .cwd_relative = b.pathJoin(&.{ sysroot, "include" }) };
} else {
std.log.err("'--sysroot' is required when building SDL for Emscripten", .{});
std.process.exit(1);
@ -147,8 +177,8 @@ pub fn build(b: *std.Build) void {
.HAVE_WCSTOL = windows or linux or macos or emscripten,
.HAVE_STRLEN = windows or linux or macos or emscripten,
.HAVE_STRNLEN = windows or linux or macos or emscripten,
.HAVE_STRLCPY = macos or emscripten,
.HAVE_STRLCAT = macos or emscripten,
.HAVE_STRLCPY = linux and musl or macos or emscripten,
.HAVE_STRLCAT = linux and musl or macos or emscripten,
.HAVE_STRPBRK = windows or linux or macos or emscripten,
.HAVE__STRREV = windows,
.HAVE_INDEX = linux or macos or emscripten,
@ -199,10 +229,10 @@ pub fn build(b: *std.Build) void {
.HAVE_FMOD = windows or linux or macos or emscripten,
.HAVE_FMODF = windows or linux or macos or emscripten,
.HAVE_ISINF = windows or linux or macos or emscripten,
.HAVE_ISINFF = linux or emscripten,
.HAVE_ISINFF = linux and !musl or emscripten,
.HAVE_ISINF_FLOAT_MACRO = windows or linux or macos or emscripten,
.HAVE_ISNAN = windows or linux or macos or emscripten,
.HAVE_ISNANF = linux or emscripten,
.HAVE_ISNANF = linux and !musl or emscripten,
.HAVE_ISNAN_FLOAT_MACRO = windows or linux or macos or emscripten,
.HAVE_LOG = windows or linux or macos or emscripten,
.HAVE_LOGF = windows or linux or macos or emscripten,
@ -227,9 +257,9 @@ pub fn build(b: *std.Build) void {
.HAVE_TRUNC = windows or linux or macos or emscripten,
.HAVE_TRUNCF = windows or linux or macos or emscripten,
.HAVE__FSEEKI64 = windows,
.HAVE_FOPEN64 = windows or linux or emscripten,
.HAVE_FOPEN64 = windows or linux and !musl or emscripten,
.HAVE_FSEEKO = windows or linux or macos or emscripten,
.HAVE_FSEEKO64 = windows or linux or emscripten,
.HAVE_FSEEKO64 = windows or linux and !musl or emscripten,
.HAVE_MEMFD_CREATE = linux,
.HAVE_POSIX_FALLOCATE = linux or emscripten,
.HAVE_SIGACTION = linux or macos or emscripten,
@ -543,10 +573,11 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.link_libc = true,
.strip = strip,
.sanitize_c = resolved_sanitize_c,
.pic = pic,
});
const sdl_lib = b.addLibrary(.{
.linkage = .dynamic, //if (emscripten) .static else preferred_linkage,
.linkage = if (emscripten) .static else preferred_linkage,
.name = "SDL3",
.root_module = sdl_mod,
.version = .{
@ -556,7 +587,11 @@ pub fn build(b: *std.Build) void {
},
.use_llvm = if (emscripten) true else null,
});
sdl_lib.want_lto = lto;
if (legacy_lto_field) {
sdl_lib.want_lto = resolved_lto;
} else {
sdl_lib.lto = resolved_lto;
}
sdl_mod.addCMacro("USING_GENERATED_CONFIG_H", "1");
sdl_mod.addCMacro("SDL_BUILD_MAJOR_VERSION", std.fmt.comptimePrint("{}", .{version.major}));
@ -583,25 +618,26 @@ pub fn build(b: *std.Build) void {
if (linux_deps_values) |deps_values| {
sdl_mod.addIncludePath(deps_values.dependency.path("src"));
sdl_mod.addSystemIncludePath(deps_values.dependency.path("include"));
if (target.result.cpu.arch == .x86_64 and target.result.abi.isGnu()) {
// Currently, the only difference between these two sets of target-specific headers
// is that the x86_64 one defines G_VA_COPY_AS_ARRAY and the aarch64 one doesn't.
if (target.result.cpu.arch == .x86_64 and glibc) {
sdl_mod.addSystemIncludePath(deps_values.dependency.path("include/x86_64-linux-gnu"));
}
if (target.result.cpu.arch == .aarch64 and target.result.abi.isGnu()) {
// TODO: musl targets can piggyback off of the aarch64-linux-gnu headers for now because
// they are identical to their x86_64-linux-musl and aarch64-linux-musl equivalents.
if (target.result.cpu.arch == .aarch64 or target.result.cpu.arch == .x86_64 and musl) {
sdl_mod.addSystemIncludePath(deps_values.dependency.path("include/aarch64-linux-gnu"));
}
}
if (macos_system_include_path) |path| {
if (system_include_path) |path| {
sdl_mod.addSystemIncludePath(path);
}
if (macos_system_framework_path) |path| {
if (system_framework_path) |path| {
sdl_mod.addSystemFrameworkPath(path);
}
if (macos_library_path) |path| {
if (library_path) |path| {
sdl_mod.addLibraryPath(path);
}
if (emscripten_system_include_path) |path| {
sdl_mod.addSystemIncludePath(path);
}
var sdl_c_flags: std.BoundedArray([]const u8, common_c_flags.len + 3) = .{};
sdl_c_flags.appendSliceAssumeCapacity(&common_c_flags);
@ -804,6 +840,7 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.link_libc = true,
.strip = strip,
.sanitize_c = resolved_sanitize_c,
.pic = pic,
});
const sdl_uclibc_lib = b.addLibrary(.{
@ -811,7 +848,11 @@ pub fn build(b: *std.Build) void {
.name = "SDL_uclib",
.root_module = sdl_uclibc_mod,
});
sdl_uclibc_lib.want_lto = lto;
if (legacy_lto_field) {
sdl_uclibc_lib.want_lto = resolved_lto;
} else {
sdl_uclibc_lib.lto = resolved_lto;
}
sdl_uclibc_mod.addCMacro("USING_GENERATED_CONFIG_H", "1");
@ -1290,6 +1331,7 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.link_libc = true,
.strip = strip,
.sanitize_c = resolved_sanitize_c,
.pic = pic,
});
const sdl_test_lib = b.addLibrary(.{
@ -1298,12 +1340,16 @@ pub fn build(b: *std.Build) void {
.root_module = sdl_test_mod,
.use_llvm = if (emscripten) true else null,
});
sdl_test_lib.want_lto = lto;
if (legacy_lto_field) {
sdl_test_lib.want_lto = resolved_lto;
} else {
sdl_test_lib.lto = resolved_lto;
}
sdl_test_mod.addConfigHeader(build_config_h);
sdl_test_mod.addConfigHeader(revision_h);
sdl_test_mod.addIncludePath(b.path("include"));
if (emscripten_system_include_path) |path| {
if (system_include_path) |path| {
sdl_test_mod.addSystemIncludePath(path);
}

View File

@ -3,9 +3,9 @@
.{
.name = .sdl,
.version = "0.2.1+3.2.10",
.version = "0.2.4+3.2.16",
.fingerprint = 0xec638ccbf427e2ee,
.minimum_zig_version = "0.14.0",
.minimum_zig_version = "0.14.1",
.dependencies = .{
.sdl_linux_deps = .{
.url = "git+https://github.com/castholm/SDL_linux_deps.git#085212f286621835f2638cb0cfff078fe515341a",

View File

@ -20,7 +20,7 @@
*/
/**
* Main include header for the SDL library, version 3.2.10
* Main include header for the SDL library, version 3.2.16
*
* It is almost always best to include just this one header instead of
* picking out individual headers included here. There are exceptions to

View File

@ -492,6 +492,8 @@ typedef struct SDL_MouseWheelEvent
SDL_MouseWheelDirection direction; /**< Set to one of the SDL_MOUSEWHEEL_* defines. When FLIPPED the values in X and Y will be opposite. Multiply by -1 to change them back */
float mouse_x; /**< X coordinate, relative to window */
float mouse_y; /**< Y coordinate, relative to window */
Sint32 integer_x; /**< The amount scrolled horizontally, accumulated to whole scroll "ticks" (added in 3.2.12) */
Sint32 integer_y; /**< The amount scrolled vertically, accumulated to whole scroll "ticks" (added in 3.2.12) */
} SDL_MouseWheelEvent;
/**

View File

@ -2231,7 +2231,7 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetGPUDriver(int index);
/**
* Returns the name of the backend used to create this GPU context.
*
* \param device a GPU context to query.
* \returns the name of the device's driver, or NULL on error.
*
@ -2467,9 +2467,9 @@ extern SDL_DECLSPEC SDL_GPUShader * SDLCALL SDL_CreateGPUShader(
* - `SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_DEPTH_FLOAT`: (Direct3D 12 only)
* if the texture usage is SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET, clear
* the texture to a depth of this value. Defaults to zero.
* - `SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_STENCIL_UINT8`: (Direct3D 12
* - `SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_STENCIL_NUMBER`: (Direct3D 12
* only) if the texture usage is SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
* clear the texture to a stencil of this value. Defaults to zero.
* clear the texture to a stencil of this Uint8 value. Defaults to zero.
* - `SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING`: a name that can be displayed
* in debugging tools.
*
@ -2495,13 +2495,13 @@ extern SDL_DECLSPEC SDL_GPUTexture * SDLCALL SDL_CreateGPUTexture(
SDL_GPUDevice *device,
const SDL_GPUTextureCreateInfo *createinfo);
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_R_FLOAT "SDL.gpu.texture.create.d3d12.clear.r"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_G_FLOAT "SDL.gpu.texture.create.d3d12.clear.g"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_B_FLOAT "SDL.gpu.texture.create.d3d12.clear.b"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_A_FLOAT "SDL.gpu.texture.create.d3d12.clear.a"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_DEPTH_FLOAT "SDL.gpu.texture.create.d3d12.clear.depth"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_STENCIL_UINT8 "SDL.gpu.texture.create.d3d12.clear.stencil"
#define SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING "SDL.gpu.texture.create.name"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_R_FLOAT "SDL.gpu.texture.create.d3d12.clear.r"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_G_FLOAT "SDL.gpu.texture.create.d3d12.clear.g"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_B_FLOAT "SDL.gpu.texture.create.d3d12.clear.b"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_A_FLOAT "SDL.gpu.texture.create.d3d12.clear.a"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_DEPTH_FLOAT "SDL.gpu.texture.create.d3d12.clear.depth"
#define SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_STENCIL_NUMBER "SDL.gpu.texture.create.d3d12.clear.stencil"
#define SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING "SDL.gpu.texture.create.name"
/**
* Creates a buffer object to be used in graphics or compute workflows.
@ -3775,7 +3775,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseWindowFromGPUDevice(
* supported via SDL_WindowSupportsGPUPresentMode /
* SDL_WindowSupportsGPUSwapchainComposition prior to calling this function.
*
* SDL_GPU_PRESENTMODE_VSYNC and SDL_GPU_SWAPCHAINCOMPOSITION_SDR are always
* SDL_GPU_PRESENTMODE_VSYNC with SDL_GPU_SWAPCHAINCOMPOSITION_SDR are always
* supported.
*
* \param device a GPU context.

View File

@ -1074,8 +1074,8 @@ extern "C" {
*
* By default, SDL will try all available GPU backends in a reasonable order
* until it finds one that can work, but this hint allows the app or user to
* force a specific target, such as "direct3d11" if, say, your hardware
* supports D3D12 but want to try using D3D11 instead.
* force a specific target, such as "direct3d12" if, say, your hardware
* supports Vulkan but you want to try using D3D12 instead.
*
* This hint should be set before any GPU functions are called.
*
@ -2026,8 +2026,8 @@ extern "C" {
*
* The variable can be set to the following values:
*
* - "0": RAWINPUT drivers are not used.
* - "1": RAWINPUT drivers are used. (default)
* - "0": RAWINPUT drivers are not used. (default)
* - "1": RAWINPUT drivers are used.
*
* This hint should be set before SDL is initialized.
*

View File

@ -79,7 +79,7 @@ typedef Uint32 SDL_InitFlags;
#define SDL_INIT_AUDIO 0x00000010u /**< `SDL_INIT_AUDIO` implies `SDL_INIT_EVENTS` */
#define SDL_INIT_VIDEO 0x00000020u /**< `SDL_INIT_VIDEO` implies `SDL_INIT_EVENTS`, should be initialized on the main thread */
#define SDL_INIT_JOYSTICK 0x00000200u /**< `SDL_INIT_JOYSTICK` implies `SDL_INIT_EVENTS`, should be initialized on the same thread as SDL_INIT_VIDEO on Windows if you don't set SDL_HINT_JOYSTICK_THREAD */
#define SDL_INIT_JOYSTICK 0x00000200u /**< `SDL_INIT_JOYSTICK` implies `SDL_INIT_EVENTS` */
#define SDL_INIT_HAPTIC 0x00001000u
#define SDL_INIT_GAMEPAD 0x00002000u /**< `SDL_INIT_GAMEPAD` implies `SDL_INIT_JOYSTICK` */
#define SDL_INIT_EVENTS 0x00004000u

View File

@ -517,7 +517,7 @@ typedef enum SDL_PackedLayout
* ABGR32, define a platform-independent encoding into bytes in the order
* specified. For example, in RGB24 data, each pixel is encoded in 3 bytes
* (red, green, blue) in that order, and in ABGR32 data, each pixel is
* encoded in 4 bytes alpha, blue, green, red) in that order. Use these
* encoded in 4 bytes (alpha, blue, green, red) in that order. Use these
* names if the property of a format that is important to you is the order
* of the bytes in memory or on disk.
* - Names with a bit count per component, such as ARGB8888 and XRGB1555, are

View File

@ -1607,8 +1607,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderViewport(SDL_Renderer *renderer, S
* Return whether an explicit rectangle was set as the viewport.
*
* This is useful if you're saving and restoring the viewport and want to know
* whether you should restore a specific rectangle or NULL. Note that the
* viewport is always reset when changing rendering targets.
* whether you should restore a specific rectangle or NULL.
*
* Each render target has its own viewport. This function checks the viewport
* for the current render target.

View File

@ -4656,7 +4656,7 @@ extern SDL_DECLSPEC float SDLCALL SDL_atanf(float x);
*
* Domain: `-INF <= x <= INF`, `-INF <= y <= INF`
*
* Range: `-Pi/2 <= y <= Pi/2`
* Range: `-Pi <= y <= Pi`
*
* This function operates on double-precision floating point values, use
* SDL_atan2f for single-precision floats.
@ -4692,7 +4692,7 @@ extern SDL_DECLSPEC double SDLCALL SDL_atan2(double y, double x);
*
* Domain: `-INF <= x <= INF`, `-INF <= y <= INF`
*
* Range: `-Pi/2 <= y <= Pi/2`
* Range: `-Pi <= y <= Pi`
*
* This function operates on single-precision floating point values, use
* SDL_atan2 for double-precision floats.
@ -5974,8 +5974,12 @@ size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t size);
size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t size);
#endif
#ifndef _WIN32
/* strdup is not ANSI but POSIX, and its prototype might be hidden... */
/* not for windows: might conflict with string.h where strdup may have
* dllimport attribute: https://github.com/libsdl-org/SDL/issues/12948 */
char *strdup(const char *str);
#endif
/* Starting LLVM 16, the analyser errors out if these functions do not have
their prototype defined (clang-diagnostic-implicit-function-declaration) */

View File

@ -1135,9 +1135,6 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FillSurfaceRects(SDL_Surface *dst, const SD
* If either `srcrect` or `dstrect` are NULL, the entire surface (`src` or
* `dst`) is copied while ensuring clipping to `dst->clip_rect`.
*
* The final blit rectangles are saved in `srcrect` and `dstrect` after all
* clipping is performed.
*
* The blit function should not be called on a locked surface.
*
* The blit semantics for surfaces with and without blending and colorkey are
@ -1282,10 +1279,11 @@ extern SDL_DECLSPEC bool SDLCALL SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src
*
* \param src the SDL_Surface structure to be copied from.
* \param srcrect the SDL_Rect structure representing the rectangle to be
* copied, may not be NULL.
* copied, or NULL to copy the entire surface.
* \param dst the SDL_Surface structure that is the blit target.
* \param dstrect the SDL_Rect structure representing the target rectangle in
* the destination surface, may not be NULL.
* the destination surface, or NULL to fill the entire
* destination surface.
* \param scaleMode the SDL_ScaleMode to be used.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.

View File

@ -62,7 +62,7 @@ extern "C" {
*
* \since This macro is available since SDL 3.2.0.
*/
#define SDL_MICRO_VERSION 10
#define SDL_MICRO_VERSION 16
/**
* This macro turns the version numbers into a numeric value.

View File

@ -426,10 +426,10 @@ typedef SDL_EGLint *(SDLCALL *SDL_EGLIntArrayCallback)(void *userdata, SDL_EGLDi
*/
typedef enum SDL_GLAttr
{
SDL_GL_RED_SIZE, /**< the minimum number of bits for the red channel of the color buffer; defaults to 3. */
SDL_GL_GREEN_SIZE, /**< the minimum number of bits for the green channel of the color buffer; defaults to 3. */
SDL_GL_BLUE_SIZE, /**< the minimum number of bits for the blue channel of the color buffer; defaults to 2. */
SDL_GL_ALPHA_SIZE, /**< the minimum number of bits for the alpha channel of the color buffer; defaults to 0. */
SDL_GL_RED_SIZE, /**< the minimum number of bits for the red channel of the color buffer; defaults to 8. */
SDL_GL_GREEN_SIZE, /**< the minimum number of bits for the green channel of the color buffer; defaults to 8. */
SDL_GL_BLUE_SIZE, /**< the minimum number of bits for the blue channel of the color buffer; defaults to 8. */
SDL_GL_ALPHA_SIZE, /**< the minimum number of bits for the alpha channel of the color buffer; defaults to 8. */
SDL_GL_BUFFER_SIZE, /**< the minimum number of bits for frame buffer size; defaults to 0. */
SDL_GL_DOUBLEBUFFER, /**< whether the output is single or double buffered; defaults to double buffering on. */
SDL_GL_DEPTH_SIZE, /**< the minimum number of bits in the depth buffer; defaults to 16. */
@ -1041,6 +1041,10 @@ extern SDL_DECLSPEC SDL_Window ** SDLCALL SDL_GetWindows(int *count);
/**
* Create a window with the specified dimensions and flags.
*
* The window size is a request and may be different than expected based on
* the desktop layout and window manager policies. Your application should be
* prepared to handle a window of any size.
*
* `flags` may be any of the following OR'd together:
*
* - `SDL_WINDOW_FULLSCREEN`: fullscreen window at desktop resolution
@ -1127,6 +1131,10 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int
/**
* Create a child popup window of the specified parent window.
*
* The window size is a request and may be different than expected based on
* the desktop layout and window manager policies. Your application should be
* prepared to handle a window of any size.
*
* The flags parameter **must** contain at least one of the following:
*
* - `SDL_WINDOW_TOOLTIP`: The popup window is a tooltip and will not pass any
@ -1189,6 +1197,10 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren
/**
* Create a window with the specified properties.
*
* The window size is a request and may be different than expected based on
* the desktop layout and window manager policies. Your application should be
* prepared to handle a window of any size.
*
* These are the supported properties:
*
* - `SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN`: true if the window should

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,10 @@
#include "SDL_hints_c.h"
#ifdef SDL_PLATFORM_ANDROID
#include "core/android/SDL_android.h"
#endif
typedef struct SDL_HintWatch
{
SDL_HintCallback callback;
@ -147,6 +151,13 @@ bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriori
}
}
#ifdef SDL_PLATFORM_ANDROID
if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
// Special handling for this hint, which needs to persist outside the normal application flow
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(value, false));
}
#endif // SDL_PLATFORM_ANDROID
SDL_UnlockProperties(hints);
return result;
@ -185,6 +196,17 @@ bool SDL_ResetHint(const char *name)
result = true;
}
#ifdef SDL_PLATFORM_ANDROID
if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
// Special handling for this hint, which needs to persist outside the normal application flow
if (env) {
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(env, false));
} else {
Android_SetAllowRecreateActivity(false);
}
}
#endif // SDL_PLATFORM_ANDROID
SDL_UnlockProperties(hints);
return result;
@ -210,6 +232,17 @@ static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, c
SDL_free(hint->value);
hint->value = NULL;
hint->priority = SDL_HINT_DEFAULT;
#ifdef SDL_PLATFORM_ANDROID
if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
// Special handling for this hint, which needs to persist outside the normal application flow
if (env) {
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(env, false));
} else {
Android_SetAllowRecreateActivity(false);
}
}
#endif // SDL_PLATFORM_ANDROID
}
void SDL_ResetHints(void)

View File

@ -265,6 +265,12 @@ extern "C" {
#include "SDL_utils_c.h"
#include "SDL_hashtable.h"
#define PUSH_SDL_ERROR() \
{ char *_error = SDL_strdup(SDL_GetError());
#define POP_SDL_ERROR() \
SDL_SetError("%s", _error); SDL_free(_error); }
// Do any initialization that needs to happen before threads are started
extern void SDL_InitMainThread(void);

View File

@ -403,6 +403,10 @@ const char *SDL_GetPersistentString(const char *string)
static int PrefixMatch(const char *a, const char *b)
{
int matchlen = 0;
// Fixes the "HORI HORl Taiko No Tatsujin Drum Controller"
if (SDL_strncmp(a, "HORI ", 5) == 0 && SDL_strncmp(b, "HORl ", 5) == 0) {
return 5;
}
while (*a && *b) {
if (SDL_tolower((unsigned char)*a++) == SDL_tolower((unsigned char)*b++)) {
++matchlen;
@ -424,8 +428,8 @@ char *SDL_CreateDeviceName(Uint16 vendor, Uint16 product, const char *vendor_nam
{ "ASTRO Gaming", "ASTRO" },
{ "Bensussen Deutsch & Associates,Inc.(BDA)", "BDA" },
{ "Guangzhou Chicken Run Network Technology Co., Ltd.", "GameSir" },
{ "HORI CO.,LTD", "HORI" },
{ "HORI CO.,LTD.", "HORI" },
{ "HORI CO.,LTD", "HORI" },
{ "Mad Catz Inc.", "Mad Catz" },
{ "Nintendo Co., Ltd.", "Nintendo" },
{ "NVIDIA Corporation ", "" },

View File

@ -410,6 +410,7 @@ static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid,
SDL_LockRWLockForReading(current_audio.device_hash_lock);
SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &logdev);
if (logdev) {
SDL_assert(logdev->instance_id == devid);
device = logdev->physical_device;
SDL_assert(device != NULL);
RefPhysicalAudioDevice(device); // reference it, in case the logical device migrates to a new default.
@ -459,6 +460,7 @@ static SDL_AudioDevice *ObtainPhysicalAudioDevice(SDL_AudioDeviceID devid) // !
} else {
SDL_LockRWLockForReading(current_audio.device_hash_lock);
SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &device);
SDL_assert(device->instance_id == devid);
SDL_UnlockRWLock(current_audio.device_hash_lock);
if (!device) {
@ -883,6 +885,7 @@ static bool SDLCALL FindLowestDeviceID(void *userdata, const SDL_HashTable *tabl
if (isphysical && (devid_recording == data->recording) && (devid < data->highest)) {
data->highest = devid;
data->result = (SDL_AudioDevice *) value;
SDL_assert(data->result->instance_id == devid);
}
return true; // keep iterating.
}
@ -1051,7 +1054,10 @@ static bool SDLCALL DestroyOnePhysicalAudioDevice(void *userdata, const SDL_Hash
const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key;
const bool isphysical = !!(devid & (1<<1));
if (isphysical) {
DestroyPhysicalAudioDevice((SDL_AudioDevice *) value);
SDL_AudioDevice *dev = (SDL_AudioDevice *) value;
SDL_assert(dev->instance_id == devid);
DestroyPhysicalAudioDevice(dev);
}
return true; // keep iterating.
}
@ -1464,6 +1470,7 @@ static bool SDLCALL FindAudioDeviceByCallback(void *userdata, const SDL_HashTabl
SDL_AudioDevice *device = (SDL_AudioDevice *) value;
if (data->callback(device, data->userdata)) { // found it?
data->retval = device;
SDL_assert(data->retval->instance_id == devid);
return false; // stop iterating, we found it.
}
}
@ -1502,12 +1509,33 @@ SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle)
const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
{
// bit #1 of devid is set for physical devices and unset for logical.
const bool islogical = !(devid & (1<<1));
const char *result = NULL;
SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid);
if (device) {
result = SDL_GetPersistentString(device->name);
const void *vdev = NULL;
if (!SDL_GetCurrentAudioDriver()) {
SDL_SetError("Audio subsystem is not initialized");
} else {
// This does not call ObtainPhysicalAudioDevice() because the device's name never changes, so
// it doesn't have to lock the whole device. However, just to make sure the device pointer itself
// remains valid (in case the device is unplugged at the wrong moment), we hold the
// device_hash_lock while we copy the string.
SDL_LockRWLockForReading(current_audio.device_hash_lock);
SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, &vdev);
if (!vdev) {
SDL_SetError("Invalid audio device instance ID");
} else if (islogical) {
const SDL_LogicalAudioDevice *logdev = (const SDL_LogicalAudioDevice *) vdev;
SDL_assert(logdev->instance_id == devid);
result = SDL_GetPersistentString(logdev->physical_device->name);
} else {
const SDL_AudioDevice *device = (const SDL_AudioDevice *) vdev;
SDL_assert(device->instance_id == devid);
result = SDL_GetPersistentString(device->name);
}
SDL_UnlockRWLock(current_audio.device_hash_lock);
}
ReleaseAudioDevice(device);
return result;
}
@ -1715,13 +1743,18 @@ static bool OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
SDL_copyp(&spec, inspec ? inspec : &device->default_spec);
PrepareAudioFormat(device->recording, &spec);
/* We allow the device format to change if it's better than the current settings (by various definitions of "better"). This prevents
something low quality, like an old game using S8/8000Hz audio, from ruining a music thing playing at CD quality that tries to open later.
(or some VoIP library that opens for mono output ruining your surround-sound game because it got there first).
/* We impose a simple minimum on device formats. This prevents something low quality, like an old game using S8/8000Hz audio,
from ruining a music thing playing at CD quality that tries to open later, or some VoIP library that opens for mono output
ruining your surround-sound game because it got there first.
These are just requests! The backend may change any of these values during OpenDevice method! */
device->spec.format = (SDL_AUDIO_BITSIZE(device->default_spec.format) >= SDL_AUDIO_BITSIZE(spec.format)) ? device->default_spec.format : spec.format;
device->spec.freq = SDL_max(device->default_spec.freq, spec.freq);
device->spec.channels = SDL_max(device->default_spec.channels, spec.channels);
const SDL_AudioFormat minimum_format = device->recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT;
const int minimum_channels = device->recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS;
const int minimum_freq = device->recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY;
device->spec.format = (SDL_AUDIO_BITSIZE(minimum_format) >= SDL_AUDIO_BITSIZE(spec.format)) ? minimum_format : spec.format;
device->spec.channels = SDL_max(minimum_channels, spec.channels);
device->spec.freq = SDL_max(minimum_freq, spec.freq);
device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq);
SDL_UpdatedAudioDeviceFormat(device); // start this off sane.

View File

@ -308,6 +308,12 @@ static bool BuildAAudioStream(SDL_AudioDevice *device)
ctx.AAudioStreamBuilder_setFormat(builder, format);
ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
// If no specific buffer size has been requested, the device will pick the optimal
if(SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES)) {
ctx.AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2 * device->sample_frames); // AAudio requires that the buffer capacity is at least
ctx.AAudioStreamBuilder_setFramesPerDataCallback(builder, device->sample_frames); // twice the size of the data callback buffer size
}
const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
ctx.AAudioStreamBuilder_setDirection(builder, direction);
@ -366,7 +372,7 @@ static bool BuildAAudioStream(SDL_AudioDevice *device)
hidden->processed_bytes = 0;
hidden->callback_bytes = 0;
hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers);
hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers - 1);
if (!hidden->semaphore) {
LOGI("SDL Failed SDL_CreateSemaphore %s recording:%d", SDL_GetError(), recording);
return false;

View File

@ -31,7 +31,7 @@ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSamplesPerFrame, (AAudioStreamBuild
SDL_PROC(void, AAudioStreamBuilder_setFormat, (AAudioStreamBuilder * builder, aaudio_format_t format))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSharingMode, (AAudioStreamBuilder * builder, aaudio_sharing_mode_t sharingMode))
SDL_PROC(void, AAudioStreamBuilder_setDirection, (AAudioStreamBuilder * builder, aaudio_direction_t direction))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames))
SDL_PROC(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames))
SDL_PROC(void, AAudioStreamBuilder_setPerformanceMode, (AAudioStreamBuilder * builder, aaudio_performance_mode_t mode))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setUsage, (AAudioStreamBuilder * builder, aaudio_usage_t usage)) // API 28
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setContentType, (AAudioStreamBuilder * builder, aaudio_content_type_t contentType)) // API 28

View File

@ -189,7 +189,7 @@ static bool EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
}
// limit to native freq
device->spec.freq = EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; });
device->spec.freq = MAIN_THREAD_EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; });
device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq) * 2; // double the buffer size, some browsers need more, and we'll just have to live with the latency.
SDL_UpdatedAudioDeviceFormat(device);

View File

@ -1118,7 +1118,13 @@ static bool PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
stream_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME);
if (!stream_name || *stream_name == '\0') {
stream_name = "Audio Stream";
if (app_name) {
stream_name = app_name;
} else if (app_id) {
stream_name = app_id;
} else {
stream_name = "SDL Audio Stream";
}
}
/*

View File

@ -672,7 +672,8 @@ static bool PULSEAUDIO_OpenDevice(SDL_AudioDevice *device)
paspec.rate = device->spec.freq;
// Reduced prebuffering compared to the defaults.
paattr.fragsize = device->buffer_size; // despite the name, this is only used for recording devices, according to PulseAudio docs!
paattr.fragsize = device->buffer_size * 2; // despite the name, this is only used for recording devices, according to PulseAudio docs! (times 2 because we want _more_ than our buffer size sent from the server at a time, which helps some drivers).
paattr.tlength = device->buffer_size;
paattr.prebuf = -1;
paattr.maxlength = -1;

View File

@ -130,7 +130,8 @@ static bool VITAAUD_OpenDevice(SDL_AudioDevice *device)
static bool VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
return (sceAudioOutOutput(device->hidden->port, buffer) == 0);
// sceAudioOutOutput returns amount of samples queued or < 0 on error
return (sceAudioOutOutput(device->hidden->port, buffer) >= 0);
}
// This function waits until it is possible to write a full sound buffer

View File

@ -61,7 +61,7 @@ static SDL_CameraFrameResult EMSCRIPTENCAMERA_AcquireFrame(SDL_Camera *device, S
SDL3.camera.ctx2d.drawImage(SDL3.camera.video, 0, 0, w, h);
const imgrgba = SDL3.camera.ctx2d.getImageData(0, 0, w, h).data;
Module.HEAPU8.set(imgrgba, rgba);
HEAPU8.set(imgrgba, rgba);
return 1;
}, device->actual_spec.width, device->actual_spec.height, rgba);

View File

@ -371,6 +371,7 @@ static jmethodID midShowTextInput;
static jmethodID midSupportsRelativeMouse;
static jmethodID midOpenFileDescriptor;
static jmethodID midShowFileDialog;
static jmethodID midGetPreferredLocales;
// audio manager
static jclass mAudioManagerClass;
@ -660,6 +661,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midOpenFileDescriptor = (*env)->GetStaticMethodID(env, mActivityClass, "openFileDescriptor", "(Ljava/lang/String;Ljava/lang/String;)I");
midShowFileDialog = (*env)->GetStaticMethodID(env, mActivityClass, "showFileDialog", "([Ljava/lang/String;ZZI)Z");
midGetPreferredLocales = (*env)->GetStaticMethodID(env, mActivityClass, "getPreferredLocales", "()Ljava/lang/String;");
if (!midClipboardGetText ||
!midClipboardHasText ||
@ -691,7 +693,8 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
!midShowTextInput ||
!midSupportsRelativeMouse ||
!midOpenFileDescriptor ||
!midShowFileDialog) {
!midShowFileDialog ||
!midGetPreferredLocales) {
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
}
@ -751,6 +754,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env
typedef int (*SDL_main_func)(int argc, char *argv[]);
static int run_count = 0;
static bool allow_recreate_activity;
static bool allow_recreate_activity_set;
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)(
JNIEnv *env, jclass jcls)
@ -760,10 +765,16 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)(
return tmp;
}
void Android_SetAllowRecreateActivity(bool enabled)
{
allow_recreate_activity = enabled;
allow_recreate_activity_set = true;
}
JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeAllowRecreateActivity)(
JNIEnv *env, jclass jcls)
{
return SDL_GetHintBoolean(SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY, false);
return allow_recreate_activity;
}
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeInitMainThread)(
@ -1526,6 +1537,14 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
// Note that we call setenv() directly to avoid affecting SDL environments
setenv(utfname, utfvalue, 1); // This should NOT be SDL_setenv()
if (SDL_strcmp(utfname, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
// Special handling for this hint, which needs to persist outside the normal application flow
// Only set this the first time we run, in case it's been set by the application via SDL_SetHint()
if (!allow_recreate_activity_set) {
Android_SetAllowRecreateActivity(SDL_GetStringBoolean(utfvalue, false));
}
}
(*env)->ReleaseStringUTFChars(env, name, utfname);
(*env)->ReleaseStringUTFChars(env, value, utfvalue);
}
@ -2569,65 +2588,22 @@ bool Android_JNI_ShowToast(const char *message, int duration, int gravity, int x
bool Android_JNI_GetLocale(char *buf, size_t buflen)
{
AConfiguration *cfg;
SDL_assert(buflen > 6);
// Need to re-create the asset manager if locale has changed (SDL_EVENT_LOCALE_CHANGED)
Internal_Android_Destroy_AssetManager();
if (!asset_manager) {
Internal_Android_Create_AssetManager();
}
if (!asset_manager) {
return false;
}
cfg = AConfiguration_new();
if (!cfg) {
return false;
}
{
char language[2] = {};
char country[2] = {};
size_t id = 0;
AConfiguration_fromAssetManager(cfg, asset_manager);
AConfiguration_getLanguage(cfg, language);
AConfiguration_getCountry(cfg, country);
// Indonesian is "id" according to ISO 639.2, but on Android is "in" because of Java backwards compatibility
if (language[0] == 'i' && language[1] == 'n') {
language[1] = 'd';
}
// copy language (not null terminated)
if (language[0]) {
buf[id++] = language[0];
if (language[1]) {
buf[id++] = language[1];
bool result = false;
if (buf && buflen > 0) {
*buf = '\0';
JNIEnv *env = Android_JNI_GetEnv();
jstring string = (jstring)(*env)->CallStaticObjectMethod(env, mActivityClass, midGetPreferredLocales);
if (string) {
const char *utf8string = (*env)->GetStringUTFChars(env, string, NULL);
if (utf8string) {
result = true;
SDL_strlcpy(buf, utf8string, buflen);
(*env)->ReleaseStringUTFChars(env, string, utf8string);
}
(*env)->DeleteLocalRef(env, string);
}
buf[id++] = '_';
// copy country (not null terminated)
if (country[0]) {
buf[id++] = country[0];
if (country[1]) {
buf[id++] = country[1];
}
}
buf[id++] = '\0';
SDL_assert(id <= buflen);
}
AConfiguration_delete(cfg);
return true;
return result;
}
bool Android_JNI_OpenURL(const char *url)

View File

@ -55,6 +55,8 @@ bool Android_WaitLifecycleEvent(SDL_AndroidLifecycleEvent *event, Sint64 timeout
void Android_LockActivityMutex(void);
void Android_UnlockActivityMutex(void);
void Android_SetAllowRecreateActivity(bool enabled);
// Interface from the SDL library into the Android Java activity
extern void Android_JNI_SetActivityTitle(const char *title);
extern void Android_JNI_SetWindowStyle(bool fullscreen);

View File

@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,2,10,0
PRODUCTVERSION 3,2,10,0
FILEVERSION 3,2,16,0
PRODUCTVERSION 3,2,16,0
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L
FILEOS 0x40004L
@ -23,12 +23,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "SDL\0"
VALUE "FileVersion", "3, 2, 10, 0\0"
VALUE "FileVersion", "3, 2, 16, 0\0"
VALUE "InternalName", "SDL\0"
VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0"
VALUE "OriginalFilename", "SDL3.dll\0"
VALUE "ProductName", "Simple DirectMedia Layer\0"
VALUE "ProductVersion", "3, 2, 10, 0\0"
VALUE "ProductVersion", "3, 2, 16, 0\0"
END
END
BLOCK "VarFileInfo"

View File

@ -115,7 +115,11 @@
#define CPU_CFG2_LSX (1 << 6)
#define CPU_CFG2_LASX (1 << 7)
#if defined(SDL_ALTIVEC_BLITTERS) && defined(HAVE_SETJMP) && !defined(SDL_PLATFORM_MACOS) && !defined(SDL_PLATFORM_OPENBSD) && !defined(SDL_PLATFORM_FREEBSD)
#if !defined(SDL_CPUINFO_DISABLED) && \
!((defined(SDL_PLATFORM_MACOS) && (defined(__ppc__) || defined(__ppc64__))) || (defined(SDL_PLATFORM_OPENBSD) && defined(__powerpc__))) && \
!(defined(SDL_PLATFORM_FREEBSD) && defined(__powerpc__)) && \
!(defined(SDL_PLATFORM_LINUX) && defined(__powerpc__) && defined(HAVE_GETAUXVAL)) && \
defined(SDL_ALTIVEC_BLITTERS) && defined(HAVE_SETJMP)
/* This is the brute force way of detecting instruction sets...
the idea is borrowed from the libmpeg2 library - thanks!
*/
@ -344,6 +348,8 @@ static int CPU_haveAltiVec(void)
elf_aux_info(AT_HWCAP, &cpufeatures, sizeof(cpufeatures));
altivec = cpufeatures & PPC_FEATURE_HAS_ALTIVEC;
return altivec;
#elif defined(SDL_PLATFORM_LINUX) && defined(__powerpc__) && defined(HAVE_GETAUXVAL)
altivec = getauxval(AT_HWCAP) & PPC_FEATURE_HAS_ALTIVEC;
#elif defined(SDL_ALTIVEC_BLITTERS) && defined(HAVE_SETJMP)
void (*handler)(int sig);
handler = signal(SIGILL, illegal_instruction);

View File

@ -27,6 +27,35 @@
#import <Cocoa/Cocoa.h>
#import <UniformTypeIdentifiers/UTType.h>
static void AddFileExtensionType(NSMutableArray *types, const char *pattern_ptr)
{
if (!*pattern_ptr) {
return; // in case the string had an extra ';' at the end.
}
// -[UTType typeWithFilenameExtension] will return nil if there's a period in the string. It's better to
// allow too many files than not allow the one the user actually needs, so just take the part after the '.'
const char *dot = SDL_strrchr(pattern_ptr, '.');
NSString *extstr = [NSString stringWithFormat: @"%s", dot ? (dot + 1) : pattern_ptr];
if (@available(macOS 11.0, *)) {
UTType *uttype = [UTType typeWithFilenameExtension:extstr];
if (uttype) { // still failed? Don't add the pattern. This is what the pre-macOS11 path does internally anyhow.
[types addObject:uttype];
}
} else {
[types addObject:extstr];
}
}
static void ReactivateAfterDialog(void)
{
for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
[i activateWithOptions:0];
break;
}
[NSApp activateIgnoringOtherApps:YES];
}
void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props)
{
SDL_Window* window = SDL_GetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, NULL);
@ -87,7 +116,7 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil
if (filters) {
// On macOS 11.0 and up, this is an array of UTType. Prior to that, it's an array of NSString
NSMutableArray *types = [[NSMutableArray alloc] initWithCapacity:nfilters ];
NSMutableArray *types = [[NSMutableArray alloc] initWithCapacity:nfilters];
int has_all_files = 0;
for (int i = 0; i < nfilters; i++) {
@ -102,21 +131,14 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil
for (char *c = pattern; *c; c++) {
if (*c == ';') {
*c = '\0';
if(@available(macOS 11.0, *)) {
[types addObject: [UTType typeWithFilenameExtension:[NSString stringWithFormat: @"%s", pattern_ptr]]];
} else {
[types addObject: [NSString stringWithFormat: @"%s", pattern_ptr]];
}
AddFileExtensionType(types, pattern_ptr);
pattern_ptr = c + 1;
} else if (*c == '*') {
has_all_files = 1;
}
}
if(@available(macOS 11.0, *)) {
[types addObject: [UTType typeWithFilenameExtension:[NSString stringWithFormat: @"%s", pattern_ptr]]];
} else {
[types addObject: [NSString stringWithFormat: @"%s", pattern_ptr]];
}
AddFileExtensionType(types, pattern_ptr); // get the last piece of the string.
SDL_free(pattern);
}
@ -163,6 +185,8 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil
const char *files[1] = { NULL };
callback(userdata, files, -1);
}
ReactivateAfterDialog();
}];
} else {
if ([dialog runModal] == NSModalResponseOK) {
@ -182,6 +206,7 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil
const char *files[1] = { NULL };
callback(userdata, files, -1);
}
ReactivateAfterDialog();
}
}

View File

@ -261,7 +261,7 @@ void windows_ShowFileDialog(void *ptr)
chosen_files_list[nfiles] = NULL;
if (WideCharToMultiByte(CP_UTF8, 0, file_ptr, -1, chosen_folder, MAX_PATH, NULL, NULL) >= MAX_PATH) {
if (WideCharToMultiByte(CP_UTF8, 0, file_ptr, -1, chosen_folder, MAX_PATH, NULL, NULL) == 0) {
SDL_SetError("Path too long or invalid character in path");
SDL_free(chosen_files_list);
callback(userdata, NULL, -1);
@ -273,7 +273,7 @@ void windows_ShowFileDialog(void *ptr)
SDL_strlcpy(chosen_file, chosen_folder, MAX_PATH);
chosen_file[chosen_folder_size] = '\\';
file_ptr += SDL_strlen(chosen_folder) + 1;
file_ptr += SDL_wcslen(file_ptr) + 1;
while (*file_ptr) {
nfiles++;
@ -295,7 +295,7 @@ void windows_ShowFileDialog(void *ptr)
int diff = ((int) chosen_folder_size) + 1;
if (WideCharToMultiByte(CP_UTF8, 0, file_ptr, -1, chosen_file + diff, MAX_PATH - diff, NULL, NULL) >= MAX_PATH - diff) {
if (WideCharToMultiByte(CP_UTF8, 0, file_ptr, -1, chosen_file + diff, MAX_PATH - diff, NULL, NULL) == 0) {
SDL_SetError("Path too long or invalid character in path");
for (size_t i = 0; i < nfiles - 1; i++) {
@ -308,7 +308,7 @@ void windows_ShowFileDialog(void *ptr)
return;
}
file_ptr += SDL_strlen(chosen_file) + 1 - diff;
file_ptr += SDL_wcslen(file_ptr) + 1;
chosen_files_list[nfiles - 1] = SDL_strdup(chosen_file);
@ -516,6 +516,14 @@ static void ShowFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_
filters_str = win_get_filters(filters, nfilters);
DWORD flags = 0;
if (allow_many) {
flags |= OFN_ALLOWMULTISELECT;
}
if (is_save) {
flags |= OFN_OVERWRITEPROMPT;
}
if (!filters_str && filters) {
callback(userdata, NULL, -1);
SDL_free(args);
@ -526,7 +534,7 @@ static void ShowFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_
args->filters_str = filters_str;
args->default_file = default_location ? SDL_strdup(default_location) : NULL;
args->parent = window;
args->flags = allow_many ? OFN_ALLOWMULTISELECT : 0;
args->flags = flags;
args->callback = callback;
args->userdata = userdata;
args->title = title ? SDL_strdup(title) : NULL;

View File

@ -633,9 +633,10 @@ static void SDL_LogEvent(const SDL_Event *event)
#undef PRINT_MBUTTON_EVENT
SDL_EVENT_CASE(SDL_EVENT_MOUSE_WHEEL)
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u x=%g y=%g direction=%s)",
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u x=%g y=%g integer_x=%d integer_y=%d direction=%s)",
(uint)event->wheel.timestamp, (uint)event->wheel.windowID,
(uint)event->wheel.which, event->wheel.x, event->wheel.y,
(int)event->wheel.integer_x, (int)event->wheel.integer_y,
event->wheel.direction == SDL_MOUSEWHEEL_NORMAL ? "normal" : "flipped");
break;
@ -1076,16 +1077,11 @@ static void SDL_SendWakeupEvent(void)
return;
}
SDL_LockMutex(_this->wakeup_lock);
{
if (_this->wakeup_window) {
_this->SendWakeupEvent(_this, _this->wakeup_window);
// No more wakeup events needed until we enter a new wait
_this->wakeup_window = NULL;
}
// We only want to do this once while waiting for an event, so set it to NULL atomically here
SDL_Window *wakeup_window = (SDL_Window *)SDL_SetAtomicPointer(&_this->wakeup_window, NULL);
if (wakeup_window) {
_this->SendWakeupEvent(_this, wakeup_window);
}
SDL_UnlockMutex(_this->wakeup_lock);
#endif
}
@ -1379,9 +1375,7 @@ bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool w
return true;
}
// Maximum wait of 30 seconds to prevent deadlocking forever
const Sint32 MAX_CALLBACK_WAIT = 30 * 1000;
SDL_WaitSemaphoreTimeout(entry->semaphore, MAX_CALLBACK_WAIT);
SDL_WaitSemaphore(entry->semaphore);
switch (SDL_GetAtomicInt(&entry->state)) {
case SDL_MAIN_CALLBACK_COMPLETE:
@ -1525,18 +1519,7 @@ static int SDL_WaitEventTimeout_Device(SDL_VideoDevice *_this, SDL_Window *wakeu
*/
SDL_PumpEventsInternal(true);
SDL_LockMutex(_this->wakeup_lock);
{
status = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
// If status == 0 we are going to block so wakeup will be needed.
if (status == 0) {
_this->wakeup_window = wakeup_window;
} else {
_this->wakeup_window = NULL;
}
}
SDL_UnlockMutex(_this->wakeup_lock);
status = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
if (status < 0) {
// Got an error: return
break;
@ -1549,8 +1532,6 @@ static int SDL_WaitEventTimeout_Device(SDL_VideoDevice *_this, SDL_Window *wakeu
if (timeoutNS > 0) {
Sint64 elapsed = SDL_GetTicksNS() - start;
if (elapsed >= timeoutNS) {
// Set wakeup_window to NULL without holding the lock.
_this->wakeup_window = NULL;
return 0;
}
loop_timeoutNS = (timeoutNS - elapsed);
@ -1563,9 +1544,9 @@ static int SDL_WaitEventTimeout_Device(SDL_VideoDevice *_this, SDL_Window *wakeu
loop_timeoutNS = poll_intervalNS;
}
}
SDL_SetAtomicPointer(&_this->wakeup_window, wakeup_window);
status = _this->WaitEventTimeout(_this, loop_timeoutNS);
// Set wakeup_window to NULL without holding the lock.
_this->wakeup_window = NULL;
SDL_SetAtomicPointer(&_this->wakeup_window, NULL);
if (status == 0 && poll_intervalNS != SDL_MAX_SINT64 && loop_timeoutNS == poll_intervalNS) {
// We may have woken up to poll. Try again
continue;
@ -1823,7 +1804,7 @@ void SDL_SetEventEnabled(Uint32 type, bool enabled)
Uint8 lo = (type & 0xff);
if (SDL_disabled_events[hi] &&
(SDL_disabled_events[hi]->bits[lo / 32] & (1 << (lo & 31)))) {
(SDL_disabled_events[hi]->bits[lo / 32] & (1U << (lo & 31)))) {
current_state = false;
} else {
current_state = true;
@ -1832,7 +1813,7 @@ void SDL_SetEventEnabled(Uint32 type, bool enabled)
if ((enabled != false) != current_state) {
if (enabled) {
SDL_assert(SDL_disabled_events[hi] != NULL);
SDL_disabled_events[hi]->bits[lo / 32] &= ~(1 << (lo & 31));
SDL_disabled_events[hi]->bits[lo / 32] &= ~(1U << (lo & 31));
// Gamepad events depend on joystick events
switch (type) {
@ -1863,7 +1844,7 @@ void SDL_SetEventEnabled(Uint32 type, bool enabled)
}
// Out of memory, nothing we can do...
if (SDL_disabled_events[hi]) {
SDL_disabled_events[hi]->bits[lo / 32] |= (1 << (lo & 31));
SDL_disabled_events[hi]->bits[lo / 32] |= (1U << (lo & 31));
SDL_FlushEvent(type);
}
}
@ -1882,7 +1863,7 @@ bool SDL_EventEnabled(Uint32 type)
Uint8 lo = (type & 0xff);
if (SDL_disabled_events[hi] &&
(SDL_disabled_events[hi]->bits[lo / 32] & (1 << (lo & 31)))) {
(SDL_disabled_events[hi]->bits[lo / 32] & (1U << (lo & 31)))) {
return false;
} else {
return true;

View File

@ -138,21 +138,25 @@ static void SDLCALL SDL_TouchMouseEventsChanged(void *userdata, const char *name
#ifdef SDL_PLATFORM_VITA
static void SDLCALL SDL_VitaTouchMouseDeviceChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
Uint8 vita_touch_mouse_device = 1;
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
if (hint) {
switch (*hint) {
default:
case '0':
mouse->vita_touch_mouse_device = 1;
vita_touch_mouse_device = 1;
break;
case '1':
mouse->vita_touch_mouse_device = 2;
vita_touch_mouse_device = 2;
break;
case '2':
mouse->vita_touch_mouse_device = 3;
vita_touch_mouse_device = 3;
break;
default:
break;
}
}
mouse->vita_touch_mouse_device = vita_touch_mouse_device;
}
#endif
@ -1022,18 +1026,14 @@ void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseI
SDL_SetMouseFocus(window);
}
// Accumulate fractional wheel motion if integer mode is enabled
if (mouse->integer_mode_flags & 2) {
mouse->integer_mode_residual_scroll_x = SDL_modff(mouse->integer_mode_residual_scroll_x + x, &x);
mouse->integer_mode_residual_scroll_y = SDL_modff(mouse->integer_mode_residual_scroll_y + y, &y);
}
if (x == 0.0f && y == 0.0f) {
return;
}
// Post the event, if desired
if (SDL_EventEnabled(SDL_EVENT_MOUSE_WHEEL)) {
float integer_x, integer_y;
if (!mouse->relative_mode || mouse->warp_emulation_active) {
// We're not in relative mode, so all mouse events are global mouse events
mouseID = SDL_GLOBAL_MOUSE_ID;
@ -1044,11 +1044,26 @@ void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseI
event.common.timestamp = timestamp;
event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
event.wheel.which = mouseID;
event.wheel.x = x;
event.wheel.y = y;
event.wheel.direction = direction;
event.wheel.mouse_x = mouse->x;
event.wheel.mouse_y = mouse->y;
mouse->residual_scroll_x = SDL_modff(mouse->residual_scroll_x + x, &integer_x);
event.wheel.integer_x = (Sint32)integer_x;
mouse->residual_scroll_y = SDL_modff(mouse->residual_scroll_y + y, &integer_y);
event.wheel.integer_y = (Sint32)integer_y;
// Return the accumulated values in x/y when integer wheel mode is enabled.
// This is necessary for compatibility with sdl2-compat 2.32.54.
if (mouse->integer_mode_flags & 2) {
event.wheel.x = integer_x;
event.wheel.y = integer_y;
} else {
event.wheel.x = x;
event.wheel.y = y;
}
SDL_PushEvent(&event);
}
}

View File

@ -95,8 +95,6 @@ typedef struct
Uint8 integer_mode_flags; // 1 to enable mouse quantization, 2 to enable wheel quantization
float integer_mode_residual_motion_x;
float integer_mode_residual_motion_y;
float integer_mode_residual_scroll_x;
float integer_mode_residual_scroll_y;
// Data common to all mice
SDL_Window *focus;
@ -105,6 +103,8 @@ typedef struct
float x_accu;
float y_accu;
float last_x, last_y; // the last reported x and y coordinates
float residual_scroll_x;
float residual_scroll_y;
double click_motion_x;
double click_motion_y;
bool has_position;

View File

@ -565,10 +565,19 @@ void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *wind
event.pbutton.down = down;
SDL_PushEvent(&event);
if (window && (pen_touching == instance_id)) {
if (window && (!pen_touching || (pen_touching == instance_id))) {
SDL_Mouse *mouse = SDL_GetMouse();
if (mouse && mouse->pen_mouse_events) {
SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, button + 1, down);
static const Uint8 mouse_buttons[] = {
SDL_BUTTON_LEFT,
SDL_BUTTON_RIGHT,
SDL_BUTTON_MIDDLE,
SDL_BUTTON_X1,
SDL_BUTTON_X2
};
if (button < SDL_arraysize(mouse_buttons)) {
SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, mouse_buttons[button], down);
}
}
}
}

View File

@ -56,15 +56,47 @@
}
#define CHECK_RENDERPASS \
if (!((Pass *)render_pass)->in_progress) { \
if (!((RenderPass *)render_pass)->in_progress) { \
SDL_assert_release(!"Render pass not in progress!"); \
return; \
}
#define CHECK_GRAPHICS_PIPELINE_BOUND \
if (!((CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER)->graphics_pipeline_bound) { \
SDL_assert_release(!"Graphics pipeline not bound!"); \
return; \
#define CHECK_SAMPLER_TEXTURES \
RenderPass *rp = (RenderPass *)render_pass; \
for (Uint32 color_target_index = 0; color_target_index < rp->num_color_targets; color_target_index += 1) { \
for (Uint32 texture_sampler_index = 0; texture_sampler_index < num_bindings; texture_sampler_index += 1) { \
if (rp->color_targets[color_target_index] == texture_sampler_bindings[texture_sampler_index].texture) { \
SDL_assert_release(!"Texture cannot be simultaneously bound as a color target and a sampler!"); \
} \
} \
} \
\
for (Uint32 texture_sampler_index = 0; texture_sampler_index < num_bindings; texture_sampler_index += 1) { \
if (rp->depth_stencil_target != NULL && rp->depth_stencil_target == texture_sampler_bindings[texture_sampler_index].texture) { \
SDL_assert_release(!"Texture cannot be simultaneously bound as a depth stencil target and a sampler!"); \
} \
}
#define CHECK_STORAGE_TEXTURES \
RenderPass *rp = (RenderPass *)render_pass; \
for (Uint32 color_target_index = 0; color_target_index < rp->num_color_targets; color_target_index += 1) { \
for (Uint32 texture_sampler_index = 0; texture_sampler_index < num_bindings; texture_sampler_index += 1) { \
if (rp->color_targets[color_target_index] == storage_textures[texture_sampler_index]) { \
SDL_assert_release(!"Texture cannot be simultaneously bound as a color target and a storage texture!"); \
} \
} \
} \
\
for (Uint32 texture_sampler_index = 0; texture_sampler_index < num_bindings; texture_sampler_index += 1) { \
if (rp->depth_stencil_target != NULL && rp->depth_stencil_target == storage_textures[texture_sampler_index]) { \
SDL_assert_release(!"Texture cannot be simultaneously bound as a depth stencil target and a storage texture!"); \
} \
}
#define CHECK_GRAPHICS_PIPELINE_BOUND \
if (!((RenderPass *)render_pass)->graphics_pipeline) { \
SDL_assert_release(!"Graphics pipeline not bound!"); \
return; \
}
#define CHECK_COMPUTEPASS \
@ -74,7 +106,7 @@
}
#define CHECK_COMPUTE_PIPELINE_BOUND \
if (!((CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER)->compute_pipeline_bound) { \
if (!((ComputePass *)compute_pass)->compute_pipeline) { \
SDL_assert_release(!"Compute pipeline not bound!"); \
return; \
}
@ -137,23 +169,137 @@
((CommandBufferCommonHeader *)command_buffer)->device
#define RENDERPASS_COMMAND_BUFFER \
((Pass *)render_pass)->command_buffer
((RenderPass *)render_pass)->command_buffer
#define RENDERPASS_DEVICE \
((CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER)->device
#define RENDERPASS_BOUND_PIPELINE \
((RenderPass *)render_pass)->graphics_pipeline
#define COMPUTEPASS_COMMAND_BUFFER \
((Pass *)compute_pass)->command_buffer
#define COMPUTEPASS_DEVICE \
((CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER)->device
#define COMPUTEPASS_BOUND_PIPELINE \
((ComputePass *)compute_pass)->compute_pipeline
#define COPYPASS_COMMAND_BUFFER \
((Pass *)copy_pass)->command_buffer
#define COPYPASS_DEVICE \
((CommandBufferCommonHeader *)COPYPASS_COMMAND_BUFFER)->device
static bool TextureFormatIsComputeWritable[] = {
false, // INVALID
false, // A8_UNORM
true, // R8_UNORM
true, // R8G8_UNORM
true, // R8G8B8A8_UNORM
true, // R16_UNORM
true, // R16G16_UNORM
true, // R16G16B16A16_UNORM
true, // R10G10B10A2_UNORM
false, // B5G6R5_UNORM
false, // B5G5R5A1_UNORM
false, // B4G4R4A4_UNORM
false, // B8G8R8A8_UNORM
false, // BC1_UNORM
false, // BC2_UNORM
false, // BC3_UNORM
false, // BC4_UNORM
false, // BC5_UNORM
false, // BC7_UNORM
false, // BC6H_FLOAT
false, // BC6H_UFLOAT
true, // R8_SNORM
true, // R8G8_SNORM
true, // R8G8B8A8_SNORM
true, // R16_SNORM
true, // R16G16_SNORM
true, // R16G16B16A16_SNORM
true, // R16_FLOAT
true, // R16G16_FLOAT
true, // R16G16B16A16_FLOAT
true, // R32_FLOAT
true, // R32G32_FLOAT
true, // R32G32B32A32_FLOAT
true, // R11G11B10_UFLOAT
true, // R8_UINT
true, // R8G8_UINT
true, // R8G8B8A8_UINT
true, // R16_UINT
true, // R16G16_UINT
true, // R16G16B16A16_UINT
true, // R32_UINT
true, // R32G32_UINT
true, // R32G32B32A32_UINT
true, // R8_INT
true, // R8G8_INT
true, // R8G8B8A8_INT
true, // R16_INT
true, // R16G16_INT
true, // R16G16B16A16_INT
true, // R32_INT
true, // R32G32_INT
true, // R32G32B32A32_INT
false, // R8G8B8A8_UNORM_SRGB
false, // B8G8R8A8_UNORM_SRGB
false, // BC1_UNORM_SRGB
false, // BC3_UNORM_SRGB
false, // BC3_UNORM_SRGB
false, // BC7_UNORM_SRGB
false, // D16_UNORM
false, // D24_UNORM
false, // D32_FLOAT
false, // D24_UNORM_S8_UINT
false, // D32_FLOAT_S8_UINT
false, // ASTC_4x4_UNORM
false, // ASTC_5x4_UNORM
false, // ASTC_5x5_UNORM
false, // ASTC_6x5_UNORM
false, // ASTC_6x6_UNORM
false, // ASTC_8x5_UNORM
false, // ASTC_8x6_UNORM
false, // ASTC_8x8_UNORM
false, // ASTC_10x5_UNORM
false, // ASTC_10x6_UNORM
false, // ASTC_10x8_UNORM
false, // ASTC_10x10_UNORM
false, // ASTC_12x10_UNORM
false, // ASTC_12x12_UNORM
false, // ASTC_4x4_UNORM_SRGB
false, // ASTC_5x4_UNORM_SRGB
false, // ASTC_5x5_UNORM_SRGB
false, // ASTC_6x5_UNORM_SRGB
false, // ASTC_6x6_UNORM_SRGB
false, // ASTC_8x5_UNORM_SRGB
false, // ASTC_8x6_UNORM_SRGB
false, // ASTC_8x8_UNORM_SRGB
false, // ASTC_10x5_UNORM_SRGB
false, // ASTC_10x6_UNORM_SRGB
false, // ASTC_10x8_UNORM_SRGB
false, // ASTC_10x10_UNORM_SRGB
false, // ASTC_12x10_UNORM_SRGB
false, // ASTC_12x12_UNORM_SRGB
false, // ASTC_4x4_FLOAT
false, // ASTC_5x4_FLOAT
false, // ASTC_5x5_FLOAT
false, // ASTC_6x5_FLOAT
false, // ASTC_6x6_FLOAT
false, // ASTC_8x5_FLOAT
false, // ASTC_8x6_FLOAT
false, // ASTC_8x8_FLOAT
false, // ASTC_10x5_FLOAT
false, // ASTC_10x6_FLOAT
false, // ASTC_10x8_FLOAT
false, // ASTC_10x10_FLOAT
false, // ASTC_12x10_FLOAT
false // ASTC_12x12_FLOAT
};
// Drivers
#ifndef SDL_GPU_DISABLED
@ -371,6 +517,73 @@ void SDL_GPU_BlitCommon(
SDL_EndGPURenderPass(render_pass);
}
static void SDL_GPU_CheckGraphicsBindings(SDL_GPURenderPass *render_pass)
{
RenderPass *rp = (RenderPass *)render_pass;
GraphicsPipelineCommonHeader *pipeline = (GraphicsPipelineCommonHeader *)RENDERPASS_BOUND_PIPELINE;
for (Uint32 i = 0; i < pipeline->num_vertex_samplers; i += 1) {
if (!rp->vertex_sampler_bound[i]) {
SDL_assert_release(!"Missing vertex sampler binding!");
}
}
for (Uint32 i = 0; i < pipeline->num_vertex_storage_textures; i += 1) {
if (!rp->vertex_storage_texture_bound[i]) {
SDL_assert_release(!"Missing vertex storage texture binding!");
}
}
for (Uint32 i = 0; i < pipeline->num_vertex_storage_buffers; i += 1) {
if (!rp->vertex_storage_buffer_bound[i]) {
SDL_assert_release(!"Missing vertex storage buffer binding!");
}
}
for (Uint32 i = 0; i < pipeline->num_fragment_samplers; i += 1) {
if (!rp->fragment_sampler_bound[i]) {
SDL_assert_release(!"Missing fragment sampler binding!");
}
}
for (Uint32 i = 0; i < pipeline->num_fragment_storage_textures; i += 1) {
if (!rp->fragment_storage_texture_bound[i]) {
SDL_assert_release(!"Missing fragment storage texture binding!");
}
}
for (Uint32 i = 0; i < pipeline->num_fragment_storage_buffers; i += 1) {
if (!rp->fragment_storage_buffer_bound[i]) {
SDL_assert_release(!"Missing fragment storage buffer binding!");
}
}
}
static void SDL_GPU_CheckComputeBindings(SDL_GPUComputePass *compute_pass)
{
ComputePass *cp = (ComputePass *)compute_pass;
ComputePipelineCommonHeader *pipeline = (ComputePipelineCommonHeader *)COMPUTEPASS_BOUND_PIPELINE;
for (Uint32 i = 0; i < pipeline->numSamplers; i += 1) {
if (!cp->sampler_bound[i]) {
SDL_assert_release(!"Missing compute sampler binding!");
}
}
for (Uint32 i = 0; i < pipeline->numReadonlyStorageTextures; i += 1) {
if (!cp->read_only_storage_texture_bound[i]) {
SDL_assert_release(!"Missing compute readonly storage texture binding!");
}
}
for (Uint32 i = 0; i < pipeline->numReadonlyStorageBuffers; i += 1) {
if (!cp->read_only_storage_buffer_bound[i]) {
SDL_assert_release(!"Missing compute readonly storage buffer binding!");
}
}
for (Uint32 i = 0; i < pipeline->numReadWriteStorageTextures; i += 1) {
if (!cp->read_write_storage_texture_bound[i]) {
SDL_assert_release(!"Missing compute read-write storage texture binding!");
}
}
for (Uint32 i = 0; i < pipeline->numReadWriteStorageBuffers; i += 1) {
if (!cp->read_write_storage_buffer_bound[i]) {
SDL_assert_release(!"Missing compute read-write storage buffer bbinding!");
}
}
}
// Driver Functions
#ifndef SDL_GPU_DISABLED
@ -532,7 +745,6 @@ SDL_GPUDevice *SDL_CreateGPUDeviceWithProperties(SDL_PropertiesID props)
result = selectedBackend->CreateDevice(debug_mode, preferLowPower, props);
if (result != NULL) {
result->backend = selectedBackend->name;
result->shader_formats = selectedBackend->shader_formats;
result->debug_mode = debug_mode;
}
}
@ -721,6 +933,13 @@ bool SDL_GPUTextureSupportsFormat(
CHECK_TEXTUREFORMAT_ENUM_INVALID(format, false)
}
if ((usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) ||
(usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE)) {
if (!TextureFormatIsComputeWritable[format]) {
return false;
}
}
return device->SupportsTextureFormat(
device->driverData,
format,
@ -1343,15 +1562,29 @@ SDL_GPUCommandBuffer *SDL_AcquireGPUCommandBuffer(
commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
commandBufferHeader->device = device;
commandBufferHeader->render_pass.command_buffer = command_buffer;
commandBufferHeader->render_pass.in_progress = false;
commandBufferHeader->graphics_pipeline_bound = false;
commandBufferHeader->compute_pass.command_buffer = command_buffer;
commandBufferHeader->compute_pass.in_progress = false;
commandBufferHeader->compute_pipeline_bound = false;
commandBufferHeader->copy_pass.command_buffer = command_buffer;
commandBufferHeader->copy_pass.in_progress = false;
commandBufferHeader->swapchain_texture_acquired = false;
commandBufferHeader->submitted = false;
if (device->debug_mode) {
commandBufferHeader->render_pass.in_progress = false;
commandBufferHeader->render_pass.graphics_pipeline = NULL;
commandBufferHeader->compute_pass.in_progress = false;
commandBufferHeader->compute_pass.compute_pipeline = NULL;
commandBufferHeader->copy_pass.in_progress = false;
commandBufferHeader->swapchain_texture_acquired = false;
commandBufferHeader->submitted = false;
SDL_zeroa(commandBufferHeader->render_pass.vertex_sampler_bound);
SDL_zeroa(commandBufferHeader->render_pass.vertex_storage_texture_bound);
SDL_zeroa(commandBufferHeader->render_pass.vertex_storage_buffer_bound);
SDL_zeroa(commandBufferHeader->render_pass.fragment_sampler_bound);
SDL_zeroa(commandBufferHeader->render_pass.fragment_storage_texture_bound);
SDL_zeroa(commandBufferHeader->render_pass.fragment_storage_buffer_bound);
SDL_zeroa(commandBufferHeader->compute_pass.sampler_bound);
SDL_zeroa(commandBufferHeader->compute_pass.read_only_storage_texture_bound);
SDL_zeroa(commandBufferHeader->compute_pass.read_only_storage_buffer_bound);
SDL_zeroa(commandBufferHeader->compute_pass.read_write_storage_texture_bound);
SDL_zeroa(commandBufferHeader->compute_pass.read_write_storage_buffer_bound);
}
return command_buffer;
}
@ -1469,30 +1702,47 @@ SDL_GPURenderPass *SDL_BeginGPURenderPass(
if (color_target_infos[i].cycle && color_target_infos[i].load_op == SDL_GPU_LOADOP_LOAD) {
SDL_assert_release(!"Cannot cycle color target when load op is LOAD!");
return NULL;
}
if (color_target_infos[i].store_op == SDL_GPU_STOREOP_RESOLVE || color_target_infos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
if (color_target_infos[i].resolve_texture == NULL) {
SDL_assert_release(!"Store op is RESOLVE or RESOLVE_AND_STORE but resolve_texture is NULL!");
return NULL;
} else {
TextureCommonHeader *resolveTextureHeader = (TextureCommonHeader *)color_target_infos[i].resolve_texture;
if (textureHeader->info.sample_count == SDL_GPU_SAMPLECOUNT_1) {
SDL_assert_release(!"Store op is RESOLVE or RESOLVE_AND_STORE but texture is not multisample!");
return NULL;
}
if (resolveTextureHeader->info.sample_count != SDL_GPU_SAMPLECOUNT_1) {
SDL_assert_release(!"Resolve texture must have a sample count of 1!");
return NULL;
}
if (resolveTextureHeader->info.format != textureHeader->info.format) {
SDL_assert_release(!"Resolve texture must have the same format as its corresponding color target!");
return NULL;
}
if (resolveTextureHeader->info.type == SDL_GPU_TEXTURETYPE_3D) {
SDL_assert_release(!"Resolve texture must not be of TEXTURETYPE_3D!");
return NULL;
}
if (!(resolveTextureHeader->info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
SDL_assert_release(!"Resolve texture usage must include COLOR_TARGET!");
return NULL;
}
}
}
if (color_target_infos[i].layer_or_depth_plane >= textureHeader->info.layer_count_or_depth) {
SDL_assert_release(!"Color target layer index must be less than the texture's layer count!");
return NULL;
}
if (color_target_infos[i].mip_level >= textureHeader->info.num_levels) {
SDL_assert_release(!"Color target mip level must be less than the texture's level count!");
return NULL;
}
}
if (depth_stencil_target_info != NULL) {
@ -1500,10 +1750,12 @@ SDL_GPURenderPass *SDL_BeginGPURenderPass(
TextureCommonHeader *textureHeader = (TextureCommonHeader *)depth_stencil_target_info->texture;
if (!(textureHeader->info.usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
SDL_assert_release(!"Depth target must have been created with the DEPTH_STENCIL_TARGET usage flag!");
return NULL;
}
if (depth_stencil_target_info->cycle && (depth_stencil_target_info->load_op == SDL_GPU_LOADOP_LOAD || depth_stencil_target_info->stencil_load_op == SDL_GPU_LOADOP_LOAD)) {
SDL_assert_release(!"Cannot cycle depth target when load op or stencil load op is LOAD!");
return NULL;
}
if (depth_stencil_target_info->store_op == SDL_GPU_STOREOP_RESOLVE ||
@ -1511,6 +1763,7 @@ SDL_GPURenderPass *SDL_BeginGPURenderPass(
depth_stencil_target_info->store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE ||
depth_stencil_target_info->stencil_store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
SDL_assert_release(!"RESOLVE store ops are not supported for depth-stencil targets!");
return NULL;
}
}
}
@ -1522,7 +1775,18 @@ SDL_GPURenderPass *SDL_BeginGPURenderPass(
depth_stencil_target_info);
commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
commandBufferHeader->render_pass.in_progress = true;
if (COMMAND_BUFFER_DEVICE->debug_mode) {
commandBufferHeader->render_pass.in_progress = true;
for (Uint32 i = 0; i < num_color_targets; i += 1) {
commandBufferHeader->render_pass.color_targets[i] = color_target_infos[i].texture;
}
commandBufferHeader->render_pass.num_color_targets = num_color_targets;
if (depth_stencil_target_info != NULL) {
commandBufferHeader->render_pass.depth_stencil_target = depth_stencil_target_info->texture;
}
}
return (SDL_GPURenderPass *)&(commandBufferHeader->render_pass);
}
@ -1530,8 +1794,6 @@ void SDL_BindGPUGraphicsPipeline(
SDL_GPURenderPass *render_pass,
SDL_GPUGraphicsPipeline *graphics_pipeline)
{
CommandBufferCommonHeader *commandBufferHeader;
if (render_pass == NULL) {
SDL_InvalidParamError("render_pass");
return;
@ -1545,8 +1807,10 @@ void SDL_BindGPUGraphicsPipeline(
RENDERPASS_COMMAND_BUFFER,
graphics_pipeline);
commandBufferHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER;
commandBufferHeader->graphics_pipeline_bound = true;
if (RENDERPASS_DEVICE->debug_mode) {
RENDERPASS_BOUND_PIPELINE = graphics_pipeline;
}
}
void SDL_SetGPUViewport(
@ -1696,6 +1960,15 @@ void SDL_BindGPUVertexSamplers(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
if (!((CommandBufferCommonHeader*)RENDERPASS_COMMAND_BUFFER)->ignore_render_pass_texture_validation)
{
CHECK_SAMPLER_TEXTURES
}
for (Uint32 i = 0; i < num_bindings; i += 1) {
((RenderPass *)render_pass)->vertex_sampler_bound[first_slot + i] = true;
}
}
RENDERPASS_DEVICE->BindVertexSamplers(
@ -1722,6 +1995,11 @@ void SDL_BindGPUVertexStorageTextures(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_STORAGE_TEXTURES
for (Uint32 i = 0; i < num_bindings; i += 1) {
((RenderPass *)render_pass)->vertex_storage_texture_bound[first_slot + i] = true;
}
}
RENDERPASS_DEVICE->BindVertexStorageTextures(
@ -1748,6 +2026,10 @@ void SDL_BindGPUVertexStorageBuffers(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
for (Uint32 i = 0; i < num_bindings; i += 1) {
((RenderPass *)render_pass)->vertex_storage_buffer_bound[first_slot + i] = true;
}
}
RENDERPASS_DEVICE->BindVertexStorageBuffers(
@ -1774,6 +2056,14 @@ void SDL_BindGPUFragmentSamplers(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
if (!((CommandBufferCommonHeader*)RENDERPASS_COMMAND_BUFFER)->ignore_render_pass_texture_validation) {
CHECK_SAMPLER_TEXTURES
}
for (Uint32 i = 0; i < num_bindings; i += 1) {
((RenderPass *)render_pass)->fragment_sampler_bound[first_slot + i] = true;
}
}
RENDERPASS_DEVICE->BindFragmentSamplers(
@ -1800,6 +2090,11 @@ void SDL_BindGPUFragmentStorageTextures(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_STORAGE_TEXTURES
for (Uint32 i = 0; i < num_bindings; i += 1) {
((RenderPass *)render_pass)->fragment_storage_texture_bound[first_slot + i] = true;
}
}
RENDERPASS_DEVICE->BindFragmentStorageTextures(
@ -1826,6 +2121,10 @@ void SDL_BindGPUFragmentStorageBuffers(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
for (Uint32 i = 0; i < num_bindings; i += 1) {
((RenderPass *)render_pass)->fragment_storage_buffer_bound[first_slot + i] = true;
}
}
RENDERPASS_DEVICE->BindFragmentStorageBuffers(
@ -1851,6 +2150,7 @@ void SDL_DrawGPUIndexedPrimitives(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_GRAPHICS_PIPELINE_BOUND
SDL_GPU_CheckGraphicsBindings(render_pass);
}
RENDERPASS_DEVICE->DrawIndexedPrimitives(
@ -1877,6 +2177,7 @@ void SDL_DrawGPUPrimitives(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_GRAPHICS_PIPELINE_BOUND
SDL_GPU_CheckGraphicsBindings(render_pass);
}
RENDERPASS_DEVICE->DrawPrimitives(
@ -1905,6 +2206,7 @@ void SDL_DrawGPUPrimitivesIndirect(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_GRAPHICS_PIPELINE_BOUND
SDL_GPU_CheckGraphicsBindings(render_pass);
}
RENDERPASS_DEVICE->DrawPrimitivesIndirect(
@ -1932,6 +2234,7 @@ void SDL_DrawGPUIndexedPrimitivesIndirect(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_GRAPHICS_PIPELINE_BOUND
SDL_GPU_CheckGraphicsBindings(render_pass);
}
RENDERPASS_DEVICE->DrawIndexedPrimitivesIndirect(
@ -1945,6 +2248,7 @@ void SDL_EndGPURenderPass(
SDL_GPURenderPass *render_pass)
{
CommandBufferCommonHeader *commandBufferCommonHeader;
commandBufferCommonHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER;
if (render_pass == NULL) {
SDL_InvalidParamError("render_pass");
@ -1958,9 +2262,22 @@ void SDL_EndGPURenderPass(
RENDERPASS_DEVICE->EndRenderPass(
RENDERPASS_COMMAND_BUFFER);
commandBufferCommonHeader = (CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER;
commandBufferCommonHeader->render_pass.in_progress = false;
commandBufferCommonHeader->graphics_pipeline_bound = false;
if (RENDERPASS_DEVICE->debug_mode) {
commandBufferCommonHeader->render_pass.in_progress = false;
for (Uint32 i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1)
{
commandBufferCommonHeader->render_pass.color_targets[i] = NULL;
}
commandBufferCommonHeader->render_pass.num_color_targets = 0;
commandBufferCommonHeader->render_pass.depth_stencil_target = NULL;
commandBufferCommonHeader->render_pass.graphics_pipeline = NULL;
SDL_zeroa(commandBufferCommonHeader->render_pass.vertex_sampler_bound);
SDL_zeroa(commandBufferCommonHeader->render_pass.vertex_storage_texture_bound);
SDL_zeroa(commandBufferCommonHeader->render_pass.vertex_storage_buffer_bound);
SDL_zeroa(commandBufferCommonHeader->render_pass.fragment_sampler_bound);
SDL_zeroa(commandBufferCommonHeader->render_pass.fragment_storage_texture_bound);
SDL_zeroa(commandBufferCommonHeader->render_pass.fragment_storage_buffer_bound);
}
}
// Compute Pass
@ -2004,6 +2321,16 @@ SDL_GPUComputePass *SDL_BeginGPUComputePass(
SDL_assert_release(!"Texture must be created with COMPUTE_STORAGE_WRITE or COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE flag");
return NULL;
}
if (storage_texture_bindings[i].layer >= header->info.layer_count_or_depth) {
SDL_assert_release(!"Storage texture layer index must be less than the texture's layer count!");
return NULL;
}
if (storage_texture_bindings[i].mip_level >= header->info.num_levels) {
SDL_assert_release(!"Storage texture mip level must be less than the texture's level count!");
return NULL;
}
}
// TODO: validate buffer usage?
@ -2017,7 +2344,19 @@ SDL_GPUComputePass *SDL_BeginGPUComputePass(
num_storage_buffer_bindings);
commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
commandBufferHeader->compute_pass.in_progress = true;
if (COMMAND_BUFFER_DEVICE->debug_mode) {
commandBufferHeader->compute_pass.in_progress = true;
for (Uint32 i = 0; i < num_storage_texture_bindings; i += 1) {
commandBufferHeader->compute_pass.read_write_storage_texture_bound[i] = true;
}
for (Uint32 i = 0; i < num_storage_buffer_bindings; i += 1) {
commandBufferHeader->compute_pass.read_write_storage_buffer_bound[i] = true;
}
}
return (SDL_GPUComputePass *)&(commandBufferHeader->compute_pass);
}
@ -2025,8 +2364,6 @@ void SDL_BindGPUComputePipeline(
SDL_GPUComputePass *compute_pass,
SDL_GPUComputePipeline *compute_pipeline)
{
CommandBufferCommonHeader *commandBufferHeader;
if (compute_pass == NULL) {
SDL_InvalidParamError("compute_pass");
return;
@ -2044,8 +2381,10 @@ void SDL_BindGPUComputePipeline(
COMPUTEPASS_COMMAND_BUFFER,
compute_pipeline);
commandBufferHeader = (CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER;
commandBufferHeader->compute_pipeline_bound = true;
if (COMPUTEPASS_DEVICE->debug_mode) {
COMPUTEPASS_BOUND_PIPELINE = compute_pipeline;
}
}
void SDL_BindGPUComputeSamplers(
@ -2065,6 +2404,10 @@ void SDL_BindGPUComputeSamplers(
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
for (Uint32 i = 0; i < num_bindings; i += 1) {
((ComputePass *)compute_pass)->sampler_bound[first_slot + i] = true;
}
}
COMPUTEPASS_DEVICE->BindComputeSamplers(
@ -2091,6 +2434,10 @@ void SDL_BindGPUComputeStorageTextures(
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
for (Uint32 i = 0; i < num_bindings; i += 1) {
((ComputePass *)compute_pass)->read_only_storage_texture_bound[first_slot + i] = true;
}
}
COMPUTEPASS_DEVICE->BindComputeStorageTextures(
@ -2117,6 +2464,10 @@ void SDL_BindGPUComputeStorageBuffers(
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
for (Uint32 i = 0; i < num_bindings; i += 1) {
((ComputePass *)compute_pass)->read_only_storage_buffer_bound[first_slot + i] = true;
}
}
COMPUTEPASS_DEVICE->BindComputeStorageBuffers(
@ -2140,6 +2491,7 @@ void SDL_DispatchGPUCompute(
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
CHECK_COMPUTE_PIPELINE_BOUND
SDL_GPU_CheckComputeBindings(compute_pass);
}
COMPUTEPASS_DEVICE->DispatchCompute(
@ -2162,6 +2514,7 @@ void SDL_DispatchGPUComputeIndirect(
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
CHECK_COMPUTE_PIPELINE_BOUND
SDL_GPU_CheckComputeBindings(compute_pass);
}
COMPUTEPASS_DEVICE->DispatchComputeIndirect(
@ -2187,9 +2540,16 @@ void SDL_EndGPUComputePass(
COMPUTEPASS_DEVICE->EndComputePass(
COMPUTEPASS_COMMAND_BUFFER);
commandBufferCommonHeader = (CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER;
commandBufferCommonHeader->compute_pass.in_progress = false;
commandBufferCommonHeader->compute_pipeline_bound = false;
if (COMPUTEPASS_DEVICE->debug_mode) {
commandBufferCommonHeader = (CommandBufferCommonHeader *)COMPUTEPASS_COMMAND_BUFFER;
commandBufferCommonHeader->compute_pass.in_progress = false;
commandBufferCommonHeader->compute_pass.compute_pipeline = false;
SDL_zeroa(commandBufferCommonHeader->compute_pass.sampler_bound);
SDL_zeroa(commandBufferCommonHeader->compute_pass.read_only_storage_texture_bound);
SDL_zeroa(commandBufferCommonHeader->compute_pass.read_only_storage_buffer_bound);
SDL_zeroa(commandBufferCommonHeader->compute_pass.read_write_storage_texture_bound);
SDL_zeroa(commandBufferCommonHeader->compute_pass.read_write_storage_buffer_bound);
}
}
// TransferBuffer Data
@ -2247,7 +2607,11 @@ SDL_GPUCopyPass *SDL_BeginGPUCopyPass(
command_buffer);
commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
commandBufferHeader->copy_pass.in_progress = true;
if (COMMAND_BUFFER_DEVICE->debug_mode) {
commandBufferHeader->copy_pass.in_progress = true;
}
return (SDL_GPUCopyPass *)&(commandBufferHeader->copy_pass);
}
@ -2498,7 +2862,9 @@ void SDL_EndGPUCopyPass(
COPYPASS_DEVICE->EndCopyPass(
COPYPASS_COMMAND_BUFFER);
((CommandBufferCommonHeader *)COPYPASS_COMMAND_BUFFER)->copy_pass.in_progress = false;
if (COPYPASS_DEVICE->debug_mode) {
((CommandBufferCommonHeader *)COPYPASS_COMMAND_BUFFER)->copy_pass.in_progress = false;
}
}
void SDL_GenerateMipmapsForGPUTexture(
@ -2528,11 +2894,19 @@ void SDL_GenerateMipmapsForGPUTexture(
SDL_assert_release(!"GenerateMipmaps texture must be created with SAMPLER and COLOR_TARGET usage flags!");
return;
}
CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
commandBufferHeader->ignore_render_pass_texture_validation = true;
}
COMMAND_BUFFER_DEVICE->GenerateMipmaps(
command_buffer,
texture);
if (COMMAND_BUFFER_DEVICE->debug_mode) {
CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader *)command_buffer;
commandBufferHeader->ignore_render_pass_texture_validation = false;
}
}
void SDL_BlitGPUTexture(

View File

@ -24,6 +24,21 @@
#ifndef SDL_GPU_DRIVER_H
#define SDL_GPU_DRIVER_H
// GraphicsDevice Limits
#define MAX_TEXTURE_SAMPLERS_PER_STAGE 16
#define MAX_STORAGE_TEXTURES_PER_STAGE 8
#define MAX_STORAGE_BUFFERS_PER_STAGE 8
#define MAX_UNIFORM_BUFFERS_PER_STAGE 4
#define MAX_COMPUTE_WRITE_TEXTURES 8
#define MAX_COMPUTE_WRITE_BUFFERS 8
#define UNIFORM_BUFFER_SIZE 32768
#define MAX_VERTEX_BUFFERS 16
#define MAX_VERTEX_ATTRIBUTES 16
#define MAX_COLOR_TARGET_BINDINGS 4
#define MAX_PRESENT_COUNT 16
#define MAX_FRAMES_IN_FLIGHT 3
// Common Structs
typedef struct Pass
@ -32,16 +47,51 @@ typedef struct Pass
bool in_progress;
} Pass;
typedef struct ComputePass
{
SDL_GPUCommandBuffer *command_buffer;
bool in_progress;
SDL_GPUComputePipeline *compute_pipeline;
bool sampler_bound[MAX_TEXTURE_SAMPLERS_PER_STAGE];
bool read_only_storage_texture_bound[MAX_STORAGE_TEXTURES_PER_STAGE];
bool read_only_storage_buffer_bound[MAX_STORAGE_BUFFERS_PER_STAGE];
bool read_write_storage_texture_bound[MAX_COMPUTE_WRITE_TEXTURES];
bool read_write_storage_buffer_bound[MAX_COMPUTE_WRITE_BUFFERS];
} ComputePass;
typedef struct RenderPass
{
SDL_GPUCommandBuffer *command_buffer;
bool in_progress;
SDL_GPUTexture *color_targets[MAX_COLOR_TARGET_BINDINGS];
Uint32 num_color_targets;
SDL_GPUTexture *depth_stencil_target;
SDL_GPUGraphicsPipeline *graphics_pipeline;
bool vertex_sampler_bound[MAX_TEXTURE_SAMPLERS_PER_STAGE];
bool vertex_storage_texture_bound[MAX_STORAGE_TEXTURES_PER_STAGE];
bool vertex_storage_buffer_bound[MAX_STORAGE_BUFFERS_PER_STAGE];
bool fragment_sampler_bound[MAX_TEXTURE_SAMPLERS_PER_STAGE];
bool fragment_storage_texture_bound[MAX_STORAGE_TEXTURES_PER_STAGE];
bool fragment_storage_buffer_bound[MAX_STORAGE_BUFFERS_PER_STAGE];
} RenderPass;
typedef struct CommandBufferCommonHeader
{
SDL_GPUDevice *device;
Pass render_pass;
bool graphics_pipeline_bound;
Pass compute_pass;
bool compute_pipeline_bound;
RenderPass render_pass;
ComputePass compute_pass;
Pass copy_pass;
bool swapchain_texture_acquired;
bool submitted;
// used to avoid tripping assert on GenerateMipmaps
bool ignore_render_pass_texture_validation;
} CommandBufferCommonHeader;
typedef struct TextureCommonHeader
@ -49,6 +99,29 @@ typedef struct TextureCommonHeader
SDL_GPUTextureCreateInfo info;
} TextureCommonHeader;
typedef struct GraphicsPipelineCommonHeader
{
Uint32 num_vertex_samplers;
Uint32 num_vertex_storage_textures;
Uint32 num_vertex_storage_buffers;
Uint32 num_vertex_uniform_buffers;
Uint32 num_fragment_samplers;
Uint32 num_fragment_storage_textures;
Uint32 num_fragment_storage_buffers;
Uint32 num_fragment_uniform_buffers;
} GraphicsPipelineCommonHeader;
typedef struct ComputePipelineCommonHeader
{
Uint32 numSamplers;
Uint32 numReadonlyStorageTextures;
Uint32 numReadonlyStorageBuffers;
Uint32 numReadWriteStorageTextures;
Uint32 numReadWriteStorageBuffers;
Uint32 numUniformBuffers;
} ComputePipelineCommonHeader;
typedef struct BlitFragmentUniforms
{
// texcoord space
@ -136,6 +209,7 @@ static inline Sint32 Texture_GetBlockWidth(
case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT:
case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT:
case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM:
@ -253,6 +327,7 @@ static inline Sint32 Texture_GetBlockHeight(
case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT:
case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT:
case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB:
case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM:
@ -385,21 +460,6 @@ static inline Uint32 BytesPerRow(
return blocksPerRow * SDL_GPUTextureFormatTexelBlockSize(format);
}
// GraphicsDevice Limits
#define MAX_TEXTURE_SAMPLERS_PER_STAGE 16
#define MAX_STORAGE_TEXTURES_PER_STAGE 8
#define MAX_STORAGE_BUFFERS_PER_STAGE 8
#define MAX_UNIFORM_BUFFERS_PER_STAGE 4
#define MAX_COMPUTE_WRITE_TEXTURES 8
#define MAX_COMPUTE_WRITE_BUFFERS 8
#define UNIFORM_BUFFER_SIZE 32768
#define MAX_VERTEX_BUFFERS 16
#define MAX_VERTEX_ATTRIBUTES 16
#define MAX_COLOR_TARGET_BINDINGS 4
#define MAX_PRESENT_COUNT 16
#define MAX_FRAMES_IN_FLIGHT 3
// Internal Macros
#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \

File diff suppressed because it is too large Load Diff

View File

@ -476,33 +476,21 @@ typedef struct MetalShader
typedef struct MetalGraphicsPipeline
{
GraphicsPipelineCommonHeader header;
id<MTLRenderPipelineState> handle;
SDL_GPURasterizerState rasterizerState;
SDL_GPUPrimitiveType primitiveType;
id<MTLDepthStencilState> depth_stencil_state;
Uint32 vertexSamplerCount;
Uint32 vertexUniformBufferCount;
Uint32 vertexStorageBufferCount;
Uint32 vertexStorageTextureCount;
Uint32 fragmentSamplerCount;
Uint32 fragmentUniformBufferCount;
Uint32 fragmentStorageBufferCount;
Uint32 fragmentStorageTextureCount;
} MetalGraphicsPipeline;
typedef struct MetalComputePipeline
{
ComputePipelineCommonHeader header;
id<MTLComputePipelineState> handle;
Uint32 numSamplers;
Uint32 numReadonlyStorageTextures;
Uint32 numReadWriteStorageTextures;
Uint32 numReadonlyStorageBuffers;
Uint32 numReadWriteStorageBuffers;
Uint32 numUniformBuffers;
Uint32 threadcountX;
Uint32 threadcountY;
Uint32 threadcountZ;
@ -840,6 +828,10 @@ static MetalLibraryFunction METAL_INTERNAL_CompileShader(
dispatch_data_t data;
id<MTLFunction> function;
if (!entrypoint) {
entrypoint = "main0";
}
if (format == SDL_GPU_SHADERFORMAT_MSL) {
NSString *codeString = [[NSString alloc]
initWithBytes:code
@ -1055,12 +1047,12 @@ static SDL_GPUComputePipeline *METAL_CreateComputePipeline(
pipeline = SDL_calloc(1, sizeof(MetalComputePipeline));
pipeline->handle = handle;
pipeline->numSamplers = createinfo->num_samplers;
pipeline->numReadonlyStorageTextures = createinfo->num_readonly_storage_textures;
pipeline->numReadWriteStorageTextures = createinfo->num_readwrite_storage_textures;
pipeline->numReadonlyStorageBuffers = createinfo->num_readonly_storage_buffers;
pipeline->numReadWriteStorageBuffers = createinfo->num_readwrite_storage_buffers;
pipeline->numUniformBuffers = createinfo->num_uniform_buffers;
pipeline->header.numSamplers = createinfo->num_samplers;
pipeline->header.numReadonlyStorageTextures = createinfo->num_readonly_storage_textures;
pipeline->header.numReadWriteStorageTextures = createinfo->num_readwrite_storage_textures;
pipeline->header.numReadonlyStorageBuffers = createinfo->num_readonly_storage_buffers;
pipeline->header.numReadWriteStorageBuffers = createinfo->num_readwrite_storage_buffers;
pipeline->header.numUniformBuffers = createinfo->num_uniform_buffers;
pipeline->threadcountX = createinfo->threadcount_x;
pipeline->threadcountY = createinfo->threadcount_y;
pipeline->threadcountZ = createinfo->threadcount_z;
@ -1203,14 +1195,14 @@ static SDL_GPUGraphicsPipeline *METAL_CreateGraphicsPipeline(
result->depth_stencil_state = depthStencilState;
result->rasterizerState = createinfo->rasterizer_state;
result->primitiveType = createinfo->primitive_type;
result->vertexSamplerCount = vertexShader->numSamplers;
result->vertexUniformBufferCount = vertexShader->numUniformBuffers;
result->vertexStorageBufferCount = vertexShader->numStorageBuffers;
result->vertexStorageTextureCount = vertexShader->numStorageTextures;
result->fragmentSamplerCount = fragmentShader->numSamplers;
result->fragmentUniformBufferCount = fragmentShader->numUniformBuffers;
result->fragmentStorageBufferCount = fragmentShader->numStorageBuffers;
result->fragmentStorageTextureCount = fragmentShader->numStorageTextures;
result->header.num_vertex_samplers = vertexShader->numSamplers;
result->header.num_vertex_uniform_buffers = vertexShader->numUniformBuffers;
result->header.num_vertex_storage_buffers = vertexShader->numStorageBuffers;
result->header.num_vertex_storage_textures = vertexShader->numStorageTextures;
result->header.num_fragment_samplers = fragmentShader->numSamplers;
result->header.num_fragment_uniform_buffers = fragmentShader->numUniformBuffers;
result->header.num_fragment_storage_buffers = fragmentShader->numStorageBuffers;
result->header.num_fragment_storage_textures = fragmentShader->numStorageTextures;
return (SDL_GPUGraphicsPipeline *)result;
}
}
@ -1494,7 +1486,9 @@ static SDL_GPUTexture *METAL_CreateTexture(
// Copy properties so we don't lose information when the client destroys them
container->header.info = *createinfo;
container->header.info.props = SDL_CreateProperties();
SDL_CopyProperties(createinfo->props, container->header.info.props);
if (createinfo->props) {
SDL_CopyProperties(createinfo->props, container->header.info.props);
}
container->activeTexture = texture;
container->textureCapacity = 1;
@ -2406,14 +2400,14 @@ static void METAL_BindGraphicsPipeline(
metalCommandBuffer->needFragmentUniformBufferBind[i] = true;
}
for (i = 0; i < pipeline->vertexUniformBufferCount; i += 1) {
for (i = 0; i < pipeline->header.num_vertex_uniform_buffers; i += 1) {
if (metalCommandBuffer->vertexUniformBuffers[i] == NULL) {
metalCommandBuffer->vertexUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool(
metalCommandBuffer);
}
}
for (i = 0; i < pipeline->fragmentUniformBufferCount; i += 1) {
for (i = 0; i < pipeline->header.num_fragment_uniform_buffers; i += 1) {
if (metalCommandBuffer->fragmentUniformBuffers[i] == NULL) {
metalCommandBuffer->fragmentUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool(
metalCommandBuffer);
@ -2644,11 +2638,11 @@ static void METAL_INTERNAL_BindGraphicsResources(
// Vertex Samplers+Textures
if (commandBuffer->needVertexSamplerBind) {
if (graphicsPipeline->vertexSamplerCount > 0) {
if (graphicsPipeline->header.num_vertex_samplers > 0) {
[commandBuffer->renderEncoder setVertexSamplerStates:commandBuffer->vertexSamplers
withRange:NSMakeRange(0, graphicsPipeline->vertexSamplerCount)];
withRange:NSMakeRange(0, graphicsPipeline->header.num_vertex_samplers)];
[commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexTextures
withRange:NSMakeRange(0, graphicsPipeline->vertexSamplerCount)];
withRange:NSMakeRange(0, graphicsPipeline->header.num_vertex_samplers)];
}
commandBuffer->needVertexSamplerBind = false;
}
@ -2656,10 +2650,10 @@ static void METAL_INTERNAL_BindGraphicsResources(
// Vertex Storage Textures
if (commandBuffer->needVertexStorageTextureBind) {
if (graphicsPipeline->vertexStorageTextureCount > 0) {
if (graphicsPipeline->header.num_vertex_storage_textures > 0) {
[commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexStorageTextures
withRange:NSMakeRange(graphicsPipeline->vertexSamplerCount,
graphicsPipeline->vertexStorageTextureCount)];
withRange:NSMakeRange(graphicsPipeline->header.num_vertex_samplers,
graphicsPipeline->header.num_vertex_storage_textures)];
}
commandBuffer->needVertexStorageTextureBind = false;
}
@ -2667,20 +2661,20 @@ static void METAL_INTERNAL_BindGraphicsResources(
// Vertex Storage Buffers
if (commandBuffer->needVertexStorageBufferBind) {
if (graphicsPipeline->vertexStorageBufferCount > 0) {
if (graphicsPipeline->header.num_vertex_storage_buffers > 0) {
[commandBuffer->renderEncoder setVertexBuffers:commandBuffer->vertexStorageBuffers
offsets:offsets
withRange:NSMakeRange(graphicsPipeline->vertexUniformBufferCount,
graphicsPipeline->vertexStorageBufferCount)];
withRange:NSMakeRange(graphicsPipeline->header.num_vertex_uniform_buffers,
graphicsPipeline->header.num_vertex_storage_buffers)];
}
commandBuffer->needVertexStorageBufferBind = false;
}
// Vertex Uniform Buffers
for (Uint32 i = 0; i < graphicsPipeline->vertexUniformBufferCount; i += 1) {
for (Uint32 i = 0; i < graphicsPipeline->header.num_vertex_uniform_buffers; i += 1) {
if (commandBuffer->needVertexUniformBufferBind[i]) {
if (graphicsPipeline->vertexUniformBufferCount > i) {
if (graphicsPipeline->header.num_vertex_uniform_buffers > i) {
[commandBuffer->renderEncoder
setVertexBuffer:commandBuffer->vertexUniformBuffers[i]->handle
offset:commandBuffer->vertexUniformBuffers[i]->drawOffset
@ -2693,11 +2687,11 @@ static void METAL_INTERNAL_BindGraphicsResources(
// Fragment Samplers+Textures
if (commandBuffer->needFragmentSamplerBind) {
if (graphicsPipeline->fragmentSamplerCount > 0) {
if (graphicsPipeline->header.num_fragment_samplers > 0) {
[commandBuffer->renderEncoder setFragmentSamplerStates:commandBuffer->fragmentSamplers
withRange:NSMakeRange(0, graphicsPipeline->fragmentSamplerCount)];
withRange:NSMakeRange(0, graphicsPipeline->header.num_fragment_samplers)];
[commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentTextures
withRange:NSMakeRange(0, graphicsPipeline->fragmentSamplerCount)];
withRange:NSMakeRange(0, graphicsPipeline->header.num_fragment_samplers)];
}
commandBuffer->needFragmentSamplerBind = false;
}
@ -2705,10 +2699,10 @@ static void METAL_INTERNAL_BindGraphicsResources(
// Fragment Storage Textures
if (commandBuffer->needFragmentStorageTextureBind) {
if (graphicsPipeline->fragmentStorageTextureCount > 0) {
if (graphicsPipeline->header.num_fragment_storage_textures > 0) {
[commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentStorageTextures
withRange:NSMakeRange(graphicsPipeline->fragmentSamplerCount,
graphicsPipeline->fragmentStorageTextureCount)];
withRange:NSMakeRange(graphicsPipeline->header.num_fragment_samplers,
graphicsPipeline->header.num_fragment_storage_textures)];
}
commandBuffer->needFragmentStorageTextureBind = false;
}
@ -2716,20 +2710,20 @@ static void METAL_INTERNAL_BindGraphicsResources(
// Fragment Storage Buffers
if (commandBuffer->needFragmentStorageBufferBind) {
if (graphicsPipeline->fragmentStorageBufferCount > 0) {
if (graphicsPipeline->header.num_fragment_storage_buffers > 0) {
[commandBuffer->renderEncoder setFragmentBuffers:commandBuffer->fragmentStorageBuffers
offsets:offsets
withRange:NSMakeRange(graphicsPipeline->fragmentUniformBufferCount,
graphicsPipeline->fragmentStorageBufferCount)];
withRange:NSMakeRange(graphicsPipeline->header.num_fragment_uniform_buffers,
graphicsPipeline->header.num_fragment_storage_buffers)];
}
commandBuffer->needFragmentStorageBufferBind = false;
}
// Fragment Uniform Buffers
for (Uint32 i = 0; i < graphicsPipeline->fragmentUniformBufferCount; i += 1) {
for (Uint32 i = 0; i < graphicsPipeline->header.num_fragment_uniform_buffers; i += 1) {
if (commandBuffer->needFragmentUniformBufferBind[i]) {
if (graphicsPipeline->fragmentUniformBufferCount > i) {
if (graphicsPipeline->header.num_fragment_uniform_buffers > i) {
[commandBuffer->renderEncoder
setFragmentBuffer:commandBuffer->fragmentUniformBuffers[i]->handle
offset:commandBuffer->fragmentUniformBuffers[i]->drawOffset
@ -2748,38 +2742,38 @@ static void METAL_INTERNAL_BindComputeResources(
NSUInteger offsets[MAX_STORAGE_BUFFERS_PER_STAGE] = { 0 };
if (commandBuffer->needComputeSamplerBind) {
if (computePipeline->numSamplers > 0) {
if (computePipeline->header.numSamplers > 0) {
[commandBuffer->computeEncoder setTextures:commandBuffer->computeSamplerTextures
withRange:NSMakeRange(0, computePipeline->numSamplers)];
withRange:NSMakeRange(0, computePipeline->header.numSamplers)];
[commandBuffer->computeEncoder setSamplerStates:commandBuffer->computeSamplers
withRange:NSMakeRange(0, computePipeline->numSamplers)];
withRange:NSMakeRange(0, computePipeline->header.numSamplers)];
}
commandBuffer->needComputeSamplerBind = false;
}
if (commandBuffer->needComputeReadOnlyStorageTextureBind) {
if (computePipeline->numReadonlyStorageTextures > 0) {
if (computePipeline->header.numReadonlyStorageTextures > 0) {
[commandBuffer->computeEncoder setTextures:commandBuffer->computeReadOnlyTextures
withRange:NSMakeRange(
computePipeline->numSamplers,
computePipeline->numReadonlyStorageTextures)];
computePipeline->header.numSamplers,
computePipeline->header.numReadonlyStorageTextures)];
}
commandBuffer->needComputeReadOnlyStorageTextureBind = false;
}
if (commandBuffer->needComputeReadOnlyStorageBufferBind) {
if (computePipeline->numReadonlyStorageBuffers > 0) {
if (computePipeline->header.numReadonlyStorageBuffers > 0) {
[commandBuffer->computeEncoder setBuffers:commandBuffer->computeReadOnlyBuffers
offsets:offsets
withRange:NSMakeRange(computePipeline->numUniformBuffers,
computePipeline->numReadonlyStorageBuffers)];
withRange:NSMakeRange(computePipeline->header.numUniformBuffers,
computePipeline->header.numReadonlyStorageBuffers)];
}
commandBuffer->needComputeReadOnlyStorageBufferBind = false;
}
for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
if (commandBuffer->needComputeUniformBufferBind[i]) {
if (computePipeline->numUniformBuffers > i) {
if (computePipeline->header.numUniformBuffers > i) {
[commandBuffer->computeEncoder
setBuffer:commandBuffer->computeUniformBuffers[i]->handle
offset:commandBuffer->computeUniformBuffers[i]->drawOffset
@ -3127,7 +3121,7 @@ static void METAL_BindComputePipeline(
metalCommandBuffer->needComputeUniformBufferBind[i] = true;
}
for (Uint32 i = 0; i < pipeline->numUniformBuffers; i += 1) {
for (Uint32 i = 0; i < pipeline->header.numUniformBuffers; i += 1) {
if (metalCommandBuffer->computeUniformBuffers[i] == NULL) {
metalCommandBuffer->computeUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool(
metalCommandBuffer);
@ -3135,22 +3129,22 @@ static void METAL_BindComputePipeline(
}
// Bind write-only resources
if (pipeline->numReadWriteStorageTextures > 0) {
if (pipeline->header.numReadWriteStorageTextures > 0) {
[metalCommandBuffer->computeEncoder setTextures:metalCommandBuffer->computeReadWriteTextures
withRange:NSMakeRange(
pipeline->numSamplers +
pipeline->numReadonlyStorageTextures,
pipeline->numReadWriteStorageTextures)];
pipeline->header.numSamplers +
pipeline->header.numReadonlyStorageTextures,
pipeline->header.numReadWriteStorageTextures)];
}
NSUInteger offsets[MAX_COMPUTE_WRITE_BUFFERS] = { 0 };
if (pipeline->numReadWriteStorageBuffers > 0) {
if (pipeline->header.numReadWriteStorageBuffers > 0) {
[metalCommandBuffer->computeEncoder setBuffers:metalCommandBuffer->computeReadWriteBuffers
offsets:offsets
withRange:NSMakeRange(
pipeline->numUniformBuffers +
pipeline->numReadonlyStorageBuffers,
pipeline->numReadWriteStorageBuffers)];
pipeline->header.numUniformBuffers +
pipeline->header.numReadonlyStorageBuffers,
pipeline->header.numReadWriteStorageBuffers)];
}
}
}
@ -4564,6 +4558,7 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD
SDL_GPUDevice *result = SDL_calloc(1, sizeof(SDL_GPUDevice));
ASSIGN_DRIVER(METAL)
result->driverData = (SDL_GPURenderer *)renderer;
result->shader_formats = SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_METALLIB;
renderer->sdlGPUDevice = result;
return result;

File diff suppressed because it is too large Load Diff

View File

@ -807,6 +807,8 @@ typedef struct LIBUSB_hid_device_ LIBUSB_hid_device;
#define hid_send_feature_report LIBUSB_hid_send_feature_report
#define hid_set_nonblocking LIBUSB_hid_set_nonblocking
#define hid_write LIBUSB_hid_write
#define hid_version LIBUSB_hid_version
#define hid_version_str LIBUSB_hid_version_str
#define input_report LIBUSB_input_report
#define make_path LIBUSB_make_path
#define new_hid_device LIBUSB_new_hid_device

View File

@ -62,6 +62,10 @@
#define wcsstr SDL_wcsstr
#define wcstol SDL_wcstol
// These functions conflict when linking both SDL and hidapi statically
#define hid_winapi_descriptor_reconstruct_pp_data SDL_hid_winapi_descriptor_reconstruct_pp_data
#define hid_winapi_get_container_id SDL_hid_winapi_get_container_id
#undef HIDAPI_H__
#include "windows/hid.c"
#define HAVE_PLATFORM_BACKEND 1

View File

@ -1238,6 +1238,7 @@ static void init_xbox360(libusb_device_handle *device_handle, unsigned short idV
(void)conf_desc;
if ((idVendor == 0x05ac && idProduct == 0x055b) /* Gamesir-G3w */ ||
(idVendor == 0x20d6 && idProduct == 0x4010) /* PowerA Battle Dragon Advanced Wireless Controller */ ||
idVendor == 0x0f0d /* Hori Xbox controllers */) {
unsigned char data[20];

View File

@ -775,13 +775,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
// All other gamepads have the standard set of 19 buttons and 6 axes
SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", sizeof(mapping_string));
if (SDL_IsJoystickXboxSeriesX(vendor, product)) {
// XBox Series X Controllers have a share button under the guide button
SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
} else if (SDL_IsJoystickXboxOneElite(vendor, product)) {
// XBox One Elite Controllers have 4 back paddle buttons
SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,", sizeof(mapping_string));
} else if (SDL_IsJoystickSteamController(vendor, product)) {
if (SDL_IsJoystickSteamController(vendor, product)) {
// Steam controllers have 2 back paddle buttons
SDL_strlcat(mapping_string, "paddle1:b12,paddle2:b11,", sizeof(mapping_string));
} else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) ||
@ -822,6 +816,15 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
SDL_strlcat(mapping_string, "paddle1:b16,paddle2:b15,paddle3:b14,paddle4:b13,", sizeof(mapping_string));
}
break;
case SDL_GAMEPAD_TYPE_XBOXONE:
if (SDL_IsJoystickXboxOneElite(vendor, product)) {
// XBox One Elite Controllers have 4 back paddle buttons
SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,", sizeof(mapping_string));
} else if (SDL_IsJoystickXboxSeriesX(vendor, product)) {
// XBox Series X Controllers have a share button under the guide button
SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
}
break;
default:
if (vendor == 0 && product == 0) {
// This is a Bluetooth Nintendo Switch Pro controller
@ -2751,6 +2754,7 @@ SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id)
gamepad->joystick = SDL_OpenJoystick(instance_id);
if (!gamepad->joystick) {
SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);
SDL_free(gamepad);
SDL_UnlockJoysticks();
return NULL;
@ -2759,6 +2763,7 @@ SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id)
if (gamepad->joystick->naxes) {
gamepad->last_match_axis = (SDL_GamepadBinding **)SDL_calloc(gamepad->joystick->naxes, sizeof(*gamepad->last_match_axis));
if (!gamepad->last_match_axis) {
SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);
SDL_CloseJoystick(gamepad->joystick);
SDL_free(gamepad);
SDL_UnlockJoysticks();
@ -2768,6 +2773,7 @@ SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id)
if (gamepad->joystick->nhats) {
gamepad->last_hat_mask = (Uint8 *)SDL_calloc(gamepad->joystick->nhats, sizeof(*gamepad->last_hat_mask));
if (!gamepad->last_hat_mask) {
SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);
SDL_CloseJoystick(gamepad->joystick);
SDL_free(gamepad->last_match_axis);
SDL_free(gamepad);

View File

@ -215,7 +215,7 @@ static const char *s_GamepadMappings[] = {
"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,",
"03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,",
"030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
"0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,start:b11,x:b3,y:b4,",
"0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,rightx:a3,righty:a4,righttrigger:-a5,start:b11,x:b3,y:b4,",
"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,",
"030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
@ -421,7 +421,7 @@ static const char *s_GamepadMappings[] = {
"030000004b120000014d000000010000,NYKO AIRFLO EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
"050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,start:b11,x:b3,y:b4,",
"0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,rightx:a3,righty:a4,righttrigger:a5,start:b11,x:b3,y:b4,",
"030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
@ -553,6 +553,7 @@ static const char *s_GamepadMappings[] = {
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
"03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
"0500000049190000020400001b010000,GameSir T4 Pro,crc:8283,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b23,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
"03000000373500009710000001020000,GameSir-K1 FLUX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
"03000000ac0500001a06000011010000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
"03000000c01100000140000011010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
@ -867,7 +868,9 @@ static const char *s_GamepadMappings[] = {
"05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,",
"05000000ac05000004000000a8986d04,8BitDo Micro gamepad,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,lefttrigger:b12,rightshoulder:b13,righttrigger:b14,start:b3,x:b6,y:b5,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000ac05000004000000fd216d04,8BitDo Pro 2,crc:ac95,a:b3,b:b2,back:b6,dpdown:b9,dpleft:b10,dpright:b11,dpup:b12,guide:b4,leftshoulder:b13,leftstick:b14,lefttrigger:+a2,leftx:a0,lefty:a1~,paddle1:b1,paddle2:b0,rightshoulder:b16,rightstick:b17,righttrigger:+a5,rightx:a3,righty:a4~,start:b5,x:b8,y:b7,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000ac050000040000003b8a6d04,8BitDo SN30 Pro+,crc:3e00,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000ac05000004000000209f6d04,8Bitdo SN30 Pro,crc:40d6,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -904,7 +907,7 @@ static const char *s_GamepadMappings[] = {
"0000000050535669746120436f6e7400,PSVita Controller,crc:d598,a:b2,b:b1,back:b10,dpdown:b6,dpleft:b7,dpright:b9,dpup:b8,leftshoulder:b4,leftstick:b14,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
#endif
#ifdef SDL_JOYSTICK_N3DS
"000000004e696e74656e646f20334400,Nintendo 3DS,crc:3210,a:b0,b:b1,back:b2,dpdown:b7,dpleft:b5,dpright:b4,dpup:b6,leftshoulder:b9,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b15,rightx:a2,righty:a3,start:b3,x:b10,y:b11,",
"000000004e696e74656e646f20334400,Nintendo 3DS,crc:3210,a:b1,b:b0,back:b2,dpdown:b7,dpleft:b5,dpright:b4,dpup:b6,leftshoulder:b9,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b15,rightx:a2,righty:a3,start:b3,x:b11,y:b10,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
#endif
NULL
};

View File

@ -277,6 +277,7 @@ static Uint32 initial_blacklist_devices[] = {
MAKE_VIDPID(0x1532, 0x0282), // Razer Huntsman Mini Analog, non-functional DInput device
MAKE_VIDPID(0x26ce, 0x01a2), // ASRock LED Controller
MAKE_VIDPID(0x20d6, 0x0002), // PowerA Enhanced Wireless Controller for Nintendo Switch (charging port only)
MAKE_VIDPID(0x3434, 0x0211), // Keychron K1 Pro System Control
};
static SDL_vidpid_list blacklist_devices = {
SDL_HINT_JOYSTICK_BLACKLIST_DEVICES, 0, 0, NULL,
@ -289,7 +290,17 @@ static Uint32 initial_flightstick_devices[] = {
MAKE_VIDPID(0x044f, 0x0402), // HOTAS Warthog Joystick
MAKE_VIDPID(0x044f, 0xb10a), // ThrustMaster, Inc. T.16000M Joystick
MAKE_VIDPID(0x046d, 0xc215), // Logitech Extreme 3D
MAKE_VIDPID(0x0583, 0x6258), // Padix USB joystick with viewfinder
MAKE_VIDPID(0x0583, 0x688f), // Padix QF-688uv Windstorm Pro
MAKE_VIDPID(0x0583, 0x7070), // Padix QF-707u Bazooka
MAKE_VIDPID(0x0583, 0xa019), // Padix USB vibration joystick with viewfinder
MAKE_VIDPID(0x0583, 0xa131), // Padix USB Wireless 2.4GHz
MAKE_VIDPID(0x0583, 0xa209), // Padix MetalStrike ForceFeedback
MAKE_VIDPID(0x0583, 0xb010), // Padix MetalStrike Pro
MAKE_VIDPID(0x0583, 0xb012), // Padix Wireless MetalStrike
MAKE_VIDPID(0x0583, 0xb013), // Padix USB Wireless 2.4GHZ
MAKE_VIDPID(0x0738, 0x2221), // Saitek Pro Flight X-56 Rhino Stick
MAKE_VIDPID(0x10f5, 0x7084), // Turtle Beach VelocityOne
MAKE_VIDPID(0x231d, 0x0126), // Gunfighter Mk.III 'Space Combat Edition' (right)
MAKE_VIDPID(0x231d, 0x0127), // Gunfighter Mk.III 'Space Combat Edition' (left)
MAKE_VIDPID(0x362c, 0x0001), // Yawman Arrow
@ -331,6 +342,7 @@ static SDL_vidpid_list rog_gamepad_mice = {
static Uint32 initial_throttle_devices[] = {
MAKE_VIDPID(0x044f, 0x0404), // HOTAS Warthog Throttle
MAKE_VIDPID(0x0738, 0xa221), // Saitek Pro Flight X-56 Rhino Throttle
MAKE_VIDPID(0x10f5, 0x7085), // Turtle Beach VelocityOne Throttle
};
static SDL_vidpid_list throttle_devices = {
SDL_HINT_JOYSTICK_THROTTLE_DEVICES, 0, 0, NULL,
@ -374,6 +386,14 @@ static Uint32 initial_wheel_devices[] = {
MAKE_VIDPID(0x046d, 0xca03), // Logitech Momo Racing
MAKE_VIDPID(0x0483, 0x0522), // Simagic Wheelbase (including M10, Alpha Mini, Alpha, Alpha U)
MAKE_VIDPID(0x0483, 0xa355), // VRS DirectForce Pro Wheel Base
MAKE_VIDPID(0x0583, 0xa132), // Padix USB Wireless 2.4GHz Wheelpad
MAKE_VIDPID(0x0583, 0xa133), // Padix USB Wireless 2.4GHz Wheel
MAKE_VIDPID(0x0583, 0xa202), // Padix Force Feedback Wheel
MAKE_VIDPID(0x0583, 0xb002), // Padix Vibration USB Wheel
MAKE_VIDPID(0x0583, 0xb005), // Padix USB Wheel
MAKE_VIDPID(0x0583, 0xb008), // Padix USB Wireless 2.4GHz Wheel
MAKE_VIDPID(0x0583, 0xb009), // Padix USB Wheel
MAKE_VIDPID(0x0583, 0xb018), // Padix TW6 Wheel
MAKE_VIDPID(0x0eb7, 0x0001), // Fanatec ClubSport Wheel Base V2
MAKE_VIDPID(0x0eb7, 0x0004), // Fanatec ClubSport Wheel Base V2.5
MAKE_VIDPID(0x0eb7, 0x0005), // Fanatec CSL Elite Wheel Base+ (PS4)
@ -1122,7 +1142,11 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
joystick->attached = true;
joystick->led_expiration = SDL_GetTicks();
joystick->battery_percent = -1;
#ifdef SDL_JOYSTICK_VIRTUAL
joystick->is_virtual = (driver == &SDL_VIRTUAL_JoystickDriver);
#else
joystick->is_virtual = false;
#endif
if (!driver->Open(joystick, device_index)) {
SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
@ -1166,9 +1190,35 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
// If this joystick is known to have all zero centered axes, skip the auto-centering code
if (SDL_JoystickAxesCenteredAtZero(joystick)) {
int i;
for (int i = 0; i < joystick->naxes; ++i) {
joystick->axes[i].has_initial_value = true;
}
}
for (i = 0; i < joystick->naxes; ++i) {
// We know the initial values for HIDAPI and XInput joysticks
if ((SDL_IsJoystickHIDAPI(joystick->guid) ||
SDL_IsJoystickXInput(joystick->guid) ||
SDL_IsJoystickRAWINPUT(joystick->guid) ||
SDL_IsJoystickWGI(joystick->guid)) &&
joystick->naxes >= SDL_GAMEPAD_AXIS_COUNT) {
int left_trigger, right_trigger;
if (SDL_IsJoystickXInput(joystick->guid)) {
left_trigger = 2;
right_trigger = 5;
} else {
left_trigger = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
right_trigger = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
}
for (int i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) {
int initial_value;
if (i == left_trigger || i == right_trigger) {
initial_value = SDL_MIN_SINT16;
} else {
initial_value = 0;
}
joystick->axes[i].value = initial_value;
joystick->axes[i].zero = initial_value;
joystick->axes[i].initial_value = initial_value;
joystick->axes[i].has_initial_value = true;
}
}
@ -1814,6 +1864,14 @@ bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint
result = true;
} else {
result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);
if (result) {
joystick->trigger_rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
if (joystick->trigger_rumble_resend == 0) {
joystick->trigger_rumble_resend = 1;
}
} else {
joystick->trigger_rumble_resend = 0;
}
}
if (result) {
@ -1824,6 +1882,7 @@ bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint
joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
} else {
joystick->trigger_rumble_expiration = 0;
joystick->trigger_rumble_resend = 0;
}
}
}
@ -2478,6 +2537,15 @@ void SDL_UpdateJoysticks(void)
if (joystick->trigger_rumble_expiration && now >= joystick->trigger_rumble_expiration) {
SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
joystick->trigger_rumble_resend = 0;
}
if (joystick->trigger_rumble_resend && now >= joystick->trigger_rumble_resend) {
joystick->driver->RumbleTriggers(joystick, joystick->left_trigger_rumble, joystick->right_trigger_rumble);
joystick->trigger_rumble_resend = now + SDL_RUMBLE_RESEND_MS;
if (joystick->trigger_rumble_resend == 0) {
joystick->trigger_rumble_resend = 1;
}
}
}
@ -2858,7 +2926,8 @@ bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id)
}
if (vendor_id == USB_VENDOR_HORI) {
if (product_id == USB_PRODUCT_HORI_FIGHTING_COMMANDER_OCTA_SERIES_X ||
product_id == USB_PRODUCT_HORI_HORIPAD_PRO_SERIES_X) {
product_id == USB_PRODUCT_HORI_HORIPAD_PRO_SERIES_X ||
product_id == USB_PRODUCT_HORI_TAIKO_DRUM_CONTROLLER) {
return true;
}
}

View File

@ -113,6 +113,7 @@ struct SDL_Joystick
Uint16 left_trigger_rumble _guarded;
Uint16 right_trigger_rumble _guarded;
Uint64 trigger_rumble_expiration _guarded;
Uint64 trigger_rumble_resend _guarded;
Uint8 led_red _guarded;
Uint8 led_green _guarded;

View File

@ -751,11 +751,12 @@ bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoysti
++SDL_HIDAPI_numjoysticks;
SDL_PrivateJoystickAdded(joystickID);
if (pJoystickID) {
*pJoystickID = joystickID;
}
SDL_PrivateJoystickAdded(joystickID);
return true;
}
@ -1051,6 +1052,11 @@ static bool HIDAPI_CreateCombinedJoyCons(void)
info.usage = USB_USAGE_GENERIC_GAMEPAD;
info.manufacturer_string = L"Nintendo";
info.product_string = L"Switch Joy-Con (L/R)";
if (children[0]->is_bluetooth || children[1]->is_bluetooth) {
info.bus_type = SDL_HID_API_BUS_BLUETOOTH;
} else {
info.bus_type = SDL_HID_API_BUS_USB;
}
combined = HIDAPI_AddDevice(&info, 2, children);
if (combined && combined->driver) {

View File

@ -76,6 +76,7 @@
#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5 0x0184
#define USB_PRODUCT_HORI_STEAM_CONTROLLER 0x01AB
#define USB_PRODUCT_HORI_STEAM_CONTROLLER_BT 0x0196
#define USB_PRODUCT_HORI_TAIKO_DRUM_CONTROLLER 0x01b2
#define USB_PRODUCT_LOGITECH_F310 0xc216
#define USB_PRODUCT_LOGITECH_CHILLSTREAM 0xcad1
#define USB_PRODUCT_MADCATZ_SAITEK_SIDE_PANEL_CONTROL_DECK 0x2218

View File

@ -158,6 +158,7 @@ struct joystick_hwdata
Uint8 wgi_correlation_count;
Uint8 wgi_uncorrelate_count;
WindowsGamingInputGamepadState *wgi_slot;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
#endif
bool triggers_rumbling;
@ -449,7 +450,6 @@ typedef struct WindowsGamingInputGamepadState
bool used; // Is currently mapped to an SDL device
bool connected; // Just used during update to track disconnected
Uint8 correlation_id;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
} WindowsGamingInputGamepadState;
static struct
@ -1030,7 +1030,7 @@ static bool RAWINPUT_JoystickInit(void)
{
SDL_assert(!SDL_RAWINPUT_inited);
if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, true)) {
if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, false)) {
return true;
}
@ -1482,12 +1482,11 @@ static bool RAWINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency
#ifdef SDL_JOYSTICK_RAWINPUT_WGI
// Save off the motor state in case trigger rumble is started
WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
HRESULT hr;
gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
if (!rumbled && ctx->wgi_correlated) {
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, ctx->vibration);
if (SUCCEEDED(hr)) {
rumbled = true;
}
@ -1509,12 +1508,11 @@ static bool RAWINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_
#ifdef SDL_JOYSTICK_RAWINPUT_WGI
RAWINPUT_DeviceContext *ctx = joystick->hwdata;
ctx->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;
ctx->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;
if (ctx->wgi_correlated) {
WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
HRESULT hr;
gamepad_state->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;
gamepad_state->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, ctx->vibration);
if (!SUCCEEDED(hr)) {
return SDL_SetError("Setting vibration failed: 0x%lx", hr);
}

View File

@ -304,6 +304,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
if (stderr_option == SDL_PROCESS_STDIO_INHERITED) {
stderr_option = SDL_PROCESS_STDIO_NULL;
}
creation_flags |= CREATE_NO_WINDOW;
}
switch (stdin_option) {
@ -329,7 +330,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
startup_info.hStdInput = stdin_pipe[READ_END];
break;
case SDL_PROCESS_STDIO_NULL:
startup_info.hStdInput = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
startup_info.hStdInput = CreateFile(TEXT("\\\\.\\NUL"), (GENERIC_READ | GENERIC_WRITE), 0, &security_attributes, OPEN_EXISTING, 0, NULL);
break;
case SDL_PROCESS_STDIO_INHERITED:
default:
@ -366,7 +367,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
startup_info.hStdOutput = stdout_pipe[WRITE_END];
break;
case SDL_PROCESS_STDIO_NULL:
startup_info.hStdOutput = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
startup_info.hStdOutput = CreateFile(TEXT("\\\\.\\NUL"), (GENERIC_READ | GENERIC_WRITE), 0, &security_attributes, OPEN_EXISTING, 0, NULL);
break;
case SDL_PROCESS_STDIO_INHERITED:
default:
@ -412,7 +413,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
startup_info.hStdError = stderr_pipe[WRITE_END];
break;
case SDL_PROCESS_STDIO_NULL:
startup_info.hStdError = CreateFile(TEXT("\\\\.\\NUL"), GENERIC_ALL, 0, &security_attributes, OPEN_EXISTING, 0, NULL);
startup_info.hStdError = CreateFile(TEXT("\\\\.\\NUL"), (GENERIC_READ | GENERIC_WRITE), 0, &security_attributes, OPEN_EXISTING, 0, NULL);
break;
case SDL_PROCESS_STDIO_INHERITED:
default:

View File

@ -1658,8 +1658,6 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
}
}
surface_colorspace = SDL_GetSurfaceColorspace(surface);
// Try to have the best pixel format for the texture
// No alpha, but a colorkey => promote to alpha
if (!SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
@ -1721,6 +1719,9 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
}
}
surface_colorspace = SDL_GetSurfaceColorspace(surface);
texture_colorspace = surface_colorspace;
if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
if (SDL_ISPIXELFORMAT_FLOAT(format)) {
@ -2572,7 +2573,7 @@ static void UpdateLogicalPresentation(SDL_Renderer *renderer)
const float logical_h = view->logical_h;
int iwidth, iheight;
if (renderer->target) {
if (!is_main_view && renderer->target) {
iwidth = (int)renderer->target->w;
iheight = (int)renderer->target->h;
} else {
@ -3930,8 +3931,7 @@ bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_F
real_srcrect.w = (float)texture->w;
real_srcrect.h = (float)texture->h;
if (srcrect) {
if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect) ||
real_srcrect.w == 0.0f || real_srcrect.h == 0.0f) {
if (!SDL_GetRectIntersectionFloat(srcrect, &real_srcrect, &real_srcrect)) {
return true;
}
}

View File

@ -445,7 +445,6 @@ static void GPU_InvalidateCachedState(SDL_Renderer *renderer)
{
GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
data->state.render_target = NULL;
data->state.scissor_enabled = false;
}

View File

@ -79,15 +79,10 @@ static const size_t CONSTANTS_OFFSET_DECODE_BT2020_LIMITED = ALIGN_CONSTANTS(16,
static const size_t CONSTANTS_OFFSET_DECODE_BT2020_FULL = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_DECODE_BT2020_LIMITED + sizeof(float) * 4 * 4);
static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_DECODE_BT2020_FULL + sizeof(float) * 4 * 4;
// Sampler types
typedef enum
{
SDL_METAL_SAMPLER_NEAREST_CLAMP,
SDL_METAL_SAMPLER_NEAREST_WRAP,
SDL_METAL_SAMPLER_LINEAR_CLAMP,
SDL_METAL_SAMPLER_LINEAR_WRAP,
SDL_NUM_METAL_SAMPLERS
} SDL_METAL_sampler_type;
#define RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v) \
(((scale_mode == SDL_SCALEMODE_NEAREST) << 0) | \
((address_u == SDL_TEXTURE_ADDRESS_WRAP) << 1) | \
((address_v == SDL_TEXTURE_ADDRESS_WRAP) << 2))
typedef enum SDL_MetalVertexFunction
{
@ -139,7 +134,7 @@ typedef struct METAL_ShaderPipelines
@property(nonatomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
@property(nonatomic, retain) id<MTLLibrary> mtllibrary;
@property(nonatomic, retain) id<CAMetalDrawable> mtlbackbuffer;
@property(nonatomic, retain) NSMutableArray<id<MTLSamplerState>> *mtlsamplers;
@property(nonatomic, retain) NSMutableDictionary<NSNumber *, id<MTLSamplerState>> *mtlsamplers;
@property(nonatomic, retain) id<MTLBuffer> mtlbufconstants;
@property(nonatomic, retain) id<MTLBuffer> mtlbufquadindices;
@property(nonatomic, assign) SDL_MetalView mtlview;
@ -1295,6 +1290,9 @@ typedef struct
__unsafe_unretained id<MTLBuffer> vertex_buffer;
size_t constants_offset;
SDL_Texture *texture;
SDL_ScaleMode texture_scale_mode;
SDL_TextureAddressMode texture_address_mode_u;
SDL_TextureAddressMode texture_address_mode_v;
bool cliprect_dirty;
bool cliprect_enabled;
SDL_Rect cliprect;
@ -1452,6 +1450,58 @@ static bool SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
return true;
}
static id<MTLSamplerState> GetSampler(SDL3METAL_RenderData *data, SDL_ScaleMode scale_mode, SDL_TextureAddressMode address_u, SDL_TextureAddressMode address_v)
{
NSNumber *key = [NSNumber numberWithInteger:RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v)];
id<MTLSamplerState> mtlsampler = data.mtlsamplers[key];
if (mtlsampler == nil) {
MTLSamplerDescriptor *samplerdesc;
samplerdesc = [[MTLSamplerDescriptor alloc] init];
switch (scale_mode) {
case SDL_SCALEMODE_NEAREST:
samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
break;
case SDL_SCALEMODE_LINEAR:
samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
break;
default:
SDL_SetError("Unknown scale mode: %d", scale_mode);
return nil;
}
switch (address_u) {
case SDL_TEXTURE_ADDRESS_CLAMP:
samplerdesc.sAddressMode = MTLSamplerAddressModeClampToEdge;
break;
case SDL_TEXTURE_ADDRESS_WRAP:
samplerdesc.sAddressMode = MTLSamplerAddressModeRepeat;
break;
default:
SDL_SetError("Unknown texture address mode: %d", address_u);
return nil;
}
switch (address_v) {
case SDL_TEXTURE_ADDRESS_CLAMP:
samplerdesc.tAddressMode = MTLSamplerAddressModeClampToEdge;
break;
case SDL_TEXTURE_ADDRESS_WRAP:
samplerdesc.tAddressMode = MTLSamplerAddressModeRepeat;
break;
default:
SDL_SetError("Unknown texture address mode: %d", address_v);
return nil;
}
mtlsampler = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
if (mtlsampler == nil) {
SDL_SetError("Couldn't create sampler");
return nil;
}
data.mtlsamplers[key] = mtlsampler;
}
return mtlsampler;
}
static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const size_t constants_offset,
id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
{
@ -1467,33 +1517,6 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
}
if (texture != statecache->texture) {
id<MTLSamplerState> mtlsampler;
if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_NEAREST) {
switch (cmd->data.draw.texture_address_mode) {
case SDL_TEXTURE_ADDRESS_CLAMP:
mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_NEAREST_CLAMP];
break;
case SDL_TEXTURE_ADDRESS_WRAP:
mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_NEAREST_WRAP];
break;
default:
return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
}
} else {
switch (cmd->data.draw.texture_address_mode) {
case SDL_TEXTURE_ADDRESS_CLAMP:
mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_LINEAR_CLAMP];
break;
case SDL_TEXTURE_ADDRESS_WRAP:
mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_LINEAR_WRAP];
break;
default:
return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
}
}
[data.mtlcmdencoder setFragmentSamplerState:mtlsampler atIndex:0];
[data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
#ifdef SDL_HAVE_YUV
if (texturedata.yuv || texturedata.nv12) {
@ -1503,6 +1526,20 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
#endif
statecache->texture = texture;
}
if (cmd->data.draw.texture_scale_mode != statecache->texture_scale_mode ||
cmd->data.draw.texture_address_mode != statecache->texture_address_mode_u ||
cmd->data.draw.texture_address_mode != statecache->texture_address_mode_v) {
id<MTLSamplerState> mtlsampler = GetSampler(data, cmd->data.draw.texture_scale_mode, cmd->data.draw.texture_address_mode, cmd->data.draw.texture_address_mode);
if (mtlsampler == nil) {
return false;
}
[data.mtlcmdencoder setFragmentSamplerState:mtlsampler atIndex:0];
statecache->texture_scale_mode = cmd->data.draw.texture_scale_mode;
statecache->texture_address_mode_u = cmd->data.draw.texture_address_mode;
statecache->texture_address_mode_v = cmd->data.draw.texture_address_mode;
}
return true;
}
@ -1523,6 +1560,9 @@ static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
statecache.vertex_buffer = nil;
statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
statecache.texture = NULL;
statecache.texture_scale_mode = SDL_SCALEMODE_INVALID;
statecache.texture_address_mode_u = SDL_TEXTURE_ADDRESS_INVALID;
statecache.texture_address_mode_v = SDL_TEXTURE_ADDRESS_INVALID;
statecache.shader_constants_dirty = true;
statecache.cliprect_dirty = true;
statecache.viewport_dirty = true;
@ -1883,7 +1923,6 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
int maxtexsize, quadcount = UINT16_MAX / 4;
UInt16 *indexdata;
size_t indicessize = sizeof(UInt16) * quadcount * 6;
MTLSamplerDescriptor *samplerdesc;
id<MTLCommandQueue> mtlcmdqueue;
id<MTLLibrary> mtllibrary;
id<MTLBuffer> mtlbufconstantstaging, mtlbufquadindicesstaging, mtlbufconstants, mtlbufquadindices;
@ -2043,27 +2082,7 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
data.allpipelines = NULL;
ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm);
static struct
{
MTLSamplerMinMagFilter filter;
MTLSamplerAddressMode address;
} samplerParams[] = {
{ MTLSamplerMinMagFilterNearest, MTLSamplerAddressModeClampToEdge },
{ MTLSamplerMinMagFilterNearest, MTLSamplerAddressModeRepeat },
{ MTLSamplerMinMagFilterLinear, MTLSamplerAddressModeClampToEdge },
{ MTLSamplerMinMagFilterLinear, MTLSamplerAddressModeRepeat },
};
SDL_COMPILE_TIME_ASSERT(samplerParams_SIZE, SDL_arraysize(samplerParams) == SDL_NUM_METAL_SAMPLERS);
data.mtlsamplers = [[NSMutableArray<id<MTLSamplerState>> alloc] init];
samplerdesc = [[MTLSamplerDescriptor alloc] init];
for (int i = 0; i < SDL_arraysize(samplerParams); ++i) {
samplerdesc.minFilter = samplerParams[i].filter;
samplerdesc.magFilter = samplerParams[i].filter;
samplerdesc.sAddressMode = samplerParams[i].address;
samplerdesc.tAddressMode = samplerParams[i].address;
[data.mtlsamplers addObject:[data.mtldevice newSamplerStateWithDescriptor:samplerdesc]];
}
data.mtlsamplers = [[NSMutableDictionary<NSNumber *, id<MTLSamplerState>> alloc] init];
mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];

View File

@ -60,7 +60,7 @@ typedef struct
static int vsync_sema_id = 0;
// PRIVATE METHODS
static int vsync_handler(void)
static int vsync_handler(int reason)
{
iSignalSema(vsync_sema_id);

View File

@ -34,6 +34,9 @@ static TitleStorageBootStrap *titlebootstrap[] = {
static UserStorageBootStrap *userbootstrap[] = {
#ifdef SDL_STORAGE_STEAM
&STEAM_userbootstrap,
#endif
#ifdef SDL_STORAGE_PRIVATE
&PRIVATE_userbootstrap,
#endif
&GENERIC_userbootstrap,
NULL

View File

@ -44,6 +44,7 @@ extern TitleStorageBootStrap GENERIC_titlebootstrap;
// Steam does not have title storage APIs
extern UserStorageBootStrap GENERIC_userbootstrap;
extern UserStorageBootStrap PRIVATE_userbootstrap;
extern UserStorageBootStrap STEAM_userbootstrap;
extern SDL_Storage *GENERIC_OpenFileStorage(const char *path);

View File

@ -58,8 +58,8 @@ typedef enum
static gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags);
static void (*g_object_unref)(gpointer object);
static gchar *(*g_mkdtemp)(gchar *template);
gpointer (*g_object_ref_sink)(gpointer object);
gpointer (*g_object_ref)(gpointer object);
static gpointer (*g_object_ref_sink)(gpointer object);
static gpointer (*g_object_ref)(gpointer object);
// glib_typeof requires compiler-specific code and includes that are too complex
// to be worth copy-pasting here
@ -541,7 +541,7 @@ SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
return NULL;
}
entry->submenu->menu = (GtkMenuShell *)gtk_menu_new();
entry->submenu->menu = g_object_ref_sink(gtk_menu_new());
entry->submenu->parent_tray = NULL;
entry->submenu->parent_entry = entry;
entry->submenu->nEntries = 0;

View File

@ -544,7 +544,7 @@ void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
mii.dwTypeData = label_w;
mii.cch = (UINT) SDL_wcslen(label_w);
if (!SetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, TRUE, &mii)) {
if (!SetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, FALSE, &mii)) {
SDL_SetError("Couldn't update tray entry label");
}

View File

@ -20,12 +20,13 @@
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
#include "SDL_blit.h"
extern SDL_BlitFuncEntry SDL_GeneratedBlitFuncTable[];
/* *INDENT-ON* */ // clang-format on

View File

@ -1101,9 +1101,9 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac
int dst_w, dst_h;
// Make sure the surfaces aren't locked
if (!SDL_SurfaceValid(src)) {
if (!SDL_SurfaceValid(src) || !src->pixels) {
return SDL_InvalidParamError("src");
} else if (!SDL_SurfaceValid(dst)) {
} else if (!SDL_SurfaceValid(dst) || !dst->pixels) {
return SDL_InvalidParamError("dst");
} else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
return SDL_SetError("Surfaces must not be locked during blit");
@ -1133,6 +1133,13 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac
return SDL_BlitSurface(src, srcrect, dst, dstrect);
}
if (src_w == 0) {
src_w = 1;
}
if (src_h == 0) {
src_h = 1;
}
scaling_w = (double)dst_w / src_w;
scaling_h = (double)dst_h / src_h;

View File

@ -396,8 +396,7 @@ struct SDL_VideoDevice
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
void *wakeup_window;
int num_displays;
SDL_VideoDisplay **displays;
SDL_Rect desktop_bounds;

View File

@ -1731,12 +1731,25 @@ SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window)
return SDL_GetVideoDisplay(displayID);
}
#define SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY "sdl2-compat.window.preferred_fullscreen_display"
SDL_DisplayID SDL_GetDisplayForWindow(SDL_Window *window)
{
SDL_DisplayID displayID = 0;
CHECK_WINDOW_MAGIC(window, 0);
/* sdl2-compat calls this function to get a display on which to make the window fullscreen,
* so pass it the preferred fullscreen display ID in a property.
*/
SDL_PropertiesID window_props = SDL_GetWindowProperties(window);
SDL_VideoDisplay *fs_display = SDL_GetVideoDisplayForFullscreenWindow(window);
if (fs_display) {
SDL_SetNumberProperty(window_props, SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY, fs_display->id);
} else {
SDL_ClearProperty(window_props, SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY);
}
// An explicit fullscreen display overrides all
if (window->flags & SDL_WINDOW_FULLSCREEN) {
displayID = window->current_fullscreen_mode.displayID;
@ -2500,7 +2513,9 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
SDL_UpdateWindowHierarchy(window, parent);
if (_this->CreateSDLWindow && !_this->CreateSDLWindow(_this, window, props)) {
PUSH_SDL_ERROR()
SDL_DestroyWindow(window);
POP_SDL_ERROR()
return NULL;
}
@ -2748,7 +2763,7 @@ SDL_Window *SDL_GetWindowFromID(SDL_WindowID id)
}
}
}
SDL_SetError("Invalid window ID"); \
SDL_SetError("Invalid window ID");
return NULL;
}
@ -4266,9 +4281,7 @@ void SDL_DestroyWindow(SDL_Window *window)
_this->current_glwin = NULL;
}
if (_this->wakeup_window == window) {
_this->wakeup_window = NULL;
}
SDL_CompareAndSwapAtomicPointer(&_this->wakeup_window, window, NULL);
// Now invalidate magic
SDL_SetObjectValid(window, SDL_OBJECT_TYPE_WINDOW, false);
@ -5021,8 +5034,7 @@ bool SDL_GL_GetAttribute(SDL_GLAttr attr, int *value)
}
if (fbo_type != GL_NONE) {
glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, attachmentattrib, (GLint *)value);
}
else {
} else {
*value = 0;
}
if (glBindFramebufferFunc && (current_fbo != 0)) {
@ -5253,7 +5265,7 @@ bool SDL_GL_SwapWindow(SDL_Window *window)
bool SDL_GL_DestroyContext(SDL_GLContext context)
{
if (!_this) {
return SDL_UninitializedVideo(); \
return SDL_UninitializedVideo();
}
if (!context) {
return SDL_InvalidParamError("context");

View File

@ -158,6 +158,17 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this)
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
// SetClipboardText specialization so text is available after the app quits
if (_this->clipboard_callback && _this->num_clipboard_mime_types == 1) {
if (SDL_strncmp(_this->clipboard_mime_types[0], "text/plain;charset=utf-8", 24) == 0) {
[pasteboard declareTypes:@[ NSPasteboardTypeString ] owner:nil];
[pasteboard setString:@((char *)_this->clipboard_userdata) forType:NSPasteboardTypeString];
data.clipboard_count = [pasteboard changeCount];
return true;
}
}
NSPasteboardItem *newItem = [NSPasteboardItem new];
NSMutableArray *utiTypes = [NSMutableArray new];
Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata];

View File

@ -450,12 +450,18 @@ void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
// 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
// created under the mouse on macOS 12.7. But, only set the focus if
// the event acutally has a non-NULL window, otherwise what would happen
// is that after an NSEventTypeMouseEntered there would sometimes be
// NSEventTypeMouseMoved without a window causing us to suppress subsequent
// mouse move events.
NSEventType event_type = [event type];
if (event_type == NSEventTypeMouseExited) {
Cocoa_MouseFocus = NULL;
} else {
Cocoa_MouseFocus = [event window];
if ([event window] != NULL) {
Cocoa_MouseFocus = [event window];
}
}
switch (event_type) {

View File

@ -49,9 +49,6 @@ static void Cocoa_VideoQuit(SDL_VideoDevice *_this);
static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
{
@autoreleasepool {
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
CFBridgingRelease(device->internal);
SDL_free(device);
}
@ -81,7 +78,6 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
return NULL;
}
device->internal = (SDL_VideoData *)CFBridgingRetain(data);
device->wakeup_lock = SDL_CreateMutex();
device->system_theme = Cocoa_GetSystemTheme();
// Set the function pointers

View File

@ -2022,6 +2022,7 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
@interface SDL3View : NSView
{
SDL_Window *_sdlWindow;
NSTrackingArea *_trackingArea; // only used on macOS <= 11.0
}
- (void)setSDLWindow:(SDL_Window *)window;
@ -2033,6 +2034,7 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent;
- (BOOL)wantsUpdateLayer;
- (void)updateLayer;
- (void)updateTrackingAreas;
@end
@implementation SDL3View
@ -2104,15 +2106,62 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
if (_sdlWindow->flags & SDL_WINDOW_POPUP_MENU) {
return YES;
} else if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
return SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
} else {
return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", false);
}
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
if (@available(macOS 12.0, *)) {
// we (currently) use the tracking areas as a workaround for older macOSes, but we might be safe everywhere...
} else {
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)_sdlWindow->internal;
if (_trackingArea) {
[self removeTrackingArea:_trackingArea];
}
_trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingMouseEnteredAndExited|NSTrackingActiveAlways owner:windata.listener userInfo:nil];
[self addTrackingArea:_trackingArea];
}
}
@end
static void Cocoa_UpdateMouseFocus()
{
const NSPoint mouseLocation = [NSEvent mouseLocation];
// Find the topmost window under the pointer and send a motion event if it is an SDL window.
[NSApp enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack
usingBlock:^(NSWindow *nswin, BOOL *stop) {
NSRect r = [nswin contentRectForFrameRect:[nswin frame]];
if (NSPointInRect(mouseLocation, r)) {
SDL_VideoDevice *vid = SDL_GetVideoDevice();
SDL_Window *sdlwindow;
for (sdlwindow = vid->windows; sdlwindow; sdlwindow = sdlwindow->next) {
if (nswin == ((__bridge SDL_CocoaWindowData *)sdlwindow->internal).nswindow) {
break;
}
}
*stop = YES;
if (sdlwindow) {
int wx, wy;
SDL_RelativeToGlobalForWindow(sdlwindow, sdlwindow->x, sdlwindow->y, &wx, &wy);
// Calculate the cursor coordinates relative to the window.
const float dx = mouseLocation.x - wx;
const float dy = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - mouseLocation.y) - wy;
SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, dx, dy);
}
}
}];
}
static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow *nswindow, NSView *nsview)
{
@autoreleasepool {
@ -2210,8 +2259,10 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
} else {
if (window->flags & SDL_WINDOW_TOOLTIP) {
[nswindow setIgnoresMouseEvents:YES];
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
[nswindow setAcceptsMouseMovedEvents:NO];
} else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_HIDDEN)) {
Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
Cocoa_UpdateMouseFocus();
}
}
@ -2531,8 +2582,8 @@ void Cocoa_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window)
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->internal;
NSSize maxSize;
maxSize.width = window->max_w;
maxSize.height = window->max_h;
maxSize.width = window->max_w ? window->max_w : CGFLOAT_MAX;
maxSize.height = window->max_h ? window->max_h : CGFLOAT_MAX;
[windata.nswindow setContentMaxSize:maxSize];
}
@ -2596,6 +2647,9 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
[nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]];
}
}
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
Cocoa_UpdateMouseFocus();
}
}
[nswindow setIsVisible:YES];
@ -2643,6 +2697,7 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
Cocoa_SetKeyboardFocus(new_focus, set_focus);
Cocoa_UpdateMouseFocus();
} else if (window->parent && waskey) {
/* Key status is not automatically set on the parent when a child is hidden. Check if the
* child window was key, and set the first visible parent to be key if so.

View File

@ -78,7 +78,7 @@ bool Emscripten_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *wind
if (!Module['SDL3']) Module['SDL3'] = {};
var SDL3 = Module['SDL3'];
if (SDL3.ctxCanvas !== canvas) {
SDL3.ctx = Module['createContext'](canvas, false, true);
SDL3.ctx = Browser.createContext(canvas, false, true);
SDL3.ctxCanvas = canvas;
}
if (SDL3.w !== w || SDL3.h !== h || SDL3.imageCtx !== SDL3.ctx) {

View File

@ -101,7 +101,7 @@ SDL_GLContext Emscripten_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *
context = emscripten_webgl_create_context(window_data->canvas_id, &attribs);
if (context < 0) {
if (!context) {
SDL_SetError("Could not create webgl context");
return NULL;
}

View File

@ -64,7 +64,7 @@ static SDL_SystemTheme Emscripten_GetSystemTheme(void)
/* Technically, light theme can mean explicit light theme or no preference.
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme#syntax */
int theme_code = EM_ASM_INT({
int theme_code = MAIN_THREAD_EM_ASM_INT({
if (!window.matchMedia) {
return -1;
}

View File

@ -1368,9 +1368,14 @@ bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window)
windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev,
dispdata->mode.hdisplay, dispdata->mode.vdisplay,
surface_fmt, surface_flags);
if (!windata->gs && errno == ENOSYS) {
// Try again without the scanout flags, needed on NVIDIA drivers
windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev,
dispdata->mode.hdisplay, dispdata->mode.vdisplay,
surface_fmt, 0);
}
if (!windata->gs) {
return SDL_SetError("Could not create GBM surface");
return SDL_SetError("Could not create GBM surface: %s", strerror(errno));
}
/* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
@ -1729,9 +1734,9 @@ bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
}
/* Create the window surfaces with the size we have just chosen.
Needs the window diverdata in place. */
Needs the window driverdata in place. */
if (!KMSDRM_CreateSurfaces(_this, window)) {
return SDL_SetError("Can't window GBM/EGL surfaces on window creation.");
return false;
}
} // NON-Vulkan block ends.

View File

@ -118,6 +118,8 @@ static SDL_VideoDevice *PSP_Create(void)
device->PumpEvents = PSP_PumpEvents;
device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY;
return device;
}

View File

@ -100,7 +100,7 @@ RECENT REVISION HISTORY:
Bug & warning fixes
Marc LeBlanc David Woo Guillaume George Martins Mozeiko
Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski
Phil Jordan Dave Moore Roy Eltham
Phil Jordan Henner Zeller Dave Moore Roy Eltham
Hayaki Saito Nathan Reed Won Chun
Luke Graham Johan Duparc Nick Verigakis the Horde3D community
Thomas Ruf Ronny Chevalier github:rlyeh
@ -1914,6 +1914,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r
int i,j;
unsigned char *good;
if (data == NULL) return data;
if (req_comp == img_n) return data;
STBI_ASSERT(req_comp >= 1 && req_comp <= 4);

View File

@ -124,31 +124,30 @@ static BOOL UIKit_ShowMessageBoxAlertController(const SDL_MessageBoxData *messag
return YES;
}
static void UIKit_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID, int *result)
typedef struct UIKit_ShowMessageBoxData
{
const SDL_MessageBoxData *messageboxdata;
int *buttonID;
bool result;
} UIKit_ShowMessageBoxData;
static void SDLCALL UIKit_ShowMessageBoxMainThreadCallback(void *userdata)
{
@autoreleasepool {
if (UIKit_ShowMessageBoxAlertController(messageboxdata, buttonID)) {
*result = true;
} else {
*result = SDL_SetError("Could not show message box.");
}
UIKit_ShowMessageBoxData *data = (UIKit_ShowMessageBoxData *) userdata;
data->result = UIKit_ShowMessageBoxAlertController(data->messageboxdata, data->buttonID);
}
}
bool UIKit_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
@autoreleasepool {
__block int result = true;
if ([NSThread isMainThread]) {
UIKit_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
UIKit_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
});
}
return result;
UIKit_ShowMessageBoxData data = { messageboxdata, buttonID, false };
if (!SDL_RunOnMainThread(UIKit_ShowMessageBoxMainThreadCallback, &data, true)) {
return false;
} else if (!data.result) {
return SDL_SetError("Could not show message box.");
}
return true;
}
#endif // SDL_VIDEO_DRIVER_UIKIT

View File

@ -240,7 +240,7 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
int i;
SDL_MouseButtonFlags buttons = SDL_GetMouseState(NULL, NULL);
for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) {
for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
if (buttons & SDL_BUTTON_MASK(i)) {
SDL_SendMouseButton(UIKit_GetEventTimestamp([touch timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, (Uint8)i, false);
}

View File

@ -153,6 +153,8 @@ static SDL_VideoDevice *VITA_Create(void)
device->PumpEvents = VITA_PumpEvents;
device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY;
return device;
}

View File

@ -60,8 +60,6 @@ extern SDL_Window *Vita_Window;
// Display and window functions
extern bool VITA_VideoInit(SDL_VideoDevice *_this);
extern void VITA_VideoQuit(SDL_VideoDevice *_this);
extern bool VITA_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern bool VITA_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern bool VITA_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void VITA_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern bool VITA_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);

View File

@ -163,16 +163,9 @@ static bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface)
static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp)
{
static Uint64 last;
static Uint64 timestamp_offset;
static Uint64 timestamp_offset = 0;
const Uint64 now = SDL_GetTicksNS();
if (nsTimestamp < last) {
// 32-bit timer rollover, bump the offset
timestamp_offset += SDL_MS_TO_NS(0x100000000LLU);
}
last = nsTimestamp;
if (!timestamp_offset) {
timestamp_offset = (now - nsTimestamp);
}
@ -196,19 +189,24 @@ static const struct zwp_input_timestamps_v1_listener timestamp_listener = {
Wayland_input_timestamp_listener
};
static Uint64 Wayland_EventTimestampMSToNS(Uint32 wl_timestamp_ms)
{
static Uint64 timestamp_offset = 0;
static Uint32 last = 0;
// Handle 32-bit timer rollover.
if (wl_timestamp_ms < last) {
timestamp_offset += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000));
}
last = wl_timestamp_ms;
return SDL_MS_TO_NS(wl_timestamp_ms) + timestamp_offset;
}
static Uint64 Wayland_GetKeyboardTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
}
return 0;
}
static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms);
return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
}
return 0;
@ -217,7 +215,7 @@ static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Ui
static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
}
return 0;
@ -226,7 +224,7 @@ static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32
Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
}
return 0;
@ -269,10 +267,11 @@ void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input)
static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed)
{
bool ret = false;
while (elapsed >= repeat_info->next_repeat_ns) {
if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
const Uint64 timestamp = repeat_info->wl_press_time_ns + repeat_info->next_repeat_ns;
SDL_SendKeyboardKeyIgnoreModifiers(Wayland_GetEventTimestamp(timestamp), repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
const Uint64 timestamp = repeat_info->base_time_ns + repeat_info->next_repeat_ns;
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
}
if (repeat_info->text[0]) {
SDL_SendKeyboardText(repeat_info->text);
@ -291,8 +290,8 @@ static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info)
repeat_info->is_key_down = false;
}
static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint64 wl_press_time_ns,
uint32_t scancode, bool has_text, char text[8])
static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint32 wl_press_time_ms,
Uint64 base_time_ns, uint32_t scancode, bool has_text, char text[8])
{
if (!repeat_info->is_initialized || !repeat_info->repeat_rate) {
return;
@ -300,12 +299,13 @@ static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 k
repeat_info->is_key_down = true;
repeat_info->keyboard_id = keyboard_id;
repeat_info->key = key;
repeat_info->wl_press_time_ns = wl_press_time_ns;
repeat_info->wl_press_time_ms = wl_press_time_ms;
repeat_info->base_time_ns = base_time_ns;
repeat_info->sdl_press_time_ns = SDL_GetTicksNS();
repeat_info->next_repeat_ns = SDL_MS_TO_NS(repeat_info->repeat_delay_ms);
repeat_info->scancode = scancode;
if (has_text) {
SDL_copyp(repeat_info->text, text);
SDL_memcpy(repeat_info->text, text, sizeof(repeat_info->text));
} else {
repeat_info->text[0] = '\0';
}
@ -598,11 +598,11 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
wind->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
input->buttons_pressed = 0;
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false);
}
/* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event.
@ -1199,7 +1199,7 @@ static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t ti
const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;
SDL_SendTouchMotion(Wayland_GetPointerTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
SDL_SendTouchMotion(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
(SDL_FingerID)(id + 1), window_data->sdlwindow, x, y, 1.0f);
}
}
@ -1855,7 +1855,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
char text[8];
bool has_text = false;
bool handled_by_ime = false;
const Uint64 timestamp_raw_ns = Wayland_GetKeyboardTimestampRaw(input, time);
const Uint64 timestamp_ns = Wayland_GetKeyboardTimestamp(input, time);
Wayland_UpdateImplicitGrabSerial(input, serial);
@ -1871,7 +1871,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
* Using SDL_GetTicks would be wrong, as it would report when the release event is processed,
* which may be off if the application hasn't pumped events for a while.
*/
keyboard_repeat_handle(&input->keyboard_repeat, timestamp_raw_ns - input->keyboard_repeat.wl_press_time_ns);
const Uint64 elapsed = SDL_MS_TO_NS(time - input->keyboard_repeat.wl_press_time_ms);
keyboard_repeat_handle(&input->keyboard_repeat, elapsed);
keyboard_repeat_clear(&input->keyboard_repeat);
}
keyboard_input_get_text(text, input, key, false, &handled_by_ime);
@ -1879,9 +1880,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key);
Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp_ns, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (has_text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
@ -1890,7 +1890,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
}
}
if (input->xkb.keymap && WAYLAND_xkb_keymap_key_repeats(input->xkb.keymap, key + 8)) {
keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, timestamp_raw_ns, scancode, has_text, text);
keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, time, timestamp_ns, scancode, has_text, text);
}
}
}
@ -2951,7 +2951,7 @@ static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool
return; // Not a pen we report on.
}
const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_MS_TO_NS(time));
const Uint64 timestamp = Wayland_GetEventTimestamp(Wayland_EventTimestampMSToNS(time));
const SDL_PenID instance_id = sdltool->instance_id;
SDL_Window *window = sdltool->tool_focus;

View File

@ -49,17 +49,18 @@ typedef struct SDL_WaylandTabletInput
typedef struct
{
int32_t repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
int32_t repeat_delay_ms; // Time to first repeat event in milliseconds
Uint32 keyboard_id; // ID of the source keyboard.
Sint32 repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
Sint32 repeat_delay_ms; // Time to first repeat event in milliseconds
Uint32 keyboard_id; // ID of the source keyboard.
bool is_initialized;
bool is_key_down;
uint32_t key;
Uint64 wl_press_time_ns; // Key press time as reported by the Wayland API
Uint32 key;
Uint32 wl_press_time_ms; // Key press time as reported by the Wayland API in milliseconds
Uint64 base_time_ns; // Key press time as reported by the Wayland API in nanoseconds
Uint64 sdl_press_time_ns; // Key press time expressed in SDL ticks
Uint64 next_repeat_ns; // Next repeat event in nanoseconds
uint32_t scancode;
Uint32 scancode;
char text[8];
} SDL_WaylandKeyboardRepeat;

View File

@ -58,7 +58,7 @@ bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
if (internal->text_input_manager) {
if (input && input->text_input) {
const SDL_Rect *rect = &input->text_input->cursor_rect;
const SDL_Rect *rect = &input->text_input->text_input_rect;
enum zwp_text_input_v3_content_hint hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE;
enum zwp_text_input_v3_content_purpose purpose;
@ -125,12 +125,21 @@ bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
// Now that it's enabled, set the input properties
zwp_text_input_v3_set_content_type(input->text_input->text_input, hint, purpose);
if (!SDL_RectEmpty(rect)) {
// This gets reset on enable so we have to cache it
SDL_WindowData *wind = window->internal;
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / wind->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / wind->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / wind->pointer_scale.x);
// Clamp the x value so it doesn't run too far past the end of the text input area.
zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input,
rect->x,
rect->y,
rect->w,
rect->h);
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
scaled_rect.y,
1,
scaled_rect.h);
}
zwp_text_input_v3_commit(input->text_input->text_input);
}
@ -174,13 +183,24 @@ bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
if (internal->text_input_manager) {
struct SDL_WaylandInput *input = internal->input;
if (input && input->text_input) {
if (!SDL_RectsEqual(&window->text_input_rect, &input->text_input->cursor_rect)) {
SDL_copyp(&input->text_input->cursor_rect, &window->text_input_rect);
SDL_WindowData *wind = window->internal;
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / wind->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / wind->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / wind->pointer_scale.x);
if (!SDL_RectsEqual(&scaled_rect, &input->text_input->text_input_rect) || scaled_cursor != input->text_input->text_input_cursor) {
SDL_copyp(&input->text_input->text_input_rect, &scaled_rect);
input->text_input->text_input_cursor = scaled_cursor;
// Clamp the x value so it doesn't run too far past the end of the text input area.
zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input,
window->text_input_rect.x,
window->text_input_rect.y,
window->text_input_rect.w,
window->text_input_rect.h);
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
scaled_rect.y,
1,
scaled_rect.h);
zwp_text_input_v3_commit(input->text_input->text_input);
}
}

View File

@ -26,7 +26,8 @@
typedef struct SDL_WaylandTextInput
{
struct zwp_text_input_v3 *text_input;
SDL_Rect cursor_rect;
SDL_Rect text_input_rect;
int text_input_cursor;
bool has_preedit;
} SDL_WaylandTextInput;

View File

@ -446,9 +446,6 @@ static void Wayland_DeleteDevice(SDL_VideoDevice *device)
WAYLAND_wl_display_disconnect(data->display);
SDL_ClearProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER);
}
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(data);
SDL_free(device);
SDL_WAYLAND_UnloadSymbols();
@ -589,7 +586,6 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
}
device->internal = data;
device->wakeup_lock = SDL_CreateMutex();
// Set the function pointers
device->VideoInit = Wayland_VideoInit;

View File

@ -2055,6 +2055,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
struct wl_callback *cb = wl_display_sync(_this->internal->display);
wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
data->showing_window = true;
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
data->showing_window = false;
// Send an exposure event to signal that the client should draw.
if (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
@ -2277,6 +2281,11 @@ SDL_FullscreenResult Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Win
return SDL_FULLSCREEN_FAILED;
}
// Drop fullscreen leave requests when showing the window.
if (wind->showing_window && fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
return SDL_FULLSCREEN_SUCCEEDED;
}
if (wind->show_hide_sync_required) {
WAYLAND_wl_display_roundtrip(_this->internal->display);
}
@ -2331,6 +2340,11 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *wind = window->internal;
// Drop restore requests when showing the window.
if (wind->showing_window) {
return;
}
// Not currently fullscreen or maximized, and no state pending; nothing to do.
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) &&
!wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) {

View File

@ -198,6 +198,7 @@ struct SDL_WindowData
bool is_fullscreen;
bool fullscreen_exclusive;
bool drop_fullscreen_requests;
bool showing_window;
bool fullscreen_was_positioned;
bool show_hide_sync_required;
bool scale_to_display;

View File

@ -352,7 +352,7 @@ static void WIN_UpdateMouseCapture(void)
}
}
static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus)
static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, DWORD pos)
{
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
@ -389,7 +389,8 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus)
// In relative mode we are guaranteed to have mouse focus if we have keyboard focus
if (!SDL_GetMouse()->relative_mode) {
GetCursorPos(&cursorPos);
cursorPos.x = (LONG)GET_X_LPARAM(pos);
cursorPos.y = (LONG)GET_Y_LPARAM(pos);
ScreenToClient(hwnd, &cursorPos);
SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)cursorPos.x, (float)cursorPos.y);
}
@ -472,11 +473,6 @@ static SDL_MOUSE_EVENT_SOURCE GetMouseMessageSource(ULONG extrainfo)
return SDL_MOUSE_EVENT_SOURCE_PEN;
}
}
/* Sometimes WM_INPUT events won't have the correct touch signature,
so we have to rely purely on the touch bit being set. */
if (SDL_TouchDevicesAvailable() && extrainfo & 0x80) {
return SDL_MOUSE_EVENT_SOURCE_TOUCH;
}
return SDL_MOUSE_EVENT_SOURCE_MOUSE;
}
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
@ -614,7 +610,8 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
return;
}
if (GetMouseMessageSource(rawmouse->ulExtraInformation) != SDL_MOUSE_EVENT_SOURCE_MOUSE) {
if (GetMouseMessageSource(rawmouse->ulExtraInformation) != SDL_MOUSE_EVENT_SOURCE_MOUSE ||
(SDL_TouchDevicesAvailable() && (rawmouse->ulExtraInformation & 0x80) == 0x80)) {
return;
}
@ -1235,22 +1232,19 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_NCACTIVATE:
{
// Don't immediately clip the cursor in case we're clicking minimize/maximize buttons
// This is the only place that this flag is set. This causes all subsequent calls to
// WIN_UpdateClipCursor for this window to be no-ops in this frame's message-pumping.
// This flag is unset at the end of message pumping each frame for every window, and
// should never be carried over between frames.
data->skip_update_clipcursor = true;
data->postpone_clipcursor = true;
data->clipcursor_queued = true;
/* Update the focus here, since it's possible to get WM_ACTIVATE and WM_SETFOCUS without
actually being the foreground window, but this appears to get called in all cases where
the global foreground window changes to and from this window. */
WIN_UpdateFocus(data->window, !!wParam);
WIN_UpdateFocus(data->window, !!wParam, GetMessagePos());
} break;
case WM_ACTIVATE:
{
// Update the focus in case we changed focus to a child window and then away from the application
WIN_UpdateFocus(data->window, !!LOWORD(wParam));
WIN_UpdateFocus(data->window, !!LOWORD(wParam), GetMessagePos());
} break;
case WM_MOUSEACTIVATE:
@ -1273,14 +1267,14 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_SETFOCUS:
{
// Update the focus in case it's changing between top-level windows in the same application
WIN_UpdateFocus(data->window, true);
WIN_UpdateFocus(data->window, true, GetMessagePos());
} break;
case WM_KILLFOCUS:
case WM_ENTERIDLE:
{
// Update the focus in case it's changing between top-level windows in the same application
WIN_UpdateFocus(data->window, false);
WIN_UpdateFocus(data->window, false, GetMessagePos());
} break;
case WM_POINTERENTER:
@ -1361,8 +1355,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
const Uint64 timestamp = WIN_GetEventTimestamp();
SDL_Window *window = data->window;
const bool istouching = IS_POINTER_INCONTACT_WPARAM(wParam) && IS_POINTER_FIRSTBUTTON_WPARAM(wParam);
// if lifting off, do it first, so any motion changes don't cause app issues.
if (msg == WM_POINTERUP) {
if (!istouching) {
SDL_SendPenTouch(timestamp, pen, window, (pen_info.penFlags & PEN_FLAG_INVERTED) != 0, false);
}
@ -1392,7 +1388,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
// if setting down, do it last, so the pen is positioned correctly from the first contact.
if (msg == WM_POINTERDOWN) {
if (istouching) {
SDL_SendPenTouch(timestamp, pen, window, (pen_info.penFlags & PEN_FLAG_INVERTED) != 0, true);
}
@ -1408,9 +1404,8 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
window->flags & (SDL_WINDOW_MOUSE_RELATIVE_MODE | SDL_WINDOW_MOUSE_GRABBED) ||
(window->mouse_rect.w > 0 && window->mouse_rect.h > 0)
);
if (wish_clip_cursor) {
data->skip_update_clipcursor = false;
WIN_UpdateClipCursor(window);
if (wish_clip_cursor) { // queue clipcursor refresh on pump finish
data->clipcursor_queued = true;
}
}
@ -1435,6 +1430,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam));
}
}
} break;
case WM_LBUTTONUP:
@ -1498,8 +1494,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !IsIconic(hwnd)) {
SDL_Mouse *mouse;
DWORD pos = GetMessagePos();
POINT cursorPos;
GetCursorPos(&cursorPos);
cursorPos.x = GET_X_LPARAM(pos);
cursorPos.y = GET_Y_LPARAM(pos);
ScreenToClient(hwnd, &cursorPos);
mouse = SDL_GetMouse();
if (!mouse->was_touch_mouse_events) { // we're not a touch handler causing a mouse leave?
@ -1638,7 +1636,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
// Reference: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
if (SendMessage(hwnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION) {
POINT cursorPos;
GetCursorPos(&cursorPos);
GetCursorPos(&cursorPos); // want the most current pos so as to not cause position change
ScreenToClient(hwnd, &cursorPos);
PostMessage(hwnd, WM_MOUSEMOVE, 0, cursorPos.x | (((Uint32)((Sint16)cursorPos.y)) << 16));
}
@ -1852,6 +1850,9 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
data->initial_size_rect.bottom = data->window->y + data->window->h;
SetTimer(hwnd, (UINT_PTR)SDL_IterateMainCallbacks, USER_TIMER_MINIMUM, NULL);
// Reset the keyboard, as we won't get any key up events during the modal loop
SDL_ResetKeyboard();
}
} break;
@ -2590,13 +2591,27 @@ void WIN_PumpEvents(SDL_VideoDevice *_this)
}
}
// Update the clipping rect in case someone else has stolen it
// fire queued clipcursor refreshes
if (_this) {
SDL_Window *window = _this->windows;
while (window) {
bool refresh_clipcursor = false;
SDL_WindowData *data = window->internal;
if (data && data->skip_update_clipcursor) {
data->skip_update_clipcursor = false;
if (data) {
refresh_clipcursor = data->clipcursor_queued;
data->clipcursor_queued = false; // Must be cleared unconditionally.
data->postpone_clipcursor = false; // Must be cleared unconditionally.
// Must happen before UpdateClipCursor.
// Although its occurrence currently
// always coincides with the queuing of
// clipcursor, it is logically distinct
// and this coincidence might no longer
// be true in the future.
// Ergo this placement concordantly
// conveys its unconditionality
// vis-a-vis the queuing of clipcursor.
}
if (refresh_clipcursor) {
WIN_UpdateClipCursor(window);
}
window = window->next;

View File

@ -120,9 +120,6 @@ static void WIN_DeleteDevice(SDL_VideoDevice *device)
SDL_UnloadObject(data->dxgiDLL);
}
#endif
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(device->internal->rawinput);
SDL_free(device->internal);
SDL_free(device);
@ -148,7 +145,6 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
return NULL;
}
device->internal = data;
device->wakeup_lock = SDL_CreateMutex();
device->system_theme = WIN_GetSystemTheme();
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)

View File

@ -348,6 +348,10 @@ bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRec
// Update any child windows
for (child_window = window->first_child; child_window; child_window = child_window->next_sibling) {
if (!child_window->internal) {
// This child window is not yet fully initialized.
continue;
}
if (!WIN_SetWindowPositionInternal(child_window, flags, SDL_WINDOWRECT_CURRENT)) {
result = false;
}
@ -1028,8 +1032,9 @@ void WIN_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *
void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
DWORD style;
HWND hwnd;
bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true);
@ -1038,17 +1043,30 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
WIN_SetWindowPosition(_this, window);
}
hwnd = window->internal->hwnd;
// If the window isn't borderless and will be fullscreen, use the borderless style to hide the initial borders.
if (window->pending_flags & SDL_WINDOW_FULLSCREEN) {
if (!(window->flags & SDL_WINDOW_BORDERLESS)) {
window->flags |= SDL_WINDOW_BORDERLESS;
style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~STYLE_MASK;
style |= GetWindowStyle(window);
SetWindowLong(hwnd, GWL_STYLE, style);
window->flags &= ~SDL_WINDOW_BORDERLESS;
}
}
style = GetWindowLong(hwnd, GWL_EXSTYLE);
if (style & WS_EX_NOACTIVATE) {
bActivate = false;
}
data->showing_window = true;
if (bActivate) {
ShowWindow(hwnd, SW_SHOW);
} else {
// Use SetWindowPos instead of ShowWindow to avoid activating the parent window if this is a child window
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, window->internal->copybits_flag | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
data->showing_window = false;
if (window->flags & SDL_WINDOW_POPUP_MENU && bActivate) {
WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
@ -1207,10 +1225,12 @@ void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
HWND hwnd = data->hwnd;
data->expected_resize = true;
ShowWindow(hwnd, SW_RESTORE);
data->expected_resize = false;
if (!data->showing_window || window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) {
HWND hwnd = data->hwnd;
data->expected_resize = true;
ShowWindow(hwnd, SW_RESTORE);
data->expected_resize = false;
}
} else {
data->windowed_mode_was_maximized = false;
}
@ -1218,18 +1238,22 @@ void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
static void WIN_UpdateCornerRoundingForHWND(SDL_VideoDevice *_this, HWND hwnd, DWM_WINDOW_CORNER_PREFERENCE cornerPref)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *videodata = _this->internal;
if (videodata->DwmSetWindowAttribute) {
videodata->DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
}
#endif
}
static void WIN_UpdateBorderColorForHWND(SDL_VideoDevice *_this, HWND hwnd, COLORREF colorRef)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *videodata = _this->internal;
if (videodata->DwmSetWindowAttribute) {
videodata->DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &colorRef, sizeof(colorRef));
}
#endif
}
/**
@ -1591,7 +1615,7 @@ void WIN_UnclipCursorForWindow(SDL_Window *window) {
void WIN_UpdateClipCursor(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
if (data->in_title_click || data->focus_click_pending || data->skip_update_clipcursor) {
if (data->in_title_click || data->focus_click_pending || data->postpone_clipcursor) {
return;
}

View File

@ -78,11 +78,13 @@ struct SDL_WindowData
bool in_border_change;
bool in_title_click;
Uint8 focus_click_pending;
bool skip_update_clipcursor;
bool postpone_clipcursor;
bool clipcursor_queued;
bool windowed_mode_was_maximized;
bool in_window_deactivation;
bool force_ws_maximizebox;
bool disable_move_size_events;
bool showing_window;
int in_modal_loop;
RECT initial_size_rect;
RECT cursor_clipped_rect; // last successfully committed clipping rect for this window

View File

@ -71,6 +71,9 @@
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
#include <X11/extensions/shape.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XTEST
#include <X11/extensions/XTest.h>
#endif
#ifdef __cplusplus
extern "C" {

View File

@ -506,17 +506,32 @@ static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data)
static void X11_DispatchMapNotify(SDL_WindowData *data)
{
SDL_Window *window = data->window;
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
data->was_shown = true;
// This may be sent when restoring a minimized window.
if (window->flags & SDL_WINDOW_MINIMIZED) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
}
if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
SDL_UpdateWindowGrab(window);
}
}
static void X11_DispatchUnmapNotify(SDL_WindowData *data)
{
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
SDL_Window *window = data->window;
// This may be sent when minimizing a window.
if (!window->is_hiding) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
} else {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
}
}
static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point)
@ -1096,6 +1111,41 @@ void X11_GetBorderValues(SDL_WindowData *data)
}
}
void X11_EmitConfigureNotifyEvents(SDL_WindowData *data, XConfigureEvent *xevent)
{
if (xevent->x != data->last_xconfigure.x ||
xevent->y != data->last_xconfigure.y) {
if (!data->size_move_event_flags) {
SDL_Window *w;
int x = xevent->x;
int y = xevent->y;
data->pending_operation &= ~X11_PENDING_OP_MOVE;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
for (w = data->window->first_child; w; w = w->next_sibling) {
// Don't update hidden child popup windows, their relative position doesn't change
if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w, true);
}
}
}
}
if (xevent->width != data->last_xconfigure.width ||
xevent->height != data->last_xconfigure.height) {
if (!data->size_move_event_flags) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->width,
xevent->height);
}
}
SDL_copyp(&data->last_xconfigure, xevent);
}
static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
{
SDL_VideoData *videodata = _this->internal;
@ -1291,8 +1341,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
}
// We ungrab in LeaveNotify, so we may need to grab again here
SDL_UpdateWindowGrab(data->window);
// We ungrab in LeaveNotify, so we may need to grab again here, but not if captured, as the capture can be lost.
if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
SDL_UpdateWindowGrab(data->window);
}
X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true);
} break;
@ -1320,7 +1372,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
xevent->xcrossing.detail != NotifyInferior) {
/* In order for interaction with the window decorations and menu to work properly
on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
on Mutter, we need to ungrab the keyboard when the mouse leaves. */
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
X11_SetWindowKeyboardGrab(_this, data->window, false);
}
@ -1447,9 +1499,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
xevent->xconfigure.x, xevent->xconfigure.y,
xevent->xconfigure.width, xevent->xconfigure.height);
#endif
// Real configure notify events are relative to the parent, synthetic events are absolute.
if (!xevent->xconfigure.send_event)
{
// Real configure notify events are relative to the parent, synthetic events are absolute.
if (!xevent->xconfigure.send_event) {
unsigned int NumChildren;
Window ChildReturn, Root, Parent;
Window *Children;
@ -1462,41 +1513,23 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
&ChildReturn);
}
if (xevent->xconfigure.x != data->last_xconfigure.x ||
xevent->xconfigure.y != data->last_xconfigure.y) {
if (!data->size_move_event_flags) {
SDL_Window *w;
int x = xevent->xconfigure.x;
int y = xevent->xconfigure.y;
/* Xfce sends ConfigureNotify before PropertyNotify when toggling fullscreen and maximized, which
* is backwards from every other window manager, as well as what is expected by SDL and its clients.
* Defer emitting the size/move events until the corresponding PropertyNotify arrives.
*/
const Uint32 changed = X11_GetNetWMState(_this, data->window, xevent->xproperty.window) ^ data->window->flags;
if (changed & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) {
SDL_copyp(&data->pending_xconfigure, &xevent->xconfigure);
data->emit_size_move_after_property_notify = true;
}
data->pending_operation &= ~X11_PENDING_OP_MOVE;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
for (w = data->window->first_child; w; w = w->next_sibling) {
// Don't update hidden child popup windows, their relative position doesn't change
if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w, true);
}
}
}
if (!data->emit_size_move_after_property_notify) {
X11_EmitConfigureNotifyEvents(data, &xevent->xconfigure);
}
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
X11_HandleConfigure(data->window, &xevent->xconfigure);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
if (xevent->xconfigure.width != data->last_xconfigure.width ||
xevent->xconfigure.height != data->last_xconfigure.height) {
if (!data->size_move_event_flags) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->xconfigure.width,
xevent->xconfigure.height);
}
}
data->last_xconfigure = xevent->xconfigure;
} break;
// Have we been requested to quit (or another client message?)
@ -1782,19 +1815,15 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) {
/* Get the new state from the window manager.
Compositing window managers can alter visibility of windows
without ever mapping / unmapping them, so we handle that here,
because they use the NETWM protocol to notify us of changes.
* Compositing window managers can alter visibility of windows
* without ever mapping / unmapping them, so we handle that here,
* because they use the NETWM protocol to notify us of changes.
*/
const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window);
const SDL_WindowFlags changed = flags ^ data->window->flags;
if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) {
if (flags & SDL_WINDOW_HIDDEN) {
X11_DispatchUnmapNotify(data);
} else {
X11_DispatchMapNotify(data);
}
if ((changed & SDL_WINDOW_HIDDEN) && !(flags & SDL_WINDOW_HIDDEN)) {
X11_DispatchMapNotify(data);
}
if (!SDL_WINDOW_IS_POPUP(data->window)) {
@ -1805,6 +1834,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (!(flags & SDL_WINDOW_MINIMIZED)) {
const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
// Ensure the maximized flag is cleared before entering fullscreen.
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
if (commit) {
/* This was initiated by the compositor, or the mode was changed between the request and the window
@ -1896,6 +1927,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
}
}
}
if (data->emit_size_move_after_property_notify) {
X11_EmitConfigureNotifyEvents(data, &data->pending_xconfigure);
data->emit_size_move_after_property_notify = false;
}
if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
if (data->pending_move) {
DispatchWindowMove(_this, data, &data->pending_move_point);

View File

@ -762,7 +762,9 @@ void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
break;
}
(void)SDL_snprintf(deeplink, sizeof(deeplink),
"steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d",
"steam://open/keyboard?XPosition=%i&YPosition=%i&Width=%i&Height=%i&Mode=%d",
window->text_input_rect.x, window->text_input_rect.y,
window->text_input_rect.w, window->text_input_rect.h,
mode);
SDL_OpenURL(deeplink);
videodata->steam_keyboard_open = true;

View File

@ -425,6 +425,13 @@ static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
Display *display = data->display;
SDL_WindowData *windowdata = NULL;
const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
#ifdef XRANDR_DISABLED_BY_DEFAULT
const bool use_xrandr_by_default = false;
#else
const bool use_xrandr_by_default = true;
#endif
#endif
if (messageboxdata->window) {
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window);
@ -497,7 +504,17 @@ static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
const SDL_DisplayData *dpydata = dpy->internal;
x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2);
y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3);
} else { // oh well. This will misposition on a multi-head setup. Init first next time.
}
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
else if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default)) {
XRRScreenResources *screen = X11_XRRGetScreenResourcesCurrent(display, DefaultRootWindow(display));
XRRCrtcInfo *crtc_info = X11_XRRGetCrtcInfo(display, screen, screen->crtcs[0]);
x = (crtc_info->width - data->dialog_width) / 2;
y = (crtc_info->height - data->dialog_height) / 3;
}
#endif
else {
// oh well. This will misposition on a multi-head setup. Init first next time.
x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2;
y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3;
}

View File

@ -283,14 +283,16 @@ static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo
X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid)
{
SDL_VideoData *data = _this->internal;
int num_device_info = 0;
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
if (device_info) {
SDL_assert(num_device_info == 1);
X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info);
X11_XIFreeDeviceInfo(device_info);
return handle;
if (X11_Xinput2IsInitialized()) {
SDL_VideoData *data = _this->internal;
int num_device_info = 0;
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
if (device_info) {
SDL_assert(num_device_info == 1);
X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info);
X11_XIFreeDeviceInfo(device_info);
return handle;
}
}
return NULL;
}
@ -306,6 +308,10 @@ void X11_RemovePenByDeviceID(int deviceid)
void X11_InitPen(SDL_VideoDevice *_this)
{
if (!X11_Xinput2IsInitialized()) {
return; // we need XIQueryDevice() for this.
}
SDL_VideoData *data = _this->internal;
#define LOOKUP_PEN_ATOM(X) X11_XInternAtom(data->display, X, False)

View File

@ -63,9 +63,6 @@ static void X11_DeleteDevice(SDL_VideoDevice *device)
X11_XCloseDisplay(data->request_display);
}
SDL_free(data->windowlist);
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(device->internal);
SDL_free(device);
@ -78,23 +75,6 @@ static bool X11_IsXWayland(Display *d)
return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True;
}
static bool X11_CheckCurrentDesktop(const char *name)
{
SDL_Environment *env = SDL_GetEnvironment();
const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION");
if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) {
return true;
}
desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP");
if (desktopVar && SDL_strcasestr(desktopVar, name)) {
return true;
}
return false;
}
static SDL_VideoDevice *X11_CreateDevice(void)
{
SDL_VideoDevice *device;
@ -146,8 +126,6 @@ static SDL_VideoDevice *X11_CreateDevice(void)
return NULL;
}
device->wakeup_lock = SDL_CreateMutex();
#ifdef X11_DEBUG
X11_XSynchronize(data->display, True);
#endif
@ -275,17 +253,11 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT;
/* Openbox doesn't send the new window dimensions when entering fullscreen, so the events must be synthesized.
* This is otherwise not wanted, as it can break fullscreen window positioning on multi-monitor configurations.
*/
if (!X11_CheckCurrentDesktop("openbox")) {
device->device_caps |= VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES;
}
data->is_xwayland = X11_IsXWayland(x11_display);
if (data->is_xwayland) {
device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED |
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS;
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
}
return device;

View File

@ -534,7 +534,7 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties
}
const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false);
const bool use_resize_sync = !(window->flags & SDL_WINDOW_VULKAN); /* doesn't work well with Vulkan */
const bool use_resize_sync = !!(window->flags & SDL_WINDOW_OPENGL); // Doesn't work well with Vulkan
SDL_WindowData *windowdata;
Display *display = data->display;
int screen = displaydata->screen;
@ -934,6 +934,75 @@ static int X11_CatchAnyError(Display *d, XErrorEvent *e)
return 0;
}
static void X11_ExternalResizeMoveSync(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
int (*prev_handler)(Display *, XErrorEvent *);
unsigned int childCount;
Window childReturn, root, parent;
Window *children;
XWindowAttributes attrs;
Uint64 timeout = 0;
int x, y;
const bool send_move = !!(data->pending_operation & X11_PENDING_OP_MOVE);
const bool send_resize = !!(data->pending_operation & X11_PENDING_OP_RESIZE);
X11_XSync(display, False);
X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
prev_handler = X11_XSetErrorHandler(X11_CatchAnyError);
/* Wait a brief time to see if the window manager decided to let the move or resize happen.
* If the window changes at all, even to an unexpected value, we break out.
*/
timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(100);
while (true) {
caught_x11_error = false;
X11_XSync(display, False);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &x, &y, &childReturn);
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
if (!caught_x11_error) {
if ((data->pending_operation & X11_PENDING_OP_MOVE) && (x == data->expected.x + data->border_left && y == data->expected.y + data->border_top)) {
data->pending_operation &= ~X11_PENDING_OP_MOVE;
}
if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (attrs.width == data->expected.w && attrs.height == data->expected.h)) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
}
if (data->pending_operation == X11_PENDING_OP_NONE) {
break;
}
}
if (SDL_GetTicksNS() >= timeout) {
// Timed out without the expected values. Update the requested data so future sync calls won't block.
data->pending_operation &= ~(X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE);
data->expected.x = x;
data->expected.y = y;
data->expected.w = attrs.width;
data->expected.h = attrs.height;
break;
}
SDL_Delay(10);
}
if (!caught_x11_error) {
if (send_move) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
}
if (send_resize) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height);
}
}
X11_XSetErrorHandler(prev_handler);
caught_x11_error = false;
}
/* Wait a brief time, or not, to see if the window manager decided to move/resize the window.
* Send MOVED and RESIZED window events */
static bool X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, Uint64 param_timeout)
@ -1214,9 +1283,10 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
sizehints->min_width = sizehints->max_width = window->pending.w;
sizehints->min_height = sizehints->max_height = window->pending.h;
data->expected.w = sizehints->min_width = sizehints->max_width = window->pending.w;
data->expected.h = sizehints->min_height = sizehints->max_height = window->pending.h;
sizehints->flags |= PMinSize | PMaxSize;
data->pending_operation |= X11_PENDING_OP_RESIZE;
X11_XSetWMNormalHints(display, data->xwindow, sizehints);
@ -1504,6 +1574,12 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
X11_PumpEvents(_this);
data->size_move_event_flags = 0;
/* A MapNotify or PropertyNotify may not have arrived, so ensure that the shown event is dispatched
* to apply pending state before clearing the flag.
*/
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
data->was_shown = true;
// If a configure event was received (type is non-zero), send the final window size and coordinates.
if (data->last_xconfigure.type) {
int x, y;
@ -1695,6 +1771,11 @@ void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
// Don't restore the window the first time it is being shown.
if (!window->internal->was_shown) {
return;
}
if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) {
SDL_SyncWindow(window);
}
@ -1729,6 +1810,10 @@ static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this,
Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
Atom _NET_WM_STATE_FULLSCREEN = data->videodata->atoms._NET_WM_STATE_FULLSCREEN;
if (!data->was_shown && fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
return SDL_FULLSCREEN_SUCCEEDED;
}
if (X11_IsWindowMapped(_this, window)) {
XEvent e;
@ -2223,6 +2308,15 @@ void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
// If the window is external and has only a pending resize or move event, use the special external sync path to avoid processing events.
if ((window->flags & SDL_WINDOW_EXTERNAL) &&
(data->pending_operation & ~(X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE)) == X11_PENDING_OP_NONE) {
X11_ExternalResizeMoveSync(window);
return true;
}
const Uint64 current_time = SDL_GetTicksNS();
Uint64 timeout = 0;

View File

@ -68,6 +68,7 @@ struct SDL_WindowData
bool pending_move;
SDL_Point pending_move_point;
XConfigureEvent last_xconfigure;
XConfigureEvent pending_xconfigure;
struct SDL_VideoData *videodata;
unsigned long user_time;
Atom xdnd_req;
@ -115,6 +116,8 @@ struct SDL_WindowData
bool previous_borders_nonzero;
bool toggle_borders;
bool fullscreen_borders_forced_on;
bool was_shown;
bool emit_size_move_after_property_notify;
SDL_HitTestResult hit_test_result;
XPoint xim_spot;

11
lib/sdl3/build.zig vendored
View File

@ -3,6 +3,7 @@ const std = @import("std");
pub fn addShaderDefinition(
b: *std.Build,
comptime sdl3Path: []const u8,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
shaderName: []const u8,
jsonPath: std.Build.LazyPath,
@ -16,7 +17,7 @@ pub fn addShaderDefinition(
const module = b.createModule(.{ .root_source_file = reflectedZigOut, .optimize = optimize });
const dep = b.dependency("shaderTypes", .{
.target = b.graph.host,
.target = target,
.optimize = optimize,
});
module.addImport("shaderTypes", dep.module("shaderTypes"));
@ -28,11 +29,12 @@ pub fn shaderDefintion(
b: *std.Build,
module: *std.Build.Module,
comptime sdl3Path: []const u8,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
shaderName: []const u8,
jsonPath: std.Build.LazyPath,
) void {
const mod = addShaderDefinition(b, sdl3Path, optimize, shaderName, jsonPath);
const mod = addShaderDefinition(b, sdl3Path, target, optimize, shaderName, jsonPath);
module.addImport(shaderName, mod);
}
@ -67,7 +69,7 @@ pub fn build(b: *std.Build) void {
mod.addImport("shaderTypes", shaderTypes.module("shaderTypes"));
mod.addIncludePath(b.path("SDL/include"));
// mod.linkLibrary(sdl3_lib);
mod.linkLibrary(sdl3_lib);
const test_step = b.step("test", "run unit tests for sdl3");
const tests = b.addExecutable(.{
@ -80,11 +82,12 @@ pub fn build(b: *std.Build) void {
tests.root_module.addImport("sdl3", mod);
const vertexDefinitions = addShaderDefinition(b, ".", optimize, "hello-triangle.vert", b.path("content/hello-triangle.vert.json"));
const vertexDefinitions = addShaderDefinition(b, ".", target, optimize, "hello-triangle.vert", b.path("content/hello-triangle.vert.json"));
tests.root_module.addImport("hello-triangle.vert", vertexDefinitions);
const runArtifact = b.addRunArtifact(tests);
test_step.dependOn(&runArtifact.step);
b.installArtifact(sdl3_lib);
b.installArtifact(tests);
}

View File

@ -3,14 +3,6 @@
using namespace metal;
struct type_TransformBuffer
{
float4x4 modelMatrix;
float4x4 viewMatrix;
float4x4 projectionMatrix;
float time;
};
struct Scene
{
float4 color;
@ -21,19 +13,8 @@ struct type_StructuredBuffer_Scene
Scene _m0[1];
};
struct Scene2
{
float4 color;
float4 lmfao;
};
struct type_StructuredBuffer_Scene2
{
Scene2 _m0[1];
};
constant float2 _40 = {};
constant float4 _41 = {};
constant float2 _30 = {};
constant float4 _31 = {};
struct main0_out
{
@ -41,45 +22,45 @@ struct main0_out
float4 gl_Position [[position]];
};
vertex main0_out main0(constant type_TransformBuffer& TransformBuffer [[buffer(0)]], const device type_StructuredBuffer_Scene& test [[buffer(1)]], const device type_StructuredBuffer_Scene2& test2 [[buffer(2)]], uint gl_VertexIndex [[vertex_id]])
vertex main0_out main0(const device type_StructuredBuffer_Scene& test [[buffer(0)]], uint gl_VertexIndex [[vertex_id]])
{
main0_out out = {};
float4 _68;
float2 _69;
float4 _58;
float2 _59;
if (gl_VertexIndex == 0u)
{
_68 = test._m0[0u].color;
_69 = float2(-1.0);
_58 = test._m0[0u].color;
_59 = float2(-1.0);
}
else
{
float4 _66;
float2 _67;
float4 _56;
float2 _57;
if (gl_VertexIndex == 1u)
{
_66 = test2._m0[1u].color;
_67 = float2(1.0, -1.0);
_56 = test._m0[1u].color;
_57 = float2(1.0, -1.0);
}
else
{
bool _58 = gl_VertexIndex == 2u;
float4 _63;
if (_58)
bool _48 = gl_VertexIndex == 2u;
float4 _53;
if (_48)
{
_63 = test._m0[2u].color;
_53 = test._m0[2u].color;
}
else
{
_63 = _41;
_53 = _31;
}
_66 = _63;
_67 = select(_40, float2(0.0, 1.0), bool2(_58));
_56 = _53;
_57 = select(_30, float2(0.0, 1.0), bool2(_48));
}
_68 = _66;
_69 = _67;
_58 = _56;
_59 = _57;
}
out.out_var_TEXCOORD0 = _68;
out.gl_Position = TransformBuffer.modelMatrix * float4(_69, 0.0, 1.0);
out.out_var_TEXCOORD0 = _58;
out.gl_Position = float4(_59, 0.0, 1.0);
return out;
}

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