Update SDL3 from 3.2.4 to 3.2.20
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -135,7 +135,8 @@ VideoBootStrap RPI_bootstrap = {
|
||||
"rpi",
|
||||
"RPI Video Driver",
|
||||
RPI_Create,
|
||||
NULL // no ShowMessageBox implementation
|
||||
NULL, // no ShowMessageBox implementation
|
||||
false
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user