Update SDL3 from 3.2.4 to 3.2.20

This commit is contained in:
Sven Balzer
2025-08-27 21:24:05 +02:00
parent 6283160467
commit ad651462df
332 changed files with 20334 additions and 4852 deletions
+47 -5
View File
@@ -22,6 +22,7 @@
#ifdef SDL_HAVE_BLIT_A
#include "SDL_pixels_c.h"
#include "SDL_surface_c.h"
// Functions to perform alpha blended blitting
@@ -968,6 +969,10 @@ static void Blit8888to8888PixelAlphaSwizzle(SDL_BlitInfo *info)
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = !dstfmt->Amask;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
while (height--) {
int i = 0;
@@ -976,6 +981,9 @@ static void Blit8888to8888PixelAlphaSwizzle(SDL_BlitInfo *info)
Uint32 src32 = *(Uint32 *)src;
Uint32 dst32 = *(Uint32 *)dst;
ALPHA_BLEND_SWIZZLE_8888(src32, dst32, srcfmt, dstfmt);
if (fill_alpha) {
dst32 |= dstAmask;
}
*(Uint32 *)dst = dst32;
src += 4;
dst += 4;
@@ -998,6 +1006,10 @@ static void SDL_TARGETING("sse4.1") Blit8888to8888PixelAlphaSwizzleSSE41(SDL_Bli
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = !dstfmt->Amask;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
// The byte offsets for the start of each pixel
const __m128i mask_offsets = _mm_set_epi8(
@@ -1011,7 +1023,7 @@ static void SDL_TARGETING("sse4.1") Blit8888to8888PixelAlphaSwizzleSSE41(SDL_Bli
mask_offsets);
const __m128i alpha_splat_mask = _mm_add_epi8(_mm_set1_epi8(srcfmt->Ashift >> 3), mask_offsets);
const __m128i alpha_fill_mask = _mm_set1_epi32((int)dstfmt->Amask);
const __m128i alpha_fill_mask = _mm_set1_epi32((int)dstAmask);
while (height--) {
int i = 0;
@@ -1057,7 +1069,11 @@ static void SDL_TARGETING("sse4.1") Blit8888to8888PixelAlphaSwizzleSSE41(SDL_Bli
dst_hi = _mm_mulhi_epu16(dst_hi, _mm_set1_epi16(257));
// Blend the pixels together and save the result
_mm_storeu_si128((__m128i *)dst, _mm_packus_epi16(dst_lo, dst_hi));
dst128 = _mm_packus_epi16(dst_lo, dst_hi);
if (fill_alpha) {
dst128 = _mm_or_si128(dst128, alpha_fill_mask);
}
_mm_storeu_si128((__m128i *)dst, dst128);
src += 16;
dst += 16;
@@ -1067,6 +1083,9 @@ static void SDL_TARGETING("sse4.1") Blit8888to8888PixelAlphaSwizzleSSE41(SDL_Bli
Uint32 src32 = *(Uint32 *)src;
Uint32 dst32 = *(Uint32 *)dst;
ALPHA_BLEND_SWIZZLE_8888(src32, dst32, srcfmt, dstfmt);
if (fill_alpha) {
dst32 |= dstAmask;
}
*(Uint32 *)dst = dst32;
src += 4;
dst += 4;
@@ -1091,6 +1110,10 @@ static void SDL_TARGETING("avx2") Blit8888to8888PixelAlphaSwizzleAVX2(SDL_BlitIn
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = !dstfmt->Amask;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
// The byte offsets for the start of each pixel
const __m256i mask_offsets = _mm256_set_epi8(
@@ -1104,7 +1127,7 @@ static void SDL_TARGETING("avx2") Blit8888to8888PixelAlphaSwizzleAVX2(SDL_BlitIn
mask_offsets);
const __m256i alpha_splat_mask = _mm256_add_epi8(_mm256_set1_epi8(srcfmt->Ashift >> 3), mask_offsets);
const __m256i alpha_fill_mask = _mm256_set1_epi32((int)dstfmt->Amask);
const __m256i alpha_fill_mask = _mm256_set1_epi32((int)dstAmask);
while (height--) {
int i = 0;
@@ -1150,7 +1173,11 @@ static void SDL_TARGETING("avx2") Blit8888to8888PixelAlphaSwizzleAVX2(SDL_BlitIn
dst_hi = _mm256_mulhi_epu16(dst_hi, _mm256_set1_epi16(257));
// Blend the pixels together and save the result
_mm256_storeu_si256((__m256i *)dst, _mm256_packus_epi16(dst_lo, dst_hi));
dst256 = _mm256_packus_epi16(dst_lo, dst_hi);
if (fill_alpha) {
dst256 = _mm256_or_si256(dst256, alpha_fill_mask);
}
_mm256_storeu_si256((__m256i *)dst, dst256);
src += 32;
dst += 32;
@@ -1160,6 +1187,9 @@ static void SDL_TARGETING("avx2") Blit8888to8888PixelAlphaSwizzleAVX2(SDL_BlitIn
Uint32 src32 = *(Uint32 *)src;
Uint32 dst32 = *(Uint32 *)dst;
ALPHA_BLEND_SWIZZLE_8888(src32, dst32, srcfmt, dstfmt);
if (fill_alpha) {
dst32 |= dstAmask;
}
*(Uint32 *)dst = dst32;
src += 4;
dst += 4;
@@ -1184,6 +1214,10 @@ static void Blit8888to8888PixelAlphaSwizzleNEON(SDL_BlitInfo *info)
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = !dstfmt->Amask;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
// The byte offsets for the start of each pixel
const uint8x16_t mask_offsets = vreinterpretq_u8_u64(vcombine_u64(
@@ -1197,7 +1231,7 @@ static void Blit8888to8888PixelAlphaSwizzleNEON(SDL_BlitInfo *info)
((srcfmt->Bshift >> 3) << dstfmt->Bshift))));
const uint8x16_t alpha_splat_mask = vaddq_u8(vdupq_n_u8(srcfmt->Ashift >> 3), mask_offsets);
const uint8x16_t alpha_fill_mask = vreinterpretq_u8_u32(vdupq_n_u32(dstfmt->Amask));
const uint8x16_t alpha_fill_mask = vreinterpretq_u8_u32(vdupq_n_u32(dstAmask));
while (height--) {
int i = 0;
@@ -1242,6 +1276,10 @@ static void Blit8888to8888PixelAlphaSwizzleNEON(SDL_BlitInfo *info)
// temp = vraddhn_u16(res_lo, vrshrq_n_u16(res_lo, 8));
// dst128 = vraddhn_high_u16(temp, res_hi, vrshrq_n_u16(res_hi, 8));
if (fill_alpha) {
dst128 = vorrq_u8(dst128, alpha_fill_mask);
}
// Save the result
vst1q_u8(dst, dst128);
@@ -1266,6 +1304,10 @@ static void Blit8888to8888PixelAlphaSwizzleNEON(SDL_BlitInfo *info)
dst32 = vaddhn_u16(res, vshrq_n_u16(res, 8));
if (fill_alpha) {
dst32 = vorr_u8(dst32, vget_low_u8(alpha_fill_mask));
}
// Save the result, only low 32-bits
vst1_lane_u32((Uint32*)dst, vreinterpret_u32_u8(dst32), 0);
+267
View File
@@ -22,6 +22,7 @@
#ifdef SDL_HAVE_BLIT_N
#include "SDL_pixels_c.h"
#include "SDL_surface_c.h"
#include "SDL_blit_copy.h"
@@ -2551,6 +2552,255 @@ static void BlitNtoNKeyCopyAlpha(SDL_BlitInfo *info)
}
}
// Convert between two 8888 pixels with differing formats.
#define SWIZZLE_8888_SRC_ALPHA(src, dst, srcfmt, dstfmt) \
do { \
dst = (((src >> srcfmt->Rshift) & 0xFF) << dstfmt->Rshift) | \
(((src >> srcfmt->Gshift) & 0xFF) << dstfmt->Gshift) | \
(((src >> srcfmt->Bshift) & 0xFF) << dstfmt->Bshift) | \
(((src >> srcfmt->Ashift) & 0xFF) << dstfmt->Ashift); \
} while (0)
#define SWIZZLE_8888_DST_ALPHA(src, dst, srcfmt, dstfmt, dstAmask) \
do { \
dst = (((src >> srcfmt->Rshift) & 0xFF) << dstfmt->Rshift) | \
(((src >> srcfmt->Gshift) & 0xFF) << dstfmt->Gshift) | \
(((src >> srcfmt->Bshift) & 0xFF) << dstfmt->Bshift) | \
dstAmask; \
} while (0)
#ifdef SDL_SSE4_1_INTRINSICS
static void SDL_TARGETING("sse4.1") Blit8888to8888PixelSwizzleSSE41(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = (!srcfmt->Amask || !dstfmt->Amask);
Uint32 srcAmask, srcAshift;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(srcfmt, &srcAmask, &srcAshift);
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
// The byte offsets for the start of each pixel
const __m128i mask_offsets = _mm_set_epi8(
12, 12, 12, 12, 8, 8, 8, 8, 4, 4, 4, 4, 0, 0, 0, 0);
const __m128i convert_mask = _mm_add_epi32(
_mm_set1_epi32(
((srcfmt->Rshift >> 3) << dstfmt->Rshift) |
((srcfmt->Gshift >> 3) << dstfmt->Gshift) |
((srcfmt->Bshift >> 3) << dstfmt->Bshift) |
((srcAshift >> 3) << dstAshift)),
mask_offsets);
const __m128i alpha_fill_mask = _mm_set1_epi32((int)dstAmask);
while (height--) {
int i = 0;
for (; i + 4 <= width; i += 4) {
// Load 4 src pixels
__m128i src128 = _mm_loadu_si128((__m128i *)src);
// Convert to dst format
src128 = _mm_shuffle_epi8(src128, convert_mask);
if (fill_alpha) {
// Set the alpha channels of src to 255
src128 = _mm_or_si128(src128, alpha_fill_mask);
}
// Save the result
_mm_storeu_si128((__m128i *)dst, src128);
src += 16;
dst += 16;
}
for (; i < width; ++i) {
Uint32 src32 = *(Uint32 *)src;
Uint32 dst32;
if (fill_alpha) {
SWIZZLE_8888_DST_ALPHA(src32, dst32, srcfmt, dstfmt, dstAmask);
} else {
SWIZZLE_8888_SRC_ALPHA(src32, dst32, srcfmt, dstfmt);
}
*(Uint32 *)dst = dst32;
src += 4;
dst += 4;
}
src += srcskip;
dst += dstskip;
}
}
#endif
#ifdef SDL_AVX2_INTRINSICS
static void SDL_TARGETING("avx2") Blit8888to8888PixelSwizzleAVX2(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = (!srcfmt->Amask || !dstfmt->Amask);
Uint32 srcAmask, srcAshift;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(srcfmt, &srcAmask, &srcAshift);
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
// The byte offsets for the start of each pixel
const __m256i mask_offsets = _mm256_set_epi8(
28, 28, 28, 28, 24, 24, 24, 24, 20, 20, 20, 20, 16, 16, 16, 16, 12, 12, 12, 12, 8, 8, 8, 8, 4, 4, 4, 4, 0, 0, 0, 0);
const __m256i convert_mask = _mm256_add_epi32(
_mm256_set1_epi32(
((srcfmt->Rshift >> 3) << dstfmt->Rshift) |
((srcfmt->Gshift >> 3) << dstfmt->Gshift) |
((srcfmt->Bshift >> 3) << dstfmt->Bshift) |
((srcAshift >> 3) << dstAshift)),
mask_offsets);
const __m256i alpha_fill_mask = _mm256_set1_epi32((int)dstAmask);
while (height--) {
int i = 0;
for (; i + 8 <= width; i += 8) {
// Load 8 src pixels
__m256i src256 = _mm256_loadu_si256((__m256i *)src);
// Convert to dst format
src256 = _mm256_shuffle_epi8(src256, convert_mask);
if (fill_alpha) {
// Set the alpha channels of src to 255
src256 = _mm256_or_si256(src256, alpha_fill_mask);
}
// Save the result
_mm256_storeu_si256((__m256i *)dst, src256);
src += 32;
dst += 32;
}
for (; i < width; ++i) {
Uint32 src32 = *(Uint32 *)src;
Uint32 dst32;
if (fill_alpha) {
SWIZZLE_8888_DST_ALPHA(src32, dst32, srcfmt, dstfmt, dstAmask);
} else {
SWIZZLE_8888_SRC_ALPHA(src32, dst32, srcfmt, dstfmt);
}
*(Uint32 *)dst = dst32;
src += 4;
dst += 4;
}
src += srcskip;
dst += dstskip;
}
}
#endif
#if defined(SDL_NEON_INTRINSICS) && (__ARM_ARCH >= 8)
static void Blit8888to8888PixelSwizzleNEON(SDL_BlitInfo *info)
{
int width = info->dst_w;
int height = info->dst_h;
Uint8 *src = info->src;
int srcskip = info->src_skip;
Uint8 *dst = info->dst;
int dstskip = info->dst_skip;
const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
bool fill_alpha = (!srcfmt->Amask || !dstfmt->Amask);
Uint32 srcAmask, srcAshift;
Uint32 dstAmask, dstAshift;
SDL_Get8888AlphaMaskAndShift(srcfmt, &srcAmask, &srcAshift);
SDL_Get8888AlphaMaskAndShift(dstfmt, &dstAmask, &dstAshift);
// The byte offsets for the start of each pixel
const uint8x16_t mask_offsets = vreinterpretq_u8_u64(vcombine_u64(
vcreate_u64(0x0404040400000000), vcreate_u64(0x0c0c0c0c08080808)));
const uint8x16_t convert_mask = vreinterpretq_u8_u32(vaddq_u32(
vreinterpretq_u32_u8(mask_offsets),
vdupq_n_u32(
((srcfmt->Rshift >> 3) << dstfmt->Rshift) |
((srcfmt->Gshift >> 3) << dstfmt->Gshift) |
((srcfmt->Bshift >> 3) << dstfmt->Bshift) |
((srcAshift >> 3) << dstAshift))));
const uint8x16_t alpha_fill_mask = vreinterpretq_u8_u32(vdupq_n_u32(dstAmask));
while (height--) {
int i = 0;
for (; i + 4 <= width; i += 4) {
// Load 4 src pixels
uint8x16_t src128 = vld1q_u8(src);
// Convert to dst format
src128 = vqtbl1q_u8(src128, convert_mask);
if (fill_alpha) {
// Set the alpha channels of src to 255
src128 = vorrq_u8(src128, alpha_fill_mask);
}
// Save the result
vst1q_u8(dst, src128);
src += 16;
dst += 16;
}
// Process 1 pixel per iteration, max 3 iterations, same calculations as above
for (; i < width; ++i) {
// Top 32-bits will be not used in src32
uint8x8_t src32 = vreinterpret_u8_u32(vld1_dup_u32((Uint32*)src));
// Convert to dst format
src32 = vtbl1_u8(src32, vget_low_u8(convert_mask));
if (fill_alpha) {
// Set the alpha channels of src to 255
src32 = vorr_u8(src32, vget_low_u8(alpha_fill_mask));
}
// Save the result, only low 32-bits
vst1_lane_u32((Uint32*)dst, vreinterpret_u32_u8(src32), 0);
src += 4;
dst += 4;
}
src += srcskip;
dst += dstskip;
}
}
#endif
// Blit_3or4_to_3or4__same_rgb: 3 or 4 bpp, same RGB triplet
static void Blit_3or4_to_3or4__same_rgb(SDL_BlitInfo *info)
{
@@ -2873,6 +3123,23 @@ SDL_BlitFunc SDL_CalculateBlitN(SDL_Surface *surface)
switch (surface->map.info.flags & ~SDL_COPY_RLE_MASK) {
case 0:
if (SDL_PIXELLAYOUT(srcfmt->format) == SDL_PACKEDLAYOUT_8888 &&
SDL_PIXELLAYOUT(dstfmt->format) == SDL_PACKEDLAYOUT_8888) {
#ifdef SDL_AVX2_INTRINSICS
if (SDL_HasAVX2()) {
return Blit8888to8888PixelSwizzleAVX2;
}
#endif
#ifdef SDL_SSE4_1_INTRINSICS
if (SDL_HasSSE41()) {
return Blit8888to8888PixelSwizzleSSE41;
}
#endif
#if defined(SDL_NEON_INTRINSICS) && (__ARM_ARCH >= 8)
return Blit8888to8888PixelSwizzleNEON;
#endif
}
blitfun = NULL;
if (dstfmt->bits_per_pixel > 8) {
Uint32 a_need = NO_ALPHA;
+2 -1
View File
@@ -20,12 +20,13 @@
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_blit.h"
#ifdef SDL_HAVE_BLIT_AUTO
/* *INDENT-OFF* */ // clang-format off
#include "SDL_blit.h"
extern SDL_BlitFuncEntry SDL_GeneratedBlitFuncTable[];
/* *INDENT-ON* */ // clang-format on
+10 -4
View File
@@ -42,6 +42,10 @@ void SDL_CancelClipboardData(Uint32 sequence)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
return;
}
if (sequence && sequence != _this->clipboard_sequence) {
// This clipboard data was already canceled
return;
@@ -62,6 +66,10 @@ bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
return SDL_UninitializedVideo();
}
SDL_FreeClipboardMimeTypes(_this);
if (mime_types && num_mime_types > 0) {
@@ -234,13 +242,11 @@ bool SDL_HasClipboardData(const char *mime_type)
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (!_this) {
SDL_UninitializedVideo();
return false;
return SDL_UninitializedVideo();
}
if (!mime_type) {
SDL_InvalidParamError("mime_type");
return false;
return SDL_InvalidParamError("mime_type");
}
if (_this->HasClipboardData) {
+41 -6
View File
@@ -155,6 +155,7 @@ const char *SDL_GetPixelFormatName(SDL_PixelFormat format)
CASE(SDL_PIXELFORMAT_NV21)
CASE(SDL_PIXELFORMAT_P010)
CASE(SDL_PIXELFORMAT_EXTERNAL_OES)
CASE(SDL_PIXELFORMAT_MJPG)
default:
return "SDL_PIXELFORMAT_UNKNOWN";
@@ -647,7 +648,7 @@ const SDL_PixelFormatDetails *SDL_GetPixelFormatDetails(SDL_PixelFormat format)
SDL_PixelFormatDetails *details;
if (SDL_ShouldInit(&SDL_format_details_init)) {
SDL_format_details = SDL_CreateHashTable(NULL, 8, SDL_HashID, SDL_KeyMatchID, SDL_NukeFreeValue, true, false);
SDL_format_details = SDL_CreateHashTable(0, true, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL);
if (!SDL_format_details) {
SDL_SetInitialized(&SDL_format_details_init, false);
return NULL;
@@ -670,9 +671,13 @@ const SDL_PixelFormatDetails *SDL_GetPixelFormatDetails(SDL_PixelFormat format)
return NULL;
}
if (!SDL_InsertIntoHashTable(SDL_format_details, (const void *)(uintptr_t)format, (void *)details)) {
if (!SDL_InsertIntoHashTable(SDL_format_details, (const void *)(uintptr_t)format, (void *)details, false)) {
SDL_free(details);
return NULL;
// uh...did another thread beat us to inserting this?
if (SDL_FindInHashTable(SDL_format_details, (const void *)(uintptr_t)format, (const void **)&details)) {
return details;
}
return NULL; // oh well.
}
return details;
@@ -687,10 +692,40 @@ void SDL_QuitPixelFormatDetails(void)
}
}
void SDL_Get8888AlphaMaskAndShift(const SDL_PixelFormatDetails *fmt, Uint32 *mask, Uint32 *shift)
{
if (fmt->Amask) {
*mask = fmt->Amask;
*shift = fmt->Ashift;
} else {
*mask = ~(fmt->Rmask | fmt->Gmask | fmt->Bmask);
switch (*mask) {
case 0x000000FF:
*shift = 0;
break;
case 0x0000FF00:
*shift = 8;
break;
case 0x00FF0000:
*shift = 16;
break;
case 0xFF000000:
*shift = 24;
break;
default:
// Should never happen
*shift = 0;
break;
}
}
}
SDL_Colorspace SDL_GetDefaultColorspaceForFormat(SDL_PixelFormat format)
{
if (SDL_ISPIXELFORMAT_FOURCC(format)) {
if (format == SDL_PIXELFORMAT_P010) {
if (format == SDL_PIXELFORMAT_MJPG) {
return SDL_COLORSPACE_SRGB;
} else if (format == SDL_PIXELFORMAT_P010) {
return SDL_COLORSPACE_HDR10;
} else {
return SDL_COLORSPACE_YUV_DEFAULT;
@@ -1130,7 +1165,7 @@ Uint8 SDL_LookupRGBAColor(SDL_HashTable *palette_map, Uint32 pixel, const SDL_Pa
Uint8 b = (Uint8)((pixel >> 8) & 0xFF);
Uint8 a = (Uint8)((pixel >> 0) & 0xFF);
color_index = SDL_FindColor(pal, r, g, b, a);
SDL_InsertIntoHashTable(palette_map, (const void *)(uintptr_t)pixel, (const void *)(uintptr_t)color_index);
SDL_InsertIntoHashTable(palette_map, (const void *)(uintptr_t)pixel, (const void *)(uintptr_t)color_index, true);
}
return color_index;
}
@@ -1496,7 +1531,7 @@ bool SDL_MapSurface(SDL_Surface *src, SDL_Surface *dst)
} else {
if (SDL_ISPIXELFORMAT_INDEXED(dstfmt->format)) {
// BitField --> Palette
map->info.palette_map = SDL_CreateHashTable(NULL, 32, SDL_HashID, SDL_KeyMatchID, NULL, false, false);
map->info.palette_map = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
} else {
// BitField --> BitField
if (srcfmt == dstfmt) {
+1 -1
View File
@@ -29,7 +29,7 @@
// Pixel format functions
extern bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, size_t *size, size_t *pitch, bool minimalPitch);
extern void SDL_Get8888AlphaMaskAndShift(const SDL_PixelFormatDetails *fmt, Uint32 *mask, Uint32 *shift);
extern SDL_Colorspace SDL_GetDefaultColorspaceForFormat(SDL_PixelFormat pixel_format);
extern void SDL_QuitPixelFormatDetails(void);
+121
View File
@@ -0,0 +1,121 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_stb_c.h"
// We currently only support JPEG, but we could add other image formats if we wanted
#ifdef SDL_HAVE_STB
#define malloc SDL_malloc
#define realloc SDL_realloc
#define free SDL_free
#undef memcpy
#define memcpy SDL_memcpy
#undef memset
#define memset SDL_memset
#undef strcmp
#define strcmp SDL_strcmp
#undef strncmp
#define strncmp SDL_strncmp
#define strtol SDL_strtol
#define abs SDL_abs
#define pow SDL_pow
#define ldexp SDL_scalbn
#define STB_IMAGE_STATIC
#define STBI_NO_THREAD_LOCALS
#define STBI_FAILURE_USERMSG
#if defined(SDL_NEON_INTRINSICS)
#define STBI_NEON
#endif
#define STBI_ONLY_JPEG
#define STBI_NO_GIF
#define STBI_NO_PNG
#define STBI_NO_HDR
#define STBI_NO_LINEAR
#define STBI_NO_ZLIB
#define STBI_NO_STDIO
#define STBI_ASSERT SDL_assert
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#undef memset
#endif
#ifdef SDL_HAVE_STB
static bool SDL_ConvertPixels_MJPG_to_NV12(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
{
int w = 0, h = 0, format = 0;
stbi__context s;
stbi__start_mem(&s, src, src_pitch);
stbi__result_info ri;
SDL_zero(ri);
ri.bits_per_channel = 8;
ri.channel_order = STBI_ORDER_RGB;
ri.num_channels = 0;
stbi__nv12 nv12;
nv12.w = width;
nv12.h = height;
nv12.pitch = dst_pitch;
nv12.y = (stbi_uc *)dst;
nv12.uv = nv12.y + (nv12.h * nv12.pitch);
void *pixels = stbi__jpeg_load(&s, &w, &h, &format, 4, &nv12, &ri);
if (!pixels) {
return false;
}
return true;
}
#endif // SDL_HAVE_STB
bool SDL_ConvertPixels_STB(int width, int height,
SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
{
#ifdef SDL_HAVE_STB
if (src_format == SDL_PIXELFORMAT_MJPG && dst_format == SDL_PIXELFORMAT_NV12) {
return SDL_ConvertPixels_MJPG_to_NV12(width, height, src, src_pitch, dst, dst_pitch);
}
bool result;
int w = 0, h = 0, format = 0;
int len = (src_format == SDL_PIXELFORMAT_MJPG) ? src_pitch : (height * src_pitch);
void *pixels = stbi_load_from_memory(src, len, &w, &h, &format, 4);
if (!pixels) {
return false;
}
if (w == width && h == height) {
result = SDL_ConvertPixelsAndColorspace(w, h, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, pixels, width * 4, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
} else {
result = SDL_SetError("Expected image size %dx%d, actual size %dx%d", width, height, w, h);
}
stbi_image_free(pixels);
return result;
#else
return SDL_SetError("SDL not built with STB image support");
#endif
}
+31
View File
@@ -0,0 +1,31 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_stb_c_h_
#define SDL_stb_c_h_
#include "SDL_internal.h"
// Image conversion functions
extern bool SDL_ConvertPixels_STB(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
#endif // SDL_stb_c_h_
+54 -12
View File
@@ -24,6 +24,7 @@
#include "SDL_video_c.h"
#include "SDL_RLEaccel_c.h"
#include "SDL_pixels_c.h"
#include "SDL_stb_c.h"
#include "SDL_yuv_c.h"
#include "../render/SDL_sysrender.h"
@@ -103,6 +104,11 @@ bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, siz
}
if (SDL_ISPIXELFORMAT_FOURCC(format)) {
if (format == SDL_PIXELFORMAT_MJPG) {
// We don't know in advance what it will be, we'll figure it out later.
return true;
}
if (!SDL_CalculateYUVSize(format, width, height, &sz, &p)) {
// Overflow...
return false;
@@ -198,6 +204,11 @@ SDL_Surface *SDL_CreateSurface(int width, int height, SDL_PixelFormat format)
return NULL;
}
if (format == SDL_PIXELFORMAT_UNKNOWN) {
SDL_InvalidParamError("format");
return NULL;
}
if (!SDL_CalculateSurfaceSize(format, width, height, &size, &pitch, false /* not minimal pitch */)) {
// Overflow...
return NULL;
@@ -213,7 +224,7 @@ SDL_Surface *SDL_CreateSurface(int width, int height, SDL_PixelFormat format)
return NULL;
}
if (surface->w && surface->h) {
if (surface->w && surface->h && format != SDL_PIXELFORMAT_MJPG) {
surface->flags &= ~SDL_SURFACE_PREALLOCATED;
surface->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), size);
if (!surface->pixels) {
@@ -244,6 +255,11 @@ SDL_Surface *SDL_CreateSurfaceFrom(int width, int height, SDL_PixelFormat format
return NULL;
}
if (format == SDL_PIXELFORMAT_UNKNOWN) {
SDL_InvalidParamError("format");
return NULL;
}
if (pitch == 0 && !pixels) {
// The application will fill these in later with valid values
} else {
@@ -1085,9 +1101,9 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac
int dst_w, dst_h;
// Make sure the surfaces aren't locked
if (!SDL_SurfaceValid(src)) {
if (!SDL_SurfaceValid(src) || !src->pixels) {
return SDL_InvalidParamError("src");
} else if (!SDL_SurfaceValid(dst)) {
} else if (!SDL_SurfaceValid(dst) || !dst->pixels) {
return SDL_InvalidParamError("dst");
} else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
return SDL_SetError("Surfaces must not be locked during blit");
@@ -1117,6 +1133,13 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac
return SDL_BlitSurface(src, srcrect, dst, dstrect);
}
if (src_w == 0) {
src_w = 1;
}
if (src_h == 0) {
src_h = 1;
}
scaling_w = (double)dst_w / src_w;
scaling_h = (double)dst_h / src_h;
@@ -1224,7 +1247,7 @@ bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surfac
// Clip again
SDL_GetRectIntersection(clip_rect, &final_dst, &final_dst);
if (final_dst.w == 0 || final_dst.h == 0 ||
if (final_dst.w <= 0 || final_dst.h <= 0 ||
final_src.w < 0 || final_src.h < 0) {
// No-op.
return true;
@@ -1916,7 +1939,18 @@ SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelForm
SDL_SetSurfaceColorspace(convert, colorspace);
if (SDL_ISPIXELFORMAT_FOURCC(format) || SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
if (!SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format, colorspace, props, convert->pixels, convert->pitch)) {
if (surface->format == SDL_PIXELFORMAT_MJPG && format == SDL_PIXELFORMAT_MJPG) {
// Just do a straight pixel copy of the JPEG image
size_t size = (size_t)surface->pitch;
convert->pixels = SDL_malloc(size);
if (!convert->pixels) {
goto error;
}
convert->flags &= ~SDL_SURFACE_PREALLOCATED;
convert->pitch = surface->pitch;
SDL_memcpy(convert->pixels, surface->pixels, size);
} else if (!SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format, colorspace, props, convert->pixels, convert->pitch)) {
goto error;
}
@@ -2277,6 +2311,10 @@ bool SDL_ConvertPixelsAndColorspace(int width, int height,
dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format);
}
if (src_format == SDL_PIXELFORMAT_MJPG) {
return SDL_ConvertPixels_STB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
}
#ifdef SDL_HAVE_YUV
if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
@@ -2293,13 +2331,17 @@ bool SDL_ConvertPixelsAndColorspace(int width, int height,
// Fast path for same format copy
if (src_format == dst_format && src_colorspace == dst_colorspace) {
int i;
const int bpp = SDL_BYTESPERPIXEL(src_format);
width *= bpp;
for (i = height; i--;) {
SDL_memcpy(dst, src, width);
src = (const Uint8 *)src + src_pitch;
dst = (Uint8 *)dst + dst_pitch;
if (src_pitch == dst_pitch) {
SDL_memcpy(dst, src, height * src_pitch);
} else {
int i;
const int bpp = SDL_BYTESPERPIXEL(src_format);
width *= bpp;
for (i = height; i--;) {
SDL_memcpy(dst, src, width);
src = (const Uint8 *)src + src_pitch;
dst = (Uint8 *)dst + dst_pitch;
}
}
return true;
}
+1
View File
@@ -83,6 +83,7 @@ struct SDL_Surface
// Surface functions
extern bool SDL_SurfaceValid(SDL_Surface *surface);
extern void SDL_UpdateSurfaceLockFlag(SDL_Surface *surface);
extern bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, size_t *size, size_t *pitch, bool minimalPitch);
extern float SDL_GetDefaultSDRWhitePoint(SDL_Colorspace colorspace);
extern float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace);
extern float SDL_GetDefaultHDRHeadroom(SDL_Colorspace colorspace);
+10 -2
View File
@@ -103,6 +103,7 @@ struct SDL_Window
bool restore_on_show; // Child was hidden recursively by the parent, restore when shown.
bool last_position_pending; // This should NOT be cleared by the backend, as it is used for fullscreen positioning.
bool last_size_pending; // This should be cleared by the backend if the new size cannot be applied.
bool constrain_popup;
bool is_destroying;
bool is_dropping; // drag/drop in progress, expecting SDL_SendDropComplete().
@@ -129,6 +130,9 @@ struct SDL_Window
SDL_WindowData *internal;
// If a toplevel window, holds the current keyboard focus for grabbing popups.
SDL_Window *keyboard_focus;
SDL_Window *prev;
SDL_Window *next;
@@ -163,6 +167,8 @@ struct SDL_VideoDisplay
float content_scale;
SDL_HDROutputProperties HDR;
// This is true if we are fullscreen or fullscreen is pending
bool fullscreen_active;
SDL_Window *fullscreen_window;
SDL_VideoDevice *device;
@@ -394,8 +400,7 @@ struct SDL_VideoDevice
bool checked_texture_framebuffer;
bool is_dummy;
bool suspend_screensaver;
SDL_Window *wakeup_window;
SDL_Mutex *wakeup_lock; // Initialized only if WaitEventTimeout/SendWakeupEvent are supported
void *wakeup_window;
int num_displays;
SDL_VideoDisplay **displays;
SDL_Rect desktop_bounds;
@@ -504,6 +509,7 @@ typedef struct VideoBootStrap
const char *desc;
SDL_VideoDevice *(*create)(void);
bool (*ShowMessageBox)(const SDL_MessageBoxData *messageboxdata, int *buttonID); // can be done without initializing backend!
bool is_preferred;
} VideoBootStrap;
// Not all of these are available in a given build. Use #ifdefs, etc.
@@ -563,6 +569,8 @@ extern bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags);
extern bool SDL_HasWindows(void);
extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y);
extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y);
extern bool SDL_ShouldFocusPopup(SDL_Window *window);
extern bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus);
extern void SDL_OnDisplayAdded(SDL_VideoDisplay *display);
extern void SDL_OnDisplayMoved(SDL_VideoDisplay *display);
+106 -13
View File
@@ -171,9 +171,10 @@ static VideoBootStrap *bootstrap[] = {
}
#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA)
// Support for macOS fullscreen spaces
// Support for macOS fullscreen spaces, etc.
extern bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window);
extern bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, bool state, bool blocking);
extern bool Cocoa_IsShowingModalDialog(SDL_Window *window);
#endif
#ifdef SDL_VIDEO_DRIVER_UIKIT
@@ -596,6 +597,7 @@ const char *SDL_GetVideoDriver(int index)
if (index >= 0 && index < SDL_GetNumVideoDrivers()) {
return deduped_bootstrap[index]->name;
}
SDL_InvalidParamError("index");
return NULL;
}
@@ -654,7 +656,8 @@ bool SDL_VideoInit(const char *driver_name)
: SDL_strlen(driver_attempt);
for (i = 0; bootstrap[i]; ++i) {
if ((driver_attempt_len == SDL_strlen(bootstrap[i]->name)) &&
if (!bootstrap[i]->is_preferred &&
(driver_attempt_len == SDL_strlen(bootstrap[i]->name)) &&
(SDL_strncasecmp(bootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) {
video = bootstrap[i]->create();
if (video) {
@@ -1433,6 +1436,11 @@ void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode
{
SDL_DisplayMode last_mode;
if (display->fullscreen_active) {
// This is a temporary mode change, don't save the desktop mode
return;
}
SDL_copyp(&last_mode, &display->desktop_mode);
if (display->desktop_mode.internal) {
@@ -1724,12 +1732,25 @@ SDL_VideoDisplay *SDL_GetVideoDisplayForFullscreenWindow(SDL_Window *window)
return SDL_GetVideoDisplay(displayID);
}
#define SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY "sdl2-compat.window.preferred_fullscreen_display"
SDL_DisplayID SDL_GetDisplayForWindow(SDL_Window *window)
{
SDL_DisplayID displayID = 0;
CHECK_WINDOW_MAGIC(window, 0);
/* sdl2-compat calls this function to get a display on which to make the window fullscreen,
* so pass it the preferred fullscreen display ID in a property.
*/
SDL_PropertiesID window_props = SDL_GetWindowProperties(window);
SDL_VideoDisplay *fs_display = SDL_GetVideoDisplayForFullscreenWindow(window);
if (fs_display) {
SDL_SetNumberProperty(window_props, SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY, fs_display->id);
} else {
SDL_ClearProperty(window_props, SDL_PROP_SDL2_COMPAT_WINDOW_PREFERRED_FULLSCREEN_DISPLAY);
}
// An explicit fullscreen display overrides all
if (window->flags & SDL_WINDOW_FULLSCREEN) {
displayID = window->current_fullscreen_mode.displayID;
@@ -1942,6 +1963,8 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b
SDL_MinimizeWindow(display->fullscreen_window);
}
display->fullscreen_active = window->fullscreen_exclusive;
if (!SDL_SetDisplayModeForDisplay(display, mode)) {
goto error;
}
@@ -1959,6 +1982,7 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
}
} else if (ret == SDL_FULLSCREEN_FAILED) {
display->fullscreen_active = false;
goto error;
}
}
@@ -1974,18 +1998,24 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b
* This is also unnecessary on Cocoa, Wayland, Win32, and X11 (will send SDL_EVENT_WINDOW_RESIZED).
*/
if (!SDL_SendsFullscreenDimensions(_this)) {
SDL_Rect displayRect;
if (mode) {
mode_w = mode->w;
mode_h = mode->h;
SDL_GetDisplayBounds(mode->displayID, &displayRect);
} else {
mode_w = display->desktop_mode.w;
mode_h = display->desktop_mode.h;
SDL_GetDisplayBounds(display->id, &displayRect);
}
if (window->w != mode_w || window->h != mode_h) {
resized = true;
}
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, displayRect.x, displayRect.y);
if (resized) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, mode_w, mode_h);
} else {
@@ -2003,6 +2033,8 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b
// Restore the desktop mode
if (display) {
display->fullscreen_active = false;
SDL_SetDisplayModeForDisplay(display, NULL);
}
if (commit) {
@@ -2032,6 +2064,7 @@ bool SDL_UpdateFullscreenMode(SDL_Window *window, SDL_FullscreenOp fullscreen, b
}
if (!SDL_SendsFullscreenDimensions(_this)) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, window->windowed.x, window->windowed.y);
if (resized) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->windowed.w, window->windowed.h);
} else {
@@ -2471,6 +2504,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
window->is_destroying = false;
window->last_displayID = SDL_GetDisplayForWindow(window);
window->external_graphics_context = external_graphics_context;
window->constrain_popup = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN, true);
if (_this->windows) {
_this->windows->prev = window;
@@ -2481,7 +2515,9 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
SDL_UpdateWindowHierarchy(window, parent);
if (_this->CreateSDLWindow && !_this->CreateSDLWindow(_this, window, props)) {
PUSH_SDL_ERROR()
SDL_DestroyWindow(window);
POP_SDL_ERROR()
return NULL;
}
@@ -2729,7 +2765,7 @@ SDL_Window *SDL_GetWindowFromID(SDL_WindowID id)
}
}
}
SDL_SetError("Invalid window ID"); \
SDL_SetError("Invalid window ID");
return NULL;
}
@@ -2765,9 +2801,16 @@ bool SDL_SetWindowTitle(SDL_Window *window, const char *title)
if (title == window->title) {
return true;
}
if (!title) {
title = "";
}
if (window->title && SDL_strcmp(title, window->title) == 0) {
return true;
}
SDL_free(window->title);
window->title = SDL_strdup(title ? title : "");
window->title = SDL_strdup(title);
if (_this->SetWindowTitle) {
_this->SetWindowTitle(_this, window);
@@ -2898,11 +2941,12 @@ bool SDL_GetWindowPosition(SDL_Window *window, int *x, int *y)
}
}
} else {
const bool use_pending = (window->flags & SDL_WINDOW_HIDDEN) && window->last_position_pending;
if (x) {
*x = window->x;
*x = use_pending ? window->pending.x : window->x;
}
if (y) {
*y = window->y;
*y = use_pending ? window->pending.y : window->y;
}
}
return true;
@@ -3619,6 +3663,10 @@ bool SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent)
CHECK_WINDOW_NOT_POPUP(parent, false);
}
if (window == parent) {
return SDL_SetError("Cannot set the parent of a window to itself.");
}
if (!_this->SetWindowParent) {
return SDL_Unsupported();
}
@@ -3664,6 +3712,48 @@ bool SDL_SetWindowModal(SDL_Window *window, bool modal)
return _this->SetWindowModal(_this, window, modal);
}
bool SDL_ShouldRelinquishPopupFocus(SDL_Window *window, SDL_Window **new_focus)
{
SDL_Window *focus = window->parent;
bool set_focus = !!(window->flags & SDL_WINDOW_INPUT_FOCUS);
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed, and can grab the keyboard focus.
while (SDL_WINDOW_IS_POPUP(focus) && ((focus->flags & SDL_WINDOW_NOT_FOCUSABLE) || focus->is_hiding || focus->is_destroying)) {
focus = focus->parent;
// If some window in the chain currently had focus, set it to the new lowest-level window.
if (!set_focus) {
set_focus = !!(focus->flags & SDL_WINDOW_INPUT_FOCUS);
}
}
*new_focus = focus;
return set_focus;
}
bool SDL_ShouldFocusPopup(SDL_Window *window)
{
SDL_Window *toplevel_parent;
for (toplevel_parent = window->parent; SDL_WINDOW_IS_POPUP(toplevel_parent); toplevel_parent = toplevel_parent->parent) {
}
SDL_Window *current_focus = toplevel_parent->keyboard_focus;
bool found_higher_focus = false;
/* Traverse the window tree from the currently focused window to the toplevel parent and see if we encounter
* the new focus request. If the new window is found, a higher-level window already has focus.
*/
SDL_Window *w;
for (w = current_focus; w != toplevel_parent; w = w->parent) {
if (w == window) {
found_higher_focus = true;
break;
}
}
return !found_higher_focus || w == toplevel_parent;
}
bool SDL_SetWindowFocusable(SDL_Window *window, bool focusable)
{
CHECK_WINDOW_MAGIC(window, false);
@@ -4082,7 +4172,9 @@ static bool SDL_ShouldMinimizeOnFocusLoss(SDL_Window *window)
#if defined(SDL_PLATFORM_MACOS) && defined(SDL_VIDEO_DRIVER_COCOA)
if (SDL_strcmp(_this->name, "cocoa") == 0) { // don't do this for X11, etc
if (Cocoa_IsWindowInFullscreenSpace(window)) {
if (Cocoa_IsShowingModalDialog(window)) {
return false; // modal system dialogs can live over fullscreen windows, don't minimize.
} else if (Cocoa_IsWindowInFullscreenSpace(window)) {
return false;
}
}
@@ -4239,9 +4331,7 @@ void SDL_DestroyWindow(SDL_Window *window)
_this->current_glwin = NULL;
}
if (_this->wakeup_window == window) {
_this->wakeup_window = NULL;
}
SDL_CompareAndSwapAtomicPointer(&_this->wakeup_window, window, NULL);
// Now invalidate magic
SDL_SetObjectValid(window, SDL_OBJECT_TYPE_WINDOW, false);
@@ -4994,8 +5084,7 @@ bool SDL_GL_GetAttribute(SDL_GLAttr attr, int *value)
}
if (fbo_type != GL_NONE) {
glGetFramebufferAttachmentParameterivFunc(GL_FRAMEBUFFER, attachment, attachmentattrib, (GLint *)value);
}
else {
} else {
*value = 0;
}
if (glBindFramebufferFunc && (current_fbo != 0)) {
@@ -5226,7 +5315,7 @@ bool SDL_GL_SwapWindow(SDL_Window *window)
bool SDL_GL_DestroyContext(SDL_GLContext context)
{
if (!_this) {
return SDL_UninitializedVideo(); \
return SDL_UninitializedVideo();
}
if (!context) {
return SDL_InvalidParamError("context");
@@ -5378,6 +5467,10 @@ bool SDL_GetTextInputMultiline(SDL_PropertiesID props)
static bool AutoShowingScreenKeyboard(void)
{
const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
if (!hint) {
// Steam will eventually have smarts about whether a keyboard is active, so always request the on-screen keyboard on Steam Deck
hint = SDL_GetHint("SteamDeck");
}
if (((!hint || SDL_strcasecmp(hint, "auto") == 0) && !SDL_HasKeyboard()) ||
SDL_GetStringBoolean(hint, false)) {
return true;
+31 -13
View File
@@ -1628,26 +1628,44 @@ static bool SDL_ConvertPixels_SwapNV_std(int width, int height, const void *src,
const int UVwidth = (width + 1) / 2;
const int UVheight = (height + 1) / 2;
const int srcUVPitch = ((src_pitch + 1) / 2) * 2;
const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2) / sizeof(Uint16);
const int dstUVPitch = ((dst_pitch + 1) / 2) * 2;
const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2) / sizeof(Uint16);
const Uint16 *srcUV;
Uint16 *dstUV;
// Skip the Y plane
src = (const Uint8 *)src + height * src_pitch;
dst = (Uint8 *)dst + height * dst_pitch;
srcUV = (const Uint16 *)src;
dstUV = (Uint16 *)dst;
y = UVheight;
while (y--) {
x = UVwidth;
while (x--) {
*dstUV++ = SDL_Swap16(*srcUV++);
bool aligned = (((uintptr_t)src | (uintptr_t)dst) & 1) == 0;
if (aligned) {
const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2) / sizeof(Uint16);
const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2) / sizeof(Uint16);
const Uint16 *srcUV = (const Uint16 *)src;
Uint16 *dstUV = (Uint16 *)dst;
y = UVheight;
while (y--) {
x = UVwidth;
while (x--) {
*dstUV++ = SDL_Swap16(*srcUV++);
}
srcUV += srcUVPitchLeft;
dstUV += dstUVPitchLeft;
}
} else {
const int srcUVPitchLeft = (srcUVPitch - UVwidth * 2);
const int dstUVPitchLeft = (dstUVPitch - UVwidth * 2);
const Uint8 *srcUV = (const Uint8 *)src;
Uint8 *dstUV = (Uint8 *)dst;
y = UVheight;
while (y--) {
x = UVwidth;
while (x--) {
Uint8 u = *srcUV++;
Uint8 v = *srcUV++;
*dstUV++ = v;
*dstUV++ = u;
}
srcUV += srcUVPitchLeft;
dstUV += dstUVPitchLeft;
}
srcUV += srcUVPitchLeft;
dstUV += dstUVPitchLeft;
}
return true;
}
@@ -157,7 +157,8 @@ static SDL_VideoDevice *Android_CreateDevice(void)
VideoBootStrap Android_bootstrap = {
ANDROID_VID_DRIVER_NAME, "SDL Android video driver",
Android_CreateDevice,
Android_ShowMessageBox
Android_ShowMessageBox,
false
};
bool Android_VideoInit(SDL_VideoDevice *_this)
@@ -158,6 +158,17 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this)
@autoreleasepool {
SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal;
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
// SetClipboardText specialization so text is available after the app quits
if (_this->clipboard_callback && _this->num_clipboard_mime_types == 1) {
if (SDL_strncmp(_this->clipboard_mime_types[0], "text/plain;charset=utf-8", 24) == 0) {
[pasteboard declareTypes:@[ NSPasteboardTypeString ] owner:nil];
[pasteboard setString:@((char *)_this->clipboard_userdata) forType:NSPasteboardTypeString];
data.clipboard_count = [pasteboard changeCount];
return true;
}
}
NSPasteboardItem *newItem = [NSPasteboardItem new];
NSMutableArray *utiTypes = [NSMutableArray new];
Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata];
+14 -3
View File
@@ -311,15 +311,26 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent)
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true))
if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true)) {
return;
}
/* The menu bar of SDL apps which don't have the typical .app bundle
* structure fails to work the first time a window is created (until it's
* de-focused and re-focused), if this call is in Cocoa_RegisterApp instead
* of here. https://bugzilla.libsdl.org/show_bug.cgi?id=3051
* of here. https://github.com/libsdl-org/SDL/issues/1913
*/
if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) {
/* this apparently became unnecessary on macOS 14.0, and will addition pop up a
hidden dock if you're moving the mouse during launch, so change the default
behaviour there. https://github.com/libsdl-org/SDL/issues/10340
(13.6 still needs it, presumably 13.7 does, too.) */
bool background_app_default = false;
if (@available(macOS 14.0, *)) {
background_app_default = true; /* by default, don't explicitly activate the dock and then us again to force to foreground */
}
if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, background_app_default)) {
// Get more aggressive for Catalina: activate the Dock first so we definitely reset all activation state.
for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
[i activateWithOptions:NSApplicationActivateIgnoringOtherApps];
@@ -30,8 +30,11 @@
#include <Carbon/Carbon.h>
// #define DEBUG_IME NSLog
#if 0
#define DEBUG_IME NSLog
#else
#define DEBUG_IME(...)
#endif
@interface SDL3TranslatorResponder : NSView <NSTextInputClient>
{
@@ -133,8 +136,12 @@
// This key event was consumed by the IME
[self clearPendingKey];
NSUInteger utf32SelectedRangeLocation = [[aString substringToIndex:selectedRange.location] lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
NSUInteger utf32SelectionRangeEnd = [[aString substringToIndex:(selectedRange.location + selectedRange.length)] lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
NSUInteger utf32SelectionRangeLength = utf32SelectionRangeEnd - utf32SelectedRangeLocation;
SDL_SendEditingText([aString UTF8String],
(int)selectedRange.location, (int)selectedRange.length);
(int)utf32SelectedRangeLocation, (int)utf32SelectionRangeLength);
DEBUG_IME(@"setMarkedText: %@, (%d, %d) replacement range (%d, %d)", _markedText,
(int)selectedRange.location, (int)selectedRange.length,
@@ -26,6 +26,8 @@
*/
#include "SDL_internal.h"
#include "../../events/SDL_windowevents_c.h"
#import "SDL_cocoametalview.h"
#if defined(SDL_VIDEO_DRIVER_COCOA) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
@@ -88,7 +90,7 @@ static bool SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
self.layer.opaque = opaque;
SDL_AddEventWatch(SDL_MetalViewEventWatch, (__bridge void *)(self));
SDL_AddWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
[self updateDrawableSize];
}
@@ -98,7 +100,7 @@ static bool SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
- (void)dealloc
{
SDL_RemoveEventWatch(SDL_MetalViewEventWatch, (__bridge void *)(self));
SDL_RemoveWindowEventWatch(SDL_WINDOW_EVENT_WATCH_EARLY, SDL_MetalViewEventWatch, (__bridge void *)(self));
}
- (NSInteger)tag
+19 -15
View File
@@ -556,22 +556,24 @@ bool Cocoa_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, S
bool Cocoa_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
{
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
NSScreen *screen = GetNSScreenForDisplayID(displaydata->display);
@autoreleasepool {
SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal;
NSScreen *screen = GetNSScreenForDisplayID(displaydata->display);
if (screen == nil) {
return SDL_SetError("Couldn't get NSScreen for display");
if (screen == nil) {
return SDL_SetError("Couldn't get NSScreen for display");
}
{
const NSRect frame = [screen visibleFrame];
rect->x = (int)frame.origin.x;
rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height);
rect->w = (int)frame.size.width;
rect->h = (int)frame.size.height;
}
return true;
}
{
const NSRect frame = [screen visibleFrame];
rect->x = (int)frame.origin.x;
rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height);
rect->w = (int)frame.size.width;
rect->h = (int)frame.size.height;
}
return true;
}
bool Cocoa_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
@@ -644,7 +646,9 @@ static CGError SetDisplayModeForDisplay(CGDirectDisplayID display, SDL_DisplayMo
result = CGDisplaySetDisplayMode(display, moderef, NULL);
if (result == kCGErrorSuccess) {
// If this mode works, try it first next time.
CFArrayExchangeValuesAtIndices(data->modes, i, 0);
if (i > 0) {
CFArrayExchangeValuesAtIndices(data->modes, i, 0);
}
break;
}
}
+21 -5
View File
@@ -447,13 +447,29 @@ void Cocoa_HandleMouseEvent(SDL_VideoDevice *_this, NSEvent *event)
float deltaX, deltaY;
bool seenWarp;
switch ([event type]) {
case NSEventTypeMouseEntered:
Cocoa_MouseFocus = [event window];
return;
case NSEventTypeMouseExited:
// All events except NSEventTypeMouseExited can only happen if the window
// has mouse focus, so we'll always set the focus even if we happen to miss
// NSEventTypeMouseEntered, which apparently happens if the window is
// created under the mouse on macOS 12.7. But, only set the focus if
// the event acutally has a non-NULL window, otherwise what would happen
// is that after an NSEventTypeMouseEntered there would sometimes be
// NSEventTypeMouseMoved without a window causing us to suppress subsequent
// mouse move events.
NSEventType event_type = [event type];
if (event_type == NSEventTypeMouseExited) {
Cocoa_MouseFocus = NULL;
} else {
if ([event window] != NULL) {
Cocoa_MouseFocus = [event window];
}
}
switch (event_type) {
case NSEventTypeMouseEntered:
case NSEventTypeMouseExited:
// Focus is handled above
return;
case NSEventTypeMouseMoved:
case NSEventTypeLeftMouseDragged:
case NSEventTypeRightMouseDragged:
+2 -5
View File
@@ -49,9 +49,6 @@ static void Cocoa_VideoQuit(SDL_VideoDevice *_this);
static void Cocoa_DeleteDevice(SDL_VideoDevice *device)
{
@autoreleasepool {
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
CFBridgingRelease(device->internal);
SDL_free(device);
}
@@ -81,7 +78,6 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
return NULL;
}
device->internal = (SDL_VideoData *)CFBridgingRetain(data);
device->wakeup_lock = SDL_CreateMutex();
device->system_theme = Cocoa_GetSystemTheme();
// Set the function pointers
@@ -195,7 +191,8 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
VideoBootStrap COCOA_bootstrap = {
"cocoa", "SDL Cocoa video driver",
Cocoa_CreateDevice,
Cocoa_ShowMessageBox
Cocoa_ShowMessageBox,
false
};
static bool Cocoa_VideoInit(SDL_VideoDevice *_this)
+1 -1
View File
@@ -146,13 +146,13 @@ typedef enum
@property(nonatomic) BOOL was_zoomed;
@property(nonatomic) NSInteger window_number;
@property(nonatomic) NSInteger flash_request;
@property(nonatomic) SDL_Window *keyboard_focus;
@property(nonatomic) SDL3Cocoa_WindowListener *listener;
@property(nonatomic) NSModalSession modal_session;
@property(nonatomic) SDL_CocoaVideoData *videodata;
@property(nonatomic) bool pending_size;
@property(nonatomic) bool pending_position;
@property(nonatomic) bool border_toggled;
@property(nonatomic) bool has_modal_dialog;
#ifdef SDL_VIDEO_OPENGL_EGL
@property(nonatomic) EGLSurface egl_surface;
+202 -116
View File
@@ -411,6 +411,39 @@ bool Cocoa_IsWindowInFullscreenSpace(SDL_Window *window)
}
}
bool Cocoa_IsWindowZoomed(SDL_Window *window)
{
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
NSWindow *nswindow = data.nswindow;
bool zoomed = false;
// isZoomed always returns true if the window is not resizable or the window is fullscreen
if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] &&
!(window->flags & SDL_WINDOW_FULLSCREEN) && !Cocoa_IsWindowInFullscreenSpace(window)) {
// If we are at our desired floating area, then we're not zoomed
bool floating = (window->x == window->floating.x &&
window->y == window->floating.y &&
window->w == window->floating.w &&
window->h == window->floating.h);
if (!floating) {
zoomed = true;
}
}
return zoomed;
}
bool Cocoa_IsShowingModalDialog(SDL_Window *window)
{
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
return data.has_modal_dialog;
}
void Cocoa_SetWindowHasModalDialog(SDL_Window *window, bool has_modal)
{
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
data.has_modal_dialog = has_modal;
}
typedef enum CocoaMenuVisibility
{
COCOA_MENU_VISIBILITY_AUTO = 0,
@@ -686,13 +719,10 @@ static SDL_Window *GetParentToplevelWindow(SDL_Window *window)
static void Cocoa_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
{
SDL_Window *toplevel = GetParentToplevelWindow(window);
SDL_CocoaWindowData *toplevel_data;
toplevel_data = (__bridge SDL_CocoaWindowData *)toplevel->internal;
toplevel_data.keyboard_focus = window;
toplevel->keyboard_focus = window;
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
SDL_SetKeyboardFocus(window);
SDL_SetKeyboardFocus(window);
}
}
@@ -847,14 +877,16 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
return NO; // we only allow you to make a Space on fullscreen desktop windows.
} else if (!state && window->last_fullscreen_exclusive_display) {
return NO; // we only handle leaving the Space on windows that were previously fullscreen desktop.
} else if (state == isFullscreenSpace) {
} else if (state == isFullscreenSpace && !inFullscreenTransition) {
return YES; // already there.
}
if (inFullscreenTransition) {
if (state) {
[self clearPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
[self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
} else {
[self clearPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
[self addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
}
return YES;
@@ -882,6 +914,11 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
pendingWindowOperation &= ~operation;
}
- (void)clearAllPendingWindowOperations
{
pendingWindowOperation = PENDING_OPERATION_NONE;
}
- (void)addPendingWindowOperation:(PendingWindowOperation)operation
{
pendingWindowOperation |= operation;
@@ -894,7 +931,8 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
- (BOOL)hasPendingWindowOperation
{
return pendingWindowOperation != PENDING_OPERATION_NONE ||
// A pending zoom may be deferred until leaving fullscreen, so don't block on it.
return (pendingWindowOperation & ~PENDING_OPERATION_ZOOM) != PENDING_OPERATION_NONE ||
isMiniaturizing || inFullscreenTransition;
}
@@ -1161,21 +1199,27 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
ScheduleContextUpdates(_data);
/* isZoomed always returns true if the window is not resizable
* and fullscreen windows are considered zoomed.
/* The OS can resize the window automatically if the display density
* changes while the window is miniaturized or hidden.
*/
if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] &&
!(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) {
zoomed = YES;
} else {
zoomed = NO;
}
if (!zoomed) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
} else if (zoomed) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
if ([self windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) {
[nswindow miniaturize:nil];
if ([nswindow isVisible])
{
/* isZoomed always returns true if the window is not resizable
* and fullscreen windows are considered zoomed.
*/
if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed] &&
!(window->flags & SDL_WINDOW_FULLSCREEN) && ![self isInFullscreenSpace]) {
zoomed = YES;
} else {
zoomed = NO;
}
if (!zoomed) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
} else {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
if ([self windowOperationIsPending:PENDING_OPERATION_MINIMIZE]) {
[nswindow miniaturize:nil];
}
}
}
@@ -1208,8 +1252,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
// Always send restored before maximized.
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
// isZoomed always returns true if the window is not resizable.
if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) {
if (Cocoa_IsWindowZoomed(_data.window)) {
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
}
@@ -1224,7 +1267,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
// We're going to get keyboard events, since we're key.
// This needs to be done before restoring the relative mouse mode.
Cocoa_SetKeyboardFocus(_data.keyboard_focus ? _data.keyboard_focus : window, true);
Cocoa_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window, true);
// If we just gained focus we need the updated mouse position
if (!(window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)) {
@@ -1329,21 +1372,14 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
inFullscreenTransition = YES;
}
/* This is usually sent after an unexpected windowDidExitFullscreen if the window
* failed to become fullscreen.
*
* Since something went wrong and the current state is unknown, dump any pending events.
*/
- (void)windowDidFailToEnterFullScreen:(NSNotification *)aNotification
{
SDL_Window *window = _data.window;
if (window->is_destroying) {
return;
}
SetWindowStyle(window, GetWindowStyle(window));
[self clearPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
isFullscreenSpace = NO;
inFullscreenTransition = NO;
[self windowDidExitFullScreen:nil];
[self clearAllPendingWindowOperations];
}
- (void)windowDidEnterFullScreen:(NSNotification *)aNotification
@@ -1351,6 +1387,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
SDL_Window *window = _data.window;
inFullscreenTransition = NO;
isFullscreenSpace = YES;
[self clearPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
if ([self windowOperationIsPending:PENDING_OPERATION_LEAVE_FULLSCREEN]) {
@@ -1401,26 +1438,14 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
inFullscreenTransition = YES;
}
/* This may be sent before windowDidExitFullscreen to signal that the window was
* dumped out of fullscreen with no animation.
*
* Since something went wrong and the state is unknown, dump any pending events.
*/
- (void)windowDidFailToExitFullScreen:(NSNotification *)aNotification
{
SDL_Window *window = _data.window;
const NSUInteger flags = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
if (window->is_destroying) {
return;
}
_data.pending_position = NO;
_data.pending_size = NO;
window->last_position_pending = false;
window->last_size_pending = false;
SetWindowStyle(window, flags);
isFullscreenSpace = YES;
inFullscreenTransition = NO;
[self windowDidEnterFullScreen:nil];
[self clearAllPendingWindowOperations];
}
- (void)windowDidExitFullScreen:(NSNotification *)aNotification
@@ -1429,16 +1454,24 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
NSWindow *nswindow = _data.nswindow;
inFullscreenTransition = NO;
isFullscreenSpace = NO;
_data.fullscreen_space_requested = NO;
/* As of macOS 10.15, the window decorations can go missing sometimes after
certain fullscreen-desktop->exlusive-fullscreen->windowed mode flows
certain fullscreen-desktop->exclusive-fullscreen->windowed mode flows
sometimes. Making sure the style mask always uses the windowed mode style
when returning to windowed mode from a space (instead of using a pending
fullscreen mode style mask) seems to work around that issue.
*/
SetWindowStyle(window, GetWindowWindowedStyle(window));
/* This can happen if the window failed to enter fullscreen, as this
* may be called *before* windowDidFailToEnterFullScreen in that case.
*/
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
[self clearAllPendingWindowOperations];
}
/* Don't recurse back into UpdateFullscreenMode() if this was hit in
* a blocking transition, as the caller is already waiting in
* UpdateFullscreenMode().
@@ -1483,9 +1516,10 @@ static NSCursor *Cocoa_GetDesiredCursor(void)
if ([self windowOperationIsPending:PENDING_OPERATION_ZOOM]) {
[self clearPendingWindowOperation:PENDING_OPERATION_ZOOM];
[nswindow zoom:nil];
_data.was_zoomed = !_data.was_zoomed;
}
if (![nswindow isZoomed]) {
if (!_data.was_zoomed) {
// Apply a pending window size, if not zoomed.
NSRect rect;
rect.origin.x = _data.pending_position ? window->pending.x : window->floating.x;
@@ -2003,6 +2037,7 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
@interface SDL3View : NSView
{
SDL_Window *_sdlWindow;
NSTrackingArea *_trackingArea; // only used on macOS <= 11.0
}
- (void)setSDLWindow:(SDL_Window *)window;
@@ -2014,6 +2049,7 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent;
- (BOOL)wantsUpdateLayer;
- (void)updateLayer;
- (void)updateTrackingAreas;
@end
@implementation SDL3View
@@ -2085,15 +2121,62 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
if (_sdlWindow->flags & SDL_WINDOW_POPUP_MENU) {
return YES;
} else if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
return SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
} else {
return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", false);
}
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
if (@available(macOS 12.0, *)) {
// we (currently) use the tracking areas as a workaround for older macOSes, but we might be safe everywhere...
} else {
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)_sdlWindow->internal;
if (_trackingArea) {
[self removeTrackingArea:_trackingArea];
}
_trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingMouseEnteredAndExited|NSTrackingActiveAlways owner:windata.listener userInfo:nil];
[self addTrackingArea:_trackingArea];
}
}
@end
static void Cocoa_UpdateMouseFocus()
{
const NSPoint mouseLocation = [NSEvent mouseLocation];
// Find the topmost window under the pointer and send a motion event if it is an SDL window.
[NSApp enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack
usingBlock:^(NSWindow *nswin, BOOL *stop) {
NSRect r = [nswin contentRectForFrameRect:[nswin frame]];
if (NSPointInRect(mouseLocation, r)) {
SDL_VideoDevice *vid = SDL_GetVideoDevice();
SDL_Window *sdlwindow;
for (sdlwindow = vid->windows; sdlwindow; sdlwindow = sdlwindow->next) {
if (nswin == ((__bridge SDL_CocoaWindowData *)sdlwindow->internal).nswindow) {
break;
}
}
*stop = YES;
if (sdlwindow) {
int wx, wy;
SDL_RelativeToGlobalForWindow(sdlwindow, sdlwindow->x, sdlwindow->y, &wx, &wy);
// Calculate the cursor coordinates relative to the window.
const float dx = mouseLocation.x - wx;
const float dy = (CGDisplayPixelsHigh(kCGDirectMainDisplay) - mouseLocation.y) - wy;
SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, dx, dy);
}
}
}];
}
static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow *nswindow, NSView *nsview)
{
@autoreleasepool {
@@ -2105,6 +2188,7 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
if (!data) {
return SDL_OutOfMemory();
}
window->internal = (SDL_WindowData *)CFBridgingRetain(data);
data.window = window;
data.nswindow = nswindow;
data.videodata = videodata;
@@ -2190,8 +2274,12 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
} else {
if (window->flags & SDL_WINDOW_TOOLTIP) {
[nswindow setIgnoresMouseEvents:YES];
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
Cocoa_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
[nswindow setAcceptsMouseMovedEvents:NO];
} else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_HIDDEN)) {
if (!(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
Cocoa_SetKeyboardFocus(window, true);
}
Cocoa_UpdateMouseFocus();
}
}
@@ -2225,7 +2313,6 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, NSWindow
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER, SDL_METALVIEW_TAG);
// All done!
window->internal = (SDL_WindowData *)CFBridgingRetain(data);
return true;
}
}
@@ -2281,7 +2368,7 @@ bool Cocoa_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti
rect.origin.y -= screenRect.origin.y;
// Constrain the popup
if (SDL_WINDOW_IS_POPUP(window)) {
if (SDL_WINDOW_IS_POPUP(window) && window->constrain_popup) {
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
rect.origin.x -= (rect.origin.x + rect.size.width) - (screenRect.origin.x + screenRect.size.width);
}
@@ -2437,7 +2524,7 @@ bool Cocoa_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
ConvertNSRect(&rect);
// Position and constrain the popup
if (SDL_WINDOW_IS_POPUP(window)) {
if (SDL_WINDOW_IS_POPUP(window) && window->constrain_popup) {
NSRect screenRect = [ScreenForRect(&rect) frame];
if (rect.origin.x + rect.size.width > screenRect.origin.x + screenRect.size.width) {
@@ -2469,30 +2556,24 @@ void Cocoa_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
return;
}
// isZoomed always returns true if the window is not resizable
if (!(window->flags & SDL_WINDOW_RESIZABLE) || ![nswindow isZoomed]) {
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
int x, y;
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
if (!Cocoa_IsWindowZoomed(window)) {
int x, y;
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
/* Cocoa will resize the window from the bottom-left rather than the
* top-left when -[nswindow setContentSize:] is used, so we must set the
* entire frame based on the new size, in order to preserve the position.
*/
SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y);
rect.origin.x = x;
rect.origin.y = y;
rect.size.width = window->pending.w;
rect.size.height = window->pending.h;
ConvertNSRect(&rect);
/* Cocoa will resize the window from the bottom-left rather than the
* top-left when -[nswindow setContentSize:] is used, so we must set the
* entire frame based on the new size, in order to preserve the position.
*/
SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, &x, &y);
rect.origin.x = x;
rect.origin.y = y;
rect.size.width = window->pending.w;
rect.size.height = window->pending.h;
ConvertNSRect(&rect);
[nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
ScheduleContextUpdates(windata);
} else {
// Can't set the window size.
window->last_size_pending = false;
}
} else {
[nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
ScheduleContextUpdates(windata);
} else {
// Can't set the window size.
window->last_size_pending = false;
}
@@ -2518,8 +2599,8 @@ void Cocoa_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window)
SDL_CocoaWindowData *windata = (__bridge SDL_CocoaWindowData *)window->internal;
NSSize maxSize;
maxSize.width = window->max_w;
maxSize.height = window->max_h;
maxSize.width = window->max_w ? window->max_w : CGFLOAT_MAX;
maxSize.height = window->max_h ? window->max_h : CGFLOAT_MAX;
[windata.nswindow setContentMaxSize:maxSize];
}
@@ -2583,6 +2664,11 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
[nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]];
}
}
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (!(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
Cocoa_SetKeyboardFocus(window, true);
}
Cocoa_UpdateMouseFocus();
}
}
[nswindow setIsVisible:YES];
@@ -2615,21 +2701,11 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
Cocoa_SetWindowModal(_this, window, false);
// Transfer keyboard focus back to the parent when closing a popup menu
if (window->flags & SDL_WINDOW_POPUP_MENU) {
SDL_Window *new_focus = window->parent;
bool set_focus = window == SDL_GetKeyboardFocus();
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
new_focus = new_focus->parent;
// If some window in the chain currently had focus, set it to the new lowest-level window.
if (!set_focus) {
set_focus = new_focus == SDL_GetKeyboardFocus();
}
}
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
Cocoa_SetKeyboardFocus(new_focus, set_focus);
Cocoa_UpdateMouseFocus();
} else if (window->parent && waskey) {
/* Key status is not automatically set on the parent when a child is hidden. Check if the
* child window was key, and set the first visible parent to be key if so.
@@ -2741,7 +2817,7 @@ void Cocoa_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
![data.listener isInFullscreenSpace]) {
if ([nswindow isMiniaturized]) {
[nswindow deminiaturize:nil];
} else if ((window->flags & SDL_WINDOW_RESIZABLE) && [data.nswindow isZoomed]) {
} else if (Cocoa_IsWindowZoomed(window)) {
[nswindow zoom:nil];
}
} else if (data.was_zoomed) {
@@ -2767,6 +2843,7 @@ void Cocoa_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bo
} else {
data.border_toggled = true;
}
Cocoa_UpdateClipCursor(window);
}
}
@@ -3053,20 +3130,19 @@ void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
#endif // SDL_VIDEO_OPENGL
SDL_Window *topmost = GetParentToplevelWindow(window);
SDL_CocoaWindowData *topmost_data = (__bridge SDL_CocoaWindowData *)topmost->internal;
/* Reset the input focus of the root window if this window is still set as keyboard focus.
* SDL_DestroyWindow will have already taken care of reassigning focus if this is the SDL
* keyboard focus, this ensures that an inactive window with this window set as input focus
* does not try to reference it the next time it gains focus.
*/
if (topmost_data.keyboard_focus == window) {
if (topmost->keyboard_focus == window) {
SDL_Window *new_focus = window;
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
new_focus = new_focus->parent;
}
topmost_data.keyboard_focus = new_focus;
topmost->keyboard_focus = new_focus;
}
if ([data.listener isInFullscreenSpace]) {
@@ -3231,6 +3307,20 @@ bool Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOper
bool Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
{
if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
Cocoa_SetKeyboardFocus(new_focus, set_focus);
} else if (focusable) {
if (SDL_ShouldFocusPopup(window)) {
Cocoa_SetKeyboardFocus(window, true);
}
}
}
}
return true; // just succeed, the real work is done elsewhere.
}
@@ -3245,25 +3335,21 @@ bool Cocoa_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float op
bool Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
bool result = true;
bool result = false;
@autoreleasepool {
/* The timeout needs to be high enough that animated fullscreen
* spaces transitions won't cause it to time out.
*/
Uint64 timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(2000);
const Uint64 timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(2500);
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->internal;
while (true) {
for (;;) {
SDL_PumpEvents();
if (SDL_GetTicksNS() >= timeout) {
result = false;
break;
}
if (![data.listener hasPendingWindowOperation]) {
result = ![data.listener hasPendingWindowOperation];
if (result || SDL_GetTicksNS() >= timeout) {
break;
}
// Small delay before going again.
SDL_Delay(10);
}
}
+4 -2
View File
@@ -121,7 +121,8 @@ static SDL_VideoDevice *DUMMY_CreateDevice(void)
VideoBootStrap DUMMY_bootstrap = {
DUMMYVID_DRIVER_NAME, "SDL dummy video driver",
DUMMY_CreateDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
#ifdef SDL_INPUT_LINUXEV
@@ -144,7 +145,8 @@ static SDL_VideoDevice *DUMMY_EVDEV_CreateDevice(void)
VideoBootStrap DUMMY_evdev_bootstrap = {
DUMMYVID_DRIVER_EVDEV_NAME, "SDL dummy video driver with evdev",
DUMMY_EVDEV_CreateDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
#endif // SDL_INPUT_LINUXEV
@@ -1005,6 +1005,27 @@ static void Emscripten_unset_drag_event_callbacks(SDL_WindowData *data)
}, data->canvas_id);
}
static const char *Emscripten_GetKeyboardTargetElement()
{
const char *target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
if (!target || !*target) {
return EMSCRIPTEN_EVENT_TARGET_WINDOW;
}
if (SDL_strcmp(target, "#none") == 0) {
return NULL;
} else if (SDL_strcmp(target, "#window") == 0) {
return EMSCRIPTEN_EVENT_TARGET_WINDOW;
} else if (SDL_strcmp(target, "#document") == 0) {
return EMSCRIPTEN_EVENT_TARGET_DOCUMENT;
} else if (SDL_strcmp(target, "#screen") == 0) {
return EMSCRIPTEN_EVENT_TARGET_SCREEN;
}
return target;
}
void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
{
const char *keyElement;
@@ -1033,12 +1054,8 @@ void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandlePointerLockChange);
// Keyboard events are awkward
keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
if (!keyElement || !*keyElement) {
keyElement = EMSCRIPTEN_EVENT_TARGET_WINDOW;
}
if (SDL_strcmp(keyElement, "#none") != 0) {
keyElement = Emscripten_GetKeyboardTargetElement();
if (keyElement) {
emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
@@ -1094,12 +1111,8 @@ void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
if (!target) {
target = EMSCRIPTEN_EVENT_TARGET_WINDOW;
}
if (*target) {
target = Emscripten_GetKeyboardTargetElement();
if (target) {
emscripten_set_keydown_callback(target, NULL, 0, NULL);
emscripten_set_keyup_callback(target, NULL, 0, NULL);
emscripten_set_keypress_callback(target, NULL, 0, NULL);
@@ -78,7 +78,7 @@ bool Emscripten_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *wind
if (!Module['SDL3']) Module['SDL3'] = {};
var SDL3 = Module['SDL3'];
if (SDL3.ctxCanvas !== canvas) {
SDL3.ctx = Module['createContext'](canvas, false, true);
SDL3.ctx = Browser.createContext(canvas, false, true);
SDL3.ctxCanvas = canvas;
}
if (SDL3.w !== w || SDL3.h !== h || SDL3.imageCtx !== SDL3.ctx) {
@@ -101,7 +101,7 @@ SDL_GLContext Emscripten_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *
context = emscripten_webgl_create_context(window_data->canvas_id, &attribs);
if (context < 0) {
if (!context) {
SDL_SetError("Could not create webgl context");
return NULL;
}
@@ -64,7 +64,7 @@ static SDL_SystemTheme Emscripten_GetSystemTheme(void)
/* Technically, light theme can mean explicit light theme or no preference.
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme#syntax */
int theme_code = EM_ASM_INT({
int theme_code = MAIN_THREAD_EM_ASM_INT({
if (!window.matchMedia) {
return -1;
}
@@ -195,7 +195,8 @@ static SDL_VideoDevice *Emscripten_CreateDevice(void)
VideoBootStrap Emscripten_bootstrap = {
EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
Emscripten_CreateDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
bool Emscripten_VideoInit(SDL_VideoDevice *_this)
+5 -1
View File
@@ -113,7 +113,8 @@ static SDL_VideoDevice * HAIKU_CreateDevice(void)
VideoBootStrap HAIKU_bootstrap = {
"haiku", "Haiku graphics",
HAIKU_CreateDevice,
HAIKU_ShowMessageBox
HAIKU_ShowMessageBox,
false
};
void HAIKU_DeleteDevice(SDL_VideoDevice * device)
@@ -240,6 +241,9 @@ static bool HAIKU_SetRelativeMouseMode(bool enabled)
SDL_BWin *bewin = _ToBeWin(window);
BGLView *_SDL_GLView = bewin->GetGLView();
if (!_SDL_GLView) {
return false;
}
bewin->Lock();
if (enabled)
+11 -5
View File
@@ -332,7 +332,8 @@ VideoBootStrap KMSDRM_bootstrap = {
"kmsdrm",
"KMS/DRM Video Driver",
KMSDRM_CreateDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
static void KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
@@ -1367,9 +1368,14 @@ bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window)
windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev,
dispdata->mode.hdisplay, dispdata->mode.vdisplay,
surface_fmt, surface_flags);
if (!windata->gs && errno == ENOSYS) {
// Try again without the scanout flags, needed on NVIDIA drivers
windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev,
dispdata->mode.hdisplay, dispdata->mode.vdisplay,
surface_fmt, 0);
}
if (!windata->gs) {
return SDL_SetError("Could not create GBM surface");
return SDL_SetError("Could not create GBM surface: %s", strerror(errno));
}
/* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
@@ -1728,9 +1734,9 @@ bool KMSDRM_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
}
/* Create the window surfaces with the size we have just chosen.
Needs the window diverdata in place. */
Needs the window driverdata in place. */
if (!KMSDRM_CreateSurfaces(_this, window)) {
return SDL_SetError("Can't window GBM/EGL surfaces on window creation.");
return false;
}
} // NON-Vulkan block ends.
+1 -1
View File
@@ -118,7 +118,7 @@ static SDL_VideoDevice *N3DS_CreateDevice(void)
return device;
}
VideoBootStrap N3DS_bootstrap = { N3DSVID_DRIVER_NAME, "N3DS Video Driver", N3DS_CreateDevice, NULL /* no ShowMessageBox implementation */ };
VideoBootStrap N3DS_bootstrap = { N3DSVID_DRIVER_NAME, "N3DS Video Driver", N3DS_CreateDevice, NULL, /* no ShowMessageBox implementation */ false };
static bool N3DS_VideoInit(SDL_VideoDevice *_this)
{
@@ -118,7 +118,8 @@ static SDL_VideoDevice *OFFSCREEN_CreateDevice(void)
VideoBootStrap OFFSCREEN_bootstrap = {
OFFSCREENVID_DRIVER_NAME, "SDL offscreen video driver",
OFFSCREEN_CreateDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
static bool OFFSCREEN_VideoInit(SDL_VideoDevice *_this)
+2 -2
View File
@@ -911,7 +911,7 @@ static SDL_GLContext OPENVR_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window
OPENVR_SetupFrame(_this, window);
SDL_GLContext result = malloc(sizeof(struct SDL_GLContextState));
SDL_GLContext result = SDL_malloc(sizeof(struct SDL_GLContextState));
if (!result) {
return NULL;
}
@@ -1657,7 +1657,7 @@ error:
}
VideoBootStrap OPENVR_bootstrap = {
"openvr", "SDL OpenVR video driver", OPENVR_CreateDevice
"openvr", "SDL OpenVR video driver", OPENVR_CreateDevice, NULL, false
};
#endif // SDL_VIDEO_DRIVER_WINDOWS
+2 -1
View File
@@ -114,7 +114,8 @@ VideoBootStrap PS2_bootstrap = {
"ps2",
"PS2 Video Driver",
PS2_CreateDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
#endif // SDL_VIDEO_DRIVER_PS2
+4 -1
View File
@@ -118,6 +118,8 @@ static SDL_VideoDevice *PSP_Create(void)
device->PumpEvents = PSP_PumpEvents;
device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY;
return device;
}
@@ -265,7 +267,8 @@ VideoBootStrap PSP_bootstrap = {
"psp",
"PSP Video Driver",
PSP_Create,
PSP_ShowMessageBox
PSP_ShowMessageBox,
false
};
/*****************************************************************************/
+2 -1
View File
@@ -346,5 +346,6 @@ static SDL_VideoDevice *createDevice(void)
VideoBootStrap QNX_bootstrap = {
"qnx", "QNX Screen",
createDevice,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
+2 -1
View File
@@ -135,7 +135,8 @@ VideoBootStrap RPI_bootstrap = {
"rpi",
"RPI Video Driver",
RPI_Create,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
/*****************************************************************************/
+2 -1
View File
@@ -96,7 +96,8 @@ static SDL_VideoDevice *RISCOS_CreateDevice(void)
VideoBootStrap RISCOS_bootstrap = {
RISCOSVID_DRIVER_NAME, "SDL RISC OS video driver",
RISCOS_CreateDevice,
RISCOS_ShowMessageBox
RISCOS_ShowMessageBox,
false
};
static bool RISCOS_VideoInit(SDL_VideoDevice *_this)
+10 -2
View File
@@ -112,7 +112,6 @@ sub open_file {
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_surface_c.h"
#ifdef SDL_HAVE_BLIT_AUTO
@@ -641,6 +640,14 @@ sub output_copyinc
__EOF__
}
sub output_copyinc_h
{
print FILE <<__EOF__;
#include "SDL_blit.h"
__EOF__
}
sub output_copyfunctable
{
print FILE <<__EOF__;
@@ -693,7 +700,7 @@ __EOF__
}
}
print FILE <<__EOF__;
{ 0, 0, 0, 0, NULL }
{ SDL_PIXELFORMAT_UNKNOWN, SDL_PIXELFORMAT_UNKNOWN, 0, 0, NULL }
};
__EOF__
@@ -716,6 +723,7 @@ sub output_copyfunc_c
}
open_file("SDL_blit_auto.h");
output_copyinc_h();
output_copydefs();
for (my $i = 0; $i <= $#src_formats; ++$i) {
for (my $j = 0; $j <= $#dst_formats; ++$j) {
File diff suppressed because it is too large Load Diff
+16 -17
View File
@@ -124,31 +124,30 @@ static BOOL UIKit_ShowMessageBoxAlertController(const SDL_MessageBoxData *messag
return YES;
}
static void UIKit_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID, int *result)
typedef struct UIKit_ShowMessageBoxData
{
const SDL_MessageBoxData *messageboxdata;
int *buttonID;
bool result;
} UIKit_ShowMessageBoxData;
static void SDLCALL UIKit_ShowMessageBoxMainThreadCallback(void *userdata)
{
@autoreleasepool {
if (UIKit_ShowMessageBoxAlertController(messageboxdata, buttonID)) {
*result = true;
} else {
*result = SDL_SetError("Could not show message box.");
}
UIKit_ShowMessageBoxData *data = (UIKit_ShowMessageBoxData *) userdata;
data->result = UIKit_ShowMessageBoxAlertController(data->messageboxdata, data->buttonID);
}
}
bool UIKit_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
@autoreleasepool {
__block int result = true;
if ([NSThread isMainThread]) {
UIKit_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
UIKit_ShowMessageBoxImpl(messageboxdata, buttonID, &result);
});
}
return result;
UIKit_ShowMessageBoxData data = { messageboxdata, buttonID, false };
if (!SDL_RunOnMainThread(UIKit_ShowMessageBoxMainThreadCallback, &data, true)) {
return false;
} else if (!data.result) {
return SDL_SetError("Could not show message box.");
}
return true;
}
#endif // SDL_VIDEO_DRIVER_UIKIT
+4 -1
View File
@@ -37,6 +37,7 @@
#include "SDL_uikitvulkan.h"
#include "SDL_uikitmetalview.h"
#include "SDL_uikitmessagebox.h"
#include "SDL_uikitpen.h"
#define UIKITVID_DRIVER_NAME "uikit"
@@ -144,7 +145,8 @@ static SDL_VideoDevice *UIKit_CreateDevice(void)
VideoBootStrap UIKIT_bootstrap = {
UIKITVID_DRIVER_NAME, "SDL UIKit video driver",
UIKit_CreateDevice,
UIKit_ShowMessageBox
UIKit_ShowMessageBox,
false
};
static bool UIKit_VideoInit(SDL_VideoDevice *_this)
@@ -169,6 +171,7 @@ static void UIKit_VideoQuit(SDL_VideoDevice *_this)
SDL_QuitGCKeyboard();
SDL_QuitGCMouse();
UIKit_QuitPen(_this);
UIKit_QuitModes(_this);
}
+1 -1
View File
@@ -240,7 +240,7 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
int i;
SDL_MouseButtonFlags buttons = SDL_GetMouseState(NULL, NULL);
for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) {
for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
if (buttons & SDL_BUTTON_MASK(i)) {
SDL_SendMouseButton(UIKit_GetEventTimestamp([touch timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, (Uint8)i, false);
}
+4 -1
View File
@@ -153,6 +153,8 @@ static SDL_VideoDevice *VITA_Create(void)
device->PumpEvents = VITA_PumpEvents;
device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY;
return device;
}
@@ -160,7 +162,8 @@ VideoBootStrap VITA_bootstrap = {
"vita",
"VITA Video Driver",
VITA_Create,
VITA_ShowMessageBox
VITA_ShowMessageBox,
false
};
/*****************************************************************************/
-2
View File
@@ -60,8 +60,6 @@ extern SDL_Window *Vita_Window;
// Display and window functions
extern bool VITA_VideoInit(SDL_VideoDevice *_this);
extern void VITA_VideoQuit(SDL_VideoDevice *_this);
extern bool VITA_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern bool VITA_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern bool VITA_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern void VITA_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern bool VITA_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
@@ -107,7 +107,8 @@ VideoBootStrap VIVANTE_bootstrap = {
"vivante",
"Vivante EGL Video Driver",
VIVANTE_Create,
NULL // no ShowMessageBox implementation
NULL, // no ShowMessageBox implementation
false
};
/*****************************************************************************/
@@ -0,0 +1,303 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include "SDL_waylandcolor.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h"
#include "color-management-v1-client-protocol.h"
#include "../../events/SDL_windowevents_c.h"
typedef struct Wayland_ColorInfoState
{
struct wp_image_description_v1 *wp_image_description;
struct wp_image_description_info_v1 *wp_image_description_info;
union
{
SDL_WindowData *window_data;
SDL_DisplayData *display_data;
};
enum
{
WAYLAND_COLOR_OBJECT_TYPE_WINDOW,
WAYLAND_COLOR_OBJECT_TYPE_DISPLAY
} object_type;
SDL_HDROutputProperties HDR;
// The ICC fd is only valid if the size is non-zero.
int icc_fd;
Uint32 icc_size;
bool deferred_event_processing;
} Wayland_ColorInfoState;
static void Wayland_CancelColorInfoRequest(Wayland_ColorInfoState *state)
{
if (state) {
if (state->wp_image_description_info) {
wp_image_description_info_v1_destroy(state->wp_image_description_info);
state->wp_image_description_info = NULL;
}
if (state->wp_image_description) {
wp_image_description_v1_destroy(state->wp_image_description);
state->wp_image_description = NULL;
}
}
}
void Wayland_FreeColorInfoState(Wayland_ColorInfoState *state)
{
if (state) {
Wayland_CancelColorInfoRequest(state);
switch (state->object_type) {
case WAYLAND_COLOR_OBJECT_TYPE_WINDOW:
state->window_data->color_info_state = NULL;
break;
case WAYLAND_COLOR_OBJECT_TYPE_DISPLAY:
state->display_data->color_info_state = NULL;
break;
}
SDL_free(state);
}
}
static void image_description_info_handle_done(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
Wayland_CancelColorInfoRequest(state);
switch (state->object_type) {
case WAYLAND_COLOR_OBJECT_TYPE_WINDOW:
{
SDL_SetWindowHDRProperties(state->window_data->sdlwindow, &state->HDR, true);
if (state->icc_size) {
state->window_data->icc_fd = state->icc_fd;
state->window_data->icc_size = state->icc_size;
SDL_SendWindowEvent(state->window_data->sdlwindow, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
}
} break;
case WAYLAND_COLOR_OBJECT_TYPE_DISPLAY:
{
SDL_copyp(&state->display_data->HDR, &state->HDR);
} break;
}
}
static void image_description_info_handle_icc_file(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
int32_t icc, uint32_t icc_size)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
state->icc_fd = icc;
state->icc_size = icc_size;
}
static void image_description_info_handle_primaries(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
int32_t r_x, int32_t r_y,
int32_t g_x, int32_t g_y,
int32_t b_x, int32_t b_y,
int32_t w_x, int32_t w_y)
{
// NOP
}
static void image_description_info_handle_primaries_named(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t primaries)
{
// NOP
}
static void image_description_info_handle_tf_power(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t eexp)
{
// NOP
}
static void image_description_info_handle_tf_named(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t tf)
{
// NOP
}
static void image_description_info_handle_luminances(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t min_lum,
uint32_t max_lum,
uint32_t reference_lum)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
state->HDR.HDR_headroom = (float)max_lum / (float)reference_lum;
}
static void image_description_info_handle_target_primaries(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
int32_t r_x, int32_t r_y,
int32_t g_x, int32_t g_y,
int32_t b_x, int32_t b_y,
int32_t w_x, int32_t w_y)
{
// NOP
}
static void image_description_info_handle_target_luminance(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t min_lum,
uint32_t max_lum)
{
// NOP
}
static void image_description_info_handle_target_max_cll(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t max_cll)
{
// NOP
}
static void image_description_info_handle_target_max_fall(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1,
uint32_t max_fall)
{
// NOP
}
static const struct wp_image_description_info_v1_listener image_description_info_listener = {
image_description_info_handle_done,
image_description_info_handle_icc_file,
image_description_info_handle_primaries,
image_description_info_handle_primaries_named,
image_description_info_handle_tf_power,
image_description_info_handle_tf_named,
image_description_info_handle_luminances,
image_description_info_handle_target_primaries,
image_description_info_handle_target_luminance,
image_description_info_handle_target_max_cll,
image_description_info_handle_target_max_fall
};
static void PumpColorspaceEvents(Wayland_ColorInfoState *state)
{
SDL_VideoData *vid = SDL_GetVideoDevice()->internal;
// Run the image description sequence to completion in its own queue.
struct wl_event_queue *queue = WAYLAND_wl_display_create_queue(vid->display);
if (state->deferred_event_processing) {
WAYLAND_wl_proxy_set_queue((struct wl_proxy *)state->wp_image_description_info, queue);
} else {
WAYLAND_wl_proxy_set_queue((struct wl_proxy *)state->wp_image_description, queue);
}
while (state->wp_image_description) {
WAYLAND_wl_display_dispatch_queue(vid->display, queue);
}
WAYLAND_wl_event_queue_destroy(queue);
Wayland_FreeColorInfoState(state);
}
static void image_description_handle_failed(void *data,
struct wp_image_description_v1 *wp_image_description_v1,
uint32_t cause,
const char *msg)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
Wayland_CancelColorInfoRequest(state);
if (state->deferred_event_processing) {
Wayland_FreeColorInfoState(state);
}
}
static void image_description_handle_ready(void *data,
struct wp_image_description_v1 *wp_image_description_v1,
uint32_t identity)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
// This will inherit the queue of the factory image description object.
state->wp_image_description_info = wp_image_description_v1_get_information(state->wp_image_description);
wp_image_description_info_v1_add_listener(state->wp_image_description_info, &image_description_info_listener, data);
if (state->deferred_event_processing) {
PumpColorspaceEvents(state);
}
}
static const struct wp_image_description_v1_listener image_description_listener = {
image_description_handle_failed,
image_description_handle_ready
};
void Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, bool defer_event_processing)
{
// Cancel any pending request, as it is out-of-date.
Wayland_FreeColorInfoState(window_data->color_info_state);
Wayland_ColorInfoState *state = SDL_calloc(1, sizeof(Wayland_ColorInfoState));
if (state) {
window_data->color_info_state = state;
state->window_data = window_data;
state->object_type = WAYLAND_COLOR_OBJECT_TYPE_WINDOW;
state->deferred_event_processing = defer_event_processing;
state->wp_image_description = wp_color_management_surface_feedback_v1_get_preferred(window_data->wp_color_management_surface_feedback);
wp_image_description_v1_add_listener(state->wp_image_description, &image_description_listener, state);
if (!defer_event_processing) {
PumpColorspaceEvents(state);
}
}
}
void Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, bool defer_event_processing)
{
// Cancel any pending request, as it is out-of-date.
Wayland_FreeColorInfoState(display_data->color_info_state);
Wayland_ColorInfoState *state = SDL_calloc(1, sizeof(Wayland_ColorInfoState));
if (state) {
display_data->color_info_state = state;
state->display_data = display_data;
state->object_type = WAYLAND_COLOR_OBJECT_TYPE_DISPLAY;
state->deferred_event_processing = defer_event_processing;
state->wp_image_description = wp_color_management_output_v1_get_image_description(display_data->wp_color_management_output);
wp_image_description_v1_add_listener(state->wp_image_description, &image_description_listener, state);
if (!defer_event_processing) {
PumpColorspaceEvents(state);
}
}
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
@@ -0,0 +1,35 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_waylandcolor_h_
#define SDL_waylandcolor_h_
#include "../SDL_sysvideo.h"
struct Wayland_ColorInfoState;
extern void Wayland_FreeColorInfoState(struct Wayland_ColorInfoState *state);
extern void Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, bool defer_event_processing);
extern void Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, bool defer_event_processing);
#endif // SDL_waylandcolor_h_
@@ -272,7 +272,7 @@ void Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSo
}
}
static void *Wayland_clone_data_buffer(const void *buffer, size_t *len)
static void *Wayland_clone_data_buffer(const void *buffer, const size_t *len)
{
void *clone = NULL;
if (*len > 0 && buffer) {
+50 -44
View File
@@ -163,16 +163,9 @@ static bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface)
static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp)
{
static Uint64 last;
static Uint64 timestamp_offset;
static Uint64 timestamp_offset = 0;
const Uint64 now = SDL_GetTicksNS();
if (nsTimestamp < last) {
// 32-bit timer rollover, bump the offset
timestamp_offset += SDL_MS_TO_NS(0x100000000LLU);
}
last = nsTimestamp;
if (!timestamp_offset) {
timestamp_offset = (now - nsTimestamp);
}
@@ -196,19 +189,24 @@ static const struct zwp_input_timestamps_v1_listener timestamp_listener = {
Wayland_input_timestamp_listener
};
static Uint64 Wayland_EventTimestampMSToNS(Uint32 wl_timestamp_ms)
{
static Uint64 timestamp_offset = 0;
static Uint32 last = 0;
// Handle 32-bit timer rollover.
if (wl_timestamp_ms < last) {
timestamp_offset += SDL_MS_TO_NS(SDL_UINT64_C(0x100000000));
}
last = wl_timestamp_ms;
return SDL_MS_TO_NS(wl_timestamp_ms) + timestamp_offset;
}
static Uint64 Wayland_GetKeyboardTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
}
return 0;
}
static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms);
return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
}
return 0;
@@ -217,7 +215,7 @@ static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Ui
static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
}
return 0;
@@ -226,7 +224,7 @@ static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32
Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
{
if (wl_timestamp_ms) {
return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : Wayland_EventTimestampMSToNS(wl_timestamp_ms));
}
return 0;
@@ -269,10 +267,11 @@ void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input)
static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed)
{
bool ret = false;
while (elapsed >= repeat_info->next_repeat_ns) {
if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
const Uint64 timestamp = repeat_info->wl_press_time_ns + repeat_info->next_repeat_ns;
SDL_SendKeyboardKeyIgnoreModifiers(Wayland_GetEventTimestamp(timestamp), repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
const Uint64 timestamp = repeat_info->base_time_ns + repeat_info->next_repeat_ns;
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
}
if (repeat_info->text[0]) {
SDL_SendKeyboardText(repeat_info->text);
@@ -291,8 +290,8 @@ static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info)
repeat_info->is_key_down = false;
}
static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint64 wl_press_time_ns,
uint32_t scancode, bool has_text, char text[8])
static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint32 wl_press_time_ms,
Uint64 base_time_ns, uint32_t scancode, bool has_text, char text[8])
{
if (!repeat_info->is_initialized || !repeat_info->repeat_rate) {
return;
@@ -300,12 +299,13 @@ static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 k
repeat_info->is_key_down = true;
repeat_info->keyboard_id = keyboard_id;
repeat_info->key = key;
repeat_info->wl_press_time_ns = wl_press_time_ns;
repeat_info->wl_press_time_ms = wl_press_time_ms;
repeat_info->base_time_ns = base_time_ns;
repeat_info->sdl_press_time_ns = SDL_GetTicksNS();
repeat_info->next_repeat_ns = SDL_MS_TO_NS(repeat_info->repeat_delay_ms);
repeat_info->scancode = scancode;
if (has_text) {
SDL_copyp(repeat_info->text, text);
SDL_memcpy(repeat_info->text, text, sizeof(repeat_info->text));
} else {
repeat_info->text[0] = '\0';
}
@@ -598,11 +598,11 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
wind->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
input->buttons_pressed = 0;
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false);
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false);
SDL_SendMouseButton(0, wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false);
}
/* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event.
@@ -1023,8 +1023,8 @@ static void relative_pointer_handle_relative_motion(void *data,
dx = wl_fixed_to_double(dx_unaccel_w);
dy = wl_fixed_to_double(dy_unaccel_w);
} else {
dx = wl_fixed_to_double(dx_w);
dy = wl_fixed_to_double(dy_w);
dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
}
SDL_SendMouseMotion(timestamp, window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy);
@@ -1199,7 +1199,7 @@ static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t ti
const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;
SDL_SendTouchMotion(Wayland_GetPointerTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
SDL_SendTouchMotion(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
(SDL_FingerID)(id + 1), window_data->sdlwindow, x, y, 1.0f);
}
}
@@ -1717,7 +1717,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
window->keyboard_device = input;
// Restore the keyboard focus to the child popup that was holding it
SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window->sdlwindow);
SDL_SetKeyboardFocus(window->sdlwindow->keyboard_focus ? window->sdlwindow->keyboard_focus : window->sdlwindow);
#ifdef SDL_USE_IME
if (!input->text_input) {
@@ -1855,7 +1855,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
char text[8];
bool has_text = false;
bool handled_by_ime = false;
const Uint64 timestamp_raw_ns = Wayland_GetKeyboardTimestampRaw(input, time);
const Uint64 timestamp_ns = Wayland_GetKeyboardTimestamp(input, time);
Wayland_UpdateImplicitGrabSerial(input, serial);
@@ -1871,7 +1871,8 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
* Using SDL_GetTicks would be wrong, as it would report when the release event is processed,
* which may be off if the application hasn't pumped events for a while.
*/
keyboard_repeat_handle(&input->keyboard_repeat, timestamp_raw_ns - input->keyboard_repeat.wl_press_time_ns);
const Uint64 elapsed = SDL_MS_TO_NS(time - input->keyboard_repeat.wl_press_time_ms);
keyboard_repeat_handle(&input->keyboard_repeat, elapsed);
keyboard_repeat_clear(&input->keyboard_repeat);
}
keyboard_input_get_text(text, input, key, false, &handled_by_ime);
@@ -1879,18 +1880,17 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key);
Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp_ns, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (has_text && !(SDL_GetModState() & SDL_KMOD_CTRL)) {
if (has_text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
if (!handled_by_ime) {
SDL_SendKeyboardText(text);
}
}
if (input->xkb.keymap && WAYLAND_xkb_keymap_key_repeats(input->xkb.keymap, key + 8)) {
keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, timestamp_raw_ns, scancode, has_text, text);
keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, time, timestamp_ns, scancode, has_text, text);
}
}
}
@@ -2563,8 +2563,6 @@ static void primary_selection_device_handle_selection(void *data, struct zwp_pri
SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_selection on primary_selection_offer 0x%08x",
(id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
notifyFromMimes(offer ? &offer->mimes : NULL);
}
static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
@@ -2953,7 +2951,7 @@ static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool
return; // Not a pen we report on.
}
const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_MS_TO_NS(time));
const Uint64 timestamp = Wayland_GetEventTimestamp(Wayland_EventTimestampMSToNS(time));
const SDL_PenID instance_id = sdltool->instance_id;
SDL_Window *window = sdltool->tool_focus;
@@ -3306,6 +3304,11 @@ bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *w
return SDL_SetError("No pointer to confine");
}
// The confinement region will be created when the window is mapped.
if (w->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_SHOWN) {
return true;
}
/* A confine may already be active, in which case we should destroy it and
* create a new one.
*/
@@ -3355,6 +3358,9 @@ bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *w
wl_region_destroy(confine_rect);
}
// Commit the double-buffered confinement region.
wl_surface_commit(w->surface);
w->confined_pointer = confined_pointer;
return true;
}
@@ -49,17 +49,18 @@ typedef struct SDL_WaylandTabletInput
typedef struct
{
int32_t repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
int32_t repeat_delay_ms; // Time to first repeat event in milliseconds
Uint32 keyboard_id; // ID of the source keyboard.
Sint32 repeat_rate; // Repeat rate in range of [1, 1000] character(s) per second
Sint32 repeat_delay_ms; // Time to first repeat event in milliseconds
Uint32 keyboard_id; // ID of the source keyboard.
bool is_initialized;
bool is_key_down;
uint32_t key;
Uint64 wl_press_time_ns; // Key press time as reported by the Wayland API
Uint32 key;
Uint32 wl_press_time_ms; // Key press time as reported by the Wayland API in milliseconds
Uint64 base_time_ns; // Key press time as reported by the Wayland API in nanoseconds
Uint64 sdl_press_time_ns; // Key press time expressed in SDL ticks
Uint64 next_repeat_ns; // Next repeat event in nanoseconds
uint32_t scancode;
Uint32 scancode;
char text[8];
} SDL_WaylandKeyboardRepeat;
@@ -58,7 +58,7 @@ bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
if (internal->text_input_manager) {
if (input && input->text_input) {
const SDL_Rect *rect = &input->text_input->cursor_rect;
const SDL_Rect *rect = &input->text_input->text_input_rect;
enum zwp_text_input_v3_content_hint hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE;
enum zwp_text_input_v3_content_purpose purpose;
@@ -125,12 +125,21 @@ bool Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
// Now that it's enabled, set the input properties
zwp_text_input_v3_set_content_type(input->text_input->text_input, hint, purpose);
if (!SDL_RectEmpty(rect)) {
// This gets reset on enable so we have to cache it
SDL_WindowData *wind = window->internal;
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / wind->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / wind->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / wind->pointer_scale.x);
// Clamp the x value so it doesn't run too far past the end of the text input area.
zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input,
rect->x,
rect->y,
rect->w,
rect->h);
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
scaled_rect.y,
1,
scaled_rect.h);
}
zwp_text_input_v3_commit(input->text_input->text_input);
}
@@ -174,13 +183,24 @@ bool Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
if (internal->text_input_manager) {
struct SDL_WaylandInput *input = internal->input;
if (input && input->text_input) {
if (!SDL_RectsEqual(&window->text_input_rect, &input->text_input->cursor_rect)) {
SDL_copyp(&input->text_input->cursor_rect, &window->text_input_rect);
SDL_WindowData *wind = window->internal;
const SDL_Rect scaled_rect = {
(int)SDL_floor(window->text_input_rect.x / wind->pointer_scale.x),
(int)SDL_floor(window->text_input_rect.y / wind->pointer_scale.y),
(int)SDL_ceil(window->text_input_rect.w / wind->pointer_scale.x),
(int)SDL_ceil(window->text_input_rect.h / wind->pointer_scale.y)
};
const int scaled_cursor = (int)SDL_floor(window->text_input_cursor / wind->pointer_scale.x);
if (!SDL_RectsEqual(&scaled_rect, &input->text_input->text_input_rect) || scaled_cursor != input->text_input->text_input_cursor) {
SDL_copyp(&input->text_input->text_input_rect, &scaled_rect);
input->text_input->text_input_cursor = scaled_cursor;
// Clamp the x value so it doesn't run too far past the end of the text input area.
zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input,
window->text_input_rect.x,
window->text_input_rect.y,
window->text_input_rect.w,
window->text_input_rect.h);
SDL_min(scaled_rect.x + scaled_cursor, scaled_rect.x + scaled_rect.w),
scaled_rect.y,
1,
scaled_rect.h);
zwp_text_input_v3_commit(input->text_input->text_input);
}
}
@@ -26,7 +26,8 @@
typedef struct SDL_WaylandTextInput
{
struct zwp_text_input_v3 *text_input;
SDL_Rect cursor_rect;
SDL_Rect text_input_rect;
int text_input_cursor;
bool has_preedit;
} SDL_WaylandTextInput;
@@ -598,6 +598,13 @@ static SDL_Cursor *Wayland_CreateDefaultCursor(void)
static void Wayland_FreeCursorData(SDL_CursorData *d)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
struct SDL_WaylandInput *input = vd->internal->input;
if (input->current_cursor == d) {
input->current_cursor = NULL;
}
// Buffers for system cursors must not be destroyed.
if (d->is_system_cursor) {
if (d->cursor_data.system.frame_callback) {
@@ -896,6 +903,7 @@ static SDL_MouseButtonFlags SDLCALL Wayland_GetGlobalMouseState(float *x, float
int off_x, off_y;
result = viddata->input->buttons_pressed;
SDL_GetMouseState(x, y);
SDL_RelativeToGlobalForWindow(focus, focus->x, focus->y, &off_x, &off_y);
*x += off_x;
*y += off_y;
+73 -17
View File
@@ -27,6 +27,7 @@
#include "../../events/SDL_events_c.h"
#include "SDL_waylandclipboard.h"
#include "SDL_waylandcolor.h"
#include "SDL_waylandevents_c.h"
#include "SDL_waylandkeyboard.h"
#include "SDL_waylandmessagebox.h"
@@ -63,6 +64,7 @@
#include "xdg-output-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#include "xdg-toplevel-icon-v1-client-protocol.h"
#include "color-management-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -249,7 +251,7 @@ static int SDLCALL Wayland_DisplayPositionCompare(const void *a, const void *b)
* The primary is determined by the following criteria, in order:
* - Landscape is preferred over portrait
* - The highest native resolution
* - TODO: A higher HDR range is preferred
* - A higher HDR range is preferred
* - Higher refresh is preferred (ignoring small differences)
* - Lower scale values are preferred (larger display)
*/
@@ -271,6 +273,7 @@ static int Wayland_GetPrimaryDisplay(SDL_VideoData *vid)
int best_width = 0;
int best_height = 0;
double best_scale = 0.0;
float best_headroom = 0.0f;
int best_refresh = 0;
bool best_is_landscape = false;
int best_index = 0;
@@ -286,11 +289,15 @@ static int Wayland_GetPrimaryDisplay(SDL_VideoData *vid)
if (d->pixel_width > best_width || d->pixel_height > best_height) {
have_new_best = true;
} else if (d->pixel_width == best_width && d->pixel_height == best_height) {
if (d->refresh - best_refresh > REFRESH_DELTA) { // Favor a higher refresh rate, but ignore small differences (e.g. 59.97 vs 60.1)
have_new_best = true;
} else if (d->scale_factor < best_scale && SDL_abs(d->refresh - best_refresh) <= REFRESH_DELTA) {
// Prefer a lower scale display if the difference in refresh rate is small.
if (d->HDR.HDR_headroom > best_headroom) { // Favor a higher HDR luminance range
have_new_best = true;
} else if (d->HDR.HDR_headroom == best_headroom) {
if (d->refresh - best_refresh > REFRESH_DELTA) { // Favor a higher refresh rate, but ignore small differences (e.g. 59.97 vs 60.1)
have_new_best = true;
} else if (d->scale_factor < best_scale && SDL_abs(d->refresh - best_refresh) <= REFRESH_DELTA) {
// Prefer a lower scale display if the difference in refresh rate is small.
have_new_best = true;
}
}
}
}
@@ -299,6 +306,7 @@ static int Wayland_GetPrimaryDisplay(SDL_VideoData *vid)
best_width = d->pixel_width;
best_height = d->pixel_height;
best_scale = d->scale_factor;
best_headroom = d->HDR.HDR_headroom;
best_refresh = d->refresh;
best_is_landscape = is_landscape;
best_index = i;
@@ -438,9 +446,6 @@ static void Wayland_DeleteDevice(SDL_VideoDevice *device)
WAYLAND_wl_display_disconnect(data->display);
SDL_ClearProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER);
}
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(data);
SDL_free(device);
SDL_WAYLAND_UnloadSymbols();
@@ -581,7 +586,6 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
}
device->internal = data;
device->wakeup_lock = SDL_CreateMutex();
// Set the function pointers
device->VideoInit = Wayland_VideoInit;
@@ -630,6 +634,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
device->SetWindowIcon = Wayland_SetWindowIcon;
device->GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels;
device->GetWindowContentScale = Wayland_GetWindowContentScale;
device->GetWindowICCProfile = Wayland_GetWindowICCProfile;
device->GetDisplayForWindow = Wayland_GetDisplayForWindow;
device->DestroyWindow = Wayland_DestroyWindow;
device->SetWindowHitTest = Wayland_SetWindowHitTest;
@@ -637,6 +642,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
device->SyncWindow = Wayland_SyncWindow;
device->SetWindowFocusable = Wayland_SetWindowFocusable;
#ifdef SDL_USE_LIBDBUS
if (SDL_SystemTheme_Init())
@@ -685,13 +691,15 @@ static SDL_VideoDevice *Wayland_Fallback_CreateDevice(void)
VideoBootStrap Wayland_preferred_bootstrap = {
WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
Wayland_Preferred_CreateDevice,
Wayland_ShowMessageBox
Wayland_ShowMessageBox,
true
};
VideoBootStrap Wayland_bootstrap = {
WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
Wayland_Fallback_CreateDevice,
Wayland_ShowMessageBox
Wayland_ShowMessageBox,
false
};
static void xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output,
@@ -716,7 +724,7 @@ static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xd
static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
{
SDL_DisplayData *internal = (void *)data;
SDL_DisplayData *internal = data;
/*
* xdg-output.done events are deprecated and only apply below version 3 of the protocol.
@@ -1048,6 +1056,8 @@ static void display_handle_done(void *data,
AddEmulatedModes(internal, native_mode.w, native_mode.h);
}
SDL_SetDisplayHDRProperties(dpy, &internal->HDR);
if (internal->display == 0) {
// First time getting display info, initialize the VideoDisplay
if (internal->physical_width_mm >= internal->physical_height_mm) {
@@ -1105,6 +1115,18 @@ static const struct wl_output_listener output_listener = {
display_handle_description // Version 4
};
static void handle_output_image_description_changed(void *data,
struct wp_color_management_output_v1 *wp_color_management_output_v1)
{
SDL_DisplayData *display = (SDL_DisplayData *)data;
// wl_display.done is called after this event, so the display HDR status will be updated there.
Wayland_GetColorInfoForOutput(display, false);
}
static const struct wp_color_management_output_v1_listener wp_color_management_output_listener = {
handle_output_image_description_changed
};
static bool Wayland_add_display(SDL_VideoData *d, uint32_t id, uint32_t version)
{
struct wl_output *output;
@@ -1134,6 +1156,11 @@ static bool Wayland_add_display(SDL_VideoData *d, uint32_t id, uint32_t version)
data->xdg_output = zxdg_output_manager_v1_get_xdg_output(data->videodata->xdg_output_manager, output);
zxdg_output_v1_add_listener(data->xdg_output, &xdg_output_listener, data);
}
if (data->videodata->wp_color_manager_v1) {
data->wp_color_management_output = wp_color_manager_v1_get_output(data->videodata->wp_color_manager_v1, output);
wp_color_management_output_v1_add_listener(data->wp_color_management_output, &wp_color_management_output_listener, data);
Wayland_GetColorInfoForOutput(data, true);
}
return true;
}
@@ -1151,6 +1178,11 @@ static void Wayland_free_display(SDL_VideoDisplay *display, bool send_event)
SDL_free(display_data->wl_output_name);
if (display_data->wp_color_management_output) {
Wayland_FreeColorInfoState(display_data->color_info_state);
wp_color_management_output_v1_destroy(display_data->wp_color_management_output);
}
if (display_data->xdg_output) {
zxdg_output_v1_destroy(display_data->xdg_output);
}
@@ -1178,13 +1210,23 @@ static void Wayland_FinalizeDisplays(SDL_VideoData *vid)
static void Wayland_init_xdg_output(SDL_VideoData *d)
{
for(int i = 0; i < d->output_count; ++i) {
for (int i = 0; i < d->output_count; ++i) {
SDL_DisplayData *disp = d->output_list[i];
disp->xdg_output = zxdg_output_manager_v1_get_xdg_output(disp->videodata->xdg_output_manager, disp->output);
zxdg_output_v1_add_listener(disp->xdg_output, &xdg_output_listener, disp);
}
}
static void Wayland_InitColorManager(SDL_VideoData *d)
{
for (int i = 0; i < d->output_count; ++i) {
SDL_DisplayData *disp = d->output_list[i];
disp->wp_color_management_output = wp_color_manager_v1_get_output(disp->videodata->wp_color_manager_v1, disp->output);
wp_color_management_output_v1_add_listener(disp->wp_color_management_output, &wp_color_management_output_listener, disp);
Wayland_GetColorInfoForOutput(disp, true);
}
}
static void handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial)
{
xdg_wm_base_pong(xdg, serial);
@@ -1278,6 +1320,9 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
d->xdg_toplevel_icon_manager_v1 = wl_registry_bind(d->registry, id, &xdg_toplevel_icon_manager_v1_interface, 1);
} else if (SDL_strcmp(interface, "frog_color_management_factory_v1") == 0) {
d->frog_color_management_factory_v1 = wl_registry_bind(d->registry, id, &frog_color_management_factory_v1_interface, 1);
} else if (SDL_strcmp(interface, "wp_color_manager_v1") == 0) {
d->wp_color_manager_v1 = wl_registry_bind(d->registry, id, &wp_color_manager_v1_interface, 1);
Wayland_InitColorManager(d);
}
}
@@ -1366,10 +1411,16 @@ bool Wayland_VideoInit(SDL_VideoDevice *_this)
// First roundtrip to receive all registry objects.
WAYLAND_wl_display_roundtrip(data->display);
// Require viewports for display scaling.
if (data->scale_to_display_enabled && !data->viewporter) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Display scaling requires the missing 'wp_viewporter' protocol: disabling");
data->scale_to_display_enabled = false;
// Require viewports and xdg-output for display scaling.
if (data->scale_to_display_enabled) {
if (!data->viewporter) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Display scaling requires the missing 'wp_viewporter' protocol: disabling");
data->scale_to_display_enabled = false;
}
if (!data->xdg_output_manager) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "wayland: Display scaling requires the missing 'zxdg_output_manager_v1' protocol: disabling");
data->scale_to_display_enabled = false;
}
}
// Now that we have all the protocols, load libdecor if applicable
@@ -1559,6 +1610,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
data->frog_color_management_factory_v1 = NULL;
}
if (data->wp_color_manager_v1) {
wp_color_manager_v1_destroy(data->wp_color_manager_v1);
data->wp_color_manager_v1 = NULL;
}
if (data->compositor) {
wl_compositor_destroy(data->compositor);
data->compositor = NULL;
@@ -83,6 +83,7 @@ struct SDL_VideoData
struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1;
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1;
struct frog_color_management_factory_v1 *frog_color_management_factory_v1;
struct wp_color_manager_v1 *wp_color_manager_v1;
struct zwp_tablet_manager_v2 *tablet_manager;
struct xkb_context *xkb_context;
@@ -102,6 +103,7 @@ struct SDL_DisplayData
SDL_VideoData *videodata;
struct wl_output *output;
struct zxdg_output_v1 *xdg_output;
struct wp_color_management_output_v1 *wp_color_management_output;
char *wl_output_name;
double scale_factor;
uint32_t registry_id;
@@ -111,9 +113,12 @@ struct SDL_DisplayData
SDL_DisplayOrientation orientation;
int physical_width_mm, physical_height_mm;
bool has_logical_position, has_logical_size;
bool running_colorspace_event_queue;
SDL_HDROutputProperties HDR;
SDL_DisplayID display;
SDL_VideoDisplay placeholder;
int wl_output_done_count;
struct Wayland_ColorInfoState *color_info_state;
};
// Needed here to get wl_surface declaration, fixes GitHub#4594
+192 -78
View File
@@ -23,6 +23,8 @@
#ifdef SDL_VIDEO_DRIVER_WAYLAND
#include <sys/mman.h>
#include "../SDL_sysvideo.h"
#include "../../events/SDL_events_c.h"
#include "../../core/unix/SDL_appid.h"
@@ -31,6 +33,7 @@
#include "SDL_waylandwindow.h"
#include "SDL_waylandvideo.h"
#include "../../SDL_hints_c.h"
#include "SDL_waylandcolor.h"
#include "alpha-modifier-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@@ -43,6 +46,7 @@
#include "xdg-dialog-v1-client-protocol.h"
#include "frog-color-management-v1-client-protocol.h"
#include "xdg-toplevel-icon-v1-client-protocol.h"
#include "color-management-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -177,7 +181,7 @@ static void SetMinMaxDimensions(SDL_Window *window)
#ifdef HAVE_LIBDECOR_H
if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
if (!wind->shell_surface.libdecor.initial_configure_seen || !wind->shell_surface.libdecor.frame) {
if (!wind->shell_surface.libdecor.frame) {
return; // Can't do anything yet, wait for ShowWindow
}
/* No need to change these values if the window is non-resizable,
@@ -194,7 +198,7 @@ static void SetMinMaxDimensions(SDL_Window *window)
} else
#endif
if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) {
if (wind->shell_surface.xdg.toplevel.xdg_toplevel == NULL) {
if (!wind->shell_surface.xdg.toplevel.xdg_toplevel) {
return; // Can't do anything yet, wait for ShowWindow
}
xdg_toplevel_set_min_size(wind->shell_surface.xdg.toplevel.xdg_toplevel,
@@ -211,32 +215,32 @@ static void EnsurePopupPositionIsValid(SDL_Window *window, int *x, int *y)
int adj_count = 0;
/* Per the xdg-positioner spec, child popup windows must intersect or at
* least be partially adjacent to the parent window.
* least be partially adjoining the parent window.
*
* Failure to ensure this on a compositor that enforces this restriction
* can result in behavior ranging from the window being spuriously closed
* to a protocol violation.
*/
if (*x + window->w < 0) {
if (*x + window->w <= 0) {
*x = -window->w;
++adj_count;
}
if (*y + window->h < 0) {
if (*y + window->h <= 0) {
*y = -window->h;
++adj_count;
}
if (*x > window->parent->w) {
if (*x >= window->parent->w) {
*x = window->parent->w;
++adj_count;
}
if (*y > window->parent->h) {
if (*y >= window->parent->h) {
*y = window->parent->h;
++adj_count;
}
/* If adjustment was required on the x and y axes, the popup is aligned with
* the parent corner-to-corner and is neither overlapping nor adjacent, so it
* must be nudged by 1 to be considered adjacent.
* the parent corner-to-corner and is neither overlapping nor adjoining, so it
* must be nudged by 1 to be considered adjoining.
*/
if (adj_count > 1) {
*x += *x < 0 ? 1 : -1;
@@ -703,6 +707,9 @@ static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time
}
}
// Create the pointer confinement region, if necessary.
Wayland_input_confine_pointer(wind->waylandData->input, wind->sdlwindow);
/* If the window was initially set to the suspended state, send the occluded event now,
* as we don't want to mark the window as occluded until at least one frame has been submitted.
*/
@@ -746,7 +753,9 @@ static void handle_configure_xdg_shell_surface(void *data, struct xdg_surface *x
xdg_surface_ack_configure(xdg, serial);
}
wind->shell_surface.xdg.initial_configure_seen = true;
if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME;
}
}
static const struct xdg_surface_listener shell_surface_listener_xdg = {
@@ -963,10 +972,6 @@ static void handle_configure_xdg_toplevel(void *data,
wind->active = active;
window->tiled = tiled;
wind->resizing = resizing;
if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME;
}
}
static void handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel)
@@ -1115,9 +1120,7 @@ static void handle_configure_zxdg_decoration(void *data,
WAYLAND_wl_display_roundtrip(internal->waylandData->display);
Wayland_HideWindow(device, window);
SDL_zero(internal->shell_surface);
internal->shell_surface_type = WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR;
Wayland_ShowWindow(device, window);
}
}
@@ -1399,11 +1402,8 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
libdecor_state_free(state);
}
if (!wind->shell_surface.libdecor.initial_configure_seen) {
LibdecorGetMinContentSize(frame, &wind->system_limits.min_width, &wind->system_limits.min_height);
wind->shell_surface.libdecor.initial_configure_seen = true;
}
if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
LibdecorGetMinContentSize(frame, &wind->system_limits.min_width, &wind->system_limits.min_height);
wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME;
}
@@ -1647,7 +1647,19 @@ static const struct frog_color_managed_surface_listener frog_surface_listener =
frog_preferred_metadata_handler
};
static void SetKeyboardFocus(SDL_Window *window, bool set_focus)
static void feedback_surface_preferred_changed(void *data,
struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1,
uint32_t identity)
{
SDL_WindowData *wind = (SDL_WindowData *)data;
Wayland_GetColorInfoForWindow(wind, false);
}
static const struct wp_color_management_surface_feedback_v1_listener color_management_surface_feedback_listener = {
feedback_surface_preferred_changed
};
static void Wayland_SetKeyboardFocus(SDL_Window *window, bool set_focus)
{
SDL_Window *toplevel = window;
@@ -1656,7 +1668,7 @@ static void SetKeyboardFocus(SDL_Window *window, bool set_focus)
toplevel = toplevel->parent;
}
toplevel->internal->keyboard_focus = window;
toplevel->keyboard_focus = window;
if (set_focus && !window->is_hiding && !window->is_destroying) {
SDL_SetKeyboardFocus(window);
@@ -1800,12 +1812,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
}
/* The window was hidden, but the sync point hasn't yet been reached.
* Pump events to avoid a possible protocol violation.
*/
if (data->show_hide_sync_required) {
// Always roundtrip to ensure there are no pending buffer attachments.
do {
WAYLAND_wl_display_roundtrip(c->display);
}
} while (data->show_hide_sync_required);
data->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE;
@@ -1838,7 +1848,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
} else {
libdecor_frame_set_app_id(data->shell_surface.libdecor.frame, data->app_id);
libdecor_frame_map(data->shell_surface.libdecor.frame);
libdecor_frame_set_visibility(data->shell_surface.libdecor.frame, !(window->flags & SDL_WINDOW_BORDERLESS));
if (window->flags & SDL_WINDOW_BORDERLESS) {
// Note: Calling this with 'true' immediately after mapping will cause the libdecor Cairo plugin to crash.
libdecor_frame_set_visibility(data->shell_surface.libdecor.frame, false);
}
if (c->zxdg_exporter_v2) {
data->exported = zxdg_exporter_v2_export_toplevel(c->zxdg_exporter_v2, data->surface);
@@ -1883,8 +1896,9 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
data->shell_surface.xdg.popup.xdg_positioner = xdg_wm_base_create_positioner(c->shell.xdg);
xdg_positioner_set_anchor(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_anchor_rect(data->shell_surface.xdg.popup.xdg_positioner, 0, 0, parent->internal->current.logical_width, parent->internal->current.logical_width);
xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.popup.xdg_positioner,
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
const Uint32 constraint = window->constrain_popup ? (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) : XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE;
xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.popup.xdg_positioner, constraint);
xdg_positioner_set_gravity(data->shell_surface.xdg.popup.xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
xdg_positioner_set_size(data->shell_surface.xdg.popup.xdg_positioner, data->current.logical_width, data->current.logical_height);
@@ -1913,8 +1927,8 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
wl_region_add(region, 0, 0, 0, 0);
wl_surface_set_input_region(data->surface, region);
wl_region_destroy(region);
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
} else if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
Wayland_SetKeyboardFocus(window, true);
}
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER, data->shell_surface.xdg.popup.xdg_popup);
@@ -1962,7 +1976,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
#ifdef HAVE_LIBDECOR_H
if (data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
if (data->shell_surface.libdecor.frame) {
while (!data->shell_surface.libdecor.initial_configure_seen) {
while (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
WAYLAND_wl_display_flush(c->display);
WAYLAND_wl_display_dispatch(c->display);
}
@@ -1976,7 +1990,7 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
*/
wl_surface_commit(data->surface);
if (data->shell_surface.xdg.surface) {
while (!data->shell_surface.xdg.initial_configure_seen) {
while (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
WAYLAND_wl_display_flush(c->display);
WAYLAND_wl_display_dispatch(c->display);
}
@@ -2033,6 +2047,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
struct wl_callback *cb = wl_display_sync(_this->internal->display);
wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
data->showing_window = true;
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
data->showing_window = false;
// Send an exposure event to signal that the client should draw.
if (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
@@ -2057,21 +2075,10 @@ static void Wayland_ReleasePopup(SDL_VideoDevice *_this, SDL_Window *popup)
return;
}
if (popup->flags & SDL_WINDOW_POPUP_MENU) {
SDL_Window *new_focus = popup->parent;
bool set_focus = popup == SDL_GetKeyboardFocus();
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
new_focus = new_focus->parent;
// If some window in the chain currently had focus, set it to the new lowest-level window.
if (!set_focus) {
set_focus = new_focus == SDL_GetKeyboardFocus();
}
}
SetKeyboardFocus(new_focus, set_focus);
if ((popup->flags & SDL_WINDOW_POPUP_MENU) && !(popup->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(popup, &new_focus);
Wayland_SetKeyboardFocus(new_focus, set_focus);
}
xdg_popup_destroy(popupdata->shell_surface.xdg.popup.xdg_popup);
@@ -2109,12 +2116,6 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
wind->server_decoration = NULL;
}
// Be sure to detach after this is done, otherwise ShowWindow crashes!
if (wind->shell_surface_type != WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP) {
wl_surface_attach(wind->surface, NULL, 0, 0);
wl_surface_commit(wind->surface);
}
// Clean up the export handle.
if (wind->exported) {
zxdg_exported_v2_destroy(wind->exported);
@@ -2132,26 +2133,31 @@ void Wayland_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
if (wind->shell_surface.libdecor.frame) {
libdecor_frame_unref(wind->shell_surface.libdecor.frame);
wind->shell_surface.libdecor.frame = NULL;
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL);
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL);
}
} else
#endif
{
if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_POPUP) {
Wayland_ReleasePopup(_this, window);
} else if (wind->shell_surface.xdg.toplevel.xdg_toplevel) {
xdg_toplevel_destroy(wind->shell_surface.xdg.toplevel.xdg_toplevel);
wind->shell_surface.xdg.toplevel.xdg_toplevel = NULL;
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL);
}
if (wind->shell_surface.xdg.surface) {
xdg_surface_destroy(wind->shell_surface.xdg.surface);
wind->shell_surface.xdg.surface = NULL;
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL);
Wayland_ReleasePopup(_this, window);
} else if (wind->shell_surface.xdg.toplevel.xdg_toplevel) {
xdg_toplevel_destroy(wind->shell_surface.xdg.toplevel.xdg_toplevel);
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, NULL);
}
if (wind->shell_surface.xdg.surface) {
xdg_surface_destroy(wind->shell_surface.xdg.surface);
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, NULL);
}
}
// Attach a null buffer to unmap the surface.
wl_surface_attach(wind->surface, NULL, 0, 0);
wl_surface_commit(wind->surface);
SDL_zero(wind->shell_surface);
wind->show_hide_sync_required = true;
struct wl_callback *cb = wl_display_sync(_this->internal->display);
wl_callback_add_listener(cb, &show_hide_sync_listener, (void*)((uintptr_t)window->id));
@@ -2255,6 +2261,11 @@ SDL_FullscreenResult Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Win
return SDL_FULLSCREEN_FAILED;
}
// Drop fullscreen leave requests when showing the window.
if (wind->showing_window && fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
return SDL_FULLSCREEN_SUCCEEDED;
}
if (wind->show_hide_sync_required) {
WAYLAND_wl_display_roundtrip(_this->internal->display);
}
@@ -2280,8 +2291,8 @@ SDL_FullscreenResult Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Win
}
// Don't send redundant fullscreen set/unset events.
if (fullscreen != wind->is_fullscreen) {
wind->fullscreen_was_positioned = fullscreen;
if (!!fullscreen != wind->is_fullscreen) {
wind->fullscreen_was_positioned = !!fullscreen;
SetFullscreen(window, fullscreen ? output : NULL);
} else if (wind->is_fullscreen) {
/*
@@ -2309,6 +2320,11 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *wind = window->internal;
// Drop restore requests when showing the window.
if (wind->showing_window) {
return;
}
// Not currently fullscreen or maximized, and no state pending; nothing to do.
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) &&
!wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) {
@@ -2594,7 +2610,11 @@ bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Proper
}
if (!custom_surface_role) {
if (c->frog_color_management_factory_v1) {
if (c->wp_color_manager_v1) {
data->wp_color_management_surface_feedback = wp_color_manager_v1_get_surface_feedback(c->wp_color_manager_v1, data->surface);
wp_color_management_surface_feedback_v1_add_listener(data->wp_color_management_surface_feedback, &color_management_surface_feedback_listener, data);
Wayland_GetColorInfoForWindow(data, true);
} else if (c->frog_color_management_factory_v1) {
data->frog_color_managed_surface = frog_color_management_factory_v1_get_color_managed_surface(c->frog_color_management_factory_v1, data->surface);
frog_color_managed_surface_add_listener(data->frog_color_managed_surface, &frog_surface_listener, data);
}
@@ -2837,28 +2857,56 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
struct xdg_toplevel *toplevel = NULL;
if (!_this->internal->xdg_toplevel_icon_manager_v1) {
return SDL_SetError("wayland: cannot set icon; xdg_toplevel_icon_v1 protocol not supported");
return SDL_SetError("wayland: cannot set icon; required xdg_toplevel_icon_v1 protocol not supported");
}
if (icon->w != icon->h) {
return SDL_SetError("wayland: icon width and height must be equal, got %ix%i", icon->w, icon->h);
}
int image_count = 0;
SDL_Surface **images = SDL_GetSurfaceImages(icon, &image_count);
if (!images || !image_count) {
return false;
}
// Release the old icon resources.
if (wind->xdg_toplevel_icon_v1) {
xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
wind->xdg_toplevel_icon_v1 = NULL;
}
// TODO: Add high-DPI icon support
Wayland_ReleaseSHMBuffer(&wind->icon);
if (!Wayland_AllocSHMBuffer(icon->w, icon->h, &wind->icon)) {
return SDL_SetError("wayland: failed to allocate SHM buffer for the icon");
for (int i = 0; i < wind->icon_buffer_count; ++i) {
Wayland_ReleaseSHMBuffer(&wind->icon_buffers[i]);
}
SDL_PremultiplyAlpha(icon->w, icon->h, icon->format, icon->pixels, icon->pitch, SDL_PIXELFORMAT_ARGB8888, wind->icon.shm_data, icon->w * 4, true);
SDL_free(wind->icon_buffers);
wind->icon_buffer_count = 0;
wind->xdg_toplevel_icon_v1 = xdg_toplevel_icon_manager_v1_create_icon(_this->internal->xdg_toplevel_icon_manager_v1);
xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, wind->icon.wl_buffer, 1);
wind->icon_buffers = SDL_calloc(image_count, sizeof(struct Wayland_SHMBuffer));
if (!wind->icon_buffers) {
goto failure_cleanup;
}
for (int i = 0; i < image_count; ++i) {
if (images[i]->w == images[i]->h) {
struct Wayland_SHMBuffer *buffer = &wind->icon_buffers[wind->icon_buffer_count];
if (!Wayland_AllocSHMBuffer(images[i]->w, images[i]->h, buffer)) {
SDL_SetError("wayland: failed to allocate SHM buffer for the icon");
goto failure_cleanup;
}
SDL_PremultiplyAlpha(images[i]->w, images[i]->h, images[i]->format, images[i]->pixels, images[i]->pitch, SDL_PIXELFORMAT_ARGB8888, buffer->shm_data, images[i]->w * 4, true);
const int scale = (int)SDL_ceil((double)images[i]->w / (double)icon->w);
xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer->wl_buffer, scale);
wind->icon_buffer_count++;
} else {
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; skipping", images[i]->w, images[i]->h, i);
}
}
SDL_free(images);
#ifdef HAVE_LIBDECOR_H
if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR && wind->shell_surface.libdecor.frame) {
@@ -2874,6 +2922,42 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
}
return true;
failure_cleanup:
if (wind->xdg_toplevel_icon_v1) {
xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
wind->xdg_toplevel_icon_v1 = NULL;
}
for (int i = 0; i < wind->icon_buffer_count; ++i) {
Wayland_ReleaseSHMBuffer(&wind->icon_buffers[i]);
}
SDL_free(wind->icon_buffers);
wind->icon_buffers = NULL;
wind->icon_buffer_count = 0;
return false;
}
void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size)
{
SDL_WindowData *wind = window->internal;
void *ret = NULL;
if (wind->icc_size > 0) {
void *icc_map = mmap(NULL, wind->icc_size, PROT_READ, MAP_PRIVATE, wind->icc_fd, 0);
if (icc_map != MAP_FAILED) {
ret = SDL_malloc(wind->icc_size);
if (ret) {
*size = wind->icc_size;
SDL_memcpy(ret, icc_map, *size);
}
munmap(icc_map, wind->icc_size);
}
}
return ret;
}
bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
@@ -2887,6 +2971,27 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
return true;
}
bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
{
if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
Wayland_SetKeyboardFocus(new_focus, set_focus);
} else if (focusable) {
if (SDL_ShouldFocusPopup(window)) {
Wayland_SetKeyboardFocus(window, true);
}
}
}
return true;
}
return SDL_SetError("wayland: focus can only be toggled on popup menu windows");
}
void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
{
SDL_WindowData *wind = window->internal;
@@ -2995,6 +3100,11 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
frog_color_managed_surface_destroy(wind->frog_color_managed_surface);
}
if (wind->wp_color_management_surface_feedback) {
Wayland_FreeColorInfoState(wind->color_info_state);
wp_color_management_surface_feedback_v1_destroy(wind->wp_color_management_surface_feedback);
}
SDL_free(wind->outputs);
SDL_free(wind->app_id);
@@ -3018,7 +3128,11 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
}
Wayland_ReleaseSHMBuffer(&wind->icon);
for (int i = 0; i < wind->icon_buffer_count; ++i) {
Wayland_ReleaseSHMBuffer(&wind->icon_buffers[i]);
}
SDL_free(wind->icon_buffers);
wind->icon_buffer_count = 0;
SDL_free(wind);
WAYLAND_wl_display_flush(data->display);
@@ -48,7 +48,6 @@ struct SDL_WindowData
struct
{
struct libdecor_frame *frame;
bool initial_configure_seen;
} libdecor;
#endif
struct
@@ -66,7 +65,6 @@ struct SDL_WindowData
struct xdg_positioner *xdg_positioner;
} popup;
};
bool initial_configure_seen;
} xdg;
} shell_surface;
enum
@@ -116,18 +114,20 @@ struct SDL_WindowData
struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1;
struct xdg_toplevel_icon_v1 *xdg_toplevel_icon_v1;
struct frog_color_managed_surface *frog_color_managed_surface;
struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback;
struct Wayland_ColorInfoState *color_info_state;
SDL_AtomicInt swap_interval_ready;
SDL_DisplayData **outputs;
int num_outputs;
SDL_Window *keyboard_focus;
char *app_id;
double scale_factor;
struct Wayland_SHMBuffer icon;
struct Wayland_SHMBuffer *icon_buffers;
int icon_buffer_count;
struct
{
@@ -184,6 +184,8 @@ struct SDL_WindowData
int fullscreen_deadline_count;
int maximized_restored_deadline_count;
Uint64 last_focus_event_time_ns;
int icc_fd;
Uint32 icc_size;
bool floating;
bool suspended;
bool resizing;
@@ -192,6 +194,7 @@ struct SDL_WindowData
bool is_fullscreen;
bool fullscreen_exclusive;
bool drop_fullscreen_requests;
bool showing_window;
bool fullscreen_was_positioned;
bool show_hide_sync_required;
bool scale_to_display;
@@ -230,7 +233,9 @@ extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this);
extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
extern bool Wayland_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
extern float Wayland_GetWindowContentScale(SDL_VideoDevice *_this, SDL_Window *window);
extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled);
extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
@@ -145,7 +145,7 @@ static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size)
pbfh->bfSize = (DWORD)bmp_size;
pbfh->bfReserved1 = 0;
pbfh->bfReserved2 = 0;
pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + color_table_size);
pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + pbih->biSize + color_table_size);
SDL_memcpy((Uint8 *)bmp + sizeof(BITMAPFILEHEADER), dib, dib_size);
*size = bmp_size;
}
+196 -88
View File
@@ -317,7 +317,42 @@ static void WIN_CheckAsyncMouseRelease(Uint64 timestamp, SDL_WindowData *data)
data->mouse_button_flags = (WPARAM)-1;
}
static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus)
static void WIN_UpdateMouseCapture(void)
{
SDL_Window *focusWindow = SDL_GetKeyboardFocus();
if (focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
SDL_WindowData *data = focusWindow->internal;
if (!data->mouse_tracked) {
POINT cursorPos;
if (GetCursorPos(&cursorPos) && ScreenToClient(data->hwnd, &cursorPos)) {
bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
SDL_MouseID mouseID = SDL_GLOBAL_MOUSE_ID;
SDL_SendMouseMotion(WIN_GetEventTimestamp(), data->window, mouseID, false, (float)cursorPos.x, (float)cursorPos.y);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
!swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT,
(GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
!swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT,
(GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
SDL_BUTTON_MIDDLE,
(GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
SDL_BUTTON_X1,
(GetAsyncKeyState(VK_XBUTTON1) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
SDL_BUTTON_X2,
(GetAsyncKeyState(VK_XBUTTON2) & 0x8000) != 0);
}
}
}
}
static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, DWORD pos)
{
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
@@ -350,11 +385,12 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus)
}
}
SDL_SetKeyboardFocus(data->keyboard_focus ? data->keyboard_focus : window);
SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window);
// In relative mode we are guaranteed to have mouse focus if we have keyboard focus
if (!SDL_GetMouse()->relative_mode) {
GetCursorPos(&cursorPos);
cursorPos.x = (LONG)GET_X_LPARAM(pos);
cursorPos.y = (LONG)GET_Y_LPARAM(pos);
ScreenToClient(hwnd, &cursorPos);
SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)cursorPos.x, (float)cursorPos.y);
}
@@ -437,11 +473,6 @@ static SDL_MOUSE_EVENT_SOURCE GetMouseMessageSource(ULONG extrainfo)
return SDL_MOUSE_EVENT_SOURCE_PEN;
}
}
/* Sometimes WM_INPUT events won't have the correct touch signature,
so we have to rely purely on the touch bit being set. */
if (SDL_TouchDevicesAvailable() && extrainfo & 0x80) {
return SDL_MOUSE_EVENT_SOURCE_TOUCH;
}
return SDL_MOUSE_EVENT_SOURCE_MOUSE;
}
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
@@ -579,13 +610,14 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
return;
}
if (GetMouseMessageSource(rawmouse->ulExtraInformation) != SDL_MOUSE_EVENT_SOURCE_MOUSE) {
if (GetMouseMessageSource(rawmouse->ulExtraInformation) != SDL_MOUSE_EVENT_SOURCE_MOUSE ||
(SDL_TouchDevicesAvailable() && (rawmouse->ulExtraInformation & 0x80) == 0x80)) {
return;
}
SDL_WindowData *windowdata = window->internal;
if (haveMotion) {
if (haveMotion && !windowdata->in_modal_loop) {
if (!isAbsolute) {
SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)dx, (float)dy);
} else {
@@ -700,6 +732,10 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
float fAmount = (float)amount / WHEEL_DELTA;
SDL_SendMouseWheel(WIN_GetEventTimestamp(), window, mouseID, fAmount, 0.0f, SDL_MOUSEWHEEL_NORMAL);
}
/* Invalidate the mouse button flags. If we don't do this then disabling raw input
will cause held down mouse buttons to persist when released. */
windowdata->mouse_button_flags = (WPARAM)-1;
}
}
@@ -861,31 +897,96 @@ static bool HasDeviceID(Uint32 deviceID, const Uint32 *list, int count)
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
static void GetDeviceName(HDEVINFO devinfo, const char *instance, char *name, size_t len)
static char *GetDeviceName(HANDLE hDevice, HDEVINFO devinfo, const char *instance, const char *default_name, bool hid_loaded)
{
name[0] = '\0';
char *vendor_name = NULL;
char *product_name = NULL;
char *name = NULL;
SP_DEVINFO_DATA data;
SDL_zero(data);
data.cbSize = sizeof(data);
for (DWORD i = 0;; ++i) {
if (!SetupDiEnumDeviceInfo(devinfo, i, &data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
break;
} else {
continue;
// These are 126 for USB, but can be longer for Bluetooth devices
WCHAR vend[256], prod[256];
vend[0] = 0;
prod[0] = 0;
HIDD_ATTRIBUTES attr;
attr.VendorID = 0;
attr.ProductID = 0;
attr.Size = sizeof(attr);
if (hid_loaded) {
char devName[MAX_PATH + 1];
UINT cap = sizeof(devName) - 1;
UINT len = GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, devName, &cap);
if (len != (UINT)-1) {
devName[len] = '\0';
// important: for devices with exclusive access mode as per
// https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections-opened-by-windows-for-system-use
// they can only be opened with a desired access of none instead of generic read.
HANDLE hFile = CreateFileA(devName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
SDL_HidD_GetAttributes(hFile, &attr);
SDL_HidD_GetManufacturerString(hFile, vend, sizeof(vend));
SDL_HidD_GetProductString(hFile, prod, sizeof(prod));
CloseHandle(hFile);
}
}
}
char DeviceInstanceId[64];
if (!SetupDiGetDeviceInstanceIdA(devinfo, &data, DeviceInstanceId, sizeof(DeviceInstanceId), NULL))
continue;
if (vend[0]) {
vendor_name = WIN_StringToUTF8W(vend);
}
if (SDL_strcasecmp(instance, DeviceInstanceId) == 0) {
SetupDiGetDeviceRegistryPropertyA(devinfo, &data, SPDRP_DEVICEDESC, NULL, (PBYTE)name, (DWORD)len, NULL);
return;
if (prod[0]) {
product_name = WIN_StringToUTF8W(prod);
} else {
SP_DEVINFO_DATA data;
SDL_zero(data);
data.cbSize = sizeof(data);
for (DWORD i = 0;; ++i) {
if (!SetupDiEnumDeviceInfo(devinfo, i, &data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
break;
} else {
continue;
}
}
char DeviceInstanceId[64];
if (!SetupDiGetDeviceInstanceIdA(devinfo, &data, DeviceInstanceId, sizeof(DeviceInstanceId), NULL))
continue;
if (SDL_strcasecmp(instance, DeviceInstanceId) == 0) {
DWORD size = 0;
if (SetupDiGetDeviceRegistryPropertyW(devinfo, &data, SPDRP_DEVICEDESC, NULL, (PBYTE)prod, sizeof(prod), &size)) {
// Make sure the device description is null terminated
size /= sizeof(*prod);
if (size >= SDL_arraysize(prod)) {
// Truncated description...
size = (SDL_arraysize(prod) - 1);
}
prod[size] = 0;
if (attr.VendorID || attr.ProductID) {
SDL_asprintf(&product_name, "%S (0x%.4x/0x%.4x)", prod, attr.VendorID, attr.ProductID);
} else {
product_name = WIN_StringToUTF8W(prod);
}
}
break;
}
}
}
if (!product_name && (attr.VendorID || attr.ProductID)) {
SDL_asprintf(&product_name, "%s (0x%.4x/0x%.4x)", default_name, attr.VendorID, attr.ProductID);
}
name = SDL_CreateDeviceName(attr.VendorID, attr.ProductID, vendor_name, product_name, default_name);
SDL_free(vendor_name);
SDL_free(product_name);
return name;
}
void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check)
@@ -931,6 +1032,7 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check
old_keyboards = SDL_GetKeyboards(&old_keyboard_count);
old_mice = SDL_GetMice(&old_mouse_count);
bool hid_loaded = WIN_LoadHIDDLL();
for (UINT i = 0; i < raw_device_count; i++) {
RID_DEVICE_INFO rdi;
char devName[MAX_PATH] = { 0 };
@@ -938,7 +1040,7 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check
UINT nameSize = SDL_arraysize(devName);
int vendor = 0, product = 0;
DWORD dwType = raw_devices[i].dwType;
char *instance, *ptr, name[64];
char *instance, *ptr, *name;
if (dwType != RIM_TYPEKEYBOARD && dwType != RIM_TYPEMOUSE) {
continue;
@@ -976,8 +1078,9 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check
SDL_KeyboardID keyboardID = (Uint32)(uintptr_t)raw_devices[i].hDevice;
AddDeviceID(keyboardID, &new_keyboards, &new_keyboard_count);
if (!HasDeviceID(keyboardID, old_keyboards, old_keyboard_count)) {
GetDeviceName(devinfo, instance, name, sizeof(name));
name = GetDeviceName(raw_devices[i].hDevice, devinfo, instance, "Keyboard", hid_loaded);
SDL_AddKeyboard(keyboardID, name, send_event);
SDL_free(name);
}
}
break;
@@ -986,8 +1089,9 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check
SDL_MouseID mouseID = (Uint32)(uintptr_t)raw_devices[i].hDevice;
AddDeviceID(mouseID, &new_mice, &new_mouse_count);
if (!HasDeviceID(mouseID, old_mice, old_mouse_count)) {
GetDeviceName(devinfo, instance, name, sizeof(name));
name = GetDeviceName(raw_devices[i].hDevice, devinfo, instance, "Mouse", hid_loaded);
SDL_AddMouse(mouseID, name, send_event);
SDL_free(name);
}
}
break;
@@ -995,6 +1099,9 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, bool initial_check
break;
}
}
if (hid_loaded) {
WIN_UnloadHIDDLL();
}
for (int i = old_keyboard_count; i--;) {
if (!HasDeviceID(old_keyboards[i], new_keyboards, new_keyboard_count)) {
@@ -1129,22 +1236,19 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_NCACTIVATE:
{
// Don't immediately clip the cursor in case we're clicking minimize/maximize buttons
// This is the only place that this flag is set. This causes all subsequent calls to
// WIN_UpdateClipCursor for this window to be no-ops in this frame's message-pumping.
// This flag is unset at the end of message pumping each frame for every window, and
// should never be carried over between frames.
data->skip_update_clipcursor = true;
data->postpone_clipcursor = true;
data->clipcursor_queued = true;
/* Update the focus here, since it's possible to get WM_ACTIVATE and WM_SETFOCUS without
actually being the foreground window, but this appears to get called in all cases where
the global foreground window changes to and from this window. */
WIN_UpdateFocus(data->window, !!wParam);
WIN_UpdateFocus(data->window, !!wParam, GetMessagePos());
} break;
case WM_ACTIVATE:
{
// Update the focus in case we changed focus to a child window and then away from the application
WIN_UpdateFocus(data->window, !!LOWORD(wParam));
WIN_UpdateFocus(data->window, !!LOWORD(wParam), GetMessagePos());
} break;
case WM_MOUSEACTIVATE:
@@ -1167,14 +1271,14 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_SETFOCUS:
{
// Update the focus in case it's changing between top-level windows in the same application
WIN_UpdateFocus(data->window, true);
WIN_UpdateFocus(data->window, true, GetMessagePos());
} break;
case WM_KILLFOCUS:
case WM_ENTERIDLE:
{
// Update the focus in case it's changing between top-level windows in the same application
WIN_UpdateFocus(data->window, false);
WIN_UpdateFocus(data->window, false, GetMessagePos());
} break;
case WM_POINTERENTER:
@@ -1255,8 +1359,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
const Uint64 timestamp = WIN_GetEventTimestamp();
SDL_Window *window = data->window;
const bool istouching = IS_POINTER_INCONTACT_WPARAM(wParam) && IS_POINTER_FIRSTBUTTON_WPARAM(wParam);
// if lifting off, do it first, so any motion changes don't cause app issues.
if (msg == WM_POINTERUP) {
if (!istouching) {
SDL_SendPenTouch(timestamp, pen, window, (pen_info.penFlags & PEN_FLAG_INVERTED) != 0, false);
}
@@ -1286,7 +1392,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
// if setting down, do it last, so the pen is positioned correctly from the first contact.
if (msg == WM_POINTERDOWN) {
if (istouching) {
SDL_SendPenTouch(timestamp, pen, window, (pen_info.penFlags & PEN_FLAG_INVERTED) != 0, true);
}
@@ -1302,9 +1408,8 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
window->flags & (SDL_WINDOW_MOUSE_RELATIVE_MODE | SDL_WINDOW_MOUSE_GRABBED) ||
(window->mouse_rect.w > 0 && window->mouse_rect.h > 0)
);
if (wish_clip_cursor) {
data->skip_update_clipcursor = false;
WIN_UpdateClipCursor(window);
if (wish_clip_cursor) { // queue clipcursor refresh on pump finish
data->clipcursor_queued = true;
}
}
@@ -1318,6 +1423,8 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
if (TrackMouseEvent(&trackMouseEvent)) {
data->mouse_tracked = true;
}
WIN_CheckAsyncMouseRelease(WIN_GetEventTimestamp(), data);
}
if (!data->videodata->raw_mouse_enabled) {
@@ -1327,6 +1434,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam));
}
}
} break;
case WM_LBUTTONUP:
@@ -1390,8 +1498,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !IsIconic(hwnd)) {
SDL_Mouse *mouse;
DWORD pos = GetMessagePos();
POINT cursorPos;
GetCursorPos(&cursorPos);
cursorPos.x = GET_X_LPARAM(pos);
cursorPos.y = GET_Y_LPARAM(pos);
ScreenToClient(hwnd, &cursorPos);
mouse = SDL_GetMouse();
if (!mouse->was_touch_mouse_events) { // we're not a touch handler causing a mouse leave?
@@ -1525,6 +1635,15 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_NCLBUTTONDOWN:
{
data->in_title_click = true;
// Fix for 500ms hang after user clicks on the title bar, but before moving mouse
// Reference: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
if (SendMessage(hwnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION) {
POINT cursorPos;
GetCursorPos(&cursorPos); // want the most current pos so as to not cause position change
ScreenToClient(hwnd, &cursorPos);
PostMessage(hwnd, WM_MOUSEMOVE, 0, cursorPos.x | (((Uint32)((Sint16)cursorPos.y)) << 16));
}
} break;
case WM_CAPTURECHANGED:
@@ -1675,7 +1794,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
if (!data->disable_move_size_events) {
if (GetClientRect(hwnd, &rect) && !WIN_IsRectEmpty(&rect)) {
if (GetClientRect(hwnd, &rect) && WIN_WindowRectValid(&rect)) {
ClientToScreen(hwnd, (LPPOINT) &rect);
ClientToScreen(hwnd, (LPPOINT) &rect + 1);
@@ -1687,7 +1806,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
// Moving the window from one display to another can change the size of the window (in the handling of SDL_EVENT_WINDOW_MOVED), so we need to re-query the bounds
if (GetClientRect(hwnd, &rect) && !WIN_IsRectEmpty(&rect)) {
if (GetClientRect(hwnd, &rect) && WIN_WindowRectValid(&rect)) {
w = rect.right;
h = rect.bottom;
@@ -1735,6 +1854,9 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
data->initial_size_rect.bottom = data->window->y + data->window->h;
SetTimer(hwnd, (UINT_PTR)SDL_IterateMainCallbacks, USER_TIMER_MINIMUM, NULL);
// Reset the keyboard, as we won't get any key up events during the modal loop
SDL_ResetKeyboard();
}
} break;
@@ -1742,6 +1864,15 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
{
if (wParam == (UINT_PTR)SDL_IterateMainCallbacks) {
SDL_OnWindowLiveResizeUpdate(data->window);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#if 0 // This locks up the Windows compositor when called by Steam; disabling until we understand why
// Make sure graphics operations are complete for smooth refresh
if (data->videodata->DwmFlush) {
data->videodata->DwmFlush();
}
#endif
#endif
return 0;
}
} break;
@@ -1967,7 +2098,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
RECT rect;
float x, y;
if (!GetClientRect(hwnd, &rect) || WIN_IsRectEmpty(&rect)) {
if (!GetClientRect(hwnd, &rect) || !WIN_WindowRectValid(&rect)) {
if (inputs) {
SDL_small_free(inputs, isstack);
}
@@ -2317,43 +2448,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
static void WIN_UpdateMouseCapture(void)
{
SDL_Window *focusWindow = SDL_GetKeyboardFocus();
if (focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
SDL_WindowData *data = focusWindow->internal;
if (!data->mouse_tracked) {
POINT cursorPos;
if (GetCursorPos(&cursorPos) && ScreenToClient(data->hwnd, &cursorPos)) {
bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
SDL_MouseID mouseID = SDL_GLOBAL_MOUSE_ID;
SDL_SendMouseMotion(WIN_GetEventTimestamp(), data->window, mouseID, false, (float)cursorPos.x, (float)cursorPos.y);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
!swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT,
(GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
!swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT,
(GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
SDL_BUTTON_MIDDLE,
(GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
SDL_BUTTON_X1,
(GetAsyncKeyState(VK_XBUTTON1) & 0x8000) != 0);
SDL_SendMouseButton(WIN_GetEventTimestamp(), data->window, mouseID,
SDL_BUTTON_X2,
(GetAsyncKeyState(VK_XBUTTON2) & 0x8000) != 0);
}
}
}
}
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
int WIN_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
{
if (g_WindowsEnableMessageLoop) {
@@ -2501,13 +2595,27 @@ void WIN_PumpEvents(SDL_VideoDevice *_this)
}
}
// Update the clipping rect in case someone else has stolen it
// fire queued clipcursor refreshes
if (_this) {
SDL_Window *window = _this->windows;
while (window) {
bool refresh_clipcursor = false;
SDL_WindowData *data = window->internal;
if (data && data->skip_update_clipcursor) {
data->skip_update_clipcursor = false;
if (data) {
refresh_clipcursor = data->clipcursor_queued;
data->clipcursor_queued = false; // Must be cleared unconditionally.
data->postpone_clipcursor = false; // Must be cleared unconditionally.
// Must happen before UpdateClipCursor.
// Although its occurrence currently
// always coincides with the queuing of
// clipcursor, it is logically distinct
// and this coincidence might no longer
// be true in the future.
// Ergo this placement concordantly
// conveys its unconditionality
// vis-a-vis the queuing of clipcursor.
}
if (refresh_clipcursor) {
WIN_UpdateClipCursor(window);
}
window = window->next;
@@ -27,9 +27,7 @@
#ifdef HAVE_GAMEINPUT_H
#define COBJMACROS
#include <gameinput.h>
#include "../../core/windows/SDL_gameinput.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/scancodes_windows.h"
@@ -61,7 +59,6 @@ typedef struct GAMEINPUT_Device
struct WIN_GameInputData
{
void *hGameInputDLL;
IGameInput *pGameInput;
GameInputCallbackToken gameinput_callback_token;
int num_devices;
@@ -237,20 +234,7 @@ bool WIN_InitGameInput(SDL_VideoDevice *_this)
goto done;
}
data->hGameInputDLL = SDL_LoadObject("gameinput.dll");
if (!data->hGameInputDLL) {
goto done;
}
typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput);
GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(data->hGameInputDLL, "GameInputCreate");
if (!GameInputCreateFunc) {
goto done;
}
hr = GameInputCreateFunc(&data->pGameInput);
if (FAILED(hr)) {
SDL_SetError("GameInputCreate failure with HRESULT of %08X", hr);
if (!SDL_InitGameInput(&data->pGameInput)) {
goto done;
}
@@ -293,6 +277,9 @@ static void GAMEINPUT_InitialMouseReading(WIN_GameInputData *data, SDL_Window *w
bool down = ((state.buttons & mask) != 0);
SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
}
// Invalidate mouse button flags
window->internal->mouse_button_flags = (WPARAM)-1;
}
}
@@ -323,6 +310,9 @@ static void GAMEINPUT_HandleMouseDelta(WIN_GameInputData *data, SDL_Window *wind
SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
}
}
// Invalidate mouse button flags
window->internal->mouse_button_flags = (WPARAM)-1;
}
if (delta.wheelX || delta.wheelY) {
float fAmountX = (float)delta.wheelX / WHEEL_DELTA;
@@ -485,12 +475,13 @@ void WIN_UpdateGameInput(SDL_VideoDevice *_this)
device->last_mouse_reading = reading;
}
if (hr != GAMEINPUT_E_READING_NOT_FOUND) {
// The last reading is too old, resynchronize
IGameInputReading_Release(device->last_mouse_reading);
device->last_mouse_reading = NULL;
if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindMouse, device->pDevice, &reading))) {
GAMEINPUT_HandleMouseDelta(data, window, device, device->last_mouse_reading, reading);
IGameInputReading_Release(device->last_mouse_reading);
device->last_mouse_reading = reading;
}
}
}
if (!device->last_mouse_reading) {
} else {
if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindMouse, device->pDevice, &reading))) {
GAMEINPUT_InitialMouseReading(data, window, device, reading);
device->last_mouse_reading = reading;
@@ -514,12 +505,13 @@ void WIN_UpdateGameInput(SDL_VideoDevice *_this)
device->last_keyboard_reading = reading;
}
if (hr != GAMEINPUT_E_READING_NOT_FOUND) {
// The last reading is too old, resynchronize
IGameInputReading_Release(device->last_keyboard_reading);
device->last_keyboard_reading = NULL;
if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindKeyboard, device->pDevice, &reading))) {
GAMEINPUT_HandleKeyboardDelta(data, window, device, device->last_keyboard_reading, reading);
IGameInputReading_Release(device->last_keyboard_reading);
device->last_keyboard_reading = reading;
}
}
}
if (!device->last_keyboard_reading) {
} else {
if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindKeyboard, device->pDevice, &reading))) {
GAMEINPUT_InitialKeyboardReading(data, window, device, reading);
device->last_keyboard_reading = reading;
@@ -587,9 +579,9 @@ void WIN_QuitGameInput(SDL_VideoDevice *_this)
data->pGameInput = NULL;
}
if (data->hGameInputDLL) {
SDL_UnloadObject(data->hGameInputDLL);
data->hGameInputDLL = NULL;
if (data->pGameInput) {
SDL_QuitGameInput();
data->pGameInput = NULL;
}
if (data->lock) {
+34 -14
View File
@@ -72,18 +72,22 @@ static SDL_Cursor *WIN_CreateCursorAndData(HCURSOR hcursor)
}
SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
if (cursor) {
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_free(cursor);
return NULL;
}
data->cursor = hcursor;
cursor->internal = data;
if (!cursor) {
return NULL;
}
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_free(cursor);
return NULL;
}
data->cursor = hcursor;
cursor->internal = data;
return cursor;
}
static bool IsMonochromeSurface(SDL_Surface *surface)
{
int x, y;
@@ -129,6 +133,9 @@ static HBITMAP CreateColorBitmap(SDL_Surface *surface)
bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &pixels, NULL, 0);
if (!bitmap || !pixels) {
WIN_SetError("CreateDIBSection()");
if (bitmap) {
DeleteObject(bitmap);
}
return NULL;
}
@@ -158,6 +165,7 @@ static HBITMAP CreateMaskBitmap(SDL_Surface *surface, bool is_monochrome)
pixels = SDL_small_alloc(Uint8, size * (is_monochrome ? 2 : 1), &isstack);
if (!pixels) {
SDL_OutOfMemory();
return NULL;
}
@@ -210,20 +218,30 @@ static HCURSOR WIN_CreateHCursor(SDL_Surface *surface, int hot_x, int hot_y)
if (!ii.hbmMask || (!is_monochrome && !ii.hbmColor)) {
SDL_SetError("Couldn't create cursor bitmaps");
if (ii.hbmMask) {
DeleteObject(ii.hbmMask);
}
if (ii.hbmColor) {
DeleteObject(ii.hbmColor);
}
return NULL;
}
hcursor = CreateIconIndirect(&ii);
if (!hcursor) {
WIN_SetError("CreateIconIndirect()");
DeleteObject(ii.hbmMask);
if (ii.hbmColor) {
DeleteObject(ii.hbmColor);
}
return NULL;
}
DeleteObject(ii.hbmMask);
if (ii.hbmColor) {
DeleteObject(ii.hbmColor);
}
if (!hcursor) {
WIN_SetError("CreateIconIndirect()");
return NULL;
}
return hcursor;
}
@@ -286,7 +304,7 @@ static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id)
name = IDC_CROSS;
break;
case SDL_SYSTEM_CURSOR_PROGRESS:
name = IDC_WAIT;
name = IDC_APPSTARTING;
break;
case SDL_SYSTEM_CURSOR_NWSE_RESIZE:
name = IDC_SIZENWSE;
@@ -353,7 +371,9 @@ static void WIN_FreeCursor(SDL_Cursor *cursor)
while (data->cache) {
CachedCursor *entry = data->cache;
data->cache = entry->next;
DestroyCursor(entry->cursor);
if (entry->cursor) {
DestroyCursor(entry->cursor);
}
SDL_free(entry);
}
if (data->cursor) {
@@ -112,7 +112,9 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
}
devices[0].dwFlags |= RIDEV_REMOVE;
devices[0].hwndTarget = NULL;
devices[1].dwFlags |= RIDEV_REMOVE;
devices[1].hwndTarget = NULL;
RegisterRawInputDevices(devices, count, sizeof(devices[0]));
DestroyWindow(window);
@@ -116,6 +116,7 @@ bool WIN_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
}
}
if (!SetWindowRgn(data->hwnd, mask, TRUE)) {
DeleteObject(mask);
return WIN_SetError("SetWindowRgn failed");
}
return true;
+24 -8
View File
@@ -76,10 +76,15 @@ static void SDLCALL UpdateWindowFrameUsableWhileCursorHidden(void *userdata, con
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
static bool WIN_SuspendScreenSaver(SDL_VideoDevice *_this)
{
DWORD result;
if (_this->suspend_screensaver) {
SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
result = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
} else {
SetThreadExecutionState(ES_CONTINUOUS);
result = SetThreadExecutionState(ES_CONTINUOUS);
}
if (result == 0) {
SDL_SetError("SetThreadExecutionState() failed");
return false;
}
return true;
}
@@ -103,6 +108,9 @@ static void WIN_DeleteDevice(SDL_VideoDevice *device)
if (data->shcoreDLL) {
SDL_UnloadObject(data->shcoreDLL);
}
if (data->dwmapiDLL) {
SDL_UnloadObject(data->dwmapiDLL);
}
#endif
#ifdef HAVE_DXGI_H
if (data->pDXGIFactory) {
@@ -112,9 +120,6 @@ static void WIN_DeleteDevice(SDL_VideoDevice *device)
SDL_UnloadObject(data->dxgiDLL);
}
#endif
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(device->internal->rawinput);
SDL_free(device->internal);
SDL_free(device);
@@ -140,7 +145,6 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
return NULL;
}
device->internal = data;
device->wakeup_lock = SDL_CreateMutex();
device->system_theme = WIN_GetSystemTheme();
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
@@ -179,6 +183,17 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
} else {
SDL_ClearError();
}
data->dwmapiDLL = SDL_LoadObject("DWMAPI.DLL");
if (data->dwmapiDLL) {
/* *INDENT-OFF* */ // clang-format off
data->DwmFlush = (HRESULT (WINAPI *)(void))SDL_LoadFunction(data->dwmapiDLL, "DwmFlush");
data->DwmEnableBlurBehindWindow = (HRESULT (WINAPI *)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind))SDL_LoadFunction(data->dwmapiDLL, "DwmEnableBlurBehindWindow");
data->DwmSetWindowAttribute = (HRESULT (WINAPI *)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute))SDL_LoadFunction(data->dwmapiDLL, "DwmSetWindowAttribute");
/* *INDENT-ON* */ // clang-format on
} else {
SDL_ClearError();
}
#endif // #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#ifdef HAVE_DXGI_H
@@ -330,10 +345,11 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
VideoBootStrap WINDOWS_bootstrap = {
"windows", "SDL Windows video driver", WIN_CreateDevice,
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
WIN_ShowMessageBox
WIN_ShowMessageBox,
#else
NULL
NULL,
#endif
false
};
static BOOL WIN_DeclareDPIAwareUnaware(SDL_VideoDevice *_this)
@@ -374,6 +374,45 @@ typedef struct tagINPUTCONTEXT2
} INPUTCONTEXT2, *PINPUTCONTEXT2, NEAR *NPINPUTCONTEXT2, FAR *LPINPUTCONTEXT2;
#endif
// Corner rounding support (Win 11+)
#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
#define DWMWA_WINDOW_CORNER_PREFERENCE 33
#endif
typedef enum {
DWMWCP_DEFAULT = 0,
DWMWCP_DONOTROUND = 1,
DWMWCP_ROUND = 2,
DWMWCP_ROUNDSMALL = 3
} DWM_WINDOW_CORNER_PREFERENCE;
// Border Color support (Win 11+)
#ifndef DWMWA_BORDER_COLOR
#define DWMWA_BORDER_COLOR 34
#endif
#ifndef DWMWA_COLOR_DEFAULT
#define DWMWA_COLOR_DEFAULT 0xFFFFFFFF
#endif
#ifndef DWMWA_COLOR_NONE
#define DWMWA_COLOR_NONE 0xFFFFFFFE
#endif
// Transparent window support
#ifndef DWM_BB_ENABLE
#define DWM_BB_ENABLE 0x00000001
#endif
#ifndef DWM_BB_BLURREGION
#define DWM_BB_BLURREGION 0x00000002
#endif
typedef struct
{
DWORD flags;
BOOL enable;
HRGN blur_region;
BOOL transition_on_maxed;
} DWM_BLURBEHIND;
// Private display data
struct SDL_VideoData
@@ -420,6 +459,11 @@ struct SDL_VideoData
BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
SDL_SharedObject *dwmapiDLL;
/* *INDENT-OFF* */ // clang-format off
HRESULT (WINAPI *DwmFlush)(void);
HRESULT (WINAPI *DwmEnableBlurBehindWindow)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
HRESULT (WINAPI *DwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
/* *INDENT-ON* */ // clang-format on
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+160 -239
View File
@@ -33,15 +33,12 @@
#include "../SDL_sysvideo.h"
#include "SDL_windowsvideo.h"
#include "SDL_windowskeyboard.h"
#include "SDL_windowswindow.h"
// Dropfile support
#include <shellapi.h>
// DWM setting support
typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
typedef HRESULT (WINAPI *DwmGetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute);
// Dark mode support
typedef enum {
UXTHEME_APPMODE_DEFAULT,
@@ -80,46 +77,6 @@ typedef UxthemePreferredAppMode (WINAPI *SetPreferredAppMode_t)(UxthemePreferred
typedef BOOL (WINAPI *SetWindowCompositionAttribute_t)(HWND, const WINDOWCOMPOSITIONATTRIBDATA *);
typedef void (NTAPI *RtlGetVersion_t)(NT_OSVERSIONINFOW *);
// Corner rounding support (Win 11+)
#ifndef DWMWA_WINDOW_CORNER_PREFERENCE
#define DWMWA_WINDOW_CORNER_PREFERENCE 33
#endif
typedef enum {
DWMWCP_DEFAULT = 0,
DWMWCP_DONOTROUND = 1,
DWMWCP_ROUND = 2,
DWMWCP_ROUNDSMALL = 3
} DWM_WINDOW_CORNER_PREFERENCE;
// Border Color support (Win 11+)
#ifndef DWMWA_BORDER_COLOR
#define DWMWA_BORDER_COLOR 34
#endif
#ifndef DWMWA_COLOR_DEFAULT
#define DWMWA_COLOR_DEFAULT 0xFFFFFFFF
#endif
#ifndef DWMWA_COLOR_NONE
#define DWMWA_COLOR_NONE 0xFFFFFFFE
#endif
// Transparent window support
#ifndef DWM_BB_ENABLE
#define DWM_BB_ENABLE 0x00000001
#endif
#ifndef DWM_BB_BLURREGION
#define DWM_BB_BLURREGION 0x00000002
#endif
typedef struct
{
DWORD flags;
BOOL enable;
HRGN blur_region;
BOOL transition_on_maxed;
} DWM_BLURBEHIND;
typedef HRESULT(WINAPI *DwmEnableBlurBehindWindow_t)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
// Windows CE compatibility
#ifndef SWP_NOCOPYBITS
#define SWP_NOCOPYBITS 0
@@ -135,12 +92,6 @@ typedef HRESULT(WINAPI *DwmEnableBlurBehindWindow_t)(HWND hwnd, const DWM_BLURBE
// #define HIGHDPI_DEBUG
// Fake window to help with DirectInput events.
HWND SDL_HelperWindow = NULL;
static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher");
static const TCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow");
static ATOM SDL_HelperWindowClass = 0;
/* For borderless Windows, still want the following flag:
- WS_MINIMIZEBOX: window will respond to Windows minimize commands sent to all windows, such as windows key + m, shaking title bar, etc.
Additionally, non-fullscreen windows can add:
@@ -392,6 +343,10 @@ bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRec
// Update any child windows
for (child_window = window->first_child; child_window; child_window = child_window->next_sibling) {
if (!child_window->internal) {
// This child window is not yet fully initialized.
continue;
}
if (!WIN_SetWindowPositionInternal(child_window, flags, SDL_WINDOWRECT_CURRENT)) {
result = false;
}
@@ -447,7 +402,6 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn
data->videodata = videodata;
data->initializing = true;
data->last_displayID = window->last_displayID;
data->dwma_border_color = DWMWA_COLOR_DEFAULT;
data->hint_erase_background_mode = GetEraseBackgroundModeHint();
@@ -535,7 +489,7 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn
}
if (!(window->flags & SDL_WINDOW_MINIMIZED)) {
RECT rect;
if (GetClientRect(hwnd, &rect) && !WIN_IsRectEmpty(&rect)) {
if (GetClientRect(hwnd, &rect) && WIN_WindowRectValid(&rect)) {
int w = rect.right;
int h = rect.bottom;
@@ -679,7 +633,7 @@ static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window)
static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending)
{
// Clamp popup windows to the output borders
// Possibly clamp popup windows to the output borders
if (SDL_WINDOW_IS_POPUP(window)) {
SDL_Window *w;
SDL_DisplayID displayID;
@@ -690,28 +644,30 @@ static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending)
const int height = window->last_size_pending ? window->pending.h : window->floating.h;
int offset_x = 0, offset_y = 0;
// Calculate the total offset from the parents
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
if (window->constrain_popup) {
// Calculate the total offset from the parents
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
offset_x += w->x;
offset_y += w->y;
}
offset_x += w->x;
offset_y += w->y;
}
abs_x += offset_x;
abs_y += offset_y;
offset_x += w->x;
offset_y += w->y;
abs_x += offset_x;
abs_y += offset_y;
// Constrain the popup window to the display of the toplevel parent
displayID = SDL_GetDisplayForWindow(w);
SDL_GetDisplayBounds(displayID, &rect);
if (abs_x + width > rect.x + rect.w) {
abs_x -= (abs_x + width) - (rect.x + rect.w);
// Constrain the popup window to the display of the toplevel parent
displayID = SDL_GetDisplayForWindow(w);
SDL_GetDisplayBounds(displayID, &rect);
if (abs_x + width > rect.x + rect.w) {
abs_x -= (abs_x + width) - (rect.x + rect.w);
}
if (abs_y + height > rect.y + rect.h) {
abs_y -= (abs_y + height) - (rect.y + rect.h);
}
abs_x = SDL_max(abs_x, rect.x);
abs_y = SDL_max(abs_y, rect.y);
}
if (abs_y + height > rect.y + rect.h) {
abs_y -= (abs_y + height) - (rect.y + rect.h);
}
abs_x = SDL_max(abs_x, rect.x);
abs_y = SDL_max(abs_y, rect.y);
if (output_to_pending) {
window->pending.x = abs_x - offset_x;
@@ -736,7 +692,7 @@ static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
toplevel = toplevel->parent;
}
toplevel->internal->keyboard_focus = window;
toplevel->keyboard_focus = window;
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
SDL_SetKeyboardFocus(window);
@@ -745,6 +701,7 @@ static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
{
SDL_VideoData *videodata = _this->internal;
HWND hwnd = (HWND)SDL_GetPointerProperty(create_props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL));
HWND parent = NULL;
if (hwnd) {
@@ -790,6 +747,9 @@ bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties
return false;
}
// Ensure that the IME isn't active on the new window until explicitly requested.
WIN_StopTextInput(_this, window);
// 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);
@@ -806,24 +766,19 @@ bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
// FIXME: does not work on all hardware configurations with different renders (i.e. hybrid GPUs)
if (window->flags & SDL_WINDOW_TRANSPARENT) {
SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll");
if (handle) {
DwmEnableBlurBehindWindow_t DwmEnableBlurBehindWindowFunc = (DwmEnableBlurBehindWindow_t)SDL_LoadFunction(handle, "DwmEnableBlurBehindWindow");
if (DwmEnableBlurBehindWindowFunc) {
/* The region indicates which part of the window will be blurred and rest will be transparent. This
is because the alpha value of the window will be used for non-blurred areas
We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred
*/
HRGN rgn = CreateRectRgn(-1, -1, 0, 0);
DWM_BLURBEHIND bb;
bb.flags = (DWM_BB_ENABLE | DWM_BB_BLURREGION);
bb.enable = TRUE;
bb.blur_region = rgn;
bb.transition_on_maxed = FALSE;
DwmEnableBlurBehindWindowFunc(hwnd, &bb);
DeleteObject(rgn);
}
SDL_UnloadObject(handle);
if (videodata->DwmEnableBlurBehindWindow) {
/* The region indicates which part of the window will be blurred and rest will be transparent. This
is because the alpha value of the window will be used for non-blurred areas
We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred
*/
HRGN rgn = CreateRectRgn(-1, -1, 0, 0);
DWM_BLURBEHIND bb;
bb.flags = (DWM_BB_ENABLE | DWM_BB_BLURREGION);
bb.enable = TRUE;
bb.blur_region = rgn;
bb.transition_on_maxed = FALSE;
videodata->DwmEnableBlurBehindWindow(hwnd, &bb);
DeleteObject(rgn);
}
}
@@ -1062,7 +1017,7 @@ void WIN_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *
HWND hwnd = data->hwnd;
RECT rect;
if (GetClientRect(hwnd, &rect) && !WIN_IsRectEmpty(&rect)) {
if (GetClientRect(hwnd, &rect) && WIN_WindowRectValid(&rect)) {
*w = rect.right;
*h = rect.bottom;
} else if (window->last_pixel_w && window->last_pixel_h) {
@@ -1077,8 +1032,9 @@ void WIN_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *
void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
DWORD style;
HWND hwnd;
bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true);
@@ -1087,20 +1043,33 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
WIN_SetWindowPosition(_this, window);
}
hwnd = window->internal->hwnd;
// If the window isn't borderless and will be fullscreen, use the borderless style to hide the initial borders.
if (window->pending_flags & SDL_WINDOW_FULLSCREEN) {
if (!(window->flags & SDL_WINDOW_BORDERLESS)) {
window->flags |= SDL_WINDOW_BORDERLESS;
style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~STYLE_MASK;
style |= GetWindowStyle(window);
SetWindowLong(hwnd, GWL_STYLE, style);
window->flags &= ~SDL_WINDOW_BORDERLESS;
}
}
style = GetWindowLong(hwnd, GWL_EXSTYLE);
if (style & WS_EX_NOACTIVATE) {
bActivate = false;
}
data->showing_window = true;
if (bActivate) {
ShowWindow(hwnd, SW_SHOW);
} else {
// Use SetWindowPos instead of ShowWindow to avoid activating the parent window if this is a child window
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, window->internal->copybits_flag | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
data->showing_window = false;
if (window->flags & SDL_WINDOW_POPUP_MENU && bActivate) {
WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) && bActivate) {
WIN_SetKeyboardFocus(window, true);
}
if (window->flags & SDL_WINDOW_MODAL) {
WIN_SetWindowModal(_this, window, true);
@@ -1117,21 +1086,10 @@ void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
ShowWindow(hwnd, SW_HIDE);
// Transfer keyboard focus back to the parent
if (window->flags & SDL_WINDOW_POPUP_MENU) {
SDL_Window *new_focus = window->parent;
bool set_focus = window == SDL_GetKeyboardFocus();
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
new_focus = new_focus->parent;
// If some window in the chain currently had keyboard focus, set it to the new lowest-level window.
if (!set_focus) {
set_focus = new_focus == SDL_GetKeyboardFocus();
}
}
// Transfer keyboard focus back to the parent from a grabbing popup.
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
WIN_SetKeyboardFocus(new_focus, set_focus);
}
}
@@ -1169,7 +1127,7 @@ void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
if (bActivate) {
SetForegroundWindow(hwnd);
if (window->flags & SDL_WINDOW_POPUP_MENU) {
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
}
} else {
@@ -1256,51 +1214,35 @@ void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
HWND hwnd = data->hwnd;
data->expected_resize = true;
ShowWindow(hwnd, SW_RESTORE);
data->expected_resize = false;
if (!data->showing_window || window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) {
HWND hwnd = data->hwnd;
data->expected_resize = true;
ShowWindow(hwnd, SW_RESTORE);
data->expected_resize = false;
}
} else {
data->windowed_mode_was_maximized = false;
}
}
static DWM_WINDOW_CORNER_PREFERENCE WIN_UpdateCornerRoundingForHWND(HWND hwnd, DWM_WINDOW_CORNER_PREFERENCE cornerPref)
static void WIN_UpdateCornerRoundingForHWND(SDL_VideoDevice *_this, HWND hwnd, DWM_WINDOW_CORNER_PREFERENCE cornerPref)
{
DWM_WINDOW_CORNER_PREFERENCE oldPref = DWMWCP_DEFAULT;
SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll");
if (handle) {
DwmGetWindowAttribute_t DwmGetWindowAttributeFunc = (DwmGetWindowAttribute_t)SDL_LoadFunction(handle, "DwmGetWindowAttribute");
DwmSetWindowAttribute_t DwmSetWindowAttributeFunc = (DwmSetWindowAttribute_t)SDL_LoadFunction(handle, "DwmSetWindowAttribute");
if (DwmGetWindowAttributeFunc && DwmSetWindowAttributeFunc) {
DwmGetWindowAttributeFunc(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &oldPref, sizeof(oldPref));
DwmSetWindowAttributeFunc(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
}
SDL_UnloadObject(handle);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *videodata = _this->internal;
if (videodata->DwmSetWindowAttribute) {
videodata->DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
}
return oldPref;
#endif
}
static COLORREF WIN_UpdateBorderColorForHWND(HWND hwnd, COLORREF colorRef)
static void WIN_UpdateBorderColorForHWND(SDL_VideoDevice *_this, HWND hwnd, COLORREF colorRef)
{
COLORREF oldPref = DWMWA_COLOR_DEFAULT;
SDL_SharedObject *handle = SDL_LoadObject("dwmapi.dll");
if (handle) {
DwmGetWindowAttribute_t DwmGetWindowAttributeFunc = (DwmGetWindowAttribute_t)SDL_LoadFunction(handle, "DwmGetWindowAttribute");
DwmSetWindowAttribute_t DwmSetWindowAttributeFunc = (DwmSetWindowAttribute_t)SDL_LoadFunction(handle, "DwmSetWindowAttribute");
if (DwmGetWindowAttributeFunc && DwmSetWindowAttributeFunc) {
DwmGetWindowAttributeFunc(hwnd, DWMWA_BORDER_COLOR, &oldPref, sizeof(oldPref));
DwmSetWindowAttributeFunc(hwnd, DWMWA_BORDER_COLOR, &colorRef, sizeof(colorRef));
}
SDL_UnloadObject(handle);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_VideoData *videodata = _this->internal;
if (videodata->DwmSetWindowAttribute) {
videodata->DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &colorRef, sizeof(colorRef));
}
return oldPref;
#endif
}
/**
@@ -1311,7 +1253,7 @@ SDL_FullscreenResult WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_DisplayData *displaydata = display->internal;
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
HWND hwnd = data ? data->hwnd : NULL;
MONITORINFO minfo;
DWORD style, styleEx;
HWND top;
@@ -1366,13 +1308,13 @@ SDL_FullscreenResult WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window
}
// Disable corner rounding & border color (Windows 11+) so the window fills the full screen
data->windowed_mode_corner_rounding = WIN_UpdateCornerRoundingForHWND(hwnd, DWMWCP_DONOTROUND);
data->dwma_border_color = WIN_UpdateBorderColorForHWND(hwnd, DWMWA_COLOR_NONE);
WIN_UpdateCornerRoundingForHWND(_this, hwnd, DWMWCP_DONOTROUND);
WIN_UpdateBorderColorForHWND(_this, hwnd, DWMWA_COLOR_NONE);
} else {
BOOL menu;
WIN_UpdateCornerRoundingForHWND(hwnd, (DWM_WINDOW_CORNER_PREFERENCE)data->windowed_mode_corner_rounding);
WIN_UpdateBorderColorForHWND(hwnd, data->dwma_border_color);
WIN_UpdateCornerRoundingForHWND(_this, hwnd, DWMWCP_DEFAULT);
WIN_UpdateBorderColorForHWND(_this, hwnd, DWMWA_COLOR_DEFAULT);
/* Restore window-maximization state, as applicable.
Special care is taken to *not* do this if and when we're
@@ -1391,6 +1333,35 @@ SDL_FullscreenResult WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window
&w, &h,
SDL_WINDOWRECT_FLOATING);
data->windowed_mode_was_maximized = false;
/* A window may have been maximized by dragging it to the top of another display, in which case the floating
* position may be out-of-date. If the window is being restored to maximized, and the maximized and floating
* position are on different displays, try to center the window on the maximized display for restoration, which
* mimics native Windows behavior.
*/
if (enterMaximized) {
const SDL_Point windowed_point = { window->windowed.x, window->windowed.y };
const SDL_Point floating_point = { window->floating.x, window->floating.y };
const SDL_DisplayID floating_display = SDL_GetDisplayForPoint(&floating_point);
const SDL_DisplayID windowed_display = SDL_GetDisplayForPoint(&windowed_point);
if (floating_display != windowed_display) {
SDL_Rect bounds;
SDL_zero(bounds);
SDL_GetDisplayUsableBounds(windowed_display, &bounds);
if (w < bounds.w) {
x = bounds.x + (bounds.w - w) / 2;
} else {
x = bounds.x;
}
if (h < bounds.h) {
y = bounds.y + (bounds.h - h) / 2;
} else {
y = bounds.y;
}
}
}
}
/* Always reset the window to the base floating size before possibly re-applying the maximized state,
@@ -1533,72 +1504,6 @@ void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
CleanupWindowData(_this, window);
}
/*
* Creates a HelperWindow used for DirectInput.
*/
bool SDL_HelperWindowCreate(void)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASS wce;
// Make sure window isn't created twice.
if (SDL_HelperWindow != NULL) {
return true;
}
// Create the class.
SDL_zero(wce);
wce.lpfnWndProc = DefWindowProc;
wce.lpszClassName = SDL_HelperWindowClassName;
wce.hInstance = hInstance;
// Register the class.
SDL_HelperWindowClass = RegisterClass(&wce);
if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) {
return WIN_SetError("Unable to create Helper Window Class");
}
// Create the window.
SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName,
SDL_HelperWindowName,
WS_OVERLAPPED, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, HWND_MESSAGE, NULL,
hInstance, NULL);
if (!SDL_HelperWindow) {
UnregisterClass(SDL_HelperWindowClassName, hInstance);
return WIN_SetError("Unable to create Helper Window");
}
return true;
}
/*
* Destroys the HelperWindow previously created with SDL_HelperWindowCreate.
*/
void SDL_HelperWindowDestroy(void)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
// Destroy the window.
if (SDL_HelperWindow != NULL) {
if (DestroyWindow(SDL_HelperWindow) == 0) {
WIN_SetError("Unable to destroy Helper Window");
return;
}
SDL_HelperWindow = NULL;
}
// Unregister the class.
if (SDL_HelperWindowClass != 0) {
if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) {
WIN_SetError("Unable to destroy Helper Window Class");
return;
}
SDL_HelperWindowClass = 0;
}
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window)
{
@@ -1633,7 +1538,7 @@ void WIN_UnclipCursorForWindow(SDL_Window *window) {
void WIN_UpdateClipCursor(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
if (data->in_title_click || data->focus_click_pending || data->skip_update_clipcursor) {
if (data->in_title_click || data->focus_click_pending || data->postpone_clipcursor) {
return;
}
@@ -2272,24 +2177,40 @@ void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
{
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
if (!SDL_WINDOW_IS_POPUP(window)) {
SDL_WindowData *data = window->internal;
HWND hwnd = data->hwnd;
const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
SDL_assert(style != 0);
SDL_assert(style != 0);
if (focusable) {
if (style & WS_EX_NOACTIVATE) {
if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) {
return WIN_SetError("SetWindowLong()");
if (focusable) {
if (style & WS_EX_NOACTIVATE) {
if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) {
return WIN_SetError("SetWindowLong()");
}
}
} else {
if (!(style & WS_EX_NOACTIVATE)) {
if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) {
return WIN_SetError("SetWindowLong()");
}
}
}
} else {
if (!(style & WS_EX_NOACTIVATE)) {
if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) {
return WIN_SetError("SetWindowLong()");
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
WIN_SetKeyboardFocus(new_focus, set_focus);
} else if (focusable) {
if (SDL_ShouldFocusPopup(window)) {
WIN_SetKeyboardFocus(window, true);
}
}
}
return true;
}
return true;
@@ -2299,38 +2220,38 @@ bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool foc
void WIN_UpdateDarkModeForHWND(HWND hwnd)
{
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_SharedObject *ntdll = SDL_LoadObject("ntdll.dll");
HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll"));
if (!ntdll) {
return;
}
// There is no function to get Windows build number, so let's get it here via RtlGetVersion
RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)SDL_LoadFunction(ntdll, "RtlGetVersion");
RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)GetProcAddress(ntdll, "RtlGetVersion");
NT_OSVERSIONINFOW os_info;
os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW);
os_info.dwBuildNumber = 0;
if (RtlGetVersionFunc) {
RtlGetVersionFunc(&os_info);
}
SDL_UnloadObject(ntdll);
FreeLibrary(ntdll);
os_info.dwBuildNumber &= ~0xF0000000;
if (os_info.dwBuildNumber < 17763) {
// Too old to support dark mode
return;
}
SDL_SharedObject *uxtheme = SDL_LoadObject("uxtheme.dll");
HMODULE uxtheme = LoadLibrary(TEXT("uxtheme.dll"));
if (!uxtheme) {
return;
}
RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(104));
ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(132));
AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(133));
RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(104));
ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(132));
AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(133));
if (os_info.dwBuildNumber < 18362) {
AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(135));
AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135));
if (AllowDarkModeForAppFunc) {
AllowDarkModeForAppFunc(true);
}
} else {
SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)SDL_LoadFunction(uxtheme, MAKEINTRESOURCEA(135));
SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135));
if (SetPreferredAppModeFunc) {
SetPreferredAppModeFunc(UXTHEME_APPMODE_ALLOW_DARK);
}
@@ -2348,7 +2269,7 @@ void WIN_UpdateDarkModeForHWND(HWND hwnd)
} else {
value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE;
}
SDL_UnloadObject(uxtheme);
FreeLibrary(uxtheme);
if (os_info.dwBuildNumber < 18362) {
SetProp(hwnd, TEXT("UseImmersiveDarkModeColors"), SDL_reinterpret_cast(HANDLE, SDL_static_cast(INT_PTR, value)));
} else {
@@ -78,22 +78,21 @@ struct SDL_WindowData
bool in_border_change;
bool in_title_click;
Uint8 focus_click_pending;
bool skip_update_clipcursor;
bool postpone_clipcursor;
bool clipcursor_queued;
bool windowed_mode_was_maximized;
bool in_window_deactivation;
bool force_ws_maximizebox;
bool disable_move_size_events;
bool showing_window;
int in_modal_loop;
RECT initial_size_rect;
RECT cursor_clipped_rect; // last successfully committed clipping rect for this window
RECT cursor_ctrlock_rect; // this is Windows-specific, but probably does not need to be per-window
UINT windowed_mode_corner_rounding;
COLORREF dwma_border_color;
bool mouse_tracked;
bool destroy_parent_with_window;
SDL_DisplayID last_displayID;
WCHAR *ICMFileName;
SDL_Window *keyboard_focus;
SDL_WindowEraseBackgroundMode hint_erase_background_mode;
struct SDL_VideoData *videodata;
#ifdef SDL_VIDEO_OPENGL_EGL
+3
View File
@@ -71,6 +71,9 @@
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
#include <X11/extensions/shape.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XTEST
#include <X11/extensions/XTest.h>
#endif
#ifdef __cplusplus
extern "C" {
+140 -68
View File
@@ -194,7 +194,7 @@ static bool X11_KeyRepeat(Display *display, XEvent *event)
return d.found;
}
static bool X11_IsWheelEvent(Display *display, int button, int *xticks, int *yticks)
bool X11_IsWheelEvent(int button, int *xticks, int *yticks)
{
/* according to the xlib docs, no specific mouse wheel events exist.
However, the defacto standard is that the vertical wheel is X buttons
@@ -233,7 +233,7 @@ void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)
#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
{
SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
SDL_VideoData *videodata = _this->internal;
// event is a union, so cookie == &event, but this is type safe.
XGenericEventCookie *cookie = &xev->xcookie;
@@ -310,6 +310,12 @@ static void X11_ReconcileModifiers(SDL_VideoData *viddata)
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE;
}
if (xk_modifiers & LockMask) {
viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS;
} else {
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS;
}
if (xk_modifiers & viddata->xkb.numlock_mask) {
viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM;
} else {
@@ -325,10 +331,11 @@ static void X11_ReconcileModifiers(SDL_VideoData *viddata)
SDL_SetModState(viddata->xkb.sdl_modifiers);
}
static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool reconcile)
static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation)
{
const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
SDL_Keymod mod = SDL_KMOD_NONE;
bool reconcile = false;
/* SDL clients expect modifier state to be activated at the same time as the
* source keypress, so we set pressed modifier state with the usual modifier
@@ -367,7 +374,36 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode
case SDLK_LEVEL5_SHIFT:
mod = SDL_KMOD_LEVEL5;
break;
case SDLK_CAPSLOCK:
case SDLK_NUMLOCKCLEAR:
case SDLK_SCROLLLOCK:
{
/* For locking modifier keys, query the lock state directly, or we may have to wait until the next
* key press event to know if a lock was actually activated from the key event.
*/
unsigned int cur_mask = viddata->xkb.xkb_modifiers;
X11_UpdateSystemKeyModifiers(viddata);
if (viddata->xkb.xkb_modifiers & LockMask) {
cur_mask |= LockMask;
} else {
cur_mask &= ~LockMask;
}
if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) {
cur_mask |= viddata->xkb.numlock_mask;
} else {
cur_mask &= ~viddata->xkb.numlock_mask;
}
if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) {
cur_mask |= viddata->xkb.scrolllock_mask;
} else {
cur_mask &= ~viddata->xkb.scrolllock_mask;
}
viddata->xkb.xkb_modifiers = cur_mask;
} SDL_FALLTHROUGH;
default:
reconcile = true;
break;
}
@@ -377,8 +413,12 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode
viddata->xkb.sdl_modifiers &= ~mod;
}
if (reconcile) {
X11_ReconcileModifiers(viddata);
if (allow_reconciliation) {
if (reconcile) {
X11_ReconcileModifiers(viddata);
} else {
SDL_SetModState(viddata->xkb.sdl_modifiers);
}
}
}
@@ -466,17 +506,32 @@ static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data)
static void X11_DispatchMapNotify(SDL_WindowData *data)
{
SDL_Window *window = data->window;
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
data->was_shown = true;
// This may be sent when restoring a minimized window.
if (window->flags & SDL_WINDOW_MINIMIZED) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
}
if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
SDL_UpdateWindowGrab(window);
}
}
static void X11_DispatchUnmapNotify(SDL_WindowData *data)
{
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
SDL_Window *window = data->window;
// This may be sent when minimizing a window.
if (!window->is_hiding) {
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
} else {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
}
}
static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point)
@@ -857,7 +912,7 @@ static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int
SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window)
{
const SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
const SDL_VideoData *videodata = _this->internal;
int i;
if (videodata && videodata->windowlist) {
@@ -879,7 +934,7 @@ Uint64 X11_GetEventTimestamp(unsigned long time)
void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent)
{
SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
SDL_VideoData *videodata = _this->internal;
Display *display = videodata->display;
KeyCode keycode = xevent->xkey.keycode;
KeySym keysym = NoSymbol;
@@ -906,7 +961,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
#endif // DEBUG SCANCODES
text[0] = '\0';
X11_UpdateSystemKeyModifiers(videodata);
videodata->xkb.xkb_modifiers = xevent->xkey.state;
if (SDL_TextInputActive(windowdata->window)) {
// filter events catches XIM events and sends them to the correct handler
@@ -937,7 +992,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
X11_HandleModifierKeys(videodata, scancode, true, true);
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
if (*text) {
if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
text[text_length] = '\0';
X11_ClearComposition(windowdata);
SDL_SendKeyboardText(text);
@@ -961,8 +1016,6 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time)
{
SDL_Window *window = windowdata->window;
const SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
Display *display = videodata->display;
int xticks = 0, yticks = 0;
Uint64 timestamp = X11_GetEventTimestamp(time);
@@ -976,7 +1029,7 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S
SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y);
}
if (X11_IsWheelEvent(display, button, &xticks, &yticks)) {
if (X11_IsWheelEvent(button, &xticks, &yticks)) {
SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
} else {
bool ignore_click = false;
@@ -1008,8 +1061,6 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S
void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time)
{
SDL_Window *window = windowdata->window;
const SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
Display *display = videodata->display;
// The X server sends a Release event for each Press for wheels. Ignore them.
int xticks = 0, yticks = 0;
Uint64 timestamp = X11_GetEventTimestamp(time);
@@ -1017,7 +1068,7 @@ void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata,
#ifdef DEBUG_XEVENTS
SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button);
#endif
if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) {
if (!X11_IsWheelEvent(button, &xticks, &yticks)) {
if (button > 7) {
// see explanation at case ButtonPress
button -= (8 - SDL_BUTTON_X1);
@@ -1056,6 +1107,41 @@ void X11_GetBorderValues(SDL_WindowData *data)
}
}
void X11_EmitConfigureNotifyEvents(SDL_WindowData *data, XConfigureEvent *xevent)
{
if (xevent->x != data->last_xconfigure.x ||
xevent->y != data->last_xconfigure.y) {
if (!data->size_move_event_flags) {
SDL_Window *w;
int x = xevent->x;
int y = xevent->y;
data->pending_operation &= ~X11_PENDING_OP_MOVE;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
for (w = data->window->first_child; w; w = w->next_sibling) {
// Don't update hidden child popup windows, their relative position doesn't change
if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w, true);
}
}
}
}
if (xevent->width != data->last_xconfigure.width ||
xevent->height != data->last_xconfigure.height) {
if (!data->size_move_event_flags) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->width,
xevent->height);
}
}
SDL_copyp(&data->last_xconfigure, xevent);
}
static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
{
SDL_VideoData *videodata = _this->internal;
@@ -1251,8 +1337,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y);
}
// We ungrab in LeaveNotify, so we may need to grab again here
SDL_UpdateWindowGrab(data->window);
// We ungrab in LeaveNotify, so we may need to grab again here, but not if captured, as the capture can be lost.
if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
SDL_UpdateWindowGrab(data->window);
}
X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true);
} break;
@@ -1280,7 +1368,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
xevent->xcrossing.detail != NotifyInferior) {
/* In order for interaction with the window decorations and menu to work properly
on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
on Mutter, we need to ungrab the keyboard when the mouse leaves. */
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
X11_SetWindowKeyboardGrab(_this, data->window, false);
}
@@ -1407,9 +1495,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
xevent->xconfigure.x, xevent->xconfigure.y,
xevent->xconfigure.width, xevent->xconfigure.height);
#endif
// Real configure notify events are relative to the parent, synthetic events are absolute.
if (!xevent->xconfigure.send_event)
{
// Real configure notify events are relative to the parent, synthetic events are absolute.
if (!xevent->xconfigure.send_event) {
unsigned int NumChildren;
Window ChildReturn, Root, Parent;
Window *Children;
@@ -1422,41 +1509,24 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
&ChildReturn);
}
if (xevent->xconfigure.x != data->last_xconfigure.x ||
xevent->xconfigure.y != data->last_xconfigure.y) {
if (!data->size_move_event_flags) {
SDL_Window *w;
int x = xevent->xconfigure.x;
int y = xevent->xconfigure.y;
/* Some window managers send ConfigureNotify before PropertyNotify when changing state (Xfce and
* fvwm are known to do this), which is backwards from other window managers, as well as what is
* expected by SDL and its clients. Defer emitting the size/move events until the corresponding
* PropertyNotify arrives for consistency.
*/
const Uint32 changed = X11_GetNetWMState(_this, data->window, xevent->xproperty.window) ^ data->window->flags;
if (changed & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) {
SDL_copyp(&data->pending_xconfigure, &xevent->xconfigure);
data->emit_size_move_after_property_notify = true;
}
data->pending_operation &= ~X11_PENDING_OP_MOVE;
SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
for (w = data->window->first_child; w; w = w->next_sibling) {
// Don't update hidden child popup windows, their relative position doesn't change
if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) {
X11_UpdateWindowPosition(w, true);
}
}
}
if (!data->emit_size_move_after_property_notify) {
X11_EmitConfigureNotifyEvents(data, &xevent->xconfigure);
}
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
X11_HandleConfigure(data->window, &xevent->xconfigure);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
if (xevent->xconfigure.width != data->last_xconfigure.width ||
xevent->xconfigure.height != data->last_xconfigure.height) {
if (!data->size_move_event_flags) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED,
xevent->xconfigure.width,
xevent->xconfigure.height);
}
}
data->last_xconfigure = xevent->xconfigure;
} break;
// Have we been requested to quit (or another client message?)
@@ -1742,19 +1812,15 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) {
/* Get the new state from the window manager.
Compositing window managers can alter visibility of windows
without ever mapping / unmapping them, so we handle that here,
because they use the NETWM protocol to notify us of changes.
* Compositing window managers can alter visibility of windows
* without ever mapping / unmapping them, so we handle that here,
* because they use the NETWM protocol to notify us of changes.
*/
const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window);
const SDL_WindowFlags changed = flags ^ data->window->flags;
if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) {
if (flags & SDL_WINDOW_HIDDEN) {
X11_DispatchUnmapNotify(data);
} else {
X11_DispatchMapNotify(data);
}
if ((changed & SDL_WINDOW_HIDDEN) && !(flags & SDL_WINDOW_HIDDEN)) {
X11_DispatchMapNotify(data);
}
if (!SDL_WINDOW_IS_POPUP(data->window)) {
@@ -1765,6 +1831,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
if (!(flags & SDL_WINDOW_MINIMIZED)) {
const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0;
// Ensure the maximized flag is cleared before entering fullscreen.
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
if (commit) {
/* This was initiated by the compositor, or the mode was changed between the request and the window
@@ -1856,6 +1924,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
}
}
}
if (data->emit_size_move_after_property_notify) {
X11_EmitConfigureNotifyEvents(data, &data->pending_xconfigure);
data->emit_size_move_after_property_notify = false;
}
if ((flags & SDL_WINDOW_INPUT_FOCUS)) {
if (data->pending_move) {
DispatchWindowMove(_this, data, &data->pending_move_point);
@@ -1875,13 +1947,12 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
right approach, but it seems to work. */
X11_UpdateKeymap(_this, true);
} else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) {
/* Events are disabled when leaving fullscreen until the borders appear to avoid
* incorrect size/position events.
*/
X11_GetBorderValues(data);
if (data->size_move_event_flags) {
/* Events are disabled when leaving fullscreen until the borders appear to avoid
* incorrect size/position events on compositing window managers.
*/
data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS;
X11_GetBorderValues(data);
}
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) {
data->toggle_borders = false;
@@ -2073,6 +2144,7 @@ void X11_PumpEvents(SDL_VideoDevice *_this)
SDL_LogError(SDL_LOG_CATEGORY_VIDEO,
"Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id);
SDL_SetDisplayModeForDisplay(_this->displays[i], NULL);
_this->displays[i]->internal->mode_switch_deadline_ns = 0;
}
}
}
+1
View File
@@ -36,5 +36,6 @@ extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *wind
extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window);
extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result);
extern bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y);
extern bool X11_IsWheelEvent(int button, int *xticks, int *yticks);
#endif // SDL_x11events_h_
+5 -2
View File
@@ -660,7 +660,8 @@ void X11_CreateInputContext(SDL_WindowData *data)
NULL);
X11_XFree(attr);
}
} else {
}
if (!data->ic) {
data->ic = X11_XCreateIC(videodata->im,
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, data->xwindow,
@@ -761,7 +762,9 @@ void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
break;
}
(void)SDL_snprintf(deeplink, sizeof(deeplink),
"steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d",
"steam://open/keyboard?XPosition=%i&YPosition=%i&Width=%i&Height=%i&Mode=%d",
window->text_input_rect.x, window->text_input_rect.y,
window->text_input_rect.w, window->text_input_rect.h,
mode);
SDL_OpenURL(deeplink);
videodata->steam_keyboard_open = true;
+43 -13
View File
@@ -30,7 +30,10 @@
#include <X11/keysym.h>
#include <locale.h>
#ifndef SDL_FORK_MESSAGEBOX
#define SDL_FORK_MESSAGEBOX 1
#endif
#define SDL_SET_LOCALE 1
#if SDL_FORK_MESSAGEBOX
@@ -48,13 +51,17 @@
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)
"-*-*-*-*-*--*-*-*-*-*-*-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.
"-*-*-*-*-*--*-*-*-*-*-*-iso8859-1"; // just give me anything latin1.
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 SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = {
{ 56, 54, 53 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,
@@ -200,13 +207,19 @@ static bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBox
if (SDL_X11_HAVE_UTF8) {
char **missing = NULL;
int num_missing = 0;
data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
&missing, &num_missing, NULL);
if (missing) {
X11_XFreeStringList(missing);
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 font %s", g_MessageBoxFont);
return SDL_SetError("Couldn't load x11 message box font");
}
} else
#endif
@@ -415,6 +428,13 @@ static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
Display *display = data->display;
SDL_WindowData *windowdata = NULL;
const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
#ifdef XRANDR_DISABLED_BY_DEFAULT
const bool use_xrandr_by_default = false;
#else
const bool use_xrandr_by_default = true;
#endif
#endif
if (messageboxdata->window) {
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window);
@@ -487,7 +507,17 @@ static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
const SDL_DisplayData *dpydata = dpy->internal;
x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2);
y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3);
} else { // oh well. This will misposition on a multi-head setup. Init first next time.
}
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
else if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default)) {
XRRScreenResources *screen = X11_XRRGetScreenResourcesCurrent(display, DefaultRootWindow(display));
XRRCrtcInfo *crtc_info = X11_XRRGetCrtcInfo(display, screen, screen->crtcs[0]);
x = (crtc_info->width - data->dialog_width) / 2;
y = (crtc_info->height - data->dialog_height) / 3;
}
#endif
else {
// oh well. This will misposition on a multi-head setup. Init first next time.
x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2;
y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3;
}
+63 -9
View File
@@ -555,22 +555,73 @@ static XRRScreenResources *X11_GetScreenResources(Display *dpy, int screen)
static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy)
{
const int screen = DefaultScreen(dpy);
XRRScreenResources *res = X11_GetScreenResources(dpy, screen);
if (!res) {
const int screencount = ScreenCount(dpy);
SDL_DisplayID *displays = SDL_GetDisplays(NULL);
if (!displays) {
return;
}
SDL_DisplayID *displays = SDL_GetDisplays(NULL);
if (displays) {
for (int screen = 0; screen < screencount; ++screen) {
XRRScreenResources *res = X11_GetScreenResources(dpy, screen);
if (!res) {
continue;
}
for (int i = 0; displays[i]; ++i) {
SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
const SDL_DisplayData *displaydata = display->internal;
X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display);
if (displaydata->screen == screen) {
X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display);
}
}
SDL_free(displays);
X11_XRRFreeScreenResources(res);
}
X11_XRRFreeScreenResources(res);
SDL_free(displays);
}
static void X11_CheckDisplaysRemoved(SDL_VideoDevice *_this, Display *dpy)
{
const int screencount = ScreenCount(dpy);
int num_displays = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
if (!displays) {
return;
}
for (int screen = 0; screen < screencount; ++screen) {
XRRScreenResources *res = X11_GetScreenResources(dpy, screen);
if (!res) {
continue;
}
for (int output = 0; output < res->noutput; output++) {
for (int i = 0; i < num_displays; ++i) {
if (!displays[i]) {
// We already removed this display from the list
continue;
}
SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
const SDL_DisplayData *displaydata = display->internal;
if (displaydata->xrandr_output == res->outputs[output]) {
// This display is active, remove it from the list
displays[i] = 0;
break;
}
}
}
X11_XRRFreeScreenResources(res);
}
for (int i = 0; i < num_displays; ++i) {
if (displays[i]) {
// This display wasn't in the XRandR list
SDL_DelVideoDisplay(displays[i], true);
}
}
SDL_free(displays);
}
static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutputChangeNotifyEvent *ev)
@@ -580,9 +631,12 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput
int i;
#if 0
printf("XRROutputChangeNotifyEvent! [output=%u, crtc=%u, mode=%u, rotation=%u, connection=%u]", (unsigned int) ev->output, (unsigned int) ev->crtc, (unsigned int) ev->mode, (unsigned int) ev->rotation, (unsigned int) ev->connection);
printf("XRROutputChangeNotifyEvent! [output=%u, crtc=%u, mode=%u, rotation=%u, connection=%u]\n", (unsigned int) ev->output, (unsigned int) ev->crtc, (unsigned int) ev->mode, (unsigned int) ev->rotation, (unsigned int) ev->connection);
#endif
// XWayland doesn't always send output disconnected events
X11_CheckDisplaysRemoved(_this, ev->display);
displays = SDL_GetDisplays(NULL);
if (displays) {
for (i = 0; displays[i]; ++i) {
+20 -14
View File
@@ -31,7 +31,7 @@
// Does this device have a valuator for pressure sensitivity?
static bool X11_XInput2DeviceIsPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
{
const SDL_VideoData *data = (SDL_VideoData *)_this->internal;
const SDL_VideoData *data = _this->internal;
for (int i = 0; i < dev->num_classes; i++) {
const XIAnyClassInfo *classinfo = dev->classes[i];
if (classinfo->type == XIValuatorClass) {
@@ -49,7 +49,7 @@ static bool X11_XInput2DeviceIsPen(SDL_VideoDevice *_this, const XIDeviceInfo *d
static bool X11_XInput2PenIsEraser(SDL_VideoDevice *_this, int deviceid, char *devicename)
{
#define PEN_ERASER_NAME_TAG "eraser" // String constant to identify erasers
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
SDL_VideoData *data = _this->internal;
if (data->atoms.pen_atom_wacom_tool_type != None) {
Atom type_return;
@@ -105,7 +105,7 @@ static bool X11_XInput2PenIsEraser(SDL_VideoDevice *_this, int deviceid, char *d
// Returns number of Sint32s written (<= max_words), or 0 on error.
static size_t X11_XInput2PenGetIntProperty(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words)
{
const SDL_VideoData *data = (SDL_VideoData *)_this->internal;
const SDL_VideoData *data = _this->internal;
Atom type_return;
int format_return;
unsigned long num_items_return;
@@ -153,7 +153,7 @@ static size_t X11_XInput2PenGetIntProperty(SDL_VideoDevice *_this, int deviceid,
// Identify Wacom devices (if true is returned) and extract their device type and serial IDs
static bool X11_XInput2PenWacomDeviceID(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial)
{
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
SDL_VideoData *data = _this->internal;
Sint32 serial_id_buf[3];
int result;
@@ -196,7 +196,7 @@ X11_PenHandle *X11_FindPenByDeviceID(int deviceid)
static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
{
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
SDL_VideoData *data = _this->internal;
SDL_PenCapabilityFlags capabilities = 0;
X11_PenHandle *handle = NULL;
@@ -283,14 +283,16 @@ static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo
X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid)
{
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
int num_device_info = 0;
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
if (device_info) {
SDL_assert(num_device_info == 1);
X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info);
X11_XIFreeDeviceInfo(device_info);
return handle;
if (X11_Xinput2IsInitialized()) {
SDL_VideoData *data = _this->internal;
int num_device_info = 0;
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
if (device_info) {
SDL_assert(num_device_info == 1);
X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info);
X11_XIFreeDeviceInfo(device_info);
return handle;
}
}
return NULL;
}
@@ -306,7 +308,11 @@ void X11_RemovePenByDeviceID(int deviceid)
void X11_InitPen(SDL_VideoDevice *_this)
{
SDL_VideoData *data = (SDL_VideoData *)_this->internal;
if (!X11_Xinput2IsInitialized()) {
return; // we need XIQueryDevice() for this.
}
SDL_VideoData *data = _this->internal;
#define LOOKUP_PEN_ATOM(X) X11_XInternAtom(data->display, X, False)
data->atoms.pen_atom_device_product_id = LOOKUP_PEN_ATOM("Device Product ID");
+1 -1
View File
@@ -31,7 +31,7 @@ static Uint8 *GenerateShapeMask(SDL_Surface *shape)
{
int x, y;
const size_t ppb = 8;
const size_t bytes_per_scanline = (size_t)(shape->w + (ppb - 1)) / ppb;
const size_t bytes_per_scanline = (shape->w + (ppb - 1)) / ppb;
const Uint8 *a;
Uint8 *mask;
Uint8 *mask_scanline;
+5 -9
View File
@@ -63,9 +63,6 @@ static void X11_DeleteDevice(SDL_VideoDevice *device)
X11_XCloseDisplay(data->request_display);
}
SDL_free(data->windowlist);
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(device->internal);
SDL_free(device);
@@ -129,8 +126,6 @@ static SDL_VideoDevice *X11_CreateDevice(void)
return NULL;
}
device->wakeup_lock = SDL_CreateMutex();
#ifdef X11_DEBUG
X11_XSynchronize(data->display, True);
#endif
@@ -256,13 +251,13 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->system_theme = SDL_SystemTheme_Get();
#endif
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT;
data->is_xwayland = X11_IsXWayland(x11_display);
if (data->is_xwayland) {
device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED |
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS;
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS |
VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
}
return device;
@@ -271,7 +266,8 @@ static SDL_VideoDevice *X11_CreateDevice(void)
VideoBootStrap X11_bootstrap = {
"x11", "SDL X11 video driver",
X11_CreateDevice,
X11_ShowMessageBox
X11_ShowMessageBox,
false
};
static int (*handler)(Display *, XErrorEvent *) = NULL;
+267 -96
View File
@@ -86,6 +86,23 @@ static Bool X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*p
}
*/
static bool X11_CheckCurrentDesktop(const char *name)
{
SDL_Environment *env = SDL_GetEnvironment();
const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION");
if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) {
return true;
}
desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP");
if (desktopVar && SDL_strcasestr(desktopVar, name)) {
return true;
}
return false;
}
static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
@@ -100,6 +117,14 @@ static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window)
}
}
static bool X11_IsDisplayOk(Display *display)
{
if (display->flags & XlibDisplayIOError) {
return false;
}
return true;
}
#if 0
static bool X11_IsActionAllowed(SDL_Window *window, Atom action)
{
@@ -207,28 +232,30 @@ static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending)
int abs_y = window->last_position_pending ? window->pending.y : window->floating.y;
int offset_x = 0, offset_y = 0;
// Calculate the total offset from the parents
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
if (window->constrain_popup) {
// Calculate the total offset from the parents
for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) {
offset_x += w->x;
offset_y += w->y;
}
offset_x += w->x;
offset_y += w->y;
}
abs_x += offset_x;
abs_y += offset_y;
offset_x += w->x;
offset_y += w->y;
abs_x += offset_x;
abs_y += offset_y;
displayID = SDL_GetDisplayForWindow(w);
displayID = SDL_GetDisplayForWindow(w);
SDL_GetDisplayBounds(displayID, &rect);
if (abs_x + window->w > rect.x + rect.w) {
abs_x -= (abs_x + window->w) - (rect.x + rect.w);
SDL_GetDisplayBounds(displayID, &rect);
if (abs_x + window->w > rect.x + rect.w) {
abs_x -= (abs_x + window->w) - (rect.x + rect.w);
}
if (abs_y + window->h > rect.y + rect.h) {
abs_y -= (abs_y + window->h) - (rect.y + rect.h);
}
abs_x = SDL_max(abs_x, rect.x);
abs_y = SDL_max(abs_y, rect.y);
}
if (abs_y + window->h > rect.y + rect.h) {
abs_y -= (abs_y + window->h) - (rect.y + rect.h);
}
abs_x = SDL_max(abs_x, rect.x);
abs_y = SDL_max(abs_y, rect.y);
if (output_to_pending) {
window->pending.x = abs_x - offset_x;
@@ -249,7 +276,7 @@ static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
toplevel = toplevel->parent;
}
toplevel->internal->keyboard_focus = window;
toplevel->keyboard_focus = window;
if (set_active_focus && !window->is_hiding && !window->is_destroying) {
SDL_SetKeyboardFocus(window);
@@ -387,11 +414,11 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w
X11_XGetWindowAttributes(data->videodata->display, w, &attrib);
if (!SDL_WINDOW_IS_POPUP(window)) {
window->x = data->expected.x = attrib.x;
window->y = data->expected.y = attrib.y - data->border_top;
window->x = window->windowed.x = window->floating.x = attrib.x;
window->y = window->windowed.y = window->floating.y = attrib.y - data->border_top;
}
window->w = data->expected.w = attrib.width;
window->h = data->expected.h = attrib.height;
window->w = window->windowed.w = window->floating.w = attrib.width;
window->h = window->windowed.h = window->floating.h = attrib.height;
if (attrib.map_state != IsUnmapped) {
window->flags &= ~SDL_WINDOW_HIDDEN;
} else {
@@ -513,7 +540,9 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties
return false;
}
SetupWindowInput(_this, window);
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_EXTERNAL_WINDOW_INPUT, true)) {
SetupWindowInput(_this, window);
}
return true;
}
@@ -524,7 +553,7 @@ bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properties
}
const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false);
const bool use_resize_sync = (window->flags & SDL_WINDOW_VULKAN); /* doesn't work well with Vulkan */
const bool use_resize_sync = !!(window->flags & SDL_WINDOW_OPENGL); // Doesn't work well with Vulkan
SDL_WindowData *windowdata;
Display *display = data->display;
int screen = displaydata->screen;
@@ -924,6 +953,75 @@ static int X11_CatchAnyError(Display *d, XErrorEvent *e)
return 0;
}
static void X11_ExternalResizeMoveSync(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
int (*prev_handler)(Display *, XErrorEvent *);
unsigned int childCount;
Window childReturn, root, parent;
Window *children;
XWindowAttributes attrs;
Uint64 timeout = 0;
int x, y;
const bool send_move = !!(data->pending_operation & X11_PENDING_OP_MOVE);
const bool send_resize = !!(data->pending_operation & X11_PENDING_OP_RESIZE);
X11_XSync(display, False);
X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
prev_handler = X11_XSetErrorHandler(X11_CatchAnyError);
/* Wait a brief time to see if the window manager decided to let the move or resize happen.
* If the window changes at all, even to an unexpected value, we break out.
*/
timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(100);
while (true) {
caught_x11_error = false;
X11_XSync(display, False);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &x, &y, &childReturn);
SDL_GlobalToRelativeForWindow(window, x, y, &x, &y);
if (!caught_x11_error) {
if ((data->pending_operation & X11_PENDING_OP_MOVE) && (x == data->expected.x + data->border_left && y == data->expected.y + data->border_top)) {
data->pending_operation &= ~X11_PENDING_OP_MOVE;
}
if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (attrs.width == data->expected.w && attrs.height == data->expected.h)) {
data->pending_operation &= ~X11_PENDING_OP_RESIZE;
}
if (data->pending_operation == X11_PENDING_OP_NONE) {
break;
}
}
if (SDL_GetTicksNS() >= timeout) {
// Timed out without the expected values. Update the requested data so future sync calls won't block.
data->pending_operation &= ~(X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE);
data->expected.x = x;
data->expected.y = y;
data->expected.w = attrs.width;
data->expected.h = attrs.height;
break;
}
SDL_Delay(10);
}
if (!caught_x11_error) {
if (send_move) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y);
}
if (send_resize) {
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height);
}
}
X11_XSetErrorHandler(prev_handler);
caught_x11_error = false;
}
/* Wait a brief time, or not, to see if the window manager decided to move/resize the window.
* Send MOVED and RESIZED window events */
static bool X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, Uint64 param_timeout)
@@ -1099,41 +1197,6 @@ bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
return true;
}
static void X11_SetWMNormalHints(SDL_VideoDevice *_this, SDL_Window *window, XSizeHints *sizehints)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
int dest_x, dest_y;
X11_XSetWMNormalHints(display, data->xwindow, sizehints);
/* From Pierre-Loup:
WMs each have their little quirks with that. When you change the
size hints, they get a ConfigureNotify event with the
WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they
don't all resize the window right away to enforce the new hints.
Some of them resize only after:
- A user-initiated move or resize
- A code-initiated move or resize
- Hiding & showing window (Unmap & map)
The following move & resize seems to help a lot of WMs that didn't
properly update after the hints were changed. We don't do a
hide/show, because there are supposedly subtle problems with doing so
and transitioning from windowed to fullscreen in Unity.
*/
X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h);
const int x = window->last_position_pending ? window->pending.x : window->floating.x;
const int y = window->last_position_pending ? window->pending.y : window->floating.y;
SDL_RelativeToGlobalForWindow(window,
x - data->border_left,
y - data->border_top,
&dest_x, &dest_y);
X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
X11_XRaiseWindow(display, data->xwindow);
}
void X11_SetWindowMinMax(SDL_Window *window, bool use_current)
{
SDL_WindowData *data = window->internal;
@@ -1235,14 +1298,42 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
*/
XSizeHints *sizehints = X11_XAllocSizeHints();
long userhints;
int dest_x, dest_y;
X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
sizehints->min_width = sizehints->max_width = window->pending.w;
sizehints->min_height = sizehints->max_height = window->pending.h;
data->expected.w = sizehints->min_width = sizehints->max_width = window->pending.w;
data->expected.h = sizehints->min_height = sizehints->max_height = window->pending.h;
sizehints->flags |= PMinSize | PMaxSize;
data->pending_operation |= X11_PENDING_OP_RESIZE;
X11_SetWMNormalHints(_this, window, sizehints);
X11_XSetWMNormalHints(display, data->xwindow, sizehints);
/* From Pierre-Loup:
WMs each have their little quirks with that. When you change the
size hints, they get a ConfigureNotify event with the
WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they
don't all resize the window right away to enforce the new hints.
Some of them resize only after:
- A user-initiated move or resize
- A code-initiated move or resize
- Hiding & showing window (Unmap & map)
The following move & resize seems to help a lot of WMs that didn't
properly update after the hints were changed. We don't do a
hide/show, because there are supposedly subtle problems with doing so
and transitioning from windowed to fullscreen in Unity.
*/
X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h);
const int x = window->last_position_pending ? window->pending.x : window->x;
const int y = window->last_position_pending ? window->pending.y : window->y;
SDL_RelativeToGlobalForWindow(window,
x - data->border_left,
y - data->border_top,
&dest_x, &dest_y);
X11_XMoveWindow(display, data->xwindow, dest_x, dest_y);
X11_XRaiseWindow(display, data->xwindow);
X11_XFree(sizehints);
}
@@ -1436,12 +1527,14 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true);
bool set_position = false;
XEvent event;
if (SDL_WINDOW_IS_POPUP(window)) {
// Update the position in case the parent moved while we were hidden
X11_ConstrainPopup(window, true);
X11_UpdateWindowPosition(window, false);
data->pending_position = true;
set_position = true;
}
/* Whether XMapRaised focuses the window is based on the window type and it is
@@ -1453,10 +1546,12 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
/* Blocking wait for "MapNotify" event.
* We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type,
* and XCheckTypedWindowEvent doesn't block */
if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) {
X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
}
X11_XFlush(display);
set_position = data->pending_position ||
(!(window->flags & SDL_WINDOW_BORDERLESS) && !window->undefined_x && !window->undefined_y);
}
if (!data->videodata->net_wm) {
@@ -1466,9 +1561,9 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
X11_XFlush(display);
}
// Popup menus grab the keyboard
if (window->flags & SDL_WINDOW_POPUP_MENU) {
X11_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
// Grabbing popup menus get keyboard focus.
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
X11_SetKeyboardFocus(window, true);
}
// Get some valid border values, if we haven't received them yet
@@ -1476,8 +1571,28 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
X11_GetBorderValues(data);
}
// Apply the pending position, if any, after the window is mapped.
data->pending_position = window->last_position_pending;
if (set_position) {
// Apply the window position, accounting for offsets due to the borders appearing.
const int tx = data->pending_position ? window->pending.x : window->x;
const int ty = data->pending_position ? window->pending.y : window->y;
int x, y;
SDL_RelativeToGlobalForWindow(window,
tx - data->border_left, ty - data->border_top,
&x, &y);
data->pending_position = false;
X11_XMoveWindow(display, data->xwindow, x, y);
}
/* XMonad ignores size hints and shrinks the client area to overlay borders on fixed-size windows,
* even if no borders were requested, resulting in the window client area being smaller than
* requested. Calling XResizeWindow after mapping seems to fix it, even though resizing fixed-size
* windows in this manner doesn't work on any other window manager.
*/
if (!(window->flags & SDL_WINDOW_RESIZABLE) && X11_CheckCurrentDesktop("xmonad")) {
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
}
/* Some window managers can send garbage coordinates while mapping the window, so don't emit size and position
* events during the initial configure events.
@@ -1487,6 +1602,12 @@ void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
X11_PumpEvents(_this);
data->size_move_event_flags = 0;
/* A MapNotify or PropertyNotify may not have arrived, so ensure that the shown event is dispatched
* to apply pending state before clearing the flag.
*/
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
data->was_shown = true;
// If a configure event was received (type is non-zero), send the final window size and coordinates.
if (data->last_xconfigure.type) {
int x, y;
@@ -1507,27 +1628,16 @@ void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
if (X11_IsWindowMapped(_this, window)) {
X11_XWithdrawWindow(display, data->xwindow, screen);
// Blocking wait for "UnmapNotify" event
if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) {
X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
}
X11_XFlush(display);
}
// Transfer keyboard focus back to the parent
if (window->flags & SDL_WINDOW_POPUP_MENU) {
SDL_Window *new_focus = window->parent;
bool set_focus = window == SDL_GetKeyboardFocus();
// Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed.
while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) {
new_focus = new_focus->parent;
// If some window in the chain currently had focus, set it to the new lowest-level window.
if (!set_focus) {
set_focus = new_focus == SDL_GetKeyboardFocus();
}
}
if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
X11_SetKeyboardFocus(new_focus, set_focus);
}
@@ -1678,6 +1788,11 @@ void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
// Don't restore the window the first time it is being shown.
if (!window->internal->was_shown) {
return;
}
if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) {
SDL_SyncWindow(window);
}
@@ -1712,6 +1827,10 @@ static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this,
Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE;
Atom _NET_WM_STATE_FULLSCREEN = data->videodata->atoms._NET_WM_STATE_FULLSCREEN;
if (!data->was_shown && fullscreen == SDL_FULLSCREEN_OP_LEAVE) {
return SDL_FULLSCREEN_SUCCEEDED;
}
if (X11_IsWindowMapped(_this, window)) {
XEvent e;
@@ -1995,6 +2114,33 @@ bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool
return true;
}
/* GNOME needs the _XWAYLAND_MAY_GRAB_KEYBOARD message on XWayland:
*
* - message_type set to "_XWAYLAND_MAY_GRAB_KEYBOARD"
* - window set to the xid of the window on which the grab is to be issued
* - data.l[0] to a non-zero value
*
* The dconf setting `org/gnome/mutter/wayland/xwayland-allow-grabs` must be enabled as well.
*
* https://gitlab.gnome.org/GNOME/mutter/-/commit/5f132f39750f684c3732b4346dec810cd218d609
*/
if (_this->internal->is_xwayland) {
Atom _XWAYLAND_MAY_GRAB_ATOM = X11_XInternAtom(display, "_XWAYLAND_MAY_GRAB_KEYBOARD", False);
if (_XWAYLAND_MAY_GRAB_ATOM != None) {
XClientMessageEvent client_message;
client_message.type = ClientMessage;
client_message.window = data->xwindow;
client_message.format = 32;
client_message.message_type = _XWAYLAND_MAY_GRAB_ATOM;
client_message.data.l[0] = 1;
client_message.data.l[1] = CurrentTime;
X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&client_message);
X11_XFlush(display);
}
}
X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
GrabModeAsync, CurrentTime);
} else {
@@ -2179,6 +2325,15 @@ void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
// If the window is external and has only a pending resize or move event, use the special external sync path to avoid processing events.
if ((window->flags & SDL_WINDOW_EXTERNAL) &&
(data->pending_operation & ~(X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE)) == X11_PENDING_OP_NONE) {
X11_ExternalResizeMoveSync(window);
return true;
}
const Uint64 current_time = SDL_GetTicksNS();
Uint64 timeout = 0;
@@ -2200,21 +2355,37 @@ bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)
bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
XWMHints *wmhints;
if (!SDL_WINDOW_IS_POPUP(window)) {
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
XWMHints *wmhints;
wmhints = X11_XGetWMHints(display, data->xwindow);
if (!wmhints) {
return SDL_SetError("Couldn't get WM hints");
wmhints = X11_XGetWMHints(display, data->xwindow);
if (!wmhints) {
return SDL_SetError("Couldn't get WM hints");
}
wmhints->input = focusable ? True : False;
wmhints->flags |= InputHint;
X11_XSetWMHints(display, data->xwindow, wmhints);
X11_XFree(wmhints);
} else if (window->flags & SDL_WINDOW_POPUP_MENU) {
if (!(window->flags & SDL_WINDOW_HIDDEN)) {
if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
SDL_Window *new_focus;
const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus);
X11_SetKeyboardFocus(new_focus, set_focus);
} else if (focusable) {
if (SDL_ShouldFocusPopup(window)) {
X11_SetKeyboardFocus(window, true);
}
}
}
return true;
}
wmhints->input = focusable ? True : False;
wmhints->flags |= InputHint;
X11_XSetWMHints(display, data->xwindow, wmhints);
X11_XFree(wmhints);
return true;
}
+3 -1
View File
@@ -68,13 +68,13 @@ struct SDL_WindowData
bool pending_move;
SDL_Point pending_move_point;
XConfigureEvent last_xconfigure;
XConfigureEvent pending_xconfigure;
struct SDL_VideoData *videodata;
unsigned long user_time;
Atom xdnd_req;
Window xdnd_source;
bool flashing_window;
Uint64 flash_cancel_time;
SDL_Window *keyboard_focus;
#ifdef SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface;
#endif
@@ -115,6 +115,8 @@ struct SDL_WindowData
bool previous_borders_nonzero;
bool toggle_borders;
bool fullscreen_borders_forced_on;
bool was_shown;
bool emit_size_move_after_property_notify;
SDL_HitTestResult hit_test_result;
XPoint xim_spot;
+20 -7
View File
@@ -129,6 +129,11 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
unsigned char mask[4] = { 0, 0, 0, 0 };
int event, err;
/* XInput2 is required for relative mouse mode, so you probably want to leave this enabled */
if (!SDL_GetHintBoolean("SDL_VIDEO_X11_XINPUT2", true)) {
return false;
}
/*
* Initialize XInput 2
* According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
@@ -279,7 +284,7 @@ static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata,
void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
SDL_VideoData *videodata = _this->internal;
if (cookie->extension != xinput2_opcode) {
return;
@@ -401,11 +406,15 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
case XI_ButtonRelease:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->sourceid);
const int button = xev->detail;
const bool down = (cookie->evtype == XI_ButtonPress);
if (pen) {
if (xev->deviceid != xev->sourceid) {
// Discard events from "Master" devices to avoid duplicates.
break;
}
// Only report button event; if there was also pen movement / pressure changes, we expect an XI_Motion event first anyway.
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
if (button == 1) { // button 1 is the pen tip
@@ -416,9 +425,12 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
} else {
// Otherwise assume a regular mouse
SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event);
int x_ticks = 0, y_ticks = 0;
if (xev->deviceid != xev->sourceid) {
// Discard events from "Master" devices to avoid duplicates.
/* Discard wheel events from "Master" devices to avoid duplicates,
* as coarse wheel events are stateless and can't be deduplicated.
*/
if (xev->deviceid != xev->sourceid && X11_IsWheelEvent(button, &x_ticks, &y_ticks)) {
break;
}
@@ -444,7 +456,8 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
videodata->global_mouse_changed = true;
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->sourceid);
if (pen) {
if (xev->deviceid != xev->sourceid) {
// Discard events from "Master" devices to avoid duplicates.
@@ -550,10 +563,10 @@ bool X11_Xinput2IsInitialized(void)
bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *windowdata = (SDL_WindowData *)window->internal;
SDL_WindowData *windowdata = window->internal;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
const SDL_VideoData *data = (SDL_VideoData *)_this->internal;
const SDL_VideoData *data = _this->internal;
if (X11_Xinput2IsInitialized()) {
XIEventMask eventmask;
+6 -6
View File
@@ -42,7 +42,7 @@ static bool xsync_version_atleast(const int version, const int wantmajor, const
void X11_InitXsync(SDL_VideoDevice *_this)
{
SDL_VideoData *data = (SDL_VideoData *) _this->internal;
SDL_VideoData *data = _this->internal;
int version = 0;
int event, error;
@@ -70,7 +70,7 @@ int X11_XsyncIsInitialized(void)
int X11_InitResizeSync(SDL_Window *window)
{
SDL_assert(window != NULL);
SDL_WindowData *data = (SDL_WindowData *) window->internal;
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER;
XSyncCounter counter;
@@ -99,7 +99,7 @@ int X11_InitResizeSync(SDL_Window *window)
void X11_TermResizeSync(SDL_Window *window)
{
SDL_WindowData *data = (SDL_WindowData *) window->internal;
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER;
XSyncCounter counter = data->resize_counter;
@@ -112,7 +112,7 @@ void X11_TermResizeSync(SDL_Window *window)
void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event)
{
SDL_WindowData *data = (SDL_WindowData *) window->internal;
SDL_WindowData *data = window->internal;
data->resize_id.lo = event->data.l[2];
data->resize_id.hi = event->data.l[3];
@@ -121,7 +121,7 @@ void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event)
void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event)
{
SDL_WindowData *data = (SDL_WindowData *) window->internal;
SDL_WindowData *data = window->internal;
if (data->resize_id.lo || data->resize_id.hi) {
data->resize_in_progress = true;
@@ -130,7 +130,7 @@ void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event)
void X11_HandlePresent(SDL_Window *window)
{
SDL_WindowData *data = (SDL_WindowData *) window->internal;
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
XSyncCounter counter = data->resize_counter;