Merge branch 'main' into patch-1

This commit is contained in:
ROllerozxa 2025-05-18 20:28:43 +02:00 committed by GitHub
commit 9eceae33ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 2214 additions and 921 deletions

View File

@ -113,7 +113,8 @@ JOB_SPECS = {
"msvc-gdk-x64": JobSpec(name="GDK (MSVC, x64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-GDK", msvc_arch=MsvcArch.X64, msvc_project="VisualC-GDK/SDL.sln", gdk=True, no_cmake=True, ),
"ubuntu-22.04": JobSpec(name="Ubuntu 22.04", os=JobOs.Ubuntu22_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04", ),
"ubuntu-24.04-arm64": JobSpec(name="Ubuntu 24.04 (ARM64)", os=JobOs.Ubuntu24_04_arm, platform=SdlPlatform.Linux, artifact="SDL-ubuntu24.04-arm64", ),
"steamrt-sniper": JobSpec(name="Steam Linux Runtime (Sniper)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Linux, artifact="SDL-slrsniper", container="registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta", ),
"steamrt3": JobSpec(name="Steam Linux Runtime 3.0 (x86_64)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Linux, artifact="SDL-steamrt3", container="registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest", ),
"steamrt3-arm64": JobSpec(name="Steam Linux Runtime 3.0 (arm64)", os=JobOs.Ubuntu24_04_arm, platform=SdlPlatform.Linux, artifact="SDL-steamrt3-arm64", container="registry.gitlab.steamos.cloud/steamrt/sniper/sdk/arm64:3.0.20250408.124536", ),
"ubuntu-intel-icx": JobSpec(name="Ubuntu 22.04 (Intel oneAPI)", os=JobOs.Ubuntu22_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04-oneapi", intel=IntelCompiler.Icx, ),
"ubuntu-intel-icc": JobSpec(name="Ubuntu 22.04 (Intel Compiler)", os=JobOs.Ubuntu22_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04-icc", intel=IntelCompiler.Icc, ),
"macos-framework-x64": JobSpec(name="MacOS (Framework) (x64)", os=JobOs.Macos13, platform=SdlPlatform.MacOS, artifact="SDL-macos-framework", apple_framework=True, apple_archs={AppleArch.Aarch64, AppleArch.X86_64, }, xcode=True, ),

View File

@ -1736,6 +1736,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
sdl_sources(
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_dbus.c"
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_system_theme.c"
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_progressbar.c"
)
endif()

View File

@ -43,7 +43,6 @@ my $wikiurl = 'https://wiki.libsdl.org';
my $bugreporturl = 'https://github.com/libsdl-org/sdlwiki/issues/new';
my $srcpath = undef;
my $wikipath = undef;
my $wikireadmesubdir = 'README';
my $warn_about_missing = 0;
my $copy_direction = 0;
my $optionsfname = undef;
@ -427,6 +426,7 @@ sub dewikify_chunk {
# make sure these can't become part of roff syntax.
$str =~ s/\./\\[char46]/gms;
$str =~ s/"/\\(dq/gms;
$str =~ s/'/\\(aq/gms;
if ($wikitype eq 'mediawiki') {
# Dump obvious wikilinks.
@ -1033,7 +1033,6 @@ sub generate_quickref {
my $incpath = "$srcpath";
$incpath .= "/$incsubdir" if $incsubdir ne '';
my $wikireadmepath = "$wikipath/$wikireadmesubdir";
my $readmepath = undef;
if (defined $readmesubdir) {
$readmepath = "$srcpath/$readmesubdir";
@ -2082,18 +2081,15 @@ if ($copy_direction == 1) { # --copy-to-headers
}
if (defined $readmepath) {
if ( -d $wikireadmepath ) {
mkdir($readmepath); # just in case
opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
while (readdir(DH)) {
my $dent = $_;
if ($dent =~ /\A(.*?)\.md\Z/) { # we only bridge Markdown files here.
next if $1 eq 'FrontPage';
filecopy("$wikireadmepath/$dent", "$readmepath/README-$dent", "\n");
}
mkdir($readmepath); # just in case
opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
while (readdir(DH)) {
my $dent = $_;
if ($dent =~ /\AREADME\-.*?\.md\Z/) { # we only bridge Markdown files here that start with "README-".
filecopy("$wikipath/$dent", "$readmepath/$dent", "\n");
}
closedir(DH);
}
closedir(DH);
}
} elsif ($copy_direction == -1) { # --copy-to-wiki
@ -2698,31 +2694,27 @@ __EOF__
# Write out READMEs...
if (defined $readmepath) {
if ( -d $readmepath ) {
mkdir($wikireadmepath); # just in case
mkdir($wikipath); # just in case
opendir(DH, $readmepath) or die("Can't opendir '$readmepath': $!\n");
while (my $d = readdir(DH)) {
my $dent = $d;
if ($dent =~ /\AREADME\-(.*?\.md)\Z/) { # we only bridge Markdown files here.
my $wikifname = $1;
next if $wikifname eq 'FrontPage.md';
filecopy("$readmepath/$dent", "$wikireadmepath/$wikifname", "\n");
if ($dent =~ /\AREADME\-.*?\.md\Z/) { # we only bridge Markdown files here that start with "README-".
filecopy("$readmepath/$dent", "$wikipath/$dent", "\n");
}
}
closedir(DH);
my @pages = ();
opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
while (my $d = readdir(DH)) {
my $dent = $d;
if ($dent =~ /\A(.*?)\.(mediawiki|md)\Z/) {
my $wikiname = $1;
next if $wikiname eq 'FrontPage';
push @pages, $wikiname;
if ($dent =~ /\A(README\-.*?)\.md\Z/) {
push @pages, $1;
}
}
closedir(DH);
open(FH, '>', "$wikireadmepath/FrontPage.md") or die("Can't open '$wikireadmepath/FrontPage.md': $!\n");
open(FH, '>', "$wikipath/READMEs.md") or die("Can't open '$wikipath/READMEs.md': $!\n");
print FH "# All READMEs available here\n\n";
foreach (sort @pages) {
my $wikiname = $_;

View File

@ -157,7 +157,7 @@ flags to the compiler.
- Use [`CMAKE_EXE_LINKER_FLAGS`](https://cmake.org/cmake/help/latest/variable/CMAKE_EXE_LINKER_FLAGS.html) to pass extra option to the linker for executables.
- Use [`CMAKE_SHARED_LINKER_FLAGS`](https://cmake.org/cmake/help/latest/variable/CMAKE_SHARED_LINKER_FLAGS.html) to pass extra options to the linker for shared libraries.
#### Examples
#### Compile Options Examples
- build a SDL library optimized for (more) modern x64 microprocessor architectures.
@ -240,7 +240,7 @@ Append with a version number to target a specific SDK revision: e.g. `iphoneos12
CMake documentation: [link](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html)
#### Examples
#### Apple Examples
- for macOS, building a dylib and/or static library for x86_64 and arm64:

View File

@ -327,6 +327,16 @@ If you add Doxygen with a `##` (`###`, etc) section header, it'll
migrate to the wiki and be _removed_ from the headers. Generally
the correct thing to do is _never use section headers in the Doxygen_.
## wikiheaders will reorder standard sections.
The standard sections are always kept in a consistent order by
wikiheaders, both in the headers and the wiki. If they're placed in
a non-standard order, wikiheaders will reorder them.
For sections that aren't standard, wikiheaders will place them at
the end of the wiki page, in the order they were seen when it loaded
the page for processing.
## It's okay to repeat yourself.
Each individual piece of documentation becomes a separate page on the wiki, so
@ -340,7 +350,7 @@ through, header users can search for the function name.
You might be reading this document on the wiki! Any `README-*.md` files in
the docs directory are bridged to the wiki, so `docs/README-linux.md` lands
at https://wiki.libsdl.org/SDL3/README/linux ...these are just copied directly
at https://wiki.libsdl.org/SDL3/README-linux ...these are just copied directly
without any further processing by wikiheaders, and changes go in both
directions.

View File

@ -103,7 +103,7 @@ getting started.
Another option is to use SDL' main callbacks, which handle this for you
without platform-specific code in your app. Please refer to
[the wiki](https://wiki.libsdl.org/SDL3/README/main-functions#main-callbacks-in-sdl3)
[the wiki](https://wiki.libsdl.org/SDL3/README-main-functions#main-callbacks-in-sdl3)
or `docs/README-main-functions.md` in the SDL source code.
@ -230,7 +230,7 @@ tools.
mkdir build
cd build
emcmake cmake ..
# you can also do `emcmake cmake -G Ninja ..` and then use `ninja` instead of this command.
# you can also try `emcmake cmake -G Ninja ..` and then use `ninja` instead of this command.
emmake make -j4
```

View File

@ -13,7 +13,7 @@ SDL3 has been known to work on the following platforms at some point:
- [macOS](README-macos.md) (10.14 and later)
- [NetBSD](README-bsd.md)
- [Nintendo Switch](README-switch.md) (Separate NDA-only fork)
- [Nintendo 3DS](README-3ds.md) (Homebrew)
- [Nintendo 3DS](README-n3ds.md) (Homebrew)
- [OpenBSD](README-bsd.md)
- [PlayStation 2](README-ps2.md) (Homebrew)
- [PlayStation 4](README-ps4.md) (Separate NDA-only fork)

View File

@ -59,6 +59,15 @@ encounter limitations or behavior that is different from other windowing systems
`SDL_APP_ID` hint string, the desktop entry file name should match the application ID. For example, if your
application ID is set to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`.
### The application progress bar can't be set via ```SDL_SetWindowProgressState()``` or ```SDL_SetWindowProgressValue()```
- Only some Desktop Environemnts support the underlying API. Known compatible DEs: Unity, KDE
- The underlying API requires a desktop entry file, aka a `.desktop` file.
Please see the [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/) for
more information on the format of this file. Note that if your application manually sets the application ID via the
`SDL_APP_ID` hint string, the desktop entry file name should match the application ID. For example, if your
application ID is set to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`.
### Keyboard grabs don't work when running under XWayland
- On GNOME based desktops, the dconf setting `org/gnome/mutter/wayland/xwayland-allow-grabs` must be enabled.

View File

@ -781,7 +781,7 @@ typedef struct SDL_TouchFingerEvent
} SDL_TouchFingerEvent;
/**
* Pressure-sensitive pen proximity event structure (event.pmotion.*)
* Pressure-sensitive pen proximity event structure (event.pproximity.*)
*
* When a pen becomes visible to the system (it is close enough to a tablet,
* etc), SDL will send an SDL_EVENT_PEN_PROXIMITY_IN event with the new pen's

View File

@ -206,8 +206,10 @@
* underlying graphics API. While it's possible that we have done something
* inefficiently, it's very unlikely especially if you are relatively
* inexperienced with GPU rendering. Please see the performance tips above and
* make sure you are following them. Additionally, tools like RenderDoc can be
* very helpful for diagnosing incorrect behavior and performance issues.
* make sure you are following them. Additionally, tools like
* [RenderDoc](https://renderdoc.org/)
* can be very helpful for diagnosing incorrect behavior and performance
* issues.
*
* ## System Requirements
*
@ -333,6 +335,39 @@
* unreferenced data in a bound resource without cycling, but overwriting a
* section of data that has already been referenced will produce unexpected
* results.
*
* ## Debugging
*
* At some point of your GPU journey, you will probably encounter issues that
* are not traceable with regular debugger - for example, your code compiles
* but you get an empty screen, or your shader fails in runtime.
*
* For debugging such cases, there are tools that allow visually inspecting
* the whole GPU frame, every drawcall, every bound resource, memory buffers,
* etc. They are the following, per platform:
*
* * For Windows/Linux, use
* [RenderDoc](https://renderdoc.org/)
* * For MacOS (Metal), use Xcode built-in debugger (Open XCode, go to Debug >
* Debug Executable..., select your application, set "GPU Frame Capture" to
* "Metal" in scheme "Options" window, run your app, and click the small
* Metal icon on the bottom to capture a frame)
*
* Aside from that, you may want to enable additional debug layers to receive
* more detailed error messages, based on your GPU backend:
*
* * For D3D12, the debug layer is an optional feature that can be installed
* via "Windows Settings -> System -> Optional features" and adding the
* "Graphics Tools" optional feature.
* * For Vulkan, you will need to install Vulkan SDK on Windows, and on Linux,
* you usually have some sort of `vulkan-validation-layers` system package
* that should be installed.
* * For Metal, it should be enough just to run the application from XCode to
* receive detailed errors or warnings in the output.
*
* Don't hesitate to use tools as RenderDoc when encountering runtime issues
* or unexpected output on screen, quick GPU frame inspection can usually help
* you fix the majority of such problems.
*/
#ifndef SDL_gpu_h_
@ -1656,6 +1691,9 @@ typedef struct SDL_GPUStencilOpState
* \since This struct is available since SDL 3.2.0.
*
* \sa SDL_GPUColorTargetDescription
* \sa SDL_GPUBlendFactor
* \sa SDL_GPUBlendOp
* \sa SDL_GPUColorComponentFlags
*/
typedef struct SDL_GPUColorTargetBlendState
{
@ -2216,6 +2254,25 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDevice(
* - `SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING`: the prefix to
* use for all vertex semantics, default is "TEXCOORD".
*
* With the Vulkan renderer:
*
* - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN`: Enable
* device feature shaderClipDistance. If disabled, clip distances are not
* supported in shader code: gl_ClipDistance[] built-ins of GLSL,
* SV_ClipDistance0/1 semantics of HLSL and [[clip_distance]] attribute of
* Metal. Defaults to true.
* - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN`: Enable device
* feature depthClamp. If disabled, there is no depth clamp support and
* enable_depth_clip in SDL_GPURasterizerState must always be set to true.
* Defaults to true.
* - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN`: Enable
* device feature drawIndirectFirstInstance. If disabled, the argument
* first_instance of SDL_GPUIndirectDrawCommand must be set to zero.
* Defaults to true.
* - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN`: Enable
* device feature samplerAnisotropy. If disabled, enable_anisotropy of
* SDL_GPUSamplerCreateInfo must be set to false. Defaults to true.
*
* \param props the properties to use.
* \returns a GPU context on success or NULL on failure; call SDL_GetError()
* for more information.
@ -2230,17 +2287,21 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDevice(
extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDeviceWithProperties(
SDL_PropertiesID props);
#define SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN "SDL.gpu.device.create.debugmode"
#define SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN "SDL.gpu.device.create.preferlowpower"
#define SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN "SDL.gpu.device.create.verbose"
#define SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING "SDL.gpu.device.create.name"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN "SDL.gpu.device.create.shaders.private"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN "SDL.gpu.device.create.shaders.spirv"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN "SDL.gpu.device.create.shaders.dxbc"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN "SDL.gpu.device.create.shaders.dxil"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN "SDL.gpu.device.create.shaders.msl"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN "SDL.gpu.device.create.shaders.metallib"
#define SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING "SDL.gpu.device.create.d3d12.semantic"
#define SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN "SDL.gpu.device.create.debugmode"
#define SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN "SDL.gpu.device.create.preferlowpower"
#define SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN "SDL.gpu.device.create.verbose"
#define SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING "SDL.gpu.device.create.name"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN "SDL.gpu.device.create.shaders.private"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN "SDL.gpu.device.create.shaders.spirv"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN "SDL.gpu.device.create.shaders.dxbc"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN "SDL.gpu.device.create.shaders.dxil"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN "SDL.gpu.device.create.shaders.msl"
#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN "SDL.gpu.device.create.shaders.metallib"
#define SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING "SDL.gpu.device.create.d3d12.semantic"
#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN "SDL.gpu.device.create.vulkan.shaderclipdistance"
#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN "SDL.gpu.device.create.vulkan.depthclamp"
#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN "SDL.gpu.device.create.vulkan.drawindirectfirstinstance"
#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN "SDL.gpu.device.create.vulkan.sampleranisotropy"
/**
* Destroys a GPU context previously returned by SDL_CreateGPUDevice.
@ -2986,6 +3047,9 @@ extern SDL_DECLSPEC SDL_GPUCommandBuffer * SDLCALL SDL_AcquireGPUCommandBuffer(
* terms this means you must ensure that vec3 and vec4 fields are 16-byte
* aligned.
*
* For detailed information about accessing uniform data from a shader, please
* refer to SDL_CreateGPUShader.
*
* \param command_buffer a command buffer.
* \param slot_index the vertex uniform slot to push data to.
* \param data client data to write.
@ -4013,7 +4077,9 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
* buffer used to acquire it.
*
* This function will fill the swapchain texture handle with NULL if too many
* frames are in flight. This is not an error.
* frames are in flight. This is not an error. This NULL pointer should not be
* passed back into SDL. Instead, it should be considered as an indication to
* wait until the swapchain is available.
*
* If you use this function, it is possible to create a situation where many
* command buffers are allocated while the rendering context waits for the GPU

View File

@ -101,7 +101,7 @@ typedef Uint32 SDL_InitFlags;
* to run.
*
* See
* [Main callbacks in SDL3](https://wiki.libsdl.org/SDL3/README/main-functions#main-callbacks-in-sdl3)
* [Main callbacks in SDL3](https://wiki.libsdl.org/SDL3/README-main-functions#main-callbacks-in-sdl3)
* for complete details.
*
* \since This enum is available since SDL 3.2.0.

View File

@ -47,7 +47,7 @@
*
* For more information, see:
*
* https://wiki.libsdl.org/SDL3/README/main-functions
* https://wiki.libsdl.org/SDL3/README-main-functions
*/
#ifndef SDL_main_h_
@ -68,7 +68,7 @@
* proper entry point for the platform, and all the other magic details
* needed, like manually calling SDL_SetMainReady.
*
* Please see [README/main-functions](README/main-functions), (or
* Please see [README-main-functions](README-main-functions), (or
* docs/README-main-functions.md in the source tree) for a more detailed
* explanation.
*
@ -85,7 +85,7 @@
* SDL_AppQuit. The app should not provide a `main` function in this case, and
* doing so will likely cause the build to fail.
*
* Please see [README/main-functions](README/main-functions), (or
* Please see [README-main-functions](README-main-functions), (or
* docs/README-main-functions.md in the source tree) for a more detailed
* explanation.
*
@ -512,7 +512,7 @@ typedef int (SDLCALL *SDL_main_func)(int argc, char *argv[]);
* SDL_MAIN_USE_CALLBACKS.
*
* Program startup is a surprisingly complex topic. Please see
* [README/main-functions](README/main-functions), (or
* [README-main-functions](README-main-functions), (or
* docs/README-main-functions.md in the source tree) for a more detailed
* explanation.
*

View File

@ -187,7 +187,7 @@ typedef Uint32 SDL_MouseButtonFlags;
* with proper synchronization practices when adding other side
* effects beyond mutation of the x and y values.
*
* \since This datatype is available since SDL 3.2.6.
* \since This datatype is available since SDL 3.4.0.
*
* \sa SDL_SetRelativeMouseTransform
*/

View File

@ -195,6 +195,12 @@ typedef enum SDL_ProcessIO
* run in the background. In this case the default input and output is
* `SDL_PROCESS_STDIO_NULL` and the exitcode of the process is not
* available, and will always be 0.
* - `SDL_PROP_PROCESS_CREATE_CMDLINE_STRING`: a string containing the program
* to run and any parameters. This string is passed directly to
* `CreateProcess` on Windows, and does nothing on other platforms. This
* property is only important if you want to start programs that does
* non-standard command-line processing, and in most cases using
* `SDL_PROP_PROCESS_CREATE_ARGS_POINTER` is sufficient.
*
* On POSIX platforms, wait() and waitpid(-1, ...) should not be called, and
* SIGCHLD should not be ignored or handled because those would prevent SDL
@ -231,6 +237,7 @@ extern SDL_DECLSPEC SDL_Process * SDLCALL SDL_CreateProcessWithProperties(SDL_Pr
#define SDL_PROP_PROCESS_CREATE_STDERR_POINTER "SDL.process.create.stderr_source"
#define SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN "SDL.process.create.stderr_to_stdout"
#define SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN "SDL.process.create.background"
#define SDL_PROP_PROCESS_CREATE_CMDLINE_STRING "SDL.process.create.cmdline"
/**
* Get the properties associated with a process.

View File

@ -247,14 +247,14 @@ typedef void (SDLCALL *SDL_iOSAnimationCallback)(void *userdata);
*
* For more information see:
*
* https://wiki.libsdl.org/SDL3/README/ios
* https://wiki.libsdl.org/SDL3/README-ios
*
* Note that if you use the "main callbacks" instead of a standard C `main`
* function, you don't have to use this API, as SDL will manage this for you.
*
* Details on main callbacks are here:
*
* https://wiki.libsdl.org/SDL3/README/main-functions
* https://wiki.libsdl.org/SDL3/README-main-functions
*
* \param window the window for which the animation callback should be set.
* \param interval the number of frames after which **callback** will be

View File

@ -1303,7 +1303,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren
* - `SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN` - true if
* the application wants to use the Wayland surface for a custom role and
* does not want it attached to an XDG toplevel window. See
* [README/wayland](README/wayland) for more information on using custom
* [README-wayland](README-wayland) for more information on using custom
* surfaces.
* - `SDL_PROP_WINDOW_CREATE_WAYLAND_CREATE_EGL_WINDOW_BOOLEAN` - true if the
* application wants an associated `wl_egl_window` object to be created and
@ -1311,7 +1311,7 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren
* property or `SDL_WINDOW_OPENGL` flag set.
* - `SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER` - the wl_surface
* associated with the window, if you want to wrap an existing window. See
* [README/wayland](README/wayland) for more information.
* [README-wayland](README-wayland) for more information.
*
* These are additional supported properties on Windows:
*

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.
}
@ -1485,6 +1491,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.
}
}
@ -1523,8 +1530,10 @@ 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 = NULL;
const void *vdev = NULL;
if (!SDL_GetCurrentAudioDriver()) {
SDL_SetError("Audio subsystem is not initialized");
@ -1534,10 +1543,16 @@ const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
// 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, (const void **) &device);
if (!device) {
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);

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);

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

@ -269,19 +269,9 @@ static const char *getAppName(void)
return SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING);
}
static void ThreadedMainloopSignal(void)
{
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // alert waiting threads to unblock.
// we need to kill any SDL_SetError state; we didn't create this thread
// so its SDL TLS slot will leak otherwise, so we do this every time
// we're (presumably) losing control of the thread.
SDL_CleanupTLS();
}
static void OperationStateChangeCallback(pa_operation *o, void *userdata)
{
ThreadedMainloopSignal(); // just signal any waiting code, it can look up the details.
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details.
}
/* This function assume you are holding `mainloop`'s lock. The operation is unref'd in here, assuming
@ -323,7 +313,7 @@ static void DisconnectFromPulseServer(void)
static void PulseContextStateChangeCallback(pa_context *context, void *userdata)
{
ThreadedMainloopSignal(); // just signal any waiting code, it can look up the details.
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details.
}
static bool ConnectToPulseServer(void)
@ -410,7 +400,7 @@ static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata)
struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata;
//SDL_Log("PULSEAUDIO WRITE CALLBACK! nbytes=%u", (unsigned int) nbytes);
h->bytes_requested += nbytes;
ThreadedMainloopSignal();
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
}
// This function waits until it is possible to write a full sound buffer
@ -481,7 +471,7 @@ static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata)
{
//SDL_Log("PULSEAUDIO READ CALLBACK! nbytes=%u", (unsigned int) nbytes);
ThreadedMainloopSignal(); // the recording code queries what it needs, we just need to signal to end any wait
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // the recording code queries what it needs, we just need to signal to end any wait
}
static bool PULSEAUDIO_WaitRecordingDevice(SDL_AudioDevice *device)
@ -602,7 +592,7 @@ static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *device)
static void PulseStreamStateChangeCallback(pa_stream *stream, void *userdata)
{
ThreadedMainloopSignal(); // just signal any waiting code, it can look up the details.
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details.
}
static bool PULSEAUDIO_OpenDevice(SDL_AudioDevice *device)
@ -803,7 +793,7 @@ static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last,
if (i) {
AddPulseAudioDevice(false, i->description, i->name, i->index, &i->sample_spec);
}
ThreadedMainloopSignal();
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
}
// This is called when PulseAudio adds a recording ("source") device.
@ -813,7 +803,7 @@ static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_la
if (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) {
AddPulseAudioDevice(true, i->description, i->name, i->index, &i->sample_spec);
}
ThreadedMainloopSignal();
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
}
static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data)
@ -838,7 +828,7 @@ static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *dat
}
}
ThreadedMainloopSignal();
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
}
static bool FindAudioDeviceByIndex(SDL_AudioDevice *device, void *userdata)
@ -882,7 +872,7 @@ static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint3
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByIndex, (void *)(uintptr_t)idx));
}
}
ThreadedMainloopSignal();
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
}
static bool CheckDefaultDevice(const bool changed, char *device_path)

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

@ -741,7 +741,7 @@ static bool MEDIAFOUNDATION_OpenDevice(SDL_Camera *device, const SDL_CameraSpec
SDL_Log("CAMERA: opening device with symlink of '%s'", utf8symlink);
#endif
wstrsymlink = WIN_UTF8ToString(utf8symlink);
wstrsymlink = WIN_UTF8ToStringW(utf8symlink);
if (!wstrsymlink) {
goto failed;
}
@ -901,7 +901,7 @@ static char *QueryActivationObjectString(IMFActivate *activation, const GUID *pg
return NULL;
}
char *utf8str = WIN_StringToUTF8(wstr);
char *utf8str = WIN_StringToUTF8W(wstr);
CoTaskMemFree(wstr);
return utf8str;
}

View File

@ -68,6 +68,7 @@ static bool LoadDBUSSyms(void)
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_signal);
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *), message_has_path);
SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *, const char *), message_new_method_call);
SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *), message_new_signal);
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, ...), message_append_args);
SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, va_list), message_append_args_valist);
SDL_DBUS_SYM(void (*)(DBusMessage *, DBusMessageIter *), message_iter_init_append);

View File

@ -67,6 +67,7 @@ typedef struct SDL_DBusContext
dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *);
dbus_bool_t (*message_has_path)(DBusMessage *, const char *);
DBusMessage *(*message_new_method_call)(const char *, const char *, const char *, const char *);
DBusMessage *(*message_new_signal)(const char *, const char *, const char *);
dbus_bool_t (*message_append_args)(DBusMessage *, int, ...);
dbus_bool_t (*message_append_args_valist)(DBusMessage *, int, va_list);
void (*message_iter_init_append)(DBusMessage *, DBusMessageIter *);

View File

@ -0,0 +1,159 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_progressbar.h"
#include "SDL_internal.h"
#include "SDL_dbus.h"
#ifdef SDL_USE_LIBDBUS
#include <unistd.h>
#include "../unix/SDL_appid.h"
#define UnityLauncherAPI_DBUS_INTERFACE "com.canonical.Unity.LauncherEntry"
#define UnityLauncherAPI_DBUS_SIGNAL "Update"
static char *GetDBUSObjectPath()
{
char *app_id = SDL_strdup(SDL_GetAppID());
if (!app_id) {
return NULL;
}
// Sanitize exe_name to make it a legal D-Bus path element
for (char *p = app_id; *p; ++p) {
if (!SDL_isalnum(*p)) {
*p = '_';
}
}
// Ensure it starts with a letter or underscore
if (!SDL_isalpha(app_id[0]) && app_id[0] != '_') {
SDL_memmove(app_id + 1, app_id, SDL_strlen(app_id) + 1);
app_id[0] = '_';
}
// Create full path
char path[1024];
SDL_snprintf(path, sizeof(path), "/org/libsdl/%s_%d", app_id, getpid());
SDL_free(app_id);
return SDL_strdup(path);
}
static char *GetAppDesktopPath()
{
const char *desktop_suffix = ".desktop";
const char *app_id = SDL_GetAppID();
const size_t desktop_path_total_length = SDL_strlen(app_id) + SDL_strlen(desktop_suffix) + 1;
char *desktop_path = (char *)SDL_malloc(desktop_path_total_length);
if (!desktop_path) {
return NULL;
}
*desktop_path = '\0';
SDL_strlcat(desktop_path, app_id, desktop_path_total_length);
SDL_strlcat(desktop_path, desktop_suffix, desktop_path_total_length);
return desktop_path;
}
static int ShouldShowProgress(SDL_ProgressState progressState)
{
if (progressState == SDL_PROGRESS_STATE_INVALID ||
progressState == SDL_PROGRESS_STATE_NONE) {
return 0;
}
// Unity LauncherAPI only supports "normal" display of progress
return 1;
}
bool DBUS_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window)
{
// Signal signature:
// signal com.canonical.Unity.LauncherEntry.Update (in s app_uri, in a{sv} properties)
SDL_DBusContext *dbus = SDL_DBus_GetContext();
if (!dbus || !dbus->session_conn) {
return false;
}
char *objectPath = GetDBUSObjectPath();
if (!objectPath) {
return false;
}
DBusMessage *msg = dbus->message_new_signal(objectPath, UnityLauncherAPI_DBUS_INTERFACE, UnityLauncherAPI_DBUS_SIGNAL);
if (!msg) {
SDL_free(objectPath);
return false;
}
char *desktop_path = GetAppDesktopPath();
if (!desktop_path) {
dbus->message_unref(msg);
SDL_free(objectPath);
return false;
}
const char *progress_visible_str = "progress-visible";
const char *progress_str = "progress";
int dbus_type_boolean_str = DBUS_TYPE_BOOLEAN;
int dbus_type_double_str = DBUS_TYPE_DOUBLE;
const int progress_visible = ShouldShowProgress(window->progress_state);
double progress = (double)window->progress_value;
DBusMessageIter args, props;
dbus->message_iter_init_append(msg, &args);
dbus->message_iter_append_basic(&args, DBUS_TYPE_STRING, &desktop_path); // Setup app_uri paramter
dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &props); // Setup properties parameter
DBusMessageIter key_it, value_it;
// Set progress visible property
dbus->message_iter_open_container(&props, DBUS_TYPE_DICT_ENTRY, NULL, &key_it);
dbus->message_iter_append_basic(&key_it, DBUS_TYPE_STRING, &progress_visible_str); // Append progress-visible key data
dbus->message_iter_open_container(&key_it, DBUS_TYPE_VARIANT, (const char *)&dbus_type_boolean_str, &value_it);
dbus->message_iter_append_basic(&value_it, DBUS_TYPE_BOOLEAN, &progress_visible); // Append progress-visible value data
dbus->message_iter_close_container(&key_it, &value_it);
dbus->message_iter_close_container(&props, &key_it);
// Set progress value property
dbus->message_iter_open_container(&props, DBUS_TYPE_DICT_ENTRY, NULL, &key_it);
dbus->message_iter_append_basic(&key_it, DBUS_TYPE_STRING, &progress_str); // Append progress key data
dbus->message_iter_open_container(&key_it, DBUS_TYPE_VARIANT, (const char *)&dbus_type_double_str, &value_it);
dbus->message_iter_append_basic(&value_it, DBUS_TYPE_DOUBLE, &progress); // Append progress value data
dbus->message_iter_close_container(&key_it, &value_it);
dbus->message_iter_close_container(&props, &key_it);
dbus->message_iter_close_container(&args, &props);
dbus->connection_send(dbus->session_conn, msg, NULL);
SDL_free(desktop_path);
dbus->message_unref(msg);
SDL_free(objectPath);
return true;
}
#endif // SDL_USE_LIBDBUS

View File

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

View File

@ -261,7 +261,7 @@ char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid)
char *result = NULL;
if (WIN_IsEqualGUID(guid, &nullguid)) {
return WIN_StringToUTF8(name); // No GUID, go with what we've got.
return WIN_StringToUTF8W(name); // No GUID, go with what we've got.
}
ptr = (const unsigned char *)guid;
@ -270,37 +270,37 @@ char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid)
ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6],
ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
strw = WIN_UTF8ToString(keystr);
strw = WIN_UTF8ToStringW(keystr);
rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS);
SDL_free(strw);
if (!rc) {
return WIN_StringToUTF8(name); // oh well.
return WIN_StringToUTF8W(name); // oh well.
}
rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS);
if (!rc) {
RegCloseKey(hkey);
return WIN_StringToUTF8(name); // oh well.
return WIN_StringToUTF8W(name); // oh well.
}
strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR));
if (!strw) {
RegCloseKey(hkey);
return WIN_StringToUTF8(name); // oh well.
return WIN_StringToUTF8W(name); // oh well.
}
rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS);
RegCloseKey(hkey);
if (!rc) {
SDL_free(strw);
return WIN_StringToUTF8(name); // oh well.
return WIN_StringToUTF8W(name); // oh well.
}
strw[len / 2] = 0; // make sure it's null-terminated.
result = WIN_StringToUTF8(strw);
result = WIN_StringToUTF8W(strw);
SDL_free(strw);
return result ? result : WIN_StringToUTF8(name);
return result ? result : WIN_StringToUTF8W(name);
#endif
}

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

@ -251,9 +251,9 @@ void SDL_SYS_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFil
SDLBRefFilter *filter = new(std::nothrow) SDLBRefFilter(filters, nfilters);
if (looper == NULL || messenger == NULL || filter == NULL) {
SDL_free(looper);
SDL_free(messenger);
SDL_free(filter);
delete looper;
delete messenger;
delete filter;
SDL_OutOfMemory();
callback(userdata, NULL, -1);
return;

View File

@ -895,8 +895,12 @@ static void SDL_LogEvent(const SDL_Event *event)
(event->type == SDL_EVENT_FINGER_MOTION) ||
(event->type == SDL_EVENT_PEN_AXIS) ||
(event->type == SDL_EVENT_PEN_MOTION) ||
(event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) ||
(event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) ||
(event->type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE) ||
(event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) ||
(event->type == SDL_EVENT_GAMEPAD_UPDATE_COMPLETE) ||
(event->type == SDL_EVENT_JOYSTICK_AXIS_MOTION) ||
(event->type == SDL_EVENT_JOYSTICK_UPDATE_COMPLETE) ||
(event->type == SDL_EVENT_SENSOR_UPDATE))) {
return;
}

View File

@ -1314,8 +1314,9 @@ static void SDL_MaybeEnableWarpEmulation(SDL_Window *window, float x, float y)
// Require two consecutive warps to the center within a certain timespan to enter warp emulation mode.
const Uint64 now = SDL_GetTicksNS();
if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) {
if (SDL_SetRelativeMouseMode(true)) {
mouse->warp_emulation_active = true;
mouse->warp_emulation_active = true;
if (!SDL_SetRelativeMouseMode(true)) {
mouse->warp_emulation_active = false;
}
}

View File

@ -181,7 +181,7 @@ char *SDL_SYS_GetPrefPath(const char *org, const char *app)
char *SDL_SYS_GetUserFolder(SDL_Folder folder)
{
typedef HRESULT (WINAPI *pfnSHGetKnownFolderPath)(REFGUID /* REFKNOWNFOLDERID */, DWORD, HANDLE, PWSTR*);
HMODULE lib = LoadLibrary(L"Shell32.dll");
HMODULE lib = LoadLibraryW(L"Shell32.dll");
pfnSHGetKnownFolderPath pSHGetKnownFolderPath = NULL;
char *result = NULL;

View File

@ -186,6 +186,114 @@
#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
false, // 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
false, // 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
@ -450,7 +558,7 @@ static const SDL_GPUBootstrap * SDL_GPUSelectBackend(SDL_PropertiesID props)
SDL_SetError("Required shader format for backend %s not provided!", gpudriver);
return NULL;
}
if (backends[i]->PrepareDriver(_this)) {
if (backends[i]->PrepareDriver(_this, props)) {
return backends[i];
}
}
@ -465,7 +573,7 @@ static const SDL_GPUBootstrap * SDL_GPUSelectBackend(SDL_PropertiesID props)
// Don't select a backend which doesn't support the app's shaders.
continue;
}
if (backends[i]->PrepareDriver(_this)) {
if (backends[i]->PrepareDriver(_this, props)) {
return backends[i];
}
}
@ -760,6 +868,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,
@ -1522,30 +1637,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) {
@ -1553,10 +1685,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 ||
@ -1564,6 +1698,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;
}
}
}
@ -1756,7 +1891,11 @@ void SDL_BindGPUVertexSamplers(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_SAMPLER_TEXTURES
if (!((CommandBufferCommonHeader*)RENDERPASS_COMMAND_BUFFER)->ignore_render_pass_texture_validation)
{
CHECK_SAMPLER_TEXTURES
}
}
RENDERPASS_DEVICE->BindVertexSamplers(
@ -1836,7 +1975,11 @@ void SDL_BindGPUFragmentSamplers(
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
CHECK_SAMPLER_TEXTURES
if (!((CommandBufferCommonHeader*)RENDERPASS_COMMAND_BUFFER)->ignore_render_pass_texture_validation)
{
CHECK_SAMPLER_TEXTURES
}
}
RENDERPASS_DEVICE->BindFragmentSamplers(
@ -2074,6 +2217,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?
@ -2605,11 +2758,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

@ -66,6 +66,8 @@ typedef struct CommandBufferCommonHeader
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
@ -1140,7 +1142,7 @@ typedef struct SDL_GPUBootstrap
{
const char *name;
const SDL_GPUShaderFormat shader_formats;
bool (*PrepareDriver)(SDL_VideoDevice *_this);
bool (*PrepareDriver)(SDL_VideoDevice *_this, SDL_PropertiesID props);
SDL_GPUDevice *(*CreateDevice)(bool debug_mode, bool prefer_low_power, SDL_PropertiesID props);
} SDL_GPUBootstrap;

View File

@ -8317,7 +8317,7 @@ static void D3D12_INTERNAL_InitBlitResources(
}
}
static bool D3D12_PrepareDriver(SDL_VideoDevice *_this)
static bool D3D12_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props)
{
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
return true;

View File

@ -4307,7 +4307,7 @@ static bool METAL_SupportsTextureFormat(
// Device Creation
static bool METAL_PrepareDriver(SDL_VideoDevice *this)
static bool METAL_PrepareDriver(SDL_VideoDevice *this, SDL_PropertiesID props)
{
if (@available(macOS 10.14, iOS 13.0, tvOS 13.0, *)) {
return (this->Metal_CreateView != NULL);

View File

@ -703,7 +703,7 @@ typedef struct WindowData
// Synchronization primitives
VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT];
VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT];
VkSemaphore *renderFinishedSemaphore;
SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
Uint32 frameCounter;
@ -1088,6 +1088,7 @@ struct VulkanRenderer
VkPhysicalDevice physicalDevice;
VkPhysicalDeviceProperties2KHR physicalDeviceProperties;
VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties;
VkPhysicalDeviceFeatures desiredDeviceFeatures;
VkDevice logicalDevice;
Uint8 integratedMemoryNotification;
Uint8 outOfDeviceLocalMemoryWarning;
@ -3164,7 +3165,6 @@ static void VULKAN_INTERNAL_DestroySwapchain(
SDL_free(windowData->textureContainers[i].activeTexture->subresources);
SDL_free(windowData->textureContainers[i].activeTexture);
}
windowData->imageCount = 0;
SDL_free(windowData->textureContainers);
windowData->textureContainers = NULL;
@ -3193,7 +3193,8 @@ static void VULKAN_INTERNAL_DestroySwapchain(
NULL);
windowData->imageAvailableSemaphore[i] = VK_NULL_HANDLE;
}
}
for (i = 0; i < windowData->imageCount; i += 1) {
if (windowData->renderFinishedSemaphore[i]) {
renderer->vkDestroySemaphore(
renderer->logicalDevice,
@ -3202,6 +3203,10 @@ static void VULKAN_INTERNAL_DestroySwapchain(
windowData->renderFinishedSemaphore[i] = VK_NULL_HANDLE;
}
}
SDL_free(windowData->renderFinishedSemaphore);
windowData->renderFinishedSemaphore = NULL;
windowData->imageCount = 0;
}
static void VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(
@ -4779,6 +4784,12 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain(
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false);
}
windowData->inFlightFences[i] = NULL;
}
windowData->renderFinishedSemaphore = SDL_malloc(
sizeof(VkSemaphore) * windowData->imageCount);
for (i = 0; i < windowData->imageCount; i += 1) {
vulkanResult = renderer->vkCreateSemaphore(
renderer->logicalDevice,
&semaphoreCreateInfo,
@ -4798,8 +4809,6 @@ static Uint32 VULKAN_INTERNAL_CreateSwapchain(
windowData->swapchain = VK_NULL_HANDLE;
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false);
}
windowData->inFlightFences[i] = NULL;
}
windowData->needsSwapchainRecreate = false;
@ -8149,7 +8158,7 @@ static void VULKAN_BeginComputePass(
vulkanCommandBuffer,
bufferContainer,
storageBufferBindings[i].cycle,
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ);
VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE);
vulkanCommandBuffer->readWriteComputeStorageBuffers[i] = buffer;
@ -10020,7 +10029,7 @@ static bool VULKAN_INTERNAL_AcquireSwapchainTexture(
}
vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] =
windowData->renderFinishedSemaphore[windowData->frameCounter];
windowData->renderFinishedSemaphore[swapchainImageIndex];
vulkanCommandBuffer->signalSemaphoreCount += 1;
*swapchainTexture = (SDL_GPUTexture *)swapchainTextureContainer;
@ -10561,7 +10570,7 @@ static bool VULKAN_Submit(
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = NULL;
presentInfo.pWaitSemaphores =
&presentData->windowData->renderFinishedSemaphore[presentData->windowData->frameCounter];
&presentData->windowData->renderFinishedSemaphore[presentData->swapchainImageIndex];
presentInfo.waitSemaphoreCount = 1;
presentInfo.pSwapchains = &presentData->windowData->swapchain;
presentInfo.swapchainCount = 1;
@ -11212,12 +11221,14 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable(
renderer->vkGetPhysicalDeviceFeatures(
physicalDevice,
&deviceFeatures);
if (!deviceFeatures.independentBlend ||
!deviceFeatures.imageCubeArray ||
!deviceFeatures.depthClamp ||
!deviceFeatures.shaderClipDistance ||
!deviceFeatures.drawIndirectFirstInstance ||
!deviceFeatures.sampleRateShading) {
if ((!deviceFeatures.independentBlend && renderer->desiredDeviceFeatures.independentBlend) ||
(!deviceFeatures.imageCubeArray && renderer->desiredDeviceFeatures.imageCubeArray) ||
(!deviceFeatures.depthClamp && renderer->desiredDeviceFeatures.depthClamp) ||
(!deviceFeatures.shaderClipDistance && renderer->desiredDeviceFeatures.shaderClipDistance) ||
(!deviceFeatures.drawIndirectFirstInstance && renderer->desiredDeviceFeatures.drawIndirectFirstInstance) ||
(!deviceFeatures.sampleRateShading && renderer->desiredDeviceFeatures.sampleRateShading) ||
(!deviceFeatures.samplerAnisotropy && renderer->desiredDeviceFeatures.samplerAnisotropy)) {
return 0;
}
@ -11433,7 +11444,6 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice(
{
VkResult vulkanResult;
VkDeviceCreateInfo deviceCreateInfo;
VkPhysicalDeviceFeatures desiredDeviceFeatures;
VkPhysicalDeviceFeatures haveDeviceFeatures;
VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures;
const char **deviceExtensions;
@ -11457,22 +11467,13 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice(
// specifying used device features
SDL_zero(desiredDeviceFeatures);
desiredDeviceFeatures.independentBlend = VK_TRUE;
desiredDeviceFeatures.samplerAnisotropy = VK_TRUE;
desiredDeviceFeatures.imageCubeArray = VK_TRUE;
desiredDeviceFeatures.depthClamp = VK_TRUE;
desiredDeviceFeatures.shaderClipDistance = VK_TRUE;
desiredDeviceFeatures.drawIndirectFirstInstance = VK_TRUE;
desiredDeviceFeatures.sampleRateShading = VK_TRUE;
if (haveDeviceFeatures.fillModeNonSolid) {
desiredDeviceFeatures.fillModeNonSolid = VK_TRUE;
renderer->desiredDeviceFeatures.fillModeNonSolid = VK_TRUE;
renderer->supportsFillModeNonSolid = true;
}
if (haveDeviceFeatures.multiDrawIndirect) {
desiredDeviceFeatures.multiDrawIndirect = VK_TRUE;
renderer->desiredDeviceFeatures.multiDrawIndirect = VK_TRUE;
renderer->supportsMultiDrawIndirect = true;
}
@ -11513,7 +11514,7 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice(
deviceCreateInfo.enabledExtensionCount);
CreateDeviceExtensionArray(&renderer->supports, deviceExtensions);
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions;
deviceCreateInfo.pEnabledFeatures = &desiredDeviceFeatures;
deviceCreateInfo.pEnabledFeatures = &renderer->desiredDeviceFeatures;
vulkanResult = renderer->vkCreateDevice(
renderer->physicalDevice,
@ -11598,11 +11599,11 @@ static bool VULKAN_INTERNAL_PrepareVulkan(
return true;
}
static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this)
static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props)
{
// Set up dummy VulkanRenderer
VulkanRenderer *renderer;
Uint8 result;
bool result = false;
if (_this->Vulkan_CreateSurface == NULL) {
return false;
@ -11612,16 +11613,27 @@ static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this)
return false;
}
renderer = (VulkanRenderer *)SDL_malloc(sizeof(VulkanRenderer));
SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
renderer = (VulkanRenderer *)SDL_calloc(1, sizeof(*renderer));
if (renderer) {
// Opt out device features (higher compatibility in exchange for reduced functionality)
renderer->desiredDeviceFeatures.samplerAnisotropy = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
renderer->desiredDeviceFeatures.depthClamp = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
renderer->desiredDeviceFeatures.shaderClipDistance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
renderer->desiredDeviceFeatures.drawIndirectFirstInstance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
result = VULKAN_INTERNAL_PrepareVulkan(renderer);
// These features have near universal support so they are always enabled
renderer->desiredDeviceFeatures.independentBlend = VK_TRUE;
renderer->desiredDeviceFeatures.sampleRateShading = VK_TRUE;
renderer->desiredDeviceFeatures.imageCubeArray = VK_TRUE;
if (result) {
renderer->vkDestroyInstance(renderer->instance, NULL);
result = VULKAN_INTERNAL_PrepareVulkan(renderer);
if (result) {
renderer->vkDestroyInstance(renderer->instance, NULL);
}
SDL_free(renderer);
}
SDL_free(renderer);
SDL_Vulkan_UnloadLibrary();
return result;
}
@ -11642,12 +11654,27 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S
return NULL;
}
renderer = (VulkanRenderer *)SDL_malloc(sizeof(VulkanRenderer));
SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
renderer = (VulkanRenderer *)SDL_calloc(1, sizeof(*renderer));
if (!renderer) {
SDL_Vulkan_UnloadLibrary();
return false;
}
renderer->debugMode = debugMode;
renderer->preferLowPower = preferLowPower;
renderer->allowedFramesInFlight = 2;
// Opt out device features (higher compatibility in exchange for reduced functionality)
renderer->desiredDeviceFeatures.samplerAnisotropy = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
renderer->desiredDeviceFeatures.depthClamp = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
renderer->desiredDeviceFeatures.shaderClipDistance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
renderer->desiredDeviceFeatures.drawIndirectFirstInstance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN, true) ? VK_TRUE : VK_FALSE;
// These features have near universal support so they are always enabled
renderer->desiredDeviceFeatures.independentBlend = VK_TRUE;
renderer->desiredDeviceFeatures.sampleRateShading = VK_TRUE;
renderer->desiredDeviceFeatures.imageCubeArray = VK_TRUE;
if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) {
SDL_free(renderer);
SDL_Vulkan_UnloadLibrary();

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

@ -779,6 +779,22 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
}
break;
}
} else if (vendor == USB_VENDOR_8BITDO &&
(product == USB_PRODUCT_8BITDO_SF30_PRO ||
product == USB_PRODUCT_8BITDO_SF30_PRO_BT ||
product == USB_PRODUCT_8BITDO_SN30_PRO ||
product == USB_PRODUCT_8BITDO_SN30_PRO_BT ||
product == USB_PRODUCT_8BITDO_PRO_2 ||
product == USB_PRODUCT_8BITDO_PRO_2_BT)) {
SDL_strlcat(mapping_string, "a:b1,b:b0,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:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string));
if (product == USB_PRODUCT_8BITDO_PRO_2 || product == USB_PRODUCT_8BITDO_PRO_2_BT) {
SDL_strlcat(mapping_string, "paddle1:b14,paddle2:b13,", sizeof(mapping_string));
}
} else if (vendor == USB_VENDOR_8BITDO &&
(product == USB_PRODUCT_8BITDO_SF30_PRO ||
product == USB_PRODUCT_8BITDO_SF30_PRO_BT)) {
// This controller has no guide button
SDL_strlcat(mapping_string, "a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string));
} else {
// All other gamepads have the standard set of 19 buttons and 6 axes
if (SDL_IsJoystickGameCube(vendor, product)) {
@ -802,20 +818,20 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
} else if (SDL_IsJoystickGoogleStadiaController(vendor, product)) {
// The Google Stadia controller has a share button and a Google Assistant button
SDL_strlcat(mapping_string, "misc1:b11,misc2:b12", sizeof(mapping_string));
SDL_strlcat(mapping_string, "misc1:b11,misc2:b12,", sizeof(mapping_string));
} else if (SDL_IsJoystickNVIDIASHIELDController(vendor, product)) {
// The NVIDIA SHIELD controller has a share button between back and start buttons
SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
if (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {
// The original SHIELD controller has a touchpad and plus/minus buttons as well
SDL_strlcat(mapping_string, "touchpad:b12,misc2:b13,misc3:b14", sizeof(mapping_string));
SDL_strlcat(mapping_string, "touchpad:b12,misc2:b13,misc3:b14,", sizeof(mapping_string));
}
} else if (SDL_IsJoystickHoriSteamController(vendor, product)) {
/* The Wireless HORIPad for Steam has QAM, Steam, Capsense L/R Sticks, 2 rear buttons, and 2 misc buttons */
SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17", sizeof(mapping_string));
} else if (SDL_IsJoystick8BitDoController(vendor, product)) {
SDL_strlcat(mapping_string, "paddle1:b12,paddle2:b11,paddle3:b14,paddle4:b13", sizeof(mapping_string));
SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17,", sizeof(mapping_string));
} else if (vendor == USB_VENDOR_8BITDO && product == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS) {
SDL_strlcat(mapping_string, "paddle1:b12,paddle2:b11,paddle3:b14,paddle4:b13,", sizeof(mapping_string));
} else {
switch (SDL_GetGamepadTypeFromGUID(guid, NULL)) {
case SDL_GAMEPAD_TYPE_PS4:
@ -1295,7 +1311,7 @@ static bool SDL_PrivateParseGamepadElement(SDL_Gamepad *gamepad, const char *szG
static bool SDL_PrivateParseGamepadConfigString(SDL_Gamepad *gamepad, const char *pchString)
{
char szGameButton[20];
char szJoystickButton[20];
char szJoystickButton[128];
bool bGameButton = true;
int i = 0;
const char *pchPos = pchString;

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[] = {
"03000000790000004418000000010000,Nintendo GameCube Controller,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,",
"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,",
@ -868,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,",

View File

@ -3175,11 +3175,6 @@ bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id)
return vendor_id == USB_VENDOR_HORI && (product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER || product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT);
}
bool SDL_IsJoystick8BitDoController(Uint16 vendor_id, Uint16 product_id)
{
return vendor_id == USB_VENDOR_8BITDO && (product_id == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS);
}
bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);

View File

@ -135,9 +135,6 @@ extern bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick is a HORI Steam controller
extern bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick is a 8BitDo controller
extern bool SDL_IsJoystick8BitDoController(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick is a Steam Deck
extern bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id);

View File

@ -346,7 +346,9 @@ static bool IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
(device->is_switch_joycon_pair && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR, 0, "")) ||
(device->is_stadia && HIDAPI_IsDevicePresent(USB_VENDOR_GOOGLE, USB_PRODUCT_GOOGLE_STADIA_CONTROLLER, 0, "")) ||
(device->is_switch_joyconL && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT, 0, "")) ||
(device->is_switch_joyconR && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT, 0, ""))) {
(device->is_switch_joyconR && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT, 0, "")) ||
(SDL_strcmp(name, "8Bitdo SN30 Pro") == 0 && (HIDAPI_IsDevicePresent(USB_VENDOR_8BITDO, USB_PRODUCT_8BITDO_SN30_PRO, 0, "") || HIDAPI_IsDevicePresent(USB_VENDOR_8BITDO, USB_PRODUCT_8BITDO_SN30_PRO_BT, 0, ""))) ||
(SDL_strcmp(name, "8BitDo Pro 2") == 0 && (HIDAPI_IsDevicePresent(USB_VENDOR_8BITDO, USB_PRODUCT_8BITDO_PRO_2, 0, "") || HIDAPI_IsDevicePresent(USB_VENDOR_8BITDO, USB_PRODUCT_8BITDO_PRO_2_BT, 0, "")))) {
// The HIDAPI driver is taking care of this device
return false;
}

View File

@ -42,6 +42,11 @@ enum
SDL_GAMEPAD_NUM_8BITDO_BUTTONS,
};
#define SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID 0x06
#define SDL_8BITDO_REPORTID_SDL_REPORTID 0x04
#define SDL_8BITDO_REPORTID_NOT_SUPPORTED_SDL_REPORTID 0x03
#define SDL_8BITDO_BT_REPORTID_SDL_REPORTID 0x01
#define ABITDO_ACCEL_SCALE 4096.f
#define SENSOR_INTERVAL_NS 8000000ULL
@ -112,9 +117,30 @@ static bool HIDAPI_Driver8BitDo_IsEnabled(void)
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_8BITDO, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
}
static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
{
SDL_memset(report, 0, length);
report[0] = report_id;
return SDL_hid_get_feature_report(dev, report, length);
}
static bool HIDAPI_Driver8BitDo_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
return SDL_IsJoystick8BitDoController(vendor_id, product_id);
if (vendor_id == USB_VENDOR_8BITDO) {
switch (product_id) {
case USB_PRODUCT_8BITDO_SF30_PRO:
case USB_PRODUCT_8BITDO_SF30_PRO_BT:
case USB_PRODUCT_8BITDO_SN30_PRO:
case USB_PRODUCT_8BITDO_SN30_PRO_BT:
case USB_PRODUCT_8BITDO_PRO_2:
case USB_PRODUCT_8BITDO_PRO_2_BT:
case USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS:
return true;
default:
break;
}
}
return false;
}
static bool HIDAPI_Driver8BitDo_InitDevice(SDL_HIDAPI_Device *device)
@ -128,15 +154,40 @@ static bool HIDAPI_Driver8BitDo_InitDevice(SDL_HIDAPI_Device *device)
if (device->product_id == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS) {
// The Ultimate 2 Wireless v1.02 firmware has 12 byte reports, v1.03 firmware has 34 byte reports
const int ULTIMATE2_WIRELESS_V103_REPORT_SIZE = 34;
const int MAX_ATTEMPTS = 3;
for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
Uint8 data[USB_PACKET_LENGTH];
int size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 80);
if (size == 0) {
// Try again
continue;
}
if (size >= ULTIMATE2_WIRELESS_V103_REPORT_SIZE) {
ctx->sensors_supported = true;
ctx->rumble_supported = true;
ctx->powerstate_supported = true;
}
break;
}
} else {
Uint8 data[USB_PACKET_LENGTH];
int size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 80);
if (size >= ULTIMATE2_WIRELESS_V103_REPORT_SIZE) {
int size = ReadFeatureReport(device->dev, SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID, data, sizeof(data));
if (size > 0) {
ctx->sensors_supported = true;
ctx->rumble_supported = true;
ctx->powerstate_supported = true;
}
}
if (device->product_id == USB_PRODUCT_8BITDO_SF30_PRO || device->product_id == USB_PRODUCT_8BITDO_SF30_PRO_BT) {
HIDAPI_SetDeviceName(device, "8BitDo SF30 Pro");
} else if (device->product_id == USB_PRODUCT_8BITDO_SN30_PRO || device->product_id == USB_PRODUCT_8BITDO_SN30_PRO_BT) {
HIDAPI_SetDeviceName(device, "8BitDo SN30 Pro");
} else if (device->product_id == USB_PRODUCT_8BITDO_PRO_2 || device->product_id == USB_PRODUCT_8BITDO_PRO_2_BT) {
HIDAPI_SetDeviceName(device, "8BitDo Pro 2");
}
return HIDAPI_JoystickConnected(device, NULL);
}
@ -162,7 +213,14 @@ static bool HIDAPI_Driver8BitDo_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joys
SDL_zeroa(ctx->last_state);
// Initialize the joystick capabilities
joystick->nbuttons = SDL_GAMEPAD_NUM_8BITDO_BUTTONS;
if (device->product_id == USB_PRODUCT_8BITDO_PRO_2 ||
device->product_id == USB_PRODUCT_8BITDO_PRO_2_BT ||
device->product_id == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS) {
// This controller has additional buttons
joystick->nbuttons = SDL_GAMEPAD_NUM_8BITDO_BUTTONS;
} else {
joystick->nbuttons = 11;
}
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
joystick->nhats = 1;
@ -232,11 +290,95 @@ static bool HIDAPI_Driver8BitDo_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *dev
}
return SDL_Unsupported();
}
static void HIDAPI_Driver8BitDo_HandleOldStatePacket(SDL_Joystick *joystick, SDL_Driver8BitDo_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
Uint64 timestamp = SDL_GetTicksNS();
if (ctx->last_state[2] != data[2]) {
Uint8 hat;
switch (data[2]) {
case 0:
hat = SDL_HAT_UP;
break;
case 1:
hat = SDL_HAT_RIGHTUP;
break;
case 2:
hat = SDL_HAT_RIGHT;
break;
case 3:
hat = SDL_HAT_RIGHTDOWN;
break;
case 4:
hat = SDL_HAT_DOWN;
break;
case 5:
hat = SDL_HAT_LEFTDOWN;
break;
case 6:
hat = SDL_HAT_LEFT;
break;
case 7:
hat = SDL_HAT_LEFTUP;
break;
default:
hat = SDL_HAT_CENTERED;
break;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
}
if (ctx->last_state[0] != data[0]) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[0] & 0x01) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[0] & 0x02) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[0] & 0x08) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[0] & 0x10) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[0] & 0x40) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[0] & 0x80) != 0));
}
if (ctx->last_state[1] != data[1]) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[1] & 0x10) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[1] & 0x04) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[1] & 0x08) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x20) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x40) != 0));
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (data[1] & 0x01) ? SDL_MAX_SINT16 : SDL_MIN_SINT16);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (data[1] & 0x02) ? SDL_MAX_SINT16 : SDL_MIN_SINT16);
}
#define READ_STICK_AXIS(offset) \
(data[offset] == 0x7f ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x7f), -0x7f, 0xff - 0x7f, SDL_MIN_SINT16, SDL_MAX_SINT16))
{
axis = READ_STICK_AXIS(3);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
axis = READ_STICK_AXIS(4);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
axis = READ_STICK_AXIS(5);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
axis = READ_STICK_AXIS(6);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
}
#undef READ_STICK_AXIS
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Driver8BitDo_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
Uint64 timestamp = SDL_GetTicksNS();
if (data[0] != 0x03 && data[0] != 0x01) {
switch (data[0]) {
case SDL_8BITDO_REPORTID_NOT_SUPPORTED_SDL_REPORTID: // Firmware without enhanced mode
case SDL_8BITDO_REPORTID_SDL_REPORTID: // Enhanced mode USB report
case SDL_8BITDO_BT_REPORTID_SDL_REPORTID: // Enhanced mode Bluetooth report
break;
default:
// We don't know how to handle this report
return;
}
@ -297,7 +439,7 @@ static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[9] & 0x40) != 0));
}
if (ctx->last_state[10] != data[10]) {
if (size > 10 && ctx->last_state[10] != data[10]) {
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_L4, ((data[10] & 0x01) != 0));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_R4, ((data[10] & 0x02) != 0));
}
@ -355,7 +497,6 @@ static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
if (ctx->sensors_enabled) {
Uint64 sensor_timestamp;
float values[3];
@ -414,7 +555,12 @@ static bool HIDAPI_Driver8BitDo_UpdateDevice(SDL_HIDAPI_Device *device)
continue;
}
HIDAPI_Driver8BitDo_HandleStatePacket(joystick, ctx, data, size);
if (size == 9) {
// Old firmware USB report for the SF30 Pro and SN30 Pro controllers
HIDAPI_Driver8BitDo_HandleOldStatePacket(joystick, ctx, data, size);
} else {
HIDAPI_Driver8BitDo_HandleStatePacket(joystick, ctx, data, size);
}
}
if (size < 0) {

File diff suppressed because it is too large Load Diff

View File

@ -60,12 +60,19 @@
#define USB_VENDOR_ZEROPLUS 0x0c12
#define USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS 0x6012
#define USB_PRODUCT_8BITDO_SF30_PRO 0x6000 // B + START
#define USB_PRODUCT_8BITDO_SF30_PRO_BT 0x6100 // B + START
#define USB_PRODUCT_8BITDO_SN30_PRO 0x6001 // B + START
#define USB_PRODUCT_8BITDO_SN30_PRO_BT 0x6101 // B + START
#define USB_PRODUCT_8BITDO_PRO_2 0x6003 // mode switch to D
#define USB_PRODUCT_8BITDO_PRO_2_BT 0x6006 // mode switch to D
#define USB_PRODUCT_AMAZON_LUNA_CONTROLLER 0x0419
#define USB_PRODUCT_ASTRO_C40_XBOX360 0x0024
#define USB_PRODUCT_BACKBONE_ONE_IOS 0x0103
#define USB_PRODUCT_BACKBONE_ONE_IOS_PS5 0x0104
#define USB_PRODUCT_BDA_XB1_CLASSIC 0x581a
#define USB_PRODUCT_BDA_XB1_FIGHTPAD 0x791a
#define USB_PRODUCT_BDA_XB1_SPECTRA_PRO 0x592a
#define USB_PRODUCT_GOOGLE_STADIA_CONTROLLER 0x9400
#define USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER1 0x1843
#define USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER2 0x1844
@ -96,6 +103,7 @@
#define USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 0x7210
#define USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104 0x7214
#define USB_PRODUCT_PDP_ROCK_CANDY 0x0246
#define USB_PRODUCT_POWERA_MINI 0x541a
#define USB_PRODUCT_RAZER_ATROX 0x0a00
#define USB_PRODUCT_RAZER_KITSUNE 0x1012
#define USB_PRODUCT_RAZER_PANTHERA 0x0401
@ -124,6 +132,7 @@
#define USB_PRODUCT_STEALTH_ULTRA_WIRED 0x7073
#define USB_PRODUCT_SWITCH_RETROBIT_CONTROLLER 0x0575
#define USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO_PS4 0xd00e
#define USB_PRODUCT_THRUSTMASTER_T_FLIGHT_HOTAS_ONE 0xb68c
#define USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE 0x1142
#define USB_PRODUCT_VICTRIX_FS_PRO 0x0203
#define USB_PRODUCT_VICTRIX_FS_PRO_V2 0x0207

View File

@ -293,7 +293,7 @@ static bool QueryDeviceName(LPDIRECTINPUTDEVICE8 device, Uint16 vendor_id, Uint1
}
*manufacturer_string = NULL;
*product_string = WIN_StringToUTF8(dipstr.wsz);
*product_string = WIN_StringToUTF8W(dipstr.wsz);
return true;
}

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
@ -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

@ -45,10 +45,18 @@ SDL_Process *SDL_CreateProcess(const char * const *args, bool pipe_stdio)
SDL_Process *SDL_CreateProcessWithProperties(SDL_PropertiesID props)
{
const char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
#if defined(SDL_PLATFORM_WINDOWS)
const char *cmdline = SDL_GetStringProperty(props, SDL_PROP_PROCESS_CREATE_CMDLINE_STRING, NULL);
if ((!args || !args[0] || !args[0][0]) && (!cmdline || !cmdline[0])) {
SDL_SetError("Either SDL_PROP_PROCESS_CREATE_ARGS_POINTER or SDL_PROP_PROCESS_CREATE_CMDLINE_STRING must be valid");
return NULL;
}
#else
if (!args || !args[0] || !args[0][0]) {
SDL_InvalidParamError("SDL_PROP_PROCESS_CREATE_ARGS_POINTER");
return NULL;
}
#endif
SDL_Process *process = (SDL_Process *)SDL_calloc(1, sizeof(*process));
if (!process) {

View File

@ -106,9 +106,12 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
len = 0;
for (i = 0; args[i]; i++) {
const char *a = args[i];
bool quotes = *a == '\0' || SDL_strpbrk(a, " \r\n\t\v") != NULL;
/* two double quotes to surround an argument with */
len += 2;
if (quotes) {
/* surround the argument with double quote if it is empty or contains whitespaces */
len += 2;
}
for (; *a; a++) {
switch (*a) {
@ -116,8 +119,8 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
len += 2;
break;
case '\\':
/* only escape backslashes that precede a double quote */
len += (a[1] == '"' || a[1] == '\0') ? 2 : 1;
/* only escape backslashes that precede a double quote (including the enclosing double quote) */
len += (a[1] == '"' || (quotes && a[1] == '\0')) ? 2 : 1;
break;
case ' ':
case '^':
@ -149,8 +152,11 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
i_out = 0;
for (i = 0; args[i]; i++) {
const char *a = args[i];
bool quotes = *a == '\0' || SDL_strpbrk(a, " \r\n\t\v") != NULL;
result[i_out++] = '"';
if (quotes) {
result[i_out++] = '"';
}
for (; *a; a++) {
switch (*a) {
case '"':
@ -163,7 +169,7 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
break;
case '\\':
result[i_out++] = *a;
if (a[1] == '"' || a[1] == '\0') {
if (a[1] == '"' || (quotes && a[1] == '\0')) {
result[i_out++] = *a;
}
break;
@ -188,7 +194,9 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
break;
}
}
result[i_out++] = '"';
if (quotes) {
result[i_out++] = '"';
}
result[i_out++] = ' ';
}
SDL_assert(i_out == len);
@ -237,6 +245,7 @@ static bool join_env(char **env, LPWSTR *env_out)
bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
{
const char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
const char *cmdline = SDL_GetStringProperty(props, SDL_PROP_PROCESS_CREATE_CMDLINE_STRING, NULL);
SDL_Environment *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, SDL_GetEnvironment());
char **envp = NULL;
const char *working_directory = SDL_GetStringProperty(props, SDL_PROP_PROCESS_CREATE_WORKING_DIRECTORY_STRING, NULL);
@ -286,7 +295,12 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
security_attributes.bInheritHandle = TRUE;
security_attributes.lpSecurityDescriptor = NULL;
if (!join_arguments(args, &createprocess_cmdline)) {
if (cmdline) {
createprocess_cmdline = WIN_UTF8ToString(cmdline);
if (!createprocess_cmdline) {
goto done;
}
} else if (!join_arguments(args, &createprocess_cmdline)) {
goto done;
}

View File

@ -2627,11 +2627,12 @@ static void UpdateLogicalPresentation(SDL_Renderer *renderer)
const float logical_h = view->logical_h;
int iwidth, iheight;
if (renderer->target) {
if (is_main_view) {
SDL_GetRenderOutputSize(renderer, &iwidth, &iheight);
} else {
SDL_assert(renderer->target != NULL);
iwidth = (int)renderer->target->w;
iheight = (int)renderer->target->h;
} else {
SDL_GetRenderOutputSize(renderer, &iwidth, &iheight);
}
view->logical_src_rect.x = 0.0f;

View File

@ -2511,13 +2511,7 @@ SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const
/* Ctrl-G toggle mouse grab */
SDL_Window *window = SDL_GetWindowFromEvent(event);
if (window) {
if (SDL_RectEmpty(SDL_GetWindowMouseRect(window))) {
SDL_Rect r = { 10, 10, 200, 200};
SDL_SetWindowMouseRect(window, &r);
} else {
SDL_SetWindowMouseRect(window, NULL);
}
//SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window));
SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window));
}
}
break;

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

@ -2266,6 +2266,12 @@ static void SDL_FinishWindowCreation(SDL_Window *window, SDL_WindowFlags flags)
SDL_ShowWindow(window);
}
}
#if defined(SDL_PLATFORM_LINUX)
// On Linux the progress state is persisted throughout multiple program runs, so reset state on window creation
SDL_SetWindowProgressState(window, SDL_PROGRESS_STATE_NONE);
SDL_SetWindowProgressValue(window, 0.0f);
#endif
}
static bool SDL_ContextNotSupported(const char *name)

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

@ -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

@ -22,7 +22,9 @@
#ifdef SDL_VIDEO_DRIVER_OPENVR
#if 0
#define DEBUG_OPENVR
#endif
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_keyboard_c.h"
@ -445,7 +447,7 @@ static void OPENVR_VirtualControllerUpdate(void *userdata)
static bool OPENVR_SetupJoystickBasedOnLoadedActionManifest(SDL_VideoData * videodata)
{
SDL_VirtualJoystickDesc desc;
int virtual_index;
SDL_JoystickID virtual_id;
EVRInputError e = 0;
@ -537,22 +539,22 @@ static bool OPENVR_SetupJoystickBasedOnLoadedActionManifest(SDL_VideoData * vide
desc.RumbleTriggers = OPENVR_VirtualControllerRumbleTriggers;
desc.Update = OPENVR_VirtualControllerUpdate;
desc.userdata = videodata;
virtual_index = SDL_AttachVirtualJoystick(&desc);
virtual_id = SDL_AttachVirtualJoystick(&desc);
if (virtual_index < 0) {
if (!virtual_id) {
return SDL_SetError("OPENVR: Couldn't attach virtual joystick device: %s", SDL_GetError());
}
videodata->virtual_joystick = SDL_OpenJoystick(virtual_id);
if (!videodata->virtual_joystick) {
return SDL_SetError("OPENVR: Couldn't open virtual joystick device: %s", SDL_GetError());
} else {
videodata->virtual_joystick = SDL_OpenJoystick(virtual_index);
if (!videodata->virtual_joystick) {
return SDL_SetError("OPENVR: Couldn't open virtual joystick device: %s", SDL_GetError());
}
}
#ifdef DEBUG_OPENVR
SDL_Log("Loaded virtual joystick with %d buttons and %d axes", videodata->input_action_handles_buttons_count, videodata->input_action_handles_axes_count);
#endif
return false;
return true;
}
static bool OPENVR_InitializeOverlay(SDL_VideoDevice *_this,SDL_Window *window)
@ -710,7 +712,7 @@ static bool OPENVR_ReleaseFrame(SDL_VideoDevice *_this)
if (videodata->overlaytexture != 0 &&
videodata->targh == videodata->last_targh &&
videodata->targw == videodata->last_targw) {
// Only submit frames to OpenVR if the textu re exists.
// Only submit frames to OpenVR if the texture exists.
struct Texture_t tex;
// Setup a Texture_t object to send in the texture.

View File

@ -90,7 +90,7 @@
// Scoped function declarations
static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat);
struct SDL_WaylandTouchPoint
typedef struct
{
SDL_TouchID id;
wl_fixed_t fx;
@ -98,11 +98,11 @@ struct SDL_WaylandTouchPoint
struct wl_surface *surface;
struct wl_list link;
};
} SDL_WaylandTouchPoint;
static void Wayland_SeatAddTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface)
{
struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(SDL_WaylandTouchPoint));
SDL_zerop(tp);
tp->id = id;
@ -113,9 +113,37 @@ static void Wayland_SeatAddTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed
WAYLAND_wl_list_insert(&seat->touch.points, &tp->link);
}
static void Wayland_SeatCancelTouch(SDL_WaylandSeat *seat, SDL_WaylandTouchPoint *tp)
{
if (tp->surface) {
SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(tp->surface);
if (window_data) {
const float x = (float)(wl_fixed_to_double(tp->fx) / window_data->current.logical_width);
const float y = (float)(wl_fixed_to_double(tp->fy) / window_data->current.logical_height);
SDL_SendTouch(0, (SDL_TouchID)(uintptr_t)seat->touch.wl_touch,
(SDL_FingerID)(tp->id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_CANCELED, x, y, 0.0f);
--window_data->active_touch_count;
/* If the window currently has mouse focus and has no currently active keyboards, pointers,
* or touch events, then consider mouse focus to be lost.
*/
if (SDL_GetMouseFocus() == window_data->sdlwindow && !window_data->keyboard_focus_count &&
!window_data->pointer_focus_count && !window_data->active_touch_count) {
SDL_SetMouseFocus(NULL);
}
}
}
WAYLAND_wl_list_remove(&tp->link);
SDL_free(tp);
}
static void Wayland_SeatUpdateTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface **surface)
{
struct SDL_WaylandTouchPoint *tp;
SDL_WaylandTouchPoint *tp;
wl_list_for_each (tp, &seat->touch.points, link) {
if (tp->id == id) {
@ -131,7 +159,7 @@ static void Wayland_SeatUpdateTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fi
static void Wayland_SeatRemoveTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_surface **surface)
{
struct SDL_WaylandTouchPoint *tp;
SDL_WaylandTouchPoint *tp;
wl_list_for_each (tp, &seat->touch.points, link) {
if (tp->id == id) {
@ -152,23 +180,6 @@ static void Wayland_SeatRemoveTouch(SDL_WaylandSeat *seat, SDL_TouchID id, wl_fi
}
}
static bool Wayland_SurfaceHasActiveTouches(SDL_VideoData *display, struct wl_surface *surface)
{
struct SDL_WaylandTouchPoint *tp;
SDL_WaylandSeat *seat;
// Check all seats for active touches on the surface.
wl_list_for_each (seat, &display->seat_list, link) {
wl_list_for_each (tp, &seat->touch.points, link) {
if (tp->surface == surface) {
return true;
}
}
}
return false;
}
static void Wayland_GetScaledMouseRect(SDL_Window *window, SDL_Rect *scaled_rect)
{
SDL_WindowData *window_data = window->internal;
@ -751,7 +762,7 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
*/
SDL_Window *mouse_focus = SDL_GetMouseFocus();
const bool had_focus = mouse_focus && window->sdlwindow == mouse_focus;
if (!--window->pointer_focus_count && had_focus && !Wayland_SurfaceHasActiveTouches(seat->display, surface)) {
if (!--window->pointer_focus_count && had_focus && !window->active_touch_count) {
SDL_SetMouseFocus(NULL);
}
@ -901,7 +912,7 @@ static void pointer_handle_button_common(SDL_WaylandSeat *seat, uint32_t serial,
*
* The mouse is not captured in relative mode.
*/
if (!seat->display->relative_mode_enabled || !Wayland_SeatHasRelativePointerFocus(seat)) {
if (!seat->pointer.relative_pointer) {
if (seat->pointer.buttons_pressed != 0) {
window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
} else {
@ -1153,30 +1164,23 @@ static void relative_pointer_handle_relative_motion(void *data,
wl_fixed_t dy_unaccel_w)
{
SDL_WaylandSeat *seat = data;
SDL_WindowData *window = seat->pointer.focus;
SDL_Mouse *mouse = SDL_GetMouse();
if (seat->display->relative_mode_enabled) {
SDL_WindowData *window = seat->pointer.focus;
// Relative pointer event times are in microsecond granularity.
const Uint64 timestamp = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
// Relative motion follows keyboard focus.
if (Wayland_SeatHasRelativePointerFocus(seat)) {
SDL_Mouse *mouse = SDL_GetMouse();
// Relative pointer event times are in microsecond granularity.
const Uint64 timestamp = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
double dx;
double dy;
if (mouse->InputTransform || !mouse->enable_relative_system_scale) {
dx = wl_fixed_to_double(dx_unaccel_w);
dy = wl_fixed_to_double(dy_unaccel_w);
} else {
dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
}
SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
}
double dx;
double dy;
if (mouse->InputTransform || !mouse->enable_relative_system_scale) {
dx = wl_fixed_to_double(dx_unaccel_w);
dy = wl_fixed_to_double(dy_unaccel_w);
} else {
dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
}
SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
}
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
@ -1243,6 +1247,7 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri
y = (float)wl_fixed_to_double(fy) / (window_data->current.logical_height - 1);
}
++window_data->active_touch_count;
SDL_SetMouseFocus(window_data->sdlwindow);
SDL_SendTouch(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch,
@ -1269,11 +1274,13 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial
SDL_SendTouch(Wayland_GetTouchTimestamp(seat, timestamp), (SDL_TouchID)(uintptr_t)touch,
(SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_UP, x, y, 0.0f);
/* If the window currently has mouse focus, the keyboard focus is another window or NULL, the window has no
* pointers active on it, and the surface has no active touch events, then consider mouse focus to be lost.
--window_data->active_touch_count;
/* If the window currently has mouse focus and has no currently active keyboards, pointers,
* or touch events, then consider mouse focus to be lost.
*/
if (SDL_GetMouseFocus() == window_data->sdlwindow && seat->keyboard.focus != window_data &&
!window_data->pointer_focus_count && !Wayland_SurfaceHasActiveTouches(seat->display, surface)) {
if (SDL_GetMouseFocus() == window_data->sdlwindow && !window_data->keyboard_focus_count &&
!window_data->pointer_focus_count && !window_data->active_touch_count) {
SDL_SetMouseFocus(NULL);
}
}
@ -1308,39 +1315,11 @@ static void touch_handler_frame(void *data, struct wl_touch *touch)
static void touch_handler_cancel(void *data, struct wl_touch *touch)
{
SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
struct SDL_WaylandTouchPoint *tp, *temp;
SDL_WaylandTouchPoint *tp, *temp;
// Need the safe loop variant here as cancelling a touch point removes it from the list.
wl_list_for_each_safe (tp, temp, &seat->touch.points, link) {
bool removed = false;
if (tp->surface) {
SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(tp->surface);
if (window_data) {
const float x = (float)(wl_fixed_to_double(tp->fx) / window_data->current.logical_width);
const float y = (float)(wl_fixed_to_double(tp->fy) / window_data->current.logical_height);
SDL_SendTouch(0, (SDL_TouchID)(uintptr_t)touch,
(SDL_FingerID)(tp->id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_CANCELED, x, y, 0.0f);
// Remove the touch from the list before checking for still-active touches on the surface.
WAYLAND_wl_list_remove(&tp->link);
removed = true;
/* If the window currently has mouse focus, the keyboard focus is another window or NULL, the window has no
* pointers active on it, and the surface has no active touch events, then consider mouse focus to be lost.
*/
if (SDL_GetMouseFocus() == window_data->sdlwindow && seat->keyboard.focus != window_data &&
!window_data->pointer_focus_count && !Wayland_SurfaceHasActiveTouches(seat->display, tp->surface)) {
SDL_SetMouseFocus(NULL);
}
}
}
if (!removed) {
WAYLAND_wl_list_remove(&tp->link);
}
SDL_free(tp);
Wayland_SeatCancelTouch(seat, tp);
}
}
@ -1924,8 +1903,7 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
/* If the window has mouse focus, has no pointers within it, and no active touches, consider
* mouse focus to be lost.
*/
if (SDL_GetMouseFocus() == window->sdlwindow && !window->pointer_focus_count &&
!Wayland_SurfaceHasActiveTouches(seat->display, surface)) {
if (SDL_GetMouseFocus() == window->sdlwindow && !window->pointer_focus_count && !window->active_touch_count) {
SDL_SetMouseFocus(NULL);
}
}
@ -2092,26 +2070,6 @@ static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_repeat_info, // Version 4
};
static void Wayland_SeatCreateRelativePointer(SDL_WaylandSeat *seat)
{
if (seat->display->relative_pointer_manager) {
if (seat->pointer.wl_pointer && !seat->pointer.relative_pointer) {
seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer);
zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer,
&relative_pointer_listener,
seat);
}
}
}
void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display)
{
SDL_WaylandSeat *seat;
wl_list_for_each(seat, &display->seat_list, link) {
Wayland_SeatCreateRelativePointer(seat);
}
}
static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event)
{
// Make sure focus is removed from a surface before the pointer is destroyed.
@ -2254,8 +2212,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w
wl_pointer_set_user_data(seat->pointer.wl_pointer, seat);
wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat);
Wayland_SeatCreateRelativePointer(seat);
seat->pointer.sdl_id = SDL_GetNextObjectID();
if (seat->name) {
@ -3416,6 +3372,36 @@ void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat,
WAYLAND_wl_display_flush(display->display);
}
void Wayland_DisplayRemoveWindowReferencesFromSeats(SDL_VideoData *display, SDL_WindowData *window)
{
SDL_WaylandSeat *seat;
wl_list_for_each (seat, &display->seat_list, link)
{
if (seat->keyboard.focus == window) {
keyboard_handle_leave(seat, seat->keyboard.wl_keyboard, 0, window->surface);
}
if (seat->pointer.focus == window) {
pointer_handle_leave(seat, seat->pointer.wl_pointer, 0, window->surface);
}
// Need the safe loop variant here as cancelling a touch point removes it from the list.
SDL_WaylandTouchPoint *tp, *temp;
wl_list_for_each_safe (tp, temp, &seat->touch.points, link) {
if (tp->surface == window->surface) {
Wayland_SeatCancelTouch(seat, tp);
}
}
SDL_WaylandPenTool *tool;
wl_list_for_each (tool, &seat->tablet.tool_list, link) {
if (tool->tool_focus == window->sdlwindow) {
tablet_tool_handle_proximity_out(tool, tool->wltool);
}
}
}
}
void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events)
{
if (!seat) {
@ -3475,16 +3461,36 @@ void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events)
SDL_free(seat);
}
bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat)
static void Wayland_SeatUpdateRelativePointer(SDL_WaylandSeat *seat)
{
/* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard
* attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard
* focus on the window with pointer focus.
*/
if (seat->keyboard.wl_keyboard) {
return seat->keyboard.focus && seat->keyboard.focus == seat->pointer.focus;
} else {
return seat->pointer.focus && seat->pointer.focus->keyboard_focus_count != 0;
if (seat->display->relative_pointer_manager) {
bool relative_focus = false;
if (seat->pointer.focus) {
/* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard
* attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard
* focus on the window with pointer focus.
*/
if (seat->pointer.focus->sdlwindow->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE) {
if (seat->keyboard.wl_keyboard) {
relative_focus = seat->keyboard.focus == seat->pointer.focus;
} else {
relative_focus = seat->pointer.focus->keyboard_focus_count != 0;
}
} else {
relative_focus = SDL_GetMouse()->warp_emulation_active;
}
}
if (relative_focus) {
if (!seat->pointer.relative_pointer) {
seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer);
zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer, &relative_pointer_listener, seat);
}
} else if (seat->pointer.relative_pointer) {
zwp_relative_pointer_v1_destroy(seat->pointer.relative_pointer);
seat->pointer.relative_pointer = NULL;
}
}
}
@ -3517,11 +3523,10 @@ static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat)
void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
{
SDL_VideoData *display = seat->display;
Wayland_SeatUpdateRelativePointer(seat);
if (display->pointer_constraints) {
const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat);
if (seat->pointer.locked_pointer && (!display->relative_mode_enabled || !has_relative_focus)) {
if (seat->pointer.locked_pointer && !seat->pointer.relative_pointer) {
zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
seat->pointer.locked_pointer = NULL;
@ -3531,7 +3536,7 @@ void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
if (seat->pointer.wl_pointer) {
// If relative mode is active, and the pointer focus matches the keyboard focus, lock it.
if (seat->display->relative_mode_enabled && has_relative_focus) {
if (seat->pointer.relative_pointer) {
if (!seat->pointer.locked_pointer) {
// Creating a lock on a surface with an active confinement region on the same seat is a protocol error.
if (seat->pointer.confined_pointer) {

View File

@ -191,7 +191,6 @@ extern int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display);
extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display);
extern void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display);
extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display);
extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display);
extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display);
@ -201,10 +200,10 @@ extern void Wayland_DisplayCreateTextInputManager(SDL_VideoData *d, uint32_t id)
extern void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id);
extern void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events);
extern bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat);
extern void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat);
extern void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window);
extern void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window);
extern void Wayland_DisplayRemoveWindowReferencesFromSeats(SDL_VideoData *display, SDL_WindowData *window);
/* The implicit grab serial needs to be updated on:
* - Keyboard key down/up

View File

@ -881,8 +881,7 @@ static bool Wayland_WarpMouseRelative(SDL_Window *window, float x, float y)
if (d->pointer_constraints) {
wl_list_for_each (seat, &d->seat_list, link) {
if (wind == seat->pointer.focus ||
(!seat->pointer.focus && wind == seat->keyboard.focus)) {
if (wind == seat->pointer.focus) {
Wayland_SeatWarpMouse(seat, wind, x, y);
}
}
@ -939,7 +938,7 @@ static bool Wayland_SetRelativeMouseMode(bool enabled)
return SDL_SetError("Failed to enable relative mode: compositor lacks support for the required zwp_pointer_constraints_v1 protocol");
}
data->relative_mode_enabled = enabled;
// Windows have a relative mode flag, so just update the grabs on a state change.
Wayland_DisplayUpdatePointerGrabs(data, NULL);
return true;
}
@ -1121,14 +1120,11 @@ void Wayland_SeatUpdateCursor(SDL_WaylandSeat *seat)
SDL_Mouse *mouse = SDL_GetMouse();
SDL_WindowData *pointer_focus = seat->pointer.focus;
if (pointer_focus) {
const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat);
if (!seat->display->relative_mode_enabled || !has_relative_focus || !mouse->relative_mode_hide_cursor) {
if (pointer_focus && mouse->cursor_visible) {
if (!seat->pointer.relative_pointer || !mouse->relative_mode_hide_cursor) {
const SDL_HitTestResult rc = pointer_focus->hit_test_result;
if ((seat->display->relative_mode_enabled && has_relative_focus) ||
rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
if (seat->pointer.relative_pointer || rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
Wayland_SeatSetCursor(seat, mouse->cur_cursor);
} else {
Wayland_SeatSetCursor(seat, sys_cursors[rc]);

View File

@ -24,6 +24,7 @@
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include "../../core/linux/SDL_system_theme.h"
#include "../../core/linux/SDL_progressbar.h"
#include "../../events/SDL_events_c.h"
#include "SDL_waylandclipboard.h"
@ -629,6 +630,9 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
device->DestroyWindow = Wayland_DestroyWindow;
device->SetWindowHitTest = Wayland_SetWindowHitTest;
device->FlashWindow = Wayland_FlashWindow;
#ifdef SDL_USE_LIBDBUS
device->ApplyWindowProgress = DBUS_ApplyWindowProgress;
#endif // SDL_USE_LIBDBUS
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
device->SyncWindow = Wayland_SyncWindow;
@ -1263,7 +1267,6 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
} else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
Wayland_DisplayInitRelativePointerManager(d);
} else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1);
} else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {

View File

@ -96,9 +96,7 @@ struct SDL_VideoData
int output_count;
int output_max;
bool relative_mode_enabled;
bool display_externally_owned;
bool scale_to_display_enabled;
};

View File

@ -2209,7 +2209,7 @@ static const struct xdg_activation_token_v1_listener activation_listener_xdg = {
*
* As you might expect from Wayland, the general policy is to go with #2 unless
* the client can prove to the compositor beyond a reasonable doubt that raising
* the window will not be malicuous behavior.
* the window will not be malicious behavior.
*
* For SDL this means RaiseWindow and FlashWindow both use the same protocol,
* but in different ways: RaiseWindow will provide as _much_ information as
@ -3093,6 +3093,12 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
WAYLAND_wl_display_roundtrip(data->display);
}
/* The compositor should have relinquished keyboard, pointer, touch, and tablet tool focus when the toplevel
* window was destroyed upon being hidden, but there is no guarantee of this, so ensure that all references
* to the window held by seats are released before destroying the underlying surface and struct.
*/
Wayland_DisplayRemoveWindowReferencesFromSeats(data, wind);
#ifdef SDL_VIDEO_OPENGL_EGL
if (wind->egl_surface) {
SDL_EGL_DestroySurface(_this, wind->egl_surface);

View File

@ -132,9 +132,10 @@ struct SDL_WindowData
struct Wayland_SHMBuffer *icon_buffers;
int icon_buffer_count;
// Keyboard and pointer focus refcount.
// Keyboard, pointer, and touch focus refcount.
int keyboard_focus_count;
int pointer_focus_count;
int active_touch_count;
struct
{

View File

@ -193,7 +193,7 @@ static bool WIN_SetClipboardText(SDL_VideoDevice *_this, const char *mime_type)
clipboard_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &clipboard_data_size);
if (clipboard_data && clipboard_data_size > 0) {
SIZE_T i, size;
LPTSTR tstr = (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)clipboard_data, clipboard_data_size);
LPWSTR tstr = (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)clipboard_data, clipboard_data_size);
if (!tstr) {
return SDL_SetError("Couldn't convert text from UTF-8");
}
@ -210,7 +210,7 @@ static bool WIN_SetClipboardText(SDL_VideoDevice *_this, const char *mime_type)
// Save the data to the clipboard
hMem = GlobalAlloc(GMEM_MOVEABLE, size);
if (hMem) {
LPTSTR dst = (LPTSTR)GlobalLock(hMem);
LPWSTR dst = (LPWSTR)GlobalLock(hMem);
if (dst) {
// Copy the text over, adding carriage returns as necessary
for (i = 0; tstr[i]; ++i) {

View File

@ -1400,8 +1400,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
}
return 0;
} break;
case WM_LBUTTONUP:

View File

@ -46,7 +46,7 @@ static void WIN_UpdateDisplayMode(SDL_VideoDevice *_this, LPCWSTR deviceName, DW
data->DeviceMode.dmFields = (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS);
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition): No simple way to extract the assignment
if (index == ENUM_CURRENT_SETTINGS && (hdc = CreateDC(deviceName, NULL, NULL, NULL)) != NULL) {
if (index == ENUM_CURRENT_SETTINGS && (hdc = CreateDCW(deviceName, NULL, NULL, NULL)) != NULL) {
char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
LPBITMAPINFO bmi;
HBITMAP hbm;
@ -158,7 +158,7 @@ static void WIN_ReleaseDXGIOutput(void *dxgi_output)
#endif
}
static SDL_DisplayOrientation WIN_GetNaturalOrientation(DEVMODE *mode)
static SDL_DisplayOrientation WIN_GetNaturalOrientation(DEVMODEW *mode)
{
int width = mode->dmPelsWidth;
int height = mode->dmPelsHeight;
@ -177,7 +177,7 @@ static SDL_DisplayOrientation WIN_GetNaturalOrientation(DEVMODE *mode)
}
}
static SDL_DisplayOrientation WIN_GetDisplayOrientation(DEVMODE *mode)
static SDL_DisplayOrientation WIN_GetDisplayOrientation(DEVMODEW *mode)
{
if (WIN_GetNaturalOrientation(mode) == SDL_ORIENTATION_LANDSCAPE) {
switch (mode->dmDisplayOrientation) {
@ -208,7 +208,7 @@ static SDL_DisplayOrientation WIN_GetDisplayOrientation(DEVMODE *mode)
}
}
static void WIN_GetRefreshRate(void *dxgi_output, DEVMODE *mode, int *numerator, int *denominator)
static void WIN_GetRefreshRate(void *dxgi_output, DEVMODEW *mode, int *numerator, int *denominator)
{
// We're not currently using DXGI to query display modes, so fake NTSC timings
switch (mode->dmDisplayFrequency) {
@ -274,7 +274,7 @@ static float WIN_GetContentScale(SDL_VideoDevice *_this, HMONITOR hMonitor)
static bool WIN_GetDisplayMode(SDL_VideoDevice *_this, void *dxgi_output, HMONITOR hMonitor, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode, SDL_DisplayOrientation *natural_orientation, SDL_DisplayOrientation *current_orientation)
{
SDL_DisplayModeData *data;
DEVMODE devmode;
DEVMODEW devmode;
devmode.dmSize = sizeof(devmode);
devmode.dmDriverExtra = 0;

View File

@ -41,7 +41,7 @@ struct SDL_DisplayData
struct SDL_DisplayModeData
{
DEVMODE DeviceMode;
DEVMODEW DeviceMode;
};
extern bool WIN_InitModes(SDL_VideoDevice *_this);

View File

@ -1450,7 +1450,7 @@ void *WIN_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t
char *filename_utf8;
void *iccProfileData = NULL;
filename_utf8 = WIN_StringToUTF8(data->ICMFileName);
filename_utf8 = WIN_StringToUTF8W(data->ICMFileName);
if (filename_utf8) {
iccProfileData = SDL_LoadFile(filename_utf8, size);
if (!iccProfileData) {
@ -2061,7 +2061,7 @@ static STDMETHODIMP SDLDropTarget_Drop(SDLDropTarget *target,
". In Drop Text for GlobalLock, format %08x '%s', memory (%lu) %p",
fetc.cfFormat, format_mime, (unsigned long)bsize, buffer);
if (buffer) {
buffer = WIN_StringToUTF8((const wchar_t *)buffer);
buffer = WIN_StringToUTF8W((const wchar_t *)buffer);
if (buffer) {
const size_t lbuffer = SDL_strlen((const char *)buffer);
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
@ -2208,8 +2208,8 @@ void WIN_AcceptDragAndDrop(SDL_Window *window, bool accept)
drop_target->lpVtbl = vtDropTarget;
drop_target->window = window;
drop_target->hwnd = data->hwnd;
drop_target->format_file = RegisterClipboardFormat(L"text/uri-list");
drop_target->format_text = RegisterClipboardFormat(L"text/plain;charset=utf-8");
drop_target->format_file = RegisterClipboardFormatW(L"text/uri-list");
drop_target->format_text = RegisterClipboardFormatW(L"text/plain;charset=utf-8");
data->drop_target = drop_target;
SDLDropTarget_AddRef(drop_target);
RegisterDragDrop(data->hwnd, (LPDROPTARGET)drop_target);

View File

@ -492,17 +492,7 @@ static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data)
/* If another window has already processed a focus in, then don't try to
* remove focus here. Doing so will incorrectly remove focus from that
* window, and the focus lost event for this window will have already
* been dispatched anyway.
*/
if (data->tracking_mouse_outside_window && data->window == SDL_GetMouseFocus()) {
// If tracking the pointer and keyboard focus is lost, raise all buttons and relinquish mouse focus.
SDL_SendMouseButton(0, data->window, SDL_GLOBAL_MOUSE_ID, SDL_BUTTON_LEFT, false);
SDL_SendMouseButton(0, data->window, SDL_GLOBAL_MOUSE_ID, SDL_BUTTON_MIDDLE, false);
SDL_SendMouseButton(0, data->window, SDL_GLOBAL_MOUSE_ID, SDL_BUTTON_RIGHT, false);
SDL_SendMouseButton(0, data->window, SDL_GLOBAL_MOUSE_ID, SDL_BUTTON_X1, false);
SDL_SendMouseButton(0, data->window, SDL_GLOBAL_MOUSE_ID, SDL_BUTTON_X2, false);
SDL_SetMouseFocus(NULL);
}
* been dispatched anyway. */
if (data->window == SDL_GetKeyboardFocus()) {
SDL_SetKeyboardFocus(NULL);
}
@ -997,26 +987,29 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
}
}
if (!handled_by_ime) {
if (pressed) {
X11_HandleModifierKeys(videodata, scancode, true, true);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
text[text_length] = '\0';
X11_ClearComposition(windowdata);
SDL_SendKeyboardText(text);
}
} else {
if (X11_KeyRepeat(display, xevent)) {
// We're about to get a repeated key down, ignore the key up
return;
}
X11_HandleModifierKeys(videodata, scancode, false, true);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false);
}
}
if (pressed) {
X11_HandleModifierKeys(videodata, scancode, true, true);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
// Synthesize a text event if the IME didn't consume a printable character
if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
text[text_length] = '\0';
X11_ClearComposition(windowdata);
SDL_SendKeyboardText(text);
}
X11_UpdateUserTime(windowdata, xevent->xkey.time);
} else {
if (X11_KeyRepeat(display, xevent)) {
// We're about to get a repeated key down, ignore the key up
return;
}
X11_HandleModifierKeys(videodata, scancode, false, true);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false);
}
}
@ -1084,16 +1077,6 @@ void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata,
// see explanation at case ButtonPress
button -= (8 - SDL_BUTTON_X1);
}
/* If the mouse is captured and all buttons are now released, clear the capture
* flag so the focus will be cleared if the mouse is outside the window.
*/
if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) &&
!(SDL_GetMouseState(NULL, NULL) & ~SDL_BUTTON_MASK(button))) {
window->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
windowdata->tracking_mouse_outside_window = false;
}
SDL_SendMouseButton(timestamp, window, mouseID, button, false);
}
}
@ -1339,8 +1322,6 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
SDL_Log("Mode: NotifyUngrab");
}
#endif
data->tracking_mouse_outside_window = false;
SDL_SetMouseFocus(data->window);
mouse->last_x = xevent->xcrossing.x;
@ -1360,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;
@ -1387,17 +1370,14 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (xevent->xcrossing.mode != NotifyGrab &&
xevent->xcrossing.mode != NotifyUngrab &&
xevent->xcrossing.detail != NotifyInferior) {
if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
/* In order for interaction with the window decorations and menu to work properly
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);
}
SDL_SetMouseFocus(NULL);
} else {
data->tracking_mouse_outside_window = true;
/* In order for interaction with the window decorations and menu to work properly
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);
}
SDL_SetMouseFocus(NULL);
}
} break;

View File

@ -425,10 +425,12 @@ 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) {
@ -502,12 +504,16 @@ 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 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default)) {
}
#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;
} else {
}
#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

@ -25,6 +25,7 @@
#include <unistd.h> // For getpid() and readlink()
#include "../../core/linux/SDL_system_theme.h"
#include "../../core/linux/SDL_progressbar.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../SDL_pixels_c.h"
@ -204,6 +205,9 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
device->UpdateWindowShape = X11_UpdateWindowShape;
device->FlashWindow = X11_FlashWindow;
#ifdef SDL_USE_LIBDBUS
device->ApplyWindowProgress = DBUS_ApplyWindowProgress;
#endif // SDL_USE_LIBDBUS
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
device->SetWindowFocusable = X11_SetWindowFocusable;
device->SyncWindow = X11_SyncWindow;
@ -280,7 +284,7 @@ static SDL_VideoDevice *X11_CreateDevice(void)
* 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;
device->device_caps |= VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
}
data->is_xwayland = X11_IsXWayland(x11_display);

View File

@ -117,7 +117,6 @@ struct SDL_WindowData
bool fullscreen_borders_forced_on;
bool was_shown;
bool emit_size_move_after_property_notify;
bool tracking_mouse_outside_window;
SDL_HitTestResult hit_test_result;
XPoint xim_spot;

View File

@ -467,17 +467,15 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]);
}
}
} else {
} else if (!pointer_emulated && xev->deviceid == videodata->xinput_master_pointer_device) {
// Use the master device for non-relative motion, as the slave devices can seemingly lag behind.
SDL_Mouse *mouse = SDL_GetMouse();
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
if (!mouse->relative_mode && !pointer_emulated && window &&
(xev->deviceid == videodata->xinput_master_pointer_device || window->internal->tracking_mouse_outside_window)) {
/* Use the master device for non-relative motion, as the slave devices can seemingly lag behind, unless
* tracking the mouse outside the window, in which case the slave devices deliver coordinates, while the
* master does not.
*/
X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false);
SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y);
if (!mouse->relative_mode) {
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
if (window) {
X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false);
SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y);
}
}
}
} break;

View File

@ -2,6 +2,11 @@
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_test.h>
#ifdef SDL_PLATFORM_WINDOWS
#include <io.h>
#include <fcntl.h>
#endif
#include <stdio.h>
#include <errno.h>
@ -102,6 +107,11 @@ int main(int argc, char *argv[]) {
if (print_arguments) {
int print_i;
#ifdef SDL_PLATFORM_WINDOWS
/* reopen stdout as binary to prevent newline conversion */
_setmode(_fileno(stdout), _O_BINARY);
#endif
for (print_i = 0; i + print_i < argc; print_i++) {
fprintf(stdout, "|%d=%s|\r\n", print_i, argv[i + print_i]);
}

View File

@ -82,7 +82,7 @@ static int SDLCALL process_testArguments(void *arg)
"",
" ",
"a b c",
"a\tb\tc\t",
"a\tb\tc\t\v\r\n",
"\"a b\" c",
"'a' 'b' 'c'",
"%d%%%s",
@ -965,6 +965,165 @@ cleanup:
return TEST_COMPLETED;
}
static int process_testWindowsCmdline(void *arg)
{
TestProcessData *data = (TestProcessData *)arg;
const char *process_args[] = {
data->childprocess_path,
"--print-arguments",
"--",
"",
" ",
"a b c",
"a\tb\tc\t",
"\"a b\" c",
"'a' 'b' 'c'",
"%d%%%s",
"\\t\\c",
"evil\\",
"a\\b\"c\\",
"\"\\^&|<>%", /* characters with a special meaning */
NULL
};
/* this will have the same result as process_args, but escaped in a different way */
const char *process_cmdline_template =
"%s "
"--print-arguments "
"-- "
"\"\" "
"\" \" "
"a\" \"b\" \"c\t" /* using tab as delimiter */
"\"a\tb\tc\t\" "
"\"\"\"\"a b\"\"\" c\" "
"\"'a' 'b' 'c'\" "
"%%d%%%%%%s " /* will be passed to sprintf */
"\\t\\c "
"evil\\ "
"a\\b\"\\\"\"c\\ "
"\\\"\\^&|<>%%";
char process_cmdline[65535];
SDL_PropertiesID props;
SDL_Process *process = NULL;
char *buffer;
int exit_code;
int i;
size_t total_read = 0;
#ifndef SDL_PLATFORM_WINDOWS
SDLTest_AssertPass("SDL_PROP_PROCESS_CREATE_CMDLINE_STRING only works on Windows");
return TEST_SKIPPED;
#endif
props = SDL_CreateProperties();
SDLTest_AssertCheck(props != 0, "SDL_CreateProperties()");
if (!props) {
goto failed;
}
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN, true);
process = SDL_CreateProcessWithProperties(props);
SDLTest_AssertCheck(process == NULL, "SDL_CreateProcessWithProperties() should fail");
SDL_snprintf(process_cmdline, SDL_arraysize(process_cmdline), process_cmdline_template, data->childprocess_path);
SDL_SetStringProperty(props, SDL_PROP_PROCESS_CREATE_CMDLINE_STRING, process_cmdline);
process = SDL_CreateProcessWithProperties(props);
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()");
if (!process) {
goto failed;
}
exit_code = 0xdeadbeef;
buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code);
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
if (!buffer) {
goto failed;
}
SDLTest_LogEscapedString("stdout of process: ", buffer, total_read);
for (i = 3; process_args[i]; i++) {
char line[64];
SDL_snprintf(line, sizeof(line), "|%d=%s|", i - 3, process_args[i]);
SDLTest_AssertCheck(!!SDL_strstr(buffer, line), "Check %s is in output", line);
}
SDL_free(buffer);
SDLTest_AssertPass("About to destroy process");
SDL_DestroyProcess(process);
return TEST_COMPLETED;
failed:
SDL_DestroyProcess(process);
return TEST_ABORTED;
}
static int process_testWindowsCmdlinePrecedence(void *arg)
{
TestProcessData *data = (TestProcessData *)arg;
const char *process_args[] = {
data->childprocess_path,
"--print-arguments",
"--",
"argument 1",
NULL
};
const char *process_cmdline_template = "%s --print-arguments -- \"argument 2\"";
char process_cmdline[65535];
SDL_PropertiesID props;
SDL_Process *process = NULL;
char *buffer;
int exit_code;
size_t total_read = 0;
#ifndef SDL_PLATFORM_WINDOWS
SDLTest_AssertPass("SDL_PROP_PROCESS_CREATE_CMDLINE_STRING only works on Windows");
return TEST_SKIPPED;
#endif
props = SDL_CreateProperties();
SDLTest_AssertCheck(props != 0, "SDL_CreateProperties()");
if (!props) {
goto failed;
}
SDL_snprintf(process_cmdline, SDL_arraysize(process_cmdline), process_cmdline_template, data->childprocess_path);
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
SDL_SetStringProperty(props, SDL_PROP_PROCESS_CREATE_CMDLINE_STRING, (const char *)process_cmdline);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN, true);
process = SDL_CreateProcessWithProperties(props);
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()");
if (!process) {
goto failed;
}
exit_code = 0xdeadbeef;
buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code);
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
if (!buffer) {
goto failed;
}
SDLTest_LogEscapedString("stdout of process: ", buffer, total_read);
SDLTest_AssertCheck(!!SDL_strstr(buffer, "|0=argument 2|"), "Check |0=argument 2| is printed");
SDL_free(buffer);
SDLTest_AssertPass("About to destroy process");
SDL_DestroyProcess(process);
return TEST_COMPLETED;
failed:
SDL_DestroyProcess(process);
return TEST_ABORTED;
}
static const SDLTest_TestCaseReference processTestArguments = {
process_testArguments, "process_testArguments", "Test passing arguments to child process", TEST_ENABLED
};
@ -1017,6 +1176,14 @@ static const SDLTest_TestCaseReference processTestFileRedirection = {
process_testFileRedirection, "process_testFileRedirection", "Test redirection from/to files", TEST_ENABLED
};
static const SDLTest_TestCaseReference processTestWindowsCmdline = {
process_testWindowsCmdline, "process_testWindowsCmdline", "Test passing cmdline directly to CreateProcess", TEST_ENABLED
};
static const SDLTest_TestCaseReference processTestWindowsCmdlinePrecedence = {
process_testWindowsCmdlinePrecedence, "process_testWindowsCmdlinePrecedence", "Test SDL_PROP_PROCESS_CREATE_CMDLINE_STRING precedence over SDL_PROP_PROCESS_CREATE_ARGS_POINTER", TEST_ENABLED
};
static const SDLTest_TestCaseReference *processTests[] = {
&processTestArguments,
&processTestExitCode,
@ -1031,6 +1198,8 @@ static const SDLTest_TestCaseReference *processTests[] = {
&processTestNonExistingExecutable,
&processTestBatBadButVulnerability,
&processTestFileRedirection,
&processTestWindowsCmdline,
&processTestWindowsCmdlinePrecedence,
NULL
};