diff options
author | Liang Qi <liang.qi@qt.io> | 2016-06-06 08:34:03 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2016-06-06 09:04:55 +0200 |
commit | 57057f76add416d0faf2e09a90c126baafb6198e (patch) | |
tree | 07d54f8e5daeb3ed1161723542e94c324d745166 | |
parent | dc0ae02ebc8e221f952829230c0301a718a6f10b (diff) | |
parent | fd70978693bd92761e9989bc1c76bf83c1e8c987 (diff) |
Merge remote-tracking branch 'origin/5.6' into 5.7
Conflicts:
.qmake.conf
config.tests/unix/nis/nis.cpp
mkspecs/unsupported/freebsd-g++/qplatformdefs.h
src/corelib/tools/qdatetime.cpp
src/corelib/tools/qsimd.cpp
src/corelib/tools/qsimd_p.h
src/network/access/access.pri
src/network/access/qnetworkreplynsurlconnectionimpl.mm
src/network/access/qnetworkreplynsurlconnectionimpl_p.h
src/plugins/platforms/cocoa/qnsview.mm
src/plugins/printsupport/windows/qwindowsprintdevice.cpp
tests/auto/corelib/kernel/qobject/tst_qobject.cpp
tests/auto/network/access/qnetworkreply/BLACKLIST
tests/auto/widgets/widgets/qopenglwidget/BLACKLIST
Change-Id: I4b32055bbf922392ef0264fd403405416fffee57
137 files changed, 2584 insertions, 1357 deletions
diff --git a/config.tests/qpa/eglfs-egldevice/eglfs-egldevice.pro b/config.tests/qpa/eglfs-egldevice/eglfs-egldevice.pro index 521a911219..d0945465a0 100644 --- a/config.tests/qpa/eglfs-egldevice/eglfs-egldevice.pro +++ b/config.tests/qpa/eglfs-egldevice/eglfs-egldevice.pro @@ -6,7 +6,10 @@ for(p, QMAKE_LIBDIR_EGL) { INCLUDEPATH += $$QMAKE_INCDIR_EGL LIBS += $$QMAKE_LIBS_EGL - -LIBS += -ldrm - +CONFIG += link_pkgconfig +!contains(QT_CONFIG, no-pkg-config) { + PKGCONFIG += libdrm +} else { + LIBS += -ldrm +} CONFIG -= qt diff --git a/config.tests/unix/libdl/libdl.cpp b/config.tests/unix/dlopen/dlopen.cpp index 28a82330f2..28a82330f2 100644 --- a/config.tests/unix/libdl/libdl.cpp +++ b/config.tests/unix/dlopen/dlopen.cpp diff --git a/config.tests/unix/dlopen/dlopen.pro b/config.tests/unix/dlopen/dlopen.pro new file mode 100644 index 0000000000..1d34314332 --- /dev/null +++ b/config.tests/unix/dlopen/dlopen.pro @@ -0,0 +1,2 @@ +SOURCES = $$PWD/dlopen.cpp +CONFIG -= qt dylib diff --git a/config.tests/unix/iconv/iconv.pro b/config.tests/unix/iconv/iconv.pro index ca12e51947..65273ccc94 100644 --- a/config.tests/unix/iconv/iconv.pro +++ b/config.tests/unix/iconv/iconv.pro @@ -1,3 +1,3 @@ SOURCES = iconv.cpp CONFIG -= qt dylib -mac|mingw|qnx|haiku:LIBS += -liconv +mac|mingw|openbsd|qnx|haiku:LIBS += -liconv diff --git a/config.tests/unix/libdl/libdl.pro b/config.tests/unix/libdl/libdl.pro index 8ed5231a8f..4016395d35 100644 --- a/config.tests/unix/libdl/libdl.pro +++ b/config.tests/unix/libdl/libdl.pro @@ -1,3 +1,2 @@ -SOURCES = libdl.cpp -CONFIG -= qt dylib -!qnx: LIBS += -ldl +include(../dlopen/dlopen.pro) +LIBS += -ldl diff --git a/config.tests/unix/mysql/mysql.cpp b/config.tests/unix/mysql/mysql.cpp index b66ad62534..697148cbcb 100644 --- a/config.tests/unix/mysql/mysql.cpp +++ b/config.tests/unix/mysql/mysql.cpp @@ -41,5 +41,6 @@ int main(int, char **) { + mysql_get_client_version(); return 0; } diff --git a/config.tests/unix/nis/nis.cpp b/config.tests/unix/nis/nis.cpp deleted file mode 100644 index 5849c51b91..0000000000 --- a/config.tests/unix/nis/nis.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <sys/types.h> -#include <rpc/rpc.h> -#include <rpcsvc/ypclnt.h> -#include <rpcsvc/yp_prot.h> - -int main(int, char **) -{ - char *d; - yp_get_default_domain(&d); - return 0; -} diff --git a/config.tests/unix/nis/nis.pro b/config.tests/unix/nis/nis.pro deleted file mode 100644 index b7f5693b60..0000000000 --- a/config.tests/unix/nis/nis.pro +++ /dev/null @@ -1,4 +0,0 @@ -SOURCES = nis.cpp -CONFIG -= qt dylib -solaris-*:LIBS += -lnsl -else:LIBS += $$QMAKE_LIBS_NIS diff --git a/config.tests/unix/objcopy/objcopy.pro b/config.tests/unix/objcopy/objcopy.pro index 3c28b89ef3..3c76ae92ef 100644 --- a/config.tests/unix/objcopy/objcopy.pro +++ b/config.tests/unix/objcopy/objcopy.pro @@ -1,4 +1,7 @@ SOURCES = objcopy.cpp CONFIG -= qt +TARGET = objcopytest -QMAKE_POST_LINK += $$QMAKE_OBJCOPY --only-keep-debug objcopy objcopy.debug && $$QMAKE_OBJCOPY --strip-debug objcopy && $$QMAKE_OBJCOPY --add-gnu-debuglink=objcopy.debug objcopy +load(resolve_target) + +QMAKE_POST_LINK += $$QMAKE_OBJCOPY --only-keep-debug $$QMAKE_RESOLVED_TARGET objcopytest.debug && $$QMAKE_OBJCOPY --strip-debug $$QMAKE_RESOLVED_TARGET && $$QMAKE_OBJCOPY --add-gnu-debuglink=objcopytest.debug $$QMAKE_RESOLVED_TARGET diff --git a/config.tests/unix/openssl/openssl.cpp b/config.tests/unix/openssl/openssl.cpp index 860567ef4e..d33b62389c 100644 --- a/config.tests/unix/openssl/openssl.cpp +++ b/config.tests/unix/openssl/openssl.cpp @@ -43,6 +43,12 @@ # error "OpenSSL >= 0.9.7 is required" #endif +#include <openssl/ssl.h> + +#if OPENSSL_VERSION_NUMBER-0 >= 0x10002000L && !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES) +# error "OpenSSL was reported as >= 1.0.2 but is missing required features, possibly it's libressl which is unsupported" +#endif + int main() { } @@ -719,7 +719,6 @@ CFG_LIBINPUT=auto CFG_OBSOLETE_WAYLAND=no CFG_EVDEV=auto CFG_TSLIB=auto -CFG_NIS=auto CFG_CUPS=auto CFG_ICONV=auto CFG_DBUS=auto @@ -2052,13 +2051,6 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; - nis) - if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then - CFG_NIS="$VAL" - else - UNKNOWN_OPT=yes - fi - ;; largefile) if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then CFG_LARGEFILE="$VAL" @@ -2655,9 +2647,6 @@ Additional options: -silent ............ Reduce the build output so that warnings and errors can be seen more easily. - -no-nis ............ Do not compile NIS support. - * -nis ............... Compile NIS support. - -no-cups ........... Do not compile CUPS support. * -cups .............. Compile CUPS support. Requires cups/cups.h and libcups.so.2. @@ -2891,10 +2880,17 @@ if [ -z "$PLATFORM" ]; then PLATFORM=ultrix-g++ ;; FreeBSD:*) - PLATFORM=freebsd-clang - PLATFORM_NOTES=" - - Also available for FreeBSD: freebsd-icc - " + if [ "$(uname -r | cut -d. -f1)" -ge 10 ]; then + PLATFORM=freebsd-clang + PLATFORM_NOTES=" + - Also available for FreeBSD: freebsd-g++ + " + else + PLATFORM=freebsd-g++ + PLATFORM_NOTES=" + - Also available for FreeBSD: freebsd-clang + " + fi ;; OpenBSD:*) PLATFORM=openbsd-g++ @@ -3244,6 +3240,9 @@ if [ "$XPLATFORM_ANDROID" = "yes" ]; then if [ "$CFG_DBUS" = "auto" ]; then CFG_DBUS="no" fi + if [ "$CFG_EGLFS" = "auto" ]; then + CFG_EGLFS="no" + fi if [ -z "$CFG_DEFAULT_ANDROID_NDK_HOST" ]; then case $PLATFORM in linux-*) @@ -4829,9 +4828,13 @@ if [ "$CFG_LIBPNG" = "auto" ]; then fi # detect dl -if ! compileTest unix/libdl "libdl"; then - QMakeVar add DEFINES QT_NO_DYNAMIC_LIBRARY +if compileTest unix/dlopen "dlopen"; then QMAKE_CONFIG="$QMAKE_CONFIG no-libdl" +else + if ! compileTest unix/libdl "libdl"; then + QMAKE_CONFIG="$QMAKE_CONFIG no-libdl" + QMakeVar add DEFINES QT_NO_DYNAMIC_LIBRARY + fi fi if [ "$CFG_EGLFS" = "yes" ]; then @@ -5097,23 +5100,6 @@ for _SQLDR in $CFG_SQL_AVAILABLE; do esac done -# auto-detect NIS support -if [ "$CFG_NIS" != "no" ]; then - if compileTest unix/nis "NIS"; then - CFG_NIS=yes - else - if [ "$CFG_NIS" = "yes" ] && [ "$CFG_CONFIGURE_EXIT_ON_ERROR" = "yes" ]; then - echo "NIS support cannot be enabled due to functionality tests!" - echo " Turn on verbose messaging (-v) to $0 to see the final report." - echo " If you believe this message is in error you may use the continue" - echo " switch (-continue) to $0 to continue." - exit 101 - else - CFG_NIS=no - fi - fi -fi - # auto-detect CUPS support if [ "$CFG_CUPS" != "no" ]; then if compileTest unix/cups "Cups"; then @@ -6585,7 +6571,6 @@ elif [ "$CFG_ZLIB" = "system" ]; then fi [ "$CFG_MTDEV" = "yes" ] && QT_CONFIG="$QT_CONFIG mtdev" -[ "$CFG_NIS" = "yes" ] && QT_CONFIG="$QT_CONFIG nis" [ "$CFG_CUPS" = "yes" ] && QT_CONFIG="$QT_CONFIG cups" [ "$CFG_ICONV" = "yes" ] && QT_CONFIG="$QT_CONFIG iconv" [ "$CFG_ICONV" = "sun" ] && QT_CONFIG="$QT_CONFIG sun-libiconv" @@ -7013,7 +6998,6 @@ QMakeVar set sql-plugins "$SQL_PLUGINS" [ "$CFG_INOTIFY" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_INOTIFY" [ "$CFG_EVENTFD" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_EVENTFD" [ "$CFG_CLOEXEC" = "yes" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_THREADSAFE_CLOEXEC=1" -[ "$CFG_NIS" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_NIS" [ "$CFG_OPENSSL" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_OPENSSL" [ "$CFG_OPENSSL" = "linked" ]&& QCONFIG_FLAGS="$QCONFIG_FLAGS QT_LINKED_OPENSSL" [ "$CFG_OPENSSL" = "no" ] && [ "$CFG_SECURETRANSPORT" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_SSL" @@ -7480,7 +7464,6 @@ report_support " libproxy.............." "$CFG_LIBPROXY" report_support " OpenSSL .............." "$CFG_OPENSSL" yes "loading libraries at run-time" linked "linked to the libraries" [ "$XPLATFORM_MAC" = "yes" ] && \ report_support " SecureTransport ......" "$CFG_SECURETRANSPORT" -report_support " NIS ...................." "$CFG_NIS" report_support " OpenGL / OpenVG:" report_support " EGL .................." "$CFG_EGL" report_support " OpenGL ..............." "$CFG_OPENGL" yes "Desktop OpenGL" es2 "OpenGL ES 2.0+" diff --git a/dist/changes-5.6.1 b/dist/changes-5.6.1 new file mode 100644 index 0000000000..c256f36305 --- /dev/null +++ b/dist/changes-5.6.1 @@ -0,0 +1,165 @@ +Qt 5.6.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.6.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + https://doc.qt.io/qt-5/ + +The Qt version 5.6 series is binary compatible with the 5.5.x series. +Applications compiled for 5.5 will continue to run with 5.6. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Important Behavior Changes * +**************************************************************************** + + - Support for DirectFB is disabled by default, due to lack of + development in upstream. To enable the platform plugin, pass the + -directfb option to configure. + - [QTBUG-44964] The new X event compression feature that was added in 5.6.0 + no longer applies to motion events from drawing tablets. + +configure & build system +------------------------ + + - The configure -D/-I/-L/-l/-R options do not affect the build of Qt's + host tools any more when cross-building. While this usually improves + the chances of a build succeeding, it may also require adjustments. + +qmake +----- + + - [Unix] Paths passed to configure -R are not automatically used by 3rd + party projects any more. Use QMAKE_RPATHDIR if your project explicitly + depends on external libraries. Note that this is not needed for Qt or + its transitive dependencies. + - Expansions of ${QMAKE_FILE_IN_BASE} and ${QMAKE_FILE_OUT_BASE} in extra + compilers are now automatically quoted. + +**************************************************************************** +* Library * +**************************************************************************** + +QtCore +------ + + - QObject: + * [QTBUG-52542] If the compiler supports variadic templates, functors + connected to signals will no longer be copied each time the signal is + emitted. + + - QRect: + * Fixed integer overflow in center(). This fixes the result for some + corner-cases such as a 1x1 rectangle at (INT_MIN, INT_MIN), for which the + previous implementation could return anything (due to invoking undefined + behavior), but commonly returned (0, 0). + + - QStringRef: + * Fixed relational operators against (const char*) to return the correct + result. + +QtGui +----- + + - [QTBUG-50199] QWheelEvent::phase() now returns zero rather than + Qt::ScrollUpdate when the wheel event comes from an actual non-emulated + mouse wheel, and the QT_ENABLE_MOUSE_WHEEL_TRACKING environment variable + is set. + +- Image: + * [QTBUG-50745] Fixed possible crash in QImage::pixel() for mono or indexed + images. + +QtWidgets +--------- + + - Dialogs: + * [QTBUG-51148] Fixed font dialog support for fonts with a non-existent + family name and/or pixel size. + + - QHeaderView: + * [QTBUG-50171] Fixed a repainting issue when items had been reordered. + + - QListWidget: + * [QTBUG-15741] Fixed a bug that caused the default drop action to be + ignored when using icon mode. + + +**************************************************************************** +* Platform-specific Changes * +**************************************************************************** + +Android +------- + + - The navigation bar is now hidden only on Android API level 19 and above. + +OS X +---- + - [QTBUG-50262] QStandardPaths now returns the correct display name for the + download folder. + - [QTBUG-7000] QMacPrintEngine now really sets the printer resolution. + - [QTBUG-48138] QPinchGesture on OS X now behaves like on other platforms: + totalScaleFactor is the magnitude of the pinch and scaleFactor is the delta + for the current event. + +Windows +------- + + - Text: + * [QTBUG-18711] Fixed disabling hints for application fonts. + For example, when automatic scaling by device pixel ratio is in effect. + * [QTBUG-47141] Made it possible to disable antialiasing for text when + drawing into images. + +X11/XCB +------- + + - [QTBUG-49071] Fixed failure to deliver focusIn events on hide/show. + +**************************************************************************** +* Tools * +**************************************************************************** + +configure & build system +------------------------ + + - [QTBUG-11545][Windows] Added missing -pch/-no-pch options to configure.exe. + - [QTBUG-37952][Apple] configure -separate-debug-info is now supported. + - [QTBUG-47313][QTBUG-47639] Fixed builds with "debug" and/or "release" in + the build path's name. + - [QTBUG-51621][Unix] Fixed transitive dependencies on non-Qt libraries. + - [QTBUG-51644][QTBUG-53017] Fixed cross-builds which use a host compiler + which is significantly different from the target compiler. + - [QTBUG-52578][QNX] Unified some defaults between the Unix and Windows + configures. + - [Unix] configure -R now supports paths relative to -libdir. + - [Android@Windows] Added missing -android-ndk-host option to configure.exe. + - [MinGW] Fixed -release -force-debug-info builds actually being neither. + - [WinCE] Fixed (Open)SSL detection. + - Fixed builds with static libc. + +qmake +----- + + - [QTBUG-34182] Fixed UTF-8 BOM breaking dependency calculation. + - [QTBUG-38802][WinRT] Capabilities needed by Qt are now automatically + added to the manifest. + - [QTBUG-50924][WEC7][VS] Fixed deployment of Qt. + - [QTBUG-51775][Unix@Windows] Fixed installation of target.targets when + cross-building. + - [QTBUG-51782] Fixed simultaneous use of the separate_debug_info and + no_plugin_name_prefix CONFIG flags. + - [QTBUG-52008] qmake-generated Visual Studio projects now automatically + invoke windeployqt by default. + - [QTBUG-52998] Restored use of -P option when invoking lex. + - The expansions ${QMAKE_FILE_IN_EXT}, ${QMAKE_FILE_IN_NAME}, and + ${QMAKE_FILE_OUT_PATH} are now understood in extra compilers. diff --git a/examples/network/http/httpwindow.cpp b/examples/network/http/httpwindow.cpp index c85ad97248..fddd2c809a 100644 --- a/examples/network/http/httpwindow.cpp +++ b/examples/network/http/httpwindow.cpp @@ -56,9 +56,9 @@ #include "ui_authenticationdialog.h" #ifndef QT_NO_SSL -static const char defaultUrl[] = "https://qt-project.org/"; +static const char defaultUrl[] = "https://www.qt.io/"; #else -static const char defaultUrl[] = "http://qt-project.org/"; +static const char defaultUrl[] = "http://www.qt.io/"; #endif static const char defaultFileName[] = "index.html"; @@ -106,7 +106,7 @@ HttpWindow::HttpWindow(QWidget *parent) connect(urlLineEdit, &QLineEdit::textChanged, this, &HttpWindow::enableDownloadButton); formLayout->addRow(tr("&URL:"), urlLineEdit); - QString downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + QString downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::TempLocation); if (downloadDirectory.isEmpty() || !QFileInfo(downloadDirectory).isDir()) downloadDirectory = QDir::currentPath(); downloadDirectoryLineEdit->setText(QDir::toNativeSeparators(downloadDirectory)); diff --git a/examples/opengl/legacy/shared/qtlogo.h b/examples/opengl/legacy/shared/qtlogo.h index 92ada8eba5..ed2d7d750d 100644 --- a/examples/opengl/legacy/shared/qtlogo.h +++ b/examples/opengl/legacy/shared/qtlogo.h @@ -54,7 +54,8 @@ #include <QObject> #include <QColor> -class QOpenGLFunctions_1_1; +QT_FORWARD_DECLARE_CLASS(QOpenGLFunctions_1_1) + class Patch; struct Geometry; diff --git a/examples/widgets/mac/qmaccocoaviewcontainer/qmaccocoaviewcontainer.pro b/examples/widgets/mac/qmaccocoaviewcontainer/qmaccocoaviewcontainer.pro index 9dea65bf9f..9db3452488 100644 --- a/examples/widgets/mac/qmaccocoaviewcontainer/qmaccocoaviewcontainer.pro +++ b/examples/widgets/mac/qmaccocoaviewcontainer/qmaccocoaviewcontainer.pro @@ -5,3 +5,6 @@ LIBS += -framework AppKit QT += widgets +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mac/qmaccocoaviewcontainer +INSTALLS += target diff --git a/examples/widgets/mac/qmacnativewidget/qmacnativewidget.pro b/examples/widgets/mac/qmacnativewidget/qmacnativewidget.pro index 9da91c7be0..5a19d41dc8 100644 --- a/examples/widgets/mac/qmacnativewidget/qmacnativewidget.pro +++ b/examples/widgets/mac/qmacnativewidget/qmacnativewidget.pro @@ -5,3 +5,7 @@ LIBS += -framework AppKit QT += widgets #QT += widgets-private gui-private core-private + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mac/qmacnativewidget +INSTALLS += target diff --git a/mkspecs/android-g++/qmake.conf b/mkspecs/android-g++/qmake.conf index fc14eeae5a..ef78c42de9 100644 --- a/mkspecs/android-g++/qmake.conf +++ b/mkspecs/android-g++/qmake.conf @@ -168,13 +168,6 @@ QMAKE_STRIP = QMAKE_RANLIB = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLS_PREFIX-ranlib -QMAKE_INCDIR = $$ANDROID_SOURCES_CXX_STL_INCDIR $$QMAKE_ANDROID_PLATFORM_INCDIR -QMAKE_LIBDIR = $$ANDROID_SOURCES_CXX_STL_LIBDIR $$QMAKE_ANDROID_PLATFORM_LIBDIR $$LIBGCC_PATH -QMAKE_INCDIR_X11 = -QMAKE_LIBDIR_X11 = -QMAKE_INCDIR_OPENGL = $$QMAKE_ANDROID_PLATFORM_INCDIR -QMAKE_LIBDIR_OPENGL = $$QMAKE_ANDROID_PLATFORM_LIBDIR - equals(ANDROID_TARGET_ARCH, armeabi)|equals(ANDROID_TARGET_ARCH, armeabi-v7a): \ LIBGCC_PATH_FULL = $$system("$$QMAKE_CC -mthumb-interwork -print-libgcc-file-name") else: \ @@ -182,20 +175,19 @@ else: \ LIBGCC_PATH = $$dirname(LIBGCC_PATH_FULL) +QMAKE_INCDIR = $$ANDROID_SOURCES_CXX_STL_INCDIR $$QMAKE_ANDROID_PLATFORM_INCDIR +QMAKE_LIBDIR = $$ANDROID_SOURCES_CXX_STL_LIBDIR $$QMAKE_ANDROID_PLATFORM_LIBDIR $$LIBGCC_PATH +QMAKE_INCDIR_X11 = +QMAKE_LIBDIR_X11 = +QMAKE_INCDIR_OPENGL = $$QMAKE_ANDROID_PLATFORM_INCDIR +QMAKE_LIBDIR_OPENGL = $$QMAKE_ANDROID_PLATFORM_LIBDIR + QMAKE_LINK = $$QMAKE_CXX QMAKE_LINK_SHLIB = $$QMAKE_CXX QMAKE_LFLAGS = --sysroot=$$ANDROID_PLATFORM_ROOT_PATH QMAKE_RPATHLINK = $$QMAKE_ANDROID_PLATFORM_LIBDIR QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared - -contains(NDK_ROOT, ".*r6")|contains(NDK_ROOT, ".*r5.*") { - !equals(ANDROID_PLATFORM, android-4):!equals(ANDROID_PLATFORM, android-5):!equals(ANDROID_PLATFORM, android-8) { - warning("Your NDK version is outdated. A workaround is enabled. Consider updating your NDK (workarounds are required until r6(a))") - QMAKE_LFLAGS_SHLIB += $$QMAKE_ANDROID_PLATFORM_LIBDIR/crtbegin_so.o $$QMAKE_ANDROID_PLATFORM_LIBDIR/crtend_so.o - } -} - QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined QMAKE_LFLAGS_RPATH = -Wl,-rpath= diff --git a/mkspecs/features/qpa/basicunixfontdatabase.prf b/mkspecs/features/qpa/basicunixfontdatabase.prf index 261eadb5d0..f2e4016c99 100644 --- a/mkspecs/features/qpa/basicunixfontdatabase.prf +++ b/mkspecs/features/qpa/basicunixfontdatabase.prf @@ -1,3 +1,3 @@ contains(QT_CONFIG, system-freetype) { - LIBS += -lfreetype + LIBS_PRIVATE += -lfreetype } diff --git a/mkspecs/features/qpa/genericunixfontdatabase.prf b/mkspecs/features/qpa/genericunixfontdatabase.prf index fc11bea470..b0d81e5571 100644 --- a/mkspecs/features/qpa/genericunixfontdatabase.prf +++ b/mkspecs/features/qpa/genericunixfontdatabase.prf @@ -1,7 +1,7 @@ CONFIG += qpa/basicunixfontdatabase contains(QT_CONFIG, fontconfig) { DEFINES += Q_FONTCONFIGDATABASE - LIBS += -lfontconfig + LIBS_PRIVATE += -lfontconfig } else:!android { fonts.path = $$[QT_INSTALL_LIBS]/fonts fonts.files = $$QT_SOURCE_TREE/lib/fonts/* diff --git a/mkspecs/features/win32/separate_debug_info.prf b/mkspecs/features/win32/separate_debug_info.prf new file mode 100644 index 0000000000..2838020f01 --- /dev/null +++ b/mkspecs/features/win32/separate_debug_info.prf @@ -0,0 +1,18 @@ +have_target:!static:!isEmpty(QMAKE_OBJCOPY) { + load(resolve_target) + QMAKE_TARGET_DEBUG_INFO = $${QMAKE_RESOLVED_TARGET}.debug + + shell_target = $$shell_quote($$relative_path($$QMAKE_RESOLVED_TARGET, $$OUT_PWD)) + shell_target_debug_info = $$shell_quote($$relative_path($$QMAKE_TARGET_DEBUG_INFO, $$OUT_PWD)) + copy_debug_info = $$QMAKE_OBJCOPY --only-keep-debug $$shell_target $$shell_target_debug_info + link_debug_info = $$QMAKE_OBJCOPY --add-gnu-debuglink=$$shell_target_debug_info $$shell_target + strip_debug_info = $$QMAKE_OBJCOPY --strip-debug $$shell_target + + !isEmpty(QMAKE_POST_LINK):QMAKE_POST_LINK = $$escape_expand(\\n\\t)$$QMAKE_POST_LINK + + QMAKE_POST_LINK = $$copy_debug_info && $$strip_debug_info && $$link_debug_info $$QMAKE_POST_LINK + silent:QMAKE_POST_LINK = @echo creating $@.debug && $$QMAKE_POST_LINK + + target.targets += $$QMAKE_TARGET_DEBUG_INFO + QMAKE_DISTCLEAN += $$QMAKE_TARGET_DEBUG_INFO +} diff --git a/mkspecs/unsupported/freebsd-g++/qmake.conf b/mkspecs/freebsd-g++/qmake.conf index 527a94870c..282b6bdfa7 100644 --- a/mkspecs/unsupported/freebsd-g++/qmake.conf +++ b/mkspecs/freebsd-g++/qmake.conf @@ -5,7 +5,7 @@ MAKEFILE_GENERATOR = UNIX QMAKE_PLATFORM = freebsd bsd -include(../../common/unix.conf) +include(../common/unix.conf) QMAKE_CFLAGS_THREAD = -pthread -D_THREAD_SAFE @@ -29,6 +29,6 @@ QMAKE_OBJCOPY = objcopy QMAKE_NM = nm -P QMAKE_RANLIB = -include(../../common/gcc-base-unix.conf) -include(../../common/g++-unix.conf) +include(../common/gcc-base-unix.conf) +include(../common/g++-unix.conf) load(qt_config) diff --git a/mkspecs/freebsd-icc/qplatformdefs.h b/mkspecs/freebsd-g++/qplatformdefs.h index e2f2d7c85b..a7305fe8ea 100644 --- a/mkspecs/freebsd-icc/qplatformdefs.h +++ b/mkspecs/freebsd-g++/qplatformdefs.h @@ -37,4 +37,4 @@ ** ****************************************************************************/ -#include "../freebsd-g++/qplatformdefs.h" +#include "../freebsd-clang/qplatformdefs.h" diff --git a/mkspecs/freebsd-icc/qmake.conf b/mkspecs/freebsd-icc/qmake.conf deleted file mode 100644 index d72ea73278..0000000000 --- a/mkspecs/freebsd-icc/qmake.conf +++ /dev/null @@ -1,95 +0,0 @@ -# -# qmake configuration for freebsd-icc -# -# Written for Intel C++ 7.1 and 8.0 on FreeBSD -# -# Note: Some of the remarks from the Intel compiler are disabled (even -# with 'warn_on' specified): -# -# remark #171: invalid type conversion: "int" to "void *" -# remark #193: zero used for undefined preprocessing identifier -# remark #279: controlling expression is constant -# remark #304: access control not specified ("public" by default) -# remark #310: old-style parameter list (anachronism) -# remark #383: value copied to temporary, reference to temporary used -# remark #424: extra ";" ignored -# remark #444: destructor for base class "Class" is not virtual -# remark #488: template parameter "T" is not used in declaring the parameter -# types of function template "function" -# remark #810: conversion from "type1" to "type2" may loose significant bits -# remark #858: type qualifier on return type is meaningless -# remark #967: conversion from "type1" to "type2"; sizes do not match -# remark #981: operands are evaluated in unspecified order -# remark #1418: external definition with no prior declaration -# remark #1419: external declaration in primary source file -# warning #1476: field uses tail padding of a base class -# warning #1477: GNU C++ compilers may use bit field padding -# warning #1572: floating-point equality and inequality comparisons are unreliable -# - -MAKEFILE_GENERATOR = UNIX -QMAKE_PLATFORM = freebsd bsd - -include(../common/unix.conf) - -QMAKE_COMPILER = gcc intel_icc # icc pretends to be gcc - -QMAKE_CC = icc -QMAKE_LEX = flex -QMAKE_LEXFLAGS = -QMAKE_YACC = yacc -QMAKE_YACCFLAGS = -d -QMAKE_CFLAGS = -wd858,1572 -QMAKE_CFLAGS_DEPS = -M -QMAKE_CFLAGS_WARN_ON = -w2 -wd171,193,279,304,310,383,424,444,488,810,967,981,1418,1419,1476,1477 -QMAKE_CFLAGS_WARN_OFF = -w -QMAKE_CFLAGS_RELEASE = -QMAKE_CFLAGS_DEBUG = -g -QMAKE_CFLAGS_SHLIB = -fpic -QMAKE_CFLAGS_STATIC_LIB = $$QMAKE_CFLAGS_SHLIB -QMAKE_CFLAGS_YACC = -QMAKE_CFLAGS_THREAD = -D_THREAD_SAFE - -QMAKE_CXX = icpc -QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -QMAKE_CXXFLAGS_DEPS = $$QMAKE_CFLAGS_DEPS -QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON -QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF -QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE -QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG -QMAKE_CXXFLAGS_SHLIB = $$QMAKE_CFLAGS_SHLIB -QMAKE_CXXFLAGS_STATIC_LIB = $$QMAKE_CFLAGS_STATIC_LIB -QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC -QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD - -# Addon software goes into /usr/local on the BSDs, by default we will look there -QMAKE_INCDIR = /usr/local/include -QMAKE_LIBDIR = /usr/local/lib -QMAKE_INCDIR_X11 = /usr/X11R6/include -QMAKE_LIBDIR_X11 = /usr/X11R6/lib -QMAKE_INCDIR_OPENGL = /usr/X11R6/include -QMAKE_LIBDIR_OPENGL = /usr/X11R6/lib - -QMAKE_LINK = icpc -QMAKE_LINK_SHLIB = icpc -QMAKE_LFLAGS = -QMAKE_LFLAGS_RELEASE = -QMAKE_LFLAGS_DEBUG = -QMAKE_LFLAGS_SHLIB = -shared -QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB -QMAKE_LFLAGS_SONAME = -Qoption,ld,-soname, -QMAKE_LFLAGS_THREAD = -mt -QMAKE_LFLAGS_RPATH = -Qoption,ld,-rpath, - -QMAKE_LIBS = -QMAKE_LIBS_DYNLOAD = -QMAKE_LIBS_X11 = -lXext -lX11 -lm -QMAKE_LIBS_OPENGL = -lGL -QMAKE_LIBS_THREAD = - -QMAKE_AR = ar cqs -QMAKE_OBJCOPY = objcopy -QMAKE_NM = nm -P -QMAKE_RANLIB = - -load(qt_config) diff --git a/mkspecs/netbsd-g++/qmake.conf b/mkspecs/netbsd-g++/qmake.conf index 04c675da24..b470c9d08a 100644 --- a/mkspecs/netbsd-g++/qmake.conf +++ b/mkspecs/netbsd-g++/qmake.conf @@ -61,6 +61,7 @@ QMAKE_LFLAGS_RPATH = -Wl,-rpath, QMAKE_LIBS = QMAKE_LIBS_DYNLOAD = +QMAKE_LIBS_EXECINFO = -lexecinfo QMAKE_LIBS_X11 = -lXext -lX11 -lm QMAKE_LIBS_OPENGL = -lGL QMAKE_LIBS_THREAD = diff --git a/mkspecs/openbsd-g++/qmake.conf b/mkspecs/openbsd-g++/qmake.conf index 10330bc081..6a66f086ed 100644 --- a/mkspecs/openbsd-g++/qmake.conf +++ b/mkspecs/openbsd-g++/qmake.conf @@ -62,6 +62,7 @@ QMAKE_LFLAGS_NOUNDEF = -Wl,-no_unresolved QMAKE_LIBS = QMAKE_LIBS_DYNLOAD = +QMAKE_LIBS_EXECINFO = -lexecinfo QMAKE_LIBS_X11 = -lXext -lX11 -lm QMAKE_LIBS_OPENGL = -lGL QMAKE_LIBS_THREAD = diff --git a/mkspecs/unsupported/freebsd-g++/qplatformdefs.h b/mkspecs/unsupported/freebsd-g++/qplatformdefs.h deleted file mode 100644 index a3086b973a..0000000000 --- a/mkspecs/unsupported/freebsd-g++/qplatformdefs.h +++ /dev/null @@ -1,40 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the qmake spec of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "../../freebsd-clang/qplatformdefs.h" diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index 77026bb479..8078c377b2 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -1616,6 +1616,13 @@ \note Platform-specific variables that change the extension override the contents of this variable. + \target QMAKE_EXTENSION_STATICLIB + \section1 QMAKE_EXTENSION_STATICLIB + + Contains the extension for shared static libraries. The value of + this variable is typically handled by qmake or + \l{#QMAKESPEC}{qmake.conf} and rarely needs to be modified. + \section1 QMAKE_EXT_MOC Contains the extension used on included moc files. diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index ad05ab1a01..7b711e197b 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -30,6 +30,9 @@ #include "forkfd.h" #include <sys/types.h> +#if defined(__OpenBSD__) || defined(__NetBSD__) +# include <sys/param.h> +#endif #include <sys/time.h> #include <sys/resource.h> #include <sys/wait.h> @@ -65,7 +68,9 @@ # undef HAVE_WAITID #endif -#if defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1000032 +#if (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1000032) || \ + (defined(__OpenBSD__) && OpenBSD >= 201505) || \ + (defined(__NetBSD__) && __NetBSD_Version__ >= 600000000) # define HAVE_PIPE2 1 #endif #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) || \ @@ -410,6 +415,26 @@ chain_handler: old_sigaction.sa_handler(signum); } +static void ignore_sigpipe() +{ +#ifdef O_NOSIGPIPE + static ffd_atomic_int done = FFD_ATOMIC_INIT(0); + if (ffd_atomic_load(&done, FFD_ATOMIC_RELAXED)) + return; +#endif + + struct sigaction action; + memset(&action, 0, sizeof action); + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_IGN; + action.sa_flags = 0; + sigaction(SIGPIPE, &action, NULL); + +#ifdef O_NOSIGPIPE + ffd_atomic_store(&done, 1, FFD_ATOMIC_RELAXED); +#endif +} + static void forkfd_initialize() { #if defined(HAVE_BROKEN_WAITID) @@ -446,6 +471,11 @@ static void forkfd_initialize() */ sigaction(SIGCHLD, &action, &old_sigaction); +#ifndef O_NOSIGPIPE + /* disable SIGPIPE too */ + ignore_sigpipe(); +#endif + #ifndef __GNUC__ atexit(cleanup); #endif @@ -486,13 +516,23 @@ static void cleanup() static int create_pipe(int filedes[], int flags) { - int ret; + int ret = -1; #ifdef HAVE_PIPE2 /* use pipe2(2) whenever possible, since it can thread-safely create a * cloexec pair of pipes. Without it, we have a race condition setting * FD_CLOEXEC */ - ret = pipe2(filedes, O_CLOEXEC); + +# ifdef O_NOSIGPIPE + /* try first with O_NOSIGPIPE */ + ret = pipe2(filedes, O_CLOEXEC | O_NOSIGPIPE); + if (ret == -1) { + /* O_NOSIGPIPE not supported, ignore SIGPIPE */ + ignore_sigpipe(); + } +# endif + if (ret == -1) + ret = pipe2(filedes, O_CLOEXEC); if (ret == -1) return ret; diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index dd846955f6..6a8948822c 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -28,8 +28,9 @@ SOURCES += \ global/qmalloc.cpp \ global/qnumeric.cpp \ global/qlogging.cpp \ - global/qhooks.cpp \ - global/qversiontagging.cpp + global/qhooks.cpp + +VERSIONTAGGING_SOURCES = global/qversiontagging.cpp # qlibraryinfo.cpp includes qconfig.cpp INCLUDEPATH += $$QT_BUILD_TREE/src/corelib/global @@ -63,3 +64,21 @@ journald { syslog { DEFINES += QT_USE_SYSLOG } + +gcc:ltcg { + versiontagging_compiler.commands = $$QMAKE_CXX -c $(CXXFLAGS) $(INCPATH) + + # Disable LTO, as the symbols disappear somehow under GCC + versiontagging_compiler.commands += -fno-lto + + versiontagging_compiler.commands += -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} + versiontagging_compiler.dependency_type = TYPE_C + versiontagging_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} + versiontagging_compiler.input = VERSIONTAGGING_SOURCES + versiontagging_compiler.variable_out = OBJECTS + versiontagging_compiler.name = compiling[versiontagging] ${QMAKE_FILE_IN} + silent: versiontagging_compiler.commands = @echo compiling[versiontagging] ${QMAKE_FILE_IN} && $$versiontagging_compiler.commands + QMAKE_EXTRA_COMPILERS += versiontagging_compiler +} else { + SOURCES += $$VERSIONTAGGING_SOURCES +} diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 85613283f9..669ab136ac 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -756,7 +756,9 @@ # define Q_COMPILER_TEMPLATE_ALIAS # endif # if __has_feature(cxx_thread_local) -# define Q_COMPILER_THREAD_LOCAL +# if !defined(__FreeBSD__) /* FreeBSD clang fails on __cxa_thread_atexit */ +# define Q_COMPILER_THREAD_LOCAL +# endif # endif # if __has_feature(cxx_user_literals) # define Q_COMPILER_UDL @@ -1114,7 +1116,8 @@ # define Q_DECL_NOTHROW Q_DECL_NOEXCEPT #endif -#if defined(Q_COMPILER_ALIGNOF) && !defined(Q_ALIGNOF) +#if defined(Q_COMPILER_ALIGNOF) +# undef Q_ALIGNOF # define Q_ALIGNOF(x) alignof(x) #endif diff --git a/src/corelib/global/qendian.h b/src/corelib/global/qendian.h index c5ff82c10a..34bb015a2f 100644 --- a/src/corelib/global/qendian.h +++ b/src/corelib/global/qendian.h @@ -49,6 +49,11 @@ QT_BEGIN_NAMESPACE +#ifdef __has_builtin +# define QT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define QT_HAS_BUILTIN(x) 0 +#endif /* * ENDIAN FUNCTIONS @@ -71,18 +76,29 @@ template <typename T> inline void qbswap(const T src, uchar *dest) // Used to implement a type-safe and alignment-safe copy operation // If you want to avoid the memcpy, you must write specializations for these functions -template <typename T> inline void qToUnaligned(const T src, uchar *dest) +template <typename T> Q_ALWAYS_INLINE void qToUnaligned(const T src, uchar *dest) { // Using sizeof(T) inside memcpy function produces internal compiler error with // MSVC2008/ARM in tst_endian -> use extra indirection to resolve size of T. const size_t size = sizeof(T); - memcpy(dest, &src, size); +#if QT_HAS_BUILTIN(__builtin_memcpy) + __builtin_memcpy +#else + memcpy +#endif + (dest, &src, size); } -template <typename T> inline T qFromUnaligned(const uchar *src) + +template <typename T> Q_ALWAYS_INLINE T qFromUnaligned(const uchar *src) { T dest; const size_t size = sizeof(T); - memcpy(&dest, src, size); +#if QT_HAS_BUILTIN(__builtin_memcpy) + __builtin_memcpy +#else + memcpy +#endif + (&dest, src, size); return dest; } diff --git a/src/corelib/global/qendian.qdoc b/src/corelib/global/qendian.qdoc index 63d924211f..3b22dcec87 100644 --- a/src/corelib/global/qendian.qdoc +++ b/src/corelib/global/qendian.qdoc @@ -34,6 +34,29 @@ */ /*! + \internal + \fn T qFromUnaligned(const uchar *ptr) + \since 5.5 + + Loads a \c{T} from address \a ptr, which may be misaligned. + + Use of this function avoids the undefined behavior that the C++ standard + otherwise attributes to unaligned loads. +*/ + +/*! + \internal + \fn void qToUnaligned(T t, uchar *ptr) + \since 4.5 + + Stores \a t to address \a ptr, which may be misaligned. + + Use of this function avoids the undefined behavior that the C++ standard + otherwise attributes to unaligned stores. +*/ + + +/*! \fn T qFromBigEndian(const uchar *src) \since 4.3 \relates <QtEndian> diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h index 97f750a3a6..1e95d1a2ec 100644 --- a/src/corelib/global/qsystemdetection.h +++ b/src/corelib/global/qsystemdetection.h @@ -67,6 +67,7 @@ NETBSD - NetBSD OPENBSD - OpenBSD BSDI - BSD/OS + INTERIX - Interix IRIX - SGI Irix OSF - HP Tru64 UNIX SCO - SCO OpenServer 5 @@ -172,6 +173,9 @@ #elif defined(__bsdi__) # define Q_OS_BSDI # define Q_OS_BSD4 +#elif defined(__INTERIX) +# define Q_OS_INTERIX +# define Q_OS_BSD4 #elif defined(__sgi) # define Q_OS_IRIX #elif defined(__osf__) diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 06674e9a3f..1cb2909c2a 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -149,7 +149,6 @@ win32 { SOURCES += io/qsettings_mac.cpp OBJECTIVE_SOURCES += io/qurl_mac.mm } - freebsd: LIBS_PRIVATE += -lutil # qlockfile_unix.cpp requires this mac { SOURCES += io/qstorageinfo_mac.cpp OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index 8b11830fbe..acea610625 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -58,7 +58,7 @@ # include "qfilesystemwatcher_win_p.h" #elif defined(USE_INOTIFY) # include "qfilesystemwatcher_inotify_p.h" -#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_IOS) +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_IOS) # include "qfilesystemwatcher_kqueue_p.h" #elif defined(Q_OS_OSX) # include "qfilesystemwatcher_fsevents_p.h" @@ -74,7 +74,7 @@ QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine(QObject // there is a chance that inotify may fail on Linux pre-2.6.13 (August // 2005), so we can't just new inotify directly. return QInotifyFileSystemWatcherEngine::create(parent); -#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_IOS) +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_IOS) return QKqueueFileSystemWatcherEngine::create(parent); #elif defined(Q_OS_OSX) return QFseventsFileSystemWatcherEngine::create(parent); diff --git a/src/corelib/io/qfilesystemwatcher_kqueue.cpp b/src/corelib/io/qfilesystemwatcher_kqueue.cpp index 264973d556..4f6c83ebcf 100644 --- a/src/corelib/io/qfilesystemwatcher_kqueue.cpp +++ b/src/corelib/io/qfilesystemwatcher_kqueue.cpp @@ -172,7 +172,6 @@ QStringList QKqueueFileSystemWatcherEngine::removePaths(const QStringList &paths QStringList *files, QStringList *directories) { - bool isEmpty; QStringList p = paths; if (pathToID.isEmpty()) return p; @@ -193,7 +192,6 @@ QStringList QKqueueFileSystemWatcherEngine::removePaths(const QStringList &paths else files->removeAll(path); } - isEmpty = pathToID.isEmpty(); return p; } diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp index 03dc5fc660..e1bd769e77 100644 --- a/src/corelib/io/qlockfile_unix.cpp +++ b/src/corelib/io/qlockfile_unix.cpp @@ -71,14 +71,12 @@ #elif defined(Q_OS_HAIKU) # include <kernel/OS.h> #elif defined(Q_OS_BSD4) && !defined(Q_OS_IOS) +# if !defined(Q_OS_NETBSD) # include <sys/user.h> -# if defined(__GLIBC__) && defined(__FreeBSD_kernel__) +# endif # include <sys/cdefs.h> # include <sys/param.h> # include <sys/sysctl.h> -# else -# include <libutil.h> -# endif #endif QT_BEGIN_NAMESPACE @@ -286,30 +284,33 @@ QString QLockFilePrivate::processNameByPid(qint64 pid) return QString(); return QFile::decodeName(info.name); #elif defined(Q_OS_BSD4) && !defined(Q_OS_IOS) -# if defined(__GLIBC__) && defined(__FreeBSD_kernel__) - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; - size_t len = 0; - if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0) - return QString(); - kinfo_proc *proc = static_cast<kinfo_proc *>(malloc(len)); +# if defined(Q_OS_NETBSD) + struct kinfo_proc2 kp; + int mib[6] = { CTL_KERN, KERN_PROC2, KERN_PROC_PID, (int)pid, sizeof(struct kinfo_proc2), 1 }; +# elif defined(Q_OS_OPENBSD) + struct kinfo_proc kp; + int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid, sizeof(struct kinfo_proc), 1 }; # else - kinfo_proc *proc = kinfo_getproc(pid); + struct kinfo_proc kp; + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid }; # endif - if (!proc) + size_t len = sizeof(kp); + u_int mib_len = sizeof(mib)/sizeof(u_int); + + if (sysctl(mib, mib_len, &kp, &len, NULL, 0) < 0) return QString(); -# if defined(__GLIBC__) && defined(__FreeBSD_kernel__) - if (sysctl(mib, 4, proc, &len, NULL, 0) < 0) { - free(proc); + +# if defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) + if (kp.p_pid != pid) return QString(); - } - if (proc->ki_pid != pid) { - free(proc); + QString name = QFile::decodeName(kp.p_comm); +# else + if (kp.ki_pid != pid) return QString(); - } + QString name = QFile::decodeName(kp.ki_comm); # endif - QString name = QFile::decodeName(proc->ki_comm); - free(proc); return name; + #else Q_UNUSED(pid); return QString(); diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp index 9062df37da..4afab3d135 100644 --- a/src/corelib/io/qlockfile_win.cpp +++ b/src/corelib/io/qlockfile_win.cpp @@ -143,9 +143,11 @@ bool QLockFilePrivate::isApparentlyStale() const if (!procHandle) return true; // We got a handle but check if process is still alive - DWORD dwR = ::WaitForSingleObject(procHandle, 0); + DWORD exitCode = 0; + if (!::GetExitCodeProcess(procHandle, &exitCode)) + exitCode = 0; ::CloseHandle(procHandle); - if (dwR == WAIT_TIMEOUT) + if (exitCode != STILL_ACTIVE) return true; const QString processName = processNameByPid(pid); if (!processName.isEmpty() && processName != appname) diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index a9d3e0bfe8..914d4421d0 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -2097,10 +2097,7 @@ void QProcess::start(const QString &program, const QStringList &arguments, OpenM return; } if (program.isEmpty()) { - Q_D(QProcess); - d->processError = QProcess::FailedToStart; - setErrorString(tr("No program defined")); - emit error(d->processError); + d->setErrorAndEmit(QProcess::FailedToStart, tr("No program defined")); return; } @@ -2127,10 +2124,7 @@ void QProcess::start(OpenMode mode) return; } if (d->program.isEmpty()) { - Q_D(QProcess); - d->processError = QProcess::FailedToStart; - setErrorString(tr("No program defined")); - emit error(d->processError); + d->setErrorAndEmit(QProcess::FailedToStart, tr("No program defined")); return; } diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index 3a9f74bf59..0daf041954 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -84,7 +84,7 @@ # if !defined(ST_RDONLY) # define ST_RDONLY MNT_RDONLY # endif -# if !defined(_STATFS_F_FLAGS) +# if !defined(_STATFS_F_FLAGS) && !defined(Q_OS_NETBSD) # define _STATFS_F_FLAGS 1 # endif #elif defined(Q_OS_ANDROID) diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp index ab179641f8..b0ca0d979b 100644 --- a/src/corelib/io/qwindowspipewriter.cpp +++ b/src/corelib/io/qwindowspipewriter.cpp @@ -208,6 +208,8 @@ bool QWindowsPipeWriter::write(const QByteArray &ba) void QWindowsPipeWriter::stop() { stopped = true; + bytesWrittenPending = false; + pendingBytesWrittenValue = 0; if (writeSequenceStarted) { if (!qt_cancelIo(handle, &overlapped)) { const DWORD dwError = GetLastError(); diff --git a/src/corelib/json/qjson_p.h b/src/corelib/json/qjson_p.h index 5e34845fe3..f92a6821d7 100644 --- a/src/corelib/json/qjson_p.h +++ b/src/corelib/json/qjson_p.h @@ -410,7 +410,7 @@ public: // pack with itself, we'll discard the high part anyway chunk = _mm_packus_epi16(chunk, chunk); // unaligned 64-bit store - qUnalignedStore(l + i, _mm_cvtsi128_si64(chunk)); + qToUnaligned(_mm_cvtsi128_si64(chunk), l + i); i += 8; } # endif diff --git a/src/corelib/json/qjsonvalue.cpp b/src/corelib/json/qjsonvalue.cpp index b21f59ae35..718dfa43b3 100644 --- a/src/corelib/json/qjsonvalue.cpp +++ b/src/corelib/json/qjsonvalue.cpp @@ -275,25 +275,11 @@ QJsonValue::QJsonValue(const QJsonValue &other) */ QJsonValue &QJsonValue::operator =(const QJsonValue &other) { - if (t == String && stringData && !stringData->ref.deref()) - free(stringData); - - t = other.t; - dbl = other.dbl; - - if (d != other.d) { - - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); - - } - - if (t == String && stringData) - stringData->ref.ref(); - + QJsonValue copy(other); + // swap(copy); + qSwap(dbl, copy.dbl); + qSwap(d, copy.d); + qSwap(t, copy.t); return *this; } diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp index 93af957d95..2042964427 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -41,16 +41,6 @@ #include "qcore_unix_p.h" #include "qelapsedtimer.h" -#ifdef Q_OS_NACL -#elif !defined (Q_OS_VXWORKS) -# if !defined(Q_OS_HPUX) || defined(__ia64) -# include <sys/select.h> -# endif -# include <sys/time.h> -#else -# include <selectLib.h> -#endif - #include <stdlib.h> #ifdef Q_OS_MAC diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index 319914a99d..53f2a50191 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -65,6 +65,16 @@ #include <sys/stat.h> #include <unistd.h> +#ifdef Q_OS_NACL +#elif !defined (Q_OS_VXWORKS) +# if !defined(Q_OS_HPUX) || defined(__ia64) +# include <sys/select.h> +# endif +# include <sys/time.h> +#else +# include <selectLib.h> +#endif + #include <sys/wait.h> #include <errno.h> #include <fcntl.h> diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 9b421d6a78..ffb50ddac3 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4699,7 +4699,7 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s QOrderedMutexLocker locker(signalSlotLock(sender), signalSlotLock(receiver)); - if (type & Qt::UniqueConnection) { + if (type & Qt::UniqueConnection && slot) { QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; if (connectionLists && connectionLists->count() > signal_index) { const QObjectPrivate::Connection *c2 = diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index e99c8f35f3..c06f702b30 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -419,8 +419,7 @@ protected: QScopedPointer<QObjectData> d_ptr; static const QMetaObject staticQtMetaObject; - friend inline const QMetaObject *qt_getQtMetaObject() Q_DECL_NOEXCEPT - { return &staticQtMetaObject; } + friend inline const QMetaObject *qt_getQtMetaObject() Q_DECL_NOEXCEPT; friend struct QMetaObject; friend struct QMetaObjectPrivate; @@ -451,6 +450,9 @@ inline QMetaObject::Connection QObject::connect(const QObject *asender, const ch const char *amember, Qt::ConnectionType atype) const { return connect(asender, asignal, this, amember, atype); } +inline const QMetaObject *qt_getQtMetaObject() Q_DECL_NOEXCEPT +{ return &QObject::staticQtMetaObject; } + #ifndef QT_NO_USERDATA class Q_CORE_EXPORT QObjectUserData { public: diff --git a/src/corelib/mimetypes/qmimemagicrule.cpp b/src/corelib/mimetypes/qmimemagicrule.cpp index 0db3407b7b..8461bf7130 100644 --- a/src/corelib/mimetypes/qmimemagicrule.cpp +++ b/src/corelib/mimetypes/qmimemagicrule.cpp @@ -48,7 +48,6 @@ #include <QtCore/QList> #include <QtCore/QDebug> #include <qendian.h> -#include <private/qsimd_p.h> // for qUnalignedLoad QT_BEGIN_NAMESPACE @@ -164,7 +163,7 @@ bool QMimeMagicRule::matchNumber(const QByteArray &data) const const char *p = data.constData() + m_startPos; const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), m_endPos + 1); for ( ; p <= e; ++p) { - if ((qUnalignedLoad<T>(p) & mask) == (value & mask)) + if ((qFromUnaligned<T>(reinterpret_cast<const uchar *>(p)) & mask) == (value & mask)) return true; } diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index 446e09b1c0..12e4687b3c 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -42,6 +42,7 @@ #include <qalgorithms.h> #include <qdatastream.h> #include <qdebug.h> +#include <qendian.h> #include <string.h> QT_BEGIN_NAMESPACE @@ -169,25 +170,6 @@ QBitArray::QBitArray(int size, bool value) Same as size(). */ -template <typename T> T qUnalignedLoad(const uchar *ptr) -{ - /* - * Testing with different compilers shows that they all optimize the memcpy - * call away and replace with direct loads whenever possible. On x86 and PPC, - * GCC does direct unaligned loads; on MIPS, it generates a pair of load-left - * and load-right instructions. ICC and Clang do the same on x86. This is both - * 32- and 64-bit. - * - * On ARM cores without unaligned loads, the compiler leaves a call to - * memcpy. - */ - - T u; - memcpy(&u, ptr, sizeof(u)); - return u; -} - - /*! If \a on is true, this function returns the number of 1-bits stored in the bit array; otherwise the number @@ -203,17 +185,17 @@ int QBitArray::count(bool on) const const quint8 *const end = reinterpret_cast<const quint8 *>(d.end()); while (bits + 7 <= end) { - quint64 v = qUnalignedLoad<quint64>(bits); + quint64 v = qFromUnaligned<quint64>(bits); bits += 8; numBits += int(qPopulationCount(v)); } if (bits + 3 <= end) { - quint32 v = qUnalignedLoad<quint32>(bits); + quint32 v = qFromUnaligned<quint32>(bits); bits += 4; numBits += int(qPopulationCount(v)); } if (bits + 1 < end) { - quint16 v = qUnalignedLoad<quint16>(bits); + quint16 v = qFromUnaligned<quint16>(bits); bits += 2; numBits += int(qPopulationCount(v)); } diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index 3cb3606013..08f89d2f02 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -95,9 +95,29 @@ static SHA3Final * const sha3Final = Final; available on all platforms (MSVC 2008, for example), we #define them to the Qt equivalents. */ + +#ifdef uint64_t +#undef uint64_t +#endif + #define uint64_t QT_PREPEND_NAMESPACE(quint64) + +#ifdef uint32_t +#undef uint32_t +#endif + #define uint32_t QT_PREPEND_NAMESPACE(quint32) + +#ifdef uint8_t +#undef uint8_t +#endif + #define uint8_t QT_PREPEND_NAMESPACE(quint8) + +#ifdef int_least16_t +#undef int_least16_t +#endif + #define int_least16_t QT_PREPEND_NAMESPACE(qint16) // Header from rfc6234 with 1 modification: diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index e18c220884..6750925853 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -2180,7 +2180,7 @@ static int qt_timezone() // number of seconds west of UTC. // - It also takes DST into account, so we need to adjust it to always // get the Standard Time offset. - return -t.tm_gmtoff + (t.tm_isdst ? SECS_PER_HOUR : 0L); + return -t.tm_gmtoff + (t.tm_isdst ? (long)SECS_PER_HOUR : 0L); #elif defined(Q_OS_INTEGRITY) return 0; #else diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 2f0886edce..593a87e65d 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -58,6 +58,7 @@ #include <qbytearray.h> #include <qdatetime.h> #include <qbasicatomic.h> +#include <qendian.h> #include <private/qsimd_p.h> #ifndef QT_BOOTSTRAPPED @@ -112,24 +113,24 @@ static uint crc32(const Char *ptr, size_t len, uint h) p += 8; for ( ; p <= e; p += 8) - h2 = _mm_crc32_u64(h2, qUnalignedLoad<qlonglong>(p - 8)); + h2 = _mm_crc32_u64(h2, qFromUnaligned<qlonglong>(p - 8)); h = h2; p -= 8; len = e - p; if (len & 4) { - h = _mm_crc32_u32(h, qUnalignedLoad<uint>(p)); + h = _mm_crc32_u32(h, qFromUnaligned<uint>(p)); p += 4; } # else p += 4; for ( ; p <= e; p += 4) - h = _mm_crc32_u32(h, qUnalignedLoad<uint>(p - 4)); + h = _mm_crc32_u32(h, qFromUnaligned<uint>(p - 4)); p -= 4; len = e - p; # endif if (len & 2) { - h = _mm_crc32_u16(h, qUnalignedLoad<ushort>(p)); + h = _mm_crc32_u16(h, qFromUnaligned<ushort>(p)); p += 2; } if (sizeof(Char) == 1 && len & 1) diff --git a/src/corelib/tools/qmessageauthenticationcode.cpp b/src/corelib/tools/qmessageauthenticationcode.cpp index 9b91474283..9a84191452 100644 --- a/src/corelib/tools/qmessageauthenticationcode.cpp +++ b/src/corelib/tools/qmessageauthenticationcode.cpp @@ -46,9 +46,29 @@ available on all platforms (MSVC 2008, for example), we #define them to the Qt equivalents. */ + +#ifdef uint64_t +#undef uint64_t +#endif + #define uint64_t QT_PREPEND_NAMESPACE(quint64) + +#ifdef uint32_t +#undef uint32_t +#endif + #define uint32_t QT_PREPEND_NAMESPACE(quint32) + +#ifdef uint8_t +#undef uint8_t +#endif + #define uint8_t QT_PREPEND_NAMESPACE(quint8) + +#ifdef int_least16_t +#undef int_least16_t +#endif + #define int_least16_t QT_PREPEND_NAMESPACE(qint16) // Header from rfc6234 with 1 modification: diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index 3a5f0f18a1..c869c4e089 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -751,26 +751,4 @@ void qDumpCPUFeatures() puts(""); } -/*! - \internal - \fn T qUnalignedLoad(const void *ptr) - \since 5.6.1 - - Loads a \c{T} from address \a ptr, which may be misaligned. - - Use of this function avoid the undefined behavior that the C++ standard - otherwise attributes to unaligned loads. -*/ - -/*! - \internal - \fn void qUnalignedStore(void *ptr, T t) - \since 5.6.1 - - Stores \a t to address \a ptr, which may be misaligned. - - Use of this function avoid the undefined behavior that the C++ standard - otherwise attributes to unaligned stores. -*/ - QT_END_NAMESPACE diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 9f968978dc..940fd5817b 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -584,7 +584,7 @@ static int ucstrncmp(const QChar *a, const uchar *c, int l) // we'll read uc[offset..offset+7] (16 bytes) and c[offset..offset+7] (8 bytes) if (uc + offset + 7 < e) { // same, but we're using an 8-byte load - __m128i chunk = _mm_cvtsi64_si128(qUnalignedLoad<long long>(c + offset)); + __m128i chunk = _mm_cvtsi64_si128(qFromUnaligned<long long>(c + offset)); __m128i secondHalf = _mm_unpacklo_epi8(chunk, nullmask); __m128i ucdata = _mm_loadu_si128((const __m128i*)(uc + offset)); diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 8040365581..c2630c8593 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -47,6 +47,8 @@ #include <qdebug.h> +#include "qlocale_tools_p.h" + #include <algorithm> QT_BEGIN_NAMESPACE @@ -376,39 +378,126 @@ static QDate calculatePosixDate(const QByteArray &dateRule, int year) } } -static QTime parsePosixTime(const QByteArray &timeRule) +// returns the time in seconds, INT_MIN if we failed to parse +static int parsePosixTime(const char *begin, const char *end) { - // Format "HH:mm:ss", put check parts count just in case - QList<QByteArray> parts = timeRule.split(':'); - int count = parts.count(); - if (count == 3) - return QTime(parts.at(0).toInt(), parts.at(1).toInt(), parts.at(2).toInt()); - else if (count == 2) - return QTime(parts.at(0).toInt(), parts.at(1).toInt(), 0); - else if (count == 1) - return QTime(parts.at(0).toInt(), 0, 0); - return QTime(2, 0, 0); + // Format "hh[:mm[:ss]]" + int hour, min = 0, sec = 0; + + // Note that the calls to qstrtoll do *not* check the end pointer, which + // means they proceed until they find a non-digit. We check that we're + // still in range at the end, but we may have read from past end. It's the + // caller's responsibility to ensure that begin is part of a + // null-terminated string. + + bool ok = false; + hour = qstrtoll(begin, &begin, 10, &ok); + if (!ok || hour < 0) + return INT_MIN; + if (begin < end && *begin == ':') { + // minutes + ++begin; + min = qstrtoll(begin, &begin, 10, &ok); + if (!ok || min < 0) + return INT_MIN; + + if (begin < end && *begin == ':') { + // seconds + ++begin; + sec = qstrtoll(begin, &begin, 10, &ok); + if (!ok || sec < 0) + return INT_MIN; + } + } + + // we must have consumed everything + if (begin != end) + return INT_MIN; + + return (hour * 60 + min) * 60 + sec; } -static int parsePosixOffset(const QByteArray &timeRule) +static QTime parsePosixTransitionTime(const QByteArray &timeRule) +{ + // Format "hh[:mm[:ss]]" + int value = parsePosixTime(timeRule.constBegin(), timeRule.constEnd()); + if (value == INT_MIN) { + // if we failed to parse, return 02:00 + return QTime(2, 0, 0); + } + return QTime::fromMSecsSinceStartOfDay(value * 1000); +} + +static int parsePosixOffset(const char *begin, const char *end) { // Format "[+|-]hh[:mm[:ss]]" - QList<QByteArray> parts = timeRule.split(':'); - int count = parts.count(); - if (count == 3) { - int hour = parts.at(0).toInt(); - int sign = hour >= 0 ? -1 : 1; - return sign * ((qAbs(hour) * 60 * 60) + (parts.at(1).toInt() * 60) + parts.at(2).toInt()); - } else if (count == 2) { - int hour = parts.at(0).toInt(); - int sign = hour >= 0 ? -1 : 1; - return sign * ((qAbs(hour) * 60 * 60) + (parts.at(1).toInt() * 60)); - } else if (count == 1) { - int hour = parts.at(0).toInt(); - int sign = hour >= 0 ? -1 : 1; - return sign * (qAbs(hour) * 60 * 60); - } - return 0; + // note that the sign is inverted because POSIX counts in hours West of GMT + bool negate = true; + if (*begin == '+') { + ++begin; + } else if (*begin == '-') { + negate = false; + ++begin; + } + + int value = parsePosixTime(begin, end); + if (value == INT_MIN) + return value; + return negate ? -value : value; +} + +static inline bool asciiIsLetter(char ch) +{ + ch |= 0x20; // lowercases if it is a letter, otherwise just corrupts ch + return ch >= 'a' && ch <= 'z'; +} + +// Returns the zone name, the offset (in seconds) and advances \a begin to +// where the parsing ended. Returns a zone of INT_MIN in case an offset +// couldn't be read. +static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const char *end) +{ + static const char offsetChars[] = "0123456789:"; + QPair<QString, int> result = qMakePair(QString(), INT_MIN); + + const char *nameBegin = pos; + const char *nameEnd; + Q_ASSERT(pos < end); + + if (*pos == '<') { + nameBegin = pos + 1; // skip the '<' + nameEnd = nameBegin; + while (nameEnd < end && *nameEnd != '>') { + // POSIX says only alphanumeric, but we allow anything + ++nameEnd; + } + pos = nameEnd + 1; // skip the '>' + } else { + nameBegin = pos; + nameEnd = nameBegin; + while (nameEnd < end && asciiIsLetter(*nameEnd)) + ++nameEnd; + pos = nameEnd; + } + if (nameEnd - nameBegin < 3) + return result; // name must be at least 3 characters long + + // zone offset, form [+-]hh:mm:ss + const char *zoneBegin = pos; + const char *zoneEnd = pos; + if (zoneEnd < end && (zoneEnd[0] == '+' || zoneEnd[0] == '-')) + ++zoneEnd; + while (zoneEnd < end) { + if (strchr(offsetChars, char(*zoneEnd)) == NULL) + break; + ++zoneEnd; + } + + result.first = QString::fromUtf8(nameBegin, nameEnd - nameBegin); + if (zoneEnd > zoneBegin) + result.second = parsePosixOffset(zoneBegin, zoneEnd); + pos = zoneEnd; + return result; } static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray &posixRule, @@ -425,58 +514,45 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra // POSIX Format is like "TZ=CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00" // i.e. "std offset dst [offset],start[/time],end[/time]" - // See http://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + // See the section about TZ at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html QList<QByteArray> parts = posixRule.split(','); - QString name = QString::fromUtf8(parts.at(0)); - QString stdName; - QString stdOffsetString; - QString dstName; - QString dstOffsetString; - bool parsedStdName = false; - bool parsedStdOffset = false; - for (int i = 0; i < name.size(); ++i) { - if (name.at(i).isLetter()) { - if (parsedStdName) { - parsedStdOffset = true; - dstName.append(name.at(i)); - } else { - stdName.append(name.at(i)); + QPair<QString, int> stdZone, dstZone; + { + const QByteArray &zoneinfo = parts.at(0); + const char *begin = zoneinfo.constBegin(); + + stdZone = parsePosixZoneNameAndOffset(begin, zoneinfo.constEnd()); + if (stdZone.second == INT_MIN) { + stdZone.second = 0; // reset to UTC if we failed to parse + } else if (begin < zoneinfo.constEnd()) { + dstZone = parsePosixZoneNameAndOffset(begin, zoneinfo.constEnd()); + if (dstZone.second == INT_MIN) { + // if the dst offset isn't provided, it is 1 hour ahead of the standard offset + dstZone.second = stdZone.second + (60 * 60); } - } else { - parsedStdName = true; - if (parsedStdOffset) - dstOffsetString.append(name.at(i)); - else - stdOffsetString.append(name.at(i)); } } - int utcOffset = parsePosixOffset(stdOffsetString.toUtf8()); - // If only the name part then no transitions if (parts.count() == 1) { QTimeZonePrivate::Data data; data.atMSecsSinceEpoch = lastTranMSecs; - data.offsetFromUtc = utcOffset; - data.standardTimeOffset = utcOffset; + data.offsetFromUtc = stdZone.second; + data.standardTimeOffset = stdZone.second; data.daylightTimeOffset = 0; - data.abbreviation = stdName; + data.abbreviation = stdZone.first; result << data; return result; } - // If not populated the total dst offset is 1 hour - int dstOffset = utcOffset + (60 * 60); - if (!dstOffsetString.isEmpty()) - dstOffset = parsePosixOffset(dstOffsetString.toUtf8()); // Get the std to dst transtion details QList<QByteArray> dstParts = parts.at(1).split('/'); QByteArray dstDateRule = dstParts.at(0); QTime dstTime; if (dstParts.count() > 1) - dstTime = parsePosixTime(dstParts.at(1)); + dstTime = parsePosixTransitionTime(dstParts.at(1)); else dstTime = QTime(2, 0, 0); @@ -485,25 +561,25 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra QByteArray stdDateRule = stdParts.at(0); QTime stdTime; if (stdParts.count() > 1) - stdTime = parsePosixTime(stdParts.at(1)); + stdTime = parsePosixTransitionTime(stdParts.at(1)); else stdTime = QTime(2, 0, 0); for (int year = startYear; year <= endYear; ++year) { QTimeZonePrivate::Data dstData; QDateTime dst(calculatePosixDate(dstDateRule, year), dstTime, Qt::UTC); - dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - (utcOffset * 1000); - dstData.offsetFromUtc = dstOffset; - dstData.standardTimeOffset = utcOffset; - dstData.daylightTimeOffset = dstOffset - utcOffset; - dstData.abbreviation = dstName; + dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - (stdZone.second * 1000); + dstData.offsetFromUtc = dstZone.second; + dstData.standardTimeOffset = stdZone.second; + dstData.daylightTimeOffset = dstZone.second - stdZone.second; + dstData.abbreviation = dstZone.first; QTimeZonePrivate::Data stdData; QDateTime std(calculatePosixDate(stdDateRule, year), stdTime, Qt::UTC); - stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstOffset * 1000); - stdData.offsetFromUtc = utcOffset; - stdData.standardTimeOffset = utcOffset; + stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstZone.second * 1000); + stdData.offsetFromUtc = stdZone.second; + stdData.standardTimeOffset = stdZone.second; stdData.daylightTimeOffset = 0; - stdData.abbreviation = stdName; + stdData.abbreviation = stdZone.first; // Part of the high year will overflow if (year == 292278994 && (dstData.atMSecsSinceEpoch < 0 || stdData.atMSecsSinceEpoch < 0)) { if (dstData.atMSecsSinceEpoch > 0) { diff --git a/src/gui/doc/snippets/code/src_gui_kernel_qclipboard.cpp b/src/gui/doc/snippets/code/src_gui_kernel_qclipboard.cpp index d5f610cf28..8581510133 100644 --- a/src/gui/doc/snippets/code/src_gui_kernel_qclipboard.cpp +++ b/src/gui/doc/snippets/code/src_gui_kernel_qclipboard.cpp @@ -49,7 +49,7 @@ ****************************************************************************/ //! [0] -QClipboard *clipboard = QApplication::clipboard(); +QClipboard *clipboard = QGuiApplication::clipboard(); QString originalText = clipboard->text(); ... clipboard->setText(newText); diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index b8290861af..9545abfd21 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -289,6 +289,12 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int format = QImage::Format_Mono; } + if (depth != 32) { + ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits; + if (ncols < 1 || ncols > 256) // sanity check - don't run out of mem if color table is broken + return false; + } + if (bi.biHeight < 0) h = -h; // support images with negative height @@ -296,19 +302,15 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int image = QImage(w, h, format); if (image.isNull()) // could not create image return false; - } - - if (depth != 32) { - ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits; - if (ncols < 1 || ncols > 256) // sanity check - don't run out of mem if color table is broken - return false; - image.setColorCount(ncols); + if (ncols) + image.setColorCount(ncols); // Ensure valid QImage } image.setDotsPerMeterX(bi.biXPelsPerMeter); image.setDotsPerMeterY(bi.biYPelsPerMeter); if (ncols > 0) { // read color table + image.setColorCount(ncols); uchar rgb[4]; int rgb_len = t == BMP_OLD ? 3 : 4; for (int i=0; i<ncols; i++) { diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 82ad9b1738..36595025f5 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -176,6 +176,9 @@ QImageData::~QImageData() data = 0; } +#if defined(_M_ARM) +#pragma optimize("", off) +#endif bool QImageData::checkForAlphaPixels() const { @@ -290,6 +293,9 @@ bool QImageData::checkForAlphaPixels() const return has_alpha_pixels; } +#if defined(_M_ARM) +#pragma optimize("", on) +#endif /*! \class QImage diff --git a/src/gui/kernel/qclipboard.cpp b/src/gui/kernel/qclipboard.cpp index e5e268ace3..21dbdadcf8 100644 --- a/src/gui/kernel/qclipboard.cpp +++ b/src/gui/kernel/qclipboard.cpp @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE Drop}. There is a single QClipboard object in an application, accessible - as QApplication::clipboard(). + as QGuiApplication::clipboard(). Example: \snippet code/src_gui_kernel_qclipboard.cpp 0 @@ -137,7 +137,7 @@ QT_BEGIN_NAMESPACE \endlist - \sa QApplication + \sa QGuiApplication */ /*! @@ -147,7 +147,7 @@ QT_BEGIN_NAMESPACE Do not call this function. - Call QApplication::clipboard() instead to get a pointer to the + Call QGuiApplication::clipboard() instead to get a pointer to the application's global clipboard object. There is only one clipboard in the window system, and creating @@ -165,7 +165,7 @@ QClipboard::QClipboard(QObject *parent) Destroys the clipboard. - You should never delete the clipboard. QApplication will do this + You should never delete the clipboard. QGuiApplication will do this when the application terminates. */ QClipboard::~QClipboard() diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 9cad9e0a1d..2c5e0672b1 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -2186,11 +2186,10 @@ void QPainter::setBrushOrigin(const QPointF &p) destination pixel in such a way that the alpha component of the source defines the translucency of the pixel. - When the paint device is a QImage, the image format must be set to - \l {QImage::Format}{Format_ARGB32_Premultiplied} or - \l {QImage::Format}{Format_ARGB32} for the composition modes to have - any effect. For performance the premultiplied version is the preferred - format. + Several composition modes require an alpha channel in the source or + target images to have an effect. For optimal performance the + image format \l {QImage::Format}{Format_ARGB32_Premultiplied} is + preferred. When a composition mode is set it applies to all painting operator, pens, brushes, gradients and pixmap/image drawing. diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 9b50581bea..22df0eda30 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -2732,8 +2732,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script) } if (req.pointSize < 0) req.pointSize = req.pixelSize*72.0/d->dpi; - if (req.weight == 0) - req.weight = QFont::Normal; + req.weight = QFont::Normal; if (req.stretch == 0) req.stretch = 100; diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 74317e99c3..fe5d544dba 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1833,8 +1833,7 @@ QFontEngine *QFontEngineMulti::loadEngine(int at) request.family = fallbackFamilyAt(at - 1); if (QFontEngine *engine = QFontDatabase::findFont(request, m_script)) { - if (request.weight > QFont::Normal) - engine->fontDef.weight = request.weight; + engine->fontDef.weight = request.weight; if (request.style > QFont::StyleNormal) engine->fontDef.style = request.style; return engine; diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index 7ee02b71c9..28fb9d2769 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1645,8 +1645,14 @@ void QTextEngine::itemize() const if (analysis->bidiLevel % 2) --analysis->bidiLevel; analysis->flags = QScriptAnalysis::LineOrParagraphSeparator; - if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) + if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) { + const int offset = uc - string; + layoutData->string.detach(); + string = reinterpret_cast<const ushort *>(layoutData->string.unicode()); + uc = string + offset; + e = uc + length; *const_cast<ushort*>(uc) = 0x21B5; // visual line separator + } break; case QChar::Tabulation: analysis->flags = QScriptAnalysis::Tab; diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 402bd75a76..2109b15a85 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -364,9 +364,10 @@ void QTextFormatPrivate::recalcFont() const f.setPixelSize(props.at(i).value.toInt()); break; case QTextFormat::FontWeight: { - int weight = props.at(i).value.toInt(); - if (weight == 0) weight = QFont::Normal; - f.setWeight(weight); + const QVariant weightValue = props.at(i).value; + int weight = weightValue.toInt(); + if (weight >= 0 && weightValue.isValid()) + f.setWeight(weight); break; } case QTextFormat::FontItalic: f.setItalic(props.at(i).value.toBool()); diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index bc627521ff..805affd87c 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -431,9 +431,9 @@ public: { return doubleProperty(FontPointSize); } inline void setFontWeight(int weight) - { if (weight == QFont::Normal) weight = 0; setProperty(FontWeight, weight); } + { setProperty(FontWeight, weight); } inline int fontWeight() const - { int weight = intProperty(FontWeight); if (weight == 0) weight = QFont::Normal; return weight; } + { return hasProperty(FontWeight) ? intProperty(FontWeight) : QFont::Normal; } inline void setFontItalic(bool italic) { setProperty(FontItalic, italic); } inline bool fontItalic() const diff --git a/src/network/access/access.pri b/src/network/access/access.pri index 42c7c80f3b..38e9c25269 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -72,14 +72,4 @@ SOURCES += \ mac: LIBS_PRIVATE += -framework Security -ios { - HEADERS += \ - access/qnetworkreplynsurlconnectionimpl_p.h - - OBJECTIVE_SOURCES += \ - access/qnetworkreplynsurlconnectionimpl.mm - - LIBS_PRIVATE += -framework Foundation -} - include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 56716cbe01..2f7be01078 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -331,7 +331,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection() priv->phase = QAuthenticatorPrivate::Start; QString connectHost = connection->d_func()->hostName; - qint16 connectPort = connection->d_func()->port; + quint16 connectPort = connection->d_func()->port; #ifndef QT_NO_NETWORKPROXY // HTTPS always use transparent proxy. diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 927e103abc..587ab27a0f 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -56,10 +56,6 @@ #include "qnetworkreplydataimpl_p.h" #include "qnetworkreplyfileimpl_p.h" -#if defined(Q_OS_IOS) && defined(QT_NO_SSL) -#include "qnetworkreplynsurlconnectionimpl_p.h" -#endif - #include "QtCore/qbuffer.h" #include "QtCore/qurl.h" #include "QtCore/qvector.h" @@ -1207,12 +1203,6 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera } } -// Use NSURLConnection for https on iOS when OpenSSL is disabled. -#if defined(Q_OS_IOS) && defined(QT_NO_SSL) - if (scheme == QLatin1String("https")) - return new QNetworkReplyNSURLConnectionImpl(this, request, op, outgoingData); -#endif - #ifndef QT_NO_HTTP // Since Qt 5 we use the new QNetworkReplyHttpImpl if (scheme == QLatin1String("http") || scheme == QLatin1String("preconnect-http") diff --git a/src/network/access/qnetworkreplynsurlconnectionimpl.mm b/src/network/access/qnetworkreplynsurlconnectionimpl.mm deleted file mode 100644 index 58a3ba1448..0000000000 --- a/src/network/access/qnetworkreplynsurlconnectionimpl.mm +++ /dev/null @@ -1,458 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qnetworkreplynsurlconnectionimpl_p.h" - -#include "QtCore/qdatetime.h" -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <Foundation/Foundation.h> - -QT_BEGIN_NAMESPACE - -// Network reply implementation using NSUrlConnection. -// -// Class/object structure: -// -// QNetworkReplyNSURLConnectionImpl -// |- QNetworkReplyNSURLConnectionImplPrivate -// |- (bytes read) -// |- (QIODevice and CFStream for async POST data transfer) -// |- NSURLConnection -// |- QtNSURLConnectionDelegate <NSURLConnectionDataDelegate> -// |- NSURLResponse/NSHTTPURLResponse -// |- (response data) -// -// The main entry point is the QNetworkReplyNSURLConnectionImpl constructor, which -// receives a network request from QNetworkAccessManager. The constructor -// creates a NSURLRequest and initiates a NSURLConnection with a QtNSURLConnectionDelegate. -// The delegate callbacks are then called asynchronously as the request completes. -// - -@class QtNSURLConnectionDelegate; -class QNetworkReplyNSURLConnectionImplPrivate: public QNetworkReplyPrivate -{ -public: - QNetworkReplyNSURLConnectionImplPrivate(); - virtual ~QNetworkReplyNSURLConnectionImplPrivate(); - - Q_DECLARE_PUBLIC(QNetworkReplyNSURLConnectionImpl) - NSURLConnection * urlConnection; - QtNSURLConnectionDelegate *urlConnectionDelegate; - qint64 bytesRead; - - // Sequental outgiong data streaming - QIODevice *outgoingData; - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - CFIndex transferBufferSize; - - // Forwarding functions to the public class. - void setFinished(); - void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value); - void setRawHeader(const QByteArray &headerName, const QByteArray &value); - void setError(QNetworkReply::NetworkError errorCode, const QString &errorString); - void setAttribute(QNetworkRequest::Attribute code, const QVariant &value); -}; - -@interface QtNSURLConnectionDelegate : NSObject -{ - NSURLResponse *response; - NSMutableData *responseData; - QNetworkReplyNSURLConnectionImplPrivate * replyprivate; -} - -- (id)initWithQNetworkReplyNSURLConnectionImplPrivate:(QNetworkReplyNSURLConnectionImplPrivate *)a_replyPrivate ; -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_3_0) -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; -#endif -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError*)error; -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response; -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data; -- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten - totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite; -- (NSCachedURLResponse*)connection:(NSURLConnection*)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse; -- (NSURLRequest*)connection:(NSURLConnection*)connection willSendRequest:(NSURLRequest*)request redirectResponse:(NSURLResponse*)redirectResponse; -- (void)connectionDidFinishLoading:(NSURLConnection*)connection; -- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection*)connection; - -@end - -QNetworkReplyNSURLConnectionImplPrivate::QNetworkReplyNSURLConnectionImplPrivate() - : QNetworkReplyPrivate() - , urlConnection(0) - , urlConnectionDelegate(0) - , bytesRead(0) - , readStream(0) - , writeStream(0) - , transferBufferSize(4096) -{ -} - -QNetworkReplyNSURLConnectionImplPrivate::~QNetworkReplyNSURLConnectionImplPrivate() -{ - [urlConnection cancel]; - [urlConnection release]; - [urlConnectionDelegate release]; - if (readStream) - CFRelease(readStream); - if (writeStream) - CFRelease(writeStream); -} - -void QNetworkReplyNSURLConnectionImplPrivate::setFinished() -{ - q_func()->setFinished(true); - QMetaObject::invokeMethod(q_func(), "finished", Qt::QueuedConnection); -} - -void QNetworkReplyNSURLConnectionImplPrivate::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value) -{ - q_func()->setHeader(header, value); -} - -void QNetworkReplyNSURLConnectionImplPrivate::setRawHeader(const QByteArray &headerName, const QByteArray &value) -{ - q_func()->setRawHeader(headerName, value); -} - -void QNetworkReplyNSURLConnectionImplPrivate::setError(QNetworkReply::NetworkError errorCode, const QString &errorString) -{ - q_func()->setError(errorCode, errorString); -} - -void QNetworkReplyNSURLConnectionImplPrivate::setAttribute(QNetworkRequest::Attribute code, const QVariant &value) -{ - q_func()->setAttribute(code, value); -} - -void QNetworkReplyNSURLConnectionImpl::readyReadOutgoingData() -{ - Q_D(QNetworkReplyNSURLConnectionImpl); - int bytesRead = 0; - do { - char data[d->transferBufferSize]; - bytesRead = d->outgoingData->read(data, d->transferBufferSize); - if (bytesRead <= 0) - break; - CFIndex bytesWritten = CFWriteStreamWrite(d->writeStream, reinterpret_cast<unsigned char *>(data), bytesRead); - if (bytesWritten != bytesRead) { - CFErrorRef err = CFWriteStreamCopyError(d->writeStream); - qWarning() << "QNetworkReplyNSURLConnectionImpl: CFWriteStreamWrite error" - << (err ? QString::number(CFErrorGetCode(err)) : QStringLiteral("")); - } - } while (bytesRead > 0); - - if (d->outgoingData->atEnd()) - CFWriteStreamClose(d->writeStream); -} - -@interface QtNSURLConnectionDelegate () - -@property (nonatomic, retain) NSURLResponse* response; -@property (nonatomic, retain) NSMutableData* responseData; - -@end - -@implementation QtNSURLConnectionDelegate - -@synthesize response; -@synthesize responseData; - -- (id)initWithQNetworkReplyNSURLConnectionImplPrivate:(QNetworkReplyNSURLConnectionImplPrivate *)a_replyPrivate -{ - if (self = [super init]) - replyprivate = a_replyPrivate; - return self; -} - -- (void)dealloc -{ - [response release]; - [responseData release]; - [super dealloc]; -} - -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_3_0) -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge -{ - Q_UNUSED(connection) - Q_UNUSED(challenge) - - if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; - SecTrustResultType resultType; - SecTrustEvaluate(serverTrust, &resultType); - if (resultType == kSecTrustResultUnspecified) { - // All good - [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; - } else if (resultType == kSecTrustResultRecoverableTrustFailure) { - // Certificate verification error, ask user - // ### TODO actually ask user - // (test site: https://testssl-expire.disig.sk/index.en.html) - qWarning() << "QNetworkReplyNSURLConnection: Certificate verification error handlig is" - << "not implemented. Connection will time out."; - } else { - // other error, which the default handler will handle - [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; - } - } - - [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; -} -#endif - -- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error -{ - Q_UNUSED(connection) - - QNetworkReply::NetworkError qtError = QNetworkReply::UnknownNetworkError; - if ([[error domain] isEqualToString:NSURLErrorDomain]) { - switch ([error code]) { - case NSURLErrorTimedOut: qtError = QNetworkReply::TimeoutError; break; - case NSURLErrorUnsupportedURL: qtError = QNetworkReply::ProtocolUnknownError; break; - case NSURLErrorCannotFindHost: qtError = QNetworkReply::HostNotFoundError; break; - case NSURLErrorCannotConnectToHost: qtError = QNetworkReply::ConnectionRefusedError; break; - case NSURLErrorNetworkConnectionLost: qtError = QNetworkReply::NetworkSessionFailedError; break; - case NSURLErrorDNSLookupFailed: qtError = QNetworkReply::HostNotFoundError; break; - case NSURLErrorNotConnectedToInternet: qtError = QNetworkReply::NetworkSessionFailedError; break; - case NSURLErrorUserAuthenticationRequired: qtError = QNetworkReply::AuthenticationRequiredError; break; - default: break; - } - } - - replyprivate->setError(qtError, QString::fromNSString([error localizedDescription])); - replyprivate->setFinished(); -} - -- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)aResponse -{ - Q_UNUSED(connection) - self.response = aResponse; - self.responseData = [NSMutableData data]; - - // copy headers - if ([aResponse isKindOfClass:[NSHTTPURLResponse class]]) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)aResponse; - NSDictionary *headers = [httpResponse allHeaderFields]; - for (NSString *key in headers) { - NSString *value = [headers objectForKey:key]; - replyprivate->setRawHeader(QString::fromNSString(key).toUtf8(), QString::fromNSString(value).toUtf8()); - } - - int code = [httpResponse statusCode]; - replyprivate->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, code); - } else { - if ([aResponse expectedContentLength] != NSURLResponseUnknownLength) - replyprivate->setHeader(QNetworkRequest::ContentLengthHeader, [aResponse expectedContentLength]); - } - - QMetaObject::invokeMethod(replyprivate->q_func(), "metaDataChanged", Qt::QueuedConnection); -} - -- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data -{ - Q_UNUSED(connection) - [responseData appendData:data]; - - if ([response expectedContentLength] != NSURLResponseUnknownLength) { - QMetaObject::invokeMethod(replyprivate->q_func(), "downloadProgress", Qt::QueuedConnection, - Q_ARG(qint64, qint64([responseData length] + replyprivate->bytesRead)), - Q_ARG(qint64, qint64([response expectedContentLength]))); - } - - QMetaObject::invokeMethod(replyprivate->q_func(), "readyRead", Qt::QueuedConnection); -} - -- (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten - totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite -{ - Q_UNUSED(connection) - Q_UNUSED(bytesWritten) - QMetaObject::invokeMethod(replyprivate->q_func(), "uploadProgress", Qt::QueuedConnection, - Q_ARG(qint64, qint64(totalBytesWritten)), - Q_ARG(qint64, qint64(totalBytesExpectedToWrite))); -} - -- (NSCachedURLResponse*)connection:(NSURLConnection*)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse -{ - Q_UNUSED(connection) - return cachedResponse; -} - -- (NSURLRequest*)connection:(NSURLConnection*)connection willSendRequest:(NSURLRequest*)request redirectResponse:(NSURLResponse*)redirectResponse -{ - Q_UNUSED(connection) - Q_UNUSED(redirectResponse) - return request; -} - -- (void)connectionDidFinishLoading:(NSURLConnection*)connection -{ - Q_UNUSED(connection) - replyprivate->setFinished(); -} - -- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection*)connection -{ - Q_UNUSED(connection) - return YES; -} - -@end - -QNetworkReplyNSURLConnectionImpl::~QNetworkReplyNSURLConnectionImpl() -{ -} - -QNetworkReplyNSURLConnectionImpl::QNetworkReplyNSURLConnectionImpl(QObject *parent, - const QNetworkRequest &request, const QNetworkAccessManager::Operation operation, QIODevice* outgoingData) - : QNetworkReply(*new QNetworkReplyNSURLConnectionImplPrivate(), parent) -{ - setRequest(request); - setUrl(request.url()); - setOperation(operation); - QNetworkReply::open(QIODevice::ReadOnly); - - QNetworkReplyNSURLConnectionImplPrivate *d = (QNetworkReplyNSURLConnectionImplPrivate*) d_func(); - - QUrl url = request.url(); - if (url.host() == QLatin1String("localhost")) - url.setHost(QString()); - - if (url.path().isEmpty()) - url.setPath(QLatin1String("/")); - setUrl(url); - - // Create a NSMutableURLRequest from QNetworkRequest - NSMutableURLRequest *nsRequest = [NSMutableURLRequest requestWithURL:request.url().toNSURL() - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0]; - // copy headers - const auto headers = request.rawHeaderList(); - for (const QByteArray &header : headers) { - QByteArray headerValue = request.rawHeader(header); - [nsRequest addValue:QString::fromUtf8(headerValue).toNSString() - forHTTPHeaderField:QString::fromUtf8(header).toNSString()]; - } - - if (operation == QNetworkAccessManager::GetOperation) - [nsRequest setHTTPMethod:@"GET"]; - else if (operation == QNetworkAccessManager::PostOperation) - [nsRequest setHTTPMethod:@"POST"]; - else if (operation == QNetworkAccessManager::PutOperation) - [nsRequest setHTTPMethod:@"PUT"]; - else if (operation == QNetworkAccessManager::DeleteOperation) - [nsRequest setHTTPMethod:@"DELETE"]; - else - qWarning() << "QNetworkReplyNSURLConnection: Unsupported netork operation" << operation; - - if (outgoingData) { - d->outgoingData = outgoingData; - if (outgoingData->isSequential()) { - // set up streaming from outgoingData iodevice to request - CFStreamCreateBoundPair(kCFAllocatorDefault, &d->readStream, &d->writeStream, d->transferBufferSize); - CFWriteStreamOpen(d->writeStream); - [nsRequest setHTTPBodyStream:reinterpret_cast<NSInputStream *>(d->readStream)]; - connect(outgoingData, SIGNAL(readyRead()), this, SLOT(readyReadOutgoingData())); - readyReadOutgoingData(); - } else { - // move all data at once - QByteArray data = outgoingData->readAll(); - [nsRequest setHTTPBody:[NSData dataWithBytes:data.constData() length:data.length()]]; - } - } - - // Create connection - d->urlConnectionDelegate = [[QtNSURLConnectionDelegate alloc] initWithQNetworkReplyNSURLConnectionImplPrivate:d]; - d->urlConnection = [[NSURLConnection alloc] initWithRequest:nsRequest delegate:d->urlConnectionDelegate]; - if (!d->urlConnection) { - // ### what type of error is an initWithRequest fail? - setError(QNetworkReply::ProtocolUnknownError, QStringLiteral("QNetworkReplyNSURLConnection internal error")); - } -} - -void QNetworkReplyNSURLConnectionImpl::close() -{ - // No-op? Network ops should continue (especially POSTs) - QNetworkReply::close(); -} - -void QNetworkReplyNSURLConnectionImpl::abort() -{ - Q_D(QNetworkReplyNSURLConnectionImpl); - [d->urlConnection cancel]; - QNetworkReply::close(); -} - -qint64 QNetworkReplyNSURLConnectionImpl::bytesAvailable() const -{ - Q_D(const QNetworkReplyNSURLConnectionImpl); - qint64 available = QNetworkReply::bytesAvailable() + - [[d->urlConnectionDelegate responseData] length]; - return available; -} - -bool QNetworkReplyNSURLConnectionImpl::isSequential() const -{ - return true; -} - -qint64 QNetworkReplyNSURLConnectionImpl::size() const -{ - Q_D(const QNetworkReplyNSURLConnectionImpl); - return [[d->urlConnectionDelegate responseData] length] + d->bytesRead; -} - -/*! - \internal -*/ -qint64 QNetworkReplyNSURLConnectionImpl::readData(char *data, qint64 maxlen) -{ - Q_D(QNetworkReplyNSURLConnectionImpl); - qint64 dataSize = [[d->urlConnectionDelegate responseData] length]; - qint64 canRead = qMin(maxlen, dataSize); - const char *sourceBase = static_cast<const char *>([[d->urlConnectionDelegate responseData] bytes]); - memcpy(data, sourceBase, canRead); - [[d->urlConnectionDelegate responseData] replaceBytesInRange:NSMakeRange(0, canRead) withBytes:NULL length:0]; - d->bytesRead += canRead; - return canRead; -} - -QT_END_NAMESPACE diff --git a/src/network/access/qnetworkreplynsurlconnectionimpl_p.h b/src/network/access/qnetworkreplynsurlconnectionimpl_p.h deleted file mode 100644 index c948e66629..0000000000 --- a/src/network/access/qnetworkreplynsurlconnectionimpl_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QNETWORKREPLYNSURLCONNECTIONIMPL_H -#define QNETWORKREPLYNSURLCONNECTIONIMPL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of the Network Access API. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qnetworkreply.h" -#include "qnetworkreply_p.h" -#include "qnetworkaccessmanager.h" -#include <QFile> -#include <private/qabstractfileengine_p.h> - -QT_BEGIN_NAMESPACE - - -class QNetworkReplyNSURLConnectionImplPrivate; -class QNetworkReplyNSURLConnectionImpl: public QNetworkReply -{ - Q_OBJECT -public: - QNetworkReplyNSURLConnectionImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op, QIODevice* outgoingData); - virtual ~QNetworkReplyNSURLConnectionImpl(); - virtual void abort(); - - // reimplemented from QNetworkReply - virtual void close(); - virtual qint64 bytesAvailable() const; - virtual bool isSequential () const; - qint64 size() const; - - virtual qint64 readData(char *data, qint64 maxlen); -public Q_SLOTS: - void readyReadOutgoingData(); - - Q_DECLARE_PRIVATE(QNetworkReplyNSURLConnectionImpl) -}; - -QT_END_NAMESPACE - -#endif // QNetworkReplyNSURLConnectionImpl_H diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp index 35981a2f2c..41038dc8da 100644 --- a/src/network/kernel/qdnslookup_unix.cpp +++ b/src/network/kernel/qdnslookup_unix.cpp @@ -47,7 +47,9 @@ #include <sys/types.h> #include <netinet/in.h> #include <arpa/nameser.h> -#include <arpa/nameser_compat.h> +#if !defined(Q_OS_OPENBSD) +# include <arpa/nameser_compat.h> +#endif #include <resolv.h> #if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__) @@ -58,6 +60,9 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_LIBRARY +#if defined(Q_OS_OPENBSD) +typedef struct __res_state* res_state; +#endif typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int); static dn_expand_proto local_dn_expand = 0; typedef void (*res_nclose_proto)(res_state); diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index b3e456be69..7ef9d7f26b 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -560,7 +560,9 @@ int QNativeSocketEnginePrivate::nativeAccept() setError(QAbstractSocket::SocketResourceError, NotSocketErrorString); break; case EPROTONOSUPPORT: +#if !defined(Q_OS_OPENBSD) case EPROTO: +#endif case EAFNOSUPPORT: case EINVAL: setError(QAbstractSocket::UnsupportedSocketOperationError, ProtocolUnsupportedErrorString); @@ -900,7 +902,9 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVIF && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sockaddr_dl))) { sockaddr_dl *sdl = reinterpret_cast<sockaddr_dl *>(CMSG_DATA(cmsgptr)); - +# if defined(Q_OS_OPENBSD) +# define LLINDEX(s) ((s)->sdl_index) +# endif header->ifindex = LLINDEX(sdl); } # endif diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index 181c49f200..dd43615f98 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -337,7 +337,7 @@ bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port) d->socketState = QAbstractSocket::ConnectingState; hr = QEventDispatcherWinRT::runOnXamlThread([d]() { return d->connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>( - d, &QNativeSocketEnginePrivate::handleConnectToHost).Get()); + d, &QNativeSocketEnginePrivate::handleConnectOpFinished).Get()); }); Q_ASSERT_SUCCEEDED(hr); @@ -477,7 +477,10 @@ void QNativeSocketEngine::close() hr = socket3->CancelIOAsync(&action); Q_ASSERT_SUCCEEDED(hr); hr = QWinRTFunctions::await(action); - Q_ASSERT_SUCCEEDED(hr); + // If there is no pending IO (no read established before) the function will fail with + // "function was called at an unexpected time" which is fine. + if (hr != E_ILLEGAL_METHOD_CALL) + Q_ASSERT_SUCCEEDED(hr); return S_OK; }); Q_ASSERT_SUCCEEDED(hr); @@ -759,7 +762,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) if (d->socketState == QAbstractSocket::ConnectingState) { HRESULT hr = QWinRTFunctions::await(d->connectOp, QWinRTFunctions::ProcessMainThreadEvents); if (SUCCEEDED(hr)) { - d->handleConnectionEstablished(d->connectOp.Get()); + d->handleConnectOpFinished(d->connectOp.Get(), Completed); return true; } } @@ -1214,38 +1217,32 @@ HRESULT QNativeSocketEnginePrivate::handleClientConnection(IStreamSocketListener return S_OK; } -HRESULT QNativeSocketEnginePrivate::handleConnectToHost(IAsyncAction *action, AsyncStatus) -{ - handleConnectionEstablished(action); - return S_OK; -} - -void QNativeSocketEnginePrivate::handleConnectionEstablished(IAsyncAction *action) +HRESULT QNativeSocketEnginePrivate::handleConnectOpFinished(IAsyncAction *action, AsyncStatus) { Q_Q(QNativeSocketEngine); if (wasDeleted || !connectOp) // Protect against a late callback - return; + return S_OK; HRESULT hr = action->GetResults(); switch (hr) { case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString); socketState = QAbstractSocket::UnconnectedState; - break; + return S_OK; case 0x80072751: // A socket operation was attempted to an unreachable host. setError(QAbstractSocket::HostNotFoundError, HostUnreachableErrorString); socketState = QAbstractSocket::UnconnectedState; - break; + return S_OK; case 0x8007274d: // No connection could be made because the target machine actively refused it. setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); socketState = QAbstractSocket::UnconnectedState; - break; + return S_OK; default: if (FAILED(hr)) { setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString); socketState = QAbstractSocket::UnconnectedState; + return S_OK; } - break; } // The callback might be triggered several times if we do not cancel/reset it here @@ -1267,13 +1264,14 @@ void QNativeSocketEnginePrivate::handleConnectionEstablished(IAsyncAction *actio emit q->connectionReady(); if (socketType != QAbstractSocket::TcpSocket) - return; + return S_OK; // Delay the reader so that the SSL socket can upgrade if (sslSocket) QObject::connect(qobject_cast<QSslSocket *>(sslSocket), &QSslSocket::encrypted, q, &QNativeSocketEngine::establishRead); else q->establishRead(); + return S_OK; } HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status) diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h index 325e4965e6..5b76c2d223 100644 --- a/src/network/socket/qnativesocketengine_winrt_p.h +++ b/src/network/socket/qnativesocketengine_winrt_p.h @@ -220,8 +220,7 @@ private: ABI::Windows::Networking::Sockets::IDatagramSocketMessageReceivedEventArgs *args); HRESULT handleClientConnection(ABI::Windows::Networking::Sockets::IStreamSocketListener *tcpListener, ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs *args); - HRESULT handleConnectToHost(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus); - void handleConnectionEstablished(ABI::Windows::Foundation::IAsyncAction *action); + HRESULT handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus); HRESULT handleReadyRead(ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer *, UINT32> *asyncInfo, ABI::Windows::Foundation::AsyncStatus); }; diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index eb9264ba20..a57a1dca2c 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1122,7 +1122,9 @@ bool QSocks5SocketEngine::connectInternal() } if (d->socketState != QAbstractSocket::ConnectingState) { - if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized) { + if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized + // We may have new auth credentials since an earlier failure: + || d->socks5State == QSocks5SocketEnginePrivate::AuthenticatingError) { setState(QAbstractSocket::ConnectingState); //limit buffer in internal socket, data is buffered in the external socket under application control d->data->controlSocket->setReadBufferSize(65536); diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index a5ee9bf0c1..472db3aa81 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -2632,7 +2632,8 @@ QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories() << "/var/ssl/certs/" // AIX << "/usr/local/ssl/certs/" // Solaris << "/etc/openssl/certs/" // BlackBerry - << "/opt/openssl/certs/"; // HP-UX + << "/opt/openssl/certs/" // HP-UX + << "/etc/ssl/"; // OpenBSD } /*! diff --git a/src/platformheaders/doc/src/qtplatformheaders.qdoc b/src/platformheaders/doc/src/qtplatformheaders.qdoc index 745edf7983..6eb7b27cce 100644 --- a/src/platformheaders/doc/src/qtplatformheaders.qdoc +++ b/src/platformheaders/doc/src/qtplatformheaders.qdoc @@ -80,6 +80,19 @@ \sa QXcbWindowFunctions QWindowsWindowFunctions + \section1 Getting Started + + To include the definitions of the module's functions and classes, use the following directives: + \code + #include <QtPlatformHeaders/QWindowsWindowFunctions> + #include <QtPlatformHeaders/QXcbWindowFunctions> + \endcode + + As the module is header-only, no further modifications to the .pro files are required to use it. + + \note The module name (\c QtPlatformHeaders) must appear in the \c #include directive. + \note It is not necessary to enclose the code in \c #ifdef directives depending on platform. + \section1 API Reference \list \li \l{Qt Platform Headers C++ Classes}{C++ Classes} diff --git a/src/platformsupport/devicediscovery/devicediscovery.pri b/src/platformsupport/devicediscovery/devicediscovery.pri index 1ac25da901..9829ae88ba 100644 --- a/src/platformsupport/devicediscovery/devicediscovery.pri +++ b/src/platformsupport/devicediscovery/devicediscovery.pri @@ -1,18 +1,14 @@ HEADERS += $$PWD/qdevicediscovery_p.h -linux { - contains(QT_CONFIG, libudev) { - SOURCES += $$PWD/qdevicediscovery_udev.cpp - HEADERS += $$PWD/qdevicediscovery_udev_p.h - INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV - LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV - } else: contains(QT_CONFIG, evdev) { - SOURCES += $$PWD/qdevicediscovery_static.cpp - HEADERS += $$PWD/qdevicediscovery_static_p.h - } else { - SOURCES += $$PWD/qdevicediscovery_dummy.cpp - HEADERS += $$PWD/qdevicediscovery_dummy_p.h - } +contains(QT_CONFIG, libudev) { + SOURCES += $$PWD/qdevicediscovery_udev.cpp + HEADERS += $$PWD/qdevicediscovery_udev_p.h + INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV + LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV +} else: contains(QT_CONFIG, evdev) { + SOURCES += $$PWD/qdevicediscovery_static.cpp + HEADERS += $$PWD/qdevicediscovery_static_p.h } else { SOURCES += $$PWD/qdevicediscovery_dummy.cpp + HEADERS += $$PWD/qdevicediscovery_dummy_p.h } diff --git a/src/platformsupport/eglconvenience/qeglconvenience.cpp b/src/platformsupport/eglconvenience/qeglconvenience.cpp index f8efd105bc..da41cfeabf 100644 --- a/src/platformsupport/eglconvenience/qeglconvenience.cpp +++ b/src/platformsupport/eglconvenience/qeglconvenience.cpp @@ -587,6 +587,10 @@ int q_screenDepthFromFb(int framebufferDevice) qreal q_refreshRateFromFb(int framebufferDevice) { +#ifndef Q_OS_LINUX + Q_UNUSED(framebufferDevice) +#endif + static qreal rate = 0; #ifdef Q_OS_LINUX diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp index 36de1e0c69..c7f6dd2740 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp @@ -60,7 +60,7 @@ Q_LOGGING_CATEGORY(qLcEvdevKeyMap, "qt.qpa.input.keymap") #include "qevdevkeyboard_defaultmap_p.h" QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, int fd, bool disableZap, bool enableCompose, const QString &keymapFile) - : m_device(device), m_fd(fd), + : m_device(device), m_fd(fd), m_notify(Q_NULLPTR), m_modifiers(0), m_composing(0), m_dead_unicode(0xffff), m_no_zap(disableZap), m_do_compose(enableCompose), m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0) @@ -75,9 +75,8 @@ QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, int fd, bool unloadKeymap(); // socket notifier for events on the keyboard device - QSocketNotifier *notifier; - notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)), this, SLOT(readKeycode())); + m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); + connect(m_notify, SIGNAL(activated(int)), this, SLOT(readKeycode())); } QEvdevKeyboardHandler::~QEvdevKeyboardHandler() @@ -162,6 +161,14 @@ void QEvdevKeyboardHandler::readKeycode() } else if (result < 0) { if (errno != EINTR && errno != EAGAIN) { qErrnoWarning(errno, "evdevkeyboard: Could not read from input device"); + // If the device got disconnected, stop reading, otherwise we get flooded + // by the above error over and over again. + if (errno == ENODEV) { + delete m_notify; + m_notify = Q_NULLPTR; + qt_safe_close(m_fd); + m_fd = -1; + } return; } } else { diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h index 89fa879115..b94323fcbb 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h @@ -57,6 +57,8 @@ QT_BEGIN_NAMESPACE +class QSocketNotifier; + namespace QEvdevKeyboardMap { const quint32 FileMagic = 0x514d4150; // 'QMAP' @@ -186,6 +188,7 @@ private: QString m_device; int m_fd; + QSocketNotifier *m_notify; // keymap handling quint8 m_modifiers; diff --git a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp index 382b9b1514..d5ea04bee8 100644 --- a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp +++ b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp @@ -111,9 +111,8 @@ QEvdevMouseHandler::QEvdevMouseHandler(const QString &device, int fd, bool abs, m_abs = getHardwareMaximum(); // socket notifier for events on the mouse device - QSocketNotifier *notifier; - notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)), this, SLOT(readMouseData())); + m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); + connect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData())); } QEvdevMouseHandler::~QEvdevMouseHandler() @@ -202,6 +201,14 @@ void QEvdevMouseHandler::readMouseData() } else if (result < 0) { if (errno != EINTR && errno != EAGAIN) { qErrnoWarning(errno, "evdevmouse: Could not read from input device"); + // If the device got disconnected, stop reading, otherwise we get flooded + // by the above error over and over again. + if (errno == ENODEV) { + delete m_notify; + m_notify = Q_NULLPTR; + qt_safe_close(m_fd); + m_fd = -1; + } return; } } else { diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index 81c7faa389..60be964b74 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -21,7 +21,11 @@ include(accessibility/accessibility.pri) include(linuxaccessibility/linuxaccessibility.pri) include(clipboard/clipboard.pri) include(platformcompositor/platformcompositor.pri) -contains(QT_CONFIG, dbus) { + +# dbus convenience, but not for darwin: the platform +# plugins for these platforms do not use dbus and we +# don't want to create a false dependency. +!darwin: contains(QT_CONFIG, dbus) { include(dbusmenu/dbusmenu.pri) include(dbustray/dbustray.pri) } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index eaa0170748..91ce91004f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -153,7 +153,7 @@ static bool isMouseEvent(NSEvent *ev) if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO)) { - QNSView *contentView = (QNSView *)pw->contentView(); + QNSView *contentView = pw->m_qtView; [contentView handleFrameStrutMouseEvent: theEvent]; } } @@ -1188,7 +1188,11 @@ NSView *QCocoaWindow::contentView() const void QCocoaWindow::setContentView(NSView *contentView) { // Remove and release the previous content view - [m_contentView removeFromSuperview]; + if (m_nsWindow) + [m_nsWindow setContentView:nil]; + else + [m_contentView removeFromSuperview]; + [m_contentView release]; // Insert and retain the new content view diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 5972cf9504..3469166fdc 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -663,7 +663,9 @@ QT_WARNING_POP - (BOOL)becomeFirstResponder { - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (!m_window || !m_platformWindow) + return NO; + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); @@ -672,11 +674,13 @@ QT_WARNING_POP - (BOOL)acceptsFirstResponder { + if (!m_window || !m_platformWindow) + return NO; if (m_isMenuView) return NO; if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) return NO; - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; if ((m_window->flags() & Qt::ToolTip) == Qt::ToolTip) return NO; @@ -686,7 +690,9 @@ QT_WARNING_POP - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { Q_UNUSED(theEvent) - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (!m_window || !m_platformWindow) + return NO; + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; return YES; } @@ -2199,7 +2205,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly - m_buttons &= ~(m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton); + NSUInteger pmb = [NSEvent pressedMouseButtons]; + for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value + if (!(pmb & (1 << buttonNumber))) + m_buttons &= ~cocoaButton2QtButton(buttonNumber); + } NSPoint windowPoint = [self convertPoint: point fromView: nil]; QPoint qtWindowPoint(windowPoint.x, windowPoint.y); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp index abff88b4bd..5b779d6732 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp @@ -132,7 +132,7 @@ void QEglFSKmsGbmCursor::updateMouseStatus() m_state = visible ? CursorPendingVisible : CursorPendingHidden; #ifndef QT_NO_CURSOR - changeCursor(nullptr, m_screen->topLevelAt(pos())); + changeCursor(Q_NULLPTR, m_screen->topLevelAt(pos())); #endif } diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index 82bcf48855..2b0643f26e 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -42,6 +42,7 @@ #include <UIKit/UIKit.h> +#include <QtCore/qlocale.h> #include <QtGui/qevent.h> #include <QtGui/qtransform.h> #include <qpa/qplatforminputcontext.h> @@ -52,6 +53,7 @@ const char kImePlatformDataReturnKeyType[] = "returnKeyType"; QT_BEGIN_NAMESPACE +@class QIOSLocaleListener; @class QIOSKeyboardListener; @class QIOSTextInputResponder; @protocol KeyboardState; @@ -98,6 +100,8 @@ public: void reset() Q_DECL_OVERRIDE; void commit() Q_DECL_OVERRIDE; + QLocale locale() const Q_DECL_OVERRIDE; + void clearCurrentFocusObject(); void setFocusObject(QObject *object) Q_DECL_OVERRIDE; @@ -118,6 +122,7 @@ public: private: UIView* scrollableRootView(); + QIOSLocaleListener *m_localeListener; QIOSKeyboardListener *m_keyboardHideGesture; QIOSTextInputResponder *m_textResponder; KeyboardState m_keyboardState; diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index d553d16698..ef93d68cf0 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -62,6 +62,39 @@ static QUIView *focusView() // ------------------------------------------------------------------------- +@interface QIOSLocaleListener : NSObject +@end + +@implementation QIOSLocaleListener + +- (id)init +{ + if (self = [super init]) { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(localeDidChange:) + name:NSCurrentLocaleDidChangeNotification object:nil]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)localeDidChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + QIOSInputContext::instance()->emitLocaleChanged(); +} + +@end + +// ------------------------------------------------------------------------- + @interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> { @private QIOSInputContext *m_context; @@ -291,6 +324,7 @@ QIOSInputContext *QIOSInputContext::instance() QIOSInputContext::QIOSInputContext() : QPlatformInputContext() + , m_localeListener([QIOSLocaleListener new]) , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_textResponder(0) { @@ -304,6 +338,7 @@ QIOSInputContext::QIOSInputContext() QIOSInputContext::~QIOSInputContext() { + [m_localeListener release]; [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture]; [m_keyboardHideGesture release]; @@ -663,3 +698,8 @@ void QIOSInputContext::commit() [m_textResponder unmarkText]; [m_textResponder notifyInputDelegate:Qt::ImSurroundingText]; } + +QLocale QIOSInputContext::locale() const +{ + return QLocale(QString::fromNSString([[NSLocale currentLocale] objectForKey:NSLocaleIdentifier])); +} diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index eb12739e98..37d5557794 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -359,6 +359,7 @@ - (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers { + QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true); QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers); QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers); QWindowSystemInterface::flushWindowSystemEvents(); diff --git a/src/plugins/platforms/minimal/minimal.pro b/src/plugins/platforms/minimal/minimal.pro index 0d31d6605b..bd6f2d8e6f 100644 --- a/src/plugins/platforms/minimal/minimal.pro +++ b/src/plugins/platforms/minimal/minimal.pro @@ -11,6 +11,7 @@ HEADERS = qminimalintegration.h \ OTHER_FILES += minimal.json CONFIG += qpa/genericunixfontdatabase +darwin: DEFINES += QT_NO_FONTCONFIG PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QMinimalIntegrationPlugin diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index cb3764f5df..a5b64636af 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -2,9 +2,9 @@ TEMPLATE = subdirs android: SUBDIRS += android -SUBDIRS += minimal +!android: SUBDIRS += minimal -!win32|contains(QT_CONFIG, freetype):SUBDIRS += offscreen +!android:if(!win32|contains(QT_CONFIG, freetype)): SUBDIRS += offscreen contains(QT_CONFIG, xcb) { SUBDIRS += xcb diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index a3f9f0b44b..0ce4e87e52 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -205,26 +205,9 @@ QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display) { } -QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) +bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor) { - const HDC dc = QWindowsContext::instance()->displayContext(); - if (!dc){ - qWarning("%s: No Display", __FUNCTION__); - return 0; - } - - if (!libEGL.init()) { - qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); - return 0; - } - if (!libGLESv2.init()) { - qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); - return 0; - } - - EGLDisplay display = EGL_NO_DISPLAY; - EGLint major = 0; - EGLint minor = 0; #ifdef EGL_ANGLE_platform_angle if (libEGL.eglGetPlatformDisplayEXT && (preferredType & QWindowsOpenGLTester::AngleBackendMask)) { @@ -242,16 +225,52 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11Warp) attributes = anglePlatformAttributes[2]; if (attributes) { - display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); - if (!libEGL.eglInitialize(display, &major, &minor)) { - display = EGL_NO_DISPLAY; - major = minor = 0; + *display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); + if (!libEGL.eglInitialize(*display, major, minor)) { + libEGL.eglTerminate(*display); + *display = EGL_NO_DISPLAY; + *major = *minor = 0; + return false; } } } #else // EGL_ANGLE_platform_angle - Q_UNUSED(preferredType) + Q_UNUSED(preferredType); + Q_UNUSED(dc); + Q_UNUSED(display); + Q_UNUSED(major); + Q_UNUSED(minor); #endif + return true; +} + +QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) +{ + const HDC dc = QWindowsContext::instance()->displayContext(); + if (!dc){ + qWarning("%s: No Display", __FUNCTION__); + return 0; + } + + if (!libEGL.init()) { + qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); + return 0; + } + if (!libGLESv2.init()) { + qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); + return 0; + } + + EGLDisplay display = EGL_NO_DISPLAY; + EGLint major = 0; + EGLint minor = 0; + + if (!initializeAngle(preferredType, dc, &display, &major, &minor) + && (preferredType & QWindowsOpenGLTester::AngleRendererD3d11)) { + preferredType &= ~QWindowsOpenGLTester::AngleRendererD3d11; + initializeAngle(preferredType, dc, &display, &major, &minor); + } + if (display == EGL_NO_DISPLAY) display = libEGL.eglGetDisplay(dc); if (!display) { diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h index c7f7cee3c2..48a19f81e5 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.h +++ b/src/plugins/platforms/windows/qwindowseglcontext.h @@ -131,6 +131,8 @@ public: private: explicit QWindowsEGLStaticContext(EGLDisplay display); + static bool initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor); const EGLDisplay m_display; }; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 806af6458b..4f3df32f16 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1346,8 +1346,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, fontEngine->fontDef.pixelSize, data); - if (fontEngine->fontDef.weight > QFont::Normal) - fedw->fontDef.weight = fontEngine->fontDef.weight; + fedw->fontDef.weight = fontEngine->fontDef.weight; if (fontEngine->fontDef.style > QFont::StyleNormal) fedw->fontDef.style = fontEngine->fontDef.style; fedw->fontDef.family = fam; @@ -1364,8 +1363,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) // reason QFontEngine *fe = new QWindowsFontEngine(fam, lf, data); - if (fontEngine->fontDef.weight > QFont::Normal) - fe->fontDef.weight = fontEngine->fontDef.weight; + fe->fontDef.weight = fontEngine->fontDef.weight; if (fontEngine->fontDef.style > QFont::StyleNormal) fe->fontDef.style = fontEngine->fontDef.style; fe->fontDef.family = fam; diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 96940f3f4c..4bf424f5f6 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -138,7 +138,16 @@ public: explicit ShGetFileInfoFunction(const wchar_t *fn, DWORD a, SHFILEINFO *i, UINT f, bool *r) : m_fileName(fn), m_attributes(a), m_flags(f), m_info(i), m_result(r) {} - void operator()() const { *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); } + void operator()() const + { +#ifndef Q_OS_WINCE + const UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); +#endif + *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); +#ifndef Q_OS_WINCE + SetErrorMode(oldErrorMode); +#endif + } private: const wchar_t *m_fileName; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 31c9984559..76a36851ce 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1677,11 +1677,12 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, return false; PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + // Observed painting problems with Aero style disabled (QTBUG-7865). + // 5.8: Consider making it dependent on !DwmIsCompositionEnabled(). if (testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) - InvalidateRect(hwnd, 0, false); - - BeginPaint(hwnd, &ps); + SelectClipRgn(ps.hdc, NULL); // If the a window is obscured by another window (such as a child window) // we still need to send isExposed=true, for compatibility. diff --git a/src/plugins/platforms/winrt/qwinrtdrag.cpp b/src/plugins/platforms/winrt/qwinrtdrag.cpp new file mode 100644 index 0000000000..2ef50aa4e2 --- /dev/null +++ b/src/plugins/platforms/winrt/qwinrtdrag.cpp @@ -0,0 +1,893 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwinrtdrag.h" + +#include <QtCore/qglobal.h> +#include <QtCore/QMimeData> +#include <QtCore/QStringList> +#include <QtGui/QGuiApplication> +#include <qpa/qwindowsysteminterface.h> + +#include <qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> + +#include <Windows.ApplicationModel.datatransfer.h> +#include <windows.ui.xaml.h> +#include <windows.foundation.collections.h> +#include <windows.graphics.imaging.h> +#include <windows.storage.streams.h> +#include <functional> +#include <robuffer.h> + +using namespace ABI::Windows::ApplicationModel::DataTransfer; +using namespace ABI::Windows::ApplicationModel::DataTransfer::DragDrop; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Graphics::Imaging; +using namespace ABI::Windows::Storage; +using namespace ABI::Windows::Storage::Streams; +using namespace ABI::Windows::UI::Xaml; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") + +ComPtr<IBuffer> createIBufferFromData(const char *data, qint32 size) +{ + static ComPtr<IBufferFactory> bufferFactory; + HRESULT hr; + if (!bufferFactory) { + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr<IBuffer> buffer; + const UINT32 length = size; + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(length); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; + hr = buffer.As(&byteArrayAccess); + Q_ASSERT_SUCCEEDED(hr); + + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + Q_ASSERT_SUCCEEDED(hr); + memcpy(bytes, data, length); + return buffer; +} + +class DragThreadTransferData : public QObject +{ + Q_OBJECT +public slots: + void handleDrag(); +public: + explicit DragThreadTransferData(QObject *parent = Q_NULLPTR); + QWindow *window; + QWinRTInternalMimeData *mime; + QPoint point; + Qt::DropActions actions; + bool dropAction; + ComPtr<IDragEventArgs> nativeArgs; + ComPtr<IDragOperationDeferral> deferral; +}; + +inline QString hStringToQString(const HString &hString) +{ + quint32 l; + const wchar_t *raw = hString.GetRawBuffer(&l); + return (QString::fromWCharArray(raw, l)); +} + +inline HString qStringToHString(const QString &qString) +{ + HString h; + h.Set(reinterpret_cast<const wchar_t*>(qString.utf16()), qString.size()); + return h; +} + +namespace NativeFormatStrings { + static ComPtr<IStandardDataFormatsStatics> dataStatics; + static HSTRING text; // text/plain + static HSTRING html; // text/html + static HSTRING storage; // text/uri-list +} + +static inline DataPackageOperation translateFromQDragDropActions(const Qt::DropAction action) +{ + switch (action) { + case Qt::CopyAction: + return DataPackageOperation_Copy; + case Qt::MoveAction: + return DataPackageOperation_Move; + case Qt::LinkAction: + return DataPackageOperation_Link; + case Qt::IgnoreAction: + default: + return DataPackageOperation_None; + } +} + +static inline Qt::DropActions translateToQDragDropActions(const DataPackageOperation op) +{ + Qt::DropActions actions = Qt::IgnoreAction; + // None needs to be interpreted as the sender being able to handle + // anything and let the receiver decide + if (op == DataPackageOperation_None) + actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction; + if (op & DataPackageOperation_Link) + actions |= Qt::LinkAction; + if (op & DataPackageOperation_Copy) + actions |= Qt::CopyAction; + if (op & DataPackageOperation_Move) + actions |= Qt::MoveAction; + return actions; +} + +QWinRTInternalMimeData::QWinRTInternalMimeData() + : QInternalMimeData() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + if (!NativeFormatStrings::dataStatics) { + HRESULT hr; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_StandardDataFormats).Get(), + IID_PPV_ARGS(&NativeFormatStrings::dataStatics)); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_Text(&NativeFormatStrings::text); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_Html(&NativeFormatStrings::html); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_StorageItems(&NativeFormatStrings::storage); + Q_ASSERT_SUCCEEDED(hr); + } +} + +QWinRTInternalMimeData::~QWinRTInternalMimeData() +{ +} + +bool QWinRTInternalMimeData::hasFormat_sys(const QString &mimetype) const +{ + qCDebug(lcQpaMime) << __FUNCTION__ << mimetype; + + if (!dataView) + return false; + + return formats_sys().contains(mimetype); +} + +QStringList QWinRTInternalMimeData::formats_sys() const +{ + qCDebug(lcQpaMime) << __FUNCTION__; + + if (!dataView) + return QStringList(); + + if (!formats.isEmpty()) + return formats; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([this]() { + boolean contains; + HRESULT hr; + hr = dataView->Contains(NativeFormatStrings::text, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/plain")); + + hr = dataView->Contains(NativeFormatStrings::html, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/html")); + + hr = dataView->Contains(NativeFormatStrings::storage, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/uri-list")); + + // We need to add any additional format as well, for legacy windows + // reasons, but also in case someone adds custom formats. + ComPtr<IVectorView<HSTRING>> availableFormats; + hr = dataView->get_AvailableFormats(&availableFormats); + RETURN_OK_IF_FAILED("Could not query available formats."); + + quint32 size; + hr = availableFormats->get_Size(&size); + RETURN_OK_IF_FAILED("Could not query format vector size."); + for (quint32 i = 0; i < size; ++i) { + HString str; + hr = availableFormats->GetAt(i, str.GetAddressOf()); + if (FAILED(hr)) + continue; + formats.append(hStringToQString(str)); + } + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + + return formats; +} + +QVariant QWinRTInternalMimeData::retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const +{ + qCDebug(lcQpaMime) << __FUNCTION__ << mimetype << preferredType; + + if (!dataView || !formats.contains(mimetype)) + return QVariant(); + + QVariant result; + HRESULT hr; + if (mimetype == QLatin1String("text/plain")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<HSTRING>> op; + HString res; + hr = dataView->GetTextAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(res)); + return S_OK; + }); + } else if (mimetype == QLatin1String("text/uri-list")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<IVectorView<IStorageItem*>*>> op; + hr = dataView->GetStorageItemsAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<IStorageItem*>> nativeItems; + hr = QWinRTFunctions::await(op, nativeItems.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + QList<QVariant> items; + quint32 count; + hr = nativeItems->get_Size(&count); + for (quint32 i = 0; i < count; ++i) { + ComPtr<IStorageItem> item; + hr = nativeItems->GetAt(i, &item); + Q_ASSERT_SUCCEEDED(hr); + HString path; + hr = item->get_Path(path.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + items.append(QUrl::fromLocalFile(hStringToQString(path))); + } + result.setValue(items); + return S_OK; + }); + } else if (mimetype == QLatin1String("text/html")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<HSTRING>> op; + HString res; + hr = dataView->GetHtmlFormatAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(res)); + return S_OK; + }); + } else { + // Asking for custom data + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result, mimetype]() { + HRESULT hr; + ComPtr<IAsyncOperation<IInspectable*>> op; + ComPtr<IInspectable> res; + HString type; + type.Set(reinterpret_cast<const wchar_t*>(mimetype.utf16()), mimetype.size()); + hr = dataView->GetDataAsync(type.Get(), &op); + RETURN_OK_IF_FAILED("Could not query custom drag data."); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + if (FAILED(hr) || !res) { + qCDebug(lcQpaMime) << "Custom drop data operation returned no results or failed."; + return S_OK; + } + + // Test for properties + ComPtr<IPropertyValue> propertyValue; + hr = res.As(&propertyValue); + if (SUCCEEDED(hr)) { + // We need to check which type of custom data we are receiving + PropertyType type; + propertyValue->get_Type(&type); + switch (type) { + case PropertyType_UInt8: { + quint8 v; + hr = propertyValue->GetUInt8(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int16: { + qint16 v; + hr = propertyValue->GetInt16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt16: { + quint16 v; + hr = propertyValue->GetUInt16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int32: { + qint32 v; + hr = propertyValue->GetInt32(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt32: { + quint32 v; + hr = propertyValue->GetUInt32(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int64: { + qint64 v; + hr = propertyValue->GetInt64(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt64: { + quint64 v; + hr = propertyValue->GetUInt64(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Single: { + float v; + hr = propertyValue->GetSingle(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Double: { + double v; + hr = propertyValue->GetDouble(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Char16: { + wchar_t v; + hr = propertyValue->GetChar16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(QString::fromWCharArray(&v, 1)); + return S_OK; + } + case PropertyType_Boolean: { + boolean v; + hr = propertyValue->GetBoolean(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_String: { + HString stringProperty; + hr = propertyValue->GetString(stringProperty.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(stringProperty)); + return S_OK; + } + default: + qCDebug(lcQpaMime) << "Unknown property type dropped:" << type; + } + return S_OK; + } + + // Custom data can be read via input streams + ComPtr<IRandomAccessStream> randomAccessStream; + hr = res.As(&randomAccessStream); + if (SUCCEEDED(hr)) { + UINT64 size; + hr = randomAccessStream->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IInputStream> stream; + hr = randomAccessStream.As(&stream); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IBufferFactory> bufferFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 length = qBound(quint64(0), quint64(size), quint64(UINT_MAX)); + ComPtr<IBuffer> buffer; + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp; + hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &readOp); + + ComPtr<IBuffer> effectiveBuffer; + hr = QWinRTFunctions::await(readOp, effectiveBuffer.GetAddressOf()); + + hr = effectiveBuffer->get_Length(&length); + + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; + hr = effectiveBuffer.As(&byteArrayAccess); + + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + QByteArray array((char *)bytes, length); + result.setValue(array); + return S_OK; + } + + HSTRING runtimeClass; + hr = res->GetRuntimeClassName(&runtimeClass); + Q_ASSERT_SUCCEEDED(hr); + HString converted; + converted.Set(runtimeClass); + qCDebug(lcQpaMime) << "Unknown drop data type received (" << hStringToQString(converted) + << "). Ignoring..."; + return S_OK; + }); + } + return result; +} + +void QWinRTInternalMimeData::setDataView(const Microsoft::WRL::ComPtr<IDataPackageView> &d) +{ + dataView = d; + formats.clear(); +} + +static HRESULT qt_drag_enter(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e); + return S_OK; +} + +static HRESULT qt_drag_over(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e); + return S_OK; +} + +static HRESULT qt_drag_leave(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + // Qt internally checks for new drags and auto sends leave events + // Also there is no QPA function for handling leave + Q_UNUSED(sender); + Q_UNUSED(e); + return S_OK; +} + +static HRESULT qt_drop(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e, true); + return S_OK; +} + +#define Q_DECLARE_DRAGHANDLER(name,func) \ +class QtDragEventHandler##name : public IDragEventHandler \ +{ \ +public: \ + virtual HRESULT STDMETHODCALLTYPE Invoke(IInspectable *sender, \ + ABI::Windows::UI::Xaml::IDragEventArgs *e) \ + { \ + return qt_##func(sender, e);\ + } \ + \ + STDMETHODIMP \ + QueryInterface(REFIID riid, void FAR* FAR* ppvObj) \ + { \ + if (riid == IID_IUnknown || riid == IID_IDragEventHandler) { \ + *ppvObj = this; \ + AddRef(); \ + return NOERROR; \ + } \ + *ppvObj = NULL; \ + return ResultFromScode(E_NOINTERFACE); \ + } \ + \ + STDMETHODIMP_(ULONG) \ + AddRef(void) \ + { \ + return ++m_refs; \ + } \ + \ + STDMETHODIMP_(ULONG) \ + Release(void) \ + { \ + if (--m_refs == 0) { \ + delete this; \ + return 0; \ + } \ + return m_refs; \ + } \ +private: \ +ULONG m_refs{0}; \ +}; + +Q_DECLARE_DRAGHANDLER(Enter, drag_enter) +Q_DECLARE_DRAGHANDLER(Over, drag_over) +Q_DECLARE_DRAGHANDLER(Leave, drag_leave) +Q_DECLARE_DRAGHANDLER(Drop, drop) + +#define Q_INST_DRAGHANDLER(name) QtDragEventHandler##name() + +Q_GLOBAL_STATIC(QWinRTDrag, gDrag); + +extern ComPtr<ABI::Windows::UI::Input::IPointerPoint> qt_winrt_lastPointerPoint; // qwinrtscreen.cpp + +QWinRTDrag::QWinRTDrag() + : QPlatformDrag() + , m_dragTarget(0) +{ + qCDebug(lcQpaMime) << __FUNCTION__; + m_enter = new Q_INST_DRAGHANDLER(Enter); + m_over = new Q_INST_DRAGHANDLER(Over); + m_leave = new Q_INST_DRAGHANDLER(Leave); + m_drop = new Q_INST_DRAGHANDLER(Drop); + m_mimeData = new QWinRTInternalMimeData; +} + +QWinRTDrag::~QWinRTDrag() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + delete m_enter; + delete m_over; + delete m_leave; + delete m_drop; + delete m_mimeData; +} + +QWinRTDrag *QWinRTDrag::instance() +{ + return gDrag; +} + +inline HRESULT resetUiElementDrag(ComPtr<IUIElement3> &elem3, EventRegistrationToken startingToken) +{ + return QEventDispatcherWinRT::runOnXamlThread([elem3, startingToken]() { + HRESULT hr; + hr = elem3->put_CanDrag(false); + Q_ASSERT_SUCCEEDED(hr); + hr = elem3->remove_DragStarting(startingToken); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); +} + +Qt::DropAction QWinRTDrag::drag(QDrag *drag) +{ + qCDebug(lcQpaMime) << __FUNCTION__ << drag; + + if (!qt_winrt_lastPointerPoint) { + Q_ASSERT_X(qt_winrt_lastPointerPoint, Q_FUNC_INFO, "No pointerpoint known"); + return Qt::IgnoreAction; + } + + ComPtr<IUIElement3> elem3; + HRESULT hr = m_ui.As(&elem3); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<ABI::Windows::ApplicationModel::DataTransfer::DataPackageOperation>> op; + EventRegistrationToken startingToken; + + hr = QEventDispatcherWinRT::runOnXamlThread([drag, &op, &hr, elem3, &startingToken, this]() { + + hr = elem3->put_CanDrag(true); + Q_ASSERT_SUCCEEDED(hr); + + auto startingCallback = Callback<ITypedEventHandler<UIElement*, DragStartingEventArgs*>> ([drag](IInspectable *, IDragStartingEventArgs *args) { + qCDebug(lcQpaMime) << "Drag starting" << args; + + ComPtr<IDataPackage> dataPackage; + HRESULT hr; + hr = args->get_Data(dataPackage.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + Qt::DropAction action = drag->defaultAction(); + hr = dataPackage->put_RequestedOperation(translateFromQDragDropActions(action)); + Q_ASSERT_SUCCEEDED(hr); + +#ifndef QT_WINRT_LIMITED_DRAGANDDROP + ComPtr<IDragStartingEventArgs2> args2; + hr = args->QueryInterface(IID_PPV_ARGS(&args2)); + Q_ASSERT_SUCCEEDED(hr); + + Qt::DropActions actions = drag->supportedActions(); + DataPackageOperation allowedOperations = DataPackageOperation_None; + if (actions & Qt::CopyAction) + allowedOperations |= DataPackageOperation_Copy; + if (actions & Qt::MoveAction) + allowedOperations |= DataPackageOperation_Move; + if (actions & Qt::LinkAction) + allowedOperations |= DataPackageOperation_Link; + hr = args2->put_AllowedOperations(allowedOperations); + Q_ASSERT_SUCCEEDED(hr); +#endif // QT_WINRT_LIMITED_DRAGANDDROP + QMimeData *mimeData = drag->mimeData(); + if (mimeData->hasText()) { + hr = dataPackage->SetText(qStringToHString(mimeData->text()).Get()); + Q_ASSERT_SUCCEEDED(hr); + } + if (mimeData->hasHtml()) { + hr = dataPackage->SetHtmlFormat(qStringToHString(mimeData->html()).Get()); + Q_ASSERT_SUCCEEDED(hr); + } + // ### TODO: Missing: weblink, image + + if (!drag->pixmap().isNull()) { + const QImage image2 = drag->pixmap().toImage(); + const QImage image = image2.convertToFormat(QImage::Format_ARGB32); + if (!image.isNull()) { + // Create IBuffer containing image + ComPtr<IBuffer> imageBuffer = createIBufferFromData(reinterpret_cast<const char*>(image.bits()), image.byteCount()); + + ComPtr<ISoftwareBitmapFactory> bitmapFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_SoftwareBitmap).Get(), + IID_PPV_ARGS(&bitmapFactory)); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<ISoftwareBitmap> bitmap; + hr = bitmapFactory->Create(BitmapPixelFormat_Rgba8, image.width(), image.height(), &bitmap); + Q_ASSERT_SUCCEEDED(hr); + + hr = bitmap->CopyFromBuffer(imageBuffer.Get()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IDragUI> dragUi; + hr = args->get_DragUI(dragUi.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + hr = dragUi->SetContentFromSoftwareBitmap(bitmap.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + } + + const QStringList formats = mimeData->formats(); + for (auto item : formats) { + QByteArray data = mimeData->data(item); + + ComPtr<IBuffer> buffer = createIBufferFromData(data.constData(), data.size()); + + // We cannot push the buffer to the data package as the result on + // recipient side is different from native events. It still sends a + // buffer, but that potentially cannot be parsed. Hence we need to create + // a IRandomAccessStream which gets forwarded as is to the drop side. + ComPtr<IRandomAccessStream> ras; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &ras); + Q_ASSERT_SUCCEEDED(hr); + + hr = ras->put_Size(data.size()); + ComPtr<IOutputStream> outputStream; + hr = ras->GetOutputStreamAt(0, &outputStream); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperationWithProgress<UINT32,UINT32>> writeOp; + hr = outputStream->WriteAsync(buffer.Get(), &writeOp); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 result; + hr = QWinRTFunctions::await(writeOp, &result); + Q_ASSERT_SUCCEEDED(hr); + + unsigned char flushResult; + ComPtr<IAsyncOperation<bool>> flushOp; + hr = outputStream->FlushAsync(&flushOp); + Q_ASSERT_SUCCEEDED(hr); + + hr = QWinRTFunctions::await(flushOp, &flushResult); + Q_ASSERT_SUCCEEDED(hr); + + hr = dataPackage->SetData(qStringToHString(item).Get(), ras.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + return S_OK; + }); + + hr = elem3->add_DragStarting(startingCallback.Get(), &startingToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = elem3->StartDragAsync(qt_winrt_lastPointerPoint.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + + return hr; + }); + if (!op || FAILED(hr)) { + qCDebug(lcQpaMime) << "Drag failed:" << hr; + hr = resetUiElementDrag(elem3, startingToken); + Q_ASSERT_SUCCEEDED(hr); + return Qt::IgnoreAction; + } + + DataPackageOperation nativeOperationType; + // Do not yield, as that can cause deadlocks when dropping inside the same app + hr = QWinRTFunctions::await(op, &nativeOperationType, QWinRTFunctions::ProcessThreadEvents); + Q_ASSERT_SUCCEEDED(hr); + + hr = resetUiElementDrag(elem3, startingToken); + Q_ASSERT_SUCCEEDED(hr); + + Qt::DropAction resultAction; + switch (nativeOperationType) { + case DataPackageOperation_Link: + resultAction = Qt::LinkAction; + break; + case DataPackageOperation_Copy: + resultAction = Qt::CopyAction; + break; + case DataPackageOperation_Move: + resultAction = Qt::MoveAction; + break; + case DataPackageOperation_None: + default: + resultAction = Qt::IgnoreAction; + break; + } + + return resultAction; +} + +void QWinRTDrag::setDropTarget(QWindow *target) +{ + qCDebug(lcQpaMime) << __FUNCTION__ << target; + m_dragTarget = target; +} + +QMimeData *QWinRTDrag::platformDropData() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + return m_mimeData; +} + +void QWinRTDrag::setUiElement(ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element) +{ + qCDebug(lcQpaMime) << __FUNCTION__; + m_ui = element; + // We set the element to always accept drops and then evaluate during + // runtime + HRESULT hr = element->put_AllowDrop(TRUE); + EventRegistrationToken tok; + hr = element->add_DragEnter(m_enter, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragEnter handler."); + hr = element->add_DragOver(m_over, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragOver handler."); + hr = element->add_DragLeave(m_leave, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragLeave handler."); + hr = element->add_Drop(m_drop, &tok); + RETURN_VOID_IF_FAILED("Failed to add Drop handler."); +} + +void QWinRTDrag::handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop) +{ + Q_UNUSED(sender); + + if (!m_dragTarget) + return; + + HRESULT hr; + Point relativePoint; + hr = e->GetPosition(m_ui.Get(), &relativePoint); + RETURN_VOID_IF_FAILED("Could not query drag position."); + const QPoint p(relativePoint.X, relativePoint.Y); + + ComPtr<IDragEventArgs2> e2; + hr = e->QueryInterface(IID_PPV_ARGS(&e2)); + RETURN_VOID_IF_FAILED("Could not convert drag event args"); + + DragDropModifiers modifiers; + hr = e2->get_Modifiers(&modifiers); + +#ifndef QT_WINRT_LIMITED_DRAGANDDROP + ComPtr<IDragEventArgs3> e3; + hr = e->QueryInterface(IID_PPV_ARGS(&e3)); + Q_ASSERT_SUCCEEDED(hr); + + DataPackageOperation dataOp; + hr = e3->get_AllowedOperations(&dataOp); + if (FAILED(hr)) + qCDebug(lcQpaMime) << __FUNCTION__ << "Could not query drag operations"; + + const Qt::DropActions actions = translateToQDragDropActions(dataOp); +#else // !QT_WINRT_LIMITED_DRAGANDDROP + const Qt::DropActions actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction;; +#endif // !QT_WINRT_LIMITED_DRAGANDDROP + + ComPtr<IDataPackageView> dataView; + hr = e2->get_DataView(&dataView); + Q_ASSERT_SUCCEEDED(hr); + + m_mimeData->setDataView(dataView); + + // We use deferral as we need to jump to the Qt thread to handle + // the drag event + ComPtr<IDragOperationDeferral> deferral; + hr = e2->GetDeferral(&deferral); + Q_ASSERT_SUCCEEDED(hr); + + DragThreadTransferData *transferData = new DragThreadTransferData; + transferData->moveToThread(qGuiApp->thread()); + transferData->window = m_dragTarget; + transferData->point = p; + transferData->mime = m_mimeData; + transferData->actions = actions; + transferData->dropAction = drop; + transferData->nativeArgs = e; + transferData->deferral = deferral; + QMetaObject::invokeMethod(transferData, "handleDrag", Qt::QueuedConnection); +} + +DragThreadTransferData::DragThreadTransferData(QObject *parent) + : QObject(parent) + , dropAction(false) +{ +} + +void DragThreadTransferData::handleDrag() +{ + bool accepted = false; + Qt::DropAction acceptedAction; + if (dropAction) { + QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(window, mime, point, actions); + accepted = response.isAccepted(); + acceptedAction = response.acceptedAction(); + } else { + QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, mime, point, actions); + accepted = response.isAccepted(); + acceptedAction = response.acceptedAction(); + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([accepted, acceptedAction, this]() { + HRESULT hr; + hr = nativeArgs->put_Handled(accepted); + if (acceptedAction != Qt::IgnoreAction) { + ComPtr<IDragEventArgs2> e2; + hr = nativeArgs.As(&e2); + if (SUCCEEDED(hr)) + hr = e2->put_AcceptedOperation(translateFromQDragDropActions(acceptedAction)); + } + deferral->Complete(); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + deleteLater(); +} + +QT_END_NAMESPACE + +#include "qwinrtdrag.moc" diff --git a/src/plugins/platforms/winrt/qwinrtdrag.h b/src/plugins/platforms/winrt/qwinrtdrag.h new file mode 100644 index 0000000000..97079d831b --- /dev/null +++ b/src/plugins/platforms/winrt/qwinrtdrag.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qpa/qplatformdrag.h> + +#include <QtCore/QLoggingCategory> +#include <QtCore/QMimeData> +#include <QtGui/private/qdnd_p.h> // QInternalMime + +#include <wrl.h> + +namespace ABI { + namespace Windows { + namespace ApplicationModel { + namespace DataTransfer { + struct IDataPackageView; + } + } + namespace UI { + namespace Xaml { + struct IUIElement; + struct IDragEventArgs; + struct IDragOperationDeferral; + //struct IDataPackageView; + } + } + } +} + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaMime) + +class QtDragEventHandlerEnter; +class QtDragEventHandlerOver; +class QtDragEventHandlerLeave; +class QtDragEventHandlerDrop; +class QWinRTInternalMimeData; + +class QWinRTInternalMimeData : public QInternalMimeData { +public: + QWinRTInternalMimeData(); + virtual ~QWinRTInternalMimeData(); + + bool hasFormat_sys(const QString &mimetype) const Q_DECL_OVERRIDE; + QStringList formats_sys() const Q_DECL_OVERRIDE; + QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const Q_DECL_OVERRIDE; + + void setDataView(const Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> &d); +private: + Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> dataView; + mutable QStringList formats; +}; + +class QWinRTDrag : public QPlatformDrag { +public: + QWinRTDrag(); + virtual ~QWinRTDrag(); + static QWinRTDrag *instance(); + + QMimeData *platformDropData(void) Q_DECL_OVERRIDE; + Qt::DropAction drag(QDrag *) Q_DECL_OVERRIDE; + + void setDropTarget(QWindow *target); + + // Native integration and registration + void setUiElement(Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element); + + void handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop = false); +private: + Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> m_ui; + QWindow *m_dragTarget; + QtDragEventHandlerEnter *m_enter; + QtDragEventHandlerOver *m_over; + QtDragEventHandlerLeave *m_leave; + QtDragEventHandlerDrop *m_drop; + QWinRTInternalMimeData *m_mimeData; +}; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index 8ef560ba20..42b7f7e909 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -48,6 +48,9 @@ #include "qwinrtfontdatabase.h" #include "qwinrttheme.h" #include "qwinrtclipboard.h" +#ifndef QT_NO_DRAGANDDROP +#include "qwinrtdrag.h" +#endif #include <QtGui/QOffscreenSurface> #include <QtGui/QOpenGLContext> @@ -312,6 +315,17 @@ QPlatformClipboard *QWinRTIntegration::clipboard() const return d->clipboard; } +#ifndef QT_NO_DRAGANDDROP +QPlatformDrag *QWinRTIntegration::drag() const +{ +#if _MSC_VER >= 1900 + return QWinRTDrag::instance(); +#else + return QPlatformIntegration::drag(); +#endif +} +#endif // QT_NO_DRAGANDDROP + Qt::KeyboardModifiers QWinRTIntegration::queryKeyboardModifiers() const { Q_D(const QWinRTIntegration); diff --git a/src/plugins/platforms/winrt/qwinrtintegration.h b/src/plugins/platforms/winrt/qwinrtintegration.h index 9e28beb8fa..7b4d5531fc 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.h +++ b/src/plugins/platforms/winrt/qwinrtintegration.h @@ -97,6 +97,10 @@ public: QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE; QPlatformServices *services() const Q_DECL_OVERRIDE; QPlatformClipboard *clipboard() const Q_DECL_OVERRIDE; +#ifndef QT_NO_DRAGANDDROP + QPlatformDrag *drag() const Q_DECL_OVERRIDE; +#endif + Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE; QStringList themeNames() const Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index aed33f6b48..ad32e63cad 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -42,6 +42,9 @@ #include "qwinrtbackingstore.h" #include "qwinrtinputcontext.h" #include "qwinrtcursor.h" +#ifndef QT_NO_DRAGANDDROP +#include "qwinrtdrag.h" +#endif #include "qwinrtwindow.h" #include <private/qeventdispatcher_winrt_p.h> @@ -556,6 +559,9 @@ QWinRTScreen::QWinRTScreen() ComPtr<Xaml::IUIElement> uiElement; hr = canvas.As(&uiElement); Q_ASSERT_SUCCEEDED(hr); +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + QWinRTDrag::instance()->setUiElement(uiElement); +#endif hr = window->put_Content(uiElement.Get()); Q_ASSERT_SUCCEEDED(hr); hr = canvas.As(&d->canvas); @@ -764,6 +770,10 @@ void QWinRTScreen::addWindow(QWindow *window) QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); QWindowSystemInterface::flushWindowSystemEvents(); + +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + QWinRTDrag::instance()->setDropTarget(window); +#endif } void QWinRTScreen::removeWindow(QWindow *window) @@ -778,6 +788,10 @@ void QWinRTScreen::removeWindow(QWindow *window) QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); QWindowSystemInterface::flushWindowSystemEvents(); +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + if (wasTopWindow) + QWinRTDrag::instance()->setDropTarget(topWindow()); +#endif } void QWinRTScreen::raise(QWindow *window) @@ -973,6 +987,9 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) return S_OK; } +// Required for qwinrtdrag.cpp +ComPtr<IPointerPoint> qt_winrt_lastPointerPoint; + HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) { Q_D(QWinRTScreen); @@ -980,6 +997,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) if (FAILED(args->get_CurrentPoint(&pointerPoint))) return E_INVALIDARG; + qt_winrt_lastPointerPoint = pointerPoint; // Common traits - point, modifiers, properties Point point; pointerPoint->get_Position(&point); diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 144c581015..28456f66ec 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -14,6 +14,7 @@ SOURCES = \ qwinrtbackingstore.cpp \ qwinrtclipboard.cpp \ qwinrtcursor.cpp \ + qwinrtdrag.cpp \ qwinrteglcontext.cpp \ qwinrteventdispatcher.cpp \ qwinrtfiledialoghelper.cpp \ @@ -32,6 +33,7 @@ HEADERS = \ qwinrtbackingstore.h \ qwinrtclipboard.h \ qwinrtcursor.h \ + qwinrtdrag.h \ qwinrteglcontext.h \ qwinrteventdispatcher.h \ qwinrtfiledialoghelper.h \ @@ -47,6 +49,15 @@ HEADERS = \ OTHER_FILES += winrt.json +WINRT_SDK_VERSION_STRING = $$(UCRTVersion) +WINRT_SDK_VERSION = $$member($$list($$split(WINRT_SDK_VERSION_STRING, .)), 2) +lessThan(WINRT_SDK_VERSION, 14322): DEFINES += QT_WINRT_LIMITED_DRAGANDDROP + +*-msvc2013|contains(DEFINES, QT_NO_DRAGANDDROP) { + SOURCES -= qwinrtdrag.cpp + HEADERS -= qwinrtdrag.h +} + PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 4b0e94eda0..bc62b500e5 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/QDebug> #include "qxcbconnection.h" @@ -264,6 +265,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) } else { screen = createScreen(virtualDesktop, output, outputInfo.data()); qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; + QHighDpiScaling::updateHighDpiScaling(); } } } else if (screen) { diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 55660ce31c..0f4207c636 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -6,7 +6,7 @@ qtHaveModule(network):!contains(QT_DISABLED_FEATURES, bearermanagement): SUBDIRS qtHaveModule(gui) { SUBDIRS *= platforms platforminputcontexts platformthemes !contains(QT_DISABLED_FEATURES, imageformatplugin): SUBDIRS *= imageformats - !contains(QT_DISABLED_FEATURES, library): SUBDIRS *= generic + !android:!contains(QT_DISABLED_FEATURES, library): SUBDIRS *= generic } !winrt:!wince*:qtHaveModule(widgets):!contains(QT_DISABLED_FEATURES, printer) { diff --git a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp index 6f5fb417c6..7e6eb7c559 100644 --- a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp +++ b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 John Layt <jlayt@kde.org> +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -80,6 +81,22 @@ static QPrint::InputSlot paperBinToInputSlot(int windowsId, const QString &name) return slot; } +static LPDEVMODE getDevmode(HANDLE hPrinter, const QString &printerId) +{ + LPWSTR printerIdUtf16 = const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(printerId.utf16())); + // Allocate the required DEVMODE buffer + LONG dmSize = DocumentProperties(NULL, hPrinter, printerIdUtf16, NULL, NULL, 0); + if (dmSize < 0) + return Q_NULLPTR; + LPDEVMODE pDevMode = reinterpret_cast<LPDEVMODE>(malloc(dmSize)); + // Get the default DevMode + LONG result = DocumentProperties(NULL, hPrinter, printerIdUtf16, pDevMode, NULL, DM_OUT_BUFFER); + if (result != IDOK) { + free(pDevMode); + pDevMode = Q_NULLPTR; + } + return pDevMode; +} QWindowsPrintDevice::QWindowsPrintDevice() : QPlatformPrintDevice(), @@ -197,26 +214,21 @@ QPageSize QWindowsPrintDevice::defaultPageSize() const QPageSize pageSize; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default paper size - if (result == IDOK && pDevMode->dmFields & DM_PAPERSIZE) { - // Find the supported page size that matches, in theory default should be one of them - foreach (const QPageSize &ps, m_pageSizes) { - if (ps.windowsId() == pDevMode->dmPaperSize) { - pageSize = ps; - break; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default paper size + if (pDevMode->dmFields & DM_PAPERSIZE) { + // Find the supported page size that matches, in theory default should be one of them + foreach (const QPageSize &ps, m_pageSizes) { + if (ps.windowsId() == pDevMode->dmPaperSize) { + pageSize = ps; + break; + } } } + // Clean-up + free(pDevMode); } - // Clean-up - free(pDevMode); return pageSize; } @@ -232,20 +244,14 @@ QMarginsF QWindowsPrintDevice::printableMargins(const QPageSize &pageSize, QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) { PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data()); - DEVMODE *devMode = info->pDevMode; + LPDEVMODE devMode = info->pDevMode; bool separateDevMode = false; if (!devMode) { // GetPrinter() didn't include the DEVMODE. Get it a different way. - LONG result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), - NULL, NULL, 0); - devMode = (DEVMODE *)malloc(result); - separateDevMode = true; - result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), - devMode, NULL, DM_OUT_BUFFER); - if (result != IDOK) { - free(devMode); + devMode = getDevmode(m_hPrinter, m_id); + if (!devMode) return margins; - } + separateDevMode = true; } HDC pDC = CreateDC(NULL, (LPWSTR)m_id.utf16(), NULL, devMode); @@ -297,23 +303,17 @@ int QWindowsPrintDevice::defaultResolution() const { int resolution = 72; // TODO Set a sensible default? - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default resolution - if (result == IDOK && pDevMode->dmFields & DM_YRESOLUTION) { - if (pDevMode->dmPrintQuality > 0) - resolution = pDevMode->dmPrintQuality; - else - resolution = pDevMode->dmYResolution; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default resolution + if (pDevMode->dmFields & DM_YRESOLUTION) { + if (pDevMode->dmPrintQuality > 0) + resolution = pDevMode->dmPrintQuality; + else + resolution = pDevMode->dmYResolution; + } + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return resolution; } @@ -346,26 +346,20 @@ QPrint::InputSlot QWindowsPrintDevice::defaultInputSlot() const { QPrint::InputSlot inputSlot = QPlatformPrintDevice::defaultInputSlot();; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default input slot - if (result == IDOK && pDevMode->dmFields & DM_DEFAULTSOURCE) { - QPrint::InputSlot tempSlot = paperBinToInputSlot(pDevMode->dmDefaultSource, QString()); - foreach (const QPrint::InputSlot &slot, supportedInputSlots()) { - if (slot.key == tempSlot.key) { - inputSlot = slot; - break; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default input slot + if (pDevMode->dmFields & DM_DEFAULTSOURCE) { + QPrint::InputSlot tempSlot = paperBinToInputSlot(pDevMode->dmDefaultSource, QString()); + foreach (const QPrint::InputSlot &slot, supportedInputSlots()) { + if (slot.key == tempSlot.key) { + inputSlot = slot; + break; + } } } + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return inputSlot; } @@ -392,23 +386,17 @@ QPrint::DuplexMode QWindowsPrintDevice::defaultDuplexMode() const { QPrint::DuplexMode duplexMode = QPrint::DuplexNone; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default duplex mode - if (result == IDOK && pDevMode->dmFields & DM_DUPLEX) { - if (pDevMode->dmDuplex == DMDUP_VERTICAL) - duplexMode = QPrint::DuplexLongSide; - else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL) - duplexMode = QPrint::DuplexShortSide; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default duplex mode + if (pDevMode->dmFields & DM_DUPLEX) { + if (pDevMode->dmDuplex == DMDUP_VERTICAL) + duplexMode = QPrint::DuplexLongSide; + else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL) + duplexMode = QPrint::DuplexShortSide; + } + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return duplexMode; } @@ -430,21 +418,13 @@ QPrint::ColorMode QWindowsPrintDevice::defaultColorMode() const QPrint::ColorMode colorMode = QPrint::GrayScale; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default color mode - if (result == IDOK && pDevMode->dmFields & DM_COLOR) { - if (pDevMode->dmColor == DMCOLOR_COLOR) + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default color mode + if (pDevMode->dmFields & DM_COLOR && pDevMode->dmColor == DMCOLOR_COLOR) colorMode = QPrint::Color; + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return colorMode; } diff --git a/src/testlib/doc/qttestlib.qdocconf b/src/testlib/doc/qttestlib.qdocconf index 0fafc733b1..899f94ec53 100644 --- a/src/testlib/doc/qttestlib.qdocconf +++ b/src/testlib/doc/qttestlib.qdocconf @@ -4,7 +4,7 @@ project = QtTestLib description = Qt Test Reference Documentation version = $QT_VERSION -examplesinstallpath = testlib +examplesinstallpath = qtestlib qhp.projects = QtTestLib diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc index 62cc6c9654..fc8f3b747e 100644 --- a/src/testlib/doc/src/qttestlib-manual.qdoc +++ b/src/testlib/doc/src/qttestlib-manual.qdoc @@ -487,6 +487,7 @@ \nextpage {Chapter 2: Data Driven Testing}{Chapter 2} \title Chapter 1: Writing a Unit Test + \brief How to write a unit test. In this first chapter we will see how to write a simple unit test for a class, and how to execute it. @@ -562,6 +563,7 @@ \nextpage {Chapter 3: Simulating Gui Events}{Chapter 3} \title Chapter 2: Data Driven Testing + \brief How to create data driven tests. In this chapter we will demonstrate how to execute a test multiple times with different test data. @@ -667,6 +669,7 @@ \nextpage {Chapter 4: Replaying GUI Events}{Chapter 4} \title Chapter 3: Simulating GUI Events + \brief Howe to simulate GUI events. Qt Test features some mechanisms to test graphical user interfaces. Instead of simulating native window system events, @@ -727,6 +730,7 @@ \nextpage {Chapter 5: Writing a Benchmark}{Chapter 5} \title Chapter 4: Replaying GUI Events + \brief How to replay GUI events. In this chapter, we will show how to simulate a GUI event, and how to store a series of GUI events as well as replay them on @@ -806,6 +810,7 @@ \contentspage {Qt Test Tutorial}{Contents} \title Chapter 5: Writing a Benchmark + \brief How to write a benchmark. In this final chapter we will demonstrate how to write benchmarks using Qt Test. diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp index 4ef2dd3f67..05ad5cef5b 100644 --- a/src/testlib/qtestblacklist.cpp +++ b/src/testlib/qtestblacklist.cpp @@ -52,21 +52,38 @@ QT_BEGIN_NAMESPACE /* - The file format is simply a grouped listing of keywords - Ungrouped entries at the beginning apply to the whole testcase - Groups define testfunctions or specific test data to ignore. - After the groups come a list of entries (one per line) that define - for which platform/os combination to ignore the test result. - All keys in a single line have to match to blacklist the test. - - mac - [testFunction] - linux - windows 64bit - [testfunction2:testData] - msvc - - The known keys are listed below: + The BLACKLIST file format is a grouped listing of keywords. + + Blank lines and lines starting with # are simply ignored. An initial #-line + referring to this documentation is kind to readers. Comments can also be used + to indicate the reasons for ignoring particular cases. + + A key names a platform, O/S, distribution, tool-chain or architecture; a ! + prefix reverses what it checks. A version, joined to a key (at present, only + for distributions and for msvc) with a hyphen, limits the key to the specific + version. A keyword line matches if every key on it applies to the present + run. Successive lines are alternate conditions for ignoring a test. + + Ungrouped lines at the beginning of a file apply to the whole testcase. + A group starts with a [square-bracketed] identification of a test function, + optionally with (after a colon, the name of) a specific data set, to ignore. + Subsequent lines give conditions for ignoring this test. + + # See qtbase/src/testlib/qtestblacklist.cpp for format + osx + + # QTBUG-12345 + [testFunction] + linux + windows 64bit + + # Needs basic C++11 support + [testfunction2:testData] + msvc-2010 + + Keys are lower-case. Distribution name and version are supported if + QSysInfo's productType() and productVersion() return them. + The other known keys are listed below: */ static QSet<QByteArray> keywords() @@ -148,19 +165,30 @@ static QSet<QByteArray> keywords() return set; } -static bool checkCondition(const QByteArray &condition) +static QSet<QByteArray> activeConditions() { - static QSet<QByteArray> matchedConditions = keywords(); - QList<QByteArray> conds = condition.split(' '); + QSet<QByteArray> result = keywords(); QByteArray distributionName = QSysInfo::productType().toLower().toUtf8(); QByteArray distributionRelease = QSysInfo::productVersion().toLower().toUtf8(); if (!distributionName.isEmpty()) { - if (matchedConditions.find(distributionName) == matchedConditions.end()) - matchedConditions.insert(distributionName); - matchedConditions.insert(distributionName + "-" + distributionRelease); + if (result.find(distributionName) == result.end()) + result.insert(distributionName); + if (!distributionRelease.isEmpty()) { + QByteArray versioned = distributionName + "-" + distributionRelease; + if (result.find(versioned) == result.end()) + result.insert(versioned); + } } + return result; +} + +static bool checkCondition(const QByteArray &condition) +{ + static const QSet<QByteArray> matchedConditions = activeConditions(); + QList<QByteArray> conds = condition.split(' '); + for (int i = 0; i < conds.size(); ++i) { QByteArray c = conds.at(i); bool result = c.startsWith('!'); diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index dcd1268f6f..2231f12e2d 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1258,7 +1258,7 @@ static bool debuggerPresent() if (fd == -1) return false; char buffer[2048]; - ssize_t size = read(fd, buffer, sizeof(buffer)); + ssize_t size = read(fd, buffer, sizeof(buffer) - 1); if (size == -1) { close(fd); return false; diff --git a/src/widgets/dialogs/qdialog.cpp b/src/widgets/dialogs/qdialog.cpp index 5eb6130a45..8f3072e41c 100644 --- a/src/widgets/dialogs/qdialog.cpp +++ b/src/widgets/dialogs/qdialog.cpp @@ -871,6 +871,12 @@ void QDialog::adjustPosition(QWidget* w) if (p.y() < desk.y()) p.setY(desk.y()); + // QTBUG-52735: Manually set the correct target screen since scaling in a + // subsequent call to QWindow::resize() may otherwise use the wrong factor + // if the screen changed notification is still in an event queue. + if (QWindow *window = windowHandle()) + window->setScreen(QGuiApplication::screens().at(scrn)); + move(p); } diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 9bb4497811..26dd49f8cf 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -3671,6 +3671,9 @@ void QAbstractItemView::startDrag(Qt::DropActions supportedActions) defaultDropAction = Qt::CopyAction; if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) d->clearOrRemove(); + // Reset the drop indicator + d->dropIndicatorRect = QRect(); + d->dropIndicatorPosition = OnItem; } } #endif // QT_NO_DRAGANDDROP diff --git a/src/widgets/kernel/qdesktopwidget.cpp b/src/widgets/kernel/qdesktopwidget.cpp index fceb061867..ece673337a 100644 --- a/src/widgets/kernel/qdesktopwidget.cpp +++ b/src/widgets/kernel/qdesktopwidget.cpp @@ -46,6 +46,21 @@ QT_BEGIN_NAMESPACE +QDesktopScreenWidget::QDesktopScreenWidget(QScreen *screen, const QRect &geometry) + : QWidget(Q_NULLPTR, Qt::Desktop), m_screen(screen) +{ + setVisible(false); + if (QWindow *winHandle = windowHandle()) + winHandle->setScreen(screen); + setScreenGeometry(geometry); +} + +void QDesktopScreenWidget::setScreenGeometry(const QRect &geometry) +{ + m_geometry = geometry; + setGeometry(geometry); +} + int QDesktopScreenWidget::screenNumber() const { const QDesktopWidgetPrivate *desktopWidgetP @@ -80,54 +95,76 @@ const QRect QDesktopWidget::availableGeometry(const QWidget *widget) const return rect; } +QDesktopScreenWidget *QDesktopWidgetPrivate::widgetForScreen(QScreen *qScreen) const +{ + foreach (QDesktopScreenWidget *widget, screens) { + if (widget->screen() == qScreen) + return widget; + } + return Q_NULLPTR; +} + void QDesktopWidgetPrivate::_q_updateScreens() { Q_Q(QDesktopWidget); const QList<QScreen *> screenList = QGuiApplication::screens(); const int targetLength = screenList.length(); - const int oldLength = screens.length(); - - // Add or remove screen widgets as necessary - while (screens.size() > targetLength) - delete screens.takeLast(); - - for (int currentLength = screens.size(); currentLength < targetLength; ++currentLength) { - QScreen *qScreen = screenList.at(currentLength); - QDesktopScreenWidget *screenWidget = new QDesktopScreenWidget; - screenWidget->setGeometry(qScreen->geometry()); - QObject::connect(qScreen, SIGNAL(geometryChanged(QRect)), - q, SLOT(_q_updateScreens()), Qt::QueuedConnection); - QObject::connect(qScreen, SIGNAL(availableGeometryChanged(QRect)), - q, SLOT(_q_availableGeometryChanged()), Qt::QueuedConnection); - QObject::connect(qScreen, SIGNAL(destroyed()), - q, SLOT(_q_updateScreens()), Qt::QueuedConnection); - screens.append(screenWidget); - } + bool screenCountChanged = false; + // Re-build our screens list. This is the easiest way to later compute which signals to emit. + // Create new screen widgets as necessary. While iterating, keep the old list in place so + // that widgetForScreen works. + // Furthermore, we note which screens have changed, and compute the overall virtual geometry. + QList<QDesktopScreenWidget *> newScreens; + QList<int> changedScreens; QRegion virtualGeometry; - // update the geometry of each screen widget, determine virtual geometry, - // set the new screen for window handle and emit change signals afterwards. - QList<int> changedScreens; - for (int i = 0; i < screens.length(); i++) { - QDesktopScreenWidget *screenWidget = screens.at(i); + for (int i = 0; i < targetLength; ++i) { QScreen *qScreen = screenList.at(i); - QWindow *winHandle = screenWidget->windowHandle(); - if (winHandle && winHandle->screen() != qScreen) - winHandle->setScreen(qScreen); const QRect screenGeometry = qScreen->geometry(); - if (screenGeometry != screenWidget->geometry()) { - screenWidget->setGeometry(screenGeometry); - changedScreens.push_back(i); + QDesktopScreenWidget *screenWidget = widgetForScreen(qScreen); + if (screenWidget) { + // an old screen. update geometry and remember the index in the *new* list + if (screenGeometry != screenWidget->screenGeometry()) { + screenWidget->setScreenGeometry(screenGeometry); + changedScreens.push_back(i); + } + } else { + // a new screen, create a widget and connect the signals. + screenWidget = new QDesktopScreenWidget(qScreen, screenGeometry); + QObject::connect(qScreen, SIGNAL(geometryChanged(QRect)), + q, SLOT(_q_updateScreens()), Qt::QueuedConnection); + QObject::connect(qScreen, SIGNAL(availableGeometryChanged(QRect)), + q, SLOT(_q_availableGeometryChanged()), Qt::QueuedConnection); + QObject::connect(qScreen, SIGNAL(destroyed()), + q, SLOT(_q_updateScreens()), Qt::QueuedConnection); + screenCountChanged = true; } + // record all the screens and the overall geometry. + newScreens.push_back(screenWidget); virtualGeometry += screenGeometry; } + // Now we apply the accumulated updates. + screens.swap(newScreens); // now [newScreens] is the old screen list + Q_ASSERT(screens.size() == targetLength); q->setGeometry(virtualGeometry.boundingRect()); - if (oldLength != targetLength) - emit q->screenCountChanged(targetLength); + // Delete the QDesktopScreenWidget that are not used any more. + foreach (QDesktopScreenWidget *screen, newScreens) { + if (!screens.contains(screen)) { + delete screen; + screenCountChanged = true; + } + } + // Finally, emit the signals. + if (screenCountChanged) { + // Notice that we trigger screenCountChanged even if a screen was removed and another one added, + // in which case the total number of screens did not change. This is the only way for applications + // to notice that a screen was swapped out against another one. + emit q->screenCountChanged(targetLength); + } foreach (int changedScreen, changedScreens) emit q->resized(changedScreen); } diff --git a/src/widgets/kernel/qdesktopwidget_p.h b/src/widgets/kernel/qdesktopwidget_p.h index 7c68ad9b31..a590024b7c 100644 --- a/src/widgets/kernel/qdesktopwidget_p.h +++ b/src/widgets/kernel/qdesktopwidget_p.h @@ -61,12 +61,19 @@ QT_BEGIN_NAMESPACE class QDesktopScreenWidget : public QWidget { Q_OBJECT public: - QDesktopScreenWidget() : QWidget(Q_NULLPTR, Qt::Desktop) - { - setVisible(false); - } + explicit QDesktopScreenWidget(QScreen *screen, const QRect &geometry); int screenNumber() const; + void setScreenGeometry(const QRect &geometry); + + QScreen *screen() const { return m_screen.data(); } + QRect screenGeometry() const { return m_geometry; } + +private: + // The widget updates its screen and geometry automatically. We need to save them separately + // to detect changes, and trigger the appropriate signals. + const QPointer<QScreen> m_screen; + QRect m_geometry; }; class QDesktopWidgetPrivate : public QWidgetPrivate { @@ -76,6 +83,7 @@ public: ~QDesktopWidgetPrivate() { qDeleteAll(screens); } void _q_updateScreens(); void _q_availableGeometryChanged(); + QDesktopScreenWidget *widgetForScreen(QScreen *qScreen) const; QList<QDesktopScreenWidget *> screens; }; diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 5071c56763..124c8c000a 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -910,8 +910,10 @@ void QOpenGLWidgetPrivate::resizeViewportFramebuffer() if (!initialized) return; - if (!fbo || q->size() * q->devicePixelRatioF() != fbo->size()) + if (!fbo || q->size() * q->devicePixelRatioF() != fbo->size()) { recreateFbo(); + q->update(); + } } /*! diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 9025160e5c..bb6f3316d6 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -1006,8 +1006,18 @@ static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) static bool switchableWidgetComposition = QGuiApplicationPrivate::instance()->platformIntegration() ->hasCapability(QPlatformIntegration::SwitchableWidgetComposition); - if (!switchableWidgetComposition) + if (!switchableWidgetComposition +// The Windows compositor handles fullscreen OpenGL window specially. Besides +// having trouble with popups, it also has issues with flip-flopping between +// OpenGL-based and normal flushing. Therefore, stick with GL for fullscreen +// windows. (QTBUG-53515) +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !defined(Q_OS_WINCE) + || tlw->windowState().testFlag(Qt::WindowFullScreen) +#endif + ) + { return qt_dummy_platformTextureList(); + } } return 0; @@ -1207,10 +1217,20 @@ void QWidgetBackingStore::doSync() // We know for sure that the widget isn't overlapped if 'isMoved' is true. if (!wd->isMoved) wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove); + + // Make a copy of the widget's dirty region, to restore it in case there is an opaque + // render-to-texture child that completely covers the widget, because otherwise the + // render-to-texture child won't be visible, due to its parent widget not being redrawn + // with a proper blending mask. + const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty; + // Scrolled and moved widgets must draw all children. if (!wd->isScrolled && !wd->isMoved) wd->subtractOpaqueChildren(wd->dirty, w->rect()); + if (wd->dirty.isEmpty() && wd->textureChildSeen) + wd->dirty = dirtyBeforeSubtractedOpaqueChildren; + if (wd->dirty.isEmpty()) { resetWidget(w); continue; diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 675d54f6b4..2a4f31babf 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -883,14 +883,8 @@ void QWidgetWindow::handleExposeEvent(QExposeEvent *event) { if (isExposed()) { m_widget->setAttribute(Qt::WA_Mapped); - if (!event->region().isNull()) { - // Exposed native widgets need to be marked dirty to get them repainted correctly. - if (m_widget->internalWinId() && !m_widget->isWindow() && m_widget->isVisible() && m_widget->updatesEnabled()) { - if (QWidgetBackingStore *bs = m_widget->d_func()->maybeBackingStore()) - bs->markDirty(event->region(), m_widget); - } + if (!event->region().isNull()) m_widget->d_func()->syncBackingStore(event->region()); - } } else { m_widget->setAttribute(Qt::WA_Mapped, false); } diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp index 86903dc0c3..e01c8f536e 100644 --- a/src/widgets/widgets/qwidgetlinecontrol.cpp +++ b/src/widgets/widgets/qwidgetlinecontrol.cpp @@ -717,7 +717,7 @@ bool QWidgetLineControl::finishChange(int validateFromState, bool update, bool e if (m_transactions.count()) return false; internalUndo(validateFromState); - m_history.resize(m_undoState); + m_history.erase(m_history.begin() + m_undoState, m_history.end()); if (m_modifiedState > m_undoState) m_modifiedState = -1; m_validInput = true; @@ -796,14 +796,14 @@ void QWidgetLineControl::internalSetText(const QString &txt, int pos, bool edite */ void QWidgetLineControl::addCommand(const Command &cmd) { - if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) { - m_history.resize(m_undoState + 2); - m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend); - } else { - m_history.resize(m_undoState + 1); - } + m_history.erase(m_history.begin() + m_undoState, m_history.end()); + + if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) + m_history.push_back(Command(Separator, m_cursor, 0, m_selstart, m_selend)); + m_separator = false; - m_history[m_undoState++] = cmd; + m_history.push_back(cmd); + m_undoState = int(m_history.size()); } /*! @@ -1957,7 +1957,7 @@ bool QWidgetLineControl::isRedoAvailable() const // Same as with undo. Disabled for password modes. return !m_readOnly && m_echoMode == QLineEdit::Normal - && m_undoState < m_history.size(); + && m_undoState < int(m_history.size()); } QT_END_NAMESPACE diff --git a/src/widgets/widgets/qwidgetlinecontrol_p.h b/src/widgets/widgets/qwidgetlinecontrol_p.h index 34d19d1e77..5e52e29a51 100644 --- a/src/widgets/widgets/qwidgetlinecontrol_p.h +++ b/src/widgets/widgets/qwidgetlinecontrol_p.h @@ -67,6 +67,8 @@ #include "qplatformdefs.h" +#include <vector> + #ifdef DrawText # undef DrawText #endif @@ -469,7 +471,6 @@ private: // undo/redo handling enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection }; struct Command { - inline Command() {} inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {} uint type : 4; QChar uc; @@ -477,7 +478,7 @@ private: }; int m_modifiedState; int m_undoState; - QVector<Command> m_history; + std::vector<Command> m_history; void addCommand(const Command& cmd); inline void separate() { m_separator = true; } diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp index 9645ca9ba2..4cfbdf8c16 100644 --- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp +++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp @@ -29,6 +29,7 @@ #include <QtTest/QtTest> #include <qcoreapplication.h> #include <qstring.h> +#include <qtemporarydir.h> #include <qtemporaryfile.h> #include <qfile.h> #include <qdir.h> @@ -81,13 +82,15 @@ private slots: void QTBUG_4796(); void guaranteeUnique(); private: + QTemporaryDir m_temporaryDir; QString m_previousCurrent; }; void tst_QTemporaryFile::initTestCase() { + QVERIFY2(m_temporaryDir.isValid(), qPrintable(m_temporaryDir.errorString())); m_previousCurrent = QDir::currentPath(); - QDir::setCurrent(QDir::tempPath()); + QVERIFY(QDir::setCurrent(m_temporaryDir.path())); // For QTBUG_4796 QVERIFY(QDir("test-XXXXXX").exists() || QDir().mkdir("test-XXXXXX")); @@ -114,9 +117,6 @@ void tst_QTemporaryFile::initTestCase() void tst_QTemporaryFile::cleanupTestCase() { - // From QTBUG_4796 - QVERIFY(QDir().rmdir("test-XXXXXX")); - QDir::setCurrent(m_previousCurrent); } diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index e38dbad7d7..b605b89f34 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -135,6 +135,7 @@ private slots: void connectFunctorOverloads(); void connectFunctorQueued(); void connectFunctorWithContext(); + void connectFunctorWithContextUnique(); void connectFunctorDeadlock(); void connectStaticSlotWithObject(); void disconnectDoesNotLeakFunctor(); @@ -5999,6 +6000,22 @@ void tst_QObject::deleteLaterInAboutToBlockHandler() QCOMPARE(statusAboutToBlock, 2); } +void tst_QObject::connectFunctorWithContextUnique() +{ + // Qt::UniqueConnections currently don't work for functors, but we need to + // be sure that they don't crash. If that is implemented, change this test. + + SenderObject sender; + ReceiverObject receiver; + QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1); + receiver.count_slot1 = 0; + + QObject::connect(&sender, &SenderObject::signal1, &receiver, SlotFunctor(), Qt::UniqueConnection); + + sender.emitSignal1(); + QCOMPARE(receiver.count_slot1, 1); +} + class MyFunctor { public: diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp index 9fd418742c..c631b395f8 100644 --- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp @@ -842,6 +842,16 @@ void tst_QTimeZone::tzTest() QTzTimeZonePrivate::Data datatz2 = tztz2.data(std); QTzTimeZonePrivate::Data datautc2 = tzutc2.data(std); QCOMPARE(datatz2.offsetFromUtc, datautc2.offsetFromUtc); + + // Test a timezone with a name that isn't all letters + QTzTimeZonePrivate tzBarnaul("Asia/Barnaul"); + if (tzBarnaul.isValid()) { + QCOMPARE(tzBarnaul.data(std).abbreviation, QString("+07")); + + // first full day of the new rule (tzdata2016b) + QDateTime dt(QDate(2016, 3, 28), QTime(0, 0, 0), Qt::UTC); + QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, QString("+07")); + } #endif // Q_OS_UNIX } diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST index 0cf16b043e..0fe40e8db3 100644 --- a/tests/auto/gui/kernel/qwindow/BLACKLIST +++ b/tests/auto/gui/kernel/qwindow/BLACKLIST @@ -8,3 +8,5 @@ ubuntu-14.04 ubuntu-14.04 [modalWindowEnterEventOnHide_QTBUG35109] ubuntu-14.04 +[modalDialogClosingOneOfTwoModal] +osx diff --git a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp index 27796cda51..54bc802cf0 100644 --- a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp +++ b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp @@ -101,8 +101,6 @@ void tst_QFontCache::engineData() } if (req.pointSize < 0) req.pointSize = req.pixelSize*72.0/d->dpi; - if (req.weight == 0) - req.weight = QFont::Normal; if (req.stretch == 0) req.stretch = 100; diff --git a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp index 69cd5f00dd..4d5af44ff1 100644 --- a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp +++ b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp @@ -134,6 +134,7 @@ private slots: void xToCursorForLigatures(); void cursorInNonStopChars(); void nbsp(); + void noModificationOfInputString(); private: QFont testFont; @@ -2164,5 +2165,37 @@ void tst_QTextLayout::layoutWithCustomTabStops() QVERIFY(longWidth > shortWidth); } +void tst_QTextLayout::noModificationOfInputString() +{ + QString s = QString(QChar(QChar::LineSeparator)); + { + QTextLayout layout; + layout.setText(s); + + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QCOMPARE(s.size(), 1); + QCOMPARE(s.at(0), QChar(QChar::LineSeparator)); + } + + { + QTextLayout layout; + layout.setText(s); + + QTextOption option; + option.setFlags(QTextOption::ShowLineAndParagraphSeparators); + layout.setTextOption(option); + + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QCOMPARE(s.size(), 1); + QCOMPARE(s.at(0), QChar(QChar::LineSeparator)); + } +} + QTEST_MAIN(tst_QTextLayout) #include "tst_qtextlayout.moc" diff --git a/tests/auto/network/access/qnetworkreply/BLACKLIST b/tests/auto/network/access/qnetworkreply/BLACKLIST index efa9cb61a8..1d56c78bbc 100644 --- a/tests/auto/network/access/qnetworkreply/BLACKLIST +++ b/tests/auto/network/access/qnetworkreply/BLACKLIST @@ -1,3 +1,4 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format osx [ioGetFromBuiltinHttp:http+limited] ubuntu-14.04 @@ -7,9 +8,3 @@ ubuntu-14.04 * [backgroundRequestInterruption:ftp, bg, nobg] * -[authenticationCacheAfterCancel:http+socksauth] -rhel-7.1 -rhel-7.2 -[authenticationCacheAfterCancel:https+socksauth] -rhel-7.1 -rhel-7.2 diff --git a/tests/auto/other/compiler/tst_compiler.cpp b/tests/auto/other/compiler/tst_compiler.cpp index 50cfe48cdf..55fab00156 100644 --- a/tests/auto/other/compiler/tst_compiler.cpp +++ b/tests/auto/other/compiler/tst_compiler.cpp @@ -631,9 +631,10 @@ void tst_Compiler::cxx11_alignas() #ifndef Q_COMPILER_ALIGNAS QSKIP("Compiler does not support C++11 feature"); #else - alignas(double) char c; - Q_UNUSED(c); - QCOMPARE(Q_ALIGNOF(c), Q_ALIGNOF(double)); + struct S { + alignas(double) char c; + }; + QCOMPARE(Q_ALIGNOF(S), Q_ALIGNOF(double)); #endif } diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index 10f5a9161e..1df9a9434c 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -49,6 +49,9 @@ #include <QtWidgets/QStyle> #include <QtWidgets/QPushButton> #include <QtWidgets/QDesktopWidget> +#ifndef QT_NO_OPENGL +#include <QtWidgets/QOpenGLWidget> +#endif #include <private/qgraphicsscene_p.h> #include <private/qgraphicsview_p.h> #include "../../../shared/platforminputcontext.h" @@ -156,6 +159,9 @@ private slots: void sceneRect_growing(); void setSceneRect(); void viewport(); +#ifndef QT_NO_OPENGL + void openGLViewport(); +#endif void dragMode_scrollHand(); void dragMode_rubberBand(); void rubberBandSelectionMode(); @@ -664,6 +670,45 @@ void tst_QGraphicsView::viewport() QTest::qWait(25); } +#ifndef QT_NO_OPENGL +void tst_QGraphicsView::openGLViewport() +{ + QGraphicsScene scene; + scene.setBackgroundBrush(Qt::white); + scene.addText("GraphicsView"); + scene.addEllipse(QRectF(400, 50, 50, 50)); + scene.addEllipse(QRectF(-100, -400, 50, 50)); + scene.addEllipse(QRectF(50, -100, 50, 50)); + scene.addEllipse(QRectF(-100, 50, 50, 50)); + + QGraphicsView view(&scene); + view.setSceneRect(-400, -400, 800, 800); + view.resize(400, 400); + + QOpenGLWidget *glw = new QOpenGLWidget; + QSignalSpy spy1(glw, SIGNAL(resized())); + QSignalSpy spy2(glw, SIGNAL(frameSwapped())); + + view.setViewport(glw); + + view.show(); + QTest::qWaitForWindowExposed(&view); + QTRY_VERIFY(spy1.count() > 0); + QTRY_VERIFY(spy2.count() >= spy1.count()); + spy1.clear(); + spy2.clear(); + + // Now test for resize (QTBUG-52419). This is special when the viewport is + // a QOpenGLWidget since the underlying FBO must also be maintained. + view.resize(300, 300); + QTRY_VERIFY(spy1.count() > 0); + QTRY_VERIFY(spy2.count() >= spy1.count()); + // There is no sane way to check if the framebuffer contents got updated + // (grabFramebuffer is no good for the viewport case as that does not go + // through paintGL). So skip the actual verification. +} +#endif + void tst_QGraphicsView::dragMode_scrollHand() { for (int j = 0; j < 2; ++j) { diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 958bef3e6e..98b630b49d 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -373,7 +373,6 @@ private slots: void setMaskInResizeEvent(); void moveInResizeEvent(); - void immediateRepaintAfterShow(); void immediateRepaintAfterInvalidateBuffer(); void effectiveWinId(); @@ -8140,25 +8139,6 @@ void tst_QWidget::moveInResizeEvent() QTRY_COMPARE(testWidget.geometry(), expectedGeometry); } -void tst_QWidget::immediateRepaintAfterShow() -{ - if (m_platform == QStringLiteral("xcb")) - QSKIP("QTBUG-26424"); - if (m_platform != QStringLiteral("xcb") && m_platform != QStringLiteral("windows")) - QSKIP("We don't support immediate repaint right after show on other platforms."); - - UpdateWidget widget; - centerOnScreen(&widget); - widget.show(); - qApp->processEvents(); - // On X11 in particular, we are now waiting for a MapNotify event before - // syncing the backing store. However, if someone request a repaint() - // we must repaint immediately regardless of the current state. - widget.numPaintEvents = 0; - widget.repaint(); - QCOMPARE(widget.numPaintEvents, 1); -} - void tst_QWidget::immediateRepaintAfterInvalidateBuffer() { if (m_platform != QStringLiteral("xcb") && m_platform != QStringLiteral("windows")) diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index 200c8a6ced..35b1596003 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -83,6 +83,7 @@ private slots: void tst_showWithoutActivating(); void tst_paintEventOnSecondShow(); + void tst_paintEventOnResize_QTBUG50796(); #ifndef QT_NO_DRAGANDDROP void tst_dnd(); @@ -364,6 +365,29 @@ void tst_QWidget_window::tst_paintEventOnSecondShow() QTRY_VERIFY(w.paintEventCount > 0); } +void tst_QWidget_window::tst_paintEventOnResize_QTBUG50796() +{ + const QRect availableGeo = QGuiApplication::primaryScreen()->availableGeometry(); + + QWidget root; + root.setGeometry(availableGeo.width()/2 - 100, availableGeo.height()/2 - 100, + 200, 200); + + PaintTestWidget *native = new PaintTestWidget(&root); + native->winId(); // We're testing native widgets + native->setGeometry(10, 10, 50, 50); + + root.show(); + QVERIFY(QTest::qWaitForWindowExposed(&root)); + QVERIFY(QTest::qWaitForWindowActive(&root)); + QVERIFY(native->isVisible()); + + native->paintEventCount = 0; + native->resize(native->width() + 10, native->height() + 10); + QTest::qWait(50); // Wait for paint events + QTRY_COMPARE(native->paintEventCount, 1); // Only one paint event must occur +} + #ifndef QT_NO_DRAGANDDROP /* DnD test for QWidgetWindow (handleDrag*Event() functions). diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index c3b432788b..741d6839db 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -85,6 +85,7 @@ private slots: void mouseActivation(); #endif void tearOff(); + void submenuTearOffDontClose(); void layoutDirection(); void task208001_stylesheet(); @@ -655,6 +656,51 @@ void tst_QMenu::tearOff() #endif // QT_NO_CURSOR } +void tst_QMenu::submenuTearOffDontClose() +{ + QWidget widget; + QMenu *menu = new QMenu(&widget); + QVERIFY(!menu->isTearOffEnabled()); //default value + menu->setTearOffEnabled(true); + QVERIFY(menu->isTearOffEnabled()); + QMenu *submenu = new QMenu(&widget); + submenu->addAction("aaa"); + submenu->addAction("bbb"); + QVERIFY(!submenu->isTearOffEnabled()); //default value + submenu->setTearOffEnabled(true); + QVERIFY(submenu->isTearOffEnabled()); + menu->addMenu(submenu); + + widget.resize(300, 200); + centerOnScreen(&widget); + widget.show(); + widget.activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&widget)); + // Show parent menu + menu->popup(widget.geometry().topRight() + QPoint(50, 0)); + QVERIFY(QTest::qWaitForWindowActive(menu)); + // Then its submenu + const QRect submenuRect = menu->actionGeometry(menu->actions().at(0)); + const QPoint submenuPos(submenuRect.topLeft() + QPoint(3, 3)); + // Move then click to avoid the submenu moves from causing it to close + QTest::mouseMove(menu, submenuPos, 100); + QTest::mouseClick(menu, Qt::LeftButton, 0, submenuPos, 100); + QTRY_VERIFY(QTest::qWaitForWindowActive(submenu)); + // Make sure we enter the submenu frame directly on the tear-off area + QTest::mouseMove(submenu, QPoint(10, 3), 100); + if (submenu->style()->styleHint(QStyle::SH_Menu_SubMenuDontStartSloppyOnLeave)) { + qWarning("Sloppy menu timer disabled by the style: %s", qPrintable(QApplication::style()->objectName())); + // Submenu must get the enter event + QTRY_VERIFY(submenu->underMouse()); + } else { + const int closeTimeout = submenu->style()->styleHint(QStyle::SH_Menu_SubMenuSloppyCloseTimeout); + QTest::qWait(closeTimeout + 100); + // Menu must not disappear and it must get the enter event + QVERIFY(submenu->isVisible()); + QVERIFY(submenu->underMouse()); + } +} + void tst_QMenu::layoutDirection() { QMainWindow win; diff --git a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST index 86c7141268..fa326a6ea5 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST +++ b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST @@ -12,3 +12,6 @@ windows msvc-2010 32bit developer-build #QTBUG-31611 [reparentToNotYetCreated] windows msvc-2010 32bit developer-build + +[stackWidgetOpaqueChildIsVisible] +windows diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index c76c4d927b..e3e26d612f 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -29,11 +29,14 @@ #include <QtWidgets/QOpenGLWidget> #include <QtGui/QOpenGLFunctions> #include <QtGui/QPainter> +#include <QtGui/QScreen> +#include <QtWidgets/QDesktopWidget> #include <QtWidgets/QGraphicsView> #include <QtWidgets/QGraphicsScene> #include <QtWidgets/QGraphicsRectItem> #include <QtWidgets/QVBoxLayout> #include <QtWidgets/QPushButton> +#include <QtWidgets/QStackedWidget> #include <QtTest/QtTest> #include <QSignalSpy> @@ -54,6 +57,7 @@ private slots: void fboRedirect(); void showHide(); void nativeWindow(); + void stackWidgetOpaqueChildIsVisible(); }; void tst_QOpenGLWidget::create() @@ -420,6 +424,135 @@ void tst_QOpenGLWidget::nativeWindow() QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); } +static inline QString msgRgbMismatch(unsigned actual, unsigned expected) +{ + return QString::asprintf("Color mismatch, %#010x != %#010x", actual, expected); +} + +static QPixmap grabWidgetWithoutRepaint(const QWidget *widget, QRect clipArea) +{ + const QWidget *targetWidget = widget; +#ifdef Q_OS_WIN + // OpenGL content is not properly grabbed on Windows when passing a top level widget window, + // because GDI functions can't grab OpenGL layer content. + // Instead the whole screen should be captured, with an adjusted clip area, which contains + // the final composited content. + QDesktopWidget *desktopWidget = QApplication::desktop(); + const QWidget *mainScreenWidget = desktopWidget->screen(); + targetWidget = mainScreenWidget; + clipArea = QRect(widget->mapToGlobal(clipArea.topLeft()), + widget->mapToGlobal(clipArea.bottomRight())); +#endif + + const QWindow *window = targetWidget->window()->windowHandle(); + Q_ASSERT(window); + WId windowId = window->winId(); + + QScreen *screen = window->screen(); + Q_ASSERT(screen); + + const QSize size = clipArea.size(); + const QPixmap result = screen->grabWindow(windowId, + clipArea.x(), + clipArea.y(), + size.width(), + size.height()); + return result; +} + +#define VERIFY_COLOR(child, region, color) verifyColor(child, region, color, __LINE__) + +bool verifyColor(const QWidget *widget, const QRect &clipArea, const QColor &color, int callerLine) +{ + for (int t = 0; t < 6; t++) { + const QPixmap pixmap = grabWidgetWithoutRepaint(widget, clipArea); + if (!QTest::qCompare(pixmap.size(), + clipArea.size(), + "pixmap.size()", + "rect.size()", + __FILE__, + callerLine)) + return false; + + + const QImage image = pixmap.toImage(); + QPixmap expectedPixmap(pixmap); /* ensure equal formats */ + expectedPixmap.detach(); + expectedPixmap.fill(color); + + uint alphaCorrection = image.format() == QImage::Format_RGB32 ? 0xff000000 : 0; + uint firstPixel = image.pixel(0,0) | alphaCorrection; + + // Retry a couple of times. Some window managers have transparency animation, or are + // just slow to render. + if (t < 5) { + if (firstPixel == QColor(color).rgb() + && image == expectedPixmap.toImage()) + return true; + else + QTest::qWait(200); + } else { + if (!QTest::qVerify(firstPixel == QColor(color).rgb(), + "firstPixel == QColor(color).rgb()", + qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())), + __FILE__, callerLine)) { + return false; + } + if (!QTest::qVerify(image == expectedPixmap.toImage(), + "image == expectedPixmap.toImage()", + "grabbed pixmap differs from expected pixmap", + __FILE__, callerLine)) { + return false; + } + } + } + + return false; +} + +void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() +{ +#ifdef Q_OS_OSX + QSKIP("QScreen::grabWindow() doesn't work properly on OSX HighDPI screen: QTBUG-46803"); + return; +#endif + + QStackedWidget stack; + + QWidget* emptyWidget = new QWidget(&stack); + stack.addWidget(emptyWidget); + + // Create an opaque red QOpenGLWidget. + const int dimensionSize = 400; + ClearWidget* clearWidget = new ClearWidget(&stack, dimensionSize, dimensionSize); + clearWidget->setAttribute(Qt::WA_OpaquePaintEvent); + stack.addWidget(clearWidget); + + // Show initial QWidget. + stack.setCurrentIndex(0); + stack.resize(dimensionSize, dimensionSize); + stack.show(); + QTest::qWaitForWindowExposed(&stack); + QTest::qWaitForWindowActive(&stack); + + // Switch to the QOpenGLWidget. + stack.setCurrentIndex(1); + QTRY_COMPARE(clearWidget->m_paintCalled, true); + + // Resize the tested region to be half size in the middle, because some OSes make the widget + // have rounded corners (e.g. OSX), and the grabbed window pixmap will not coincide perfectly + // with what was actually painted. + QRect clipArea = stack.rect(); + clipArea.setSize(clipArea.size() / 2); + const int translationOffsetToMiddle = dimensionSize / 4; + clipArea.translate(translationOffsetToMiddle, translationOffsetToMiddle); + + // Verify that the QOpenGLWidget was actually painted AND displayed. + const QColor red(255, 0, 0, 255); + VERIFY_COLOR(&stack, clipArea, red); + #undef VERIFY_COLOR +} + QTEST_MAIN(tst_QOpenGLWidget) #include "tst_qopenglwidget.moc" diff --git a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp index 6ad13cd781..b58d236a91 100644 --- a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp +++ b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp @@ -1179,12 +1179,19 @@ void tst_QPlainTextEdit::selectWordsFromStringsContainingSeparators_data() QTest::addColumn<QString>("testString"); QTest::addColumn<QString>("selectedWord"); - QStringList wordSeparators; - wordSeparators << "." << "," << "?" << "!" << ":" << ";" << "-" << "<" << ">" << "[" - << "]" << "(" << ")" << "{" << "}" << "=" << "\t"<< QString(QChar::Nbsp); - - foreach (QString s, wordSeparators) - QTest::newRow(QString("separator: " + s).toLocal8Bit()) << QString("foo") + s + QString("bar") << QString("foo"); + const ushort wordSeparators[] = + {'.', ',', '?', '!', ':', ';', '-', '<', '>', '[', ']', '(', ')', '{', '}', + '=', '\t', ushort(QChar::Nbsp)}; + + for (size_t i = 0, count = sizeof(wordSeparators) / sizeof(wordSeparators[0]); i < count; ++i) { + const ushort u = wordSeparators[i]; + QByteArray rowName = QByteArrayLiteral("separator: "); + if (u >= 32 && u < 128) + rowName += char(u); + else + rowName += QByteArrayLiteral("0x") + QByteArray::number(u, 16); + QTest::newRow(rowName.constData()) << QString("foo") + QChar(u) + QString("bar") << QString("foo"); + } } void tst_QPlainTextEdit::selectWordsFromStringsContainingSeparators() diff --git a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp index f1ebfda88b..69f0ff0f7e 100644 --- a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp +++ b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp @@ -1525,12 +1525,19 @@ void tst_QTextEdit::selectWordsFromStringsContainingSeparators_data() QTest::addColumn<QString>("testString"); QTest::addColumn<QString>("selectedWord"); - QStringList wordSeparators; - wordSeparators << "." << "," << "?" << "!" << ":" << ";" << "-" << "<" << ">" << "[" - << "]" << "(" << ")" << "{" << "}" << "=" << "\t"<< QString(QChar::Nbsp); - - foreach (QString s, wordSeparators) - QTest::newRow(QString("separator: " + s).toLocal8Bit()) << QString("foo") + s + QString("bar") << QString("foo"); + const ushort wordSeparators[] = + {'.', ',', '?', '!', ':', ';', '-', '<', '>', '[', ']', '(', ')', '{', '}', + '=', '\t', ushort(QChar::Nbsp)}; + + for (size_t i = 0, count = sizeof(wordSeparators) / sizeof(wordSeparators[0]); i < count; ++i) { + const ushort u = wordSeparators[i]; + QByteArray rowName = QByteArrayLiteral("separator: "); + if (u >= 32 && u < 128) + rowName += char(u); + else + rowName += QByteArrayLiteral("0x") + QByteArray::number(u, 16); + QTest::newRow(rowName.constData()) << QString("foo") + QChar(u) + QString("bar") << QString("foo"); + } } void tst_QTextEdit::selectWordsFromStringsContainingSeparators() diff --git a/tests/manual/diaglib/qwidgetdump.cpp b/tests/manual/diaglib/qwidgetdump.cpp index 0ccf109ebb..10cfde510d 100644 --- a/tests/manual/diaglib/qwidgetdump.cpp +++ b/tests/manual/diaglib/qwidgetdump.cpp @@ -40,11 +40,50 @@ namespace QtDiag { +static const char *qtWidgetClasses[] = { + "QAbstractItemView", "QAbstractScrollArea", "QAbstractSlider", "QAbstractSpinBox", + "QCalendarWidget", "QCheckBox", "QColorDialog", "QColumnView", "QComboBox", + "QCommandLinkButton", "QDateEdit", "QDateTimeEdit", "QDesktopWidget", "QDial", + "QDialog", "QDialogButtonBox", "QDockWidget", "QDoubleSpinBox", "QErrorMessage", + "QFileDialog", "QFontComboBox", "QFontDialog", "QFrame", "QGraphicsView", + "QGroupBox", "QHeaderView", "QInputDialog", "QLCDNumber", "QLabel", "QLineEdit", + "QListView", "QListWidget", "QMainWindow", "QMdiArea", "QMdiSubWindow", "QMenu", + "QMenuBar", "QMessageBox", "QOpenGLWidget", "QPlainTextEdit", "QProgressBar", + "QProgressDialog", "QPushButton", "QRadioButton", "QRubberBand", "QScrollArea", + "QScrollBar", "QSlider", "QSpinBox", "QSplashScreen", "QSplitter", + "QStackedWidget", "QStatusBar", "QTabBar", "QTabWidget", "QTableView", + "QTableWidget", "QTextBrowser", "QTextEdit", "QTimeEdit", "QToolBar", + "QToolBox", "QToolButton", "QTreeView", "QTreeWidget", "QWidget", + "QWizard", "QWizardPage" +}; + +static bool isQtWidget(const char *className) +{ + for (size_t i = 0, count = sizeof(qtWidgetClasses) / sizeof(qtWidgetClasses[0]); i < count; ++i) { + if (!qstrcmp(className, qtWidgetClasses[i])) + return true; + } + return false; +} + +static void formatWidgetClass(QTextStream &str, const QWidget *w) +{ + const QMetaObject *mo = w->metaObject(); + str << mo->className(); + while (!isQtWidget(mo->className())) { + mo = mo->superClass(); + str << ':' << mo->className(); + } + const QString on = w->objectName(); + if (!on.isEmpty()) + str << "/\"" << on << '"'; +} + static void dumpWidgetRecursion(QTextStream &str, const QWidget *w, FormatWindowOptions options, int depth = 0) { indentStream(str, 2 * depth); - formatObject(str, w); + formatWidgetClass(str, w); str << ' ' << (w->isVisible() ? "[visible] " : "[hidden] "); if (const WId nativeWinId = w->internalWinId()) str << "[native: " << hex << showbase << nativeWinId << dec << noshowbase << "] "; @@ -109,7 +148,10 @@ void dumpAllWidgets(FormatWindowOptions options, const QWidget *root) foreach (QWidget *tw, topLevels) dumpWidgetRecursion(str, tw, options); #if QT_VERSION >= 0x050400 - qDebug().noquote() << d; + { + foreach (const QString &line, d.split(QLatin1Char('\n'))) + qDebug().noquote() << line; + } #else qDebug("%s", qPrintable(d)); #endif diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index e3eb5220be..6dc551ceb5 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -2906,7 +2906,7 @@ void Configure::generateOutputVars() qtConfig += "accessibility"; if (!qmakeLibs.isEmpty()) - qmakeVars += "LIBS += " + formatPaths(qmakeLibs); + qmakeVars += "EXTRA_LIBS += " + formatPaths(qmakeLibs); if (!dictionary["QT_LFLAGS_SQLITE"].isEmpty()) qmakeVars += "QT_LFLAGS_SQLITE += " + dictionary["QT_LFLAGS_SQLITE"]; @@ -3027,9 +3027,9 @@ void Configure::generateOutputVars() qtConfig += "rpath"; if (!qmakeDefines.isEmpty()) - qmakeVars += QString("DEFINES += ") + qmakeDefines.join(' '); + qmakeVars += QString("EXTRA_DEFINES += ") + qmakeDefines.join(' '); if (!qmakeIncludes.isEmpty()) - qmakeVars += QString("INCLUDEPATH += ") + formatPaths(qmakeIncludes); + qmakeVars += QString("EXTRA_INCLUDEPATH += ") + formatPaths(qmakeIncludes); if (!opensslLibs.isEmpty()) qmakeVars += opensslLibs; if (dictionary[ "OPENSSL" ] == "linked") { |