From c5b79e5f1beef72fc14bb9799876e18eac0a15c4 Mon Sep 17 00:00:00 2001 From: suleyth Date: Sat, 16 Aug 2025 11:56:47 +0200 Subject: [PATCH 001/134] Pick dedicated GPU with highest VRAM --- src/gpu/vulkan/SDL_gpu_vulkan.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index a1e074802a..894e1100e9 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -11252,13 +11252,14 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( VkPhysicalDevice physicalDevice, VulkanExtensions *physicalDeviceExtensions, Uint32 *queueFamilyIndex, - Uint8 *deviceRank) + Uint16 *deviceRank) { Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; VkQueueFamilyProperties *queueProps; bool supportsPresent; VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDeviceMemoryProperties deviceMemory; Uint32 i; const Uint8 *devicePriority = renderer->preferLowPower ? DEVICE_PRIORITY_LOWPOWER : DEVICE_PRIORITY_HIGHPERFORMANCE; @@ -11285,6 +11286,24 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } + /* If we prefer high performance, sum up all device local memory (rounded to megabytes) + * to deviceRank. In the niche case of someone having multiple dedicated GPUs in the same + * system, this theoretically picks the most powerful one (or at least the one with the + * most memory!) + */ + + renderer->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemory); + Uint64 videoMemory = 0; + for (i = 0; i < deviceMemory.memoryHeapCount; i++) { + VkMemoryHeap heap = deviceMemory.memoryHeaps[i]; + if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { + videoMemory += heap.size; + } + } + // Round it to megabytes (as per the vulkan spec videoMemory is in bytes) + Uint64 videoMemoryRounded = videoMemory / 1024 / 1024; + *deviceRank += videoMemoryRounded; + renderer->vkGetPhysicalDeviceFeatures( physicalDevice, &deviceFeatures); @@ -11392,7 +11411,7 @@ static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer) Uint32 i, physicalDeviceCount; Sint32 suitableIndex; Uint32 queueFamilyIndex, suitableQueueFamilyIndex; - Uint8 deviceRank, highestRank; + Uint16 deviceRank, highestRank; vulkanResult = renderer->vkEnumeratePhysicalDevices( renderer->instance, From 618f85fafe271bed66a9e057de90299e65258e99 Mon Sep 17 00:00:00 2001 From: suleyth Date: Sun, 17 Aug 2025 22:48:02 +0200 Subject: [PATCH 002/134] Make deviceRank an Uint64 to avoid potential overflow issues in case of future GPUs with huge VRAM --- src/gpu/vulkan/SDL_gpu_vulkan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 894e1100e9..9ae781e0bf 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -11252,7 +11252,7 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( VkPhysicalDevice physicalDevice, VulkanExtensions *physicalDeviceExtensions, Uint32 *queueFamilyIndex, - Uint16 *deviceRank) + Uint64 *deviceRank) { Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; VkQueueFamilyProperties *queueProps; @@ -11411,7 +11411,7 @@ static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer) Uint32 i, physicalDeviceCount; Sint32 suitableIndex; Uint32 queueFamilyIndex, suitableQueueFamilyIndex; - Uint16 deviceRank, highestRank; + Uint64 deviceRank, highestRank; vulkanResult = renderer->vkEnumeratePhysicalDevices( renderer->instance, From bad7075de875c90e4e593143c7589776d715228e Mon Sep 17 00:00:00 2001 From: suleyth Date: Sun, 17 Aug 2025 22:52:45 +0200 Subject: [PATCH 003/134] Only add VRAM to deviceRank if asking for a high performance device, and if the device already meets all the requirements. --- src/gpu/vulkan/SDL_gpu_vulkan.c | 42 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 9ae781e0bf..143a0f1356 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -11286,24 +11286,6 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } - /* If we prefer high performance, sum up all device local memory (rounded to megabytes) - * to deviceRank. In the niche case of someone having multiple dedicated GPUs in the same - * system, this theoretically picks the most powerful one (or at least the one with the - * most memory!) - */ - - renderer->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemory); - Uint64 videoMemory = 0; - for (i = 0; i < deviceMemory.memoryHeapCount; i++) { - VkMemoryHeap heap = deviceMemory.memoryHeaps[i]; - if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { - videoMemory += heap.size; - } - } - // Round it to megabytes (as per the vulkan spec videoMemory is in bytes) - Uint64 videoMemoryRounded = videoMemory / 1024 / 1024; - *deviceRank += videoMemoryRounded; - renderer->vkGetPhysicalDeviceFeatures( physicalDevice, &deviceFeatures); @@ -11399,6 +11381,30 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( return 0; } + /* If we prefer high performance, sum up all device local memory (rounded to megabytes) + * to deviceRank. In the niche case of someone having multiple dedicated GPUs in the same + * system, this theoretically picks the most powerful one (or at least the one with the + * most memory!) + * + * We do this *after* discarding all non suitable devices, which means if this computer + * has multiple dedicated GPUs that all meet our criteria, *and* the user asked for high + * performance, then we always pick the GPU with more VRAM. + */ + if (!renderer->preferLowPower) { + renderer->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemory); + Uint64 videoMemory = 0; + for (i = 0; i < deviceMemory.memoryHeapCount; i++) { + VkMemoryHeap heap = deviceMemory.memoryHeaps[i]; + if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) { + videoMemory += heap.size; + } + } + // Round it to megabytes (as per the vulkan spec videoMemory is in bytes) + Uint64 videoMemoryRounded = videoMemory / 1024 / 1024; + *deviceRank += videoMemoryRounded; + } + + // FIXME: Need better structure for checking vs storing swapchain support details return 1; } From e3db7f83c2500c841151595f9c49c255ed34cde0 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 19 Aug 2025 04:41:07 +0000 Subject: [PATCH 004/134] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_render.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index bfb758235f..29ea295ea2 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -283,7 +283,7 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window * present synchronized with the refresh rate. This property can take any * value that is supported by SDL_SetRenderVSync() for the renderer. * - * With the SDL GPU renderer: + * With the SDL GPU renderer (since SDL 3.4.0): * * - `SDL_PROP_RENDERER_CREATE_GPU_SHADERS_SPIRV_BOOLEAN`: the app is able to * provide SPIR-V shaders to SDL_GPURenderState, optional. From 03b14f5211bcf4c6e7ef150a4ceb387f276d0c19 Mon Sep 17 00:00:00 2001 From: Wilson Jallet Date: Tue, 19 Aug 2025 21:10:55 +0200 Subject: [PATCH 005/134] GPU: Fix Vulkan indexing error for resolve attachment refs (#13768) --- src/gpu/vulkan/SDL_gpu_vulkan.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 143a0f1356..e8b9b21406 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -6065,7 +6065,6 @@ static VkRenderPass VULKAN_INTERNAL_CreateRenderPass( colorAttachmentReferences[colorAttachmentReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentDescriptionCount += 1; - colorAttachmentReferenceCount += 1; if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) { VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture; @@ -6080,12 +6079,16 @@ static VkRenderPass VULKAN_INTERNAL_CreateRenderPass( attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount; - resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + resolveReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount; + resolveReferences[colorAttachmentReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentDescriptionCount += 1; resolveReferenceCount += 1; + } else { + resolveReferences[colorAttachmentReferenceCount].attachment = VK_ATTACHMENT_UNUSED; } + + colorAttachmentReferenceCount += 1; } subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; From 0b082b25b2b0d07d98fede9311d01bc2c5ed7995 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 19 Aug 2025 20:39:22 +0000 Subject: [PATCH 006/134] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_gpu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h index 85e63baf6d..a1d686176f 100644 --- a/include/SDL3/SDL_gpu.h +++ b/include/SDL3/SDL_gpu.h @@ -3755,6 +3755,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnmapGPUTransferBuffer( * \returns a copy pass handle. * * \since This function is available since SDL 3.2.0. + * + * \sa SDL_EndGPUCopyPass */ extern SDL_DECLSPEC SDL_GPUCopyPass * SDLCALL SDL_BeginGPUCopyPass( SDL_GPUCommandBuffer *command_buffer); From ee5e249008b516c4efeb807fd8c9079c19732e53 Mon Sep 17 00:00:00 2001 From: "arnau.nau" Date: Tue, 19 Aug 2025 01:38:47 +0200 Subject: [PATCH 007/134] Docs fix for SDL_stdinc.h: floor,floorf,ceil,ceilf --- include/SDL3/SDL_stdinc.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index 21266e81e4..fa8c9a50e8 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/include/SDL3/SDL_stdinc.h @@ -4722,7 +4722,7 @@ extern SDL_DECLSPEC float SDLCALL SDL_atan2f(float y, float x); /** * Compute the ceiling of `x`. * - * The ceiling of `x` is the smallest integer `y` such that `y > x`, i.e `x` + * The ceiling of `x` is the smallest integer `y` such that `y >= x`, i.e `x` * rounded up to the nearest integer. * * Domain: `-INF <= x <= INF` @@ -4750,7 +4750,7 @@ extern SDL_DECLSPEC double SDLCALL SDL_ceil(double x); /** * Compute the ceiling of `x`. * - * The ceiling of `x` is the smallest integer `y` such that `y > x`, i.e `x` + * The ceiling of `x` is the smallest integer `y` such that `y >= x`, i.e `x` * rounded up to the nearest integer. * * Domain: `-INF <= x <= INF` @@ -4992,7 +4992,7 @@ extern SDL_DECLSPEC float SDLCALL SDL_fabsf(float x); /** * Compute the floor of `x`. * - * The floor of `x` is the largest integer `y` such that `y > x`, i.e `x` + * The floor of `x` is the largest integer `y` such that `y <= x`, i.e `x` * rounded down to the nearest integer. * * Domain: `-INF <= x <= INF` @@ -5020,7 +5020,7 @@ extern SDL_DECLSPEC double SDLCALL SDL_floor(double x); /** * Compute the floor of `x`. * - * The floor of `x` is the largest integer `y` such that `y > x`, i.e `x` + * The floor of `x` is the largest integer `y` such that `y <= x`, i.e `x` * rounded down to the nearest integer. * * Domain: `-INF <= x <= INF` From dee2414ee7759c709162eabad4e53cfe47c8cdd4 Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Sat, 16 Aug 2025 09:09:20 +0000 Subject: [PATCH 008/134] Fix typo in SDL_power doc comment --- include/SDL3/SDL_power.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SDL3/SDL_power.h b/include/SDL3/SDL_power.h index 694fb0924d..bc2d9d53ef 100644 --- a/include/SDL3/SDL_power.h +++ b/include/SDL3/SDL_power.h @@ -88,8 +88,8 @@ typedef enum SDL_PowerState * can't determine a value or there is no battery. * \param percent a pointer filled in with the percentage of battery life * left, between 0 and 100, or NULL to ignore. This will be - * filled in with -1 we can't determine a value or there is no - * battery. + * filled in with -1 when we can't determine a value or there + * is no battery. * \returns the current battery state or `SDL_POWERSTATE_ERROR` on failure; * call SDL_GetError() for more information. * From 0fcaf47658be96816a851028af3e73256363a390 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 22 Aug 2025 14:32:28 -0400 Subject: [PATCH 009/134] gpu: D3D12 only requires feature level 11_0 with Resource Binding Tier 2. We previously thought this wasn't possible because constant buffer offsets and partial updates were unavailable, but we were reading the wrong table - this is only the case for D3D11... https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-downlevel-intro ... while 12 doesn't list this feature at all: https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels We double checked and Jesse Natalie confirmed that this feature is required for D3D12 even for 11_0 drivers. (Thanks Jesse!) Additionally, D3D12 requires that UAVs are accessible from all shader stages, meaning Tier 2 is enough to support the number of UAVs we need. Tier 1 could be a property to lower the requirements, but that can be done later. --- include/SDL3/SDL_gpu.h | 3 ++- src/gpu/d3d12/SDL_gpu_d3d12.c | 39 ++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h index a1d686176f..c3c3f66310 100644 --- a/include/SDL3/SDL_gpu.h +++ b/include/SDL3/SDL_gpu.h @@ -235,7 +235,8 @@ * SDL driver name: "direct3d12" * * Supported on Windows 10 or newer, Xbox One (GDK), and Xbox Series X|S - * (GDK). Requires a GPU that supports DirectX 12 Feature Level 11_1. + * (GDK). Requires a GPU that supports DirectX 12 Feature Level 11_0 and + * Resource Binding Tier 2 or above. * * ### Metal * diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c index 13a15132f5..c277ced36c 100644 --- a/src/gpu/d3d12/SDL_gpu_d3d12.c +++ b/src/gpu/d3d12/SDL_gpu_d3d12.c @@ -109,8 +109,8 @@ #define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface" #define D3D12_GET_DEBUG_INTERFACE_FUNC "D3D12GetDebugInterface" #define WINDOW_PROPERTY_DATA "SDL_GPUD3D12WindowPropertyData" -#define D3D_FEATURE_LEVEL_CHOICE D3D_FEATURE_LEVEL_11_1 -#define D3D_FEATURE_LEVEL_CHOICE_STR "11_1" +#define D3D_FEATURE_LEVEL_CHOICE D3D_FEATURE_LEVEL_11_0 +#define D3D_FEATURE_LEVEL_CHOICE_STR "11_0" #define MAX_ROOT_SIGNATURE_PARAMETERS 64 #define D3D12_FENCE_UNSIGNALED_VALUE 0 #define D3D12_FENCE_SIGNAL_VALUE 1 @@ -8347,6 +8347,7 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props) IDXGIFactory4 *factory4; IDXGIFactory6 *factory6; IDXGIAdapter1 *adapter; + bool supports_64UAVs = false; // Early check to see if the app has _any_ D3D12 formats, if not we don't // have to fuss with loading D3D in the first place. @@ -8446,12 +8447,39 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props) return false; } + SDL_COMPILE_TIME_ASSERT(featurelevel, D3D_FEATURE_LEVEL_CHOICE < D3D_FEATURE_LEVEL_11_1); + + // Check feature level 11_1 first, guarantees 64+ UAVs unlike 11_0 Tier1 res = D3D12CreateDeviceFunc( (IUnknown *)adapter, - D3D_FEATURE_LEVEL_CHOICE, + D3D_FEATURE_LEVEL_11_1, D3D_GUID(D3D_IID_ID3D12Device), (void **)&device); + if (SUCCEEDED(res)) { + supports_64UAVs = true; + } else { + res = D3D12CreateDeviceFunc( + (IUnknown *)adapter, + D3D_FEATURE_LEVEL_CHOICE, + D3D_GUID(D3D_IID_ID3D12Device), + (void **)&device); + + if (SUCCEEDED(res)) { + D3D12_FEATURE_DATA_D3D12_OPTIONS featureOptions; + SDL_zero(featureOptions); + + res = ID3D12Device_CheckFeatureSupport( + device, + D3D12_FEATURE_D3D12_OPTIONS, + &featureOptions, + sizeof(featureOptions)); + if (SUCCEEDED(res) && featureOptions.ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_2) { + supports_64UAVs = true; + } + } + } + if (SUCCEEDED(res)) { D3D12_FEATURE_DATA_SHADER_MODEL shaderModel; shaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_0; @@ -8473,6 +8501,11 @@ static bool D3D12_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props) SDL_UnloadObject(d3d12Dll); SDL_UnloadObject(dxgiDll); + if (!supports_64UAVs) { + SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: Tier 2 Resource binding is not supported"); + return false; + } + if (!supports_dxil && !has_dxbc) { SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "D3D12: DXIL is not supported and DXBC is not being provided"); return false; From 270737584e95915a3af2dd1456fe57833d60fe1b Mon Sep 17 00:00:00 2001 From: ceski <56656010+ceski-1@users.noreply.github.com> Date: Mon, 18 Aug 2025 20:05:08 -0700 Subject: [PATCH 010/134] Fix vsync-off support for direct3d11 --- CMakeLists.txt | 1 + include/build_config/SDL_build_config.h.cmake | 1 + .../build_config/SDL_build_config_windows.h | 1 + .../build_config/SDL_build_config_wingdk.h | 1 + src/render/direct3d11/SDL_render_d3d11.c | 57 +++++++++++++++++-- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 27864de8a6..c67d875ab2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2172,6 +2172,7 @@ elseif(WINDOWS) #include int main(int argc, char **argv) { return 0; }" HAVE_GAMEINPUT_H ) + check_include_file(dxgi1_5.h HAVE_DXGI1_5_H) check_include_file(dxgi1_6.h HAVE_DXGI1_6_H) check_include_file(tpcshrd.h HAVE_TPCSHRD_H) check_include_file(roapi.h HAVE_ROAPI_H) diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 8965e6db48..cc491526e5 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -223,6 +223,7 @@ #cmakedefine HAVE_WINDOWS_GAMING_INPUT_H 1 #cmakedefine HAVE_GAMEINPUT_H 1 #cmakedefine HAVE_DXGI_H 1 +#cmakedefine HAVE_DXGI1_5_H 1 #cmakedefine HAVE_DXGI1_6_H 1 #cmakedefine HAVE_MMDEVICEAPI_H 1 diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 98079cb178..7872d2a928 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -85,6 +85,7 @@ typedef unsigned int uintptr_t; #define HAVE_DXGI_H 1 #define HAVE_XINPUT_H 1 #if defined(_WIN32_MAXVER) && _WIN32_MAXVER >= 0x0A00 /* Windows 10 SDK */ +#define HAVE_DXGI1_5_H 1 #define HAVE_DXGI1_6_H 1 #define HAVE_WINDOWS_GAMING_INPUT_H 1 #endif diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index a5fb7e497a..734fb6c6c6 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -37,6 +37,7 @@ #define HAVE_DSOUND_H 1 /* No SDK version checks needed for these because the SDK has to be new. */ #define HAVE_DXGI_H 1 +#define HAVE_DXGI1_5_H 1 #define HAVE_DXGI1_6_H 1 #define HAVE_XINPUT_H 1 #define HAVE_WINDOWS_GAMING_INPUT_H 1 diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 12fd5abc45..39fc94dbe8 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -30,7 +30,11 @@ #include "../../video/SDL_pixels_c.h" #include +#ifdef HAVE_DXGI1_5_H +#include +#else #include +#endif #include #include "SDL_shaders_d3d11.h" @@ -157,6 +161,7 @@ typedef struct ID3D11DeviceContext1 *d3dContext; IDXGISwapChain1 *swapChain; DXGI_SWAP_EFFECT swapEffect; + UINT swapChainFlags; UINT syncInterval; UINT presentFlags; ID3D11RenderTargetView *mainRenderTargetView; @@ -206,6 +211,9 @@ typedef struct #pragma GCC diagnostic ignored "-Wunused-const-variable" #endif +#ifdef HAVE_DXGI1_5_H +static const GUID SDL_IID_IDXGIFactory5 = { 0x7632e1f5, 0xee65, 0x4dca, { 0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d } }; +#endif static const GUID SDL_IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } }; static const GUID SDL_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } }; static const GUID SDL_IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c } }; @@ -511,6 +519,9 @@ static HRESULT D3D11_CreateDeviceResources(SDL_Renderer *renderer) HRESULT result = S_OK; UINT creationFlags = 0; bool createDebug; +#ifdef HAVE_DXGI1_5_H + IDXGIFactory5 *dxgiFactory5 = NULL; +#endif /* This array defines the set of DirectX hardware feature levels this app will support. * Note the ordering should be preserved. @@ -601,6 +612,20 @@ static HRESULT D3D11_CreateDeviceResources(SDL_Renderer *renderer) goto done; } +#ifdef HAVE_DXGI1_5_H + // Check for tearing support, which requires the IDXGIFactory5 interface. + data->swapChainFlags = 0; + result = IDXGIFactory2_QueryInterface(data->dxgiFactory, &SDL_IID_IDXGIFactory5, (void **)&dxgiFactory5); + if (SUCCEEDED(result)) { + BOOL allowTearing = FALSE; + result = IDXGIFactory5_CheckFeatureSupport(dxgiFactory5, DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing)); + if (SUCCEEDED(result) && allowTearing) { + data->swapChainFlags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + IDXGIFactory5_Release(dxgiFactory5); + } +#endif // HAVE_DXGI1_5_H + // FIXME: Should we use the default adapter? result = IDXGIFactory2_EnumAdapters(data->dxgiFactory, 0, &data->dxgiAdapter); if (FAILED(result)) { @@ -870,7 +895,7 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } else { swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. } - swapChainDesc.Flags = 0; + swapChainDesc.Flags = data->swapChainFlags; if (coreWindow) { result = IDXGIFactory2_CreateSwapChainForCoreWindow(data->dxgiFactory, @@ -956,6 +981,28 @@ static void D3D11_ReleaseMainRenderTargetView(SDL_Renderer *renderer) SAFE_RELEASE(data->mainRenderTargetView); } +static void D3D11_UpdatePresentFlags(SDL_Renderer *renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal; + + if (data->syncInterval > 0) { + data->presentFlags = 0; + } else { + data->presentFlags = DXGI_PRESENT_DO_NOT_WAIT; +#ifdef HAVE_DXGI1_5_H + // Present tearing requires sync interval 0, a swap chain flag, and not in exclusive fullscreen mode. + if ((data->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)) { + HRESULT result = S_OK; + BOOL fullscreenState = FALSE; + result = IDXGISwapChain_GetFullscreenState(data->swapChain, &fullscreenState, NULL); + if (SUCCEEDED(result) && !fullscreenState) { + data->presentFlags = DXGI_PRESENT_ALLOW_TEARING; + } + } +#endif // HAVE_DXGI1_5_H + } +} + // Initialize all resources that change when the window's size changes. static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer *renderer) { @@ -985,7 +1032,7 @@ static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer *renderer) 0, w, h, DXGI_FORMAT_UNKNOWN, - 0); + data->swapChainFlags); if (FAILED(result)) { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain::ResizeBuffers"), result); goto done; @@ -997,6 +1044,8 @@ static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer *renderer) } } + D3D11_UpdatePresentFlags(renderer); + // Set the proper rotation for the swap chain. if (WIN_IsWindows8OrGreater()) { if (data->swapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) { @@ -2663,11 +2712,10 @@ static bool D3D11_SetVSync(SDL_Renderer *renderer, const int vsync) if (vsync > 0) { data->syncInterval = vsync; - data->presentFlags = 0; } else { data->syncInterval = 0; - data->presentFlags = DXGI_PRESENT_DO_NOT_WAIT; } + D3D11_UpdatePresentFlags(renderer); return true; } @@ -2733,6 +2781,7 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); + data->swapChainFlags = 0; data->syncInterval = 0; data->presentFlags = DXGI_PRESENT_DO_NOT_WAIT; From aab95894a6fbf547a5cdd3f3fb2cc1c8f228ee87 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 25 Aug 2025 08:54:43 -0700 Subject: [PATCH 011/134] Fixed crash if mouse functions are used after video quit --- src/events/SDL_mouse.c | 6 ++++++ src/video/cocoa/SDL_cocoamouse.m | 13 +++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 54611bf36d..170e4335b3 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -1168,6 +1168,12 @@ void SDL_QuitMouse(void) } SDL_free(SDL_mice); SDL_mice = NULL; + + if (mouse->internal) { + SDL_free(mouse->internal); + mouse->internal = NULL; + } + SDL_zerop(mouse); } bool SDL_SetRelativeMouseTransform(SDL_MouseMotionTransformCallback transform, void *userdata) diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m index f8f582972f..cbd935335e 100644 --- a/src/video/cocoa/SDL_cocoamouse.m +++ b/src/video/cocoa/SDL_cocoamouse.m @@ -373,12 +373,12 @@ bool Cocoa_InitMouse(SDL_VideoDevice *_this) { NSPoint location; SDL_Mouse *mouse = SDL_GetMouse(); - SDL_MouseData *internal = (SDL_MouseData *)SDL_calloc(1, sizeof(SDL_MouseData)); - if (internal == NULL) { + SDL_MouseData *data = (SDL_MouseData *)SDL_calloc(1, sizeof(SDL_MouseData)); + if (data == NULL) { return false; } - mouse->internal = internal; + mouse->internal = data; mouse->CreateCursor = Cocoa_CreateCursor; mouse->CreateSystemCursor = Cocoa_CreateSystemCursor; mouse->ShowCursor = Cocoa_ShowCursor; @@ -585,13 +585,6 @@ void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y) void Cocoa_QuitMouse(SDL_VideoDevice *_this) { - SDL_Mouse *mouse = SDL_GetMouse(); - if (mouse) { - if (mouse->internal) { - SDL_free(mouse->internal); - mouse->internal = NULL; - } - } } #endif // SDL_VIDEO_DRIVER_COCOA From 7bbbbb3a1c603b9411accf7e65eb28c335c5222b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 25 Aug 2025 09:45:39 -0700 Subject: [PATCH 012/134] Fixed build --- src/video/cocoa/SDL_cocoamouse.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m index cbd935335e..fbf2b06ad2 100644 --- a/src/video/cocoa/SDL_cocoamouse.m +++ b/src/video/cocoa/SDL_cocoamouse.m @@ -392,8 +392,8 @@ bool Cocoa_InitMouse(SDL_VideoDevice *_this) SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor()); location = [NSEvent mouseLocation]; - internal->lastMoveX = location.x; - internal->lastMoveY = location.y; + data->lastMoveX = location.x; + data->lastMoveY = location.y; return true; } From 226fecff78f312b02d58a31de065a5cc7ae0c7bc Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 25 Aug 2025 11:31:12 -0400 Subject: [PATCH 013/134] audio: Split current_audio.device_hash into two separate hashtables. One for physical devices, one for logical devices. Fixes #13032. --- src/audio/SDL_audio.c | 87 +++++++++++++++++++++++----------------- src/audio/SDL_sysaudio.h | 5 ++- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 8b0ecab202..f59114d094 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -411,7 +411,7 @@ static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, const bool islogical = !(devid & (1<<1)); if (islogical) { // don't bother looking if it's not a logical device id value. SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &logdev); + SDL_FindInHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) devid, (const void **) &logdev); if (logdev) { SDL_assert(logdev->instance_id == devid); device = logdev->physical_device; @@ -462,8 +462,8 @@ static SDL_AudioDevice *ObtainPhysicalAudioDevice(SDL_AudioDeviceID devid) // ! SDL_SetError("Audio subsystem is not initialized"); } 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_FindInHashTable(current_audio.device_hash_physical, (const void *) (uintptr_t) devid, (const void **) &device); + SDL_assert(!device || (device->instance_id == devid)); SDL_UnlockRWLock(current_audio.device_hash_lock); if (!device) { @@ -529,9 +529,9 @@ static SDL_AudioDevice *ObtainPhysicalAudioDeviceDefaultAllowed(SDL_AudioDeviceI static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev) { // Remove ourselves from the device_hash hashtable. - if (current_audio.device_hash) { // will be NULL while shutting down. + if (current_audio.device_hash_logical) { // will be NULL while shutting down. SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_RemoveFromHashTable(current_audio.device_hash, (const void *) (uintptr_t) logdev->instance_id); + SDL_RemoveFromHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) logdev->instance_id); SDL_UnlockRWLock(current_audio.device_hash_lock); } @@ -594,7 +594,7 @@ void UnrefPhysicalAudioDevice(SDL_AudioDevice *device) if (SDL_AtomicDecRef(&device->refcount)) { // take it out of the device list. SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (SDL_RemoveFromHashTable(current_audio.device_hash, (const void *) (uintptr_t) device->instance_id)) { + if (SDL_RemoveFromHashTable(current_audio.device_hash_physical, (const void *) (uintptr_t) device->instance_id)) { SDL_AddAtomicInt(device->recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count, -1); } SDL_UnlockRWLock(current_audio.device_hash_lock); @@ -656,7 +656,7 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recordi device->instance_id = AssignAudioDeviceInstanceId(recording, /*islogical=*/false); SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (SDL_InsertIntoHashTable(current_audio.device_hash, (const void *) (uintptr_t) device->instance_id, device, false)) { + if (SDL_InsertIntoHashTable(current_audio.device_hash_physical, (const void *) (uintptr_t) device->instance_id, device, false)) { SDL_AddAtomicInt(device_count, 1); } else { SDL_DestroyCondition(device->close_cond); @@ -885,7 +885,8 @@ static bool SDLCALL FindLowestDeviceID(void *userdata, const SDL_HashTable *tabl // bit #1 of devid is set for physical devices and unset for logical. const bool devid_recording = !(devid & (1 << 0)); const bool isphysical = !!(devid & (1 << 1)); - if (isphysical && (devid_recording == data->recording) && (devid < data->highest)) { + SDL_assert(isphysical); // should only be iterating device_hash_physical. + if ((devid_recording == data->recording) && (devid < data->highest)) { data->highest = devid; data->result = (SDL_AudioDevice *) value; SDL_assert(data->result->instance_id == devid); @@ -900,7 +901,7 @@ static SDL_AudioDevice *GetFirstAddedAudioDevice(const bool recording) // (Device IDs increase as new devices are added, so the first device added has the lowest SDL_AudioDeviceID value.) FindLowestDeviceIDData data = { recording, highest, NULL }; SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_IterateHashTable(current_audio.device_hash, FindLowestDeviceID, &data); + SDL_IterateHashTable(current_audio.device_hash_physical, FindLowestDeviceID, &data); SDL_UnlockRWLock(current_audio.device_hash_lock); return data.result; } @@ -930,8 +931,15 @@ bool SDL_InitAudio(const char *driver_name) return false; } - SDL_HashTable *device_hash = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); - if (!device_hash) { + SDL_HashTable *device_hash_physical = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); + if (!device_hash_physical) { + SDL_DestroyRWLock(device_hash_lock); + return false; + } + + SDL_HashTable *device_hash_logical = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); + if (!device_hash_logical) { + SDL_DestroyHashTable(device_hash_physical); SDL_DestroyRWLock(device_hash_lock); return false; } @@ -950,7 +958,8 @@ bool SDL_InitAudio(const char *driver_name) if (!driver_name_copy) { SDL_DestroyRWLock(device_hash_lock); - SDL_DestroyHashTable(device_hash); + SDL_DestroyHashTable(device_hash_physical); + SDL_DestroyHashTable(device_hash_logical); return false; } @@ -973,7 +982,8 @@ bool SDL_InitAudio(const char *driver_name) SDL_zero(current_audio); current_audio.pending_events_tail = ¤t_audio.pending_events; current_audio.device_hash_lock = device_hash_lock; - current_audio.device_hash = device_hash; + current_audio.device_hash_physical = device_hash_physical; + current_audio.device_hash_logical = device_hash_logical; if (bootstrap[i]->init(¤t_audio.impl)) { current_audio.name = bootstrap[i]->name; current_audio.desc = bootstrap[i]->desc; @@ -997,7 +1007,8 @@ bool SDL_InitAudio(const char *driver_name) SDL_zero(current_audio); current_audio.pending_events_tail = ¤t_audio.pending_events; current_audio.device_hash_lock = device_hash_lock; - current_audio.device_hash = device_hash; + current_audio.device_hash_physical = device_hash_physical; + current_audio.device_hash_logical = device_hash_logical; if (bootstrap[i]->init(¤t_audio.impl)) { current_audio.name = bootstrap[i]->name; current_audio.desc = bootstrap[i]->desc; @@ -1019,7 +1030,8 @@ bool SDL_InitAudio(const char *driver_name) } SDL_DestroyRWLock(device_hash_lock); - SDL_DestroyHashTable(device_hash); + SDL_DestroyHashTable(device_hash_physical); + SDL_DestroyHashTable(device_hash_logical); SDL_zero(current_audio); return false; // No driver was available, so fail. } @@ -1058,12 +1070,10 @@ static bool SDLCALL DestroyOnePhysicalAudioDevice(void *userdata, const SDL_Hash // bit #1 of devid is set for physical devices and unset for logical. const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; const bool isphysical = !!(devid & (1<<1)); - if (isphysical) { - SDL_AudioDevice *dev = (SDL_AudioDevice *) value; - - SDL_assert(dev->instance_id == devid); - DestroyPhysicalAudioDevice(dev); - } + SDL_assert(isphysical); + SDL_AudioDevice *dev = (SDL_AudioDevice *) value; + SDL_assert(dev->instance_id == devid); + DestroyPhysicalAudioDevice(dev); return true; // keep iterating. } @@ -1089,8 +1099,9 @@ void SDL_QuitAudio(void) SDL_LockRWLockForWriting(current_audio.device_hash_lock); SDL_SetAtomicInt(¤t_audio.shutting_down, 1); - SDL_HashTable *device_hash = current_audio.device_hash; - current_audio.device_hash = NULL; + SDL_HashTable *device_hash_physical = current_audio.device_hash_physical; + SDL_HashTable *device_hash_logical = current_audio.device_hash_logical; + current_audio.device_hash_physical = current_audio.device_hash_logical = NULL; SDL_PendingAudioDeviceEvent *pending_events = current_audio.pending_events.next; current_audio.pending_events.next = NULL; SDL_SetAtomicInt(¤t_audio.playback_device_count, 0); @@ -1103,13 +1114,15 @@ void SDL_QuitAudio(void) SDL_free(i); } - SDL_IterateHashTable(device_hash, DestroyOnePhysicalAudioDevice, NULL); + SDL_IterateHashTable(device_hash_physical, DestroyOnePhysicalAudioDevice, NULL); + // device_hash_* will _not_ be empty because we nulled them out in current_audio, but all their items are now free'd pointers. Just destroy the hashes, below. // Free the driver data current_audio.impl.Deinitialize(); SDL_DestroyRWLock(current_audio.device_hash_lock); - SDL_DestroyHashTable(device_hash); + SDL_DestroyHashTable(device_hash_physical); + SDL_DestroyHashTable(device_hash_logical); SDL_zero(current_audio); } @@ -1421,7 +1434,8 @@ static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table // bit #1 of devid is set for physical devices and unset for logical. const bool devid_recording = !(devid & (1<<0)); const bool isphysical = !!(devid & (1<<1)); - if (isphysical && (devid_recording == data->recording)) { + SDL_assert(isphysical); + if (devid_recording == data->recording) { SDL_assert(data->devs_seen < data->num_devices); SDL_AudioDevice *device = (SDL_AudioDevice *) value; // this is normally risky, but we hold the device_hash_lock here. const bool zombie = SDL_GetAtomicInt(&device->zombie) != 0; @@ -1446,7 +1460,7 @@ static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) result = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID)); if (result) { CountAudioDevicesData data = { 0, 0, num_devices, result, recording }; - SDL_IterateHashTable(current_audio.device_hash, CountAudioDevices, &data); + SDL_IterateHashTable(current_audio.device_hash_physical, CountAudioDevices, &data); SDL_assert((data.devs_seen + data.devs_skipped) == num_devices); num_devices = data.devs_seen; // might be less if we skipped any. result[num_devices] = 0; // null-terminated. @@ -1490,13 +1504,12 @@ static bool SDLCALL FindAudioDeviceByCallback(void *userdata, const SDL_HashTabl const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; // bit #1 of devid is set for physical devices and unset for logical. const bool isphysical = !!(devid & (1<<1)); - if (isphysical) { - 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. - } + SDL_assert(isphysical); + 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. } return true; // keep iterating. } @@ -1511,7 +1524,7 @@ SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_Audi FindAudioDeviceByCallbackData data = { callback, userdata, NULL }; SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_IterateHashTable(current_audio.device_hash, FindAudioDeviceByCallback, &data); + SDL_IterateHashTable(current_audio.device_hash_physical, FindAudioDeviceByCallback, &data); SDL_UnlockRWLock(current_audio.device_hash_lock); if (!data.retval) { @@ -1546,7 +1559,7 @@ 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, &vdev); + SDL_FindInHashTable(islogical ? current_audio.device_hash_logical : current_audio.device_hash_physical, (const void *) (uintptr_t) devid, &vdev); if (!vdev) { SDL_SetError("Invalid audio device instance ID"); } else if (islogical) { @@ -1872,7 +1885,7 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp if (result) { SDL_LockRWLockForWriting(current_audio.device_hash_lock); - const bool inserted = SDL_InsertIntoHashTable(current_audio.device_hash, (const void *) (uintptr_t) result, logdev, false); + const bool inserted = SDL_InsertIntoHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) result, logdev, false); SDL_UnlockRWLock(current_audio.device_hash_lock); if (!inserted) { SDL_CloseAudioDevice(result); diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 3b3cb9b37e..b181a65359 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -183,8 +183,9 @@ typedef struct SDL_AudioDriver const char *name; // The name of this audio driver const char *desc; // The description of this audio driver SDL_AudioDriverImpl impl; // the backend's interface - SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash` - SDL_HashTable *device_hash; // the collection of currently-available audio devices (recording, playback, logical and physical!) + SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash*` and some other things. + SDL_HashTable *device_hash_physical; // the collection of currently-available audio devices (recording and playback), for mapping SDL_AudioDeviceID to an SDL_AudioDevice*. + SDL_HashTable *device_hash_logical; // the collection of currently-available audio devices (recording and playback), for mapping SDL_AudioDeviceID to an SDL_LogicalAudioDevice*. SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams. SDL_AudioDeviceID default_playback_device_id; SDL_AudioDeviceID default_recording_device_id; From 01d94ca9defa60cdecd67cea476e88f55de6b692 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 25 Aug 2025 11:35:37 -0400 Subject: [PATCH 014/134] audio: Renamed device_hash_lock to subsystem_rwlock. It protects more than the device hashes! --- src/audio/SDL_audio.c | 132 +++++++++++++++++++-------------------- src/audio/SDL_sysaudio.h | 2 +- 2 files changed, 66 insertions(+), 68 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index f59114d094..476c0528b5 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -186,16 +186,15 @@ void OnAudioStreamCreated(SDL_AudioStream *stream) // NOTE that you can create an audio stream without initializing the audio subsystem, // but it will not be automatically destroyed during a later call to SDL_Quit! // You must explicitly destroy it yourself! - if (current_audio.device_hash_lock) { - // this isn't really part of the "device list" but it's a convenient lock to use here. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + if (current_audio.subsystem_rwlock) { + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); if (current_audio.existing_streams) { current_audio.existing_streams->prev = stream; } stream->prev = NULL; stream->next = current_audio.existing_streams; current_audio.existing_streams = stream; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } } @@ -206,9 +205,8 @@ void OnAudioStreamDestroy(SDL_AudioStream *stream) // NOTE that you can create an audio stream without initializing the audio subsystem, // but it will not be automatically destroyed during a later call to SDL_Quit! // You must explicitly destroy it yourself! - if (current_audio.device_hash_lock) { - // this isn't really part of the "device list" but it's a convenient lock to use here. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + if (current_audio.subsystem_rwlock) { + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); if (stream->prev) { stream->prev->next = stream->next; } @@ -218,7 +216,7 @@ void OnAudioStreamDestroy(SDL_AudioStream *stream) if (stream == current_audio.existing_streams) { current_audio.existing_streams = stream->next; } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } } @@ -410,7 +408,7 @@ static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, // bit #1 of devid is set for physical devices and unset for logical. const bool islogical = !(devid & (1<<1)); if (islogical) { // don't bother looking if it's not a logical device id value. - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_FindInHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) devid, (const void **) &logdev); if (logdev) { SDL_assert(logdev->instance_id == devid); @@ -418,10 +416,10 @@ static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, SDL_assert(device != NULL); RefPhysicalAudioDevice(device); // reference it, in case the logical device migrates to a new default. } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (logdev) { - // we have to release the device_hash_lock before we take the device lock, to avoid deadlocks, so do a loop + // we have to release the subsystem_rwlock before we take the device lock, to avoid deadlocks, so do a loop // to make sure the correct physical device gets locked, in case we're in a race with the default changing. while (true) { SDL_LockMutex(device->lock); @@ -461,10 +459,10 @@ static SDL_AudioDevice *ObtainPhysicalAudioDevice(SDL_AudioDeviceID devid) // ! } else if (!SDL_GetCurrentAudioDriver()) { // (the `islogical` path, above, checks this in ObtainLogicalAudioDevice.) SDL_SetError("Audio subsystem is not initialized"); } else { - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_FindInHashTable(current_audio.device_hash_physical, (const void *) (uintptr_t) devid, (const void **) &device); SDL_assert(!device || (device->instance_id == devid)); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (!device) { SDL_SetError("Invalid audio device instance ID"); @@ -486,13 +484,13 @@ static SDL_AudioDevice *ObtainPhysicalAudioDeviceDefaultAllowed(SDL_AudioDeviceI const SDL_AudioDeviceID orig_devid = devid; while (true) { - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); if (orig_devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) { devid = current_audio.default_playback_device_id; } else if (orig_devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) { devid = current_audio.default_recording_device_id; } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (devid == 0) { SDL_SetError("No default audio device available"); @@ -506,13 +504,13 @@ static SDL_AudioDevice *ObtainPhysicalAudioDeviceDefaultAllowed(SDL_AudioDeviceI // make sure the default didn't change while we were waiting for the lock... bool got_it = false; - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); if ((orig_devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) && (devid == current_audio.default_playback_device_id)) { got_it = true; } else if ((orig_devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) && (devid == current_audio.default_recording_device_id)) { got_it = true; } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (got_it) { return device; @@ -530,9 +528,9 @@ static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev) { // Remove ourselves from the device_hash hashtable. if (current_audio.device_hash_logical) { // will be NULL while shutting down. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); SDL_RemoveFromHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) logdev->instance_id); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } // remove ourselves from the physical device's list of logical devices. @@ -593,11 +591,11 @@ void UnrefPhysicalAudioDevice(SDL_AudioDevice *device) { if (SDL_AtomicDecRef(&device->refcount)) { // take it out of the device list. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); if (SDL_RemoveFromHashTable(current_audio.device_hash_physical, (const void *) (uintptr_t) device->instance_id)) { SDL_AddAtomicInt(device->recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count, -1); } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); DestroyPhysicalAudioDevice(device); // ...and nuke it. } } @@ -611,9 +609,9 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recordi { SDL_assert(name != NULL); - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); const int shutting_down = SDL_GetAtomicInt(¤t_audio.shutting_down); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (shutting_down) { return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment. } @@ -655,7 +653,7 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recordi device->instance_id = AssignAudioDeviceInstanceId(recording, /*islogical=*/false); - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); if (SDL_InsertIntoHashTable(current_audio.device_hash_physical, (const void *) (uintptr_t) device->instance_id, device, false)) { SDL_AddAtomicInt(device_count, 1); } else { @@ -665,7 +663,7 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recordi SDL_free(device); device = NULL; } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); RefPhysicalAudioDevice(device); // unref'd on device disconnect. return device; @@ -713,12 +711,12 @@ SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_ p->type = SDL_EVENT_AUDIO_DEVICE_ADDED; p->devid = device->instance_id; p->next = NULL; - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); SDL_assert(current_audio.pending_events_tail != NULL); SDL_assert(current_audio.pending_events_tail->next == NULL); current_audio.pending_events_tail->next = p; current_audio.pending_events_tail = p; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } } @@ -742,10 +740,10 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) ObtainPhysicalAudioDeviceObj(device); - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); const SDL_AudioDeviceID devid = device->instance_id; const bool is_default_device = ((devid == current_audio.default_playback_device_id) || (devid == current_audio.default_recording_device_id)); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); const bool first_disconnect = SDL_CompareAndSwapAtomicInt(&device->zombie, 0, 1); if (first_disconnect) { // if already disconnected this device, don't do it twice. @@ -790,12 +788,12 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) if (first_disconnect) { if (pending.next) { // NULL if event is disabled or disaster struck. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); SDL_assert(current_audio.pending_events_tail != NULL); SDL_assert(current_audio.pending_events_tail->next == NULL); current_audio.pending_events_tail->next = pending.next; current_audio.pending_events_tail = pending_tail; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } UnrefPhysicalAudioDevice(device); @@ -900,9 +898,9 @@ static SDL_AudioDevice *GetFirstAddedAudioDevice(const bool recording) // (Device IDs increase as new devices are added, so the first device added has the lowest SDL_AudioDeviceID value.) FindLowestDeviceIDData data = { recording, highest, NULL }; - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_IterateHashTable(current_audio.device_hash_physical, FindLowestDeviceID, &data); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); return data.result; } @@ -926,21 +924,21 @@ bool SDL_InitAudio(const char *driver_name) SDL_ChooseAudioConverters(); SDL_SetupAudioResampler(); - SDL_RWLock *device_hash_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem. - if (!device_hash_lock) { + SDL_RWLock *subsystem_rwlock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem. + if (!subsystem_rwlock) { return false; } SDL_HashTable *device_hash_physical = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); if (!device_hash_physical) { - SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyRWLock(subsystem_rwlock); return false; } SDL_HashTable *device_hash_logical = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); if (!device_hash_logical) { SDL_DestroyHashTable(device_hash_physical); - SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyRWLock(subsystem_rwlock); return false; } @@ -957,7 +955,7 @@ bool SDL_InitAudio(const char *driver_name) const char *driver_attempt = driver_name_copy; if (!driver_name_copy) { - SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyRWLock(subsystem_rwlock); SDL_DestroyHashTable(device_hash_physical); SDL_DestroyHashTable(device_hash_logical); return false; @@ -981,7 +979,7 @@ bool SDL_InitAudio(const char *driver_name) tried_to_init = true; SDL_zero(current_audio); current_audio.pending_events_tail = ¤t_audio.pending_events; - current_audio.device_hash_lock = device_hash_lock; + current_audio.subsystem_rwlock = subsystem_rwlock; current_audio.device_hash_physical = device_hash_physical; current_audio.device_hash_logical = device_hash_logical; if (bootstrap[i]->init(¤t_audio.impl)) { @@ -1006,7 +1004,7 @@ bool SDL_InitAudio(const char *driver_name) tried_to_init = true; SDL_zero(current_audio); current_audio.pending_events_tail = ¤t_audio.pending_events; - current_audio.device_hash_lock = device_hash_lock; + current_audio.subsystem_rwlock = subsystem_rwlock; current_audio.device_hash_physical = device_hash_physical; current_audio.device_hash_logical = device_hash_logical; if (bootstrap[i]->init(¤t_audio.impl)) { @@ -1029,7 +1027,7 @@ bool SDL_InitAudio(const char *driver_name) } } - SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyRWLock(subsystem_rwlock); SDL_DestroyHashTable(device_hash_physical); SDL_DestroyHashTable(device_hash_logical); SDL_zero(current_audio); @@ -1097,7 +1095,7 @@ void SDL_QuitAudio(void) } } - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); SDL_SetAtomicInt(¤t_audio.shutting_down, 1); SDL_HashTable *device_hash_physical = current_audio.device_hash_physical; SDL_HashTable *device_hash_logical = current_audio.device_hash_logical; @@ -1106,7 +1104,7 @@ void SDL_QuitAudio(void) current_audio.pending_events.next = NULL; SDL_SetAtomicInt(¤t_audio.playback_device_count, 0); SDL_SetAtomicInt(¤t_audio.recording_device_count, 0); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); SDL_PendingAudioDeviceEvent *pending_next = NULL; for (SDL_PendingAudioDeviceEvent *i = pending_events; i; i = pending_next) { @@ -1120,7 +1118,7 @@ void SDL_QuitAudio(void) // Free the driver data current_audio.impl.Deinitialize(); - SDL_DestroyRWLock(current_audio.device_hash_lock); + SDL_DestroyRWLock(current_audio.subsystem_rwlock); SDL_DestroyHashTable(device_hash_physical); SDL_DestroyHashTable(device_hash_logical); @@ -1437,7 +1435,7 @@ static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table SDL_assert(isphysical); if (devid_recording == data->recording) { SDL_assert(data->devs_seen < data->num_devices); - SDL_AudioDevice *device = (SDL_AudioDevice *) value; // this is normally risky, but we hold the device_hash_lock here. + SDL_AudioDevice *device = (SDL_AudioDevice *) value; // this is normally risky, but we hold the subsystem_rwlock here. const bool zombie = SDL_GetAtomicInt(&device->zombie) != 0; if (zombie) { data->devs_skipped++; @@ -1454,7 +1452,7 @@ static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) int num_devices = 0; if (SDL_GetCurrentAudioDriver()) { - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); { num_devices = SDL_GetAtomicInt(recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count); result = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID)); @@ -1466,7 +1464,7 @@ static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) result[num_devices] = 0; // null-terminated. } } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } else { SDL_SetError("Audio subsystem is not initialized"); } @@ -1523,9 +1521,9 @@ SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_Audi } FindAudioDeviceByCallbackData data = { callback, userdata, NULL }; - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_IterateHashTable(current_audio.device_hash_physical, FindAudioDeviceByCallback, &data); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (!data.retval) { SDL_SetError("Device not found"); @@ -1557,8 +1555,8 @@ const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) // This does not call ObtainPhysicalAudioDevice() because the device's name never changes, so // it doesn't have to lock the whole device. However, just to make sure the device pointer itself // remains valid (in case the device is unplugged at the wrong moment), we hold the - // device_hash_lock while we copy the string. - SDL_LockRWLockForReading(current_audio.device_hash_lock); + // subsystem_rwlock while we copy the string. + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_FindInHashTable(islogical ? current_audio.device_hash_logical : current_audio.device_hash_physical, (const void *) (uintptr_t) devid, &vdev); if (!vdev) { SDL_SetError("Invalid audio device instance ID"); @@ -1571,7 +1569,7 @@ const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) SDL_assert(device->instance_id == devid); result = SDL_GetPersistentString(device->name); } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } return result; @@ -1884,9 +1882,9 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp ReleaseAudioDevice(device); if (result) { - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); const bool inserted = SDL_InsertIntoHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) result, logdev, false); - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (!inserted) { SDL_CloseAudioDevice(result); result = 0; @@ -2336,7 +2334,7 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) const bool recording = new_default_device->recording; // change the official default over right away, so new opens will go to the new device. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); const SDL_AudioDeviceID current_devid = recording ? current_audio.default_recording_device_id : current_audio.default_playback_device_id; const bool is_already_default = (new_default_device->instance_id == current_devid); if (!is_already_default) { @@ -2346,7 +2344,7 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) current_audio.default_playback_device_id = new_default_device->instance_id; } } - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (is_already_default) { return; // this is already the default. @@ -2412,8 +2410,8 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) continue; // not opened as a default, leave it on the current physical device. } - // now migrate the logical device. Hold device_hash_lock so ObtainLogicalAudioDevice doesn't get a device in the middle of transition. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + // now migrate the logical device. Hold subsystem_rwlock so ObtainLogicalAudioDevice doesn't get a device in the middle of transition. + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); if (logdev->next) { logdev->next->prev = logdev->prev; } @@ -2428,7 +2426,7 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) logdev->prev = NULL; logdev->next = new_default_device->logical_devices; new_default_device->logical_devices = logdev; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); SDL_assert(SDL_GetAtomicInt(¤t_default_device->refcount) > 1); // we should hold at least one extra reference to this device, beyond logical devices, during this phase... RefPhysicalAudioDevice(new_default_device); @@ -2470,12 +2468,12 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) } if (pending.next) { - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); SDL_assert(current_audio.pending_events_tail != NULL); SDL_assert(current_audio.pending_events_tail->next == NULL); current_audio.pending_events_tail->next = pending.next; current_audio.pending_events_tail = pending_tail; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } } @@ -2552,12 +2550,12 @@ bool SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SD } if (pending.next) { - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); SDL_assert(current_audio.pending_events_tail != NULL); SDL_assert(current_audio.pending_events_tail->next == NULL); current_audio.pending_events_tail->next = pending.next; current_audio.pending_events_tail = pending_tail; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); } } @@ -2579,20 +2577,20 @@ bool SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec * // ("UpdateSubsystem" is the same naming that the other things that hook into PumpEvents use.) void SDL_UpdateAudio(void) { - SDL_LockRWLockForReading(current_audio.device_hash_lock); + SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_PendingAudioDeviceEvent *pending_events = current_audio.pending_events.next; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); if (!pending_events) { return; // nothing to do, check next time. } // okay, let's take this whole list of events so we can dump the lock, and new ones can queue up for a later update. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); + SDL_LockRWLockForWriting(current_audio.subsystem_rwlock); pending_events = current_audio.pending_events.next; // in case this changed... current_audio.pending_events.next = NULL; current_audio.pending_events_tail = ¤t_audio.pending_events; - SDL_UnlockRWLock(current_audio.device_hash_lock); + SDL_UnlockRWLock(current_audio.subsystem_rwlock); SDL_PendingAudioDeviceEvent *pending_next = NULL; for (SDL_PendingAudioDeviceEvent *i = pending_events; i; i = pending_next) { diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index b181a65359..1b6dd10805 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -183,7 +183,7 @@ typedef struct SDL_AudioDriver const char *name; // The name of this audio driver const char *desc; // The description of this audio driver SDL_AudioDriverImpl impl; // the backend's interface - SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash*` and some other things. + SDL_RWLock *subsystem_rwlock; // A rwlock that protects several things in the audio subsystem (device hashtables, etc). SDL_HashTable *device_hash_physical; // the collection of currently-available audio devices (recording and playback), for mapping SDL_AudioDeviceID to an SDL_AudioDevice*. SDL_HashTable *device_hash_logical; // the collection of currently-available audio devices (recording and playback), for mapping SDL_AudioDeviceID to an SDL_LogicalAudioDevice*. SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams. From 8f04e4af0026c8420ecfa7273913a7957b640337 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 25 Aug 2025 13:06:09 -0400 Subject: [PATCH 015/134] audio: Clean out all the bitshifting. Hide the implementation details in something human-readable. --- src/audio/SDL_audio.c | 59 ++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 476c0528b5..031d6b5abb 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -367,14 +367,28 @@ static SDL_AudioDeviceID AssignAudioDeviceInstanceId(bool recording, bool islogi bool SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid) { + // bit #1 of devid is set for physical devices and unset for logical. return (devid & (1 << 1)) != 0; } +bool SDL_IsAudioDeviceLogical(SDL_AudioDeviceID devid) +{ + // bit #1 of devid is set for physical devices and unset for logical. + return (devid & (1 << 1)) == 0; +} + bool SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid) { + // bit #0 of devid is set for playback devices and unset for recording. return (devid & (1 << 0)) != 0; } +bool SDL_IsAudioDeviceRecording(SDL_AudioDeviceID devid) +{ + // bit #0 of devid is set for playback devices and unset for recording. + return (devid & (1 << 0)) == 0; +} + static void ObtainPhysicalAudioDeviceObj(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXMEL SDL_ACQUIRE { if (device) { @@ -405,9 +419,7 @@ static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, SDL_AudioDevice *device = NULL; SDL_LogicalAudioDevice *logdev = NULL; - // bit #1 of devid is set for physical devices and unset for logical. - const bool islogical = !(devid & (1<<1)); - if (islogical) { // don't bother looking if it's not a logical device id value. + if (SDL_IsAudioDeviceLogical(devid)) { // don't bother looking if it's not a logical device id value. SDL_LockRWLockForReading(current_audio.subsystem_rwlock); SDL_FindInHashTable(current_audio.device_hash_logical, (const void *) (uintptr_t) devid, (const void **) &logdev); if (logdev) { @@ -452,9 +464,7 @@ static SDL_AudioDevice *ObtainPhysicalAudioDevice(SDL_AudioDeviceID devid) // ! { SDL_AudioDevice *device = NULL; - // bit #1 of devid is set for physical devices and unset for logical. - const bool islogical = !(devid & (1<<1)); - if (islogical) { + if (SDL_IsAudioDeviceLogical(devid)) { ObtainLogicalAudioDevice(devid, &device); } else if (!SDL_GetCurrentAudioDriver()) { // (the `islogical` path, above, checks this in ObtainLogicalAudioDevice.) SDL_SetError("Audio subsystem is not initialized"); @@ -879,12 +889,8 @@ static bool SDLCALL FindLowestDeviceID(void *userdata, const SDL_HashTable *tabl { FindLowestDeviceIDData *data = (FindLowestDeviceIDData *) userdata; const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - // bit #0 of devid is set for playback devices and unset for recording. - // bit #1 of devid is set for physical devices and unset for logical. - const bool devid_recording = !(devid & (1 << 0)); - const bool isphysical = !!(devid & (1 << 1)); - SDL_assert(isphysical); // should only be iterating device_hash_physical. - if ((devid_recording == data->recording) && (devid < data->highest)) { + SDL_assert(SDL_IsAudioDevicePhysical(devid)); // should only be iterating device_hash_physical. + if ((SDL_IsAudioDeviceRecording(devid) == data->recording) && (devid < data->highest)) { data->highest = devid; data->result = (SDL_AudioDevice *) value; SDL_assert(data->result->instance_id == devid); @@ -1065,10 +1071,8 @@ bool SDL_InitAudio(const char *driver_name) static bool SDLCALL DestroyOnePhysicalAudioDevice(void *userdata, const SDL_HashTable *table, const void *key, const void *value) { - // bit #1 of devid is set for physical devices and unset for logical. const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - const bool isphysical = !!(devid & (1<<1)); - SDL_assert(isphysical); + SDL_assert(SDL_IsAudioDevicePhysical(devid)); // should only be iterating device_hash_physical. SDL_AudioDevice *dev = (SDL_AudioDevice *) value; SDL_assert(dev->instance_id == devid); DestroyPhysicalAudioDevice(dev); @@ -1428,12 +1432,8 @@ static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table { CountAudioDevicesData *data = (CountAudioDevicesData *) userdata; const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - // bit #0 of devid is set for playback devices and unset for recording. - // bit #1 of devid is set for physical devices and unset for logical. - const bool devid_recording = !(devid & (1<<0)); - const bool isphysical = !!(devid & (1<<1)); - SDL_assert(isphysical); - if (devid_recording == data->recording) { + SDL_assert(SDL_IsAudioDevicePhysical(devid)); // should only be iterating device_hash_physical. + if (SDL_IsAudioDeviceRecording(devid) == data->recording) { SDL_assert(data->devs_seen < data->num_devices); SDL_AudioDevice *device = (SDL_AudioDevice *) value; // this is normally risky, but we hold the subsystem_rwlock here. const bool zombie = SDL_GetAtomicInt(&device->zombie) != 0; @@ -1500,9 +1500,7 @@ static bool SDLCALL FindAudioDeviceByCallback(void *userdata, const SDL_HashTabl { FindAudioDeviceByCallbackData *data = (FindAudioDeviceByCallbackData *) userdata; const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - // bit #1 of devid is set for physical devices and unset for logical. - const bool isphysical = !!(devid & (1<<1)); - SDL_assert(isphysical); + SDL_assert(SDL_IsAudioDevicePhysical(devid)); // should only be iterating device_hash_physical. SDL_AudioDevice *device = (SDL_AudioDevice *) value; if (data->callback(device, data->userdata)) { // found it? data->retval = device; @@ -1545,13 +1543,14 @@ 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; - const void *vdev = NULL; if (!SDL_GetCurrentAudioDriver()) { SDL_SetError("Audio subsystem is not initialized"); } else { + const bool islogical = SDL_IsAudioDeviceLogical(devid); + const void *vdev = NULL; + // This does not call ObtainPhysicalAudioDevice() because the device's name never changes, so // it doesn't have to lock the whole device. However, just to make sure the device pointer itself // remains valid (in case the device is unplugged at the wrong moment), we hold the @@ -1844,8 +1843,7 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp // this will let you use a logical device to make a new logical device on the parent physical device. Could be useful? SDL_AudioDevice *device = NULL; - const bool islogical = (!wants_default && !(devid & (1<<1))); - if (!islogical) { + if ((wants_default || SDL_IsAudioDevicePhysical(devid))) { device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); } else { SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); @@ -1982,7 +1980,6 @@ bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallba bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *streams, int num_streams) { - const bool islogical = !(devid & (1<<1)); SDL_AudioDevice *device = NULL; SDL_LogicalAudioDevice *logdev = NULL; bool result = true; @@ -1993,7 +1990,7 @@ bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *stre return SDL_InvalidParamError("num_streams"); } else if (!streams) { return SDL_InvalidParamError("streams"); - } else if (!islogical) { + } else if (SDL_IsAudioDevicePhysical(devid)) { return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices"); } @@ -2600,7 +2597,7 @@ void SDL_UpdateAudio(void) SDL_zero(event); event.type = i->type; event.adevice.which = (Uint32) i->devid; - event.adevice.recording = ((i->devid & (1<<0)) == 0); // bit #0 of devid is set for playback devices and unset for recording. + event.adevice.recording = SDL_IsAudioDeviceRecording(i->devid); // bit #0 of devid is set for playback devices and unset for recording. SDL_PushEvent(&event); } SDL_free(i); From 81920b5db7e5d2865c1879204d574ed9b7a352a8 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Sat, 16 Aug 2025 16:13:52 +0100 Subject: [PATCH 016/134] testffmpeg: avutil queue family version check The `AVVulkanDeviceQueueFamily` struct was introduced by libavutil 59.34.100 --- test/testffmpeg_vulkan.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c index b743fde41a..5a392a905a 100644 --- a/test/testffmpeg_vulkan.c +++ b/test/testffmpeg_vulkan.c @@ -679,7 +679,7 @@ void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID p SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, context->graphicsQueueFamilyIndex); } -#if LIBAVUTIL_VERSION_MAJOR >= 59 +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 34, 100) static void AddQueueFamily(AVVulkanDeviceContext *ctx, int idx, int num, VkQueueFlagBits flags) { AVVulkanDeviceQueueFamily *entry = &ctx->qf[ctx->nb_qf++]; @@ -687,7 +687,7 @@ static void AddQueueFamily(AVVulkanDeviceContext *ctx, int idx, int num, VkQueue entry->num = num; entry->flags = flags; } -#endif /* LIBAVUTIL_VERSION_MAJOR */ +#endif /* LIBAVUTIL_VERSION_INT */ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) { @@ -700,7 +700,7 @@ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceCon ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount; ctx->enabled_dev_extensions = context->deviceExtensions; ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount; -#if LIBAVUTIL_VERSION_MAJOR >= 59 +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 34, 100) AddQueueFamily(ctx, context->graphicsQueueFamilyIndex, context->graphicsQueueCount, VK_QUEUE_GRAPHICS_BIT); AddQueueFamily(ctx, context->transferQueueFamilyIndex, context->transferQueueCount, VK_QUEUE_TRANSFER_BIT); AddQueueFamily(ctx, context->computeQueueFamilyIndex, context->computeQueueCount, VK_QUEUE_COMPUTE_BIT); @@ -716,7 +716,7 @@ void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceCon ctx->nb_encode_queues = 0; ctx->queue_family_decode_index = context->decodeQueueFamilyIndex; ctx->nb_decode_queues = context->decodeQueueCount; -#endif /* LIBAVUTIL_VERSION_MAJOR */ +#endif /* LIBAVUTIL_VERSION_INT */ } static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer) From 1af7dfb0a7cb75a682ec0c6eb8ed40ada818688e Mon Sep 17 00:00:00 2001 From: Nintorch <92302738+Nintorch@users.noreply.github.com> Date: Sat, 16 Aug 2025 21:55:03 +0500 Subject: [PATCH 017/134] Allow Android to ignore unnecessary joysticks Previously, SDL_ShouldIgnoreJoystick wasn't being called for Android, and fingerprint sensors were recognized as joysticks. --- src/joystick/SDL_gamepad.c | 4 ++-- src/joystick/android/SDL_sysjoystick.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index dca3cfcb91..6585447f15 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -2863,8 +2863,8 @@ bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version } #endif - if (name && SDL_strcmp(name, "uinput-fpc") == 0) { - // The Google Pixel fingerprint sensor reports itself as a joystick + if (name && SDL_startswith(name, "uinput-")) { + // The Google Pixel fingerprint sensor, as well as other fingerprint sensors, reports itself as a joystick return true; } diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index 9a3402eb6a..e3693686eb 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -328,6 +328,10 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int goto done; } + if (SDL_ShouldIgnoreJoystick(vendor_id, product_id, 0, name)) { + goto done; + } + #ifdef DEBUG_JOYSTICK SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats", name, desc, vendor_id, product_id, naxes, nhats); #endif From c79a18d0fa4eb962b774aafccdfca905262cc318 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Sun, 25 May 2025 15:55:48 +0100 Subject: [PATCH 018/134] dialog: Fix save file chooser with xdg portal This correctly sets the xdg portal fields for targeting a specific new filename or existing file. "current_name" sets the dialogs placeholder name. "current_file" targets an existing file. "current_folder" for when the target is a folder. --- src/dialog/unix/SDL_portaldialog.c | 62 ++++++++++++++++++++++++++---- test/testdialog.c | 39 ++++++++++++++++++- 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/dialog/unix/SDL_portaldialog.c b/src/dialog/unix/SDL_portaldialog.c index dadfc9f556..a72534228f 100644 --- a/src/dialog/unix/SDL_portaldialog.c +++ b/src/dialog/unix/SDL_portaldialog.c @@ -26,6 +26,8 @@ #ifdef SDL_USE_LIBDBUS #include +#include +#include #include #include #include @@ -294,7 +296,12 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog bool allow_many = SDL_GetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false); const char *default_location = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, NULL); const char *accept = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_ACCEPT_STRING, NULL); + char *location_name = NULL; + char *location_folder = NULL; + struct stat statbuf; bool open_folders = false; + bool save_file_existing = false; + bool save_file_new_named = false; switch (type) { case SDL_FILEDIALOG_OPENFILE: @@ -305,6 +312,28 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog case SDL_FILEDIALOG_SAVEFILE: method = "SaveFile"; method_title = SDL_GetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, "Save File"); + if (default_location) { + if (stat(default_location, &statbuf) == 0) { + save_file_existing = S_ISREG(statbuf.st_mode); + } else if (errno == ENOENT) { + char *dirc = SDL_strdup(default_location); + if (dirc) { + location_folder = SDL_strdup(dirname(dirc)); + SDL_free(dirc); + if (location_folder) { + save_file_new_named = (stat(location_folder, &statbuf) == 0) && S_ISDIR(statbuf.st_mode); + } + } + } + + if (save_file_existing || save_file_new_named) { + char *basec = SDL_strdup(default_location); + if (basec) { + location_name = SDL_strdup(basename(basec)); + SDL_free(basec); + } + } + } break; case SDL_FILEDIALOG_OPENFOLDER: @@ -317,7 +346,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog /* This is already checked in ../SDL_dialog.c; this silences compiler warnings */ SDL_SetError("Invalid file dialog type: %d", type); callback(userdata, NULL, -1); - return; + goto cleanup; } SDL_DBusContext *dbus = SDL_DBus_GetContext(); @@ -335,20 +364,20 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog if (err_msg) { SDL_SetError("%s", err_msg); callback(userdata, NULL, -1); - return; + goto cleanup; } if (dbus == NULL) { SDL_SetError("Failed to connect to DBus"); callback(userdata, NULL, -1); - return; + goto cleanup; } msg = dbus->message_new_method_call(PORTAL_DESTINATION, PORTAL_PATH, PORTAL_INTERFACE, method); if (msg == NULL) { SDL_SetError("Failed to send message to portal"); callback(userdata, NULL, -1); - return; + goto cleanup; } dbus->message_iter_init_append(msg, ¶ms); @@ -362,7 +391,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog handle_str = SDL_malloc(len * sizeof(char)); if (!handle_str) { callback(userdata, NULL, -1); - return; + goto cleanup; } SDL_snprintf(handle_str, len, "%s%s", WAYLAND_HANDLE_PREFIX, parent_handle); @@ -373,7 +402,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog handle_str = SDL_malloc(len * sizeof(char)); if (!handle_str) { callback(userdata, NULL, -1); - return; + goto cleanup; } // The portal wants X11 window ID numbers in hex. @@ -393,7 +422,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog handle_str = SDL_malloc(sizeof(char) * (HANDLE_LEN + 1)); if (!handle_str) { callback(userdata, NULL, -1); - return; + goto cleanup; } SDL_snprintf(handle_str, HANDLE_LEN, "%u", ++handle_id); DBus_AppendStringOption(dbus, &options, "handle_token", handle_str); @@ -410,7 +439,20 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog DBus_AppendFilters(dbus, &options, filters, nfilters); } if (default_location) { - DBus_AppendByteArray(dbus, &options, "current_folder", default_location); + if (save_file_existing && location_name) { + /* Open a save dialog at an existing file */ + DBus_AppendByteArray(dbus, &options, "current_file", default_location); + /* Setting "current_name" should not be necessary however the kde-desktop-portal sets the filename without an extension. + * An alternative would be to match the extension to a filter and set "current_filter". + */ + DBus_AppendStringOption(dbus, &options, "current_name", location_name); + } else if (save_file_new_named && location_folder && location_name) { + /* Open a save dialog at a location with a suggested name */ + DBus_AppendByteArray(dbus, &options, "current_folder", location_folder); + DBus_AppendStringOption(dbus, &options, "current_name", location_name); + } else { + DBus_AppendByteArray(dbus, &options, "current_folder", default_location); + } } if (accept) { DBus_AppendStringOption(dbus, &options, "accept_label", accept); @@ -469,6 +511,10 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog incorrect_type: dbus->message_unref(reply); + +cleanup: + SDL_free(location_name); + SDL_free(location_folder); } bool SDL_Portal_detect(void) diff --git a/test/testdialog.c b/test/testdialog.c index dcff5bd9f0..37d005f4c5 100644 --- a/test/testdialog.c +++ b/test/testdialog.c @@ -12,6 +12,7 @@ /* Sample program: Create open and save dialogs. */ #include +#include #include #include @@ -23,6 +24,8 @@ const SDL_DialogFileFilter filters[] = { }; static void SDLCALL callback(void *userdata, const char * const *files, int filter) { + char **saved_path = userdata; + if (files) { const char* filter_name = "(filter fetching unsupported)"; @@ -36,6 +39,13 @@ static void SDLCALL callback(void *userdata, const char * const *files, int filt SDL_Log("Filter used: '%s'", filter_name); + if (*files && saved_path) { + *saved_path = SDL_strdup(*files); + /* Create the file */ + SDL_IOStream *stream = SDL_IOFromFile(*saved_path, "w"); + SDL_CloseIO(stream); + } + while (*files) { SDL_Log("'%s'", *files); files++; @@ -45,6 +55,23 @@ static void SDLCALL callback(void *userdata, const char * const *files, int filt } } +char *concat_strings(const char *a, const char *b) +{ + char *out = NULL; + + if (a != NULL && b != NULL) { + const size_t out_size = SDL_strlen(a) + SDL_strlen(b) + 1; + out = (char *)SDL_malloc(out_size); + if (out) { + *out = '\0'; + SDL_strlcat(out, a, out_size); + SDL_strlcat(out, b, out_size); + } + } + + return out; +} + int main(int argc, char *argv[]) { SDL_Window *w; @@ -54,7 +81,9 @@ int main(int argc, char *argv[]) const SDL_FRect save_file_rect = { 50, 290, 220, 140 }; const SDL_FRect open_folder_rect = { 370, 50, 220, 140 }; int i; + const char *default_filename = "Untitled.index"; const char *initial_path = NULL; + char *last_saved_path = NULL; /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, 0); @@ -116,7 +145,14 @@ int main(int argc, char *argv[]) } else if (SDL_PointInRectFloat(&p, &open_folder_rect)) { SDL_ShowOpenFolderDialog(callback, NULL, w, initial_path, 1); } else if (SDL_PointInRectFloat(&p, &save_file_rect)) { - SDL_ShowSaveFileDialog(callback, NULL, w, filters, SDL_arraysize(filters), initial_path); + char *save_path = NULL; + if (last_saved_path) { + save_path = SDL_strdup(last_saved_path); + } else { + save_path = concat_strings(initial_path, default_filename); + } + SDL_ShowSaveFileDialog(callback, &last_saved_path, w, filters, SDL_arraysize(filters), save_path ? save_path : default_filename); + SDL_free(save_path); } } } @@ -145,6 +181,7 @@ int main(int argc, char *argv[]) SDL_RenderPresent(r); } + SDL_free(last_saved_path); SDLTest_CleanupTextDrawing(); SDL_DestroyRenderer(r); SDL_DestroyWindow(w); From 5be08481e175e457a66603bde8a41a79ebab66a1 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Wed, 6 Aug 2025 21:25:31 +0100 Subject: [PATCH 019/134] dialog: Print DBus errors where available --- src/dialog/unix/SDL_portaldialog.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/dialog/unix/SDL_portaldialog.c b/src/dialog/unix/SDL_portaldialog.c index a72534228f..a7f96a3381 100644 --- a/src/dialog/unix/SDL_portaldialog.c +++ b/src/dialog/unix/SDL_portaldialog.c @@ -350,6 +350,7 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog } SDL_DBusContext *dbus = SDL_DBus_GetContext(); + DBusError error; DBusMessage *msg; DBusMessageIter params, options; const char *signal_id = NULL; @@ -361,6 +362,8 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog const char *err_msg = validate_filters(filters, nfilters); + dbus->error_init(&error); + if (err_msg) { SDL_SetError("%s", err_msg); callback(userdata, NULL, -1); @@ -459,7 +462,14 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog } dbus->message_iter_close_container(¶ms, &options); - DBusMessage *reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, DBUS_TIMEOUT_INFINITE, NULL); + DBusMessage *reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, DBUS_TIMEOUT_INFINITE, &error); + if (dbus->error_is_set(&error)) { + SDL_SetError("Failed to open dialog via DBus, %s: %s", error.name, error.message); + dbus->error_free(&error); + callback(userdata, NULL, -1); + goto cleanup; + } + if (reply) { DBusMessageIter reply_iter; dbus->message_iter_init(reply, &reply_iter); @@ -485,9 +495,16 @@ void SDL_Portal_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_Dialog } SDL_snprintf(filter, filter_len, SIGNAL_FILTER"%s'", signal_id); - dbus->bus_add_match(dbus->session_conn, filter, NULL); + dbus->bus_add_match(dbus->session_conn, filter, &error); SDL_free(filter); + if (dbus->error_is_set(&error)) { + SDL_SetError("Failed to set up DBus listener for dialog, %s: %s", error.name, error.message); + dbus->error_free(&error); + callback(userdata, NULL, -1); + goto cleanup; + } + SignalCallback *data = SDL_malloc(sizeof(SignalCallback)); if (!data) { callback(userdata, NULL, -1); From 385715c0dd641296b410ae9d8b3fa639ff375cee Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Sat, 16 Aug 2025 02:36:09 -0700 Subject: [PATCH 020/134] Introduce enum for SDL_GetCameraPermissionState result --- include/SDL3/SDL_camera.h | 16 +++++++++++++++- src/camera/SDL_camera.c | 18 +++++++++--------- src/camera/SDL_syscamera.h | 4 ++-- src/camera/coremedia/SDL_camera_coremedia.m | 4 ++-- src/dynapi/SDL_dynapi_procs.h | 2 +- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index c0e2ee0fed..6dc79d3783 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -136,6 +136,20 @@ typedef enum SDL_CameraPosition SDL_CAMERA_POSITION_BACK_FACING } SDL_CameraPosition; +/** + * The current state of a request for camera access. + * + * \since This enum is available since SDL 3.4.0. + * + * \sa SDL_GetCameraPermissionState + */ +typedef enum SDL_CameraPermissionState +{ + SDL_CAMERA_PERMISSION_STATE_DENIED = -1, + SDL_CAMERA_PERMISSION_STATE_PENDING, + SDL_CAMERA_PERMISSION_STATE_APPROVED, +} SDL_CameraPermissionState; + /** * Use this function to get the number of built-in camera drivers. @@ -368,7 +382,7 @@ extern SDL_DECLSPEC SDL_Camera * SDLCALL SDL_OpenCamera(SDL_CameraID instance_id * \sa SDL_OpenCamera * \sa SDL_CloseCamera */ -extern SDL_DECLSPEC int SDLCALL SDL_GetCameraPermissionState(SDL_Camera *camera); +extern SDL_DECLSPEC SDL_CameraPermissionState SDLCALL SDL_GetCameraPermissionState(SDL_Camera *camera); /** * Get the instance ID of an opened camera. diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 48c6b500df..855c9be6aa 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -270,7 +270,7 @@ static void ClosePhysicalCamera(SDL_Camera *device) SDL_aligned_free(device->zombie_pixels); - device->permission = 0; + device->permission = SDL_CAMERA_PERMISSION_STATE_PENDING; device->zombie_pixels = NULL; device->filled_output_surfaces.next = NULL; device->empty_output_surfaces.next = NULL; @@ -581,7 +581,7 @@ void SDL_CameraPermissionOutcome(SDL_Camera *device, bool approved) pending.next = NULL; SDL_PendingCameraEvent *pending_tail = &pending; - const int permission = approved ? 1 : -1; + const SDL_CameraPermissionState permission = approved ? SDL_CAMERA_PERMISSION_STATE_APPROVED : SDL_CAMERA_PERMISSION_STATE_DENIED; ObtainPhysicalCameraObj(device); if (device->permission != permission) { @@ -665,7 +665,7 @@ bool SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec) SDL_Camera *device = camera; // currently there's no separation between physical and logical device. ObtainPhysicalCameraObj(device); - if (device->permission > 0) { + if (device->permission > SDL_CAMERA_PERMISSION_STATE_PENDING) { SDL_copyp(spec, &device->spec); result = true; } else { @@ -808,9 +808,9 @@ bool SDL_CameraThreadIterate(SDL_Camera *device) } const int permission = device->permission; - if (permission <= 0) { + if (permission <= SDL_CAMERA_PERMISSION_STATE_PENDING) { SDL_UnlockMutex(device->lock); - return (permission < 0) ? false : true; // if permission was denied, shut it down. if undecided, we're done for now. + return (permission < SDL_CAMERA_PERMISSION_STATE_PENDING) ? false : true; // if permission was denied, shut it down. if undecided, we're done for now. } bool failed = false; // set to true if disaster worthy of treating the device as lost has happened. @@ -1264,7 +1264,7 @@ SDL_Surface *SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS) ObtainPhysicalCameraObj(device); - if (device->permission <= 0) { + if (device->permission <= SDL_CAMERA_PERMISSION_STATE_PENDING) { ReleaseCamera(device); SDL_SetError("Camera permission has not been granted"); return NULL; @@ -1371,12 +1371,12 @@ SDL_PropertiesID SDL_GetCameraProperties(SDL_Camera *camera) return result; } -int SDL_GetCameraPermissionState(SDL_Camera *camera) +SDL_CameraPermissionState SDL_GetCameraPermissionState(SDL_Camera *camera) { - int result; + SDL_CameraPermissionState result; if (!camera) { SDL_InvalidParamError("camera"); - result = -1; + result = SDL_CAMERA_PERMISSION_STATE_DENIED; } else { SDL_Camera *device = camera; // currently there's no separation between physical and logical device. ObtainPhysicalCameraObj(device); diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 30a02f391a..094ad0faa9 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -160,8 +160,8 @@ struct SDL_Camera // Optional properties. SDL_PropertiesID props; - // -1: user denied permission, 0: waiting for user response, 1: user approved permission. - int permission; + // Current state of user permission check. + SDL_CameraPermissionState permission; // Data private to this driver, used when device is opened and running. struct SDL_PrivateCameraData *hidden; diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index f58fb92784..94b7110970 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -85,7 +85,7 @@ static void CoreMediaFormatToSDL(FourCharCode fmt, SDL_PixelFormat *pixel_format static bool CheckCameraPermissions(SDL_Camera *device) { - if (device->permission == 0) { // still expecting a permission result. + if (device->permission == SDL_CAMERA_PERMISSION_STATE_PENDING) { // still expecting a permission result. if (@available(macOS 14, *)) { const AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (status != AVAuthorizationStatusNotDetermined) { // NotDetermined == still waiting for an answer from the user. @@ -96,7 +96,7 @@ static bool CheckCameraPermissions(SDL_Camera *device) } } - return (device->permission > 0); + return (device->permission > SDL_CAMERA_PERMISSION_STATE_PENDING); } // this delegate just receives new video frames on a Grand Central Dispatch queue, and fires off the diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d1e362cdff..1230d583f0 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -288,7 +288,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetCameraDriver,(int a),(a),return) SDL_DYNAPI_PROC(bool,SDL_GetCameraFormat,(SDL_Camera *a, SDL_CameraSpec *b),(a,b),return) SDL_DYNAPI_PROC(SDL_CameraID,SDL_GetCameraID,(SDL_Camera *a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetCameraName,(SDL_CameraID a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetCameraPermissionState,(SDL_Camera *a),(a),return) +SDL_DYNAPI_PROC(SDL_CameraPermissionState,SDL_GetCameraPermissionState,(SDL_Camera *a),(a),return) SDL_DYNAPI_PROC(SDL_CameraPosition,SDL_GetCameraPosition,(SDL_CameraID a),(a),return) SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetCameraProperties,(SDL_Camera *a),(a),return) SDL_DYNAPI_PROC(SDL_CameraSpec**,SDL_GetCameraSupportedFormats,(SDL_CameraID a, int *b),(a,b),return) From b13416d74f715e9f1d2396637d6fc3900130032c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 25 Aug 2025 11:47:18 -0700 Subject: [PATCH 021/134] Fixed relative motion having desktop mouse scale on Wayland Fixes https://github.com/libsdl-org/SDL/issues/13753 --- src/video/wayland/SDL_waylandevents.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 43f31552cd..20138d0551 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1243,8 +1243,8 @@ static void relative_pointer_handle_relative_motion(void *data, seat->pointer.pending_frame.have_relative = true; seat->pointer.pending_frame.relative.dx = dx; seat->pointer.pending_frame.relative.dy = dy; - seat->pointer.pending_frame.relative.dx_unaccel = dx; - seat->pointer.pending_frame.relative.dy_unaccel = dy; + seat->pointer.pending_frame.relative.dx_unaccel = dx_unaccel; + seat->pointer.pending_frame.relative.dy_unaccel = dy_unaccel; seat->pointer.pending_frame.timestamp_ns = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo)); if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) { From 1fbed16cb0512e08651b99f7b0a6c1db4b0b61c6 Mon Sep 17 00:00:00 2001 From: Nintorch <92302738+Nintorch@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:08:22 +0500 Subject: [PATCH 022/134] Add Emscripten joystick rumble support Adds support for Emscripten (Web) joystick rumble support via EM_ASM_INT macros and HTML5's Gamepad API. --- src/joystick/emscripten/SDL_sysjoystick.c | 41 ++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c index b481d5da73..cddd975a71 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/src/joystick/emscripten/SDL_sysjoystick.c @@ -305,6 +305,7 @@ static SDL_JoystickID EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index) static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) { SDL_joylist_item *item = JoystickByDeviceIndex(device_index); + bool rumble_available = false; if (!item) { return SDL_SetError("No such device"); @@ -323,6 +324,22 @@ static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->nbuttons = item->nbuttons; joystick->naxes = item->naxes; + rumble_available = EM_ASM_INT({ + let gamepads = navigator['getGamepads'](); + if (!gamepads) { + return 0; + } + let gamepad = gamepads[$0]; + if (!gamepad || !gamepad['vibrationActuator']) { + return 0; + } + return 1; + }, item->index); + + if (rumble_available) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); + } + return true; } @@ -390,7 +407,29 @@ static SDL_GUID EMSCRIPTEN_JoystickGetDeviceGUID(int device_index) static bool EMSCRIPTEN_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { - return SDL_Unsupported(); + SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata; + + // clang-format off + bool result = EM_ASM_INT({ + let gamepads = navigator['getGamepads'](); + if (!gamepads) { + return 0; + } + let gamepad = gamepads[$0]; + if (!gamepad || !gamepad['vibrationActuator']) { + return 0; + } + + gamepad['vibrationActuator']['playEffect']('dual-rumble', { + 'startDelay': 0, + 'duration': 3000, + 'weakMagnitude': $1 / 0xFFFF, + 'strongMagnitude': $2 / 0xFFFF, + }); + return 1; + }, item->index, low_frequency_rumble, high_frequency_rumble); + + return result; } static bool EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) From 66ab91a314a3108ca3428e8c454064a7fc31d589 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 26 Aug 2025 05:50:51 -0700 Subject: [PATCH 023/134] Added SDL_FLIP_HORIZONTAL_AND_VERTICAL Closes https://github.com/libsdl-org/SDL/pull/13788 --- include/SDL3/SDL_surface.h | 7 ++++--- src/video/SDL_surface.c | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 69f4d69ca9..afd7a82d18 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -96,9 +96,10 @@ typedef enum SDL_ScaleMode */ typedef enum SDL_FlipMode { - SDL_FLIP_NONE, /**< Do not flip */ - SDL_FLIP_HORIZONTAL, /**< flip horizontally */ - SDL_FLIP_VERTICAL /**< flip vertically */ + SDL_FLIP_NONE, /**< Do not flip */ + SDL_FLIP_HORIZONTAL, /**< flip horizontally */ + SDL_FLIP_VERTICAL, /**< flip vertically */ + SDL_FLIP_HORIZONTAL_AND_VERTICAL, /**< flip horizontally and vertically (not a diagonal flip) */ } SDL_FlipMode; #ifndef SDL_INTERNAL diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index a8f8353236..dc897fbab9 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -1874,14 +1874,23 @@ bool SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip) return true; } + bool result = true; switch (flip) { case SDL_FLIP_HORIZONTAL: - return SDL_FlipSurfaceHorizontal(surface); + result = SDL_FlipSurfaceHorizontal(surface); + break; case SDL_FLIP_VERTICAL: - return SDL_FlipSurfaceVertical(surface); + result = SDL_FlipSurfaceVertical(surface); + break; + case SDL_FLIP_HORIZONTAL_AND_VERTICAL: + result &= SDL_FlipSurfaceHorizontal(surface); + result &= SDL_FlipSurfaceVertical(surface); + break; default: - return SDL_InvalidParamError("flip"); + result = SDL_InvalidParamError("flip"); + break; } + return result; } SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props) From 065fdca953c91c4f76d8c7cb8dc824eee3e18c2f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 26 Aug 2025 08:35:51 -0700 Subject: [PATCH 024/134] Disable rumble for the Ultimate 2 Wireless Controller in dongle mode The dongle appears to stop sending input reports with some combination of rumble patterns, easily reproduced with Forza Horizon 5. We'll disable rumble temporarily until @8BitDo can investigate. --- src/joystick/hidapi/SDL_hidapi_8bitdo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/joystick/hidapi/SDL_hidapi_8bitdo.c b/src/joystick/hidapi/SDL_hidapi_8bitdo.c index 95227869da..5561819991 100644 --- a/src/joystick/hidapi/SDL_hidapi_8bitdo.c +++ b/src/joystick/hidapi/SDL_hidapi_8bitdo.c @@ -179,6 +179,11 @@ static bool HIDAPI_Driver8BitDo_InitDevice(SDL_HIDAPI_Device *device) ctx->sensors_supported = true; ctx->rumble_supported = true; ctx->powerstate_supported = true; + + if (!device->is_bluetooth) { + // The dongle appears to just stop sending reports after intense rumble activity + ctx->rumble_supported = false; + } } break; } From f53f054fd6f784c941cfe965632570e83686a779 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 25 Aug 2025 13:33:33 -0400 Subject: [PATCH 025/134] x11: Enable the relative mouse system scale hint. Use the scaled motion values if the relative system scale hint is set, and a custom transformation function is not set. --- src/video/x11/SDL_x11xinput2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index 11afba68ef..3e51408625 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -74,9 +74,13 @@ static bool xinput2_scrolling_supported; static void parse_relative_valuators(SDL_XInput2DeviceInfo *devinfo, const XIRawEvent *rawev) { + SDL_Mouse *mouse = SDL_GetMouse(); double processed_coords[2] = { 0.0, 0.0 }; int values_i = 0, found = 0; + // Use the raw values if a custom transform function is set, or the relative system scale hint is unset. + const bool use_raw_vals = mouse->InputTransform || !mouse->enable_relative_system_scale; + for (int i = 0; i < rawev->valuators.mask_len * 8 && found < 2; ++i) { if (!XIMaskIsSet(rawev->valuators.mask, i)) { continue; @@ -84,7 +88,7 @@ static void parse_relative_valuators(SDL_XInput2DeviceInfo *devinfo, const XIRaw for (int j = 0; j < 2; ++j) { if (devinfo->number[j] == i) { - double current_val = rawev->raw_values[values_i]; + const double current_val = use_raw_vals ? rawev->raw_values[values_i] : rawev->valuators.values[values_i]; if (devinfo->relative[j]) { processed_coords[j] = current_val; @@ -102,7 +106,6 @@ static void parse_relative_valuators(SDL_XInput2DeviceInfo *devinfo, const XIRaw } // Relative mouse motion is delivered to the window with keyboard focus - SDL_Mouse *mouse = SDL_GetMouse(); if (mouse->relative_mode && SDL_GetKeyboardFocus()) { SDL_SendMouseMotion(rawev->time, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]); } From 6516f7a9e8055b38faa0156dd3de760354ef54f8 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 26 Aug 2025 11:43:41 -0400 Subject: [PATCH 026/134] keyboard: Release bound keymaps during the destruction process If a bound keymap is being destroyed, unbind it to ensure that the keyboard state won't be left pointing to an invalid keymap. Backends now no longer need to manually do this when destroying keymaps during keyboard removal and shutdown. --- src/events/SDL_keymap.c | 4 ++++ src/video/wayland/SDL_waylandevents.c | 1 - src/video/windows/SDL_windowskeyboard.c | 1 - src/video/x11/SDL_x11keyboard.c | 2 -- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c index 21d287ccaa..5b6261e6be 100644 --- a/src/events/SDL_keymap.c +++ b/src/events/SDL_keymap.c @@ -207,6 +207,10 @@ void SDL_DestroyKeymap(SDL_Keymap *keymap) return; } + if (keymap == SDL_GetCurrentKeymap(true)) { + SDL_SetKeymap(NULL, false); + } + SDL_DestroyHashTable(keymap->scancode_to_keycode); SDL_DestroyHashTable(keymap->keycode_to_scancode); SDL_free(keymap); diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 20138d0551..f9209143d9 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -2258,7 +2258,6 @@ static void Wayland_SeatDestroyKeyboard(SDL_WaylandSeat *seat, bool send_event) if (seat->keyboard.sdl_keymap) { if (seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts && seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout] == SDL_GetCurrentKeymap(true)) { - SDL_SetKeymap(NULL, false); SDL_SetModState(SDL_KMOD_NONE); } for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c index c8342c3cef..081bcaebe2 100644 --- a/src/video/windows/SDL_windowskeyboard.c +++ b/src/video/windows/SDL_windowskeyboard.c @@ -240,7 +240,6 @@ void WIN_QuitKeyboard(SDL_VideoDevice *_this) } #endif // !SDL_DISABLE_WINDOWS_IME - SDL_SetKeymap(NULL, false); for (int i = 0; i < keymap_cache_size; ++i) { SDL_DestroyKeymap(keymap_cache[i].keymap); } diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index d26087a12f..45532e3ba7 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -380,7 +380,6 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) if (data->keyboard.xkb_enabled) { XkbStateRec state; - SDL_SetKeymap(NULL, false); for (unsigned int i = 0; i < XkbNumKbdGroups; ++i) { SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]); data->keyboard.xkb.keymaps[i] = SDL_CreateKeymap(false); @@ -515,7 +514,6 @@ void X11_QuitKeyboard(SDL_VideoDevice *_this) #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB if (data->keyboard.xkb_enabled) { - SDL_SetKeymap(NULL, false); for (int i = 0; i < XkbNumKbdGroups; ++i) { SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]); data->keyboard.xkb.keymaps[i] = NULL; From 5b688514c754fc7f91c676bc75e923f58f576b5c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 26 Aug 2025 14:43:59 -0400 Subject: [PATCH 027/134] wikiheaders: Don't autolink things that are already linked. This is a dirty hack, but it currently works for our purposes. We just refuse to link an eligible string (like, say...`SDL_shadercross`), if there's a '[' character right before it. This WILL NOT WORK if the string is in the middle of a link! For example, if you had `[please refer to SDL_shadercross](https://example.com/)`, this will still fail in the usual ways, but we're not building out a robust parser here at the moment, and this catches the most common problem. Fixes #13160. Fixes https://github.com/libsdl-org/sdlwiki/issues/752 Closes https://github.com/libsdl-org/sdlwiki/pull/725 --- build-scripts/wikiheaders.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl index c2732af52e..4896a2e669 100755 --- a/build-scripts/wikiheaders.pl +++ b/build-scripts/wikiheaders.pl @@ -343,7 +343,7 @@ sub wikify_chunk { # Convert obvious API things to wikilinks. if (defined $apiprefixregex) { - $str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms; + $str =~ s/(\A|[^\/a-zA-Z0-9_\[])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms; } $str = $codedstr . $str; From 15e60cd37f7e5599f70e0256369232d4f5127893 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 26 Aug 2025 18:55:26 +0000 Subject: [PATCH 028/134] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_render.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 29ea295ea2..d8f1bf4f4d 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -2720,7 +2720,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderVSync(SDL_Renderer *renderer, int * break. If the text goes out of the window, it's gone. * * For serious text rendering, there are several good options, such as - * SDL_ttf, stb_truetype, or other external libraries. + * [SDL_ttf](https://wiki.libsdl.org/SDL3_ttf/FrontPage) + * , stb_truetype, or other external libraries. * * On first use, this will create an internal texture for rendering glyphs. * This texture will live until the renderer is destroyed. From 355c001a541d286997a321a63a6eaed306982bae Mon Sep 17 00:00:00 2001 From: Semphris Date: Tue, 6 May 2025 22:48:52 -0400 Subject: [PATCH 029/134] Implement IFileDialog for Windows --- src/dialog/windows/SDL_windowsdialog.c | 315 +++++++++++++++++++++++++ 1 file changed, 315 insertions(+) diff --git a/src/dialog/windows/SDL_windowsdialog.c b/src/dialog/windows/SDL_windowsdialog.c index 2f4a12e813..ba9d9224b8 100644 --- a/src/dialog/windows/SDL_windowsdialog.c +++ b/src/dialog/windows/SDL_windowsdialog.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "../../core/windows/SDL_windows.h" #include "../../thread/SDL_systhread.h" @@ -35,9 +36,11 @@ typedef struct { bool is_save; wchar_t *filters_str; + int nfilters; char *default_file; SDL_Window *parent; DWORD flags; + bool allow_many; SDL_DialogFileCallback callback; void *userdata; char *title; @@ -48,6 +51,7 @@ typedef struct typedef struct { SDL_Window *parent; + bool allow_many; SDL_DialogFileCallback callback; char *default_folder; void *userdata; @@ -101,18 +105,318 @@ char *clear_filt_names(const char *filt) return cleared; } +bool windows_ShowModernFileFolderDialog(SDL_FileDialogType dialog_type, const char *default_file, SDL_Window *parent, bool allow_many, SDL_DialogFileCallback callback, void *userdata, const char *title, const char *accept, const char *cancel, wchar_t *filter_wchar, int nfilters) +{ + bool is_save = dialog_type == SDL_FILEDIALOG_SAVEFILE; + bool is_folder = dialog_type == SDL_FILEDIALOG_OPENFOLDER; + + if (is_save) { + // Just in case; the code relies on that + allow_many = false; + } + + IFileDialog *pFileDialog = NULL; + IFileOpenDialog *pFileOpenDialog = NULL; + IFileDialog2 *pFileDialog2 = NULL; + IShellItemArray *pItemArray = NULL; + IShellItem *pItem = NULL; + IShellItem *pFolderItem = NULL; + LPWSTR filePath = NULL; + COMDLG_FILTERSPEC *filter_data = NULL; + char **files = NULL; + wchar_t *title_w = NULL; + wchar_t *accept_w = NULL; + wchar_t *cancel_w = NULL; + FILEOPENDIALOGOPTIONS pfos; + + wchar_t *default_file_w = NULL; + wchar_t *default_folder_w = NULL; + + bool success = false; + bool co_init = false; + + if (filter_wchar && nfilters > 0) { + wchar_t *filter_ptr = filter_wchar; + filter_data = SDL_calloc(sizeof(COMDLG_FILTERSPEC), nfilters); + if (!filter_data) { + goto quit; + } + + for (int i = 0; i < nfilters; i++) { + filter_data[i].pszName = filter_ptr; + filter_ptr += SDL_wcslen(filter_ptr) + 1; + filter_data[i].pszSpec = filter_ptr; + filter_ptr += SDL_wcslen(filter_ptr) + 1; + } + + // assert(*filter_ptr == L'\0'); + } + + if (title && !(title_w = WIN_UTF8ToStringW(title))) { + goto quit; + } + + if (accept && !(accept_w = WIN_UTF8ToStringW(accept))) { + goto quit; + } + + if (cancel && !(cancel_w = WIN_UTF8ToStringW(cancel))) { + goto quit; + } + + if (default_file) { + default_folder_w = WIN_UTF8ToStringW(default_file); + + if (!default_folder_w) { + goto quit; + } + + for (wchar_t *chrptr = default_folder_w; *chrptr; chrptr++) { + if (*chrptr == L'/' || *chrptr == L'\\') { + default_file_w = chrptr; + } + } + + if (!default_file_w) { + default_file_w = default_folder_w; + default_folder_w = NULL; + } else { + *default_file_w = L'\0'; + default_file_w++; + + if (SDL_wcslen(default_file_w) == 0) { + default_file_w = NULL; + } + } + } + +#define CHECK(op) if (!SUCCEEDED(op)) { goto quit; } + + CHECK(WIN_CoInitialize()); + + co_init = true; + + CHECK(CoCreateInstance(is_save ? &CLSID_FileSaveDialog : &CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, &IID_IFileDialog, (void**)&pFileDialog)); + CHECK(pFileDialog->lpVtbl->QueryInterface(pFileDialog, &IID_IFileDialog2, (void**)&pFileDialog2)); + + if (allow_many) { + CHECK(pFileDialog->lpVtbl->QueryInterface(pFileDialog, &IID_IFileOpenDialog, (void**)&pFileOpenDialog)); + } + + CHECK(pFileDialog2->lpVtbl->GetOptions(pFileDialog2, &pfos)); + + pfos |= FOS_NOCHANGEDIR; + if (allow_many) pfos |= FOS_ALLOWMULTISELECT; + if (is_save) pfos |= FOS_OVERWRITEPROMPT; + if (is_folder) pfos |= FOS_PICKFOLDERS; + + CHECK(pFileDialog2->lpVtbl->SetOptions(pFileDialog2, pfos)); + + if (cancel_w) { + CHECK(pFileDialog2->lpVtbl->SetCancelButtonLabel(pFileDialog2, cancel_w)); + } + + if (accept_w) { + CHECK(pFileDialog->lpVtbl->SetOkButtonLabel(pFileDialog, accept_w)); + } + + if (title_w) { + CHECK(pFileDialog->lpVtbl->SetTitle(pFileDialog, title_w)); + } + + if (filter_data) { + CHECK(pFileDialog->lpVtbl->SetFileTypes(pFileDialog, nfilters, filter_data)); + } + + // SetFolder would enforce using the same location each and every time, but + // Windows docs recommend against it + if (default_folder_w) { + CHECK(SHCreateItemFromParsingName(default_folder_w, NULL, &IID_IShellItem, (void**)&pFolderItem)); + CHECK(pFileDialog->lpVtbl->SetDefaultFolder(pFileDialog, pFolderItem)); + } + + if (default_file_w) { + CHECK(pFileDialog->lpVtbl->SetFileName(pFileDialog, default_file_w)); + } + + if (parent) { + HWND window = (HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(parent), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); + + HRESULT hr = pFileDialog->lpVtbl->Show(pFileDialog, window); + + if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { + const char * const results[] = { NULL }; + UINT selected_filter; + + // This is a one-based index, not zero-based. Doc link in similar comment below + CHECK(pFileDialog->lpVtbl->GetFileTypeIndex(pFileDialog, &selected_filter)); + callback(userdata, results, selected_filter - 1); + success = true; + goto quit; + } else if (!SUCCEEDED(hr)) { + goto quit; + } + } else { + HRESULT hr = pFileDialog->lpVtbl->Show(pFileDialog, NULL); + + if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { + const char * const results[] = { NULL }; + UINT selected_filter; + + // This is a one-based index, not zero-based. Doc link in similar comment below + CHECK(pFileDialog->lpVtbl->GetFileTypeIndex(pFileDialog, &selected_filter)); + callback(userdata, results, selected_filter - 1); + success = true; + goto quit; + } else if (!SUCCEEDED(hr)) { + goto quit; + } + } + + if (allow_many) { + DWORD nResults; + UINT selected_filter; + + CHECK(pFileOpenDialog->lpVtbl->GetResults(pFileOpenDialog, &pItemArray)); + CHECK(pFileDialog->lpVtbl->GetFileTypeIndex(pFileDialog, &selected_filter)); + CHECK(pItemArray->lpVtbl->GetCount(pItemArray, &nResults)); + + files = SDL_calloc(nResults + 1, sizeof(char*)); + if (!files) { + goto quit; + } + char** files_ptr = files; + + for (DWORD i = 0; i < nResults; i++) { + CHECK(pItemArray->lpVtbl->GetItemAt(pItemArray, i, &pItem)); + CHECK(pItem->lpVtbl->GetDisplayName(pItem, SIGDN_FILESYSPATH, &filePath)); + + *(files_ptr++) = WIN_StringToUTF8(filePath); + + CoTaskMemFree(filePath); + filePath = NULL; + pItem->lpVtbl->Release(pItem); + pItem = NULL; + } + + callback(userdata, (const char * const *) files, selected_filter - 1); + success = true; + } else { + // This is a one-based index, not zero-based. + // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifiledialog-getfiletypeindex#parameters + UINT selected_filter; + + CHECK(pFileDialog->lpVtbl->GetResult(pFileDialog, &pItem)); + CHECK(pFileDialog->lpVtbl->GetFileTypeIndex(pFileDialog, &selected_filter)); + CHECK(pItem->lpVtbl->GetDisplayName(pItem, SIGDN_FILESYSPATH, &filePath)); + + char *file = WIN_StringToUTF8(filePath); + if (!file) { + goto quit; + } + const char * const results[] = { file, NULL }; + callback(userdata, results, selected_filter - 1); + success = true; + SDL_free(file); + } + + success = true; + +#undef CHECK + +quit: + if (!success) { + WIN_SetError("dialogg"); + callback(userdata, NULL, -1); + } + + if (co_init) { + WIN_CoUninitialize(); + } + + if (pFileOpenDialog) { + pFileOpenDialog->lpVtbl->Release(pFileOpenDialog); + } + + if (pFileDialog2) { + pFileDialog2->lpVtbl->Release(pFileDialog2); + } + + if (pFileDialog) { + pFileDialog->lpVtbl->Release(pFileDialog); + } + + if (pItem) { + pItem->lpVtbl->Release(pItem); + } + + if (pFolderItem) { + pFolderItem->lpVtbl->Release(pFolderItem); + } + + if (pItemArray) { + pItemArray->lpVtbl->Release(pItemArray); + } + + if (filePath) { + CoTaskMemFree(filePath); + } + + // If both default_file_w and default_folder_w are non-NULL, then + // default_file_w is a pointer into default_folder_w. + if (default_folder_w) { + SDL_free(default_folder_w); + } else if (default_file_w) { + SDL_free(default_file_w); + } + + if (title_w) { + SDL_free(title_w); + } + + if (accept_w) { + SDL_free(accept_w); + } + + if (cancel_w) { + SDL_free(cancel_w); + } + + if (filter_data) { + SDL_free(filter_data); + } + + if (files) { + for (char** files_ptr = files; *files_ptr; files_ptr++) { + SDL_free(*files_ptr); + } + SDL_free(files); + } + + return success; +} + // TODO: The new version of file dialogs void windows_ShowFileDialog(void *ptr) { + winArgs *args = (winArgs *) ptr; bool is_save = args->is_save; const char *default_file = args->default_file; SDL_Window *parent = args->parent; DWORD flags = args->flags; + bool allow_many = args->allow_many; SDL_DialogFileCallback callback = args->callback; void *userdata = args->userdata; const char *title = args->title; + const char *accept = args->accept; + const char *cancel = args->cancel; wchar_t *filter_wchar = args->filters_str; + int nfilters = args->nfilters; + + if (windows_ShowModernFileFolderDialog(is_save ? SDL_FILEDIALOG_SAVEFILE : SDL_FILEDIALOG_OPENFILE, default_file, parent, allow_many, callback, userdata, title, accept, cancel, filter_wchar, nfilters)) { + return; + } /* GetOpenFileName and GetSaveFileName have the same signature (yes, LPOPENFILENAMEW even for the save dialog) */ @@ -407,7 +711,15 @@ void windows_ShowFolderDialog(void *ptr) SDL_DialogFileCallback callback = args->callback; void *userdata = args->userdata; HWND parent = NULL; + int allow_many = args->allow_many; + char *default_folder = args->default_folder; const char *title = args->title; + const char *accept = args->accept; + const char *cancel = args->cancel; + + if (windows_ShowModernFileFolderDialog(SDL_FILEDIALOG_OPENFOLDER, default_folder, window, allow_many, callback, userdata, title, accept, cancel, NULL, 0)) { + return; + } if (window) { parent = (HWND) SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); @@ -532,9 +844,11 @@ static void ShowFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_ args->is_save = is_save; args->filters_str = filters_str; + args->nfilters = nfilters; args->default_file = default_location ? SDL_strdup(default_location) : NULL; args->parent = window; args->flags = flags; + args->allow_many = allow_many; args->callback = callback; args->userdata = userdata; args->title = title ? SDL_strdup(title) : NULL; @@ -571,6 +885,7 @@ void ShowFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Windo } args->parent = window; + args->allow_many = allow_many; args->callback = callback; args->default_folder = default_location ? SDL_strdup(default_location) : NULL; args->userdata = userdata; From c709b8ed9899a58d0dd43a044b3447e2215777f1 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Sat, 24 May 2025 12:16:23 +0800 Subject: [PATCH 030/134] process(windows): try sending WM_CLOSE for graceful process termination --- src/process/windows/SDL_windowsprocess.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/process/windows/SDL_windowsprocess.c b/src/process/windows/SDL_windowsprocess.c index 65d8a394a0..3ba98af97c 100644 --- a/src/process/windows/SDL_windowsprocess.c +++ b/src/process/windows/SDL_windowsprocess.c @@ -520,8 +520,23 @@ done: return result; } +static BOOL CALLBACK terminate_app(HWND hwnd, LPARAM proc_id) +{ + DWORD current_proc_id = 0; + GetWindowThreadProcessId(hwnd, ¤t_proc_id); + if (current_proc_id == (DWORD) proc_id) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + } + return TRUE; +} + bool SDL_SYS_KillProcess(SDL_Process *process, bool force) { + if (!force) { + EnumWindows(terminate_app, (LPARAM) process->internal->process_information.dwProcessId); + PostThreadMessage(process->internal->process_information.dwThreadId, WM_CLOSE, 0, 0); + return true; + } if (!TerminateProcess(process->internal->process_information.hProcess, 1)) { return WIN_SetError("TerminateProcess failed"); } From 95c44dcdc3337e185aa19b7ea23261660acc2fd1 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Sat, 24 May 2025 14:35:38 +0800 Subject: [PATCH 031/134] process(windows): fallback to GenerateConsoleCtrlEvent and TerminateProcess if necessary --- src/process/windows/SDL_windowsprocess.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/process/windows/SDL_windowsprocess.c b/src/process/windows/SDL_windowsprocess.c index 3ba98af97c..cae6cfe1fc 100644 --- a/src/process/windows/SDL_windowsprocess.c +++ b/src/process/windows/SDL_windowsprocess.c @@ -520,12 +520,12 @@ done: return result; } -static BOOL CALLBACK terminate_app(HWND hwnd, LPARAM proc_id) +static BOOL CALLBACK terminate_app(HWND hwnd, LPARAM lparam) { - DWORD current_proc_id = 0; + DWORD current_proc_id = 0, *term_info = (DWORD *) lparam; GetWindowThreadProcessId(hwnd, ¤t_proc_id); - if (current_proc_id == (DWORD) proc_id) { - PostMessage(hwnd, WM_CLOSE, 0, 0); + if (current_proc_id == term_info[0] && PostMessage(hwnd, WM_CLOSE, 0, 0)) { + term_info[1]++; } return TRUE; } @@ -533,9 +533,17 @@ static BOOL CALLBACK terminate_app(HWND hwnd, LPARAM proc_id) bool SDL_SYS_KillProcess(SDL_Process *process, bool force) { if (!force) { - EnumWindows(terminate_app, (LPARAM) process->internal->process_information.dwProcessId); - PostThreadMessage(process->internal->process_information.dwThreadId, WM_CLOSE, 0, 0); - return true; + // term_info[0] is the process ID, term_info[1] is number of successful tries + DWORD term_info[2]; + term_info[0] = process->internal->process_information.dwProcessId; + term_info[1] = 0; + EnumWindows(terminate_app, (LPARAM) &term_info); + if (term_info[1] || PostThreadMessage(process->internal->process_information.dwThreadId, WM_CLOSE, 0, 0)) { + return true; + } + if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, term_info[0])) { + return true; + } } if (!TerminateProcess(process->internal->process_information.hProcess, 1)) { return WIN_SetError("TerminateProcess failed"); From e55f636d1e05792d6d650b3246861c4b84caf25f Mon Sep 17 00:00:00 2001 From: Sylvain Date: Fri, 22 Aug 2025 10:43:46 +0200 Subject: [PATCH 032/134] Android: improve check to know if running on ChromeBook or emulator. https://stackoverflow.com/questions/39784415/how-to-detect-programmatically-if-android-app-is-running-in-chrome-book-or-in --- .../libsdl/app/HIDDeviceBLESteamController.java | 2 +- .../java/org/libsdl/app/HIDDeviceManager.java | 2 +- .../src/main/java/org/libsdl/app/SDLActivity.java | 15 +++++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java index df4763accb..bf1ca2149d 100644 --- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -161,7 +161,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe mDevice = device; mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier()); mIsRegistered = false; - mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + mIsChromebook = SDLActivity.isChromebook(); mOperations = new LinkedList(); mHandler = new Handler(Looper.getMainLooper()); diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index f7c56c44e2..9626b87321 100644 --- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -108,7 +108,7 @@ public class HIDDeviceManager { HIDDeviceRegisterCallback(); mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE); - mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + mIsChromebook = SDLActivity.isChromebook(); // if (shouldClear) { // SharedPreferences.Editor spedit = mSharedPreferences.edit(); diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 278be46825..03e4a4244f 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -1331,10 +1331,17 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh * This method is called by SDL using JNI. */ public static boolean isChromebook() { - if (getContext() == null) { - return false; + // https://stackoverflow.com/questions/39784415/how-to-detect-programmatically-if-android-app-is-running-in-chrome-book-or-in + if (getContext() != null) { + if (getContext().getPackageManager().hasSystemFeature("org.chromium.arc") + || getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management")) { + return true; + } } - return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); + + // Running on AVD emulator + boolean isChromebookEmulator = (Build.MODEL != null && Build.MODEL.startsWith("sdk_gpc_")); + return isChromebookEmulator; } /** @@ -2117,7 +2124,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh int requestCode; boolean multipleChoice; } - + /** * This method is called by SDL using JNI. */ From 5a06ef5c8f7f98251391c457eb1a51b3cb861a01 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 26 Aug 2025 22:16:21 -0400 Subject: [PATCH 033/134] keyboard: Don't clear the keymap when it is set to auto release Don't unset the keymap when it is set to auto release, or it will infinitely recurse. --- src/events/SDL_keymap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c index 5b6261e6be..9960aa9d60 100644 --- a/src/events/SDL_keymap.c +++ b/src/events/SDL_keymap.c @@ -207,7 +207,7 @@ void SDL_DestroyKeymap(SDL_Keymap *keymap) return; } - if (keymap == SDL_GetCurrentKeymap(true)) { + if (!keymap->auto_release && keymap == SDL_GetCurrentKeymap(true)) { SDL_SetKeymap(NULL, false); } From 5291e9bbbdaf06288adf3d49afc45f69eba5a31c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 26 Aug 2025 19:28:59 -0700 Subject: [PATCH 034/134] Minor cleanup --- src/joystick/hidapi/SDL_hidapi_flydigi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/joystick/hidapi/SDL_hidapi_flydigi.c b/src/joystick/hidapi/SDL_hidapi_flydigi.c index 938d4c960d..fa1606926e 100644 --- a/src/joystick/hidapi/SDL_hidapi_flydigi.c +++ b/src/joystick/hidapi/SDL_hidapi_flydigi.c @@ -42,10 +42,10 @@ enum SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS }; -/* Rate of IMU Sensor Packets over wireless Dongle observed in testcontroller tool at 1000hz */ +/* Rate of IMU Sensor Packets over wireless dongle observed in testcontroller at 1000hz */ #define SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ 1000 #define SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ) -/* Rate of IMU Sensor Packets over wired observed in testcontroller tool connection at 500hz */ +/* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 500hz */ #define SENSOR_INTERVAL_VADER_PRO4_WIRED_RATE_HZ 500 #define SENSOR_INTERVAL_VADER_PRO4_WIRED_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER_PRO4_WIRED_RATE_HZ) @@ -270,7 +270,6 @@ static bool HIDAPI_DriverFlydigi_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy } if (ctx->sensors_supported) { - const float flSensorRate = ctx->wireless ? (float)SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ : (float)SENSOR_INTERVAL_VADER_PRO4_WIRED_RATE_HZ; SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, flSensorRate); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, flSensorRate); @@ -437,10 +436,10 @@ static void HIDAPI_DriverFlydigi_HandleStatePacket(SDL_Joystick *joystick, SDL_D // These values were estimated using the testcontroller tool in lieux of hard data sheet references. const float flPitchAndYawScale = DEG2RAD(72000.0f); const float flRollScale = DEG2RAD(1200.0f); + values[0] = HIDAPI_RemapVal(-1.0f * LOAD16(data[26], data[27]), INT16_MIN, INT16_MAX, -flPitchAndYawScale, flPitchAndYawScale); values[1] = HIDAPI_RemapVal(-1.0f * LOAD16(data[18], data[20]), INT16_MIN, INT16_MAX, -flPitchAndYawScale, flPitchAndYawScale); values[2] = HIDAPI_RemapVal(-1.0f * LOAD16(data[29], data[30]), INT16_MIN, INT16_MAX, -flRollScale, flRollScale); - SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, values, 3); values[0] = -LOAD16(data[11], data[12]) * ctx->accelScale; // Acceleration along pitch axis From 26d6352500b29bd7fb655e9e1c04c61fe0aeef2d Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 27 Aug 2025 11:31:00 +0200 Subject: [PATCH 035/134] Fixed bug #13794: prevent to use the onBackPressed callback API that is enabled on API36 --- android-project/app/src/main/AndroidManifest.xml | 1 + test/android/cmake/AndroidManifest.xml.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml index f3a7cd5ce6..ab43f76aef 100644 --- a/android-project/app/src/main/AndroidManifest.xml +++ b/android-project/app/src/main/AndroidManifest.xml @@ -71,6 +71,7 @@ android:icon="@mipmap/ic_launcher" android:allowBackup="true" android:theme="@style/AppTheme" + android:enableOnBackInvokedCallback="false" android:hardwareAccelerated="true" > \n\n"; + print $fh "# $envvartitle\n\n"; + + if (defined $envvardesc) { + my $desc = "$envvardesc"; + $desc =~ s/\\n/\n/g; # replace "\n" strings with actual newlines. + print $fh "$desc\n\n"; + } + + print $fh "## Environment Variable List\n\n"; + + foreach (sort keys %headersyms) { + my $sym = $_; + next if $headersymstype{$sym} != 2; # not a #define? skip it. + my $hint = "$_"; + next if not $hint =~ s/$envvarsymregex/$replace/ee; + + my $brief = $$briefsref{$sym}; + if (not defined $brief) { + $brief = ''; + } else { + $brief = "$brief"; + chomp($brief); + my $thiswikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md'; # default to MarkDown for new stuff. + $brief = ": " . dewikify($thiswikitype, $brief); + } + print $fh "- [$hint]($sym)$brief\n"; + } + + print $fh "\n"; + + close($fh); + + rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n"); +} + + + + my $incpath = "$srcpath"; $incpath .= "/$incsubdir" if $incsubdir ne ''; @@ -2733,6 +2792,11 @@ __EOF__ generate_quickref(\%briefs, "$wikipath/QuickReference.md", 0); generate_quickref(\%briefs, "$wikipath/QuickReferenceNoUnicode.md", 1); } + + if ($envvarenabled and defined $envvarsymregex and defined $envvarsymreplace) { + generate_envvar_wiki_page(\%briefs, "$wikipath/EnvironmentVariables.md"); + } + } elsif ($copy_direction == -2) { # --copy-to-manpages # This only takes from the wiki data, since it has sections we omit from the headers, like code examples. From d14cbd7b509c986708e03708096015c4023099f6 Mon Sep 17 00:00:00 2001 From: eafton Date: Tue, 2 Sep 2025 23:22:48 +0300 Subject: [PATCH 105/134] Introduce X11 toolkit and make message dialogs use it (#13855) --- src/video/x11/SDL_x11messagebox.c | 1300 +++----------------- src/video/x11/SDL_x11settings.c | 4 - src/video/x11/SDL_x11settings.h | 4 + src/video/x11/SDL_x11sym.h | 4 + src/video/x11/SDL_x11toolkit.c | 1866 +++++++++++++++++++++++++++++ src/video/x11/SDL_x11toolkit.h | 225 ++++ 6 files changed, 2250 insertions(+), 1153 deletions(-) create mode 100644 src/video/x11/SDL_x11toolkit.c create mode 100644 src/video/x11/SDL_x11toolkit.h diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c index dd4a1bf691..32a3887dde 100644 --- a/src/video/x11/SDL_x11messagebox.c +++ b/src/video/x11/SDL_x11messagebox.c @@ -23,22 +23,13 @@ #ifdef SDL_VIDEO_DRIVER_X11 -#include "SDL_x11video.h" -#include "SDL_x11dyn.h" #include "SDL_x11messagebox.h" - -#include -#include +#include "SDL_x11toolkit.h" #ifndef SDL_FORK_MESSAGEBOX #define SDL_FORK_MESSAGEBOX 1 #endif -#define SDL_SET_LOCALE 1 -#define SDL_DIALOG_ELEMENT_PADDING 4 -#define SDL_DIALOG_ELEMENT_PADDING_2 12 -#define SDL_DIALOG_ELEMENT_PADDING_3 8 - #if SDL_FORK_MESSAGEBOX #include #include @@ -46,1183 +37,194 @@ #include #endif -#define MAX_BUTTONS 8 // Maximum number of buttons supported -#define MIN_BUTTON_WIDTH 32 // Minimum button width -#define MIN_DIALOG_WIDTH 10 // Minimum dialog width -#define MIN_DIALOG_HEIGHT 10 // Minimum dialog height - -static const char g_MessageBoxFontLatin1[] = - "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1"; - -static const char *g_MessageBoxFont[] = { - "-*-*-medium-r-normal--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) - "-*-*-medium-r-*--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) - "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems) - "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode. - "-*-*-medium-r-normal--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. - "-*-*-medium-r-*--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. - "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems) - "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1. - NULL -}; - -static const char *g_IconFont = "-*-*-bold-r-normal-*-18-*-*-*-*-*-iso8859-1[33 88 105]"; - -static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = { - { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, - { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_TEXT, - { 127, 120, 127 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, - { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, - { 235, 235, 235 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, -}; - -typedef struct SDL_MessageBoxButtonDataX11 +typedef struct SDL_MessageBoxCallbackDataX11 { - int length; // Text length - int text_a; - int text_d; - - SDL_Rect text_rect; - SDL_Rect rect; // Rectangle for entire button + int *buttonID; + SDL_ToolkitWindowX11 *window; +} SDL_MessageBoxCallbackDataX11; - const SDL_MessageBoxButtonData *buttondata; // Button data from caller -} SDL_MessageBoxButtonDataX11; - -typedef struct TextLineData +typedef struct SDL_MessageBoxControlsX11 { - int length; // String length of this text line - const char *text; // Text for this line - SDL_Rect rect; -} TextLineData; - -typedef struct SDL_MessageBoxDataX11 -{ - Display *display; - int screen; - Window window; - Visual *visual; - Colormap cmap; -#ifdef SDL_VIDEO_DRIVER_X11_XDBE - XdbeBackBuffer buf; - bool xdbe; // Whether Xdbe is present or not -#endif - long event_mask; - Atom wm_protocols; - Atom wm_delete_message; -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR - bool xrandr; // Whether Xrandr is present or not -#endif - - int dialog_width; // Dialog box width. - int dialog_height; // Dialog box height. - - XFontSet font_set; // for UTF-8 systems - XFontStruct *font_struct; // Latin1 (ASCII) fallback. - int numlines; // Count of Text lines. - int text_height; // Height for text lines. - TextLineData *linedata; - - char icon_char; // Icon, '\0' indicates that the messsage box has no icon. - XFontStruct *icon_char_font; - SDL_Rect icon_box_rect; - int icon_char_x; - int icon_char_y; - - int *pbuttonid; // Pointer to user return buttonID value. - - int button_press_index; // Index into buttondata/buttonpos for button which is pressed (or -1). - int mouse_over_index; // Index into buttondata/buttonpos for button mouse is over (or -1). - - int numbuttons; // Count of buttons. - const SDL_MessageBoxButtonData *buttondata; - SDL_MessageBoxButtonDataX11 buttonpos[MAX_BUTTONS]; - - /* Colors for rendering widgets */ - XColor xcolor[SDL_MESSAGEBOX_COLOR_COUNT]; - XColor xcolor_bevel_l1; - XColor xcolor_bevel_l2; - XColor xcolor_bevel_d; - XColor xcolor_pressed; - - /* Colors for rendering icons */ - XColor xcolor_black; - XColor xcolor_red; - XColor xcolor_red_darker; - XColor xcolor_white; - XColor xcolor_yellow; - XColor xcolor_blue; - XColor xcolor_bg_shadow; - + SDL_ToolkitWindowX11 *window; + SDL_ToolkitControlX11 *icon; + SDL_ToolkitControlX11 fake_icon; + SDL_ToolkitControlX11 *message; + SDL_ToolkitControlX11 **buttons; const SDL_MessageBoxData *messageboxdata; -} SDL_MessageBoxDataX11; +} SDL_MessageBoxControlsX11; -// Int helpers -static SDL_INLINE int IntMax(int a, int b) +static void X11_MessageBoxButtonCallback(SDL_ToolkitControlX11 *control, void *data) { - return (a > b) ? a : b; + SDL_MessageBoxCallbackDataX11 *cbdata; + + cbdata = (SDL_MessageBoxCallbackDataX11 *)data; + *cbdata->buttonID = X11Toolkit_GetButtonControlData(control)->buttonID; + X11Toolkit_SignalWindowClose(cbdata->window); } -static SDL_INLINE int IntClamp(int a, int b, int c) -{ - if (a < b) return b; - if (a > c) return c; - return a; -} - -static void GetTextWidthHeightForFont(SDL_MessageBoxDataX11 *data, XFontStruct *font, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent) -{ - XCharStruct text_structure; - int font_direction, font_descent; - X11_XTextExtents(font, str, nbytes, - &font_direction, font_ascent, &font_descent, - &text_structure); - *pwidth = text_structure.width; - *pheight = text_structure.ascent + text_structure.descent; -} - -// Return width and height for a string. -static void GetTextWidthHeight(SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent, int *font_descent) -{ -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - XFontSetExtents *extents; - XRectangle overall_ink, overall_logical; - extents = X11_XExtentsOfFontSet(data->font_set); - X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical); - *pwidth = overall_logical.width; - *pheight = overall_logical.height; - *font_ascent = -extents->max_logical_extent.y; - *font_descent = extents->max_logical_extent.height - *font_ascent; - } else -#endif - { - XCharStruct text_structure; - int font_direction; - X11_XTextExtents(data->font_struct, str, nbytes, - &font_direction, font_ascent, font_descent, - &text_structure); - *pwidth = text_structure.width; - *pheight = text_structure.ascent + text_structure.descent; - } -} - -// Return index of button if position x,y is contained therein. -static int GetHitButtonIndex(SDL_MessageBoxDataX11 *data, int x, int y) -{ - int i; - int numbuttons = data->numbuttons; - SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos; - - for (i = 0; i < numbuttons; i++) { - SDL_Rect *rect = &buttonpos[i].rect; - - if ((x >= rect->x) && - (x <= (rect->x + rect->w)) && - (y >= rect->y) && - (y <= (rect->y + rect->h))) { - return i; - } - } - - return -1; -} - -// Initialize SDL_MessageBoxData structure and Display, etc. -static bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData *messageboxdata, int *pbuttonid) -{ - int i; - int numbuttons = messageboxdata->numbuttons; - const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons; - const SDL_MessageBoxColor *colorhints; - - if (numbuttons > MAX_BUTTONS) { - return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS); - } - - data->dialog_width = MIN_DIALOG_WIDTH; - data->dialog_height = MIN_DIALOG_HEIGHT; - data->messageboxdata = messageboxdata; - data->buttondata = buttondata; - data->numbuttons = numbuttons; - data->pbuttonid = pbuttonid; - - // Convert flags to icon character - switch (data->messageboxdata->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { - case SDL_MESSAGEBOX_ERROR: - data->icon_char = 'X'; - break; - case SDL_MESSAGEBOX_WARNING: - data->icon_char = '!'; - break; - case SDL_MESSAGEBOX_INFORMATION: - data->icon_char = 'i'; - break; - default: - data->icon_char = '\0'; - } - - data->display = X11_XOpenDisplay(NULL); - if (!data->display) { - return SDL_SetError("Couldn't open X11 display"); - } - -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR - int xrandr_event_base, xrandr_error_base; - data->xrandr = X11_XRRQueryExtension(data->display, &xrandr_event_base, &xrandr_error_base); -#endif - -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - char **missing = NULL; - int num_missing = 0; - int i_font; - for (i_font = 0; g_MessageBoxFont[i_font]; ++i_font) { - data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont[i_font], - &missing, &num_missing, NULL); - if (missing) { - X11_XFreeStringList(missing); - } - if (data->font_set) { - break; - } - } - if (!data->font_set) { - return SDL_SetError("Couldn't load x11 message box font"); - } - } else -#endif - { - data->font_struct = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1); - if (!data->font_struct) { - return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1); - } - } - - if (data->icon_char != '\0') { - data->icon_char_font = X11_XLoadQueryFont(data->display, g_IconFont); - if (!data->icon_char_font) { - data->icon_char_font = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1); - if (!data->icon_char_font) { - data->icon_char = '\0'; - } - } - } - - if (messageboxdata->colorScheme) { - colorhints = messageboxdata->colorScheme->colors; - } else { - colorhints = g_default_colors; - } - - // Convert colors to 16 bpc XColor format - for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { - data->xcolor[i].flags = DoRed|DoGreen|DoBlue; - data->xcolor[i].red = colorhints[i].r * 257; - data->xcolor[i].green = colorhints[i].g * 257; - data->xcolor[i].blue = colorhints[i].b * 257; - } - - /* Generate bevel and pressed colors */ - data->xcolor_bevel_l1.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bevel_l1.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 12500, 0, 65535); - data->xcolor_bevel_l1.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 12500, 0, 65535); - data->xcolor_bevel_l1.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 12500, 0, 65535); - - data->xcolor_bevel_l2.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bevel_l2.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 32500, 0, 65535); - data->xcolor_bevel_l2.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 32500, 0, 65535); - data->xcolor_bevel_l2.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 32500, 0, 65535); - - data->xcolor_bevel_d.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bevel_d.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red - 22500, 0, 65535); - data->xcolor_bevel_d.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green - 22500, 0, 65535); - data->xcolor_bevel_d.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue - 22500, 0, 65535); - - data->xcolor_pressed.flags = DoRed|DoGreen|DoBlue; - data->xcolor_pressed.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].red - 12500, 0, 65535); - data->xcolor_pressed.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].green - 12500, 0, 65535); - data->xcolor_pressed.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].blue - 12500, 0, 65535); - - /* Icon colors */ - if (data->icon_char != '\0') { - data->xcolor_black.flags = DoRed|DoGreen|DoBlue; - data->xcolor_black.red = 0; - data->xcolor_black.green = 0; - data->xcolor_black.blue = 0; - - data->xcolor_black.flags = DoRed|DoGreen|DoBlue; - data->xcolor_white.red = 65535; - data->xcolor_white.green = 65535; - data->xcolor_white.blue = 65535; - - data->xcolor_red.flags = DoRed|DoGreen|DoBlue; - data->xcolor_red.red = 65535; - data->xcolor_red.green = 0; - data->xcolor_red.blue = 0; - - data->xcolor_red_darker.flags = DoRed|DoGreen|DoBlue; - data->xcolor_red_darker.red = 40535; - data->xcolor_red_darker.green = 0; - data->xcolor_red_darker.blue = 0; - - data->xcolor_yellow.flags = DoRed|DoGreen|DoBlue; - data->xcolor_yellow.red = 65535; - data->xcolor_yellow.green = 65535; - data->xcolor_yellow.blue = 0; - - data->xcolor_blue.flags = DoRed|DoGreen|DoBlue; - data->xcolor_blue.red = 0; - data->xcolor_blue.green = 0; - data->xcolor_blue.blue = 65535; - - data->xcolor_bg_shadow.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bg_shadow.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 12500, 0, 65535); - data->xcolor_bg_shadow.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 12500, 0, 65535); - data->xcolor_bg_shadow.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 12500, 0, 65535); - } - - return true; -} - -static int CountLinesOfText(const char *text) -{ - int result = 0; - while (text && *text) { - const char *lf = SDL_strchr(text, '\n'); - result++; // even without an endline, this counts as a line. - text = lf ? lf + 1 : NULL; - } - return result; -} - -// Calculate and initialize text and button locations. -static bool X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data) -{ - int paddingx2; - int padding2x2; - int text_width_max; - int text_height_total; - int button_height_max; - int button_width_max; - int button_width_total; - int elems_total_height; - int text_ix; +static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp, int *hp) { + int max_button_w; + int max_button_h; + int total_button_w; + int total_text_and_icon_w; + int w; + int h; int i; int t; - const SDL_MessageBoxData *messageboxdata = data->messageboxdata; - - /* Optimization and initialization */ - text_height_total = 0; - text_width_max = 0; - paddingx2 = SDL_DIALOG_ELEMENT_PADDING * 2; - padding2x2 = SDL_DIALOG_ELEMENT_PADDING_2 * 2; - data->icon_char_y = 0; - data->icon_box_rect.w = 0; - data->icon_box_rect.h = 0; - text_ix = 0; - t = 0; - button_width_max = MIN_BUTTON_WIDTH; - button_height_max = 0; - - /* Calculate icon sizing */ - if (data->icon_char != '\0') { - int icon_char_w; - int icon_char_h; - int icon_char_a; - int icon_char_max; - - GetTextWidthHeightForFont(data, data->icon_char_font, &data->icon_char, 1, &icon_char_w, &icon_char_h, &icon_char_a); - data->icon_box_rect.w = icon_char_w + paddingx2; - data->icon_box_rect.h = icon_char_h + paddingx2; - icon_char_max = IntMax(data->icon_box_rect.w, data->icon_box_rect.h) + 2; - data->icon_box_rect.w = icon_char_max; - data->icon_box_rect.h = icon_char_max; - data->icon_box_rect.y = 0; - data->icon_box_rect.x = 0; - data->icon_char_y = icon_char_a + data->icon_box_rect.y + (data->icon_box_rect.h - icon_char_h)/2 + 1; - data->icon_char_x = data->icon_box_rect.x + (data->icon_box_rect.w - icon_char_w)/2 + 1; - data->icon_box_rect.w += 2; - data->icon_box_rect.h += 2; - } - - // Go over text and break linefeeds into separate lines. - if (messageboxdata && messageboxdata->message[0]) { - int iascent; - const char *text = messageboxdata->message; - const int linecount = CountLinesOfText(text); - TextLineData *plinedata = (TextLineData *)SDL_malloc(sizeof(TextLineData) * linecount); - - if (!plinedata) { - return false; - } - - data->linedata = plinedata; - data->numlines = linecount; - iascent = 0; - - for (i = 0; i < linecount; i++) { - const char *lf = SDL_strchr(text, '\n'); - const int length = lf ? (lf - text) : SDL_strlen(text); - int ascent; - int descent; - - plinedata[i].text = text; - - GetTextWidthHeight(data, text, length, &plinedata[i].rect.w, &plinedata[i].rect.h, &ascent, &descent); - - // Text widths are the largest we've ever seen. - text_width_max = IntMax(text_width_max, plinedata[i].rect.w); - - plinedata[i].length = length; - if (lf && (lf > text) && (lf[-1] == '\r')) { - plinedata[i].length--; - } - - if (i > 0) { - plinedata[i].rect.y = ascent + descent + plinedata[i-1].rect.y; - } else { - plinedata[i].rect.y = data->icon_box_rect.y + SDL_DIALOG_ELEMENT_PADDING + ascent; - iascent = ascent; - } - plinedata[i].rect.x = text_ix = data->icon_box_rect.x + data->icon_box_rect.w + SDL_DIALOG_ELEMENT_PADDING_2; - text += length + 1; - - // Break if there are no more linefeeds. - if (!lf) { - break; - } - } - - text_height_total = plinedata[linecount-1].rect.y + plinedata[linecount-1].rect.h - iascent - data->icon_box_rect.y - SDL_DIALOG_ELEMENT_PADDING; - } - - // Loop through all buttons and calculate the button widths and height. - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].buttondata = &data->buttondata[i]; - data->buttonpos[i].length = SDL_strlen(data->buttondata[i].text); - - GetTextWidthHeight(data, data->buttondata[i].text, SDL_strlen(data->buttondata[i].text), &data->buttonpos[i].text_rect.w, &data->buttonpos[i].text_rect.h, &data->buttonpos[i].text_a, &data->buttonpos[i].text_d); - - button_height_max = IntMax(button_height_max, (data->buttonpos[i].text_rect.h + SDL_DIALOG_ELEMENT_PADDING_3 * 2)); - button_width_max = IntMax(button_width_max, (data->buttonpos[i].text_rect.w + padding2x2)); - - } - button_width_total = button_width_max * data->numbuttons + SDL_DIALOG_ELEMENT_PADDING * (data->numbuttons - 1); - button_width_total += padding2x2; - /* Dialog width */ - if (button_width_total < (text_ix + text_width_max + padding2x2)) { - data->dialog_width = IntMax(data->dialog_width, (text_ix + text_width_max + padding2x2)); - } else { - data->dialog_width = IntMax(data->dialog_width, button_width_total); + /* Init vars */ + max_button_w = 50; + max_button_h = 0; + w = h = 2; + i = t = total_button_w = total_text_and_icon_w = 0; + max_button_w *= controls->window->iscale; + + /* Positioning and sizing */ + for (i = 0; i < controls->messageboxdata->numbuttons; i++) { + max_button_w = SDL_max(max_button_w, controls->buttons[i]->rect.w); + max_button_h = SDL_max(max_button_h, controls->buttons[i]->rect.h); + controls->buttons[i]->rect.x = 0; } - /* X position of text and icon */ - t = (data->dialog_width - (text_ix + text_width_max))/2; - if (data->icon_char != '\0') { - data->icon_box_rect.y = 0; - if (data->numlines) { - data->icon_box_rect.x += t; - data->icon_char_x += t; - } else { - int tt; - - tt = t; - t = (data->dialog_width - data->icon_box_rect.w)/2; - data->icon_box_rect.x += t; - data->icon_char_x += t; - t = tt; - } - } - for (i = 0; i < data->numlines; i++) { - data->linedata[i].rect.x += t; + if (controls->icon) { + controls->icon->rect.x = controls->icon->rect.y = 0; } - /* Button poistioning */ - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].rect.w = button_width_max; - data->buttonpos[i].text_rect.x = (data->buttonpos[i].rect.w - data->buttonpos[i].text_rect.w)/2; - data->buttonpos[i].rect.h = button_height_max; - data->buttonpos[i].text_rect.y = data->buttonpos[i].text_a + (data->buttonpos[i].rect.h - data->buttonpos[i].text_rect.h)/2; - if (i > 0) { - data->buttonpos[i].rect.x += data->buttonpos[i-1].rect.x + data->buttonpos[i-1].rect.w + SDL_DIALOG_ELEMENT_PADDING_3; - data->buttonpos[i].text_rect.x += data->buttonpos[i].rect.x; - } - } - button_width_total = data->buttonpos[data->numbuttons-1].rect.x + data->buttonpos[data->numbuttons-1].rect.w; - data->dialog_width = IntMax(data->dialog_width, (button_width_total + padding2x2)); - if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].rect.x += (data->dialog_width - button_width_total)/2; - data->buttonpos[i].text_rect.x += (data->dialog_width - button_width_total)/2; - if (data->icon_box_rect.h > text_height_total) { - data->buttonpos[i].text_rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2; - } else { - int a; - - a = 0; - if (data->numlines) { - a = data->linedata[data->numlines - 1].rect.y + data->linedata[data->numlines - 1].rect.h; - } - data->buttonpos[i].text_rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2; - } - } + if (controls->icon) { + controls->message->rect.x = (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) + controls->icon->rect.x + controls->icon->rect.w; + controls->message->rect.y = X11Toolkit_GetIconControlCharY(controls->icon); } else { - for (i = data->numbuttons; i != -1; i--) { - data->buttonpos[i].rect.x += (data->dialog_width - button_width_total)/2; - data->buttonpos[i].text_rect.x += (data->dialog_width - button_width_total)/2; - if (data->icon_box_rect.h > text_height_total) { - data->buttonpos[i].text_rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2; + controls->message->rect.x = 0; + controls->message->rect.y = -2; + controls->icon = &controls->fake_icon; + controls->icon->rect.w = 0; + controls->icon->rect.h = 0; + controls->icon->rect.x = 0; + controls->icon->rect.y = 0; + } + if (controls->messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { + for (i = controls->messageboxdata->numbuttons; i != -1; i--) { + controls->buttons[i]->rect.w = max_button_w; + controls->buttons[i]->rect.h = max_button_h; + X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); + + if (controls->icon->rect.h > controls->message->rect.h) { + controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 *controls-> window->iscale); } else { - int a; - - a = 0; - if (data->numlines) { - a = data->linedata[data->numlines - 1].rect.y + data->linedata[data->numlines - 1].rect.h; - } - data->buttonpos[i].text_rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2; + controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + + if (i) { + controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale); + } + } + } else { + for (i = 0; i < controls->messageboxdata->numbuttons; i++) { + controls->buttons[i]->rect.w = max_button_w; + controls->buttons[i]->rect.h = max_button_h; + X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); + + if (controls->icon->rect.h > controls->message->rect.h) { + controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } else { + controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + + if (i) { + controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale); } } } - - /* Dialog height */ - elems_total_height = data->buttonpos[data->numbuttons-1].rect.y + data->buttonpos[data->numbuttons-1].rect.h; - data->dialog_height = IntMax(data->dialog_height, (elems_total_height + padding2x2)); - t = (data->dialog_height - elems_total_height)/2; - if (data->icon_char != '\0') { - data->icon_box_rect.y += t; - data->icon_char_y += t; - data->icon_box_rect.w -= 2; - data->icon_box_rect.h -= 2; - } - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].text_rect.y += t; - data->buttonpos[i].rect.y += t; - } - for (i = 0; i < data->numlines; i++) { - data->linedata[i].rect.y += t; - } - return true; -} - -// Free SDL_MessageBoxData data. -static void X11_MessageBoxShutdown(SDL_MessageBoxDataX11 *data) -{ - if (data->font_set) { - X11_XFreeFontSet(data->display, data->font_set); - data->font_set = NULL; - } - - if (data->font_struct) { - X11_XFreeFont(data->display, data->font_struct); - data->font_struct = NULL; - } - - if (data->icon_char != '\0') { - X11_XFreeFont(data->display, data->icon_char_font); - data->icon_char_font = NULL; - } - -#ifdef SDL_VIDEO_DRIVER_X11_XDBE - if (SDL_X11_HAVE_XDBE && data->xdbe) { - X11_XdbeDeallocateBackBufferName(data->display, data->buf); - } -#endif - - if (data->display) { - if (data->window != None) { - X11_XWithdrawWindow(data->display, data->window, data->screen); - X11_XDestroyWindow(data->display, data->window); - data->window = None; - } - - X11_XCloseDisplay(data->display); - data->display = NULL; - } - - SDL_free(data->linedata); -} - -// Create and set up our X11 dialog box indow. -static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data) -{ - int x, y, i; - XSizeHints *sizehints; - XSetWindowAttributes wnd_attr; - Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG; - Display *display = data->display; - SDL_WindowData *windowdata = NULL; - const SDL_MessageBoxData *messageboxdata = data->messageboxdata; -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR -#ifdef XRANDR_DISABLED_BY_DEFAULT - const bool use_xrandr_by_default = false; -#else - const bool use_xrandr_by_default = true; -#endif -#endif - - if (messageboxdata->window) { - SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window); - windowdata = messageboxdata->window->internal; - data->screen = displaydata->screen; + total_button_w = controls->buttons[controls->messageboxdata->numbuttons-1]->rect.x + controls->buttons[controls->messageboxdata->numbuttons-1]->rect.w; + total_text_and_icon_w = controls->message->rect.x + controls->message->rect.w; + if (total_button_w > total_text_and_icon_w) { + w = total_button_w; } else { - data->screen = DefaultScreen(display); + w = total_text_and_icon_w; } - - data->visual = DefaultVisual(display, data->screen); - data->cmap = DefaultColormap(display, data->screen); - for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { - X11_XAllocColor(display, data->cmap, &data->xcolor[i]); - } - X11_XAllocColor(display, data->cmap, &data->xcolor_bevel_l1); - X11_XAllocColor(display, data->cmap, &data->xcolor_bevel_l2); - X11_XAllocColor(display, data->cmap, &data->xcolor_bevel_d); - X11_XAllocColor(display, data->cmap, &data->xcolor_pressed); - if (data->icon_char != '\0') { - X11_XAllocColor(display, data->cmap, &data->xcolor_black); - X11_XAllocColor(display, data->cmap, &data->xcolor_white); - X11_XAllocColor(display, data->cmap, &data->xcolor_red); - X11_XAllocColor(display, data->cmap, &data->xcolor_red_darker); - X11_XAllocColor(display, data->cmap, &data->xcolor_yellow); - X11_XAllocColor(display, data->cmap, &data->xcolor_blue); - X11_XAllocColor(display, data->cmap, &data->xcolor_bg_shadow); - } - - data->event_mask = ExposureMask | - ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | - StructureNotifyMask | FocusChangeMask | PointerMotionMask; - wnd_attr.event_mask = data->event_mask; - wnd_attr.colormap = data->cmap; - - data->window = X11_XCreateWindow( - display, RootWindow(display, data->screen), - 0, 0, - data->dialog_width, data->dialog_height, - 0, DefaultDepth(display, data->screen), InputOutput, data->visual, - CWEventMask | CWColormap, &wnd_attr); - if (data->window == None) { - return SDL_SetError("Couldn't create X window"); - } - - if (windowdata) { - Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False); - Atom stateatoms[16]; - size_t statecount = 0; - // Set some message-boxy window states when attached to a parent window... - // we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False); - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False); - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False); - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False); - SDL_assert(statecount <= SDL_arraysize(stateatoms)); - X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char *)stateatoms, statecount); - - // http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR - X11_XSetTransientForHint(display, data->window, windowdata->xwindow); - } - - SDL_X11_SetWindowTitle(display, data->window, (char *)messageboxdata->title); - - // Let the window manager know this is a dialog box - _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); - _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, - PropModeReplace, - (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1); - - // Allow the window to be deleted by the window manager - data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); - X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1); - - data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); - - if (windowdata) { - XWindowAttributes attrib; - Window dummy; - - X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib); - x = attrib.x + (attrib.width - data->dialog_width) / 2; - y = attrib.y + (attrib.height - data->dialog_height) / 3; - X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy); + w += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) * 2; + if (controls->message->rect.h > controls->icon->rect.h) { + h = controls->message->rect.h; } else { - const SDL_VideoDevice *dev = SDL_GetVideoDevice(); - if (dev && dev->displays && dev->num_displays > 0) { - const SDL_VideoDisplay *dpy = dev->displays[0]; - 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); - } -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR - else if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default) && data->xrandr) { - XRRScreenResources *screen = X11_XRRGetScreenResourcesCurrent(display, DefaultRootWindow(display)); - if (!screen) { - goto XRANDRBAIL; - } - if (!screen->ncrtc) { - goto XRANDRBAIL; - } - - XRRCrtcInfo *crtc_info = X11_XRRGetCrtcInfo(display, screen, screen->crtcs[0]); - if (crtc_info) { - x = (crtc_info->width - data->dialog_width) / 2; - y = (crtc_info->height - data->dialog_height) / 3; - } else { - goto XRANDRBAIL; - } - } -#endif - else { - // oh well. This will misposition on a multi-head setup. Init first next time. - XRANDRBAIL: - x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2; - y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3; - } + h = controls->icon->rect.h; } - X11_XMoveWindow(display, data->window, x, y); - - sizehints = X11_XAllocSizeHints(); - if (sizehints) { - sizehints->flags = USPosition | USSize | PMaxSize | PMinSize; - sizehints->x = x; - sizehints->y = y; - sizehints->width = data->dialog_width; - sizehints->height = data->dialog_height; - - sizehints->min_width = sizehints->max_width = data->dialog_width; - sizehints->min_height = sizehints->max_height = data->dialog_height; - - X11_XSetWMNormalHints(display, data->window, sizehints); - - X11_XFree(sizehints); - } - - X11_XMapRaised(display, data->window); - -#ifdef SDL_VIDEO_DRIVER_X11_XDBE - // Initialise a back buffer for double buffering - if (SDL_X11_HAVE_XDBE) { - int xdbe_major, xdbe_minor; - if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) { - data->xdbe = true; - data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined); - } else { - data->xdbe = false; - } - } -#endif - - return true; -} - -// Draw our message box. -static void X11_MessageBoxDraw(SDL_MessageBoxDataX11 *data, GC ctx, bool utf8) -{ - int i; - Drawable window = data->window; - Display *display = data->display; - -#ifdef SDL_VIDEO_DRIVER_X11_XDBE - if (SDL_X11_HAVE_XDBE && data->xdbe) { - window = data->buf; - X11_XdbeBeginIdiom(data->display); - } -#endif - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); - X11_XFillRectangle(display, window, ctx, 0, 0, data->dialog_width, data->dialog_height); - - if(data->icon_char != '\0') { - X11_XSetForeground(display, ctx, data->xcolor_bg_shadow.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x + 2, data->icon_box_rect.y + 2, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - switch (data->messageboxdata->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { - case SDL_MESSAGEBOX_ERROR: - X11_XSetForeground(display, ctx, data->xcolor_red_darker.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_red.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x+1, data->icon_box_rect.y+1, data->icon_box_rect.w-2, data->icon_box_rect.h-2, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_white.pixel); - break; - case SDL_MESSAGEBOX_WARNING: - X11_XSetForeground(display, ctx, data->xcolor_black.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_yellow.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x+1, data->icon_box_rect.y+1, data->icon_box_rect.w-2, data->icon_box_rect.h-2, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_black.pixel); - break; - case SDL_MESSAGEBOX_INFORMATION: - X11_XSetForeground(display, ctx, data->xcolor_white.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_blue.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x+1, data->icon_box_rect.y+1, data->icon_box_rect.w-2, data->icon_box_rect.h-2, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_white.pixel); - break; - default: - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); - } - X11_XSetFont(display, ctx, data->icon_char_font->fid); - X11_XDrawString(display, window, ctx, data->icon_char_x, data->icon_char_y, &data->icon_char, 1); - if (!utf8) { - X11_XSetFont(display, ctx, data->font_struct->fid); - } + h += max_button_h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) * 3; + t = (w - total_text_and_icon_w) / 2; + controls->icon->rect.x += t; + controls->message->rect.x += t; + controls->icon->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + controls->message->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + t = (w - total_button_w) / 2; + for (i = 0; i < controls->messageboxdata->numbuttons; i++) { + controls->buttons[i]->rect.x += t; + controls->buttons[i]->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); } + if (!controls->messageboxdata->message) { + controls->icon->rect.x = (w - controls->icon->rect.w)/2; + } - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); - for (i = 0; i < data->numlines; i++) { - TextLineData *plinedata = &data->linedata[i]; + *wp = w; + *hp = h; +} -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - X11_Xutf8DrawString(display, window, data->font_set, ctx, - plinedata->rect.x, plinedata->rect.y, - plinedata->text, plinedata->length); - } else -#endif - { - X11_XDrawString(display, window, ctx, - plinedata->rect.x, plinedata->rect.y, - plinedata->text, plinedata->length); - } - } - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); - for (i = 0; i < data->numbuttons; i++) { - SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i]; - const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata; +static void X11_OnMessageBoxScaleChange(SDL_ToolkitWindowX11 *window, void *data) { + SDL_MessageBoxControlsX11 *controls; + int w; + int h; - /* Draw bevel */ - if (data->button_press_index == i) { - X11_XSetForeground(display, ctx, data->xcolor_bevel_d.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w, buttondatax11->rect.h); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l2.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w - 1, buttondatax11->rect.h - 1); - - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l1.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 2); - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 3); - - X11_XSetForeground(display, ctx, data->xcolor_pressed.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 4, buttondatax11->rect.h - 4); - } else { - if (buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { - X11_XSetForeground(display, ctx, data->xcolor_bevel_d.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w, buttondatax11->rect.h); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l2.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 3); - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 4, buttondatax11->rect.h - 4); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l1.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 5, buttondatax11->rect.h - 5); - - X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 3, buttondatax11->rect.y + 3, - buttondatax11->rect.w - 6, buttondatax11->rect.h - 6); - } else { - X11_XSetForeground(display, ctx, data->xcolor_bevel_d.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w, buttondatax11->rect.h); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l2.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w - 1, buttondatax11->rect.h - 1); - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 2, buttondatax11->rect.h - 2); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l1.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 3); - - X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 4, buttondatax11->rect.h - 4); - } - } - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - X11_Xutf8DrawString(display, window, data->font_set, ctx, - buttondatax11->text_rect.x, - buttondatax11->text_rect.y, - buttondata->text, buttondatax11->length); - } else -#endif - { - X11_XDrawString(display, window, ctx, - buttondatax11->text_rect.x, buttondatax11->text_rect.y, - buttondata->text, buttondatax11->length); - } - } - -#ifdef SDL_VIDEO_DRIVER_X11_XDBE - if (SDL_X11_HAVE_XDBE && data->xdbe) { - XdbeSwapInfo swap_info; - swap_info.swap_window = data->window; - swap_info.swap_action = XdbeUndefined; - X11_XdbeSwapBuffers(data->display, &swap_info, 1); - X11_XdbeEndIdiom(data->display); - } -#endif -} - -// NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef -static Bool X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg) -{ - const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *)arg; - return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False; -} - -// Loop and handle message box event messages until something kills it. -static bool X11_MessageBoxLoop(SDL_MessageBoxDataX11 *data) -{ - GC ctx; - XGCValues ctx_vals; - bool close_dialog = false; - bool has_focus = true; - KeySym last_key_pressed = XK_VoidSymbol; - unsigned long gcflags = GCForeground | GCBackground; -#ifdef X_HAVE_UTF8_STRING - const int have_utf8 = SDL_X11_HAVE_UTF8; -#else - const int have_utf8 = 0; -#endif - - SDL_zero(ctx_vals); - ctx_vals.foreground = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; - ctx_vals.background = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; - - if (!have_utf8) { - gcflags |= GCFont; - ctx_vals.font = data->font_struct->fid; - } - - ctx = X11_XCreateGC(data->display, data->window, gcflags, &ctx_vals); - if (ctx == None) { - return SDL_SetError("Couldn't create graphics context"); - } - - data->button_press_index = -1; // Reset what button is currently depressed. - data->mouse_over_index = -1; // Reset what button the mouse is over. - - while (!close_dialog) { - XEvent e; - bool draw = true; - - // can't use XWindowEvent() because it can't handle ClientMessage events. - // can't use XNextEvent() because we only want events for this window. - X11_XIfEvent(data->display, &e, X11_MessageBoxEventTest, (XPointer)data); - - /* If X11_XFilterEvent returns True, then some input method has filtered the - event, and the client should discard the event. */ - if ((e.type != Expose) && X11_XFilterEvent(&e, None)) { - continue; - } - - switch (e.type) { - case Expose: - if (e.xexpose.count > 0) { - draw = false; - } - break; - - case FocusIn: - // Got focus. - has_focus = true; - break; - - case FocusOut: - // lost focus. Reset button and mouse info. - has_focus = false; - data->button_press_index = -1; - data->mouse_over_index = -1; - break; - - case MotionNotify: - if (has_focus) { - // Mouse moved... - const int previndex = data->mouse_over_index; - data->mouse_over_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y); - if (data->mouse_over_index == previndex) { - draw = false; - } - } - break; - - case ClientMessage: - if (e.xclient.message_type == data->wm_protocols && - e.xclient.format == 32 && - e.xclient.data.l[0] == data->wm_delete_message) { - close_dialog = true; - } - break; - - case KeyPress: - // Store key press - we make sure in key release that we got both. - last_key_pressed = X11_XLookupKeysym(&e.xkey, 0); - break; - - case KeyRelease: - { - Uint32 mask = 0; - KeySym key = X11_XLookupKeysym(&e.xkey, 0); - - // If this is a key release for something we didn't get the key down for, then bail. - if (key != last_key_pressed) { - break; - } - - if (key == XK_Escape) { - mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; - } else if ((key == XK_Return) || (key == XK_KP_Enter)) { - mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; - } - - if (mask) { - int i; - - // Look for first button with this mask set, and return it if found. - for (i = 0; i < data->numbuttons; i++) { - SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i]; - - if (buttondatax11->buttondata->flags & mask) { - *data->pbuttonid = buttondatax11->buttondata->buttonID; - close_dialog = true; - break; - } - } - } - break; - } - - case ButtonPress: - data->button_press_index = -1; - if (e.xbutton.button == Button1) { - // Find index of button they clicked on. - data->button_press_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y); - } - break; - - case ButtonRelease: - // If button is released over the same button that was clicked down on, then return it. - if ((e.xbutton.button == Button1) && (data->button_press_index >= 0)) { - int button = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y); - - if (data->button_press_index == button) { - SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[button]; - - *data->pbuttonid = buttondatax11->buttondata->buttonID; - close_dialog = true; - } - } - data->button_press_index = -1; - break; - } - - if (draw) { - // Draw our dialog box. - X11_MessageBoxDraw(data, ctx, have_utf8); - } - } - - X11_XFreeGC(data->display, ctx); - return true; + controls = data; + X11_PositionMessageBox(controls, &w, &h); + X11Toolkit_ResizeWindow(window, w, h); } static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID) { - bool result = false; - SDL_MessageBoxDataX11 data; -#if SDL_SET_LOCALE - char *origlocale; -#endif - - SDL_zero(data); - - if (!SDL_X11_LoadSymbols()) { + SDL_MessageBoxControlsX11 controls; + SDL_MessageBoxCallbackDataX11 data; + const SDL_MessageBoxColor *colorhints; + int i; + int w; + int h; + + controls.messageboxdata = messageboxdata; + + /* Color scheme */ + if (messageboxdata->colorScheme) { + colorhints = messageboxdata->colorScheme->colors; + } else { + colorhints = NULL; + } + + /* Create window */ + controls.window = X11Toolkit_CreateWindowStruct(messageboxdata->window, NULL, SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG, colorhints); + controls.window->cb_data = &controls; + controls.window->cb_on_scale_change = X11_OnMessageBoxScaleChange; + if (!controls.window) { return false; } - -#if SDL_SET_LOCALE - origlocale = setlocale(LC_ALL, NULL); - if (origlocale) { - origlocale = SDL_strdup(origlocale); - if (!origlocale) { - return false; - } - (void)setlocale(LC_ALL, ""); - } -#endif - - // This code could get called from multiple threads maybe? - X11_XInitThreads(); - - // Initialize the return buttonID value to -1 (for error or dialogbox closed). - *buttonID = -1; - - // Init and display the message box. - if (!X11_MessageBoxInit(&data, messageboxdata, buttonID)) { - goto done; - } - if (!X11_MessageBoxInitPositions(&data)) { - goto done; + /* Create controls */ + controls.buttons = SDL_calloc(messageboxdata->numbuttons, sizeof(SDL_ToolkitControlX11 *)); + controls.icon = X11Toolkit_CreateIconControl(controls.window, messageboxdata->flags); + controls.message = X11Toolkit_CreateLabelControl(controls.window, (char *)messageboxdata->message); + data.buttonID = buttonID; + data.window = controls.window; + for (i = 0; i < messageboxdata->numbuttons; i++) { + controls.buttons[i] = X11Toolkit_CreateButtonControl(controls.window, &messageboxdata->buttons[i]); + X11Toolkit_RegisterCallbackForButtonControl(controls.buttons[i], &data, X11_MessageBoxButtonCallback); } - - if (!X11_MessageBoxCreateWindow(&data)) { - goto done; - } - - result = X11_MessageBoxLoop(&data); -done: - X11_MessageBoxShutdown(&data); -#if SDL_SET_LOCALE - if (origlocale) { - (void)setlocale(LC_ALL, origlocale); - SDL_free(origlocale); + /* Positioning */ + X11_PositionMessageBox(&controls, &w, &h); + + /* Actually create window, do event loop, cleanup */ + X11Toolkit_CreateWindowRes(controls.window, w, h, 0, 0, (char *)messageboxdata->title); + X11Toolkit_DoWindowEventLoop(controls.window); + X11Toolkit_DestroyWindow(controls.window); + if (controls.buttons) { + SDL_free(controls.buttons); } -#endif - - return result; + return true; } // Display an x11 message box. diff --git a/src/video/x11/SDL_x11settings.c b/src/video/x11/SDL_x11settings.c index 50bf773f3a..a608919770 100644 --- a/src/video/x11/SDL_x11settings.c +++ b/src/video/x11/SDL_x11settings.c @@ -26,10 +26,6 @@ #include "SDL_x11video.h" #include "SDL_x11settings.h" -#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" -#define SDL_XSETTINGS_GDK_UNSCALED_DPI "Gdk/UnscaledDPI" -#define SDL_XSETTINGS_XFT_DPI "Xft/DPI" - static void UpdateContentScale(SDL_VideoDevice *_this) { if (_this) { diff --git a/src/video/x11/SDL_x11settings.h b/src/video/x11/SDL_x11settings.h index f91ed70ac7..2dab71e058 100644 --- a/src/video/x11/SDL_x11settings.h +++ b/src/video/x11/SDL_x11settings.h @@ -27,6 +27,10 @@ #include #include "xsettings-client.h" +#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" +#define SDL_XSETTINGS_GDK_UNSCALED_DPI "Gdk/UnscaledDPI" +#define SDL_XSETTINGS_XFT_DPI "Xft/DPI" + typedef struct X11_SettingsData { XSettingsClient *xsettings; } SDLX11_SettingsData; diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 959aabce8c..cacec1e654 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -42,6 +42,7 @@ SDL_X11_SYM(int,XConvertSelection,(Display* a,Atom b,Atom c,Atom d,Window e,Time SDL_X11_SYM(Pixmap,XCreateBitmapFromData,(Display *dpy,Drawable d,_Xconst char *data,unsigned int width,unsigned int height)) SDL_X11_SYM(Colormap,XCreateColormap,(Display* a,Window b,Visual* c,int d)) SDL_X11_SYM(Cursor,XCreatePixmapCursor,(Display* a,Pixmap b,Pixmap c,XColor* d,XColor* e,unsigned int f,unsigned int g)) +SDL_X11_SYM(Cursor,XCreatePixmap,(Display* a,Drawable b,unsigned int d,unsigned int e,unsigned int f)) SDL_X11_SYM(Cursor,XCreateFontCursor,(Display* a,unsigned int b)) SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char** e)) SDL_X11_SYM(GC,XCreateGC,(Display* a,Drawable b,unsigned long c,XGCValues* d)) @@ -68,6 +69,7 @@ SDL_X11_SYM(int,XFreeGC,(Display* a,GC b)) SDL_X11_SYM(int,XFreeFont,(Display* a, XFontStruct* b)) SDL_X11_SYM(int,XFreeModifiermap,(XModifierKeymap* a)) SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b)) +SDL_X11_SYM(int,XFreeColormap,(Display* a,Colormap b)) SDL_X11_SYM(void,XFreeStringList,(char** a)) SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b)) SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c)) @@ -102,6 +104,8 @@ SDL_X11_SYM(Display*,XOpenDisplay,(_Xconst char* a)) SDL_X11_SYM(Status,XInitThreads,(void)) SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b)) SDL_X11_SYM(int,XPending,(Display* a)) +SDL_X11_SYM(XImage*,XGetImage,(Display* a,Drawable b,int c, int d,unsigned int e,unsigned int f,unsigned long g,int h)) +SDL_X11_SYM(void,XDestroyImage,(XImage *a)) SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j)) SDL_X11_SYM(int,XQueryKeymap,(Display* a,char b[32])) SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i)) diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c new file mode 100644 index 0000000000..cccdc6f435 --- /dev/null +++ b/src/video/x11/SDL_x11toolkit.c @@ -0,0 +1,1866 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#ifdef SDL_VIDEO_DRIVER_X11 + +#include "../../SDL_list.h" +#include "SDL_x11video.h" +#include "SDL_x11dyn.h" +#include "SDL_x11toolkit.h" +#include "SDL_x11settings.h" +#include "SDL_x11modes.h" +#include "xsettings-client.h" +#include +#include + +#define SDL_SET_LOCALE 1 +#define SDL_GRAB 1 + +typedef struct SDL_ToolkitIconControlX11 +{ + SDL_ToolkitControlX11 parent; + + /* Icon type */ + SDL_MessageBoxFlags flags; + char icon_char; + + /* Font */ + XFontStruct *icon_char_font; + int icon_char_x; + int icon_char_y; + int icon_char_a; + + /* Colors */ + XColor xcolor_black; + XColor xcolor_red; + XColor xcolor_red_darker; + XColor xcolor_white; + XColor xcolor_yellow; + XColor xcolor_blue; + XColor xcolor_bg_shadow; +} SDL_ToolkitIconControlX11; + +typedef struct SDL_ToolkitButtonControlX11 +{ + SDL_ToolkitControlX11 parent; + + /* Data */ + const SDL_MessageBoxButtonData *data; + + /* Text */ + SDL_Rect text_rect; + int text_a; + int text_d; + int str_sz; + + /* Callback */ + void *cb_data; + void (*cb)(struct SDL_ToolkitControlX11 *, void *); +} SDL_ToolkitButtonControlX11; + +typedef struct SDL_ToolkitLabelControlX11 +{ + SDL_ToolkitControlX11 parent; + + char **lines; + int *y; + size_t *szs; + size_t sz; +} SDL_ToolkitLabelControlX11; + +typedef struct SDL_ToolkitMenuBarControlX11 +{ + SDL_ToolkitControlX11 parent; + + SDL_ListNode *menu_items; +} SDL_ToolkitMenuBarControlX11; + +typedef struct SDL_ToolkitMenuControlX11 +{ + SDL_ToolkitControlX11 parent; + + SDL_ListNode *menu_items; + XColor xcolor_check_bg; +} SDL_ToolkitMenuControlX11; + +/* Font for icon control */ +static const char *g_IconFont = "-*-*-bold-r-normal-*-%d-*-*-*-*-*-iso8859-1[33 88 105]"; +#define G_ICONFONT_SIZE 18 + +/* General UI font */ +static const char g_ToolkitFontLatin1[] = + "-*-*-medium-r-normal--0-%d-*-*-p-0-iso8859-1"; +static const char *g_ToolkitFont[] = { + "-*-*-medium-r-normal--*-%d-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) + "-*-*-medium-r-*--*-%d-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) + "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems) + "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode. + "-*-*-medium-r-normal--*-v-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. + "-*-*-medium-r-*--*-%d-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. + "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems) + "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1. + NULL +}; +#define G_TOOLKITFONT_SIZE 120 + +static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = { + { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, + { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_TEXT, + { 127, 120, 127 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, + { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, + { 235, 235, 235 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, +}; + +int X11Toolkit_SettingsGetInt(XSettingsClient *client, const char *key, int fallback_value) { + XSettingsSetting *setting = NULL; + int res = fallback_value; + + if (client) { + if (xsettings_client_get_setting(client, key, &setting) != XSETTINGS_SUCCESS) { + goto no_key; + } + + if (setting->type != XSETTINGS_TYPE_INT) { + goto no_key; + } + + res = setting->data.v_int; + } + +no_key: + if (setting) { + xsettings_setting_free(setting); + } + + return res; +} + +static float X11Toolkit_GetUIScale(XSettingsClient *client, Display *display) +{ + double scale_factor = 0.0; + + // First use the forced scaling factor specified by the app/user + const char *hint = SDL_GetHint(SDL_HINT_VIDEO_X11_SCALING_FACTOR); + if (hint && *hint) { + double value = SDL_atof(hint); + if (value >= 1.0f && value <= 10.0f) { + scale_factor = value; + } + } + + // If that failed, try "Xft.dpi" from the XResourcesDatabase... + // We attempt to read this directly to get the live value, XResourceManagerString + // is cached per display connection. + if (scale_factor <= 0.0) + { + int status, real_format; + Atom real_type; + Atom res_mgr; + unsigned long items_read, items_left; + char *resource_manager; + bool owns_resource_manager = false; + + X11_XrmInitialize(); + res_mgr = X11_XInternAtom(display, "RESOURCE_MANAGER", False); + status = X11_XGetWindowProperty(display, RootWindow(display, DefaultScreen(display)), + res_mgr, 0L, 8192L, False, XA_STRING, + &real_type, &real_format, &items_read, &items_left, + (unsigned char **)&resource_manager); + + if (status == Success && resource_manager) { + owns_resource_manager = true; + } else { + // Fall back to XResourceManagerString. This will not be updated if the + // dpi value is later changed but should allow getting the initial value. + resource_manager = X11_XResourceManagerString(display); + } + + if (resource_manager) { + XrmDatabase db; + XrmValue value; + char *type; + + db = X11_XrmGetStringDatabase(resource_manager); + + // Get the value of Xft.dpi from the Database + if (X11_XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { + if (value.addr && type && SDL_strcmp(type, "String") == 0) { + int dpi = SDL_atoi(value.addr); + scale_factor = dpi / 96.0; + } + } + X11_XrmDestroyDatabase(db); + + if (owns_resource_manager) { + X11_XFree(resource_manager); + } + } + } + + // If that failed, try the XSETTINGS keys... + if (scale_factor <= 0.0) { + scale_factor = X11Toolkit_SettingsGetInt(client, "Gdk/WindowScalingFactor", -1); + + // The Xft/DPI key is stored in increments of 1024th + if (scale_factor <= 0.0) { + int dpi = X11Toolkit_SettingsGetInt(client, "Xft/DPI", -1); + if (dpi > 0) { + scale_factor = (double) dpi / 1024.0; + scale_factor /= 96.0; + } + } + } + + // If that failed, try the GDK_SCALE envvar... + if (scale_factor <= 0.0) { + const char *scale_str = SDL_getenv("GDK_SCALE"); + if (scale_str) { + scale_factor = SDL_atoi(scale_str); + } + } + + // Nothing or a bad value, just fall back to 1.0 + if (scale_factor <= 0.0) { + scale_factor = 1.0; + } + + return (float)scale_factor; +} + +static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) +{ + SDL_ToolkitWindowX11 *window; + int i; + + window = data; + + if (window->xsettings_first_time) { + return; + } + + if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_GDK_UNSCALED_DPI) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { + bool dbe_already_setup; + bool pixmap_already_setup; + + if (window->pixmap) { + pixmap_already_setup = true; + } else { + dbe_already_setup = true; + } + + /* set scale vars */ + window->scale = X11Toolkit_GetUIScale(window->xsettings, window->display); + window->iscale = (int)SDL_ceilf(window->scale); + if (roundf(window->scale) == window->scale) { + window->scale = 0; + } + + /* set up window */ + if (window->scale != 0) { + window->window_width = SDL_lroundf((window->window_width/window->iscale) * window->scale); + window->window_height = SDL_lroundf((window->window_height/window->iscale) * window->scale); + window->pixmap_width = window->window_width; + window->pixmap_height = window->window_height; + window->pixmap = true; + } else { + window->pixmap = false; + } + + if (window->pixmap) { + if (!pixmap_already_setup) { +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && window->xdbe) { + X11_XdbeDeallocateBackBufferName(window->display, window->buf); + } +#endif + } + + X11_XFreePixmap(window->display, window->drawable); + window->drawable = X11_XCreatePixmap(window->display, window->window, window->pixmap_width, window->pixmap_height, window->depth); + } else { + if (!dbe_already_setup) { + X11_XFreePixmap(window->display, window->drawable); +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && window->xdbe) { + window->buf = X11_XdbeAllocateBackBufferName(window->display, window->window, XdbeUndefined); + window->drawable = window->buf; + } +#endif + } + } + + /* setup fonts */ +#ifdef X_HAVE_UTF8_STRING + if (window->font_set) { + X11_XFreeFontSet(window->display, window->font_set); + } +#endif + if (window->font_struct) { + X11_XFreeFont(window->display, window->font_struct); + } + +#ifdef X_HAVE_UTF8_STRING + window->utf8 = true; + window->font_set = NULL; + if (SDL_X11_HAVE_UTF8) { + char **missing = NULL; + int num_missing = 0; + int i_font; + window->font_struct = NULL; + for (i_font = 0; g_ToolkitFont[i_font]; ++i_font) { + char *font; + + SDL_asprintf(&font, g_ToolkitFont[i_font], G_TOOLKITFONT_SIZE * window->iscale); + window->font_set = X11_XCreateFontSet(window->display, font, + &missing, &num_missing, NULL); + SDL_free(font); + if (missing) { + X11_XFreeStringList(missing); + } + if (window->font_set) { + break; + } + } + if (!window->font_set) { + goto load_font_traditional; + } + } else +#endif + { + char *font; + load_font_traditional: + + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); + window->font_struct = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + window->utf8 = false; + } + + /* notify controls */ + for (i = 0; i < window->controls_sz; i++) { + if (window->controls[i]->func_on_scale_change) { + window->controls[i]->func_on_scale_change(window->controls[i]); + } + + if (window->controls[i]->func_calc_size) { + window->controls[i]->func_calc_size(window->controls[i]); + } + } + + /* notify cb */ + if (window->cb_on_scale_change) { + window->cb_on_scale_change(window, window->cb_data); + } + + /* update ev scales */ + if (!window->pixmap) { + window->ev_scale = window->ev_iscale = 1; + } else { + window->ev_scale = window->scale; + window->ev_iscale = window->iscale; + } + } +} + + +static void X11Toolkit_GetTextWidthHeightForFont(XFontStruct *font, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent) +{ + XCharStruct text_structure; + int font_direction, font_descent; + X11_XTextExtents(font, str, nbytes, + &font_direction, font_ascent, &font_descent, + &text_structure); + *pwidth = text_structure.width; + *pheight = text_structure.ascent + text_structure.descent; +} + +static void X11Toolkit_GetTextWidthHeight(SDL_ToolkitWindowX11 *data, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent, int *font_descent) +{ +#ifdef X_HAVE_UTF8_STRING + if (data->utf8) { + XFontSetExtents *extents; + XRectangle overall_ink, overall_logical; + extents = X11_XExtentsOfFontSet(data->font_set); + X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical); + *pwidth = overall_logical.width; + *pheight = overall_logical.height; + *font_ascent = -extents->max_logical_extent.y; + *font_descent = extents->max_logical_extent.height - *font_ascent; + } else +#endif + { + XCharStruct text_structure; + int font_direction; + X11_XTextExtents(data->font_struct, str, nbytes, + &font_direction, font_ascent, font_descent, + &text_structure); + *pwidth = text_structure.width; + *pheight = text_structure.ascent + text_structure.descent; + } +} + +SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_ToolkitWindowX11 *tkparent, SDL_ToolkitWindowModeX11 mode, const SDL_MessageBoxColor *colorhints) +{ + SDL_ToolkitWindowX11 *window; + int i; + #define ErrorFreeRetNull(x, y) SDL_SetError(x); SDL_free(y); return NULL; + #define ErrorCloseFreeRetNull(x, y, z) X11_XCloseDisplay(z->display); SDL_SetError(x, y); SDL_free(z); return NULL; + + if (!SDL_X11_LoadSymbols()) { + return NULL; + } + + // This code could get called from multiple threads maybe? + X11_XInitThreads(); + + window = (SDL_ToolkitWindowX11 *)SDL_malloc(sizeof(SDL_ToolkitWindowX11)); + if (!window) { + SDL_SetError("Unable to allocate toolkit window structure"); + return NULL; + } + + window->mode = mode; + window->tk_parent = tkparent; + +#if SDL_SET_LOCALE + if (mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { + window->origlocale = setlocale(LC_ALL, NULL); + if (window->origlocale) { + window->origlocale = SDL_strdup(window->origlocale); + if (!window->origlocale) { + return NULL; + } + (void)setlocale(LC_ALL, ""); + } + } +#endif + + if (parent) { + SDL_VideoData *videodata = SDL_GetVideoDevice()->internal; + window->display = videodata->display; + window->display_close = false; + } else if (tkparent) { + window->display = tkparent->display; + window->display_close = false; + } else { + window->display = X11_XOpenDisplay(NULL); + window->display_close = true; + if (!window->display) { + ErrorFreeRetNull("Couldn't open X11 display", window); + } + } + +#ifdef SDL_VIDEO_DRIVER_X11_XRSANDR + int xrandr_event_base, xrandr_error_base; + window->xrandr = X11_XRRQueryExtension(window->display, &xrandr_event_base, &xrandr_error_base); +#endif + + /* Scale/Xsettings */ + window->pixmap = false; + window->xsettings_first_time = true; + window->xsettings = xsettings_client_new(window->display, DefaultScreen(window->display), X11Toolkit_SettingsNotify, NULL, window); + window->xsettings_first_time = false; + window->scale = X11Toolkit_GetUIScale(window->xsettings, window->display); + window->iscale = (int)SDL_ceilf(window->scale); + if (roundf(window->scale) == window->scale) { + window->scale = 0; + } + +#ifdef X_HAVE_UTF8_STRING + window->utf8 = true; + window->font_set = NULL; + if (SDL_X11_HAVE_UTF8) { + char **missing = NULL; + int num_missing = 0; + int i_font; + window->font_struct = NULL; + for (i_font = 0; g_ToolkitFont[i_font]; ++i_font) { + char *font; + + SDL_asprintf(&font, g_ToolkitFont[i_font], G_TOOLKITFONT_SIZE * window->iscale); + window->font_set = X11_XCreateFontSet(window->display, font, + &missing, &num_missing, NULL); + SDL_free(font); + if (missing) { + X11_XFreeStringList(missing); + } + if (window->font_set) { + break; + } + } + if (!window->font_set) { + goto load_font_traditional; + } + } else +#endif + { + char *font; + load_font_traditional: + + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); + window->font_struct = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + window->utf8 = false; + if (!window->font_struct) { + ErrorCloseFreeRetNull("Couldn't load font %s", g_ToolkitFontLatin1, window); + } + } + + if (!colorhints) { + colorhints = g_default_colors; + } + window->color_hints = colorhints; + + // Convert colors to 16 bpc XColor format + for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { + window->xcolor[i].flags = DoRed|DoGreen|DoBlue; + window->xcolor[i].red = colorhints[i].r * 257; + window->xcolor[i].green = colorhints[i].g * 257; + window->xcolor[i].blue = colorhints[i].b * 257; + } + + /* Generate bevel and pressed colors */ + window->xcolor_bevel_l1.flags = DoRed|DoGreen|DoBlue; + window->xcolor_bevel_l1.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 12500, 0, 65535); + window->xcolor_bevel_l1.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 12500, 0, 65535); + window->xcolor_bevel_l1.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 12500, 0, 65535); + + window->xcolor_bevel_l2.flags = DoRed|DoGreen|DoBlue; + window->xcolor_bevel_l2.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 32500, 0, 65535); + window->xcolor_bevel_l2.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 32500, 0, 65535); + window->xcolor_bevel_l2.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 32500, 0, 65535); + + window->xcolor_bevel_d.flags = DoRed|DoGreen|DoBlue; + window->xcolor_bevel_d.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red - 22500, 0, 65535); + window->xcolor_bevel_d.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green - 22500, 0, 65535); + window->xcolor_bevel_d.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue - 22500, 0, 65535); + + window->xcolor_pressed.flags = DoRed|DoGreen|DoBlue; + window->xcolor_pressed.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].red - 12500, 0, 65535); + window->xcolor_pressed.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].green - 12500, 0, 65535); + window->xcolor_pressed.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].blue - 12500, 0, 65535); + + window->xcolor_disabled_text.flags = DoRed|DoGreen|DoBlue; + window->xcolor_disabled_text.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].red + 19500, 0, 65535); + window->xcolor_disabled_text.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].green + 19500, 0, 65535); + window->xcolor_disabled_text.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].blue + 19500, 0, 65535); + + + /* Screen */ + window->parent = parent; + if (parent) { + SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(parent); + window->screen = displaydata->screen; + } else { + window->screen = DefaultScreen(window->display); + } + + /* Visuals */ + if (mode == SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { + window->visual = parent->internal->visual; + window->cmap = parent->internal->colormap; + X11_GetVisualInfoFromVisual(window->display, window->visual, &window->vi); + window->depth = window->vi.depth; + } else { + window->visual = DefaultVisual(window->display, window->screen); + window->cmap = DefaultColormap(window->display, window->screen); + window->depth = DefaultDepth(window->display, window->screen); + X11_GetVisualInfoFromVisual(window->display, window->visual, &window->vi); + } + + /* Allocate colors */ + for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { + X11_XAllocColor(window->display, window->cmap, &window->xcolor[i]); + } + X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_l1); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_l2); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_d); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_pressed); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_disabled_text); + + /* Control list */ + window->has_focus = false; + window->controls = NULL; + window->controls_sz = 0; + window->dyn_controls_sz = 0; + window->fiddled_control = NULL; + window->dyn_controls = NULL; + + /* Menu windows */ + window->popup_windows = NULL; + + return window; +} + +static void X11Toolkit_AddControlToWindow(SDL_ToolkitWindowX11 *window, SDL_ToolkitControlX11 *control) { + /* Add to controls list */ + window->controls_sz++; + if (window->controls_sz == 1) { + window->controls = (struct SDL_ToolkitControlX11 **)SDL_malloc(sizeof(struct SDL_ToolkitControlX11 *)); + } else { + window->controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->controls, sizeof(struct SDL_ToolkitControlX11 *) * window->controls_sz); + } + window->controls[window->controls_sz - 1] = control; + + /* If dynamic, add it to the dynamic controls list too */ + if (control->dynamic) { + window->dyn_controls_sz++; + if (window->dyn_controls_sz == 1) { + window->dyn_controls = (struct SDL_ToolkitControlX11 **)SDL_malloc(sizeof(struct SDL_ToolkitControlX11 *)); + } else { + window->dyn_controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->dyn_controls, sizeof(struct SDL_ToolkitControlX11 *) * window->dyn_controls_sz); + } + window->dyn_controls[window->dyn_controls_sz - 1] = control; + } + + /* If selected, set currently focused control to it */ + if (control->selected) { + window->focused_control = control; + } +} + +bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx, int cy, char *title) +{ + int x, y; + XSizeHints *sizehints; + XSetWindowAttributes wnd_attr; + Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, _NET_WM_WINDOW_TYPE_TOOLTIP; + SDL_WindowData *windowdata = NULL; + Display *display = data->display; + XGCValues ctx_vals; + Window root_win; + Window parent_win; + unsigned long gcflags = GCForeground | GCBackground; + unsigned long valuemask; +#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 (data->scale == 0) { + data->window_width = w; + data->window_height = h; + } else { + data->window_width = SDL_lroundf((w/data->iscale) * data->scale); + data->window_height = SDL_lroundf((h/data->iscale) * data->scale); + data->pixmap_width = w; + data->pixmap_height = h; + data->pixmap = true; + } + + if (data->parent) { + windowdata = data->parent->internal; + } + + valuemask = CWEventMask | CWColormap; + data->event_mask = ExposureMask | + ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask | FocusChangeMask | PointerMotionMask; + wnd_attr.event_mask = data->event_mask; + wnd_attr.colormap = data->cmap; + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { + valuemask |= CWOverrideRedirect | CWSaveUnder; + wnd_attr.save_under = True; + wnd_attr.override_redirect = True; + } + root_win = RootWindow(display, data->screen); + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { + parent_win = windowdata->xwindow; + } else { + parent_win = root_win; + } + + data->window = X11_XCreateWindow( + display, parent_win, + 0, 0, + data->window_width, data->window_height, + 0, data->depth, InputOutput, data->visual, + valuemask, &wnd_attr); + if (data->window == None) { + return SDL_SetError("Couldn't create X window"); + } + + if (windowdata && data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG) { + Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False); + Atom stateatoms[16]; + size_t statecount = 0; + // Set some message-boxy window states when attached to a parent window... + // we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False); + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False); + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False); + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False); + SDL_assert(statecount <= SDL_arraysize(stateatoms)); + X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char *)stateatoms, statecount); + } + + if (windowdata && data->mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { + X11_XSetTransientForHint(display, data->window, windowdata->xwindow); + } + + if (data->tk_parent) { + X11_XSetTransientForHint(display, data->window, data->tk_parent->window); + } + + SDL_X11_SetWindowTitle(display, data->window, title); + + // Let the window manager the type of the window + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG) { + _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); + _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, + (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1); + } else if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU) { + _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); + X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, + (unsigned char *)&_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, 1); + } else if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { + _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); + _NET_WM_WINDOW_TYPE_TOOLTIP = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_TOOLTIP", False); + X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, + (unsigned char *)&_NET_WM_WINDOW_TYPE_TOOLTIP, 1); + } + + // Allow the window to be deleted by the window manager + data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); + X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1); + data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); + + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { + x = cx; + y = cy; + goto MOVEWINDOW; + } + if (windowdata) { + XWindowAttributes attrib; + Window dummy; + + X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib); + x = attrib.x + (attrib.width - data->window_width) / 2; + y = attrib.y + (attrib.height - data->window_height) / 3; + X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy); + } else { + const SDL_VideoDevice *dev = SDL_GetVideoDevice(); + if (dev && dev->displays && dev->num_displays > 0) { + const SDL_VideoDisplay *dpy = dev->displays[0]; + const SDL_DisplayData *dpydata = dpy->internal; + x = dpydata->x + ((dpy->current_mode->w - data->window_width) / 2); + y = dpydata->y + ((dpy->current_mode->h - data->window_height) / 3); + } +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR + else if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default) && data->xrandr) { + XRRScreenResources *screen_res; + XRRCrtcInfo *crtc_info; + RROutput default_out; + + screen_res = X11_XRRGetScreenResourcesCurrent(display, root_win); + if (!screen_res) { + goto NOXRANDR; + } + + default_out = X11_XRRGetOutputPrimary(display, root_win); + if (default_out != None) { + XRROutputInfo *out_info; + + out_info = X11_XRRGetOutputInfo(display, screen_res, default_out); + if (out_info->connection != RR_Connected) { + X11_XRRFreeOutputInfo(out_info); + goto FIRSTOUTPUTXRANDR; + } + + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc); + if (crtc_info) { + x = (crtc_info->width - data->window_width) / 2; + y = (crtc_info->height - data->window_height) / 3; + X11_XRRFreeOutputInfo(out_info); + X11_XRRFreeCrtcInfo(crtc_info); + X11_XRRFreeScreenResources(screen_res); + } else { + X11_XRRFreeOutputInfo(out_info); + goto NOXRANDR; + } + } else { + FIRSTOUTPUTXRANDR: + if (screen_res->noutput > 0) { + XRROutputInfo *out_info; + + out_info = X11_XRRGetOutputInfo(display, screen_res, screen_res->outputs[0]); + if (!out_info) { + goto FIRSTCRTCXRANDR; + } + + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc); + if (!crtc_info) { + X11_XRRFreeOutputInfo(out_info); + goto FIRSTCRTCXRANDR; + } + + x = (crtc_info->width - data->window_width) / 2; + y = (crtc_info->height - data->window_height) / 3; + X11_XRRFreeOutputInfo(out_info); + X11_XRRFreeCrtcInfo(crtc_info); + X11_XRRFreeScreenResources(screen_res); + goto MOVEWINDOW; + } + + FIRSTCRTCXRANDR: + if (!screen_res->ncrtc) { + X11_XRRFreeScreenResources(screen_res); + goto NOXRANDR; + } + + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, screen_res->crtcs[0]); + if (crtc_info) { + x = (crtc_info->width - data->window_width) / 2; + y = (crtc_info->height - data->window_height) / 3; + X11_XRRFreeCrtcInfo(crtc_info); + X11_XRRFreeScreenResources(screen_res); + } else { + X11_XRRFreeScreenResources(screen_res); + goto NOXRANDR; + } + } + } +#endif + else { + // oh well. This will misposition on a multi-head setup. Init first next time. + NOXRANDR: + x = (DisplayWidth(display, data->screen) - data->window_width) / 2; + y = (DisplayHeight(display, data->screen) - data->window_height) / 3; + } + } + MOVEWINDOW: + X11_XMoveWindow(display, data->window, x, y); + data->window_x = x; + data->window_y = y; + + sizehints = X11_XAllocSizeHints(); + if (sizehints) { + sizehints->flags = USPosition | USSize | PMaxSize | PMinSize; + sizehints->x = x; + sizehints->y = y; + sizehints->width = data->window_width; + sizehints->height = data->window_height; + + sizehints->min_width = sizehints->max_width = data->window_width; + sizehints->min_height = sizehints->max_height = data->window_height; + + X11_XSetWMNormalHints(display, data->window, sizehints); + + X11_XFree(sizehints); + } + + X11_XMapRaised(display, data->window); + + data->drawable = data->window; + +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + // Initialise a back buffer for double buffering + if (SDL_X11_HAVE_XDBE && !data->pixmap) { + int xdbe_major, xdbe_minor; + if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) { + data->xdbe = true; + data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined); + data->drawable = data->buf; + } else { + data->xdbe = false; + } + } +#endif + + if (data->pixmap) { + data->drawable = X11_XCreatePixmap(display, data->window, data->pixmap_width, data->pixmap_height, data->depth); + } + + SDL_zero(ctx_vals); + ctx_vals.foreground = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; + ctx_vals.background = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; + if (!data->utf8) { + gcflags |= GCFont; + ctx_vals.font = data->font_struct->fid; + } + data->ctx = X11_XCreateGC(data->display, data->drawable, gcflags, &ctx_vals); + if (data->ctx == None) { + return SDL_SetError("Couldn't create graphics context"); + } + + data->close = false; + data->key_control_esc = data->key_control_enter = NULL; + if (!data->pixmap) { + data->ev_scale = data->ev_iscale = 1; + } else { + data->ev_scale = data->scale; + data->ev_iscale = data->iscale; + } + +#if SDL_GRAB + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { + X11_XGrabPointer(display, data->window, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + X11_XGrabKeyboard(display, data->window, False, GrabModeAsync, GrabModeAsync, CurrentTime); + } +#endif + + return true; +} + +static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { + int i; + +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && data->xdbe && !data->pixmap) { + X11_XdbeBeginIdiom(data->display); + } +#endif + + X11_XSetForeground(data->display, data->ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); + if (data->pixmap) { + X11_XFillRectangle(data->display, data->drawable, data->ctx, 0, 0, data->pixmap_width, data->pixmap_height); + } else { + X11_XFillRectangle(data->display, data->drawable, data->ctx, 0, 0, data->window_width, data->window_height); + } + + for (i = 0; i < data->controls_sz; i++) { + SDL_ToolkitControlX11 *control; + + control = data->controls[i]; + if (control) { + if (control->func_draw) { + control->func_draw(control); + } + } + } + +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && data->xdbe && !data->pixmap) { + XdbeSwapInfo swap_info; + swap_info.swap_window = data->window; + swap_info.swap_action = XdbeUndefined; + X11_XdbeSwapBuffers(data->display, &swap_info, 1); + X11_XdbeEndIdiom(data->display); + } +#endif + + if (data->pixmap) { + XImage *pixmap_image; + XImage *put_image; + SDL_Surface *pixmap_surface; + SDL_Surface *put_surface; + + /* FIXME: Implement SHM transport? */ + pixmap_image = X11_XGetImage(data->display, data->drawable, 0, 0 , data->pixmap_width, data->pixmap_height, AllPlanes, ZPixmap); + pixmap_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), pixmap_image->data, pixmap_image->bytes_per_line); + put_surface = SDL_ScaleSurface(pixmap_surface, data->window_width, data->window_height, SDL_SCALEMODE_LINEAR); + put_image = X11_XCreateImage(data->display, data->visual, data->vi.depth, ZPixmap, 0, put_surface->pixels, data->window_width, data->window_height, 32, put_surface->pitch); + X11_XPutImage(data->display, data->window, data->ctx, put_image, 0, 0, 0, 0, data->window_width, data->window_height); + + X11_XDestroyImage(pixmap_image); + /* Needed because XDestroyImage results in a double-free otherwise */ + put_image->data = NULL; + X11_XDestroyImage(put_image); + SDL_DestroySurface(pixmap_surface); + SDL_DestroySurface(put_surface); + } + + X11_XFlush(data->display); +} + +static SDL_ToolkitControlX11 *X11Toolkit_GetControlMouseIsOn(SDL_ToolkitWindowX11 *data, int x, int y) +{ + int i; + + for (i = 0; i < data->controls_sz; i++) { + SDL_Rect *rect = &data->controls[i]->rect; + if ((x >= rect->x) && + (x <= (rect->x + rect->w)) && + (y >= rect->y) && + (y <= (rect->y + rect->h))) { + return data->controls[i]; + } + } + + return NULL; +} + +// NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef +static Bool X11Toolkit_EventTest(Display *display, XEvent *event, XPointer arg) +{ + SDL_ToolkitWindowX11 *data = (SDL_ToolkitWindowX11 *)arg; + + if (event->xany.display != data->display) { + return False; + } + + if (event->xany.window == data->window) { + return True; + } + + return False; +} + +void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { + /* If X11_XFilterEvent returns True, then some input method has filtered the + event, and the client should discard the event. */ + if ((e->type != Expose) && X11_XFilterEvent(e, None)) { + return; + } + + data->draw = false; + data->e = e; + + switch (e->type) { + case Expose: + data->draw = true; + break; + case ClientMessage: + if (e->xclient.message_type == data->wm_protocols && + e->xclient.format == 32 && + e->xclient.data.l[0] == data->wm_delete_message) { + data->close = true; + } + break; + case FocusIn: + data->has_focus = true; + break; + case FocusOut: + data->has_focus = false; + if (data->fiddled_control) { + data->fiddled_control->selected = false; + } + data->fiddled_control = NULL; + for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { + data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + } + break; + case MotionNotify: + if (data->has_focus) { + data->previous_control = data->fiddled_control; + data->fiddled_control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); + if (data->previous_control) { + data->previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + if (data->previous_control->func_on_state_change) { + data->previous_control->func_on_state_change(data->previous_control); + } + data->draw = true; + } + if (data->fiddled_control) { + if (data->fiddled_control->dynamic) { + data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_HOVER; + if (data->fiddled_control->func_on_state_change) { + data->fiddled_control->func_on_state_change(data->fiddled_control); + } + data->draw = true; + } else { + data->fiddled_control = NULL; + } + } + } + break; + case ButtonPress: + data->previous_control = data->fiddled_control; + if (data->previous_control) { + data->previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + if (data->previous_control->func_on_state_change) { + data->previous_control->func_on_state_change(data->previous_control); + } + data->draw = true; + } + if (e->xbutton.button == Button1) { + data->fiddled_control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); + if (data->fiddled_control) { + data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD; + if (data->fiddled_control->func_on_state_change) { + data->fiddled_control->func_on_state_change(data->fiddled_control); + } + data->draw = true; + } + } + break; + case ButtonRelease: + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { + int cx; + int cy; + + cx = e->xbutton.x; + cy = e->xbutton.y; + + if (cy < 0 || cx < 0) { + data->close = true; + } + + if (cy > data->window_height || cx > data->window_width) { + data->close = true; + } + } + + if ((e->xbutton.button == Button1) && (data->fiddled_control)) { + SDL_ToolkitControlX11 *control; + + control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); + if (data->fiddled_control == control) { + data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + if (data->fiddled_control->func_on_state_change) { + data->fiddled_control->func_on_state_change(data->fiddled_control); + } + data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + data->draw = true; + } + } + break; + case KeyPress: + data->last_key_pressed = X11_XLookupKeysym(&e->xkey, 0); + + if (data->last_key_pressed == XK_Escape) { + for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { + if(data->controls[data->ev_i]->is_default_esc) { + data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + data->draw = true; + data->key_control_esc = data->controls[data->ev_i]; + } + } + } else if ((data->last_key_pressed == XK_Return) || (data->last_key_pressed == XK_KP_Enter)) { + for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { + if(data->controls[data->ev_i]->selected) { + data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + data->draw = true; + data->key_control_enter = data->controls[data->ev_i]; + } + } + } + break; + case KeyRelease: + { + KeySym key = X11_XLookupKeysym(&e->xkey, 0); + + // If this is a key release for something we didn't get the key down for, then bail. + if (key != data->last_key_pressed) { + break; + } + + if (key == XK_Escape) { + if (data->key_control_esc) { + if (data->key_control_esc->func_on_state_change) { + data->key_control_esc->func_on_state_change(data->key_control_esc); + } + } + } else if ((key == XK_Return) || (key == XK_KP_Enter)) { + if (data->key_control_enter) { + if (data->key_control_enter->func_on_state_change) { + data->key_control_enter->func_on_state_change(data->key_control_enter); + } + } + } else if (key == XK_Tab || key == XK_Left || key == XK_Right) { + if (data->focused_control) { + data->focused_control->selected = false; + } + data->draw = true; + for (data->ev_i = 0; data->ev_i < data->dyn_controls_sz; data->ev_i++) { + if (data->dyn_controls[data->ev_i] == data->focused_control) { + int next_index; + + if (key == XK_Left) { + next_index = data->ev_i - 1; + } else { + next_index = data->ev_i + 1; + } + if ((next_index >= data->dyn_controls_sz) || (next_index < 0)) { + if (key == XK_Right || key == XK_Left) { + next_index = data->ev_i; + } else { + next_index = 0; + } + } + data->focused_control = data->dyn_controls[next_index]; + data->focused_control->selected = true; + break; + } + } + } + break; + } + + } + + if (data->draw) { + X11Toolkit_DrawWindow(data); + } +} + +void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data) { + while (!data->close) { + XEvent e; + + /* Process settings events */ + X11_XPeekEvent(data->display, &e); + if (data->xsettings) { + xsettings_client_process_event(data->xsettings, &e); + } + + /* Do actual event loop */ + X11_XIfEvent(data->display, &e, X11Toolkit_EventTest, (XPointer)data); + X11Toolkit_ProcessWindowEvents(data, &e); + } +} + + +void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { + if (!data->pixmap) { + data->window_width = w; + data->window_height = h; + } else { + data->window_width = SDL_lroundf((w/data->iscale) * data->scale); + data->window_height = SDL_lroundf((h/data->iscale) * data->scale); + data->pixmap_width = w; + data->pixmap_height = h; + X11_XFreePixmap(data->display, data->drawable); + data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); + } + + X11_XResizeWindow(data->display, data->window, data->window_width, data->window_height); +} + +static void X11Toolkit_DestroyIconControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitIconControlX11 *icon_control; + + icon_control = (SDL_ToolkitIconControlX11 *)control; + X11_XFreeFont(control->window->display, icon_control->icon_char_font); + SDL_free(control); +} + +static void X11Toolkit_DrawIconControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitIconControlX11 *icon_control; + + icon_control = (SDL_ToolkitIconControlX11 *)control; + control->rect.w -= 2 * control->window->iscale; + control->rect.h -= 2 * control->window->iscale; + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_bg_shadow.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + (2 * control->window->iscale), control->rect.y + (2* control->window->iscale), control->rect.w, control->rect.h, 0, 360 * 64); + + switch (icon_control->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { + case SDL_MESSAGEBOX_ERROR: + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_red_darker.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_red.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x+(1* control->window->iscale), control->rect.y+(1* control->window->iscale), control->rect.w-(2* control->window->iscale), control->rect.h-(2* control->window->iscale), 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_white.pixel); + break; + case SDL_MESSAGEBOX_WARNING: + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_black.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_yellow.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x+(1* control->window->iscale), control->rect.y+(1* control->window->iscale), control->rect.w-(2* control->window->iscale), control->rect.h-(2* control->window->iscale), 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_black.pixel); + break; + case SDL_MESSAGEBOX_INFORMATION: + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_white.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_blue.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x+(1* control->window->iscale), control->rect.y+(1* control->window->iscale), control->rect.w-(2* control->window->iscale), control->rect.h-(2* control->window->iscale), 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_white.pixel); + break; + } + X11_XSetFont(control->window->display, control->window->ctx, icon_control->icon_char_font->fid); + X11_XDrawString(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + icon_control->icon_char_x, control->rect.y + icon_control->icon_char_y, &icon_control->icon_char, 1); + if (!control->window->utf8) { + X11_XSetFont(control->window->display, control->window->ctx, control->window->font_struct->fid); + } + + control->rect.w += 2 * control->window->iscale; + control->rect.h += 2 * control->window->iscale; +} + +static void X11Toolkit_CalculateIconControl(SDL_ToolkitControlX11 *base_control) { + SDL_ToolkitIconControlX11 *control; + int icon_char_w; + int icon_char_h; + int icon_char_max; + + control = (SDL_ToolkitIconControlX11 *)base_control; + X11Toolkit_GetTextWidthHeightForFont(control->icon_char_font, &control->icon_char, 1, &icon_char_w, &icon_char_h, &control->icon_char_a); + base_control->rect.w = icon_char_w + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2 * base_control->window->iscale; + base_control->rect.h = icon_char_h + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2 * base_control->window->iscale; + icon_char_max = SDL_max(base_control->rect.w, base_control->rect.h) + 2; + base_control->rect.w = icon_char_max; + base_control->rect.h = icon_char_max; + base_control->rect.y = 0; + base_control->rect.x = 0; + control->icon_char_y = control->icon_char_a + (base_control->rect.h - icon_char_h)/2 + 1; + control->icon_char_x = (base_control->rect.w - icon_char_w)/2 + 1; + base_control->rect.w += 2 * base_control->window->iscale; + base_control->rect.h += 2 * base_control->window->iscale; +} + +static void X11Toolkit_OnIconControlScaleChange(SDL_ToolkitControlX11 *base_control) { + SDL_ToolkitIconControlX11 *control; + char *font; + + control = (SDL_ToolkitIconControlX11 *)base_control; + X11_XFreeFont(base_control->window->display, control->icon_char_font); + SDL_asprintf(&font, g_IconFont, G_ICONFONT_SIZE * base_control->window->iscale); + control->icon_char_font = X11_XLoadQueryFont(base_control->window->display, font); + SDL_free(font); + if (!control->icon_char_font) { + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * base_control->window->iscale); + control->icon_char_font = X11_XLoadQueryFont(base_control->window->display, font); + SDL_free(font); + } +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window, SDL_MessageBoxFlags flags) { + SDL_ToolkitIconControlX11 *control; + SDL_ToolkitControlX11 *base_control; + char *font; + + /* Create control struct */ + control = (SDL_ToolkitIconControlX11 *)SDL_malloc(sizeof(SDL_ToolkitIconControlX11)); + base_control = (SDL_ToolkitControlX11 *)control; + if (!control) { + SDL_SetError("Unable to allocate icon control structure"); + return NULL; + } + + /* Fill out struct */ + base_control->window = window; + base_control->func_draw = X11Toolkit_DrawIconControl; + base_control->func_free = X11Toolkit_DestroyIconControl; + base_control->func_on_state_change = NULL; + base_control->func_calc_size = X11Toolkit_CalculateIconControl; + base_control->func_on_scale_change = X11Toolkit_OnIconControlScaleChange; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->selected = false; + base_control->dynamic = false; + base_control->is_default_enter = false; + base_control->is_default_esc = false; + control->flags = flags; + + /* Load font */ + SDL_asprintf(&font, g_IconFont, G_ICONFONT_SIZE * window->iscale); + control->icon_char_font = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + if (!control->icon_char_font) { + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); + control->icon_char_font = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + if (!control->icon_char_font) { + SDL_free(control); + return NULL; + } + } + + /* Set colors */ + switch (flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { + case SDL_MESSAGEBOX_ERROR: + control->icon_char = 'X'; + control->xcolor_white.flags = DoRed|DoGreen|DoBlue; + control->xcolor_white.red = 65535; + control->xcolor_white.green = 65535; + control->xcolor_white.blue = 65535; + control->xcolor_red.flags = DoRed|DoGreen|DoBlue; + control->xcolor_red.red = 65535; + control->xcolor_red.green = 0; + control->xcolor_red.blue = 0; + control->xcolor_red_darker.flags = DoRed|DoGreen|DoBlue; + control->xcolor_red_darker.red = 40535; + control->xcolor_red_darker.green = 0; + control->xcolor_red_darker.blue = 0; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_red); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_red_darker); + break; + case SDL_MESSAGEBOX_WARNING: + control->icon_char = '!'; + control->xcolor_black.flags = DoRed|DoGreen|DoBlue; + control->xcolor_black.red = 0; + control->xcolor_black.green = 0; + control->xcolor_black.blue = 0; + control->xcolor_yellow.flags = DoRed|DoGreen|DoBlue; + control->xcolor_yellow.red = 65535; + control->xcolor_yellow.green = 65535; + control->xcolor_yellow.blue = 0; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_black); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_yellow); + break; + case SDL_MESSAGEBOX_INFORMATION: + control->icon_char = 'i'; + control->xcolor_white.flags = DoRed|DoGreen|DoBlue; + control->xcolor_white.red = 65535; + control->xcolor_white.green = 65535; + control->xcolor_white.blue = 65535; + control->xcolor_blue.flags = DoRed|DoGreen|DoBlue; + control->xcolor_blue.red = 0; + control->xcolor_blue.green = 0; + control->xcolor_blue.blue = 65535; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_blue); + break; + default: + X11_XFreeFont(window->display, control->icon_char_font); + SDL_free(control); + return NULL; + } + control->xcolor_bg_shadow.flags = DoRed|DoGreen|DoBlue; + control->xcolor_bg_shadow.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 12500, 0, 65535); + control->xcolor_bg_shadow.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 12500, 0, 65535); + control->xcolor_bg_shadow.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 12500, 0, 65535); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_bg_shadow); + + /* Sizing and positioning */ + X11Toolkit_CalculateIconControl(base_control); + + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +bool X11Toolkit_NotifyControlOfSizeChange(SDL_ToolkitControlX11 *control) { + if (control->func_calc_size) { + control->func_calc_size(control); + return true; + } else { + return false; + } +} + +static void X11Toolkit_CalculateButtonControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + int text_d; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + X11Toolkit_GetTextWidthHeight(control->window, button_control->data->text, button_control->str_sz, &button_control->text_rect.w, &button_control->text_rect.h, &button_control->text_a, &text_d); + //control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.w; + //control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.h; + button_control->text_rect.x = (control->rect.w - button_control->text_rect.w)/2; + button_control->text_rect.y = button_control->text_a + (control->rect.h - button_control->text_rect.h)/2; + if (control->window->utf8) { + button_control->text_rect.y -= 2 * control->window->iscale; + } else { + button_control->text_rect.y -= 4 * control->window->iscale; + } +} + + +static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); + /* Draw bevel */ + if (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED || control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD) { + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_d.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w, control->rect.h); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w - (1* control->window->iscale), control->rect.h - (1* control->window->iscale)); + + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 2 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_pressed.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); + } else { + if (control->selected) { + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_d.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w, control->rect.h); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 5 * control->window->iscale, control->rect.h - 5 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_HOVER) ? control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 3 * control->window->iscale, control->rect.y + 3 * control->window->iscale, + control->rect.w - 6 * control->window->iscale, control->rect.h - 6 * control->window->iscale); + } else { + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_d.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w, control->rect.h); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w - 1 * control->window->iscale, control->rect.h - 1 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 2 * control->window->iscale, control->rect.h - 2 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); + + X11_XSetForeground(control->window->display, control->window->ctx, (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_HOVER) ? control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); + } + } + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); +#ifdef X_HAVE_UTF8_STRING + if (control->window->utf8) { + X11_Xutf8DrawString(control->window->display, control->window->drawable, control->window->font_set, control->window->ctx, + control->rect.x + button_control->text_rect.x, + control->rect.y + button_control->text_rect.y, + button_control->data->text, button_control->str_sz); + } else +#endif + { + X11_XDrawString(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + button_control->text_rect.x, control->rect.y + button_control->text_rect.y, + button_control->data->text, button_control->str_sz); + } +} + +static void X11Toolkit_OnButtonControlStateChange(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + if (button_control->cb && control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED) { + button_control->cb(control, button_control->cb_data); + } +} + +static void X11Toolkit_DestroyGenericControl(SDL_ToolkitControlX11 *control) { + SDL_free(control); +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *window, const SDL_MessageBoxButtonData *data) { + SDL_ToolkitButtonControlX11 *control; + SDL_ToolkitControlX11 *base_control; + int text_d; + + control = (SDL_ToolkitButtonControlX11 *)SDL_malloc(sizeof(SDL_ToolkitButtonControlX11)); + base_control = (SDL_ToolkitControlX11 *)control; + if (!control) { + SDL_SetError("Unable to allocate button control structure"); + return NULL; + } + base_control->window = window; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->func_calc_size = X11Toolkit_CalculateButtonControl; + base_control->func_draw = X11Toolkit_DrawButtonControl; + base_control->func_on_state_change = X11Toolkit_OnButtonControlStateChange; + base_control->func_free = X11Toolkit_DestroyGenericControl; + base_control->func_on_scale_change = NULL; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->selected = false; + base_control->dynamic = true; + base_control->is_default_enter = false; + base_control->is_default_esc = false; + if (data->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { + base_control->is_default_esc = true; + } + if (data->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { + base_control->is_default_enter = true; + base_control->selected = true; + } + control->data = data; + control->str_sz = SDL_strlen(control->data->text); + control->cb = NULL; + X11Toolkit_GetTextWidthHeight(base_control->window, control->data->text, control->str_sz, &control->text_rect.w, &control->text_rect.h, &control->text_a, &text_d); + base_control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * window->iscale + control->text_rect.w; + base_control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * window->iscale + control->text_rect.h; + control->text_rect.x = control->text_rect.y = 0; + X11Toolkit_CalculateButtonControl(base_control); + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +void X11Toolkit_RegisterCallbackForButtonControl(SDL_ToolkitControlX11 *control, void *data, void (*cb)(struct SDL_ToolkitControlX11 *, void *)) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + button_control->cb_data = data; + button_control->cb = cb; +} + +const SDL_MessageBoxButtonData *X11Toolkit_GetButtonControlData(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + return button_control->data; +} + +void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { + int i; + + if (!data) { + return; + } + +#if SDL_GRAB + if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { + X11_XUngrabPointer(data->display, CurrentTime); + X11_XUngrabKeyboard(data->display, CurrentTime); + } +#endif + + for (i = 0; i < data->controls_sz; i++) { + if (data->controls[i]->func_free) { + data->controls[i]->func_free(data->controls[i]); + } + } + if (data->controls) { + SDL_free(data->controls); + } + if (data->dyn_controls) { + SDL_free(data->dyn_controls); + } + + if (data->popup_windows) { + SDL_ListClear(&data->popup_windows); + } + + if (data->pixmap) { + X11_XFreePixmap(data->display, data->drawable); + } + +#ifdef X_HAVE_UTF8_STRING + if (data->font_set) { + X11_XFreeFontSet(data->display, data->font_set); + data->font_set = NULL; + } +#endif + + if (data->font_struct) { + X11_XFreeFont(data->display, data->font_struct); + data->font_struct = NULL; + } + +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && data->xdbe && !data->pixmap) { + X11_XdbeDeallocateBackBufferName(data->display, data->buf); + } +#endif + + if (data->xsettings) { + xsettings_client_destroy(data->xsettings); + } + + X11_XFreeGC(data->display, data->ctx); + + if (data->display) { + if (data->window != None) { + X11_XWithdrawWindow(data->display, data->window, data->screen); + X11_XDestroyWindow(data->display, data->window); + data->window = None; + } + + if (data->display_close) { + X11_XCloseDisplay(data->display); + } + data->display = NULL; + } + +#if SDL_SET_LOCALE + if (data->origlocale && (data->mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD)) { + (void)setlocale(LC_ALL, data->origlocale); + SDL_free(data->origlocale); + } +#endif + + SDL_free(data); +} + +static int X11Toolkit_CountLinesOfText(const char *text) +{ + int result = 0; + while (text && *text) { + const char *lf = SDL_strchr(text, '\n'); + result++; // even without an endline, this counts as a line. + text = lf ? lf + 1 : NULL; + } + return result; +} + +static void X11Toolkit_DrawLabelControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitLabelControlX11 *label_control; + int i; + + label_control = (SDL_ToolkitLabelControlX11 *)control; + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); + for (i = 0; i < label_control->sz; i++) { +#ifdef X_HAVE_UTF8_STRING + if (control->window->utf8) { + X11_Xutf8DrawString(control->window->display, control->window->drawable, control->window->font_set, control->window->ctx, + control->rect.x, control->rect.y + label_control->y[i], + label_control->lines[i], label_control->szs[i]); + } else +#endif + { + X11_XDrawString(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y + label_control->y[i], + label_control->lines[i], label_control->szs[i]); + } + } +} + +static void X11Toolkit_DestroyLabelControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitLabelControlX11 *label_control; + + label_control = (SDL_ToolkitLabelControlX11 *)control; + SDL_free(label_control->lines); + SDL_free(label_control->szs); + SDL_free(label_control->y); + SDL_free(label_control); +} + + +static void X11Toolkit_CalculateLabelControl(SDL_ToolkitControlX11 *base_control) { + SDL_ToolkitLabelControlX11 *control; + int ascent; + int descent; + int w; + int h; + int i; + + control = (SDL_ToolkitLabelControlX11 *)base_control; + for (i = 0; i < control->sz; i++) { + X11Toolkit_GetTextWidthHeight(base_control->window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent); + + if (i > 0) { + control->y[i] = ascent + descent + control->y[i-1]; + base_control->rect.h += ascent + descent + h; + } else { + control->y[i] = ascent; + base_control->rect.h = h; + } + } +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8) { + SDL_ToolkitLabelControlX11 *control; + SDL_ToolkitControlX11 *base_control; + int ascent; + int descent; + int i; + + if (!utf8) { + return NULL; + } + control = (SDL_ToolkitLabelControlX11 *)SDL_malloc(sizeof(SDL_ToolkitLabelControlX11)); + base_control = (SDL_ToolkitControlX11 *)control; + if (!control) { + SDL_SetError("Unable to allocate label control structure"); + return NULL; + } + base_control->window = window; + base_control->func_draw = X11Toolkit_DrawLabelControl; + base_control->func_on_state_change = NULL; + base_control->func_calc_size = X11Toolkit_CalculateLabelControl; + base_control->func_free = X11Toolkit_DestroyLabelControl; + base_control->func_on_scale_change = NULL; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->selected = false; + base_control->dynamic = false; + base_control->rect.w = 0; + base_control->rect.h = 0; + base_control->is_default_enter = false; + base_control->is_default_esc = false; + control->sz = X11Toolkit_CountLinesOfText(utf8); + control->lines = (char **)SDL_malloc(sizeof(char *) * control->sz); + control->y = (int *)SDL_calloc(control->sz, sizeof(int)); + control->szs = (size_t *)SDL_calloc(control->sz, sizeof(size_t)); + for (i = 0; i < control->sz; i++) { + const char *lf = SDL_strchr(utf8, '\n'); + const int length = lf ? (lf - utf8) : SDL_strlen(utf8); + int w; + int h; + + control->lines[i] = utf8; + X11Toolkit_GetTextWidthHeight(window, utf8, length, &w, &h, &ascent, &descent); + base_control->rect.w = SDL_max(base_control->rect.w, w); + + control->szs[i] = length; + if (lf && (lf > utf8) && (lf[-1] == '\r')) { + control->szs[i]--; + } + + if (i > 0) { + control->y[i] = ascent + descent + control->y[i-1]; + base_control->rect.h += ascent + descent + h; + } else { + control->y[i] = ascent; + base_control->rect.h = h; + } + utf8 += length + 1; + + if (!lf) { + break; + } + } + + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +int X11Toolkit_GetIconControlCharY(SDL_ToolkitControlX11 *control) { + SDL_ToolkitIconControlX11 *icon_control; + + icon_control = (SDL_ToolkitIconControlX11 *)control; + return icon_control->icon_char_y - icon_control->icon_char_a; +} + +void X11Toolkit_SignalWindowClose(SDL_ToolkitWindowX11 *data) { + data->close = true; +} + +#endif // SDL_VIDEO_DRIVER_X11 diff --git a/src/video/x11/SDL_x11toolkit.h b/src/video/x11/SDL_x11toolkit.h new file mode 100644 index 0000000000..d635512cd4 --- /dev/null +++ b/src/video/x11/SDL_x11toolkit.h @@ -0,0 +1,225 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifndef SDL_x11toolkit_h_ +#define SDL_x11toolkit_h_ + +#include "../../SDL_list.h" +#include "SDL_x11video.h" +#include "SDL_x11dyn.h" +#include "SDL_x11settings.h" +#include "SDL_x11toolkit.h" +#include "xsettings-client.h" + +#ifdef SDL_VIDEO_DRIVER_X11 + +/* Various predefined paddings */ +#define SDL_TOOLKIT_X11_ELEMENT_PADDING 4 +#define SDL_TOOLKIT_X11_ELEMENT_PADDING_2 12 +#define SDL_TOOLKIT_X11_ELEMENT_PADDING_3 8 +#define SDL_TOOLKIT_X11_ELEMENT_PADDING_4 16 +#define SDL_TOOLKIT_X11_ELEMENT_PADDING_5 3 + +typedef enum SDL_ToolkitChildModeX11 +{ + SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG, + SDL_TOOLKIT_WINDOW_MODE_X11_CHILD, /* For embedding into a normal SDL_Window */ + SDL_TOOLKIT_WINDOW_MODE_X11_MENU, + SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP +} SDL_ToolkitWindowModeX11; + +typedef struct SDL_ToolkitWindowX11 +{ + /* Locale */ + char *origlocale; + + /* Mode */ + SDL_ToolkitWindowModeX11 mode; + + /* Display */ + Display *display; + int screen; + bool display_close; + + /* Parent */ + SDL_Window *parent; + struct SDL_ToolkitWindowX11 *tk_parent; + + /* Window */ + Window window; + Drawable drawable; + + /* Visuals and drawing */ + Visual *visual; + XVisualInfo vi; + Colormap cmap; + GC ctx; + int depth; + bool pixmap; + + /* X11 extensions */ +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + XdbeBackBuffer buf; + bool xdbe; // Whether Xdbe is present or not +#endif +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR + bool xrandr; // Whether Xrandr is present or not +#endif + bool utf8; + + /* Atoms */ + Atom wm_protocols; + Atom wm_delete_message; + + /* Window and pixmap sizes */ + int window_width; // Window width. + int window_height; // Window height. + int pixmap_width; + int pixmap_height; + int window_x; + int window_y; + + /* XSettings and scaling */ + XSettingsClient *xsettings; + bool xsettings_first_time; + int iscale; + float scale; + + /* Font */ + XFontSet font_set; // for UTF-8 systems + XFontStruct *font_struct; // Latin1 (ASCII) fallback. + + /* Control colors */ + const SDL_MessageBoxColor *color_hints; + XColor xcolor[SDL_MESSAGEBOX_COLOR_COUNT]; + XColor xcolor_bevel_l1; + XColor xcolor_bevel_l2; + XColor xcolor_bevel_d; + XColor xcolor_pressed; + XColor xcolor_disabled_text; + + /* Control list */ + bool has_focus; + struct SDL_ToolkitControlX11 *focused_control; + struct SDL_ToolkitControlX11 *fiddled_control; + struct SDL_ToolkitControlX11 **controls; + size_t controls_sz; + struct SDL_ToolkitControlX11 **dyn_controls; + size_t dyn_controls_sz; + + /* User callbacks */ + void *cb_data; + void (*cb_on_scale_change)(struct SDL_ToolkitWindowX11 *, void *); + + /* Popup windows */ + SDL_ListNode *popup_windows; + + /* Event loop */ + XEvent *e; + struct SDL_ToolkitControlX11 *previous_control; + struct SDL_ToolkitControlX11 *key_control_esc; + struct SDL_ToolkitControlX11 *key_control_enter; + KeySym last_key_pressed; + int ev_i; + float ev_scale; + float ev_iscale; + bool draw; + bool close; + long event_mask; +} SDL_ToolkitWindowX11; + +typedef enum SDL_ToolkitControlStateX11 +{ + SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL, + SDL_TOOLKIT_CONTROL_STATE_X11_HOVER, + SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED, /* Key/Button Up */ + SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD, /* Key/Button Down */ + SDL_TOOLKIT_CONTROL_STATE_X11_DISABLED +} SDL_ToolkitControlStateX11; + +typedef struct SDL_ToolkitControlX11 +{ + SDL_ToolkitWindowX11 *window; + SDL_ToolkitControlStateX11 state; + SDL_Rect rect; + bool selected; + bool dynamic; + bool is_default_enter; + bool is_default_esc; + + /* User data */ + void *data; + + /* Virtual functions */ + void (*func_draw)(struct SDL_ToolkitControlX11 *); + void (*func_calc_size)(struct SDL_ToolkitControlX11 *); + void (*func_on_scale_change)(struct SDL_ToolkitControlX11 *); + void (*func_on_state_change)(struct SDL_ToolkitControlX11 *); + void (*func_free)(struct SDL_ToolkitControlX11 *); +} SDL_ToolkitControlX11; + +typedef struct SDL_ToolkitMenuItemX11 +{ + const char *utf8; + bool checkbox; + bool checked; + bool disabled; + void *cb_data; + void (*cb)(struct SDL_ToolkitMenuItemX11 *, void *); + SDL_ListNode *sub_menu; + + /* Internal use */ + SDL_Rect utf8_rect; + SDL_Rect hover_rect; + SDL_Rect check_rect; + SDL_ToolkitControlStateX11 state; + int arrow_x; + int arrow_y; + bool reverse_arrows; +} SDL_ToolkitMenuItemX11; + +/* WINDOW FUNCTIONS */ +extern SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_ToolkitWindowX11 *tkparent, SDL_ToolkitWindowModeX11 mode, const SDL_MessageBoxColor *colorhints); +extern bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx, int cy, char *title); +extern void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data); +extern void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h); +extern void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data); +extern void X11Toolkit_SignalWindowClose(SDL_ToolkitWindowX11 *data); + +/* GENERIC CONTROL FUNCTIONS */ +extern bool X11Toolkit_NotifyControlOfSizeChange(SDL_ToolkitControlX11 *control); + +/* ICON CONTROL FUNCTIONS */ +extern SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window, SDL_MessageBoxFlags flags); +extern int X11Toolkit_GetIconControlCharY(SDL_ToolkitControlX11 *control); + +/* LABEL CONTROL FUNCTIONS */ +extern SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8); + +/* BUTTON CONTROL FUNCTIONS */ +extern SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *window, const SDL_MessageBoxButtonData *data); +extern void X11Toolkit_RegisterCallbackForButtonControl(SDL_ToolkitControlX11 *control, void *data, void (*cb)(struct SDL_ToolkitControlX11 *, void *)); +extern const SDL_MessageBoxButtonData *X11Toolkit_GetButtonControlData(SDL_ToolkitControlX11 *control); + +#endif // SDL_VIDEO_DRIVER_X11 + +#endif // SDL_x11toolkit_h_ From ef19c7201549c3ec07aab2479c44ee259d616efe Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 2 Sep 2025 17:48:02 -0700 Subject: [PATCH 106/134] Set the texture scale and address mode when creating a texture Fixes https://github.com/libsdl-org/sdl2-compat/issues/506 --- src/render/opengl/SDL_render_gl.c | 93 ++++++++++++++----------- src/render/opengles2/SDL_render_gles2.c | 19 ++++- 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 8d59923658..7bd048cab0 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -442,6 +442,43 @@ static bool convert_format(Uint32 pixel_format, GLint *internalFormat, GLenum *f return true; } +static bool SetTextureScaleMode(GL_RenderData *data, GLenum textype, SDL_ScaleMode scaleMode) +{ + switch (scaleMode) { + case SDL_SCALEMODE_NEAREST: + data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + case SDL_SCALEMODE_PIXELART: // Uses linear sampling + case SDL_SCALEMODE_LINEAR: + data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + default: + return SDL_SetError("Unknown texture scale mode: %d", scaleMode); + } + return true; +} + +static GLint TranslateAddressMode(SDL_TextureAddressMode addressMode) +{ + switch (addressMode) { + case SDL_TEXTURE_ADDRESS_CLAMP: + return GL_CLAMP_TO_EDGE; + case SDL_TEXTURE_ADDRESS_WRAP: + return GL_REPEAT; + default: + SDL_assert(!"Unknown texture address mode"); + return GL_CLAMP_TO_EDGE; + } +} + +static void SetTextureAddressMode(GL_RenderData *data, GLenum textype, SDL_TextureAddressMode addressModeU, SDL_TextureAddressMode addressModeV) +{ + data->glTexParameteri(textype, GL_TEXTURE_WRAP_S, TranslateAddressMode(addressModeU)); + data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, TranslateAddressMode(addressModeV)); +} + static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; @@ -538,11 +575,13 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P data->format = format; data->formattype = type; - data->texture_scale_mode = SDL_SCALEMODE_INVALID; - data->texture_address_mode_u = SDL_TEXTURE_ADDRESS_INVALID; - data->texture_address_mode_v = SDL_TEXTURE_ADDRESS_INVALID; + data->texture_scale_mode = texture->scaleMode; + data->texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP; + data->texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP; renderdata->glEnable(textype); renderdata->glBindTexture(textype, data->texture); + SetTextureScaleMode(renderdata, textype, data->texture_scale_mode); + SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); #ifdef SDL_PLATFORM_MACOS #ifndef GL_TEXTURE_STORAGE_HINT_APPLE #define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC @@ -574,10 +613,11 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, texture_h, 0, format, type, NULL); } - renderdata->glDisable(textype); if (!GL_CheckError("glTexImage2D()", renderer)) { return false; } + SetTextureScaleMode(renderdata, textype, data->texture_scale_mode); + SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); #ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || @@ -600,11 +640,15 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P renderdata->glBindTexture(textype, data->utexture); renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w + 1) / 2, (texture_h + 1) / 2, 0, format, type, NULL); + SetTextureScaleMode(renderdata, textype, data->texture_scale_mode); + SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_U_NUMBER, data->utexture); renderdata->glBindTexture(textype, data->vtexture); renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w + 1) / 2, (texture_h + 1) / 2, 0, format, type, NULL); + SetTextureScaleMode(renderdata, textype, data->texture_scale_mode); + SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER, data->vtexture); } @@ -621,6 +665,8 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P renderdata->glBindTexture(textype, data->utexture); renderdata->glTexImage2D(textype, 0, GL_LUMINANCE_ALPHA, (texture_w + 1) / 2, (texture_h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + SetTextureScaleMode(renderdata, textype, data->texture_scale_mode); + SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_UV_NUMBER, data->utexture); } #endif @@ -660,6 +706,8 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P } #endif // SDL_HAVE_YUV + renderdata->glDisable(textype); + return GL_CheckError("", renderer); } @@ -1083,43 +1131,6 @@ static bool SetDrawState(GL_RenderData *data, const SDL_RenderCommand *cmd, cons return true; } -static bool SetTextureScaleMode(GL_RenderData *data, GLenum textype, SDL_ScaleMode scaleMode) -{ - switch (scaleMode) { - case SDL_SCALEMODE_NEAREST: - data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - case SDL_SCALEMODE_PIXELART: // Uses linear sampling - case SDL_SCALEMODE_LINEAR: - data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; - default: - return SDL_SetError("Unknown texture scale mode: %d", scaleMode); - } - return true; -} - -static GLint TranslateAddressMode(SDL_TextureAddressMode addressMode) -{ - switch (addressMode) { - case SDL_TEXTURE_ADDRESS_CLAMP: - return GL_CLAMP_TO_EDGE; - case SDL_TEXTURE_ADDRESS_WRAP: - return GL_REPEAT; - default: - SDL_assert(!"Unknown texture address mode"); - return GL_CLAMP_TO_EDGE; - } -} - -static void SetTextureAddressMode(GL_RenderData *data, GLenum textype, SDL_TextureAddressMode addressModeU, SDL_TextureAddressMode addressModeV) -{ - data->glTexParameteri(textype, GL_TEXTURE_WRAP_S, TranslateAddressMode(addressModeU)); - data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, TranslateAddressMode(addressModeV)); -} - static bool SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd) { SDL_Texture *texture = cmd->data.draw.texture; diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index ec4211fd02..8854c524fa 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -1658,9 +1658,9 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD data->texture_u = 0; data->texture_v = 0; #endif - data->texture_scale_mode = SDL_SCALEMODE_INVALID; - data->texture_address_mode_u = SDL_TEXTURE_ADDRESS_INVALID; - data->texture_address_mode_v = SDL_TEXTURE_ADDRESS_INVALID; + data->texture_scale_mode = texture->scaleMode; + data->texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP; + data->texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP; // Allocate a blob for image renderdata if (texture->access == SDL_TEXTUREACCESS_STREAMING) { @@ -1707,6 +1707,13 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD renderdata->glActiveTexture(GL_TEXTURE2); renderdata->glBindTexture(data->texture_type, data->texture_v); renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL); + if (!GL_CheckError("glTexImage2D()", renderer)) { + SDL_free(data->pixel_data); + SDL_free(data); + return false; + } + SetTextureScaleMode(renderdata, data->texture_type, data->texture_scale_mode); + SetTextureAddressMode(renderdata, data->texture_type, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_V_NUMBER, data->texture_v); data->texture_u = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER, 0); @@ -1728,6 +1735,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD SDL_free(data); return false; } + SetTextureScaleMode(renderdata, data->texture_type, data->texture_scale_mode); + SetTextureAddressMode(renderdata, data->texture_type, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER, data->texture_u); if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) { @@ -1755,6 +1764,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD SDL_free(data); return false; } + SetTextureScaleMode(renderdata, data->texture_type, data->texture_scale_mode); + SetTextureAddressMode(renderdata, data->texture_type, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_UV_NUMBER, data->texture_u); if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) { @@ -1785,6 +1796,8 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD return false; } } + SetTextureScaleMode(renderdata, data->texture_type, data->texture_scale_mode); + SetTextureAddressMode(renderdata, data->texture_type, data->texture_address_mode_u, data->texture_address_mode_v); SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER, data->texture); SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER, data->texture_type); From cc9937201e421ec55b12ad3f07ff2268f15096e8 Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Wed, 3 Sep 2025 04:37:56 +0300 Subject: [PATCH 107/134] x11: fix a typo after PR/13855 and kill lots of trailing whitespace --- src/video/x11/SDL_x11messagebox.c | 52 ++--- src/video/x11/SDL_x11settings.c | 2 +- src/video/x11/SDL_x11toolkit.c | 340 +++++++++++++++--------------- src/video/x11/SDL_x11toolkit.h | 56 ++--- 4 files changed, 223 insertions(+), 227 deletions(-) diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c index 32a3887dde..05a3f84418 100644 --- a/src/video/x11/SDL_x11messagebox.c +++ b/src/video/x11/SDL_x11messagebox.c @@ -56,7 +56,7 @@ typedef struct SDL_MessageBoxControlsX11 static void X11_MessageBoxButtonCallback(SDL_ToolkitControlX11 *control, void *data) { SDL_MessageBoxCallbackDataX11 *cbdata; - + cbdata = (SDL_MessageBoxCallbackDataX11 *)data; *cbdata->buttonID = X11Toolkit_GetButtonControlData(control)->buttonID; X11Toolkit_SignalWindowClose(cbdata->window); @@ -71,25 +71,25 @@ static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp, int h; int i; int t; - + /* Init vars */ max_button_w = 50; max_button_h = 0; w = h = 2; i = t = total_button_w = total_text_and_icon_w = 0; max_button_w *= controls->window->iscale; - + /* Positioning and sizing */ for (i = 0; i < controls->messageboxdata->numbuttons; i++) { max_button_w = SDL_max(max_button_w, controls->buttons[i]->rect.w); max_button_h = SDL_max(max_button_h, controls->buttons[i]->rect.h); controls->buttons[i]->rect.x = 0; } - + if (controls->icon) { controls->icon->rect.x = controls->icon->rect.y = 0; } - + if (controls->icon) { controls->message->rect.x = (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) + controls->icon->rect.x + controls->icon->rect.w; controls->message->rect.y = X11Toolkit_GetIconControlCharY(controls->icon); @@ -100,40 +100,40 @@ static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp, controls->icon->rect.w = 0; controls->icon->rect.h = 0; controls->icon->rect.x = 0; - controls->icon->rect.y = 0; + controls->icon->rect.y = 0; } if (controls->messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { for (i = controls->messageboxdata->numbuttons; i != -1; i--) { controls->buttons[i]->rect.w = max_button_w; controls->buttons[i]->rect.h = max_button_h; - X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); - + X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); + if (controls->icon->rect.h > controls->message->rect.h) { controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 *controls-> window->iscale); } else { - controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); - } - + controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + if (i) { controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale); } - } + } } else { for (i = 0; i < controls->messageboxdata->numbuttons; i++) { controls->buttons[i]->rect.w = max_button_w; controls->buttons[i]->rect.h = max_button_h; - X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); - + X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); + if (controls->icon->rect.h > controls->message->rect.h) { controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); } else { - controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); - } - + controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + if (i) { controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale); } - } + } } total_button_w = controls->buttons[controls->messageboxdata->numbuttons-1]->rect.x + controls->buttons[controls->messageboxdata->numbuttons-1]->rect.w; total_text_and_icon_w = controls->message->rect.x + controls->message->rect.w; @@ -161,8 +161,8 @@ static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp, } if (!controls->messageboxdata->message) { controls->icon->rect.x = (w - controls->icon->rect.w)/2; - } - + } + *wp = w; *hp = h; } @@ -171,7 +171,7 @@ static void X11_OnMessageBoxScaleChange(SDL_ToolkitWindowX11 *window, void *data SDL_MessageBoxControlsX11 *controls; int w; int h; - + controls = data; X11_PositionMessageBox(controls, &w, &h); X11Toolkit_ResizeWindow(window, w, h); @@ -185,16 +185,16 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int int i; int w; int h; - + controls.messageboxdata = messageboxdata; - + /* Color scheme */ if (messageboxdata->colorScheme) { colorhints = messageboxdata->colorScheme->colors; } else { colorhints = NULL; } - + /* Create window */ controls.window = X11Toolkit_CreateWindowStruct(messageboxdata->window, NULL, SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG, colorhints); controls.window->cb_data = &controls; @@ -202,7 +202,7 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int if (!controls.window) { return false; } - + /* Create controls */ controls.buttons = SDL_calloc(messageboxdata->numbuttons, sizeof(SDL_ToolkitControlX11 *)); controls.icon = X11Toolkit_CreateIconControl(controls.window, messageboxdata->flags); @@ -216,7 +216,7 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int /* Positioning */ X11_PositionMessageBox(&controls, &w, &h); - + /* Actually create window, do event loop, cleanup */ X11Toolkit_CreateWindowRes(controls.window, w, h, 0, 0, (char *)messageboxdata->title); X11Toolkit_DoWindowEventLoop(controls.window); diff --git a/src/video/x11/SDL_x11settings.c b/src/video/x11/SDL_x11settings.c index a608919770..da8b794405 100644 --- a/src/video/x11/SDL_x11settings.c +++ b/src/video/x11/SDL_x11settings.c @@ -41,7 +41,7 @@ static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSetti SDL_VideoDevice *_this = data; if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) == 0 || - SDL_strcmp(name, SDL_XSETTINGS_GDK_UNSCALED_DPI) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_GDK_UNSCALED_DPI) == 0 || SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { UpdateContentScale(_this); } diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c index cccdc6f435..8550360764 100644 --- a/src/video/x11/SDL_x11toolkit.c +++ b/src/video/x11/SDL_x11toolkit.c @@ -39,17 +39,17 @@ typedef struct SDL_ToolkitIconControlX11 { SDL_ToolkitControlX11 parent; - + /* Icon type */ SDL_MessageBoxFlags flags; char icon_char; - + /* Font */ XFontStruct *icon_char_font; int icon_char_x; int icon_char_y; int icon_char_a; - + /* Colors */ XColor xcolor_black; XColor xcolor_red; @@ -63,16 +63,16 @@ typedef struct SDL_ToolkitIconControlX11 typedef struct SDL_ToolkitButtonControlX11 { SDL_ToolkitControlX11 parent; - + /* Data */ const SDL_MessageBoxButtonData *data; - + /* Text */ SDL_Rect text_rect; int text_a; int text_d; int str_sz; - + /* Callback */ void *cb_data; void (*cb)(struct SDL_ToolkitControlX11 *, void *); @@ -81,7 +81,7 @@ typedef struct SDL_ToolkitButtonControlX11 typedef struct SDL_ToolkitLabelControlX11 { SDL_ToolkitControlX11 parent; - + char **lines; int *y; size_t *szs; @@ -91,14 +91,14 @@ typedef struct SDL_ToolkitLabelControlX11 typedef struct SDL_ToolkitMenuBarControlX11 { SDL_ToolkitControlX11 parent; - + SDL_ListNode *menu_items; } SDL_ToolkitMenuBarControlX11; typedef struct SDL_ToolkitMenuControlX11 { SDL_ToolkitControlX11 parent; - + SDL_ListNode *menu_items; XColor xcolor_check_bg; } SDL_ToolkitMenuControlX11; @@ -171,8 +171,7 @@ static float X11Toolkit_GetUIScale(XSettingsClient *client, Display *display) // If that failed, try "Xft.dpi" from the XResourcesDatabase... // We attempt to read this directly to get the live value, XResourceManagerString // is cached per display connection. - if (scale_factor <= 0.0) - { + if (scale_factor <= 0.0) { int status, real_format; Atom real_type; Atom res_mgr; @@ -253,30 +252,30 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, int i; window = data; - + if (window->xsettings_first_time) { return; } - + if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) == 0 || SDL_strcmp(name, SDL_XSETTINGS_GDK_UNSCALED_DPI) == 0 || SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { bool dbe_already_setup; bool pixmap_already_setup; - + if (window->pixmap) { pixmap_already_setup = true; } else { dbe_already_setup = true; } - + /* set scale vars */ window->scale = X11Toolkit_GetUIScale(window->xsettings, window->display); window->iscale = (int)SDL_ceilf(window->scale); if (roundf(window->scale) == window->scale) { window->scale = 0; } - + /* set up window */ if (window->scale != 0) { window->window_width = SDL_lroundf((window->window_width/window->iscale) * window->scale); @@ -287,7 +286,7 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, } else { window->pixmap = false; } - + if (window->pixmap) { if (!pixmap_already_setup) { #ifdef SDL_VIDEO_DRIVER_X11_XDBE @@ -310,7 +309,7 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, #endif } } - + /* setup fonts */ #ifdef X_HAVE_UTF8_STRING if (window->font_set) { @@ -320,7 +319,7 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, if (window->font_struct) { X11_XFreeFont(window->display, window->font_struct); } - + #ifdef X_HAVE_UTF8_STRING window->utf8 = true; window->font_set = NULL; @@ -331,7 +330,7 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, window->font_struct = NULL; for (i_font = 0; g_ToolkitFont[i_font]; ++i_font) { char *font; - + SDL_asprintf(&font, g_ToolkitFont[i_font], G_TOOLKITFONT_SIZE * window->iscale); window->font_set = X11_XCreateFontSet(window->display, font, &missing, &num_missing, NULL); @@ -363,17 +362,17 @@ static void X11Toolkit_SettingsNotify(const char *name, XSettingsAction action, if (window->controls[i]->func_on_scale_change) { window->controls[i]->func_on_scale_change(window->controls[i]); } - + if (window->controls[i]->func_calc_size) { window->controls[i]->func_calc_size(window->controls[i]); } } - + /* notify cb */ if (window->cb_on_scale_change) { window->cb_on_scale_change(window, window->cb_data); } - + /* update ev scales */ if (!window->pixmap) { window->ev_scale = window->ev_iscale = 1; @@ -431,19 +430,19 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool if (!SDL_X11_LoadSymbols()) { return NULL; } - + // This code could get called from multiple threads maybe? X11_XInitThreads(); - + window = (SDL_ToolkitWindowX11 *)SDL_malloc(sizeof(SDL_ToolkitWindowX11)); if (!window) { SDL_SetError("Unable to allocate toolkit window structure"); return NULL; } - + window->mode = mode; window->tk_parent = tkparent; - + #if SDL_SET_LOCALE if (mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { window->origlocale = setlocale(LC_ALL, NULL); @@ -470,13 +469,13 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool if (!window->display) { ErrorFreeRetNull("Couldn't open X11 display", window); } - } - -#ifdef SDL_VIDEO_DRIVER_X11_XRSANDR + } + +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR int xrandr_event_base, xrandr_error_base; window->xrandr = X11_XRRQueryExtension(window->display, &xrandr_event_base, &xrandr_error_base); #endif - + /* Scale/Xsettings */ window->pixmap = false; window->xsettings_first_time = true; @@ -487,7 +486,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool if (roundf(window->scale) == window->scale) { window->scale = 0; } - + #ifdef X_HAVE_UTF8_STRING window->utf8 = true; window->font_set = NULL; @@ -498,7 +497,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool window->font_struct = NULL; for (i_font = 0; g_ToolkitFont[i_font]; ++i_font) { char *font; - + SDL_asprintf(&font, g_ToolkitFont[i_font], G_TOOLKITFONT_SIZE * window->iscale); window->font_set = X11_XCreateFontSet(window->display, font, &missing, &num_missing, NULL); @@ -527,7 +526,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool ErrorCloseFreeRetNull("Couldn't load font %s", g_ToolkitFontLatin1, window); } } - + if (!colorhints) { colorhints = g_default_colors; } @@ -540,7 +539,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool window->xcolor[i].green = colorhints[i].g * 257; window->xcolor[i].blue = colorhints[i].b * 257; } - + /* Generate bevel and pressed colors */ window->xcolor_bevel_l1.flags = DoRed|DoGreen|DoBlue; window->xcolor_bevel_l1.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 12500, 0, 65535); @@ -556,18 +555,17 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool window->xcolor_bevel_d.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red - 22500, 0, 65535); window->xcolor_bevel_d.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green - 22500, 0, 65535); window->xcolor_bevel_d.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue - 22500, 0, 65535); - + window->xcolor_pressed.flags = DoRed|DoGreen|DoBlue; window->xcolor_pressed.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].red - 12500, 0, 65535); window->xcolor_pressed.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].green - 12500, 0, 65535); window->xcolor_pressed.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].blue - 12500, 0, 65535); - + window->xcolor_disabled_text.flags = DoRed|DoGreen|DoBlue; window->xcolor_disabled_text.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].red + 19500, 0, 65535); window->xcolor_disabled_text.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].green + 19500, 0, 65535); window->xcolor_disabled_text.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].blue + 19500, 0, 65535); - - + /* Screen */ window->parent = parent; if (parent) { @@ -576,7 +574,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool } else { window->screen = DefaultScreen(window->display); } - + /* Visuals */ if (mode == SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { window->visual = parent->internal->visual; @@ -599,7 +597,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_d); X11_XAllocColor(window->display, window->cmap, &window->xcolor_pressed); X11_XAllocColor(window->display, window->cmap, &window->xcolor_disabled_text); - + /* Control list */ window->has_focus = false; window->controls = NULL; @@ -607,10 +605,10 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool window->dyn_controls_sz = 0; window->fiddled_control = NULL; window->dyn_controls = NULL; - + /* Menu windows */ window->popup_windows = NULL; - + return window; } @@ -623,7 +621,7 @@ static void X11Toolkit_AddControlToWindow(SDL_ToolkitWindowX11 *window, SDL_Tool window->controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->controls, sizeof(struct SDL_ToolkitControlX11 *) * window->controls_sz); } window->controls[window->controls_sz - 1] = control; - + /* If dynamic, add it to the dynamic controls list too */ if (control->dynamic) { window->dyn_controls_sz++; @@ -638,7 +636,7 @@ static void X11Toolkit_AddControlToWindow(SDL_ToolkitWindowX11 *window, SDL_Tool /* If selected, set currently focused control to it */ if (control->selected) { window->focused_control = control; - } + } } bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx, int cy, char *title) @@ -661,10 +659,10 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx const bool use_xrandr_by_default = true; #endif #endif - + if (data->scale == 0) { - data->window_width = w; - data->window_height = h; + data->window_width = w; + data->window_height = h; } else { data->window_width = SDL_lroundf((w/data->iscale) * data->scale); data->window_height = SDL_lroundf((h/data->iscale) * data->scale); @@ -672,11 +670,11 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx data->pixmap_height = h; data->pixmap = true; } - + if (data->parent) { windowdata = data->parent->internal; } - + valuemask = CWEventMask | CWColormap; data->event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | @@ -694,7 +692,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx } else { parent_win = root_win; } - + data->window = X11_XCreateWindow( display, parent_win, 0, 0, @@ -723,7 +721,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx if (windowdata && data->mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { X11_XSetTransientForHint(display, data->window, windowdata->xwindow); } - + if (data->tk_parent) { X11_XSetTransientForHint(display, data->window, data->tk_parent->window); } @@ -749,7 +747,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&_NET_WM_WINDOW_TYPE_TOOLTIP, 1); - } + } // Allow the window to be deleted by the window manager data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); @@ -782,22 +780,22 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx XRRScreenResources *screen_res; XRRCrtcInfo *crtc_info; RROutput default_out; - + screen_res = X11_XRRGetScreenResourcesCurrent(display, root_win); if (!screen_res) { goto NOXRANDR; } - + default_out = X11_XRRGetOutputPrimary(display, root_win); if (default_out != None) { XRROutputInfo *out_info; - - out_info = X11_XRRGetOutputInfo(display, screen_res, default_out); - if (out_info->connection != RR_Connected) { + + out_info = X11_XRRGetOutputInfo(display, screen_res, default_out); + if (out_info->connection != RR_Connected) { X11_XRRFreeOutputInfo(out_info); goto FIRSTOUTPUTXRANDR; - } - + } + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc); if (crtc_info) { x = (crtc_info->width - data->window_width) / 2; @@ -808,23 +806,23 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx } else { X11_XRRFreeOutputInfo(out_info); goto NOXRANDR; - } + } } else { FIRSTOUTPUTXRANDR: if (screen_res->noutput > 0) { XRROutputInfo *out_info; - + out_info = X11_XRRGetOutputInfo(display, screen_res, screen_res->outputs[0]); if (!out_info) { goto FIRSTCRTCXRANDR; } - + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc); if (!crtc_info) { X11_XRRFreeOutputInfo(out_info); goto FIRSTCRTCXRANDR; } - + x = (crtc_info->width - data->window_width) / 2; y = (crtc_info->height - data->window_height) / 3; X11_XRRFreeOutputInfo(out_info); @@ -832,7 +830,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx X11_XRRFreeScreenResources(screen_res); goto MOVEWINDOW; } - + FIRSTCRTCXRANDR: if (!screen_res->ncrtc) { X11_XRRFreeScreenResources(screen_res); @@ -848,7 +846,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx } else { X11_XRRFreeScreenResources(screen_res); goto NOXRANDR; - } + } } } #endif @@ -863,7 +861,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx X11_XMoveWindow(display, data->window, x, y); data->window_x = x; data->window_y = y; - + sizehints = X11_XAllocSizeHints(); if (sizehints) { sizehints->flags = USPosition | USSize | PMaxSize | PMinSize; @@ -881,9 +879,9 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx } X11_XMapRaised(display, data->window); - + data->drawable = data->window; - + #ifdef SDL_VIDEO_DRIVER_X11_XDBE // Initialise a back buffer for double buffering if (SDL_X11_HAVE_XDBE && !data->pixmap) { @@ -901,7 +899,7 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx if (data->pixmap) { data->drawable = X11_XCreatePixmap(display, data->window, data->pixmap_width, data->pixmap_height, data->depth); } - + SDL_zero(ctx_vals); ctx_vals.foreground = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; ctx_vals.background = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; @@ -922,20 +920,20 @@ bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx data->ev_scale = data->scale; data->ev_iscale = data->iscale; } - + #if SDL_GRAB if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { X11_XGrabPointer(display, data->window, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); X11_XGrabKeyboard(display, data->window, False, GrabModeAsync, GrabModeAsync, CurrentTime); } #endif - + return true; } static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { int i; - + #ifdef SDL_VIDEO_DRIVER_X11_XDBE if (SDL_X11_HAVE_XDBE && data->xdbe && !data->pixmap) { X11_XdbeBeginIdiom(data->display); @@ -948,10 +946,10 @@ static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { } else { X11_XFillRectangle(data->display, data->drawable, data->ctx, 0, 0, data->window_width, data->window_height); } - + for (i = 0; i < data->controls_sz; i++) { SDL_ToolkitControlX11 *control; - + control = data->controls[i]; if (control) { if (control->func_draw) { @@ -959,7 +957,7 @@ static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { } } } - + #ifdef SDL_VIDEO_DRIVER_X11_XDBE if (SDL_X11_HAVE_XDBE && data->xdbe && !data->pixmap) { XdbeSwapInfo swap_info; @@ -975,14 +973,14 @@ static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { XImage *put_image; SDL_Surface *pixmap_surface; SDL_Surface *put_surface; - + /* FIXME: Implement SHM transport? */ pixmap_image = X11_XGetImage(data->display, data->drawable, 0, 0 , data->pixmap_width, data->pixmap_height, AllPlanes, ZPixmap); pixmap_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), pixmap_image->data, pixmap_image->bytes_per_line); put_surface = SDL_ScaleSurface(pixmap_surface, data->window_width, data->window_height, SDL_SCALEMODE_LINEAR); put_image = X11_XCreateImage(data->display, data->visual, data->vi.depth, ZPixmap, 0, put_surface->pixels, data->window_width, data->window_height, 32, put_surface->pitch); X11_XPutImage(data->display, data->window, data->ctx, put_image, 0, 0, 0, 0, data->window_width, data->window_height); - + X11_XDestroyImage(pixmap_image); /* Needed because XDestroyImage results in a double-free otherwise */ put_image->data = NULL; @@ -1015,14 +1013,14 @@ static SDL_ToolkitControlX11 *X11Toolkit_GetControlMouseIsOn(SDL_ToolkitWindowX1 static Bool X11Toolkit_EventTest(Display *display, XEvent *event, XPointer arg) { SDL_ToolkitWindowX11 *data = (SDL_ToolkitWindowX11 *)arg; - + if (event->xany.display != data->display) { return False; } - + if (event->xany.window == data->window) { return True; - } + } return False; } @@ -1036,7 +1034,7 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { data->draw = false; data->e = e; - + switch (e->type) { case Expose: data->draw = true; @@ -1062,14 +1060,14 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { } break; case MotionNotify: - if (data->has_focus) { + if (data->has_focus) { data->previous_control = data->fiddled_control; data->fiddled_control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); if (data->previous_control) { data->previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; if (data->previous_control->func_on_state_change) { data->previous_control->func_on_state_change(data->previous_control); - } + } data->draw = true; } if (data->fiddled_control) { @@ -1077,12 +1075,12 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_HOVER; if (data->fiddled_control->func_on_state_change) { data->fiddled_control->func_on_state_change(data->fiddled_control); - } - data->draw = true; + } + data->draw = true; } else { data->fiddled_control = NULL; } - } + } } break; case ButtonPress: @@ -1100,23 +1098,23 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD; if (data->fiddled_control->func_on_state_change) { data->fiddled_control->func_on_state_change(data->fiddled_control); - } + } data->draw = true; - } + } } break; case ButtonRelease: if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { int cx; int cy; - + cx = e->xbutton.x; cy = e->xbutton.y; - + if (cy < 0 || cx < 0) { data->close = true; } - + if (cy > data->window_height || cx > data->window_width) { data->close = true; } @@ -1124,7 +1122,7 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { if ((e->xbutton.button == Button1) && (data->fiddled_control)) { SDL_ToolkitControlX11 *control; - + control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); if (data->fiddled_control == control) { data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; @@ -1138,9 +1136,9 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { break; case KeyPress: data->last_key_pressed = X11_XLookupKeysym(&e->xkey, 0); - + if (data->last_key_pressed == XK_Escape) { - for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { + for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { if(data->controls[data->ev_i]->is_default_esc) { data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; data->draw = true; @@ -1148,7 +1146,7 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { } } } else if ((data->last_key_pressed == XK_Return) || (data->last_key_pressed == XK_KP_Enter)) { - for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { + for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { if(data->controls[data->ev_i]->selected) { data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; data->draw = true; @@ -1165,31 +1163,31 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { if (key != data->last_key_pressed) { break; } - + if (key == XK_Escape) { if (data->key_control_esc) { if (data->key_control_esc->func_on_state_change) { data->key_control_esc->func_on_state_change(data->key_control_esc); - } + } } } else if ((key == XK_Return) || (key == XK_KP_Enter)) { if (data->key_control_enter) { if (data->key_control_enter->func_on_state_change) { data->key_control_enter->func_on_state_change(data->key_control_enter); - } + } } } else if (key == XK_Tab || key == XK_Left || key == XK_Right) { if (data->focused_control) { data->focused_control->selected = false; } data->draw = true; - for (data->ev_i = 0; data->ev_i < data->dyn_controls_sz; data->ev_i++) { + for (data->ev_i = 0; data->ev_i < data->dyn_controls_sz; data->ev_i++) { if (data->dyn_controls[data->ev_i] == data->focused_control) { int next_index; - + if (key == XK_Left) { next_index = data->ev_i - 1; - } else { + } else { next_index = data->ev_i + 1; } if ((next_index >= data->dyn_controls_sz) || (next_index < 0)) { @@ -1206,10 +1204,9 @@ void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { } } break; - } + } + } - } - if (data->draw) { X11Toolkit_DrawWindow(data); } @@ -1223,8 +1220,8 @@ void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data) { X11_XPeekEvent(data->display, &e); if (data->xsettings) { xsettings_client_process_event(data->xsettings, &e); - } - + } + /* Do actual event loop */ X11_XIfEvent(data->display, &e, X11Toolkit_EventTest, (XPointer)data); X11Toolkit_ProcessWindowEvents(data, &e); @@ -1232,10 +1229,10 @@ void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data) { } -void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { +void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { if (!data->pixmap) { - data->window_width = w; - data->window_height = h; + data->window_width = w; + data->window_height = h; } else { data->window_width = SDL_lroundf((w/data->iscale) * data->scale); data->window_height = SDL_lroundf((h/data->iscale) * data->scale); @@ -1250,7 +1247,7 @@ void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { static void X11Toolkit_DestroyIconControl(SDL_ToolkitControlX11 *control) { SDL_ToolkitIconControlX11 *icon_control; - + icon_control = (SDL_ToolkitIconControlX11 *)control; X11_XFreeFont(control->window->display, icon_control->icon_char_font); SDL_free(control); @@ -1264,7 +1261,7 @@ static void X11Toolkit_DrawIconControl(SDL_ToolkitControlX11 *control) { control->rect.h -= 2 * control->window->iscale; X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_bg_shadow.pixel); X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + (2 * control->window->iscale), control->rect.y + (2* control->window->iscale), control->rect.w, control->rect.h, 0, 360 * 64); - + switch (icon_control->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { case SDL_MESSAGEBOX_ERROR: X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_red_darker.pixel); @@ -1293,7 +1290,7 @@ static void X11Toolkit_DrawIconControl(SDL_ToolkitControlX11 *control) { if (!control->window->utf8) { X11_XSetFont(control->window->display, control->window->ctx, control->window->font_struct->fid); } - + control->rect.w += 2 * control->window->iscale; control->rect.h += 2 * control->window->iscale; } @@ -1303,14 +1300,14 @@ static void X11Toolkit_CalculateIconControl(SDL_ToolkitControlX11 *base_control) int icon_char_w; int icon_char_h; int icon_char_max; - + control = (SDL_ToolkitIconControlX11 *)base_control; X11Toolkit_GetTextWidthHeightForFont(control->icon_char_font, &control->icon_char, 1, &icon_char_w, &icon_char_h, &control->icon_char_a); base_control->rect.w = icon_char_w + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2 * base_control->window->iscale; base_control->rect.h = icon_char_h + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2 * base_control->window->iscale; icon_char_max = SDL_max(base_control->rect.w, base_control->rect.h) + 2; base_control->rect.w = icon_char_max; - base_control->rect.h = icon_char_max; + base_control->rect.h = icon_char_max; base_control->rect.y = 0; base_control->rect.x = 0; control->icon_char_y = control->icon_char_a + (base_control->rect.h - icon_char_h)/2 + 1; @@ -1322,7 +1319,7 @@ static void X11Toolkit_CalculateIconControl(SDL_ToolkitControlX11 *base_control) static void X11Toolkit_OnIconControlScaleChange(SDL_ToolkitControlX11 *base_control) { SDL_ToolkitIconControlX11 *control; char *font; - + control = (SDL_ToolkitIconControlX11 *)base_control; X11_XFreeFont(base_control->window->display, control->icon_char_font); SDL_asprintf(&font, g_IconFont, G_ICONFONT_SIZE * base_control->window->iscale); @@ -1332,7 +1329,7 @@ static void X11Toolkit_OnIconControlScaleChange(SDL_ToolkitControlX11 *base_cont SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * base_control->window->iscale); control->icon_char_font = X11_XLoadQueryFont(base_control->window->display, font); SDL_free(font); - } + } } SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window, SDL_MessageBoxFlags flags) { @@ -1347,7 +1344,7 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window SDL_SetError("Unable to allocate icon control structure"); return NULL; } - + /* Fill out struct */ base_control->window = window; base_control->func_draw = X11Toolkit_DrawIconControl; @@ -1361,7 +1358,7 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window base_control->is_default_enter = false; base_control->is_default_esc = false; control->flags = flags; - + /* Load font */ SDL_asprintf(&font, g_IconFont, G_ICONFONT_SIZE * window->iscale); control->icon_char_font = X11_XLoadQueryFont(window->display, font); @@ -1374,8 +1371,8 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window SDL_free(control); return NULL; } - } - + } + /* Set colors */ switch (flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { case SDL_MESSAGEBOX_ERROR: @@ -1392,9 +1389,9 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window control->xcolor_red_darker.red = 40535; control->xcolor_red_darker.green = 0; control->xcolor_red_darker.blue = 0; - X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); - X11_XAllocColor(window->display, window->cmap, &control->xcolor_red); - X11_XAllocColor(window->display, window->cmap, &control->xcolor_red_darker); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_red); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_red_darker); break; case SDL_MESSAGEBOX_WARNING: control->icon_char = '!'; @@ -1406,21 +1403,21 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window control->xcolor_yellow.red = 65535; control->xcolor_yellow.green = 65535; control->xcolor_yellow.blue = 0; - X11_XAllocColor(window->display, window->cmap, &control->xcolor_black); - X11_XAllocColor(window->display, window->cmap, &control->xcolor_yellow); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_black); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_yellow); break; case SDL_MESSAGEBOX_INFORMATION: control->icon_char = 'i'; control->xcolor_white.flags = DoRed|DoGreen|DoBlue; control->xcolor_white.red = 65535; control->xcolor_white.green = 65535; - control->xcolor_white.blue = 65535; + control->xcolor_white.blue = 65535; control->xcolor_blue.flags = DoRed|DoGreen|DoBlue; control->xcolor_blue.red = 0; control->xcolor_blue.green = 0; - control->xcolor_blue.blue = 65535; - X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); - X11_XAllocColor(window->display, window->cmap, &control->xcolor_blue); + control->xcolor_blue.blue = 65535; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_blue); break; default: X11_XFreeFont(window->display, control->icon_char_font); @@ -1431,12 +1428,12 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window control->xcolor_bg_shadow.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 12500, 0, 65535); control->xcolor_bg_shadow.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 12500, 0, 65535); control->xcolor_bg_shadow.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 12500, 0, 65535); - X11_XAllocColor(window->display, window->cmap, &control->xcolor_bg_shadow); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_bg_shadow); /* Sizing and positioning */ X11Toolkit_CalculateIconControl(base_control); - - X11Toolkit_AddControlToWindow(window, base_control); + + X11Toolkit_AddControlToWindow(window, base_control); return base_control; } @@ -1452,24 +1449,24 @@ bool X11Toolkit_NotifyControlOfSizeChange(SDL_ToolkitControlX11 *control) { static void X11Toolkit_CalculateButtonControl(SDL_ToolkitControlX11 *control) { SDL_ToolkitButtonControlX11 *button_control; int text_d; - + button_control = (SDL_ToolkitButtonControlX11 *)control; X11Toolkit_GetTextWidthHeight(control->window, button_control->data->text, button_control->str_sz, &button_control->text_rect.w, &button_control->text_rect.h, &button_control->text_a, &text_d); //control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.w; - //control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.h; + //control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.h; button_control->text_rect.x = (control->rect.w - button_control->text_rect.w)/2; button_control->text_rect.y = button_control->text_a + (control->rect.h - button_control->text_rect.h)/2; if (control->window->utf8) { button_control->text_rect.y -= 2 * control->window->iscale; } else { - button_control->text_rect.y -= 4 * control->window->iscale; + button_control->text_rect.y -= 4 * control->window->iscale; } } static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { SDL_ToolkitButtonControlX11 *button_control; - + button_control = (SDL_ToolkitButtonControlX11 *)control; X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); /* Draw bevel */ @@ -1478,18 +1475,17 @@ static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h); - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w - (1* control->window->iscale), control->rect.h - (1* control->window->iscale)); - - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, control->rect.w - 3 * control->window->iscale, control->rect.h - 2 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, @@ -1510,17 +1506,17 @@ static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, control->rect.w - 5 * control->window->iscale, control->rect.h - 5 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_HOVER) ? control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 3 * control->window->iscale, control->rect.y + 3 * control->window->iscale, @@ -1535,24 +1531,24 @@ static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w - 1 * control->window->iscale, control->rect.h - 1 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, control->rect.w - 2 * control->window->iscale, control->rect.h - 2 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 1 * control->window->iscale, control->rect.y + 1 * control->window->iscale, control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); - + X11_XSetForeground(control->window->display, control->window->ctx, (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_HOVER) ? control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 2 * control->window->iscale, control->rect.y + 2 * control->window->iscale, control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); } } - + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); #ifdef X_HAVE_UTF8_STRING if (control->window->utf8) { @@ -1571,7 +1567,7 @@ static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { static void X11Toolkit_OnButtonControlStateChange(SDL_ToolkitControlX11 *control) { SDL_ToolkitButtonControlX11 *button_control; - + button_control = (SDL_ToolkitButtonControlX11 *)control; if (button_control->cb && control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED) { button_control->cb(control, button_control->cb_data); @@ -1586,7 +1582,7 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *wind SDL_ToolkitButtonControlX11 *control; SDL_ToolkitControlX11 *base_control; int text_d; - + control = (SDL_ToolkitButtonControlX11 *)SDL_malloc(sizeof(SDL_ToolkitButtonControlX11)); base_control = (SDL_ToolkitControlX11 *)control; if (!control) { @@ -1626,7 +1622,7 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *wind void X11Toolkit_RegisterCallbackForButtonControl(SDL_ToolkitControlX11 *control, void *data, void (*cb)(struct SDL_ToolkitControlX11 *, void *)) { SDL_ToolkitButtonControlX11 *button_control; - + button_control = (SDL_ToolkitButtonControlX11 *)control; button_control->cb_data = data; button_control->cb = cb; @@ -1634,18 +1630,18 @@ void X11Toolkit_RegisterCallbackForButtonControl(SDL_ToolkitControlX11 *control, const SDL_MessageBoxButtonData *X11Toolkit_GetButtonControlData(SDL_ToolkitControlX11 *control) { SDL_ToolkitButtonControlX11 *button_control; - + button_control = (SDL_ToolkitButtonControlX11 *)control; return button_control->data; } void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { int i; - + if (!data) { return; } - + #if SDL_GRAB if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { X11_XUngrabPointer(data->display, CurrentTime); @@ -1664,15 +1660,15 @@ void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { if (data->dyn_controls) { SDL_free(data->dyn_controls); } - + if (data->popup_windows) { SDL_ListClear(&data->popup_windows); } - + if (data->pixmap) { X11_XFreePixmap(data->display, data->drawable); } - + #ifdef X_HAVE_UTF8_STRING if (data->font_set) { X11_XFreeFontSet(data->display, data->font_set); @@ -1694,7 +1690,7 @@ void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { if (data->xsettings) { xsettings_client_destroy(data->xsettings); } - + X11_XFreeGC(data->display, data->ctx); if (data->display) { @@ -1703,13 +1699,13 @@ void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { X11_XDestroyWindow(data->display, data->window); data->window = None; } - + if (data->display_close) { X11_XCloseDisplay(data->display); } data->display = NULL; } - + #if SDL_SET_LOCALE if (data->origlocale && (data->mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD)) { (void)setlocale(LC_ALL, data->origlocale); @@ -1734,7 +1730,7 @@ static int X11Toolkit_CountLinesOfText(const char *text) static void X11Toolkit_DrawLabelControl(SDL_ToolkitControlX11 *control) { SDL_ToolkitLabelControlX11 *label_control; int i; - + label_control = (SDL_ToolkitLabelControlX11 *)control; X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); for (i = 0; i < label_control->sz; i++) { @@ -1755,7 +1751,7 @@ static void X11Toolkit_DrawLabelControl(SDL_ToolkitControlX11 *control) { static void X11Toolkit_DestroyLabelControl(SDL_ToolkitControlX11 *control) { SDL_ToolkitLabelControlX11 *label_control; - + label_control = (SDL_ToolkitLabelControlX11 *)control; SDL_free(label_control->lines); SDL_free(label_control->szs); @@ -1771,7 +1767,7 @@ static void X11Toolkit_CalculateLabelControl(SDL_ToolkitControlX11 *base_control int w; int h; int i; - + control = (SDL_ToolkitLabelControlX11 *)base_control; for (i = 0; i < control->sz; i++) { X11Toolkit_GetTextWidthHeight(base_control->window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent); @@ -1792,10 +1788,10 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo int ascent; int descent; int i; - + if (!utf8) { return NULL; - } + } control = (SDL_ToolkitLabelControlX11 *)SDL_malloc(sizeof(SDL_ToolkitLabelControlX11)); base_control = (SDL_ToolkitControlX11 *)control; if (!control) { @@ -1810,11 +1806,11 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo base_control->func_on_scale_change = NULL; base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; base_control->selected = false; - base_control->dynamic = false; + base_control->dynamic = false; base_control->rect.w = 0; base_control->rect.h = 0; - base_control->is_default_enter = false; - base_control->is_default_esc = false; + base_control->is_default_enter = false; + base_control->is_default_esc = false; control->sz = X11Toolkit_CountLinesOfText(utf8); control->lines = (char **)SDL_malloc(sizeof(char *) * control->sz); control->y = (int *)SDL_calloc(control->sz, sizeof(int)); @@ -1824,7 +1820,7 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo const int length = lf ? (lf - utf8) : SDL_strlen(utf8); int w; int h; - + control->lines[i] = utf8; X11Toolkit_GetTextWidthHeight(window, utf8, length, &w, &h, &ascent, &descent); base_control->rect.w = SDL_max(base_control->rect.w, w); @@ -1847,14 +1843,14 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo break; } } - - X11Toolkit_AddControlToWindow(window, base_control); + + X11Toolkit_AddControlToWindow(window, base_control); return base_control; } int X11Toolkit_GetIconControlCharY(SDL_ToolkitControlX11 *control) { SDL_ToolkitIconControlX11 *icon_control; - + icon_control = (SDL_ToolkitIconControlX11 *)control; return icon_control->icon_char_y - icon_control->icon_char_a; } diff --git a/src/video/x11/SDL_x11toolkit.h b/src/video/x11/SDL_x11toolkit.h index d635512cd4..5e0170c1da 100644 --- a/src/video/x11/SDL_x11toolkit.h +++ b/src/video/x11/SDL_x11toolkit.h @@ -49,34 +49,34 @@ typedef enum SDL_ToolkitChildModeX11 typedef struct SDL_ToolkitWindowX11 { - /* Locale */ + /* Locale */ char *origlocale; - - /* Mode */ + + /* Mode */ SDL_ToolkitWindowModeX11 mode; - + /* Display */ Display *display; int screen; bool display_close; - + /* Parent */ SDL_Window *parent; struct SDL_ToolkitWindowX11 *tk_parent; - - /* Window */ + + /* Window */ Window window; Drawable drawable; - + /* Visuals and drawing */ Visual *visual; XVisualInfo vi; Colormap cmap; - GC ctx; - int depth; + GC ctx; + int depth; bool pixmap; - /* X11 extensions */ + /* X11 extensions */ #ifdef SDL_VIDEO_DRIVER_X11_XDBE XdbeBackBuffer buf; bool xdbe; // Whether Xdbe is present or not @@ -86,37 +86,37 @@ typedef struct SDL_ToolkitWindowX11 #endif bool utf8; - /* Atoms */ + /* Atoms */ Atom wm_protocols; Atom wm_delete_message; - + /* Window and pixmap sizes */ int window_width; // Window width. int window_height; // Window height. int pixmap_width; int pixmap_height; - int window_x; + int window_x; int window_y; - + /* XSettings and scaling */ XSettingsClient *xsettings; bool xsettings_first_time; int iscale; float scale; - + /* Font */ XFontSet font_set; // for UTF-8 systems XFontStruct *font_struct; // Latin1 (ASCII) fallback. - /* Control colors */ - const SDL_MessageBoxColor *color_hints; + /* Control colors */ + const SDL_MessageBoxColor *color_hints; XColor xcolor[SDL_MESSAGEBOX_COLOR_COUNT]; XColor xcolor_bevel_l1; XColor xcolor_bevel_l2; XColor xcolor_bevel_d; XColor xcolor_pressed; - XColor xcolor_disabled_text; - + XColor xcolor_disabled_text; + /* Control list */ bool has_focus; struct SDL_ToolkitControlX11 *focused_control; @@ -126,12 +126,12 @@ typedef struct SDL_ToolkitWindowX11 struct SDL_ToolkitControlX11 **dyn_controls; size_t dyn_controls_sz; - /* User callbacks */ + /* User callbacks */ void *cb_data; void (*cb_on_scale_change)(struct SDL_ToolkitWindowX11 *, void *); - + /* Popup windows */ - SDL_ListNode *popup_windows; + SDL_ListNode *popup_windows; /* Event loop */ XEvent *e; @@ -143,7 +143,7 @@ typedef struct SDL_ToolkitWindowX11 float ev_scale; float ev_iscale; bool draw; - bool close; + bool close; long event_mask; } SDL_ToolkitWindowX11; @@ -165,10 +165,10 @@ typedef struct SDL_ToolkitControlX11 bool dynamic; bool is_default_enter; bool is_default_esc; - + /* User data */ void *data; - + /* Virtual functions */ void (*func_draw)(struct SDL_ToolkitControlX11 *); void (*func_calc_size)(struct SDL_ToolkitControlX11 *); @@ -183,10 +183,10 @@ typedef struct SDL_ToolkitMenuItemX11 bool checkbox; bool checked; bool disabled; - void *cb_data; + void *cb_data; void (*cb)(struct SDL_ToolkitMenuItemX11 *, void *); SDL_ListNode *sub_menu; - + /* Internal use */ SDL_Rect utf8_rect; SDL_Rect hover_rect; From fbbc29159a25f26d789694a74c2d1d3216dfb49d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 2 Sep 2025 20:11:00 -0700 Subject: [PATCH 108/134] Leave letterbox borders set to the frame clear color Fixes https://github.com/libsdl-org/sdl2-compat/issues/483 --- src/render/SDL_render.c | 90 ----------------------------------------- 1 file changed, 90 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index ee18d6769d..303d883b58 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -2797,94 +2797,6 @@ bool SDL_GetRenderLogicalPresentationRect(SDL_Renderer *renderer, SDL_FRect *rec return true; } -static void SDL_RenderLogicalBorders(SDL_Renderer *renderer, const SDL_FRect *dst) -{ - const SDL_RenderViewState *view = renderer->view; - - if (dst->x > 0.0f || dst->y > 0.0f) { - SDL_BlendMode saved_blend_mode = renderer->blendMode; - SDL_FColor saved_color = renderer->color; - - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); - SDL_SetRenderDrawColorFloat(renderer, 0.0f, 0.0f, 0.0f, 1.0f); - - if (dst->x > 0.0f) { - SDL_FRect rect; - - rect.x = 0.0f; - rect.y = 0.0f; - rect.w = dst->x; - rect.h = (float)view->pixel_h; - SDL_RenderFillRect(renderer, &rect); - - rect.x = dst->x + dst->w; - rect.w = (float)view->pixel_w - rect.x; - SDL_RenderFillRect(renderer, &rect); - } - - if (dst->y > 0.0f) { - SDL_FRect rect; - - rect.x = 0.0f; - rect.y = 0.0f; - rect.w = (float)view->pixel_w; - rect.h = dst->y; - SDL_RenderFillRect(renderer, &rect); - - rect.y = dst->y + dst->h; - rect.h = (float)view->pixel_h - rect.y; - SDL_RenderFillRect(renderer, &rect); - } - - SDL_SetRenderDrawBlendMode(renderer, saved_blend_mode); - SDL_SetRenderDrawColorFloat(renderer, saved_color.r, saved_color.g, saved_color.b, saved_color.a); - } -} - -static void SDL_RenderLogicalPresentation(SDL_Renderer *renderer) -{ - SDL_assert(renderer->view == &renderer->main_view); - - SDL_RenderViewState *view = &renderer->main_view; - const SDL_RendererLogicalPresentation mode = view->logical_presentation_mode; - if (mode == SDL_LOGICAL_PRESENTATION_LETTERBOX) { - // save off some state we're going to trample. - const int logical_w = view->logical_w; - const int logical_h = view->logical_h; - const float scale_x = view->scale.x; - const float scale_y = view->scale.y; - const bool clipping_enabled = view->clipping_enabled; - SDL_Rect orig_viewport, orig_cliprect; - const SDL_FRect logical_dst_rect = view->logical_dst_rect; - - SDL_copyp(&orig_viewport, &view->viewport); - if (clipping_enabled) { - SDL_copyp(&orig_cliprect, &view->clip_rect); - } - - // trample some state. - SDL_SetRenderLogicalPresentation(renderer, logical_w, logical_h, SDL_LOGICAL_PRESENTATION_DISABLED); - SDL_SetRenderViewport(renderer, NULL); - if (clipping_enabled) { - SDL_SetRenderClipRect(renderer, NULL); - } - SDL_SetRenderScale(renderer, 1.0f, 1.0f); - - // draw the borders. - SDL_RenderLogicalBorders(renderer, &logical_dst_rect); - - // now set everything back. - view->logical_presentation_mode = mode; - SDL_SetRenderViewport(renderer, &orig_viewport); - if (clipping_enabled) { - SDL_SetRenderClipRect(renderer, &orig_cliprect); - } - SDL_SetRenderScale(renderer, scale_x, scale_y); - - SDL_SetRenderLogicalPresentation(renderer, logical_w, logical_h, mode); - } -} - static bool SDL_RenderVectorFromWindow(SDL_Renderer *renderer, float window_dx, float window_dy, float *dx, float *dy) { // Convert from window coordinates to pixels within the window @@ -5401,8 +5313,6 @@ bool SDL_RenderPresent(SDL_Renderer *renderer) return SDL_SetError("You can't present on a render target"); } - SDL_RenderLogicalPresentation(renderer); - if (renderer->transparent_window) { SDL_RenderApplyWindowShape(renderer); } From 437d78499cc2ce3f6f49a4efc9461ef1ada7a7b6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 2 Sep 2025 21:18:55 -0700 Subject: [PATCH 109/134] Don't set SDL_SURFACE_LOCK_NEEDED until a surface is RLE encoded Reference https://github.com/libsdl-org/sdl2-compat/issues/476 --- src/video/SDL_RLEaccel.c | 6 ++++-- src/video/SDL_blit.c | 2 +- src/video/SDL_surface.c | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/video/SDL_RLEaccel.c b/src/video/SDL_RLEaccel.c index 890b8250aa..2ca6d7e0db 100644 --- a/src/video/SDL_RLEaccel.c +++ b/src/video/SDL_RLEaccel.c @@ -1434,6 +1434,7 @@ bool SDL_RLESurface(SDL_Surface *surface) // The surface is now accelerated surface->internal_flags |= SDL_INTERNAL_SURFACE_RLEACCEL; + SDL_UpdateSurfaceLockFlag(surface); return true; } @@ -1565,11 +1566,12 @@ void SDL_UnRLESurface(SDL_Surface *surface, bool recode) } } } - surface->map.info.flags &= - ~(SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY); + surface->map.info.flags &= ~(SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY); SDL_free(surface->map.data); surface->map.data = NULL; + + SDL_UpdateSurfaceLockFlag(surface); } } diff --git a/src/video/SDL_blit.c b/src/video/SDL_blit.c index 5ffd1814e6..860a2a9bc2 100644 --- a/src/video/SDL_blit.c +++ b/src/video/SDL_blit.c @@ -191,7 +191,7 @@ bool SDL_CalculateBlit(SDL_Surface *surface, SDL_Surface *dst) #ifdef SDL_HAVE_RLE // Clean everything out to start - if (surface->flags & SDL_INTERNAL_SURFACE_RLEACCEL) { + if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { SDL_UnRLESurface(surface, true); } #endif diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index dc897fbab9..7fe2542f76 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -49,7 +49,7 @@ bool SDL_SurfaceValid(SDL_Surface *surface) void SDL_UpdateSurfaceLockFlag(SDL_Surface *surface) { - if (SDL_SurfaceHasRLE(surface)) { + if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { surface->flags |= SDL_SURFACE_LOCK_NEEDED; } else { surface->flags &= ~SDL_SURFACE_LOCK_NEEDED; @@ -611,7 +611,6 @@ bool SDL_SetSurfaceRLE(SDL_Surface *surface, bool enabled) if (surface->map.info.flags != flags) { SDL_InvalidateMap(&surface->map); } - SDL_UpdateSurfaceLockFlag(surface); return true; } @@ -1760,6 +1759,7 @@ bool SDL_LockSurface(SDL_Surface *surface) if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { SDL_UnRLESurface(surface, true); surface->internal_flags |= SDL_INTERNAL_SURFACE_RLEACCEL; // save accel'd state + SDL_UpdateSurfaceLockFlag(surface); } #endif } From b084999d40855c8e763dde2ddbf4e6018fb4d35e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 3 Sep 2025 10:13:05 +0200 Subject: [PATCH 110/134] testautomation_events: initialize variable to prevent undefined read --- test/testautomation_events.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/testautomation_events.c b/test/testautomation_events.c index ed4e684cff..ccf417c381 100644 --- a/test/testautomation_events.c +++ b/test/testautomation_events.c @@ -230,6 +230,7 @@ static int SDLCALL IncrementCounterThread(void *userdata) SDL_Event event; SDL_assert(!SDL_IsMainThread()); + SDL_zero(event); if (data->delay > 0) { SDL_Delay(data->delay); From 51052245d12ccb89a61bc8b295b4d2bdfdb04446 Mon Sep 17 00:00:00 2001 From: eafton Date: Wed, 3 Sep 2025 16:57:16 +0300 Subject: [PATCH 111/134] X11TK: Fix locale bug for non-dialog windows --- src/video/x11/SDL_x11toolkit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c index 8550360764..71d1b19def 100644 --- a/src/video/x11/SDL_x11toolkit.c +++ b/src/video/x11/SDL_x11toolkit.c @@ -444,7 +444,7 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool window->tk_parent = tkparent; #if SDL_SET_LOCALE - if (mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { + if (mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG) { window->origlocale = setlocale(LC_ALL, NULL); if (window->origlocale) { window->origlocale = SDL_strdup(window->origlocale); @@ -1707,7 +1707,7 @@ void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { } #if SDL_SET_LOCALE - if (data->origlocale && (data->mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD)) { + if (data->origlocale && (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG)) { (void)setlocale(LC_ALL, data->origlocale); SDL_free(data->origlocale); } From 4441d8b0ad9666b8dc6e51f176551d862f8ff85a Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 3 Sep 2025 17:04:50 +0100 Subject: [PATCH 112/134] x11: Don't include XDestroyImage in the table of exported functions When linking directly to libX11 as a hard dependency, we assign the addresses of functions like XDestroyImage to function pointers like X11_XDestroyImage. However, by default Xutils.h doesn't declare XDestroyImage as a function: it only provides a macro which looks into the XImage struct and calls a function pointer directly, similar to a virtual method. SDL_x11framebuffer.c was already relying on being able to call the macro without explicitly linking to libX11, so do the same here. Fixes: d14cbd7b "Introduce X11 toolkit and make message dialogs use it" Signed-off-by: Simon McVittie --- src/video/x11/SDL_x11sym.h | 1 - src/video/x11/SDL_x11toolkit.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index cacec1e654..07f2b4539f 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -105,7 +105,6 @@ SDL_X11_SYM(Status,XInitThreads,(void)) SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b)) SDL_X11_SYM(int,XPending,(Display* a)) SDL_X11_SYM(XImage*,XGetImage,(Display* a,Drawable b,int c, int d,unsigned int e,unsigned int f,unsigned long g,int h)) -SDL_X11_SYM(void,XDestroyImage,(XImage *a)) SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j)) SDL_X11_SYM(int,XQueryKeymap,(Display* a,char b[32])) SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i)) diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c index 71d1b19def..1e1d50e663 100644 --- a/src/video/x11/SDL_x11toolkit.c +++ b/src/video/x11/SDL_x11toolkit.c @@ -981,10 +981,10 @@ static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { put_image = X11_XCreateImage(data->display, data->visual, data->vi.depth, ZPixmap, 0, put_surface->pixels, data->window_width, data->window_height, 32, put_surface->pitch); X11_XPutImage(data->display, data->window, data->ctx, put_image, 0, 0, 0, 0, data->window_width, data->window_height); - X11_XDestroyImage(pixmap_image); + XDestroyImage(pixmap_image); /* Needed because XDestroyImage results in a double-free otherwise */ put_image->data = NULL; - X11_XDestroyImage(put_image); + XDestroyImage(put_image); SDL_DestroySurface(pixmap_surface); SDL_DestroySurface(put_surface); } From 9abeeebad5b72a8f2cf94a02c01904ddbd68234a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 3 Sep 2025 11:23:45 -0700 Subject: [PATCH 113/134] Fixed warning: no previous prototype for function --- src/audio/SDL_audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 031d6b5abb..cc20b47b12 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -371,7 +371,7 @@ bool SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid) return (devid & (1 << 1)) != 0; } -bool SDL_IsAudioDeviceLogical(SDL_AudioDeviceID devid) +static bool SDL_IsAudioDeviceLogical(SDL_AudioDeviceID devid) { // bit #1 of devid is set for physical devices and unset for logical. return (devid & (1 << 1)) == 0; @@ -383,7 +383,7 @@ bool SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid) return (devid & (1 << 0)) != 0; } -bool SDL_IsAudioDeviceRecording(SDL_AudioDeviceID devid) +static bool SDL_IsAudioDeviceRecording(SDL_AudioDeviceID devid) { // bit #0 of devid is set for playback devices and unset for recording. return (devid & (1 << 0)) == 0; From 42f571ea4bbd90cd962a18f85a3fbc254e6299bb Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Wed, 3 Sep 2025 22:48:18 +0100 Subject: [PATCH 114/134] Remove unused PSP source files --- src/video/psp/SDL_pspmouse.c | 37 ---------------------------------- src/video/psp/SDL_pspmouse_c.h | 24 ---------------------- 2 files changed, 61 deletions(-) delete mode 100644 src/video/psp/SDL_pspmouse.c delete mode 100644 src/video/psp/SDL_pspmouse_c.h diff --git a/src/video/psp/SDL_pspmouse.c b/src/video/psp/SDL_pspmouse.c deleted file mode 100644 index e63be96c2a..0000000000 --- a/src/video/psp/SDL_pspmouse.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_VIDEO_DRIVER_PSP - -#include - -#include "../../events/SDL_events_c.h" - -#include "SDL_pspmouse_c.h" - -// The implementation dependent data for the window manager cursor -struct WMcursor -{ - int unused; -}; - -#endif // SDL_VIDEO_DRIVER_PSP diff --git a/src/video/psp/SDL_pspmouse_c.h b/src/video/psp/SDL_pspmouse_c.h deleted file mode 100644 index 95d1ff4464..0000000000 --- a/src/video/psp/SDL_pspmouse_c.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - 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_pspvideo.h" - -// Functions to be exported From b6a55c2856d3a65f18554d0e1e3b0c0f3e00c0cd Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 3 Sep 2025 15:36:58 -0700 Subject: [PATCH 115/134] Wait a bit for initialization to complete before requesting calibration data Fixes https://github.com/libsdl-org/SDL/issues/13856 --- src/joystick/hidapi/SDL_hidapi_switch2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/joystick/hidapi/SDL_hidapi_switch2.c b/src/joystick/hidapi/SDL_hidapi_switch2.c index 13367d4438..7b1978e79b 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch2.c +++ b/src/joystick/hidapi/SDL_hidapi_switch2.c @@ -349,10 +349,12 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device) } RecvBulkData(ctx, calibration_data, 0x40); + SDL_Delay(1); + flash_read_command[12] = 0x80; res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command)); if (res < 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res); + SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't request calibration data: %d", res); } else { res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data)); if (res < 0) { @@ -366,7 +368,7 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device) flash_read_command[12] = 0xC0; res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command)); if (res < 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res); + SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't request calibration data: %d", res); } else { res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data)); if (res < 0) { From 20c00babbc32e4ea86f6cfb44c13f9f74917ec4e Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Wed, 3 Sep 2025 19:15:48 +0100 Subject: [PATCH 116/134] Create an application bundle for examples on Mac OS X --- examples/CMakeLists.txt | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 1c5a40e69e..2e7024809b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -64,14 +64,10 @@ macro(add_sdl_example_executable TARGET) if(NOT AST_SOURCES) message(FATAL_ERROR "add_sdl_example_executable needs at least one source") endif() - set(EXTRA_SOURCES "") - if(AST_DATAFILES) - list(APPEND EXTRA_SOURCES ${DATAFILES}) - endif() if(ANDROID) - add_library(${TARGET} SHARED ${AST_SOURCES} ${EXTRA_SOURCES}) + add_library(${TARGET} SHARED ${AST_SOURCES} ${AST_DATAFILES}) else() - add_executable(${TARGET} ${AST_SOURCES} ${EXTRA_SOURCES}) + add_executable(${TARGET} ${AST_SOURCES} ${AST_DATAFILES}) endif() SDL_AddCommonCompilerFlags(${TARGET}) target_include_directories(${TARGET} PRIVATE "${SDL3_SOURCE_DIR}/src/video/khronos") @@ -85,13 +81,9 @@ macro(add_sdl_example_executable TARGET) COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${AST_DATAFILES} $/sdl-${TARGET} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) - else() + elseif(NOT APPLE) add_dependencies(${TARGET} copy-sdl-example-resources) endif() - if(APPLE) - # Make sure resource files get installed into macOS/iOS .app bundles. - set_target_properties(${TARGET} PROPERTIES RESOURCE "${AST_DATAFILES}") - endif() if(EMSCRIPTEN) foreach(res IN LISTS AST_DATAFILES) get_filename_component(res_name "${res}" NAME) @@ -101,7 +93,18 @@ macro(add_sdl_example_executable TARGET) set_property(TARGET ${TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES "$/$$/>") endif() - if(WINDOWS) + if(APPLE) + # Set Apple App ID / Bundle ID. This is needed to launch apps on some Apple + # platforms (iOS, for example). + set_target_properties(${TARGET} PROPERTIES + RESOURCES "${AST_DATAFILES}" + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "org.libsdl.${TARGET}" + MACOSX_BUNDLE_BUNDLE_VERSION "${SDL3_VERSION}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${SDL3_VERSION}" + ) + set_property(SOURCE ${AST_DATAFILES} PROPERTY MACOSX_PACKAGE_LOCATION "Resources") + elseif(WINDOWS) # CET support was added in VS 16.7 if(MSVC_VERSION GREATER 1926 AND CMAKE_GENERATOR_PLATFORM MATCHES "Win32|x64") set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -CETCOMPAT") @@ -217,18 +220,6 @@ if(RISCOS) endforeach() endif() -# Set Apple App ID / Bundle ID. This is needed to launch apps on some Apple -# platforms (iOS, for example). -if(APPLE) - foreach(CURRENT_TARGET ${SDL_EXAMPLE_EXECUTABLES}) - set_target_properties("${CURRENT_TARGET}" PROPERTIES - MACOSX_BUNDLE_GUI_IDENTIFIER "org.libsdl.${CURRENT_TARGET}" - MACOSX_BUNDLE_BUNDLE_VERSION "${SDL3_VERSION}" - MACOSX_BUNDLE_SHORT_VERSION_STRING "${SDL3_VERSION}" - ) - endforeach() -endif() - if(SDL_INSTALL_EXAMPLES) if(RISCOS) install( From 54745c5cf4844f5680fccfcd887c7dc6d0611eb1 Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Wed, 3 Sep 2025 20:16:53 +0100 Subject: [PATCH 117/134] Only bundle the required resources with examples on the 3DS --- examples/CMakeLists.txt | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2e7024809b..8703e610f0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -81,7 +81,7 @@ macro(add_sdl_example_executable TARGET) COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${AST_DATAFILES} $/sdl-${TARGET} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) - elseif(NOT APPLE) + elseif(NOT APPLE AND NOT N3DS) add_dependencies(${TARGET} copy-sdl-example-resources) endif() if(EMSCRIPTEN) @@ -114,6 +114,21 @@ macro(add_sdl_example_executable TARGET) elseif(EMSCRIPTEN) set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html") target_link_options(${TARGET} PRIVATE -sALLOW_MEMORY_GROWTH=1) + elseif(N3DS) + set(ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/romfs/${TARGET}") + file(MAKE_DIRECTORY "${ROMFS_DIR}") + file(COPY ${AST_DATAFILES} DESTINATION "${ROMFS_DIR}") + ctr_generate_smdh("${TARGET}.smdh" + NAME "SDL-${TARGET}" + DESCRIPTION "SDL3 example application" + AUTHOR "SDL3 Contributors" + ICON "${CMAKE_CURRENT_SOURCE_DIR}/../test/n3ds/logo48x48.png" + ) + ctr_create_3dsx( + ${TARGET} + ROMFS "${ROMFS_DIR}" + SMDH "${TARGET}.smdh" + ) elseif(NGAGE) string(MD5 TARGET_MD5 "${TARGET}") string(SUBSTRING "${TARGET_MD5}" 0 8 TARGET_MD5_8) @@ -184,28 +199,6 @@ if(PSP) endforeach() endif() -if(N3DS) - set(ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/romfs") - file(MAKE_DIRECTORY "${ROMFS_DIR}") - file(COPY ${RESOURCE_FILES} DESTINATION "${ROMFS_DIR}") - - foreach(APP ${SDL_EXAMPLE_EXECUTABLES}) - get_target_property(TARGET_BINARY_DIR ${APP} BINARY_DIR) - set(SMDH_FILE "${TARGET_BINARY_DIR}/${APP}.smdh") - ctr_generate_smdh("${SMDH_FILE}" - NAME "SDL-${APP}" - DESCRIPTION "SDL3 Test suite" - AUTHOR "SDL3 Contributors" - ICON "${CMAKE_CURRENT_SOURCE_DIR}/../test/n3ds/logo48x48.png" - ) - ctr_create_3dsx( - ${APP} - ROMFS "${ROMFS_DIR}" - SMDH "${SMDH_FILE}" - ) - endforeach() -endif() - if(RISCOS) set(SDL_EXAMPLE_EXECUTABLES_AIF) foreach(APP ${SDL_EXAMPLE_EXECUTABLES}) From 41452ef4bf7b0f748346b6435d3d5391380b615f Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Wed, 3 Sep 2025 21:25:12 +0100 Subject: [PATCH 118/134] Remove unused OpenGL references in the examples CMakeLists --- examples/CMakeLists.txt | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8703e610f0..fa4ad4dc29 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -16,13 +16,6 @@ else() endif() set(HAVE_EXAMPLES_LINK_SHARED "${SDL_EXAMPLES_LINK_SHARED}" PARENT_SCOPE) -# CMake incorrectly detects opengl32.lib being present on MSVC ARM64 -if(NOT (MSVC AND SDL_CPU_ARM64)) - # Prefer GLVND, if present - set(OpenGL_GL_PREFERENCE GLVND) - find_package(OpenGL) -endif() - set(SDL_EXAMPLE_EXECUTABLES) if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) @@ -70,7 +63,6 @@ macro(add_sdl_example_executable TARGET) add_executable(${TARGET} ${AST_SOURCES} ${AST_DATAFILES}) endif() SDL_AddCommonCompilerFlags(${TARGET}) - target_include_directories(${TARGET} PRIVATE "${SDL3_SOURCE_DIR}/src/video/khronos") target_link_libraries(${TARGET} PRIVATE SDL3::${sdl_name_component}) list(APPEND SDL_EXAMPLE_EXECUTABLES ${TARGET}) @@ -109,8 +101,6 @@ macro(add_sdl_example_executable TARGET) if(MSVC_VERSION GREATER 1926 AND CMAKE_GENERATOR_PLATFORM MATCHES "Win32|x64") set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -CETCOMPAT") endif() - elseif(PSP) - target_link_libraries(${TARGET} PRIVATE GL) elseif(EMSCRIPTEN) set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html") target_link_options(${TARGET} PRIVATE -sALLOW_MEMORY_GROWTH=1) @@ -134,13 +124,6 @@ macro(add_sdl_example_executable TARGET) string(SUBSTRING "${TARGET_MD5}" 0 8 TARGET_MD5_8) target_link_options(${TARGET} PRIVATE "SHELL:-s UID3=0x${TARGET_MD5_8}") endif() - - if(OPENGL_FOUND) - target_compile_definitions(${TARGET} PRIVATE HAVE_OPENGL) - endif() - - # FIXME: only add "${SDL3_BINARY_DIR}/include-config-$>" + include paths of external dependencies - target_include_directories(${TARGET} PRIVATE "$") endmacro() add_sdl_example_executable(renderer-clear SOURCES renderer/01-clear/clear.c) From 637a9b34abd7ba6612e888219bacefa1bfb1c01d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 3 Sep 2025 15:47:05 -0700 Subject: [PATCH 119/134] Added a comment explaining the initialization delay --- src/joystick/hidapi/SDL_hidapi_switch2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/joystick/hidapi/SDL_hidapi_switch2.c b/src/joystick/hidapi/SDL_hidapi_switch2.c index 7b1978e79b..d7c4e8d98a 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch2.c +++ b/src/joystick/hidapi/SDL_hidapi_switch2.c @@ -349,6 +349,7 @@ static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device) } RecvBulkData(ctx, calibration_data, 0x40); + // Wait for initialization to complete SDL_Delay(1); flash_read_command[12] = 0x80; From ea1a769322a28190423d74b7188161f4484a1a22 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 4 Sep 2025 06:04:01 -0700 Subject: [PATCH 120/134] Fixed the perspective matrix calculation (thanks @KonkolyTamas!) Fixes https://github.com/libsdl-org/SDL/issues/13867 --- test/gamepadutils.c | 2 +- test/testgles2.c | 2 +- test/testgpu_spinning_cube.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/gamepadutils.c b/test/gamepadutils.c index 21206b071f..7cc401134b 100644 --- a/test/gamepadutils.c +++ b/test/gamepadutils.c @@ -97,7 +97,7 @@ static SDL_FPoint ProjectVec3ToRect(const Vector3 *v, const SDL_FRect *rect) const float cameraZ = 4.0f; /* Camera is at(0, 0, +4), looking toward origin */ float aspect = rect->w / rect->h; - float fovScaleY = SDL_tanf((verticalFOV_deg * SDL_PI_F / 180.0f) * 0.5f); + float fovScaleY = SDL_tanf(((verticalFOV_deg / 180.0f) * SDL_PI_F) * 0.5f); float fovScaleX = fovScaleY * aspect; float relZ = cameraZ - v->z; diff --git a/test/testgles2.c b/test/testgles2.c index ba3e0ddcda..2f054fcaf6 100644 --- a/test/testgles2.c +++ b/test/testgles2.c @@ -179,7 +179,7 @@ perspective_matrix(float fovy, float aspect, float znear, float zfar, float *r) int i; float f; - f = 1.0f / SDL_tanf(fovy * 0.5f); + f = 1.0f / SDL_tanf((fovy / 180.0f) * SDL_PI_F * 0.5f); for (i = 0; i < 16; i++) { r[i] = 0.0; diff --git a/test/testgpu_spinning_cube.c b/test/testgpu_spinning_cube.c index 0eeb034fbb..7ec4aeec00 100644 --- a/test/testgpu_spinning_cube.c +++ b/test/testgpu_spinning_cube.c @@ -133,7 +133,7 @@ perspective_matrix(float fovy, float aspect, float znear, float zfar, float *r) int i; float f; - f = 1.0f/SDL_tanf(fovy * 0.5f); + f = 1.0f/SDL_tanf((fovy / 180.0f) * SDL_PI_F * 0.5f); for (i = 0; i < 16; i++) { r[i] = 0.0; From f2d2cd8152838ac007bad6882797486265cf1605 Mon Sep 17 00:00:00 2001 From: Qiang <89919117+larks-x@users.noreply.github.com> Date: Thu, 4 Sep 2025 22:41:45 +0800 Subject: [PATCH 121/134] fix: Blending rendering anomaly when using the software renderer with SDL_WINDOW_TRANSPARENT on Windows. (#13866) --- VisualC/SDL.sln | 11 + .../testsoftwaretransparent.vcxproj | 209 ++++++++++++++++++ src/video/windows/SDL_windowsframebuffer.c | 17 +- test/testsoftwaretransparent.c | 155 +++++++++++++ 4 files changed, 388 insertions(+), 4 deletions(-) create mode 100644 VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj create mode 100644 test/testsoftwaretransparent.c diff --git a/VisualC/SDL.sln b/VisualC/SDL.sln index b5cf786ae2..d6e894049e 100644 --- a/VisualC/SDL.sln +++ b/VisualC/SDL.sln @@ -131,6 +131,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01-joystick-polling", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "02-joystick-events", "examples\input\02-joystick-events\02-joystick-events.vcxproj", "{FCBDF2B2-1129-49AE-9406-3F219E65CA89}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsoftwaretransparent", "tests\testsoftwaretransparent\testsoftwaretransparent.vcxproj", "{D91C45E2-274E-4C0F-89C7-9986F9A7E85A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -579,6 +581,14 @@ Global {FCBDF2B2-1129-49AE-9406-3F219E65CA89}.Release|Win32.Build.0 = Release|Win32 {FCBDF2B2-1129-49AE-9406-3F219E65CA89}.Release|x64.ActiveCfg = Release|x64 {FCBDF2B2-1129-49AE-9406-3F219E65CA89}.Release|x64.Build.0 = Release|x64 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|Win32.ActiveCfg = Debug|Win32 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|Win32.Build.0 = Debug|Win32 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|x64.ActiveCfg = Debug|x64 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|x64.Build.0 = Debug|x64 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|Win32.ActiveCfg = Release|Win32 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|Win32.Build.0 = Release|Win32 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|x64.ActiveCfg = Release|x64 + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -644,6 +654,7 @@ Global {3DB9B219-769E-43AC-8B8B-319DB6045DCF} = {D1BF59F6-22DC-493B-BDEB-451A50DA793D} {B3852DB7-E925-4026-8B9D-D2272EFEFF3C} = {8DEAE483-FDE7-463F-9FD5-F597BBAED1F9} {FCBDF2B2-1129-49AE-9406-3F219E65CA89} = {8DEAE483-FDE7-463F-9FD5-F597BBAED1F9} + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C320C9F2-1A8F-41D7-B02B-6338F872BCAD} diff --git a/VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj b/VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj new file mode 100644 index 0000000000..1970a3b334 --- /dev/null +++ b/VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {D91C45E2-274E-4C0F-89C7-9986F9A7E85A} + testsoftwaretransparent + 10.0 + + + + Application + $(DefaultPlatformToolset) + + + Application + $(DefaultPlatformToolset) + + + Application + $(DefaultPlatformToolset) + + + Application + $(DefaultPlatformToolset) + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/testsoftwaretransparent.tlb + + + %(AdditionalOptions) /utf-8 + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Windows + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Release/testsoftwaretransparent.tlb + + + %(AdditionalOptions) /utf-8 + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Windows + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/testsoftwaretransparent.tlb + + + %(AdditionalOptions) /utf-8 + Disabled + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + OldStyle + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Debug/testsoftwaretransparent.tlb + + + %(AdditionalOptions) /utf-8 + Disabled + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + OldStyle + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + + + $(TreatWarningsAsError) + + + + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} + false + false + true + + + {da956fd3-e143-46f2-9fe5-c77bebc56b1a} + false + false + true + + + + + + + + + \ No newline at end of file diff --git a/src/video/windows/SDL_windowsframebuffer.c b/src/video/windows/SDL_windowsframebuffer.c index f2bbd590a9..d9af714bfc 100644 --- a/src/video/windows/SDL_windowsframebuffer.c +++ b/src/video/windows/SDL_windowsframebuffer.c @@ -59,6 +59,9 @@ bool WIN_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL GetDIBits(data->hdc, hbm, 0, 0, NULL, info, DIB_RGB_COLORS); DeleteObject(hbm); + // Check if a transparent channel is required + bool need_alpha = (window->flags & SDL_WINDOW_TRANSPARENT) != 0; + *format = SDL_PIXELFORMAT_UNKNOWN; if (info->bmiHeader.biCompression == BI_BITFIELDS) { int bpp; @@ -68,16 +71,22 @@ bool WIN_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL masks = (Uint32 *)((Uint8 *)info + info->bmiHeader.biSize); *format = SDL_GetPixelFormatForMasks(bpp, masks[0], masks[1], masks[2], 0); } - if (*format == SDL_PIXELFORMAT_UNKNOWN) { - // We'll use RGB format for now - *format = SDL_PIXELFORMAT_XRGB8888; + if (*format == SDL_PIXELFORMAT_UNKNOWN || need_alpha) { + // We'll use RGB or BGRA32 format for now + *format = need_alpha ? SDL_PIXELFORMAT_BGRA32 : SDL_PIXELFORMAT_XRGB8888; // Create a new one SDL_memset(info, 0, size); info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); info->bmiHeader.biPlanes = 1; info->bmiHeader.biBitCount = 32; - info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biCompression = need_alpha ? BI_BITFIELDS : BI_RGB; + + if (need_alpha) { + int tmpbpp; + Uint32 *bgr32masks = (Uint32 *)((Uint8 *)info + info->bmiHeader.biSize); + SDL_GetMasksForPixelFormat(SDL_PIXELFORMAT_BGRA32, &tmpbpp, &bgr32masks[0], &bgr32masks[1], &bgr32masks[2], &bgr32masks[3]); + } } // Fill in the size information diff --git a/test/testsoftwaretransparent.c b/test/testsoftwaretransparent.c new file mode 100644 index 0000000000..bd22d09f5a --- /dev/null +++ b/test/testsoftwaretransparent.c @@ -0,0 +1,155 @@ +/* + Copyright (C) 1997-2025 Sam Lantinga + + 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. +*/ +#include +#include + +#include "glass.h" + + +int main(int argc, char *argv[]) +{ + const char *image_file = NULL; + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + SDL_Texture *texture = NULL; + SDL_WindowFlags flags; + bool done = false; + SDL_Event event; + + int return_code = 1; + + + int windowWidth = 800; + int windowHeight = 600; + SDL_FRect destRect; + destRect.x = 0; + destRect.y = 0; + destRect.w = 100; + destRect.h = 100; + + SDL_FRect destRect2; + destRect2.x = 700; + destRect2.y = 0; + destRect2.w = 100; + destRect2.h = 100; + + SDL_FRect destRect3; + destRect3.x = 0; + destRect3.y = 500; + destRect3.w = 100; + destRect3.h = 100; + + SDL_FRect destRect4; + destRect4.x = 700; + destRect4.y = 500; + destRect4.w = 100; + destRect4.h = 100; + + SDL_FRect destRect5; + destRect5.x = 350; + destRect5.y = 250; + destRect5.w = 100; + destRect5.h = 100; + + + /* Create the window hidden */ + flags = (SDL_WINDOW_HIDDEN | SDL_WINDOW_TRANSPARENT); + //flags |= SDL_WINDOW_BORDERLESS; + + window = SDL_CreateWindow("SDL Software Renderer Transparent Test", windowWidth, windowHeight, flags); + if (!window) { + SDL_Log("Couldn't create transparent window: %s", SDL_GetError()); + goto quit; + } + + /* Create a software renderer and set the blend mode */ + renderer = SDL_CreateRenderer(window, SDL_SOFTWARE_RENDERER); + if (!renderer) { + SDL_Log("Couldn't create renderer: %s", SDL_GetError()); + goto quit; + } + + if (!SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND)) { + SDL_Log("Couldn't set renderer blend mode: %s\n", SDL_GetError()); + return false; + } + + /* Create texture and set the blend mode */ + // texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); + //texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); + // texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); + if (texture == NULL) { + SDL_Log("Couldn't create texture: %s\n", SDL_GetError()); + return false; + } + + if (!SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND_PREMULTIPLIED)) { + SDL_Log("Couldn't set texture blend mode: %s\n", SDL_GetError()); + return false; + } + + /* Show */ + SDL_ShowWindow(window); + + /* We're ready to go! */ + while (!done) { + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_KEY_DOWN: + if (event.key.key == SDLK_ESCAPE) { + done = true; + } + break; + case SDL_EVENT_QUIT: + done = true; + break; + default: + break; + } + } + + + /* Draw opaque red squares at the four corners of the form, and draw a red square with an alpha value of 180 in the center of the form */ + + SDL_SetRenderTarget(renderer, texture); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); + SDL_RenderFillRect(renderer, &destRect); + SDL_RenderFillRect(renderer, &destRect2); + SDL_RenderFillRect(renderer, &destRect3); + SDL_RenderFillRect(renderer, &destRect4); + SDL_SetRenderDrawColor(renderer, 255, 0, 0, 180); + SDL_RenderFillRect(renderer, &destRect5); + + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_RenderTexture(renderer, texture, NULL, NULL); + + /* Show everything on the screen and wait a bit */ + SDL_RenderPresent(renderer); + SDL_Delay(100); + } + + /* Success! */ + return_code = 0; + +quit: + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return return_code; +} From 9e3d5969e628427afd9362e68769a328104c0b0f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 4 Sep 2025 09:24:22 -0700 Subject: [PATCH 122/134] Fixed testsoftwaretransparent on platforms using a renderer for window surfaces --- src/video/SDL_video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index cd906e2707..81e13446e1 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -448,6 +448,7 @@ static bool SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, // codechecker_false_positive [Malloc] Static analyzer doesn't realize allocated `data` is saved to SDL_PROP_WINDOW_TEXTUREDATA_POINTER and not leaked here. return false; // NOLINT(clang-analyzer-unix.Malloc) } + SDL_SetTextureBlendMode(data->texture, SDL_BLENDMODE_NONE); // Create framebuffer data data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format); From f48c20f48150dc641f30dfd9ad504caf88f1fab0 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 4 Sep 2025 09:25:05 -0700 Subject: [PATCH 123/134] Added testsoftwaretransparent to the CMake build system --- test/CMakeLists.txt | 1 + test/testsoftwaretransparent.c | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 40156a6ef9..9a985971a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -394,6 +394,7 @@ if(EMSCRIPTEN) target_link_options(testshader PRIVATE "-sLEGACY_GL_EMULATION") endif() add_sdl_test_executable(testshape NEEDS_RESOURCES SOURCES testshape.c ${glass_bmp_header} DEPENDS generate-glass_bmp_header) +add_sdl_test_executable(testsoftwaretransparent SOURCES testsoftwaretransparent.c) add_sdl_test_executable(testsprite MAIN_CALLBACKS NEEDS_RESOURCES TESTUTILS SOURCES testsprite.c) add_sdl_test_executable(testspriteminimal SOURCES testspriteminimal.c ${icon_bmp_header} DEPENDS generate-icon_bmp_header) add_sdl_test_executable(testspritesurface SOURCES testspritesurface.c ${icon_bmp_header} DEPENDS generate-icon_bmp_header) diff --git a/test/testsoftwaretransparent.c b/test/testsoftwaretransparent.c index bd22d09f5a..cc039e4e1d 100644 --- a/test/testsoftwaretransparent.c +++ b/test/testsoftwaretransparent.c @@ -17,7 +17,6 @@ int main(int argc, char *argv[]) { - const char *image_file = NULL; SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; SDL_Texture *texture = NULL; @@ -63,7 +62,7 @@ int main(int argc, char *argv[]) /* Create the window hidden */ flags = (SDL_WINDOW_HIDDEN | SDL_WINDOW_TRANSPARENT); - //flags |= SDL_WINDOW_BORDERLESS; + /*flags |= SDL_WINDOW_BORDERLESS;*/ window = SDL_CreateWindow("SDL Software Renderer Transparent Test", windowWidth, windowHeight, flags); if (!window) { @@ -84,9 +83,9 @@ int main(int argc, char *argv[]) } /* Create texture and set the blend mode */ - // texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); - //texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); - // texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); + /* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); */ + /* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); */ + /* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); */ texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); if (texture == NULL) { SDL_Log("Couldn't create texture: %s\n", SDL_GetError()); From a9b5a1e785329f9b9c95c32608954c20ac44ce8d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 4 Sep 2025 10:15:08 -0700 Subject: [PATCH 124/134] Simplified the transparent software renderer test --- test/testsoftwaretransparent.c | 130 +++++++++++---------------------- 1 file changed, 43 insertions(+), 87 deletions(-) diff --git a/test/testsoftwaretransparent.c b/test/testsoftwaretransparent.c index cc039e4e1d..90b78a61d4 100644 --- a/test/testsoftwaretransparent.c +++ b/test/testsoftwaretransparent.c @@ -12,93 +12,66 @@ #include #include -#include "glass.h" +#define SQUARE_SIZE 100.0f +/* Draw opaque red squares at the four corners of the form, and draw a red square with an alpha value of 180 in the center of the form */ +static void draw(SDL_Renderer *renderer) +{ + SDL_FRect rect = { 0.0f, 0.0f, SQUARE_SIZE, SQUARE_SIZE }; + int w, h; + + SDL_GetCurrentRenderOutputSize(renderer, &w, &h); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + if (w >= 3 * SQUARE_SIZE && h >= 3 * SQUARE_SIZE) { + SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); + + rect.x = 0.0f; + rect.y = 0.0f; + SDL_RenderFillRect(renderer, &rect); + + rect.y = h - SQUARE_SIZE; + SDL_RenderFillRect(renderer, &rect); + + rect.x = w - SQUARE_SIZE; + SDL_RenderFillRect(renderer, &rect); + + rect.y = 0.0f; + SDL_RenderFillRect(renderer, &rect); + } + + SDL_SetRenderDrawColor(renderer, 255, 0, 0, 180); + rect.x = (w - SQUARE_SIZE) / 2; + rect.y = (h - SQUARE_SIZE) / 2; + SDL_RenderFillRect(renderer, &rect); +} int main(int argc, char *argv[]) { SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; - SDL_Texture *texture = NULL; - SDL_WindowFlags flags; bool done = false; SDL_Event event; - + int return_code = 1; - - int windowWidth = 800; - int windowHeight = 600; - SDL_FRect destRect; - destRect.x = 0; - destRect.y = 0; - destRect.w = 100; - destRect.h = 100; - - SDL_FRect destRect2; - destRect2.x = 700; - destRect2.y = 0; - destRect2.w = 100; - destRect2.h = 100; - - SDL_FRect destRect3; - destRect3.x = 0; - destRect3.y = 500; - destRect3.w = 100; - destRect3.h = 100; - - SDL_FRect destRect4; - destRect4.x = 700; - destRect4.y = 500; - destRect4.w = 100; - destRect4.h = 100; - - SDL_FRect destRect5; - destRect5.x = 350; - destRect5.y = 250; - destRect5.w = 100; - destRect5.h = 100; - - - /* Create the window hidden */ - flags = (SDL_WINDOW_HIDDEN | SDL_WINDOW_TRANSPARENT); - /*flags |= SDL_WINDOW_BORDERLESS;*/ - - window = SDL_CreateWindow("SDL Software Renderer Transparent Test", windowWidth, windowHeight, flags); + window = SDL_CreateWindow("SDL Software Renderer Transparent Test", 800, 600, SDL_WINDOW_TRANSPARENT | SDL_WINDOW_RESIZABLE); if (!window) { SDL_Log("Couldn't create transparent window: %s", SDL_GetError()); goto quit; } - /* Create a software renderer and set the blend mode */ + /* Create a software renderer */ renderer = SDL_CreateRenderer(window, SDL_SOFTWARE_RENDERER); if (!renderer) { SDL_Log("Couldn't create renderer: %s", SDL_GetError()); goto quit; } - if (!SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND)) { - SDL_Log("Couldn't set renderer blend mode: %s\n", SDL_GetError()); - return false; - } - - /* Create texture and set the blend mode */ - /* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); */ - /* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); */ - /* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); */ - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight); - if (texture == NULL) { - SDL_Log("Couldn't create texture: %s\n", SDL_GetError()); - return false; - } - - if (!SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND_PREMULTIPLIED)) { - SDL_Log("Couldn't set texture blend mode: %s\n", SDL_GetError()); - return false; - } - - /* Show */ - SDL_ShowWindow(window); + /* Make sure we're setting the alpha channel while drawing */ + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); /* We're ready to go! */ while (!done) { @@ -109,6 +82,10 @@ int main(int argc, char *argv[]) done = true; } break; + case SDL_EVENT_WINDOW_EXPOSED: + /* The software renderer is persistent, so only redraw as-needed */ + draw(renderer); + break; case SDL_EVENT_QUIT: done = true; break; @@ -117,26 +94,6 @@ int main(int argc, char *argv[]) } } - - /* Draw opaque red squares at the four corners of the form, and draw a red square with an alpha value of 180 in the center of the form */ - - SDL_SetRenderTarget(renderer, texture); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - - SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); - SDL_RenderFillRect(renderer, &destRect); - SDL_RenderFillRect(renderer, &destRect2); - SDL_RenderFillRect(renderer, &destRect3); - SDL_RenderFillRect(renderer, &destRect4); - SDL_SetRenderDrawColor(renderer, 255, 0, 0, 180); - SDL_RenderFillRect(renderer, &destRect5); - - SDL_SetRenderTarget(renderer, NULL); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - SDL_RenderTexture(renderer, texture, NULL, NULL); - /* Show everything on the screen and wait a bit */ SDL_RenderPresent(renderer); SDL_Delay(100); @@ -146,7 +103,6 @@ int main(int argc, char *argv[]) return_code = 0; quit: - SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); From 3597877277a5eeb8c7d7f274819b275c12f10e4a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Sep 2025 19:50:04 -0400 Subject: [PATCH 125/134] SDL_main.h: Cleanups in SDL_RegisterApp documentation. There is an escaped pipe character, because this ends up in a Markdown table on the wiki, and the wiki software (incorrectly, I think) sees the pipe inside a code-block and thinks it's the end of the table cell if not escaped. But this escape char looks wrong everywhere else. Keeping the parameter table terse is always a good idea, so I moved the detail out to the Remarks section, which doesn't have a problem with this character appearing in the text. --- include/SDL3/SDL_main.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/SDL3/SDL_main.h b/include/SDL3/SDL_main.h index 1278b3788f..36f2665bbd 100644 --- a/include/SDL3/SDL_main.h +++ b/include/SDL3/SDL_main.h @@ -615,11 +615,12 @@ extern SDL_DECLSPEC int SDLCALL SDL_EnterAppMainCallbacks(int argc, char *argv[] * Most applications do not need to, and should not, call this directly; SDL * will call it when initializing the video subsystem. * + * If `name` is NULL, SDL currently uses `(CS_BYTEALIGNCLIENT | CS_OWNDC)` + * for the style, regardless of what is specified here. + * * \param name the window class name, in UTF-8 encoding. If NULL, SDL * currently uses "SDL_app" but this isn't guaranteed. - * \param style the value to use in WNDCLASSEX::style. If `name` is NULL, SDL - * currently uses `(CS_BYTEALIGNCLIENT \| CS_OWNDC)` regardless - * of what is specified here. + * \param style the value to use in WNDCLASSEX::style. * \param hInst the HINSTANCE to use in WNDCLASSEX::hInstance. If zero, SDL * will use `GetModuleHandle(NULL)` instead. * \returns true on success or false on failure; call SDL_GetError() for more From 5505cb2bb9b1ec2f116a936eaa86785d5f753d94 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Thu, 4 Sep 2025 23:54:17 +0000 Subject: [PATCH 126/134] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_main.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SDL3/SDL_main.h b/include/SDL3/SDL_main.h index 36f2665bbd..411d64cb2e 100644 --- a/include/SDL3/SDL_main.h +++ b/include/SDL3/SDL_main.h @@ -615,8 +615,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_EnterAppMainCallbacks(int argc, char *argv[] * Most applications do not need to, and should not, call this directly; SDL * will call it when initializing the video subsystem. * - * If `name` is NULL, SDL currently uses `(CS_BYTEALIGNCLIENT | CS_OWNDC)` - * for the style, regardless of what is specified here. + * If `name` is NULL, SDL currently uses `(CS_BYTEALIGNCLIENT | CS_OWNDC)` for + * the style, regardless of what is specified here. * * \param name the window class name, in UTF-8 encoding. If NULL, SDL * currently uses "SDL_app" but this isn't guaranteed. From 99da00930822412100234cf85bbf5e79d5ca8b6f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sat, 30 Aug 2025 19:39:24 +0100 Subject: [PATCH 127/134] wikiheaders: Escape backslash in man pages Otherwise, groff will interpret it as a macro, causing the man page for SDL_GetPrefPath() to be mis-rendered. Escape unescaped backslashes as `\(rs` ("reverse solidus") before escaping other characters with macros that, themselves, contain backslashes. Resolves: https://github.com/libsdl-org/SDL/issues/13039 Signed-off-by: Simon McVittie --- build-scripts/wikiheaders.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl index 4dd44f818b..768f42c83c 100755 --- a/build-scripts/wikiheaders.pl +++ b/build-scripts/wikiheaders.pl @@ -436,6 +436,7 @@ sub dewikify_chunk { } } elsif ($dewikify_mode eq 'manpage') { # make sure these can't become part of roff syntax. + $str =~ s/\\/\\(rs/gms; $str =~ s/\./\\[char46]/gms; $str =~ s/"/\\(dq/gms; $str =~ s/'/\\(aq/gms; From 3572be39980c9c3941b9f60ee1b5ccb9830b7087 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 4 Sep 2025 18:20:26 -0700 Subject: [PATCH 128/134] Improved detection of FlyDigi controllers --- src/joystick/SDL_gamepad.c | 13 +--- src/joystick/hidapi/SDL_hidapi_flydigi.c | 81 +++++++++++++++++------- src/joystick/hidapi/SDL_hidapi_flydigi.h | 38 +++++++++++ 3 files changed, 98 insertions(+), 34 deletions(-) create mode 100644 src/joystick/hidapi/SDL_hidapi_flydigi.h diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index c5ef232b31..ce9c870e46 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -29,6 +29,7 @@ #include "SDL_gamepad_db.h" #include "controller_type.h" #include "usb_ids.h" +#include "hidapi/SDL_hidapi_flydigi.h" #include "hidapi/SDL_hidapi_nintendo.h" #include "../events/SDL_events_c.h" @@ -913,19 +914,9 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17", sizeof(mapping_string)); } else if (SDL_IsJoystickFlydigiController(vendor, product)) { SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b12,paddle3:b13,paddle4:b14,", sizeof(mapping_string)); - switch (guid.data[15]) { - case 20: - case 21: - case 22: - case 23: - case 28: - case 80: - case 81: - case 85: - case 105: + if (guid.data[15] >= SDL_FLYDIGI_VADER2) { // Vader series of controllers have C/Z buttons SDL_strlcat(mapping_string, "misc2:b15,misc3:b16,", sizeof(mapping_string)); - break; } } 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)); diff --git a/src/joystick/hidapi/SDL_hidapi_flydigi.c b/src/joystick/hidapi/SDL_hidapi_flydigi.c index 565f03b70d..55048f6083 100644 --- a/src/joystick/hidapi/SDL_hidapi_flydigi.c +++ b/src/joystick/hidapi/SDL_hidapi_flydigi.c @@ -25,6 +25,7 @@ #include "../SDL_sysjoystick.h" #include "SDL_hidapijoystick_c.h" #include "SDL_hidapi_rumble.h" +#include "SDL_hidapi_flydigi.h" #ifdef SDL_JOYSTICK_HIDAPI_FLYDIGI @@ -147,70 +148,104 @@ static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device) } } - if (ctx->deviceID == 0) { + Uint8 controller_type = SDL_FLYDIGI_UNKNOWN; + switch (ctx->deviceID) { + case 19: + controller_type = SDL_FLYDIGI_APEX2; + break; + case 24: + case 26: + case 29: + controller_type = SDL_FLYDIGI_APEX3; + break; + case 84: + controller_type = SDL_FLYDIGI_APEX4; + break; + case 20: + case 21: + case 23: + controller_type = SDL_FLYDIGI_VADER2; + break; + case 22: + controller_type = SDL_FLYDIGI_VADER2_PRO; + break; + case 28: + controller_type = SDL_FLYDIGI_VADER3; + break; + case 80: + case 81: + controller_type = SDL_FLYDIGI_VADER3_PRO; + break; + case 85: + case 91: + case 105: + controller_type = SDL_FLYDIGI_VADER4_PRO; + break; + default: // Try to guess from the name of the controller if (SDL_strstr(device->name, "VADER") != NULL) { if (SDL_strstr(device->name, "VADER2") != NULL) { - ctx->deviceID = 20; + controller_type = SDL_FLYDIGI_VADER2; } else if (SDL_strstr(device->name, "VADER3") != NULL) { - ctx->deviceID = 28; + controller_type = SDL_FLYDIGI_VADER3; } else if (SDL_strstr(device->name, "VADER4") != NULL) { - ctx->deviceID = 85; + controller_type = SDL_FLYDIGI_VADER4; } } else if (SDL_strstr(device->name, "APEX") != NULL) { if (SDL_strstr(device->name, "APEX2") != NULL) { - ctx->deviceID = 19; + controller_type = SDL_FLYDIGI_APEX2; } else if (SDL_strstr(device->name, "APEX3") != NULL) { - ctx->deviceID = 24; + controller_type = SDL_FLYDIGI_APEX3; } else if (SDL_strstr(device->name, "APEX4") != NULL) { - ctx->deviceID = 84; + controller_type = SDL_FLYDIGI_APEX4; + } else if (SDL_strstr(device->name, "APEX5") != NULL) { + controller_type = SDL_FLYDIGI_APEX5; } } + break; } - device->guid.data[15] = ctx->deviceID; + device->guid.data[15] = controller_type; // This is the previous sensor default of 125hz. // Override this in the switch statement below based on observed sensor packet rate. ctx->sensor_timestamp_step_ns = SDL_NS_PER_SECOND / 125; - switch (ctx->deviceID) { - case 19: + switch (controller_type) { + case SDL_FLYDIGI_APEX2: HIDAPI_SetDeviceName(device, "Flydigi Apex 2"); break; - case 24: - case 26: - case 29: + case SDL_FLYDIGI_APEX3: HIDAPI_SetDeviceName(device, "Flydigi Apex 3"); break; - case 84: + case SDL_FLYDIGI_APEX4: // The Apex 4 controller has sensors, but they're only reported when gyro mouse is enabled HIDAPI_SetDeviceName(device, "Flydigi Apex 4"); break; - case 20: - case 21: - case 23: + case SDL_FLYDIGI_APEX5: + HIDAPI_SetDeviceName(device, "Flydigi Apex 5"); + break; + case SDL_FLYDIGI_VADER2: // The Vader 2 controller has sensors, but they're only reported when gyro mouse is enabled HIDAPI_SetDeviceName(device, "Flydigi Vader 2"); ctx->has_cz = true; break; - case 22: + case SDL_FLYDIGI_VADER2_PRO: HIDAPI_SetDeviceName(device, "Flydigi Vader 2 Pro"); ctx->has_cz = true; break; - case 28: + case SDL_FLYDIGI_VADER3: HIDAPI_SetDeviceName(device, "Flydigi Vader 3"); ctx->has_cz = true; break; - case 80: - case 81: + case SDL_FLYDIGI_VADER3_PRO: HIDAPI_SetDeviceName(device, "Flydigi Vader 3 Pro"); ctx->has_cz = true; ctx->sensors_supported = true; ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f; ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER_PRO4_WIRED_NS; break; - case 85: - case 105: + case SDL_FLYDIGI_VADER4: + case SDL_FLYDIGI_VADER4_PRO: HIDAPI_SetDeviceName(device, "Flydigi Vader 4 Pro"); ctx->has_cz = true; ctx->sensors_supported = true; diff --git a/src/joystick/hidapi/SDL_hidapi_flydigi.h b/src/joystick/hidapi/SDL_hidapi_flydigi.h new file mode 100644 index 0000000000..42d6ef7ee2 --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_flydigi.h @@ -0,0 +1,38 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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. +*/ + +// These are values used in the controller type byte of the controller GUID + +typedef enum +{ + SDL_FLYDIGI_UNKNOWN, + SDL_FLYDIGI_APEX2 = (1 << 0), + SDL_FLYDIGI_APEX3, + SDL_FLYDIGI_APEX4, + SDL_FLYDIGI_APEX5, + SDL_FLYDIGI_VADER2 = (1 << 4), + SDL_FLYDIGI_VADER2_PRO, + SDL_FLYDIGI_VADER3, + SDL_FLYDIGI_VADER3_PRO, + SDL_FLYDIGI_VADER4, + SDL_FLYDIGI_VADER4_PRO, +} SDL_FlyDigiControllerType; + From 129c97f610d06cf0fd4583b2a8a96ac917500bb1 Mon Sep 17 00:00:00 2001 From: Aleksey Melekh Date: Fri, 5 Sep 2025 17:58:37 +0200 Subject: [PATCH 129/134] xbox: fix build --- src/video/windows/SDL_windowswindow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 5902f8f9c3..259ba3c239 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -737,8 +737,10 @@ bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties return false; } +#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) // Ensure that the IME isn't active on the new window until explicitly requested. WIN_StopTextInput(_this, window); +#endif // Inform Windows of the frame change so we can respond to WM_NCCALCSIZE SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); From b8197a2291aed3fdfbb777938dedbdde76dab86c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 5 Sep 2025 10:12:42 -0400 Subject: [PATCH 130/134] io: rework how we set SDL_IOStream status. This now relies on the implementation to set these flags on short reads/writes instead of the higher level checking if SDL_SetError() was called. Additionally (and crucially), this now sets ERROR or EOF on all short reads, across all backends, not just when we get a zero-byte return value. Fixes #13720. --- include/SDL3/SDL_iostream.h | 12 ++++- src/core/android/SDL_android.c | 4 ++ src/io/SDL_iostream.c | 91 +++++++++++++++++++--------------- 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/include/SDL3/SDL_iostream.h b/include/SDL3/SDL_iostream.h index 8bdfc1dbd3..677702e2a6 100644 --- a/include/SDL3/SDL_iostream.h +++ b/include/SDL3/SDL_iostream.h @@ -111,7 +111,7 @@ typedef struct SDL_IOStreamInterface /** * Read up to `size` bytes from the data stream to the area pointed - * at by `ptr`. + * at by `ptr`. `size` will always be > 0. * * On an incomplete read, you should set `*status` to a value from the * SDL_IOStatus enum. You do not have to explicitly set this on @@ -123,7 +123,7 @@ typedef struct SDL_IOStreamInterface /** * Write exactly `size` bytes from the area pointed at by `ptr` - * to data stream. + * to data stream. `size` will always be > 0. * * On an incomplete write, you should set `*status` to a value from the * SDL_IOStatus enum. You do not have to explicitly set this on @@ -580,6 +580,10 @@ extern SDL_DECLSPEC Sint64 SDLCALL SDL_TellIO(SDL_IOStream *context); * the stream is not at EOF, SDL_GetIOStatus() will return a different error * value and SDL_GetError() will offer a human-readable message. * + * A request for zero bytes on a valid stream will return zero immediately + * without accessing the stream, so the stream status (EOF, err, etc) will + * not change. + * * \param context a pointer to an SDL_IOStream structure. * \param ptr a pointer to a buffer to read data into. * \param size the number of bytes to read from the data source. @@ -609,6 +613,10 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_ReadIO(SDL_IOStream *context, void *ptr, * recoverable, such as a non-blocking write that can simply be retried later, * or a fatal error. * + * A request for zero bytes on a valid stream will return zero immediately + * without accessing the stream, so the stream status (EOF, err, etc) will + * not change. + * * \param context a pointer to an SDL_IOStream structure. * \param ptr a pointer to a buffer containing data to write. * \param size the number of bytes to write. diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index e0745a0a57..6e9d2e7389 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -1827,7 +1827,10 @@ size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOSta const int bytes = AAsset_read((AAsset *)userdata, buffer, size); if (bytes < 0) { SDL_SetError("AAsset_read() failed"); + *status = SDL_IO_STATUS_ERROR; return 0; + } else if (bytes < size) { + *status = SDL_IO_STATUS_EOF; } return (size_t)bytes; } @@ -1835,6 +1838,7 @@ size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOSta size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status) { SDL_SetError("Cannot write to Android package filesystem"); + *status = SDL_IO_STATUS_ERROR; return 0; } diff --git a/src/io/SDL_iostream.c b/src/io/SDL_iostream.c index 7c956baf5f..2c1544144f 100644 --- a/src/io/SDL_iostream.c +++ b/src/io/SDL_iostream.c @@ -220,15 +220,17 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size, switch (error) { case ERROR_BROKEN_PIPE: case ERROR_HANDLE_EOF: + *status = SDL_IO_STATUS_EOF; break; case ERROR_NO_DATA: *status = SDL_IO_STATUS_NOT_READY; break; default: + *status = SDL_IO_STATUS_ERROR; WIN_SetError("Error reading from datastream"); break; } - return 0; + return 0; // !!! FIXME: this should return the bytes read from any readahead we finished out before this (the `iodata->left > 0` code above). In that case, fail on the next read. } read_ahead = SDL_min(total_need, bytes); SDL_memcpy(ptr, iodata->data, read_ahead); @@ -241,15 +243,17 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size, switch (error) { case ERROR_BROKEN_PIPE: case ERROR_HANDLE_EOF: + *status = SDL_IO_STATUS_EOF; break; case ERROR_NO_DATA: *status = SDL_IO_STATUS_NOT_READY; break; default: + *status = SDL_IO_STATUS_ERROR; WIN_SetError("Error reading from datastream"); break; } - return 0; + return 0; // !!! FIXME: this should return the bytes read from any readahead we finished out before this (the `iodata->left > 0` code above). In that case, fail on the next read. } total_read += bytes; } @@ -263,6 +267,7 @@ static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t if (iodata->left) { if (!SetFilePointer(iodata->h, -(LONG)iodata->left, NULL, FILE_CURRENT)) { + *status = SDL_IO_STATUS_ERROR; WIN_SetError("Error seeking in datastream"); return 0; } @@ -274,16 +279,17 @@ static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t LARGE_INTEGER windowsoffset; windowsoffset.QuadPart = 0; if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, FILE_END)) { + *status = SDL_IO_STATUS_ERROR; WIN_SetError("Error seeking in datastream"); return 0; } } if (!WriteFile(iodata->h, ptr, (DWORD)size, &bytes, NULL)) { + *status = SDL_IO_STATUS_ERROR; WIN_SetError("Error writing to datastream"); return 0; - } - if (bytes == 0 && size > 0) { + } else if (bytes == 0 && size > 0) { *status = SDL_IO_STATUS_NOT_READY; } return bytes; @@ -421,9 +427,12 @@ static size_t SDLCALL fd_read(void *userdata, void *ptr, size_t size, SDL_IOStat if (errno == EAGAIN) { *status = SDL_IO_STATUS_NOT_READY; } else { + *status = SDL_IO_STATUS_ERROR; SDL_SetError("Error reading from datastream: %s", strerror(errno)); } bytes = 0; + } else if (bytes < size) { + *status = SDL_IO_STATUS_EOF; } return (size_t)bytes; } @@ -440,6 +449,7 @@ static size_t SDLCALL fd_write(void *userdata, const void *ptr, size_t size, SDL if (errno == EAGAIN) { *status = SDL_IO_STATUS_NOT_READY; } else { + *status = SDL_IO_STATUS_ERROR; SDL_SetError("Error writing to datastream: %s", strerror(errno)); } bytes = 0; @@ -606,12 +616,18 @@ static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size, SDL_IOS { IOStreamStdioData *iodata = (IOStreamStdioData *) userdata; const size_t bytes = fread(ptr, 1, size, iodata->fp); - if (bytes == 0 && ferror(iodata->fp)) { - if (errno == EAGAIN) { - *status = SDL_IO_STATUS_NOT_READY; - clearerr(iodata->fp); + if (bytes < size) { + if (ferror(iodata->fp)) { + if (errno == EAGAIN) { + *status = SDL_IO_STATUS_NOT_READY; + clearerr(iodata->fp); + } else { + *status = SDL_IO_STATUS_ERROR; + SDL_SetError("Error reading from datastream: %s", strerror(errno)); + } } else { - SDL_SetError("Error reading from datastream: %s", strerror(errno)); + SDL_assert(feof(iodata->fp)); + *status = SDL_IO_STATUS_EOF; } } return bytes; @@ -626,6 +642,7 @@ static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size, *status = SDL_IO_STATUS_NOT_READY; clearerr(iodata->fp); } else { + *status = SDL_IO_STATUS_ERROR; SDL_SetError("Error writing to datastream: %s", strerror(errno)); } } @@ -769,13 +786,22 @@ static size_t mem_io(void *userdata, void *dst, const void *src, size_t size) static size_t SDLCALL mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status) { IOStreamMemData *iodata = (IOStreamMemData *) userdata; - return mem_io(userdata, ptr, iodata->here, size); + const size_t retval = mem_io(userdata, ptr, iodata->here, size); + if ((retval < size) && (iodata->stop == iodata->here)) { + *status = SDL_IO_STATUS_EOF; + } + return retval; } static size_t SDLCALL mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status) { IOStreamMemData *iodata = (IOStreamMemData *) userdata; - return mem_io(userdata, iodata->here, ptr, size); + const size_t retval = mem_io(userdata, iodata->here, ptr, size); + if ((retval < size) && (iodata->stop == iodata->here)) { + SDL_SetError("Memory buffer is full"); + *status = SDL_IO_STATUS_ERROR; + } + return retval; } static bool SDLCALL mem_close(void *userdata) @@ -1027,7 +1053,11 @@ static Sint64 SDLCALL dynamic_mem_seek(void *userdata, Sint64 offset, SDL_IOWhen static size_t SDLCALL dynamic_mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status) { IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata; - return mem_io(&iodata->data, ptr, iodata->data.here, size); + const size_t retval = mem_io(&iodata->data, ptr, iodata->data.here, size); + if ((retval < size) && (iodata->data.stop == iodata->data.here)) { + *status = SDL_IO_STATUS_EOF; + } + return retval; } static bool dynamic_mem_realloc(IOStreamDynamicMemData *iodata, size_t size) @@ -1060,12 +1090,15 @@ static size_t SDLCALL dynamic_mem_write(void *userdata, const void *ptr, size_t if (size > (size_t)(iodata->data.stop - iodata->data.here)) { if (size > (size_t)(iodata->end - iodata->data.here)) { if (!dynamic_mem_realloc(iodata, size)) { + *status = SDL_IO_STATUS_ERROR; return 0; } } iodata->data.stop = iodata->data.here + size; } - return mem_io(&iodata->data, iodata->data.here, ptr, size); + const size_t retval = mem_io(&iodata->data, iodata->data.here, ptr, size); + SDL_assert(retval == size); // we should have allocated enough to cover this! + return retval; } static bool SDLCALL dynamic_mem_close(void *userdata) @@ -1333,8 +1366,6 @@ Sint64 SDL_TellIO(SDL_IOStream *context) size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size) { - size_t bytes; - if (!context) { SDL_InvalidParamError("context"); return 0; @@ -1342,30 +1373,18 @@ size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size) context->status = SDL_IO_STATUS_WRITEONLY; SDL_Unsupported(); return 0; + } else if (size == 0) { + return 0; // context->status doesn't change for this. } context->status = SDL_IO_STATUS_READY; SDL_ClearError(); - if (size == 0) { - return 0; - } - - bytes = context->iface.read(context->userdata, ptr, size, &context->status); - if (bytes == 0 && context->status == SDL_IO_STATUS_READY) { - if (*SDL_GetError()) { - context->status = SDL_IO_STATUS_ERROR; - } else { - context->status = SDL_IO_STATUS_EOF; - } - } - return bytes; + return context->iface.read(context->userdata, ptr, size, &context->status); } size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size) { - size_t bytes; - if (!context) { SDL_InvalidParamError("context"); return 0; @@ -1373,20 +1392,14 @@ size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size) context->status = SDL_IO_STATUS_READONLY; SDL_Unsupported(); return 0; + } else if (size == 0) { + return 0; // context->status doesn't change for this. } context->status = SDL_IO_STATUS_READY; SDL_ClearError(); - if (size == 0) { - return 0; - } - - bytes = context->iface.write(context->userdata, ptr, size, &context->status); - if ((bytes == 0) && (context->status == SDL_IO_STATUS_READY)) { - context->status = SDL_IO_STATUS_ERROR; - } - return bytes; + return context->iface.write(context->userdata, ptr, size, &context->status); } size_t SDL_IOprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) From 197bfab0b5752bd7ff12249004574c526225a25d Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Fri, 5 Sep 2025 16:44:33 +0000 Subject: [PATCH 131/134] Sync SDL3 wiki -> header [ci skip] --- include/SDL3/SDL_iostream.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/SDL3/SDL_iostream.h b/include/SDL3/SDL_iostream.h index 677702e2a6..0fb3271854 100644 --- a/include/SDL3/SDL_iostream.h +++ b/include/SDL3/SDL_iostream.h @@ -581,8 +581,8 @@ extern SDL_DECLSPEC Sint64 SDLCALL SDL_TellIO(SDL_IOStream *context); * value and SDL_GetError() will offer a human-readable message. * * A request for zero bytes on a valid stream will return zero immediately - * without accessing the stream, so the stream status (EOF, err, etc) will - * not change. + * without accessing the stream, so the stream status (EOF, err, etc) will not + * change. * * \param context a pointer to an SDL_IOStream structure. * \param ptr a pointer to a buffer to read data into. @@ -614,8 +614,8 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_ReadIO(SDL_IOStream *context, void *ptr, * or a fatal error. * * A request for zero bytes on a valid stream will return zero immediately - * without accessing the stream, so the stream status (EOF, err, etc) will - * not change. + * without accessing the stream, so the stream status (EOF, err, etc) will not + * change. * * \param context a pointer to an SDL_IOStream structure. * \param ptr a pointer to a buffer containing data to write. From 937bf4d7890e910ce7d78327a012f8b0a722a507 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 5 Sep 2025 13:50:06 -0400 Subject: [PATCH 132/134] alsa: Restart PCM devices after recovery from an overrun/underrun. snd_pcm_recover() puts the device back in SND_PCM_STATE_PREPARED state; you have to explicitly restart the device afterwards with snd_pcm_start(). Fixes #13761. --- src/audio/alsa/SDL_alsa_audio.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c index 65a4b873db..07b1b556f4 100644 --- a/src/audio/alsa/SDL_alsa_audio.c +++ b/src/audio/alsa/SDL_alsa_audio.c @@ -82,6 +82,7 @@ static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int); static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int); static int (*ALSA_snd_pcm_sw_params_set_avail_min)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); static int (*ALSA_snd_pcm_reset)(snd_pcm_t *); +static snd_pcm_state_t (*ALSA_snd_pcm_state)(snd_pcm_t *); static int (*ALSA_snd_device_name_hint)(int, const char *, void ***); static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *); static int (*ALSA_snd_device_name_free_hint)(void **); @@ -171,6 +172,7 @@ static bool load_alsa_syms(void) SDL_ALSA_SYM(snd_pcm_wait); SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); SDL_ALSA_SYM(snd_pcm_reset); + SDL_ALSA_SYM(snd_pcm_state); SDL_ALSA_SYM(snd_device_name_hint); SDL_ALSA_SYM(snd_device_name_get_hint); SDL_ALSA_SYM(snd_device_name_free_hint); @@ -352,6 +354,20 @@ static char *get_pcm_str(void *handle) return pcm_str; } +static int RecoverALSADevice(snd_pcm_t *pcm, int errnum) +{ + const snd_pcm_state_t prerecovery = ALSA_snd_pcm_state(pcm); + const int status = ALSA_snd_pcm_recover(pcm, errnum, 0); // !!! FIXME: third parameter is non-zero to prevent libasound from printing error messages. Should we do that? + if (status == 0) { + const snd_pcm_state_t postrecovery = ALSA_snd_pcm_state(pcm); + if ((prerecovery == SND_PCM_STATE_XRUN) && (postrecovery == SND_PCM_STATE_PREPARED)) { + ALSA_snd_pcm_start(pcm); // restart the device if it stopped due to an overrun or underrun. + } + } + return status; +} + + // This function waits until it is possible to write a full sound buffer static bool ALSA_WaitDevice(SDL_AudioDevice *device) { @@ -362,7 +378,7 @@ static bool ALSA_WaitDevice(SDL_AudioDevice *device) while (!SDL_GetAtomicInt(&device->shutdown)) { const int rc = ALSA_snd_pcm_avail(device->hidden->pcm); if (rc < 0) { - const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); + const int status = RecoverALSADevice(device->hidden->pcm, rc); if (status < 0) { // Hmm, not much we can do - abort SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA wait failed (unrecoverable): %s", ALSA_snd_strerror(rc)); @@ -390,7 +406,7 @@ static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu SDL_assert(rc != 0); // assuming this can't happen if we used snd_pcm_wait and queried for available space. if (rc < 0) { SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! - const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); + const int status = RecoverALSADevice(device->hidden->pcm, rc); if (status < 0) { // Hmm, not much we can do - abort SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA write failed (unrecoverable): %s", ALSA_snd_strerror(rc)); @@ -445,7 +461,7 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! if (rc < 0) { - const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); + const int status = RecoverALSADevice(device->hidden->pcm, rc); if (status < 0) { // Hmm, not much we can do - abort SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA read failed (unrecoverable): %s", ALSA_snd_strerror(rc)); From a6dc61ab321cfe5a8f31a723f813be03191bb941 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 5 Sep 2025 14:46:59 -0400 Subject: [PATCH 133/134] joystick: Emscripten can often fake a hat from the d-pad buttons. Fixes #13817. --- src/joystick/emscripten/SDL_sysjoystick.c | 80 +++++++++++++++++---- src/joystick/emscripten/SDL_sysjoystick_c.h | 3 + 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c index cddd975a71..6c51f8eaf6 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/src/joystick/emscripten/SDL_sysjoystick.c @@ -66,8 +66,17 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep goto done; } + int first_hat_button = -1; + int num_buttons = gamepadEvent->numButtons; + if ((SDL_strcmp(gamepadEvent->mapping, "standard") == 0) && (num_buttons >= 16)) { // maps to a game console gamepad layout, turn the d-pad into a hat. + num_buttons -= 4; + first_hat_button = 12; + } + + item->first_hat_button = first_hat_button; + item->nhats = (first_hat_button >= 0) ? 1 : 0; item->naxes = gamepadEvent->numAxes; - item->nbuttons = gamepadEvent->numButtons; + item->nbuttons = num_buttons; item->device_instance = SDL_GetNextObjectID(); item->timestamp = gamepadEvent->timestamp; @@ -76,9 +85,30 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep item->axis[i] = gamepadEvent->axis[i]; } - for (i = 0; i < item->nbuttons; i++) { - item->analogButton[i] = gamepadEvent->analogButton[i]; - item->digitalButton[i] = gamepadEvent->digitalButton[i]; + int buttonidx = 0; + for (i = 0; i < item->nbuttons; i++, buttonidx++) { + if (buttonidx == first_hat_button) { + buttonidx += 3; // skip these buttons, we're treating them as hat input. + } + item->analogButton[i] = gamepadEvent->analogButton[buttonidx]; + item->digitalButton[i] = gamepadEvent->digitalButton[buttonidx]; + } + + SDL_assert(item->nhats <= 1); // there is (currently) only ever one of these, faked from the d-pad buttons. + if (item->nhats) { + Uint8 value = SDL_HAT_CENTERED; + // this currently expects the first button to be up, then down, then left, then right. + if (gamepadEvent->digitalButton[first_hat_button + 0]) { + value |= SDL_HAT_UP; + } else if (gamepadEvent->digitalButton[first_hat_button + 1]) { + value |= SDL_HAT_DOWN; + } + if (gamepadEvent->digitalButton[first_hat_button + 2]) { + value |= SDL_HAT_LEFT; + } else if (gamepadEvent->digitalButton[first_hat_button + 3]) { + value |= SDL_HAT_RIGHT; + } + item->hat = value; } if (!SDL_joylist_tail) { @@ -318,9 +348,8 @@ static bool EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->hwdata = (struct joystick_hwdata *)item; item->joystick = joystick; - // HTML5 Gamepad API doesn't say anything about these - joystick->nhats = 0; - + // HTML5 Gamepad API doesn't offer hats, but we can fake it from the d-pad buttons on the "standard" mapping. + joystick->nhats = item->nhats; joystick->nbuttons = item->nbuttons; joystick->naxes = item->naxes; @@ -361,15 +390,21 @@ static void EMSCRIPTEN_JoystickUpdate(SDL_Joystick *joystick) result = emscripten_get_gamepad_status(item->index, &gamepadState); if (result == EMSCRIPTEN_RESULT_SUCCESS) { if (gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) { - for (i = 0; i < item->nbuttons; i++) { - if (item->digitalButton[i] != gamepadState.digitalButton[i]) { - bool down = (gamepadState.digitalButton[i] != 0); + const int first_hat_button = item->first_hat_button; + + int buttonidx = 0; + for (i = 0; i < item->nbuttons; i++, buttonidx++) { + if (buttonidx == first_hat_button) { + buttonidx += 4; // skip these buttons, we're treating them as hat input. + } + if (item->digitalButton[i] != gamepadState.digitalButton[buttonidx]) { + bool down = (gamepadState.digitalButton[buttonidx] != 0); SDL_SendJoystickButton(timestamp, item->joystick, i, down); } // store values to compare them in the next update - item->analogButton[i] = gamepadState.analogButton[i]; - item->digitalButton[i] = gamepadState.digitalButton[i]; + item->analogButton[i] = gamepadState.analogButton[buttonidx]; + item->digitalButton[i] = gamepadState.digitalButton[buttonidx]; } for (i = 0; i < item->naxes; i++) { @@ -383,6 +418,27 @@ static void EMSCRIPTEN_JoystickUpdate(SDL_Joystick *joystick) item->axis[i] = gamepadState.axis[i]; } + SDL_assert(item->nhats <= 1); // there is (currently) only ever one of these, faked from the d-pad buttons. + if (item->nhats) { + Uint8 value = SDL_HAT_CENTERED; + // this currently expects the first button to be up, then down, then left, then right. + if (gamepadState.digitalButton[first_hat_button + 0]) { + value |= SDL_HAT_UP; + } else if (gamepadState.digitalButton[first_hat_button + 1]) { + value |= SDL_HAT_DOWN; + } + if (gamepadState.digitalButton[first_hat_button + 2]) { + value |= SDL_HAT_LEFT; + } else if (gamepadState.digitalButton[first_hat_button + 3]) { + value |= SDL_HAT_RIGHT; + } + if (item->hat != value) { + item->hat = value; + SDL_SendJoystickHat(timestamp, item->joystick, 0, value); + } + } + + item->timestamp = gamepadState.timestamp; } } diff --git a/src/joystick/emscripten/SDL_sysjoystick_c.h b/src/joystick/emscripten/SDL_sysjoystick_c.h index e03a27c41f..991959a759 100644 --- a/src/joystick/emscripten/SDL_sysjoystick_c.h +++ b/src/joystick/emscripten/SDL_sysjoystick_c.h @@ -34,12 +34,15 @@ typedef struct SDL_joylist_item char *mapping; SDL_JoystickID device_instance; SDL_Joystick *joystick; + int first_hat_button; + int nhats; int nbuttons; int naxes; double timestamp; double axis[64]; double analogButton[64]; EM_BOOL digitalButton[64]; + Uint8 hat; // there is (currently) only ever one of these, faked from the d-pad buttons. struct SDL_joylist_item *next; } SDL_joylist_item; From c573326f11490a1cacaff45b5cc5a1cbbd90741f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 5 Sep 2025 14:47:20 -0700 Subject: [PATCH 134/134] Fixed double-free in the OpenVR video backend --- src/video/openvr/SDL_openvrvideo.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/video/openvr/SDL_openvrvideo.c b/src/video/openvr/SDL_openvrvideo.c index 6a26178f72..b163f4df51 100644 --- a/src/video/openvr/SDL_openvrvideo.c +++ b/src/video/openvr/SDL_openvrvideo.c @@ -222,8 +222,6 @@ static bool OPENVR_VideoInit(SDL_VideoDevice *_this) } else { display.desktop_mode.refresh_rate = data->oSystem->GetFloatTrackedDeviceProperty(k_unTrackedDeviceIndex_Hmd, ETrackedDeviceProperty_Prop_DisplayFrequency_Float, 0); } - - display.internal = (SDL_DisplayData *)data; display.name = (char *)"OpenVRDisplay"; SDL_AddVideoDisplay(&display, false);