diff options
48 files changed, 882 insertions, 405 deletions
diff --git a/src/3rdparty/libpng/ANNOUNCE b/src/3rdparty/libpng/ANNOUNCE index 516e078082d..ae0b6ccc13b 100644 --- a/src/3rdparty/libpng/ANNOUNCE +++ b/src/3rdparty/libpng/ANNOUNCE @@ -1,5 +1,5 @@ -libpng 1.6.50 - July 1, 2025 -============================ +libpng 1.6.51 - November 21, 2025 +================================= This is a public release of libpng, intended for use in production code. @@ -9,13 +9,13 @@ Files available for download Source files with LF line endings (for Unix/Linux): - * libpng-1.6.50.tar.xz (LZMA-compressed, recommended) - * libpng-1.6.50.tar.gz (deflate-compressed) + * libpng-1.6.51.tar.xz (LZMA-compressed, recommended) + * libpng-1.6.51.tar.gz (deflate-compressed) Source files with CRLF line endings (for Windows): - * lpng1650.7z (LZMA-compressed, recommended) - * lpng1650.zip (deflate-compressed) + * lpng1651.7z (LZMA-compressed, recommended) + * lpng1651.zip (deflate-compressed) Other information: @@ -25,18 +25,33 @@ Other information: * TRADEMARK.md -Changes from version 1.6.49 to version 1.6.50 +Changes from version 1.6.50 to version 1.6.51 --------------------------------------------- - * Improved the detection of the RVV Extension on the RISC-V platform. - (Contributed by Filip Wasil) - * Replaced inline ASM with C intrinsics in the RVV code. - (Contributed by Filip Wasil) - * Fixed a decoder defect in which unknown chunks trailing IDAT, set - to go through the unknown chunk handler, incorrectly triggered - out-of-place IEND errors. - (Contributed by John Bowler) - * Fixed the CMake file for cross-platform builds that require `libm`. + * Fixed CVE-2025-64505 (moderate severity): + Heap buffer overflow in `png_do_quantize` via malformed palette index. + (Reported by Samsung; analyzed by Fabio Gritti.) + * Fixed CVE-2025-64506 (moderate severity): + Heap buffer over-read in `png_write_image_8bit` with 8-bit input and + `convert_to_8bit` enabled. + (Reported by Samsung and <weijinjinnihao@users.noreply.github.com>; + analyzed by Fabio Gritti.) + * Fixed CVE-2025-64720 (high severity): + Buffer overflow in `png_image_read_composite` via incorrect palette + premultiplication. + (Reported by Samsung; analyzed by John Bowler.) + * Fixed CVE-2025-65018 (high severity): + Heap buffer overflow in `png_combine_row` triggered via + `png_image_finish_read`. + (Reported by <yosiimich@users.noreply.github.com>.) + * Fixed a memory leak in `png_set_quantize`. + (Reported by Samsung; analyzed by Fabio Gritti.) + * Removed the experimental and incomplete ERROR_NUMBERS code. + (Contributed by Tobias Stoeckmann.) + * Improved the RISC-V vector extension support; required RVV 1.0 or newer. + (Contributed by Filip Wasil.) + * Added GitHub Actions workflows for automated testing. + * Performed various refactorings and cleanups. Send comments/corrections/commendations to png-mng-implement at lists.sf.net. diff --git a/src/3rdparty/libpng/CHANGES b/src/3rdparty/libpng/CHANGES index b6499b1f34c..2478fd0fc08 100644 --- a/src/3rdparty/libpng/CHANGES +++ b/src/3rdparty/libpng/CHANGES @@ -6278,6 +6278,32 @@ Version 1.6.50 [July 1, 2025] (Contributed by John Bowler) Fixed the CMake file for cross-platform builds that require `libm`. +Version 1.6.51 [November 21, 2025] + Fixed CVE-2025-64505 (moderate severity): + Heap buffer overflow in `png_do_quantize` via malformed palette index. + (Reported by Samsung; analyzed by Fabio Gritti.) + Fixed CVE-2025-64506 (moderate severity): + Heap buffer over-read in `png_write_image_8bit` with 8-bit input and + `convert_to_8bit` enabled. + (Reported by Samsung and <weijinjinnihao@users.noreply.github.com>; + analyzed by Fabio Gritti.) + Fixed CVE-2025-64720 (high severity): + Buffer overflow in `png_image_read_composite` via incorrect palette + premultiplication. + (Reported by Samsung; analyzed by John Bowler.) + Fixed CVE-2025-65018 (high severity): + Heap buffer overflow in `png_combine_row` triggered via + `png_image_finish_read`. + (Reported by <yosiimich@users.noreply.github.com>.) + Fixed a memory leak in `png_set_quantize`. + (Reported by Samsung; analyzed by Fabio Gritti.) + Removed the experimental and incomplete ERROR_NUMBERS code. + (Contributed by Tobias Stoeckmann.) + Improved the RISC-V vector extension support; required RVV 1.0 or newer. + (Contributed by Filip Wasil.) + Added GitHub Actions workflows for automated testing. + Performed various refactorings and cleanups. + Send comments/corrections/commendations to png-mng-implement at lists.sf.net. Subscription is required; visit https://lists.sourceforge.net/lists/listinfo/png-mng-implement diff --git a/src/3rdparty/libpng/README b/src/3rdparty/libpng/README index 2eb633ac0fb..5ea329ee3da 100644 --- a/src/3rdparty/libpng/README +++ b/src/3rdparty/libpng/README @@ -1,4 +1,4 @@ -README for libpng version 1.6.50 +README for libpng version 1.6.51 ================================ See the note about version numbers near the top of `png.h`. diff --git a/src/3rdparty/libpng/libpng-manual.txt b/src/3rdparty/libpng/libpng-manual.txt index 6c07e1022b6..f342c18e814 100644 --- a/src/3rdparty/libpng/libpng-manual.txt +++ b/src/3rdparty/libpng/libpng-manual.txt @@ -9,7 +9,7 @@ libpng-manual.txt - A description on how to use and modify libpng Based on: - libpng version 1.6.36, December 2018, through 1.6.50 - July 2025 + libpng version 1.6.36, December 2018, through 1.6.51 - November 2025 Updated and distributed by Cosmin Truta Copyright (c) 2018-2025 Cosmin Truta @@ -3355,19 +3355,6 @@ Here is an example of writing two private chunks, prVt and miNE: /* Needed because miNE is not safe-to-copy */ png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) "miNE", 1); - # if PNG_LIBPNG_VER < 10600 - /* Deal with unknown chunk location bug in 1.5.x and earlier */ - png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); - png_set_unknown_chunk_location(png, info, 1, PNG_AFTER_IDAT); - # endif - # if PNG_LIBPNG_VER < 10500 - /* PNG_AFTER_IDAT writes two copies of the chunk prior to libpng-1.5.0, - * one before IDAT and another after IDAT, so don't use it; only use - * PNG_HAVE_IHDR location. This call resets the location previously - * set by assignment and png_set_unknown_chunk_location() for chunk 1. - */ - png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); - # endif #endif The high-level write interface diff --git a/src/3rdparty/libpng/png.c b/src/3rdparty/libpng/png.c index 6e21915c402..380c4c19e6a 100644 --- a/src/3rdparty/libpng/png.c +++ b/src/3rdparty/libpng/png.c @@ -13,7 +13,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_50 Your_png_h_is_not_version_1_6_50; +typedef png_libpng_version_1_6_51 Your_png_h_is_not_version_1_6_51; /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the * corresponding macro definitions. This causes a compile time failure if @@ -108,10 +108,16 @@ png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) if (png_ptr == NULL) return NULL; - if (items >= (~(png_alloc_size_t)0)/size) + /* This check against overflow is vestigial, dating back from + * the old times when png_zalloc used to be an exported function. + * We're still keeping it here for now, as an extra-cautious + * prevention against programming errors inside zlib, although it + * should rather be a debug-time assertion instead. + */ + if (size != 0 && items >= (~(png_alloc_size_t)0) / size) { - png_warning (png_voidcast(png_structrp, png_ptr), - "Potential overflow in png_zalloc()"); + png_warning(png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); return NULL; } @@ -238,10 +244,6 @@ png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) png_warning(png_ptr, m); #endif -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags = 0; -#endif - return 0; } @@ -815,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.50" PNG_STRING_NEWLINE \ + "libpng version 1.6.51" PNG_STRING_NEWLINE \ "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ diff --git a/src/3rdparty/libpng/png.h b/src/3rdparty/libpng/png.h index b9985e81680..fb93d2242b5 100644 --- a/src/3rdparty/libpng/png.h +++ b/src/3rdparty/libpng/png.h @@ -1,6 +1,6 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.50 + * libpng version 1.6.51 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson @@ -14,7 +14,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.50, July 2025: + * libpng versions 1.6.36, December 2018, through 1.6.51, November 2025: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -238,7 +238,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.50 16 10650 16.so.16.50[.0] + * 1.6.51 16 10651 16.so.16.51[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -274,7 +274,7 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.50" +#define PNG_LIBPNG_VER_STRING "1.6.51" #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n" /* The versions of shared library builds should stay in sync, going forward */ @@ -285,7 +285,7 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 50 +#define PNG_LIBPNG_VER_RELEASE 51 /* This should be zero for a public release, or non-zero for a * development version. @@ -316,7 +316,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10650 /* 1.6.50 */ +#define PNG_LIBPNG_VER 10651 /* 1.6.51 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -426,7 +426,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_50; +typedef char* png_libpng_version_1_6_51; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * diff --git a/src/3rdparty/libpng/pngconf.h b/src/3rdparty/libpng/pngconf.h index d1081b54ddd..981df68d87a 100644 --- a/src/3rdparty/libpng/pngconf.h +++ b/src/3rdparty/libpng/pngconf.h @@ -1,6 +1,6 @@ /* pngconf.h - machine-configurable file for libpng * - * libpng version 1.6.50 + * libpng version 1.6.51 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson diff --git a/src/3rdparty/libpng/pngdebug.h b/src/3rdparty/libpng/pngdebug.h index af1ae9e8212..0337918aec3 100644 --- a/src/3rdparty/libpng/pngdebug.h +++ b/src/3rdparty/libpng/pngdebug.h @@ -38,9 +38,6 @@ #define PNGDEBUG_H /* These settings control the formatting of messages in png.c and pngerror.c */ /* Moved to pngdebug.h at 1.5.0 */ -# ifndef PNG_LITERAL_SHARP -# define PNG_LITERAL_SHARP 0x23 -# endif # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET # define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b # endif diff --git a/src/3rdparty/libpng/pngerror.c b/src/3rdparty/libpng/pngerror.c index 01a7ef5347e..044fa2eb68c 100644 --- a/src/3rdparty/libpng/pngerror.c +++ b/src/3rdparty/libpng/pngerror.c @@ -39,46 +39,6 @@ PNG_FUNCTION(void,PNGAPI png_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - char msg[16]; - if (png_ptr != NULL) - { - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) - { - if (*error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - for (offset = 1; offset<15; offset++) - if (error_message[offset] == ' ') - break; - - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - int i; - for (i = 0; i < offset - 1; i++) - msg[i] = error_message[i + 1]; - msg[i - 1] = '\0'; - error_message = msg; - } - - else - error_message += offset; - } - - else - { - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - msg[0] = '0'; - msg[1] = '\0'; - error_message = msg; - } - } - } - } -#endif if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), error_message); @@ -216,21 +176,6 @@ void PNGAPI png_warning(png_const_structrp png_ptr, png_const_charp warning_message) { int offset = 0; - if (png_ptr != NULL) - { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) -#endif - { - if (*warning_message == PNG_LITERAL_SHARP) - { - for (offset = 1; offset < 15; offset++) - if (warning_message[offset] == ' ') - break; - } - } - } if (png_ptr != NULL && png_ptr->warning_fn != NULL) (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), warning_message + offset); @@ -712,42 +657,9 @@ png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { #ifdef PNG_CONSOLE_IO_SUPPORTED -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - /* Check on NULL only added in 1.5.4 */ - if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - char error_number[16]; - for (offset = 0; offset<15; offset++) - { - error_number[offset] = error_message[offset + 1]; - if (error_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - error_number[offset - 1] = '\0'; - fprintf(stderr, "libpng error no. %s: %s", - error_number, error_message + offset + 1); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng error: %s, offset=%d", - error_message, offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -#endif - { - fprintf(stderr, "libpng error: %s", error_message ? error_message : - "undefined"); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(error_message) /* Make compiler happy */ #endif @@ -785,40 +697,8 @@ static void /* PRIVATE */ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED -# ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*warning_message == PNG_LITERAL_SHARP) - { - int offset; - char warning_number[16]; - for (offset = 0; offset < 15; offset++) - { - warning_number[offset] = warning_message[offset + 1]; - if (warning_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - warning_number[offset + 1] = '\0'; - fprintf(stderr, "libpng warning no. %s: %s", - warning_number, warning_message + offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng warning: %s", - warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -# endif - - { - fprintf(stderr, "libpng warning: %s", warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(warning_message) /* Make compiler happy */ #endif @@ -866,12 +746,8 @@ png_get_error_ptr(png_const_structrp png_ptr) void PNGAPI png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) { - if (png_ptr != NULL) - { - png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | - PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); - } + PNG_UNUSED(png_ptr) + PNG_UNUSED(strip_mode) } #endif diff --git a/src/3rdparty/libpng/pnglibconf.h b/src/3rdparty/libpng/pnglibconf.h index f15fc16dade..00432d6c033 100644 --- a/src/3rdparty/libpng/pnglibconf.h +++ b/src/3rdparty/libpng/pnglibconf.h @@ -1,6 +1,6 @@ /* pnglibconf.h - library build configuration */ -/* libpng version 1.6.50 */ +/* libpng version 1.6.51 */ /* Copyright (c) 2018-2025 Cosmin Truta */ /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ diff --git a/src/3rdparty/libpng/pngpriv.h b/src/3rdparty/libpng/pngpriv.h index e3054b90aae..fc8d461cf5f 100644 --- a/src/3rdparty/libpng/pngpriv.h +++ b/src/3rdparty/libpng/pngpriv.h @@ -302,7 +302,7 @@ # define PNG_LOONGARCH_LSX_IMPLEMENTATION 0 #endif -#if PNG_RISCV_RVV_OPT > 0 +#if PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 # define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_rvv # ifndef PNG_RISCV_RVV_IMPLEMENTATION /* Use the intrinsics code by default. */ @@ -310,7 +310,7 @@ # endif #else # define PNG_RISCV_RVV_IMPLEMENTATION 0 -#endif +#endif /* PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 */ /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If @@ -710,7 +710,7 @@ /* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000U */ /* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000U */ #define PNG_FLAG_LIBRARY_MISMATCH 0x20000U -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000U + /* 0x40000U unused */ #define PNG_FLAG_STRIP_ERROR_TEXT 0x80000U #define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000U /* Added to libpng-1.4.0 */ #define PNG_FLAG_APP_WARNINGS_WARN 0x200000U /* Added to libpng-1.6.0 */ @@ -1546,7 +1546,7 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); #endif -#if PNG_RISCV_RVV_OPT > 0 +#if PNG_RISCV_RVV_IMPLEMENTATION == 1 PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_rvv,(png_row_infop row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_rvv,(png_row_infop @@ -2175,7 +2175,7 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); #endif -# if PNG_RISCV_RVV_OPT > 0 +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_rvv, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); #endif diff --git a/src/3rdparty/libpng/pngread.c b/src/3rdparty/libpng/pngread.c index 212afb7d215..79917daaaf9 100644 --- a/src/3rdparty/libpng/pngread.c +++ b/src/3rdparty/libpng/pngread.c @@ -3129,6 +3129,54 @@ png_image_read_colormapped(png_voidp argument) } } +/* Row reading for interlaced 16-to-8 bit depth conversion with local buffer. */ +static int +png_image_read_direct_scaled(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_bytep local_row = png_voidcast(png_bytep, display->local_row); + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + int passes; + + /* Handle interlacing. */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + /* Read each pass using local_row as intermediate buffer. */ + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep output_row = first_row; + + for (; y > 0; --y) + { + /* Read into local_row (gets transformed 8-bit data). */ + png_read_row(png_ptr, local_row, NULL); + + /* Copy from local_row to user buffer. */ + memcpy(output_row, local_row, (size_t)row_bytes); + output_row += row_bytes; + } + } + + return 1; +} + /* Just the row reading part of png_image_read. */ static int png_image_read_composite(png_voidp argument) @@ -3547,6 +3595,7 @@ png_image_read_direct(png_voidp argument) int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; int do_local_compose = 0; int do_local_background = 0; /* to avoid double gamma correction bug */ + int do_local_scale = 0; /* for interlaced 16-to-8 bit conversion */ int passes = 0; /* Add transforms to ensure the correct output format is produced then check @@ -3680,8 +3729,16 @@ png_image_read_direct(png_voidp argument) png_set_expand_16(png_ptr); else /* 8-bit output */ + { png_set_scale_16(png_ptr); + /* For interlaced images, use local_row buffer to avoid overflow + * in png_combine_row() which writes using IHDR bit-depth. + */ + if (png_ptr->interlaced != 0) + do_local_scale = 1; + } + change &= ~PNG_FORMAT_FLAG_LINEAR; } @@ -3957,6 +4014,24 @@ png_image_read_direct(png_voidp argument) return result; } + else if (do_local_scale != 0) + { + /* For interlaced 16-to-8 conversion, use an intermediate row buffer + * to avoid buffer overflows in png_combine_row. The local_row is sized + * for the transformed (8-bit) output, preventing the overflow that would + * occur if png_combine_row wrote 16-bit data directly to the user buffer. + */ + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_direct_scaled, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + else { png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes; diff --git a/src/3rdparty/libpng/pngrtran.c b/src/3rdparty/libpng/pngrtran.c index 1809db70473..2f520225515 100644 --- a/src/3rdparty/libpng/pngrtran.c +++ b/src/3rdparty/libpng/pngrtran.c @@ -501,9 +501,19 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, { int i; + /* Initialize the array to index colors. + * + * Ensure quantize_index can fit 256 elements (PNG_MAX_PALETTE_LENGTH) + * rather than num_palette elements. This is to prevent buffer overflows + * caused by malformed PNG files with out-of-range palette indices. + * + * Be careful to avoid leaking memory. Applications are allowed to call + * this function more than once per png_struct. + */ + png_free(png_ptr, png_ptr->quantize_index); png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, - (png_alloc_size_t)num_palette); - for (i = 0; i < num_palette; i++) + PNG_MAX_PALETTE_LENGTH); + for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++) png_ptr->quantize_index[i] = (png_byte)i; } @@ -515,15 +525,14 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, * Perhaps not the best solution, but good enough. */ - int i; + png_bytep quantize_sort; + int i, j; - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + /* Initialize the local array to sort colors. */ + quantize_sort = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_palette); - - /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = (png_byte)i; + quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a * bubble sort, and running it until we have sorted @@ -535,19 +544,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = num_palette - 1; i >= maximum_colors; i--) { int done; /* To stop early if the list is pre-sorted */ - int j; done = 1; for (j = 0; j < i; j++) { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) + if (histogram[quantize_sort[j]] + < histogram[quantize_sort[j + 1]]) { png_byte t; - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; + t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j + 1]; + quantize_sort[j + 1] = t; done = 0; } } @@ -559,18 +567,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, /* Swap the palette around, and set up a table, if necessary */ if (full_quantize != 0) { - int j = num_palette; + j = num_palette; /* Put all the useful colors within the max, but don't * move the others. */ for (i = 0; i < maximum_colors; i++) { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); palette[i] = palette[j]; } @@ -578,7 +586,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } else { - int j = num_palette; + j = num_palette; /* Move all the used colors inside the max limit, and * develop a translation table. @@ -586,13 +594,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = 0; i < maximum_colors; i++) { /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; @@ -630,8 +638,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } } } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; + png_free(png_ptr, quantize_sort); } else { @@ -1774,19 +1781,51 @@ png_init_read_transformations(png_structrp png_ptr) } else /* if (png_ptr->trans_alpha[i] != 0xff) */ { - png_byte v, w; - - v = png_ptr->gamma_to_1[palette[i].red]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); - palette[i].red = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[palette[i].green]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); - palette[i].green = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[w]; + if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0) + { + /* Premultiply only: + * component = round((component * alpha) / 255) + */ + png_uint_32 component; + + component = png_ptr->gamma_to_1[palette[i].red]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].red = png_ptr->gamma_from_1[component]; + + component = png_ptr->gamma_to_1[palette[i].green]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].green = png_ptr->gamma_from_1[component]; + + component = png_ptr->gamma_to_1[palette[i].blue]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].blue = png_ptr->gamma_from_1[component]; + } + else + { + /* Composite with background color: + * component = + * alpha * component + (1 - alpha) * background + */ + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } } } else @@ -5009,13 +5048,8 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) #ifdef PNG_READ_QUANTIZE_SUPPORTED if ((png_ptr->transformations & PNG_QUANTIZE) != 0) - { png_do_quantize(row_info, png_ptr->row_buf + 1, png_ptr->palette_lookup, png_ptr->quantize_index); - - if (row_info->rowbytes == 0) - png_error(png_ptr, "png_do_quantize returned rowbytes=0"); - } #endif /* READ_QUANTIZE */ #ifdef PNG_READ_EXPAND_16_SUPPORTED diff --git a/src/3rdparty/libpng/pngstruct.h b/src/3rdparty/libpng/pngstruct.h index 084422bc1e2..fe5fa041554 100644 --- a/src/3rdparty/libpng/pngstruct.h +++ b/src/3rdparty/libpng/pngstruct.h @@ -405,7 +405,6 @@ struct png_struct_def #ifdef PNG_READ_QUANTIZE_SUPPORTED /* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep quantize_sort; /* working sort array */ png_bytep index_to_palette; /* where the original index currently is in the palette */ png_bytep palette_to_index; /* which original index points to this diff --git a/src/3rdparty/libpng/pngwrite.c b/src/3rdparty/libpng/pngwrite.c index 35a5d17b601..83148960eff 100644 --- a/src/3rdparty/libpng/pngwrite.c +++ b/src/3rdparty/libpng/pngwrite.c @@ -2173,8 +2173,7 @@ png_image_write_main(png_voidp argument) * before it is written. This only applies when the input is 16-bit and * either there is an alpha channel or it is converted to 8-bit. */ - if ((linear != 0 && alpha != 0 ) || - (colormap == 0 && display->convert_to_8bit != 0)) + if (linear != 0 && (alpha != 0 || display->convert_to_8bit != 0)) { png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr))); diff --git a/src/3rdparty/libpng/qt_attribution.json b/src/3rdparty/libpng/qt_attribution.json index 9327dee564c..fe8ba663881 100644 --- a/src/3rdparty/libpng/qt_attribution.json +++ b/src/3rdparty/libpng/qt_attribution.json @@ -7,8 +7,8 @@ "Description": "libpng is the official PNG reference library.", "Homepage": "http://www.libpng.org/pub/png/libpng.html", - "Version": "1.6.50", - "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.50.tar.xz", + "Version": "1.6.51", + "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.51.tar.xz", "PURL": "pkg:github/pnggroup/libpng@v$<VERSION>", "CPE": "cpe:2.3:a:libpng:libpng:$<VERSION>:*:*:*:*:*:*:*", diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 55d375f0350..f31968f8199 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -1510,6 +1510,7 @@ endif() qt_internal_extend_target(Core CONDITION WASM SOURCES + platform/wasm/qwasmanimationdriver.cpp platform/wasm/qwasmanimationdriver_p.h platform/wasm/qwasmglobal.cpp platform/wasm/qwasmglobal_p.h platform/wasm/qstdweb.cpp platform/wasm/qstdweb_p.h platform/wasm/qwasmsocket.cpp platform/wasm/qwasmsocket_p.h diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 17814c6756a..c3e1ba4010f 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -113,6 +113,10 @@ #include "qabstractanimation_p.h" +#if defined(Q_OS_WASM) +#include <QtCore/private/qwasmanimationdriver_p.h> +#endif + #include <QtCore/qmath.h> #include <QtCore/qcoreevent.h> #include <QtCore/qpointer.h> diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index b4f462071a7..1eaa475f613 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -23,6 +23,10 @@ #include <private/qproperty_p.h> #include <qabstractanimation.h> +#if defined(Q_OS_WASM) +#include <QtCore/private/qwasmanimationdriver_p.h> +#endif + QT_REQUIRE_CONFIG(animation); QT_BEGIN_NAMESPACE @@ -184,7 +188,11 @@ private: friend class QAnimationDriver; QAnimationDriver *driver; +#if defined(Q_OS_WASM) + QWasmAnimationDriver defaultDriver; +#else QDefaultAnimationDriver defaultDriver; +#endif QBasicTimer pauseTimer; diff --git a/src/corelib/platform/wasm/qwasmanimationdriver.cpp b/src/corelib/platform/wasm/qwasmanimationdriver.cpp new file mode 100644 index 00000000000..ab0c8240dd1 --- /dev/null +++ b/src/corelib/platform/wasm/qwasmanimationdriver.cpp @@ -0,0 +1,129 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwasmanimationdriver_p.h" +#include "qwasmsuspendresumecontrol_p.h" + +#include <emscripten/val.h> + +QT_BEGIN_NAMESPACE + + +// QWasmAnimationDriver drives animations using requestAnimationFrame(). This +// ensures that animations are advanced in sync with frame update calls, which +// again are synced to the screen refresh rate. + +namespace { + constexpr int FallbackTimerInterval = 500; +} + +QWasmAnimationDriver::QWasmAnimationDriver(QUnifiedTimer *) + : QAnimationDriver(nullptr) +{ + connect(this, &QAnimationDriver::started, this, &QWasmAnimationDriver::start); + connect(this, &QAnimationDriver::stopped, this, &QWasmAnimationDriver::stop); +} + +QWasmAnimationDriver::~QWasmAnimationDriver() +{ + disconnect(this, &QAnimationDriver::started, this, &QWasmAnimationDriver::start); + disconnect(this, &QAnimationDriver::stopped, this, &QWasmAnimationDriver::stop); + + if (m_animateCallbackHandle != 0) + QWasmAnimationFrameMultiHandler::instance()->unregisterAnimateCallback(m_animateCallbackHandle); +} + +qint64 QWasmAnimationDriver::elapsed() const +{ + return isRunning() ? qint64(m_currentTimestamp - m_startTimestamp) : 0; +} + +double QWasmAnimationDriver::getCurrentTimeFromTimeline() const +{ + // Get the current timeline time, which is an equivalent time source to the + // animation frame time source. According to the documentation this API + // may be unavailable in various cases; check for null before accessing. + emscripten::val document = emscripten::val::global("document"); + emscripten::val timeline = document["timeline"]; + if (!timeline.isNull() && !timeline.isUndefined()) { + emscripten::val currentTime = timeline["currentTime"]; + if (!currentTime.isNull() && !currentTime.isUndefined()) + return currentTime.as<double>(); + } + return 0; +} + +void QWasmAnimationDriver::handleFallbackTimeout() +{ + if (!isRunning()) + return; + + // Get the current time from a timing source equivalent to the animation frame time + double currentTime = getCurrentTimeFromTimeline(); + if (currentTime == 0) + currentTime = m_currentTimestamp + FallbackTimerInterval; + const double timeSinceLastFrame = currentTime - m_currentTimestamp; + + // Advance animations if animations are active but there has been no rcent animation + // frame callback. + if (timeSinceLastFrame > FallbackTimerInterval * 0.8) { + m_currentTimestamp = currentTime; + advance(); + } +} + +void QWasmAnimationDriver::start() +{ + if (isRunning()) + return; + + // Set start timestamp to document.timeline.currentTime() + m_startTimestamp = getCurrentTimeFromTimeline(); + m_currentTimestamp = m_startTimestamp; + + // Register animate callback + m_animateCallbackHandle = QWasmAnimationFrameMultiHandler::instance()->registerAnimateCallback( + [this](double timestamp) { handleAnimationFrame(timestamp); }); + + // Start fallback timer to ensure animations advance even if animaton frame callbacks stop coming + fallbackTimer.setInterval(FallbackTimerInterval); + connect(&fallbackTimer, &QTimer::timeout, this, &QWasmAnimationDriver::handleFallbackTimeout); + fallbackTimer.start(); + + QAnimationDriver::start(); +} + +void QWasmAnimationDriver::stop() +{ + m_startTimestamp = 0; + m_currentTimestamp = 0; + + // Stop and disconnect the fallback timer + fallbackTimer.stop(); + disconnect(&fallbackTimer, &QTimer::timeout, this, &QWasmAnimationDriver::handleFallbackTimeout); + + // Deregister the animation frame callback + if (m_animateCallbackHandle != 0) { + QWasmAnimationFrameMultiHandler::instance()->unregisterAnimateCallback(m_animateCallbackHandle); + m_animateCallbackHandle = 0; + } + + QAnimationDriver::stop(); +} + +void QWasmAnimationDriver::handleAnimationFrame(double timestamp) +{ + if (!isRunning()) + return; + + m_currentTimestamp = timestamp; + + // Fall back to setting m_startTimestamp here in cases where currentTime + // was not available in start() (gives 0 elapsed time for the first frame) + if (m_startTimestamp == 0) + m_startTimestamp = timestamp; + + advance(); +} + +QT_END_NAMESPACE diff --git a/src/corelib/platform/wasm/qwasmanimationdriver_p.h b/src/corelib/platform/wasm/qwasmanimationdriver_p.h new file mode 100644 index 00000000000..f8435c17a9a --- /dev/null +++ b/src/corelib/platform/wasm/qwasmanimationdriver_p.h @@ -0,0 +1,54 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWASMANIMATIONDRIVER_P_H +#define QWASMANIMATIONDRIVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qglobal_p.h> +#include <QtCore/qabstractanimation.h> +#include <QtCore/qtimer.h> + +#include <memory> + +QT_BEGIN_NAMESPACE + +class QUnifiedTimer; + +class Q_CORE_EXPORT QWasmAnimationDriver : public QAnimationDriver +{ + Q_OBJECT +public: + QWasmAnimationDriver(QUnifiedTimer *unifiedTimer); + ~QWasmAnimationDriver() override; + + qint64 elapsed() const override; + +protected: + void start() override; + void stop() override; + +private: + void handleAnimationFrame(double timestamp); + void handleFallbackTimeout(); + double getCurrentTimeFromTimeline() const; + + QTimer fallbackTimer; + uint32_t m_animateCallbackHandle = 0; + double m_startTimestamp = 0; + double m_currentTimestamp = 0; +}; + +QT_END_NAMESPACE + +#endif // QWASMANIMATIONDRIVER_P_H diff --git a/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp b/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp index 093898c520a..5fe92926240 100644 --- a/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp +++ b/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp @@ -4,6 +4,8 @@ #include "qwasmsuspendresumecontrol_p.h" #include "qstdweb_p.h" +#include <QtCore/qapplicationstatic.h> + #include <emscripten.h> #include <emscripten/val.h> #include <emscripten/bind.h> @@ -75,32 +77,20 @@ void qtRegisterEventHandlerJs(int index) { }[name]; } - function deepShallowClone(parent, obj, depth) { + function deepShallowClone(obj) { if (obj === null) return obj; - if (typeof obj === 'function') { - if (obj.name !== "") - return createNamedFunction(obj.name, parent, obj); - } - - if (depth >= 1) - return obj; - - if (typeof obj !== 'object') + if (!(obj instanceof Event)) return obj; - if (Array.isArray(obj)) { - const arrCopy = []; - for (let i = 0; i < obj.length; i++) - arrCopy[i] = deepShallowClone(obj, obj[i], depth + 1); - - return arrCopy; - } - const objCopy = {}; - for (const key in obj) - objCopy[key] = deepShallowClone(obj, obj[key], depth + 1); + for (const key in obj) { + if (typeof obj[key] === 'function') + objCopy[key] = createNamedFunction(obj[key].name, obj, obj[key]); + else + objCopy[key] = obj[key]; + } return objCopy; } @@ -110,7 +100,7 @@ void qtRegisterEventHandlerJs(int index) { let handler = (arg) => { // Copy the top level object, alias the rest. // functions are copied by creating new forwarding functions. - arg = deepShallowClone(arg, arg, 0); + arg = deepShallowClone(arg); // Add event to event queue control.pendingEvents.push({ @@ -357,3 +347,103 @@ void QWasmTimer::clearTimeout() val::global("window").call<void>("clearTimeout", double(m_timerId)); m_timerId = 0; } + +// +// QWasmAnimationFrameMultiHandler +// +// Multiplexes multiple animate and draw callbacks to a single native requestAnimationFrame call. +// Animate callbacks are called before draw callbacks to ensure animations are advanced before drawing. +// +QWasmAnimationFrameMultiHandler::QWasmAnimationFrameMultiHandler() +{ + auto wrapper = [this](val arg) { + handleAnimationFrame(arg.as<double>()); + }; + m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(wrapper); +} + +QWasmAnimationFrameMultiHandler::~QWasmAnimationFrameMultiHandler() +{ + cancelAnimationFrameRequest(); + QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex); +} + +Q_APPLICATION_STATIC(QWasmAnimationFrameMultiHandler, s_animationFrameHandler); +QWasmAnimationFrameMultiHandler *QWasmAnimationFrameMultiHandler::instance() +{ + return s_animationFrameHandler(); +} + +// Registers a permanent animation callback. Call unregisterAnimateCallback() to unregister +uint32_t QWasmAnimationFrameMultiHandler::registerAnimateCallback(Callback callback) +{ + uint32_t handle = ++m_nextAnimateHandle; + m_animateCallbacks[handle] = std::move(callback); + ensureAnimationFrameRequested(); + return handle; +} + +// Registers a single-shot draw callback. +uint32_t QWasmAnimationFrameMultiHandler::registerDrawCallback(Callback callback) +{ + uint32_t handle = ++m_nextDrawHandle; + m_drawCallbacks[handle] = std::move(callback); + ensureAnimationFrameRequested(); + return handle; +} + +void QWasmAnimationFrameMultiHandler::unregisterAnimateCallback(uint32_t handle) +{ + m_animateCallbacks.erase(handle); + if (m_animateCallbacks.empty() && m_drawCallbacks.empty()) + cancelAnimationFrameRequest(); +} + +void QWasmAnimationFrameMultiHandler::unregisterDrawCallback(uint32_t handle) +{ + m_drawCallbacks.erase(handle); + if (m_animateCallbacks.empty() && m_drawCallbacks.empty()) + cancelAnimationFrameRequest(); +} + +void QWasmAnimationFrameMultiHandler::handleAnimationFrame(double timestamp) +{ + m_requestId = -1; + + // Advance animations. Copy the callbacks list in case callbacks are + // unregistered during iteration + auto animateCallbacksCopy = m_animateCallbacks; + for (const auto &pair : animateCallbacksCopy) + pair.second(timestamp); + + // Draw the frame. Note that draw callbacks are cleared after each + // frame, matching QWindow::requestUpdate() behavior. Copy the callbacks + // list in case new callbacks are registered while drawing the frame + auto drawCallbacksCopy = m_drawCallbacks; + m_drawCallbacks.clear(); + for (const auto &pair : drawCallbacksCopy) + pair.second(timestamp); + + // Request next frame if there are still callbacks registered + if (!m_animateCallbacks.empty() || !m_drawCallbacks.empty()) + ensureAnimationFrameRequested(); +} + +void QWasmAnimationFrameMultiHandler::ensureAnimationFrameRequested() +{ + if (m_requestId != -1) + return; + + using ReturnType = double; + val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex); + m_requestId = int64_t(val::global("window").call<ReturnType>("requestAnimationFrame", handler)); +} + +void QWasmAnimationFrameMultiHandler::cancelAnimationFrameRequest() +{ + if (m_requestId == -1) + return; + + val::global("window").call<void>("cancelAnimationFrame", double(m_requestId)); + m_requestId = -1; +} diff --git a/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h b/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h index 37c71ed8123..b750d80314c 100644 --- a/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h +++ b/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h @@ -83,4 +83,37 @@ private: uint64_t m_timerId = 0; }; +class Q_CORE_EXPORT QWasmAnimationFrameMultiHandler +{ +public: + using Callback = std::function<void(double)>; + + static QWasmAnimationFrameMultiHandler *instance(); + + uint32_t registerAnimateCallback(Callback callback); + uint32_t registerDrawCallback(Callback callback); + + void unregisterAnimateCallback(uint32_t handle); + void unregisterDrawCallback(uint32_t handle); + + QWasmAnimationFrameMultiHandler(); + ~QWasmAnimationFrameMultiHandler(); + QWasmAnimationFrameMultiHandler(const QWasmAnimationFrameMultiHandler&) = delete; + QWasmAnimationFrameMultiHandler& operator=(const QWasmAnimationFrameMultiHandler&) = delete; + +private: + void handleAnimationFrame(double timestamp); + void ensureAnimationFrameRequested(); + void cancelAnimationFrameRequest(); + + static QWasmAnimationFrameMultiHandler *s_instance; + + std::map<uint32_t, Callback> m_animateCallbacks; + std::map<uint32_t, Callback> m_drawCallbacks; + uint32_t m_nextAnimateHandle = 0; + uint32_t m_nextDrawHandle = 0; + uint32_t m_handlerIndex = 0; + int64_t m_requestId = -1; +}; + #endif diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index 092ff46b084..53cca38ba7b 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -101,8 +101,6 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt } #endif // !QT_CONFIG(opensslv30) -#include "qtcore-config_p.h" - #if QT_CONFIG(system_libb2) #include <blake2.h> #else diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 6fe1ce38a61..9f59d11375b 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -5,7 +5,6 @@ #include "qhttpnetworkconnectionchannel_p.h" #include "qhttpnetworkconnection_p.h" -#include "qhttp2configuration.h" #include "private/qnoncontiguousbytedevice_p.h" #include <qdebug.h> @@ -34,6 +33,12 @@ QT_BEGIN_NAMESPACE // connection times out) // We use 3 because we can get a _q_error 3 times depending on the timing: static const int reconnectAttemptsDefault = 3; +static const char keepAliveIdleOption[] = "QT_QNAM_TCP_KEEPIDLE"; +static const char keepAliveIntervalOption[] = "QT_QNAM_TCP_KEEPINTVL"; +static const char keepAliveCountOption[] = "QT_QNAM_TCP_KEEPCNT"; +static const int TCP_KEEPIDLE_DEF = 60; +static const int TCP_KEEPINTVL_DEF = 10; +static const int TCP_KEEPCNT_DEF = 5; QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel() : socket(nullptr) @@ -914,6 +919,13 @@ void QHttpNetworkConnectionChannel::_q_connected_abstract_socket(QAbstractSocket // not sure yet if it helps, but it makes sense absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); + int kaIdleOption = qEnvironmentVariableIntegerValue(keepAliveIdleOption).value_or(TCP_KEEPIDLE_DEF); + int kaIntervalOption = qEnvironmentVariableIntegerValue(keepAliveIntervalOption).value_or(TCP_KEEPINTVL_DEF); + int kaCountOption = qEnvironmentVariableIntegerValue(keepAliveCountOption).value_or(TCP_KEEPCNT_DEF); + absSocket->setSocketOption(QAbstractSocket::KeepAliveIdleOption, kaIdleOption); + absSocket->setSocketOption(QAbstractSocket::KeepAliveIntervalOption, kaIntervalOption); + absSocket->setSocketOption(QAbstractSocket::KeepAliveCountOption, kaCountOption); + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 3c9cae1fe8b..975332a14ab 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -360,6 +360,21 @@ allow setting the MTU for transmission. This enum value was introduced in Qt 5.11. + \value KeepAliveIdleOption The time in seconds the connection needs to + remain idle before TCP starts sending keepalive probes if + KeepAliveOption is enabled. + This enum value was introduced in Qt 6.11. + + \value KeepAliveIntervalOption The time in seconds between individual + keepalive probes, if KeepAliveOption is enabled. This option is not + supported in all OSes. + This enum value was introduced in Qt 6.11. + + \value KeepAliveCountOption The maximum number of keepalive probes to + send before TCP drops the connection, if KeepAliveOption is enabled. + This option is not supported in all OSes. + This enum value was introduced in Qt 6.11. + Possible values for \e{TypeOfServiceOption} are: \table @@ -1973,6 +1988,18 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons case PathMtuSocketOption: d_func()->socketEngine->setOption(QAbstractSocketEngine::PathMtuInformation, value.toInt()); break; + + case KeepAliveIdleOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveIdleOption, value.toInt()); + break; + + case KeepAliveIntervalOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveIntervalOption, value.toInt()); + break; + + case KeepAliveCountOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveCountOption, value.toInt()); + break; } } @@ -2019,6 +2046,18 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) case PathMtuSocketOption: ret = d_func()->socketEngine->option(QAbstractSocketEngine::PathMtuInformation); break; + + case KeepAliveIdleOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveIdleOption); + break; + + case KeepAliveIntervalOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveIntervalOption); + break; + + case KeepAliveCountOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveCountOption); + break; } if (ret == -1) return QVariant(); diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 8d2e8a299cc..e0707a8ca8a 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -108,7 +108,10 @@ public: TypeOfServiceOption, //IP_TOS SendBufferSizeSocketOption, //SO_SNDBUF ReceiveBufferSizeSocketOption, //SO_RCVBUF - PathMtuSocketOption // IP_MTU + PathMtuSocketOption, // IP_MTU + KeepAliveIdleOption, // TCP_KEEPIDLE + KeepAliveIntervalOption, // TCP_KEEPINTVL + KeepAliveCountOption // TCP_KEEPCNT }; Q_ENUM(SocketOption) enum BindFlag { diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 9340df009a7..c1d9ca56a7f 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -65,6 +65,9 @@ public: MaxStreamsSocketOption, PathMtuInformation, BindInterfaceIndex, + KeepAliveIdleOption, + KeepAliveIntervalOption, + KeepAliveCountOption, }; enum PacketHeaderOption { diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 430197ccc6e..e920de11c13 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -170,6 +170,26 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, #endif } break; + case QNativeSocketEngine::KeepAliveIdleOption: + level = IPPROTO_TCP; +#ifdef TCP_KEEPALIVE + n = TCP_KEEPALIVE; +#else + n = TCP_KEEPIDLE; +#endif + break; + case QNativeSocketEngine::KeepAliveIntervalOption: +#ifdef TCP_KEEPINTVL + level = IPPROTO_TCP; + n = TCP_KEEPINTVL; +#endif + break; + case QNativeSocketEngine::KeepAliveCountOption: +#ifdef TCP_KEEPCNT + level = IPPROTO_TCP; + n = TCP_KEEPCNT; +#endif + break; } } @@ -200,7 +220,7 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM; int socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK); - if (socket < 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) { + if (socket < 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && (errno == EAFNOSUPPORT || errno == ENOTSUP )) { domain = AF_INET; socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK); socketProtocol = QAbstractSocket::IPv4Protocol; @@ -1161,6 +1181,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l sentBytes = -2; break; case EMSGSIZE: + // seen on VxWorks + case ENOMEM: setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString); break; case ECONNRESET: diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 598467ef629..8530c6ca819 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -34,6 +34,15 @@ QT_BEGIN_NAMESPACE #ifndef IP_HOPLIMIT #define IP_HOPLIMIT 21 // Receive packet hop limit. #endif +#ifndef TCP_KEEPIDLE +#define TCP_KEEPIDLE 3 +#endif +#ifndef TCP_KEEPINTVL +#define TCP_KEEPINTVL 17 +#endif +#ifndef TCP_KEEPCNT +#define TCP_KEEPCNT 16 +#endif #if defined(QNATIVESOCKETENGINE_DEBUG) @@ -216,6 +225,18 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, case QAbstractSocketEngine::PathMtuInformation: break; // not supported on Windows + case QNativeSocketEngine::KeepAliveIdleOption: + level = IPPROTO_TCP; + n = TCP_KEEPIDLE; // defined in ws2ipdef.h + break; + case QNativeSocketEngine::KeepAliveIntervalOption: + level = IPPROTO_TCP; + n = TCP_KEEPINTVL; // defined in ws2ipdef.h + break; + case QNativeSocketEngine::KeepAliveCountOption: + level = IPPROTO_TCP; + n = TCP_KEEPCNT; // defined in ws2ipdef.h + break; } } diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm index 56ff5ec313e..ca7e9c808f7 100644 --- a/src/plugins/platforms/cocoa/qnsview_mouse.mm +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -290,8 +290,7 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID) if (qIsNaN(windowPoint.x) || qIsNaN(windowPoint.y)) { screenPoint = [NSEvent mouseLocation]; } else { - NSRect screenRect = [[theEvent window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)]; - screenPoint = screenRect.origin; + screenPoint = [theEvent.window convertPointToScreen:windowPoint]; } } else { screenPoint = [NSEvent mouseLocation]; diff --git a/src/plugins/platforms/direct2d/CMakeLists.txt b/src/plugins/platforms/direct2d/CMakeLists.txt index 0b3ecf33967..38d7e4160a5 100644 --- a/src/plugins/platforms/direct2d/CMakeLists.txt +++ b/src/plugins/platforms/direct2d/CMakeLists.txt @@ -33,6 +33,7 @@ qt_internal_add_plugin(QWindowsDirect2DIntegrationPlugin ../windows/qwindowstheme.cpp ../windows/qwindowstheme.h ../windows/qwindowsthreadpoolrunner.h ../windows/qwindowswindow.cpp ../windows/qwindowswindow.h + ../windows/qwindowswindowclassdescription.cpp ../windows/qwindowswindowclassdescription.h ../windows/qwindowswindowclassregistry.cpp ../windows/qwindowswindowclassregistry.h qwindowsdirect2dbackingstore.cpp qwindowsdirect2dbackingstore.h qwindowsdirect2dbitmap.cpp qwindowsdirect2dbitmap.h diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 99cf8885af5..1b43ff78991 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -17,19 +17,14 @@ bool QWasmCompositor::m_requestUpdateHoldEnabled = false; QWasmCompositor::QWasmCompositor(QWasmScreen *screen) : QObject(screen) -, m_animationFrameHandler(QWasmAnimationFrameHandler([this](double frametime){ - Q_UNUSED(frametime); - this->m_requestAnimationFrameId = -1; - this->deliverUpdateRequests(); - })) { QWindowSystemInterface::setSynchronousWindowSystemEvents(true); } QWasmCompositor::~QWasmCompositor() { - if (m_requestAnimationFrameId != -1) - m_animationFrameHandler.cancelAnimationFrame(m_requestAnimationFrameId); + if (m_drawCallbackHandle != 0) + QWasmAnimationFrameMultiHandler::instance()->unregisterDrawCallback(m_drawCallbackHandle); // TODO(mikolaj.boc): Investigate if m_isEnabled is needed at all. It seems like a frame should // not be generated after this instead. @@ -87,13 +82,18 @@ void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, const QRect &upda // Requests an update/new frame using RequestAnimationFrame void QWasmCompositor::requestUpdate() { - if (m_requestAnimationFrameId != -1) + if (m_drawCallbackHandle != 0) return; if (m_requestUpdateHoldEnabled) return; - m_requestAnimationFrameId = m_animationFrameHandler.requestAnimationFrame(); + m_drawCallbackHandle = QWasmAnimationFrameMultiHandler::instance()->registerDrawCallback( + [this](double frametime) { + Q_UNUSED(frametime); + m_drawCallbackHandle = 0; + deliverUpdateRequests(); + }); } void QWasmCompositor::deliverUpdateRequests() @@ -165,28 +165,3 @@ QWasmScreen *QWasmCompositor::screen() { return static_cast<QWasmScreen *>(parent()); } - -QWasmAnimationFrameHandler::QWasmAnimationFrameHandler(std::function<void(double)> handler) -{ - auto argCastWrapper = [handler](val arg){ handler(arg.as<double>()); }; - m_handlerIndex = QWasmSuspendResumeControl::get()->registerEventHandler(argCastWrapper); -} - -QWasmAnimationFrameHandler::~QWasmAnimationFrameHandler() -{ - QWasmSuspendResumeControl::get()->removeEventHandler(m_handlerIndex); -} - -int64_t QWasmAnimationFrameHandler::requestAnimationFrame() -{ - using ReturnType = double; // FIXME emscripten::val::call() does not support int64_t - val handler = QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_handlerIndex); - return int64_t(val::global("window").call<ReturnType>("requestAnimationFrame", handler)); -} - -void QWasmAnimationFrameHandler::cancelAnimationFrame(int64_t id) -{ - val::global("window").call<void>("cancelAnimationFrame", double(id)); -} - - diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 8fc290dda3b..eb529a3d30b 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -7,6 +7,7 @@ #include "qwasmwindowstack.h" #include <qpa/qplatformwindow.h> +#include <private/qwasmsuspendresumecontrol_p.h> #include <QMap> #include <tuple> @@ -20,18 +21,6 @@ class QWasmScreen; enum class QWasmWindowTreeNodeChangeType; -class QWasmAnimationFrameHandler -{ -public: - QWasmAnimationFrameHandler(std::function<void(double)> handler); - ~QWasmAnimationFrameHandler(); - int64_t requestAnimationFrame(); - void cancelAnimationFrame(int64_t id); - -private: - uint32_t m_handlerIndex; -}; - class QWasmCompositor final : public QObject { Q_OBJECT @@ -65,8 +54,7 @@ private: bool m_isEnabled = true; QMap<QWasmWindow *, std::tuple<QRect, UpdateRequestDeliveryType>> m_requestUpdateWindows; - QWasmAnimationFrameHandler m_animationFrameHandler; - int64_t m_requestAnimationFrameId = -1; + uint32_t m_drawCallbackHandle = 0; bool m_inDeliverUpdateRequest = false; static bool m_requestUpdateHoldEnabled; }; diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt index c7563c72979..40f173c8c82 100644 --- a/src/plugins/platforms/windows/CMakeLists.txt +++ b/src/plugins/platforms/windows/CMakeLists.txt @@ -38,6 +38,7 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin qwindowstheme.cpp qwindowstheme.h qwindowsthreadpoolrunner.h qwindowswindow.cpp qwindowswindow.h + qwindowswindowclassdescription.cpp qwindowswindowclassdescription.h qwindowswindowclassregistry.cpp qwindowswindowclassregistry.h NO_UNITY_BUILD_SOURCES qwindowspointerhandler.cpp diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 3013de1c068..156351987cb 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -696,7 +696,7 @@ HWND QWindowsContext::createDummyWindow(const QString &classNameIn, { if (!wndProc) wndProc = DefWindowProc; - QString className = d->m_windowClassRegistry.registerWindowClass(QWindowsWindowClassRegistry::classNamePrefix() + classNameIn, wndProc); + QString className = d->m_windowClassRegistry.registerWindowClass(classNameIn, wndProc); return CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()), windowName, style, CW_USEDEFAULT, CW_USEDEFAULT, diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 9139ec0c463..2bd2f0c9e3d 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -704,7 +704,7 @@ void QWindowsScreenManager::initialize() qCDebug(lcQpaScreen) << "Initializing screen manager"; auto className = QWindowsWindowClassRegistry::instance()->registerWindowClass( - QWindowsWindowClassRegistry::classNamePrefix() + QLatin1String("ScreenChangeObserverWindow"), + QLatin1String("ScreenChangeObserverWindow"), qDisplayChangeObserverWndProc); // HWND_MESSAGE windows do not get WM_DISPLAYCHANGE, so we need to create diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp index a2ce1e86a4d..beeab1a089e 100644 --- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp +++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp @@ -119,9 +119,9 @@ static inline HWND createTrayIconMessageWindow() if (!ctx) return nullptr; // Register window class in the platform plugin. - const QString className = - ctx->registerWindowClass(QWindowsWindowClassRegistry::classNamePrefix() + "TrayIconMessageWindowClass"_L1, - qWindowsTrayIconWndProc); + const QString className = ctx->registerWindowClass( + "TrayIconMessageWindowClass"_L1, + qWindowsTrayIconWndProc); const wchar_t windowName[] = L"QTrayIconMessageWindow"; return CreateWindowEx(0, reinterpret_cast<const wchar_t *>(className.utf16()), windowName, WS_OVERLAPPED, diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 33d7c4124ac..d132bbb6130 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -549,7 +549,7 @@ QWindowsTheme::QWindowsTheme() refreshIconPixmapSizes(); auto className = QWindowsWindowClassRegistry::instance()->registerWindowClass( - QWindowsWindowClassRegistry::classNamePrefix() + QLatin1String("ThemeChangeObserverWindow"), + QLatin1String("ThemeChangeObserverWindow"), qThemeChangeObserverWndProc); // HWND_MESSAGE windows do not get the required theme events, // so we use a real top-level window that we never show. diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index c49ddbb3247..ed391009423 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -23,6 +23,7 @@ #ifdef QT_NO_CURSOR # include "qwindowscursor.h" #endif +#include "qwindowswindowclassdescription.h" #include "qwindowswindowclassregistry.h" #include <QtGui/qguiapplication.h> @@ -886,7 +887,12 @@ QWindowsWindowData const auto appinst = reinterpret_cast<HINSTANCE>(GetModuleHandle(nullptr)); const QString windowClassName = QWindowsWindowClassRegistry::instance()->registerWindowClass(w); - const QString windowTitlebarName = QWindowsWindowClassRegistry::instance()->registerWindowClass(QStringLiteral("_q_titlebar"), DefWindowProc, CS_VREDRAW|CS_HREDRAW, nullptr, false); + + QWindowsWindowClassDescription windowTitlebarDescription; + windowTitlebarDescription.name = QStringLiteral("_q_titlebar"); + windowTitlebarDescription.style = CS_VREDRAW | CS_HREDRAW; + windowTitlebarDescription.shouldAddPrefix = false; + const QString windowTitlebarName = QWindowsWindowClassRegistry::instance()->registerWindowClass(windowTitlebarDescription); const QScreen *screen{}; const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, diff --git a/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp b/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp new file mode 100644 index 00000000000..63e16260b62 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowswindowclassdescription.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowswindowclassdescription.h" + +#include <QtGui/qwindow.h> + +#include "qwindowswindowclassregistry.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +QWindowsWindowClassDescription QWindowsWindowClassDescription::fromName(QString name, WNDPROC procedure) +{ + return { std::move(name), procedure }; +} + +QWindowsWindowClassDescription QWindowsWindowClassDescription::fromWindow(const QWindow *window, WNDPROC procedure) +{ + Q_ASSERT(window); + + QWindowsWindowClassDescription description; + description.procedure = procedure; + + const Qt::WindowFlags flags = window->flags(); + const Qt::WindowFlags type = flags & Qt::WindowType_Mask; + // Determine style and icon. + description.style = CS_DBLCLKS; + description.hasIcon = true; + // The following will not set CS_OWNDC for any widget window, even if it contains a + // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage. + if (window->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC)) + description.style |= CS_OWNDC; + if (!(flags & Qt::NoDropShadowWindowHint) + && (type == Qt::Popup || window->property("_q_windowsDropShadow").toBool())) { + description.style |= CS_DROPSHADOW; + } + switch (type) { + case Qt::Tool: + case Qt::ToolTip: + case Qt::Popup: + description.style |= CS_SAVEBITS; // Save/restore background + description.hasIcon = false; + break; + case Qt::Dialog: + if (!(flags & Qt::WindowSystemMenuHint)) + description.hasIcon = false; // QTBUG-2027, dialogs without system menu. + break; + } + // Create a unique name for the flag combination + description.name = "QWindow"_L1; + switch (type) { + case Qt::Tool: + description.name += "Tool"_L1; + break; + case Qt::ToolTip: + description.name += "ToolTip"_L1; + break; + case Qt::Popup: + description.name += "Popup"_L1; + break; + default: + break; + } + if (description.style & CS_DROPSHADOW) + description.name += "DropShadow"_L1; + if (description.style & CS_SAVEBITS) + description.name += "SaveBits"_L1; + if (description.style & CS_OWNDC) + description.name += "OwnDC"_L1; + if (description.hasIcon) + description.name += "Icon"_L1; + + return description; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindowclassdescription.h b/src/plugins/platforms/windows/qwindowswindowclassdescription.h new file mode 100644 index 00000000000..9423abf9d2d --- /dev/null +++ b/src/plugins/platforms/windows/qwindowswindowclassdescription.h @@ -0,0 +1,30 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSWINDOWCLASSDESCRIPTION_H +#define QWINDOWSWINDOWCLASSDESCRIPTION_H + +#include "qtwindowsglobal.h" + +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class QWindow; + +struct QWindowsWindowClassDescription +{ + static QWindowsWindowClassDescription fromName(QString name, WNDPROC procedure); + static QWindowsWindowClassDescription fromWindow(const QWindow *window, WNDPROC procedure); + + QString name; + WNDPROC procedure{ DefWindowProc }; + unsigned int style{ 0 }; + HBRUSH brush{ nullptr }; + bool hasIcon{ false }; + bool shouldAddPrefix{ true }; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSWINDOWCLASSDESCRIPTION_H diff --git a/src/plugins/platforms/windows/qwindowswindowclassregistry.cpp b/src/plugins/platforms/windows/qwindowswindowclassregistry.cpp index 0d405dc419a..c330720a09c 100644 --- a/src/plugins/platforms/windows/qwindowswindowclassregistry.cpp +++ b/src/plugins/platforms/windows/qwindowswindowclassregistry.cpp @@ -6,9 +6,9 @@ #include <QtCore/qlibraryinfo.h> #include <QtCore/quuid.h> -#include <QtGui/qwindow.h> #include "qwindowscontext.h" +#include "qwindowswindowclassdescription.h" QT_BEGIN_NAMESPACE @@ -55,64 +55,13 @@ QString QWindowsWindowClassRegistry::classNamePrefix() return result; } -QString QWindowsWindowClassRegistry::registerWindowClass(const QWindow *w) +QString QWindowsWindowClassRegistry::registerWindowClass(const QWindowsWindowClassDescription &description) { - Q_ASSERT(w); - const Qt::WindowFlags flags = w->flags(); - const Qt::WindowFlags type = flags & Qt::WindowType_Mask; - // Determine style and icon. - uint style = CS_DBLCLKS; - bool icon = true; - // The following will not set CS_OWNDC for any widget window, even if it contains a - // QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage. - if (w->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC)) - style |= CS_OWNDC; - if (!(flags & Qt::NoDropShadowWindowHint) - && (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) { - style |= CS_DROPSHADOW; - } - switch (type) { - case Qt::Tool: - case Qt::ToolTip: - case Qt::Popup: - style |= CS_SAVEBITS; // Save/restore background - icon = false; - break; - case Qt::Dialog: - if (!(flags & Qt::WindowSystemMenuHint)) - icon = false; // QTBUG-2027, dialogs without system menu. - break; - } - // Create a unique name for the flag combination - QString cname = classNamePrefix(); - cname += "QWindow"_L1; - switch (type) { - case Qt::Tool: - cname += "Tool"_L1; - break; - case Qt::ToolTip: - cname += "ToolTip"_L1; - break; - case Qt::Popup: - cname += "Popup"_L1; - break; - default: - break; - } - if (style & CS_DROPSHADOW) - cname += "DropShadow"_L1; - if (style & CS_SAVEBITS) - cname += "SaveBits"_L1; - if (style & CS_OWNDC) - cname += "OwnDC"_L1; - if (icon) - cname += "Icon"_L1; - - return registerWindowClass(cname, m_proc, style, nullptr, icon); -} + QString className = description.name; + + if (description.shouldAddPrefix) + className = classNamePrefix() + className; -QString QWindowsWindowClassRegistry::registerWindowClass(QString cname, WNDPROC proc, unsigned style, HBRUSH brush, bool icon) -{ // since multiple Qt versions can be used in one process // each one has to have window class names with a unique name // The first instance gets the unmodified name; if the class @@ -122,52 +71,63 @@ QString QWindowsWindowClassRegistry::registerWindowClass(QString cname, WNDPROC // Note: GetClassInfo() returns != 0 when a class exists. const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr)); WNDCLASS wcinfo; - const bool classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo) != FALSE - && wcinfo.lpfnWndProc != proc; + const bool classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(className.utf16()), &wcinfo) != FALSE + && wcinfo.lpfnWndProc != description.procedure; if (classExists) - cname += QUuid::createUuid().toString(); + className += QUuid::createUuid().toString(); - if (m_registeredWindowClassNames.contains(cname)) // already registered in our list - return cname; + if (m_registeredWindowClassNames.contains(className)) // already registered in our list + return className; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); - wc.style = style; - wc.lpfnWndProc = proc; + wc.style = description.style; + wc.lpfnWndProc = description.procedure; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = appInstance; wc.hCursor = nullptr; - wc.hbrBackground = brush; - if (icon) { + wc.hbrBackground = description.brush; + if (description.hasIcon) { wc.hIcon = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); if (wc.hIcon) { int sw = GetSystemMetrics(SM_CXSMICON); int sh = GetSystemMetrics(SM_CYSMICON); wc.hIconSm = static_cast<HICON>(LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0)); - } else { + } + else { wc.hIcon = static_cast<HICON>(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED)); wc.hIconSm = nullptr; } - } else { + } + else { wc.hIcon = nullptr; wc.hIconSm = nullptr; } wc.lpszMenuName = nullptr; - wc.lpszClassName = reinterpret_cast<LPCWSTR>(cname.utf16()); + wc.lpszClassName = reinterpret_cast<LPCWSTR>(className.utf16()); ATOM atom = RegisterClassEx(&wc); - if (!atom) { + if (!atom) qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.", - qPrintable(cname)); - } + qPrintable(className)); + + m_registeredWindowClassNames.insert(className); + qCDebug(lcQpaWindowClass).nospace() << __FUNCTION__ << ' ' << className + << " style=0x" << Qt::hex << description.style << Qt::dec + << " brush=" << description.brush << " icon=" << description.hasIcon << " atom=" << atom; + return className; +} - m_registeredWindowClassNames.insert(cname); - qCDebug(lcQpaWindowClass).nospace() << __FUNCTION__ << ' ' << cname - << " style=0x" << Qt::hex << style << Qt::dec - << " brush=" << brush << " icon=" << icon << " atom=" << atom; - return cname; +QString QWindowsWindowClassRegistry::registerWindowClass(const QWindow *window) +{ + return registerWindowClass(QWindowsWindowClassDescription::fromWindow(window, m_proc)); +} + +QString QWindowsWindowClassRegistry::registerWindowClass(QString name, WNDPROC procedure) +{ + return registerWindowClass(QWindowsWindowClassDescription::fromName(name, procedure)); } void QWindowsWindowClassRegistry::unregisterWindowClasses() diff --git a/src/plugins/platforms/windows/qwindowswindowclassregistry.h b/src/plugins/platforms/windows/qwindowswindowclassregistry.h index d599497abd0..c19b4f616fb 100644 --- a/src/plugins/platforms/windows/qwindowswindowclassregistry.h +++ b/src/plugins/platforms/windows/qwindowswindowclassregistry.h @@ -16,6 +16,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaWindowClass) class QWindow; +struct QWindowsWindowClassDescription; class QWindowsWindowClassRegistry { @@ -26,12 +27,13 @@ public: static QWindowsWindowClassRegistry *instance(); - static QString classNamePrefix(); - - QString registerWindowClass(const QWindow *w); - QString registerWindowClass(QString cname, WNDPROC proc, unsigned style = 0, HBRUSH brush = nullptr, bool icon = false); + QString registerWindowClass(const QWindowsWindowClassDescription &description); + QString registerWindowClass(const QWindow *window); + QString registerWindowClass(QString name, WNDPROC procedure); private: + static QString classNamePrefix(); + void unregisterWindowClasses(); static QWindowsWindowClassRegistry *m_instance; diff --git a/src/widgets/widgets/qwidgetanimator.cpp b/src/widgets/widgets/qwidgetanimator.cpp index 99a051357ee..1216f535b8b 100644 --- a/src/widgets/widgets/qwidgetanimator.cpp +++ b/src/widgets/widgets/qwidgetanimator.cpp @@ -53,7 +53,7 @@ void QWidgetAnimator::animate(QWidget *widget, const QRect &_final_geometry, boo //If the QStyle has animations, animate if (const int animationDuration = widget->style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, widget)) { AnimationMap::const_iterator it = m_animation_map.constFind(widget); - if (it != m_animation_map.constEnd() && (*it)->endValue().toRect() == final_geometry) + if (it != m_animation_map.constEnd() && *it && (*it)->endValue().toRect() == final_geometry) return; QPropertyAnimation *anim = new QPropertyAnimation(widget, "geometry", widget); @@ -76,7 +76,8 @@ void QWidgetAnimator::animate(QWidget *widget, const QRect &_final_geometry, boo bool QWidgetAnimator::animating() const { - return !m_animation_map.isEmpty(); + auto isActiveAnimation = [](const QPointer<QPropertyAnimation> &p) { return !p.isNull(); }; + return !std::all_of(m_animation_map.begin(), m_animation_map.end(), isActiveAnimation); } QT_END_NAMESPACE diff --git a/tests/auto/corelib/platform/android/tst_android.cpp b/tests/auto/corelib/platform/android/tst_android.cpp index 3665f100a61..b4bb0323f8a 100644 --- a/tests/auto/corelib/platform/android/tst_android.cpp +++ b/tests/auto/corelib/platform/android/tst_android.cpp @@ -430,6 +430,8 @@ void tst_Android::testFullScreenDimensions() widget.showNormal(); } + // TODO needs fix to work in local and CI on same fashion + const bool runsOnCI = qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci"); { // Translucent // available geometry == full display size (system bars visible but drawable under) @@ -437,14 +439,11 @@ void tst_Android::testFullScreenDimensions() widget.show(); QCoreApplication::processEvents(); QTRY_COMPARE(screen->availableGeometry().width(), realSize.getField<jint>("x")); - QTRY_COMPARE(screen->availableGeometry().height(), realSize.getField<jint>("y")); - - QTRY_COMPARE(screen->geometry().width(), realSize.getField<jint>("x")); - // TODO needs fix to work in local and CI on same fashion - const bool runsOnCI = qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci"); if ((sdkVersion > __ANDROID_API_V__) && runsOnCI) QEXPECT_FAIL("", "Fails on Android 16 (QTBUG-141712).", Continue); + QTRY_COMPARE(screen->availableGeometry().height(), realSize.getField<jint>("y")); + QTRY_COMPARE(screen->geometry().width(), realSize.getField<jint>("x")); QTRY_COMPARE(screen->geometry().height(), realSize.getField<jint>("y")); widget.showNormal(); } @@ -455,6 +454,8 @@ void tst_Android::testFullScreenDimensions() widget.showMaximized(); QCoreApplication::processEvents(); QTRY_COMPARE(screen->availableGeometry().width(), realSize.getField<jint>("x")); + if ((sdkVersion > __ANDROID_API_V__) && runsOnCI) + QEXPECT_FAIL("", "Fails on Android 16 (QTBUG-141712).", Continue); QTRY_COMPARE(screen->availableGeometry().height(), realSize.getField<jint>("y")); QTRY_COMPARE(screen->geometry().width(), realSize.getField<jint>("x")); diff --git a/tests/auto/gui/kernel/qguieventdispatcher/BLACKLIST b/tests/auto/gui/kernel/qguieventdispatcher/BLACKLIST new file mode 100644 index 00000000000..a8f73d73f4d --- /dev/null +++ b/tests/auto/gui/kernel/qguieventdispatcher/BLACKLIST @@ -0,0 +1,2 @@ +[postEventFromThread] +macos-26 developer-build # QTBUG-142185 diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp index 1e49847c97f..484c28a484b 100644 --- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp +++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp @@ -9,6 +9,12 @@ #include <emscripten.h> #include <emscripten/threading.h> +#if QT_CONFIG(wasm_jspi) +# define QT_WASM_EMSCRIPTEN_ASYNC ,emscripten::async() +#else +# define QT_WASM_EMSCRIPTEN_ASYNC +#endif + namespace QtWasmTest { namespace { QObject *g_testObject = nullptr; @@ -127,7 +133,7 @@ void passTest() EMSCRIPTEN_BINDINGS(qtwebtestrunner) { emscripten::function("cleanupTestCase", &cleanupTestCase); emscripten::function("getTestFunctions", &getTestFunctions); - emscripten::function("runTestFunction", &runTestFunction, emscripten::async()); + emscripten::function("runTestFunction", &runTestFunction QT_WASM_EMSCRIPTEN_ASYNC); emscripten::function("qtWasmFail", &failTest); emscripten::function("qtWasmPass", &passTest); } |
