diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/corelib.pro | 2 | ||||
-rw-r--r-- | src/corelib/global/global.pri | 2 | ||||
-rw-r--r-- | src/corelib/global/qcompilerdetection.h | 29 | ||||
-rw-r--r-- | src/corelib/global/qfloat16.cpp | 85 | ||||
-rw-r--r-- | src/corelib/global/qfloat16.h | 3 | ||||
-rw-r--r-- | src/corelib/global/qfloat16_f16c.c | 87 | ||||
-rw-r--r-- | src/corelib/global/qglobal.h | 62 | ||||
-rw-r--r-- | src/corelib/tools/tools.pri | 1 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.cpp | 2 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.h | 1 | ||||
-rw-r--r-- | src/network/kernel/qnetworkinterface_uikit_p.h | 21 | ||||
-rw-r--r-- | src/network/kernel/qnetworkinterface_unix.cpp | 6 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.cpp | 10 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbbackingstore.cpp | 199 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbcursor.cpp | 70 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 9 |
16 files changed, 472 insertions, 117 deletions
diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index 1a7a463c2d..a0d41396dd 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -11,7 +11,7 @@ DEFINES += $$MODULE_DEFINES DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x67000000 -CONFIG += optimize_full +CONFIG += simd optimize_full QMAKE_DOCS = $$PWD/doc/qtcore.qdocconf diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index e1ca8828f9..f4f4a75536 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -39,6 +39,8 @@ SOURCES += \ global/qrandom.cpp \ global/qhooks.cpp +F16C_SOURCES += global/qfloat16_f16c.c + VERSIONTAGGING_SOURCES = global/qversiontagging.cpp darwin: SOURCES += global/qoperatingsystemversion_darwin.mm diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 231ac2c9b0..50cf0889c7 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -651,6 +651,12 @@ # undef Q_COMPILER_CONSTEXPR # endif # endif +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L s +// C11 features supported. Only tested with ICC 17 and up. +# define Q_COMPILER_STATIC_ASSERT +# if __has_include(<threads.h>) +# define Q_COMPILER_THREAD_LOCAL +# endif # endif #endif @@ -803,6 +809,17 @@ # endif # endif +# if defined(__STDC_VERSION__) +# if __has_feature(c_static_assert) +# define Q_COMPILER_STATIC_ASSERT +# endif +# if __has_feature(c_thread_local) && __has_include(<threads.h>) +# if !defined(__FreeBSD__) /* FreeBSD clang fails on __cxa_thread_atexit */ +# define Q_COMPILER_THREAD_LOCAL +# endif +# endif +# endif + # if defined(__has_warning) # if __has_warning("-Wunused-private-field") # define Q_DECL_UNUSED_MEMBER Q_DECL_UNUSED @@ -898,6 +915,18 @@ # define Q_COMPILER_RETURN_TYPE_DEDUCTION # endif # endif +# if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L +# if Q_CC_GNU >= 407 + /* C11 features supported in GCC 4.7: */ +# define Q_COMPILER_STATIC_ASSERT +# endif +# if Q_CC_GNU >= 409 + /* C11 features supported in GCC 4.9: */ +# if __has_include(<threads.h>) +# define Q_COMPILER_THREAD_LOCAL +# endif +# endif +# endif #endif #if defined(Q_CC_MSVC) diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp index 89edfc8787..4c9b831469 100644 --- a/src/corelib/global/qfloat16.cpp +++ b/src/corelib/global/qfloat16.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qfloat16_p.h" +#include "private/qsimd_p.h" QT_BEGIN_NAMESPACE @@ -113,4 +114,88 @@ Q_REQUIRED_RESULT bool qIsFinite(qfloat16 f) Q_DECL_NOTHROW { return qt_is_finit exactness is stronger the smaller the numbers are. */ +#if QT_COMPILER_SUPPORTS(F16C) +static inline bool hasFastF16() +{ + // All processors with F16C also support AVX, but YMM registers + // might not be supported by the OS, or they might be disabled. + return qCpuHasFeature(F16C) && qCpuHasFeature(AVX); +} + +extern "C" { +extern void qFloatToFloat16_fast(quint16 *out, const float *in, qssize_t len) Q_DECL_NOTHROW; +extern void qFloatFromFloat16_fast(float *out, const quint16 *in, qssize_t len) Q_DECL_NOTHROW; +} + +#elif defined(__ARM_FP16_FORMAT_IEEE) && defined(__ARM_NEON__) +static inline bool hasFastF16() +{ + return true; +} + +static void qFloatToFloat16_fast(quint16 *out, const float *in, qssize_t len) Q_DECL_NOTHROW +{ + __fp16 *out_f16 = reinterpret_cast<__fp16 *>(out); + qssize_t i = 0; + for (; i < len - 3; i += 4) + vst1_f16(out_f16 + i, vcvt_f16_f32(vld1q_f32(in + i))); + SIMD_EPILOGUE(i, len, 3) + out_f16[i] = __fp16(in[i]); +} + +static void qFloatFromFloat16_fast(float *out, const quint16 *in, qssize_t len) Q_DECL_NOTHROW +{ + const __fp16 *in_f16 = reinterpret_cast<const __fp16 *>(in); + qssize_t i = 0; + for (; i < len - 3; i += 4) + vst1q_f32(out + i, vcvt_f32_f16(vld1_f16(in_f16 + i))); + SIMD_EPILOGUE(i, len, 3) + out[i] = float(in_f16[i]); +} +#else +static inline bool hasFastF16() +{ + return false; +} + +static void qFloatToFloat16_fast(quint16 *, const float *, qssize_t) Q_DECL_NOTHROW +{ + Q_UNREACHABLE(); +} + +static void qFloatFromFloat16_fast(float *, const quint16 *, qssize_t) Q_DECL_NOTHROW +{ + Q_UNREACHABLE(); +} +#endif +/*! + \since 5.11 + + Converts \a len floats from \a in to qfloat16 and stores them in \a out. + Both \a in and \a out must have \a len allocated entries. +*/ +Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *out, const float *in, qssize_t len) Q_DECL_NOTHROW +{ + if (hasFastF16()) + return qFloatToFloat16_fast(reinterpret_cast<quint16 *>(out), in, len); + + for (qssize_t i = 0; i < len; ++i) + out[i] = qfloat16(in[i]); +} + +/*! + \since 5.11 + + Converts \a len qfloat16 from \a in to floats and stores them in \a out. + Both \a in and \a out must have \a len allocated entries. +*/ +Q_CORE_EXPORT void qFloatFromFloat16(float *out, const qfloat16 *in, qssize_t len) Q_DECL_NOTHROW +{ + if (hasFastF16()) + return qFloatFromFloat16_fast(out, reinterpret_cast<const quint16 *>(in), len); + + for (qssize_t i = 0; i < len; ++i) + out[i] = float(in[i]); +} + QT_END_NAMESPACE diff --git a/src/corelib/global/qfloat16.h b/src/corelib/global/qfloat16.h index a36852fc22..6b5c550b53 100644 --- a/src/corelib/global/qfloat16.h +++ b/src/corelib/global/qfloat16.h @@ -79,6 +79,9 @@ private: Q_DECLARE_TYPEINFO(qfloat16, Q_PRIMITIVE_TYPE); +Q_CORE_EXPORT void qFloatToFloat16(qfloat16 *, const float *, qssize_t length) Q_DECL_NOTHROW; +Q_CORE_EXPORT void qFloatFromFloat16(float *, const qfloat16 *, qssize_t length) Q_DECL_NOTHROW; + Q_REQUIRED_RESULT Q_CORE_EXPORT bool qIsInf(qfloat16 f) Q_DECL_NOTHROW; // complements qnumeric.h Q_REQUIRED_RESULT Q_CORE_EXPORT bool qIsNaN(qfloat16 f) Q_DECL_NOTHROW; // complements qnumeric.h Q_REQUIRED_RESULT Q_CORE_EXPORT bool qIsFinite(qfloat16 f) Q_DECL_NOTHROW; // complements qnumeric.h diff --git a/src/corelib/global/qfloat16_f16c.c b/src/corelib/global/qfloat16_f16c.c new file mode 100644 index 0000000000..ffd35f8ebc --- /dev/null +++ b/src/corelib/global/qfloat16_f16c.c @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore 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 "private/qsimd_p.h" + +// The x86 F16C instructions operate on AVX registers, so AVX support is +// required. We don't need to check for __F16C__ because we this file wouldn't +// have been compiled if the support was missing in the first place, and not +// all compilers define it. Technically, we didn't need to check for __AVX__ +// either. +#if !defined(__AVX__) +# error "AVX support required" +#endif + +#ifdef __cplusplus +QT_BEGIN_NAMESPACE +extern "C" { +#endif + +void qFloatToFloat16_fast(quint16 *out, const float *in, qssize_t len) Q_DECL_NOTHROW +{ + qssize_t i = 0; + for (; i < len - 7; i += 8) + _mm_storeu_si128((__m128i *)(out + i), _mm256_cvtps_ph(_mm256_loadu_ps(in + i), 0)); + if (i < len - 3) { + _mm_storel_epi64((__m128i *)(out + i), _mm_cvtps_ph(_mm_loadu_ps(in + i), 0)); + i += 4; + } + // Inlining "qfloat16::qfloat16(float f)": + SIMD_EPILOGUE(i, len, 3) + out[i] = _mm_extract_epi16(_mm_cvtps_ph(_mm_set_ss(in[i]), 0), 0); +} + +void qFloatFromFloat16_fast(float *out, const quint16 *in, qssize_t len) Q_DECL_NOTHROW +{ + qssize_t i = 0; + for (; i < len - 7; i += 8) + _mm256_storeu_ps(out + i, _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(in + i)))); + if (i < len - 3) { + _mm_storeu_ps(out + i, _mm_cvtph_ps(_mm_loadl_epi64((const __m128i *)(in + i)))); + i += 4; + } + // Inlining "qfloat16::operator float()": + SIMD_EPILOGUE(i, len, 3) + out[i] = _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(in[i]))); +} + +#ifdef __cplusplus +} // extern "C" +QT_END_NAMESPACE +#endif diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index e49bace002..203c9a9da4 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -47,6 +47,7 @@ # include <utility> #endif #ifndef __ASSEMBLER__ +# include <assert.h> # include <stddef.h> #endif @@ -105,6 +106,32 @@ # define Q_OF_MACH_O #endif +/* + Avoid "unused parameter" warnings +*/ +#define Q_UNUSED(x) (void)x; + +#if defined(__cplusplus) && defined(Q_COMPILER_STATIC_ASSERT) +# define Q_STATIC_ASSERT(Condition) static_assert(bool(Condition), #Condition) +# define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message) +#elif defined(Q_COMPILER_STATIC_ASSERT) +// C11 mode - using the _S version in case <assert.h> doesn't do the right thing +# define Q_STATIC_ASSERT(Condition) _Static_assert(!!(Condition), #Condition) +# define Q_STATIC_ASSERT_X(Condition, Message) _Static_assert(!!(Condition), Message) +#else +// C89 & C99 version +# define Q_STATIC_ASSERT_PRIVATE_JOIN(A, B) Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) +# define Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) A ## B +# ifdef __COUNTER__ +# define Q_STATIC_ASSERT(Condition) \ + typedef char Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __COUNTER__) [(Condition) ? 1 : -1]; +# else +# define Q_STATIC_ASSERT(Condition) \ + typedef char Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __LINE__) [(Condition) ? 1 : -1]; +# endif /* __COUNTER__ */ +# define Q_STATIC_ASSERT_X(Condition, Message) Q_STATIC_ASSERT(Condition) +#endif + #ifdef __cplusplus #include <algorithm> @@ -219,6 +246,15 @@ typedef unsigned long long quint64; /* 64 bit unsigned */ typedef qint64 qlonglong; typedef quint64 qulonglong; +#ifndef __cplusplus +// In C++ mode, we define below using QIntegerForSize template +Q_STATIC_ASSERT_X(sizeof(ptrdiff_t) == sizeof(size_t), "Weird ptrdiff_t and size_t definitions"); +typedef ptrdiff_t qptrdiff; +typedef ptrdiff_t qssize_t; +typedef ptrdiff_t qintptr; +typedef size_t quintptr; +#endif + /* Useful type definitions for Qt */ @@ -688,11 +724,6 @@ Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qSharedBuild() Q_DECL_NOTHROW; #endif /* - Avoid "unused parameter" warnings -*/ -#define Q_UNUSED(x) (void)x; - -/* Debugging and error handling */ @@ -750,27 +781,6 @@ Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char * # endif #endif - -#ifdef Q_COMPILER_STATIC_ASSERT -#define Q_STATIC_ASSERT(Condition) static_assert(bool(Condition), #Condition) -#define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message) -#else -// Intentionally undefined -template <bool Test> class QStaticAssertFailure; -template <> class QStaticAssertFailure<true> {}; - -#define Q_STATIC_ASSERT_PRIVATE_JOIN(A, B) Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) -#define Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) A ## B -#ifdef __COUNTER__ -#define Q_STATIC_ASSERT(Condition) \ - enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __COUNTER__) = sizeof(QStaticAssertFailure<!!(Condition)>)} -#else -#define Q_STATIC_ASSERT(Condition) \ - enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __LINE__) = sizeof(QStaticAssertFailure<!!(Condition)>)} -#endif /* __COUNTER__ */ -#define Q_STATIC_ASSERT_X(Condition, Message) Q_STATIC_ASSERT(Condition) -#endif - Q_NORETURN Q_CORE_EXPORT void qt_check_pointer(const char *, int) Q_DECL_NOTHROW; Q_CORE_EXPORT void qBadAlloc(); diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 2c609098ea..56b30279bd 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -223,4 +223,3 @@ TR_EXCLUDE += ../3rdparty/* # MIPS DSP MIPS_DSP_ASM += tools/qstring_mips_dsp_asm.S MIPS_DSP_HEADERS += ../gui/painting/qt_mips_asm_dsp_p.h -CONFIG += simd diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 277190b3bd..99f72a7955 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -310,6 +310,8 @@ QT_BEGIN_NAMESPACE Http2DirectAttribute are set, Http2DirectAttribute takes priority. (This value was introduced in 5.10.) + \omitvalue ResourceTypeAttribute + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index cddd81bd19..e104c139d9 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -93,6 +93,7 @@ public: OriginalContentLengthAttribute, RedirectPolicyAttribute, Http2DirectAttribute, + ResourceTypeAttribute, // internal User = 1000, UserMax = 32767 diff --git a/src/network/kernel/qnetworkinterface_uikit_p.h b/src/network/kernel/qnetworkinterface_uikit_p.h index c32bc4446c..ea40e74f5c 100644 --- a/src/network/kernel/qnetworkinterface_uikit_p.h +++ b/src/network/kernel/qnetworkinterface_uikit_p.h @@ -237,5 +237,26 @@ struct in6_ifreq { #define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) #define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) +// The definition below is ONLY a temporary workaround to unblock +// integrations on CI. MUST be removed ASAP, as soon as SDK is +// updated. Currently, we have WatchOS SDK 3.2 and it's missing +// net/if_types.h (unlike SDK 4.0, which has it). Alas, we have to +// work this around. We only define constants that we use in code. + +#if !QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_NA, __TVOS_NA, __WATCHOS_4_0) + +#define QT_WATCHOS_OUTDATED_SDK_WORKAROUND + +#define IFT_PPP 0x17 /* RFC 1331 */ +#define IFT_LOOP 0x18 /* loopback */ +#define IFT_SLIP 0x1c /* IP over generic TTY */ + +#define IFT_GIF 0x37 /*0xf0*/ +#define IFT_STF 0x39 /*0xf3*/ + +#define IFT_IEEE1394 0x90 /* IEEE1394 High Performance SerialBus*/ + +#endif // WatchOS SDK below 4.0 + #endif // QNETWORKINTERFACE_UIKIT_P_H diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index 078b8dd143..30bb37319b 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -405,11 +405,15 @@ static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa # elif defined(Q_OS_BSD4) QT_BEGIN_INCLUDE_NAMESPACE # include <net/if_dl.h> -# include <net/if_types.h> #if defined(QT_PLATFORM_UIKIT) # include "qnetworkinterface_uikit_p.h" +#if !defined(QT_WATCHOS_OUTDATED_SDK_WORKAROUND) +// TODO: remove it as soon as SDK is updated on CI!!! +# include <net/if_types.h> +#endif #else # include <net/if_media.h> +# include <net/if_types.h> # include <netinet/in_var.h> #endif // QT_PLATFORM_UIKIT QT_END_INCLUDE_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 33815eeb54..854c19ccf8 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -2071,9 +2071,17 @@ void QWindowsWindow::propagateSizeHints() bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins) { + WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam); + if ((windowPos->flags & SWP_NOZORDER) == 0) { + if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) { + QWindow *parentWindow = qWindow->parent(); + HWND parentHWND = GetAncestor(windowPos->hwnd, GA_PARENT); + HWND desktopHWND = GetDesktopWindow(); + platformWindow->m_data.embedded = !parentWindow && parentHWND && (parentHWND != desktopHWND); + } + } if (!qWindow->isTopLevel()) // Implement hasHeightForWidth(). return false; - WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam); if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE))) return false; const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 32a381bc4f..1e046aa677 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -69,6 +69,8 @@ public: QXcbShmImage(QXcbScreen *connection, const QSize &size, uint depth, QImage::Format format); ~QXcbShmImage() { destroy(); } + void flushScrolledRegion(bool clientSideScroll); + bool scroll(const QRegion &area, int dx, int dy); QImage *image() { return &m_qimage; } @@ -86,7 +88,8 @@ private: void destroy(); void ensureGC(xcb_drawable_t dst); - void flushPixmap(const QRegion ®ion); + void shmPutImage(xcb_drawable_t drawable, const QRegion ®ion, const QPoint &offset = QPoint()); + void flushPixmap(const QRegion ®ion, bool fullRegion = false); void setClip(const QRegion ®ion); xcb_shm_segment_info_t m_shm_info; @@ -99,18 +102,26 @@ private: xcb_gcontext_t m_gc; xcb_drawable_t m_gc_drawable; - // When using shared memory this is the region currently shared with the server - QRegion m_dirtyShm; - + // When using shared memory these variables are used only for server-side scrolling. // When not using shared memory, we maintain a server-side pixmap with the backing // store as well as repainted content not yet flushed to the pixmap. We only flush // the regions we need and only when these are marked dirty. This way we can just // do a server-side copy on expose instead of sending the pixels every time xcb_pixmap_t m_xcb_pixmap; QRegion m_pendingFlush; + + // This is the scrolled region which is stored in server-side pixmap + QRegion m_scrolledRegion; + + // When using shared memory this is the region currently shared with the server + QRegion m_dirtyShm; + + // When not using shared memory this is a temporary buffer which is uploaded + // as a pixmap region to server QByteArray m_flushBuffer; bool m_hasAlpha; + bool m_clientSideScroll; }; class QXcbShmGraphicsBuffer : public QPlatformGraphicsBuffer @@ -149,6 +160,7 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI , m_gc(0) , m_gc_drawable(0) , m_xcb_pixmap(0) + , m_clientSideScroll(false) { const xcb_format_t *fmt = connection()->formatForDepth(depth); Q_ASSERT(fmt); @@ -202,13 +214,59 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI m_qimage = QImage( (uchar*) m_xcb_image->data, m_xcb_image->width, m_xcb_image->height, m_xcb_image->stride, format); m_graphics_buffer = new QXcbShmGraphicsBuffer(&m_qimage); - if (!hasShm()) { - m_xcb_pixmap = xcb_generate_id(xcb_connection()); - xcb_create_pixmap(xcb_connection(), - m_xcb_image->depth, - m_xcb_pixmap, - screen->screen()->root, - m_xcb_image->width, m_xcb_image->height); + m_xcb_pixmap = xcb_generate_id(xcb_connection()); + xcb_create_pixmap(xcb_connection(), + m_xcb_image->depth, + m_xcb_pixmap, + screen->screen()->root, + m_xcb_image->width, m_xcb_image->height); +} + +void QXcbShmImage::flushScrolledRegion(bool clientSideScroll) +{ + if (m_clientSideScroll == clientSideScroll) + return; + + m_clientSideScroll = clientSideScroll; + + if (m_scrolledRegion.isNull()) + return; + + if (hasShm() && m_dirtyShm.intersects(m_scrolledRegion)) { + connection()->sync(); + m_dirtyShm = QRegion(); + } + + if (m_clientSideScroll) { + // Copy scrolled image region from server-side pixmap to client-side memory + for (const QRect &rect : m_scrolledRegion) { + const int w = rect.width(); + const int h = rect.height(); + + auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image, + xcb_connection(), + m_xcb_image->format, + m_xcb_pixmap, + rect.x(), rect.y(), + w, h, + ~0u); + + if (reply && reply->depth == m_xcb_image->depth) { + const QImage img(xcb_get_image_data(reply.get()), w, h, m_qimage.format()); + + QPainter p(&m_qimage); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawImage(rect.topLeft(), img); + } + } + m_scrolledRegion = QRegion(); + } else { + // Copy scrolled image region from client-side memory to server-side pixmap + ensureGC(m_xcb_pixmap); + if (hasShm()) + shmPutImage(m_xcb_pixmap, m_scrolledRegion); + else + flushPixmap(m_scrolledRegion, true); } } @@ -216,21 +274,28 @@ extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &o bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy) { - if (image()->isNull()) - return false; + const QRect bounds(QPoint(), size()); + const QRegion scrollArea(area & bounds); + const QPoint delta(dx, dy); - if (hasShm()) - preparePaint(area); + if (m_clientSideScroll) { + if (m_qimage.isNull()) + return false; - const QPoint delta(dx, dy); - for (const QRect &rect : area) - qt_scrollRectInImage(*image(), rect, delta); + if (hasShm()) + preparePaint(scrollArea); + + for (const QRect &rect : scrollArea) + qt_scrollRectInImage(m_qimage, rect, delta); + } else { + if (hasShm()) + shmPutImage(m_xcb_pixmap, m_pendingFlush.intersected(scrollArea)); + else + flushPixmap(scrollArea); - if (m_xcb_pixmap) { - flushPixmap(area); ensureGC(m_xcb_pixmap); - const QRect bounds(QPoint(0, 0), size()); - for (const QRect &src : area) { + + for (const QRect &src : scrollArea) { const QRect dst = src.translated(delta).intersected(bounds); xcb_copy_area(xcb_connection(), m_xcb_pixmap, @@ -242,6 +307,12 @@ bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy) } } + m_scrolledRegion |= scrollArea.translated(delta).intersected(bounds); + if (hasShm()) { + m_pendingFlush -= scrollArea; + m_pendingFlush -= m_scrolledRegion; + } + return true; } @@ -267,10 +338,8 @@ void QXcbShmImage::destroy() delete m_graphics_buffer; m_graphics_buffer = nullptr; - if (m_xcb_pixmap) { - xcb_free_pixmap(xcb_connection(), m_xcb_pixmap); - m_xcb_pixmap = 0; - } + xcb_free_pixmap(xcb_connection(), m_xcb_pixmap); + m_xcb_pixmap = 0; } void QXcbShmImage::ensureGC(xcb_drawable_t dst) @@ -353,10 +422,36 @@ static inline quint32 round_up_scanline(quint32 base, quint32 pad) return (base + pad - 1) & -pad; } -void QXcbShmImage::flushPixmap(const QRegion ®ion) +void QXcbShmImage::shmPutImage(xcb_drawable_t drawable, const QRegion ®ion, const QPoint &offset) +{ + for (const QRect &rect : region) { + const QPoint source = rect.translated(offset).topLeft(); + xcb_shm_put_image(xcb_connection(), + drawable, + m_gc, + m_xcb_image->width, + m_xcb_image->height, + source.x(), source.y(), + rect.width(), rect.height(), + rect.x(), rect.y(), + m_xcb_image->depth, + m_xcb_image->format, + 0, // send event? + m_shm_info.shmseg, + m_xcb_image->data - m_shm_info.shmaddr); + } + m_dirtyShm |= region.translated(offset); +} + +void QXcbShmImage::flushPixmap(const QRegion ®ion, bool fullRegion) { - const QVector<QRect> rects = m_pendingFlush.intersected(region).rects(); - m_pendingFlush -= region; + QVector<QRect> rects; + if (!fullRegion) { + rects = m_pendingFlush.intersected(region).rects(); + m_pendingFlush -= region; + } else { + rects = region.rects(); + } xcb_image_t xcb_subimage; memset(&xcb_subimage, 0, sizeof(xcb_image_t)); @@ -372,7 +467,7 @@ void QXcbShmImage::flushPixmap(const QRegion ®ion) const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order; - for (const QRect &rect : rects) { + for (const QRect &rect : qAsConst(rects)) { // We must make sure that each request is not larger than max_req_size. // Each request takes req_size + m_xcb_image->stride * height bytes. static const uint32_t req_size = sizeof(xcb_put_image_request_t); @@ -445,29 +540,32 @@ void QXcbShmImage::setClip(const QRegion ®ion) void QXcbShmImage::put(xcb_drawable_t dst, const QRegion ®ion, const QPoint &offset) { + Q_ASSERT(!m_clientSideScroll); + ensureGC(dst); setClip(region); - const QRect bounds = region.boundingRect(); - const QPoint target = bounds.topLeft(); - const QRect source = bounds.translated(offset); - if (hasShm()) { - xcb_shm_put_image(xcb_connection(), + // Copy scrolled area on server-side from pixmap to window + const QRegion scrolledRegion = m_scrolledRegion.translated(-offset); + for (const QRect &rect : scrolledRegion) { + const QPoint source = rect.translated(offset).topLeft(); + xcb_copy_area(xcb_connection(), + m_xcb_pixmap, dst, m_gc, - m_xcb_image->width, - m_xcb_image->height, source.x(), source.y(), - source.width(), source.height(), - target.x(), target.y(), - m_xcb_image->depth, - m_xcb_image->format, - 0, // send event? - m_shm_info.shmseg, - m_xcb_image->data - m_shm_info.shmaddr); - m_dirtyShm |= region.translated(offset); + rect.x(), rect.y(), + rect.width(), rect.height()); + } + + // Copy non-scrolled image from client-side memory to server-side window + const QRegion notScrolledArea = region - scrolledRegion; + shmPutImage(dst, notScrolledArea, offset); } else { + const QRect bounds = region.boundingRect(); + const QPoint target = bounds.topLeft(); + const QRect source = bounds.translated(offset); flushPixmap(region); xcb_copy_area(xcb_connection(), m_xcb_pixmap, @@ -489,9 +587,9 @@ void QXcbShmImage::preparePaint(const QRegion ®ion) connection()->sync(); m_dirtyShm = QRegion(); } - } else { - m_pendingFlush |= region; } + m_scrolledRegion -= region; + m_pendingFlush |= region; } QXcbBackingStore::QXcbBackingStore(QWindow *window) @@ -573,6 +671,8 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin if (!m_image || m_image->size().isEmpty()) return; + m_image->flushScrolledRegion(false); + QSize imageSize = m_image->size(); QRegion clipped = region; @@ -603,6 +703,11 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, c QPlatformTextureList *textures, bool translucentBackground) { + if (!m_image || m_image->size().isEmpty()) + return; + + m_image->flushScrolledRegion(true); + QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground); QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle()); diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index bfa3eccdf8..34b7d0d236 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -255,29 +255,29 @@ static const uint8_t * const cursor_bits20[] = { forbidden_bits, forbiddenm_bits }; -static const char * const cursorNames[] = { - "left_ptr", - "up_arrow", - "cross", - "wait", - "ibeam", - "size_ver", - "size_hor", - "size_bdiag", - "size_fdiag", - "size_all", - "blank", - "split_v", - "split_h", - "pointing_hand", - "forbidden", - "whats_this", - "left_ptr_watch", - "openhand", - "closedhand", - "copy", - "move", - "link" +static const std::vector<const char *> cursorNames[] = { + { "left_ptr", "default", "top_left_arrow", "left_arrow" }, + { "up_arrow" }, + { "cross" }, + { "wait", "watch", "0426c94ea35c87780ff01dc239897213" }, + { "ibeam", "text", "xterm" }, + { "size_ver", "ns-resize", "v_double_arrow", "00008160000006810000408080010102" }, + { "size_hor", "ew-resize", "h_double_arrow", "028006030e0e7ebffc7f7070c0600140" }, + { "size_bdiag", "nesw-resize", "50585d75b494802d0151028115016902", "fcf1c3c7cd4491d801f1e1c78f100000" }, + { "size_fdiag", "nwse-resize", "38c5dff7c7b8962045400281044508d2", "c7088f0f3e6c8088236ef8e1e3e70000" }, + { "size_all" }, + { "blank" }, + { "split_v", "row-resize", "sb_v_double_arrow", "2870a09082c103050810ffdffffe0204", "c07385c7190e701020ff7ffffd08103c" }, + { "split_h", "col-resize", "sb_h_double_arrow", "043a9f68147c53184671403ffa811cc5", "14fef782d02440884392942c11205230" }, + { "pointing_hand", "pointer", "hand1", "e29285e634086352946a0e7090d73106" }, + { "forbidden", "not-allowed", "crossed_circle", "circle", "03b6e0fcb3499374a867c041f52298f0" }, + { "whats_this", "help", "question_arrow", "5c6cd98b3f3ebcb1f9c7f1c204630408", "d9ce0ab605698f320427677b458ad60b" }, + { "left_ptr_watch", "half-busy", "progress", "00000000000000020006000e7e9ffc3f", "08e8e1c95fe2fc01f976f1e063a24ccd" }, + { "openhand", "fleur", "5aca4d189052212118709018842178c0", "9d800788f1b08800ae810202380a0822" }, + { "closedhand", "grabbing", "208530c400c041818281048008011002" }, + { "dnd-copy", "copy" }, + { "dnd-move", "move" }, + { "dnd-link", "link" } }; QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) @@ -535,22 +535,13 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) xcb_cursor_t cursor = XCB_NONE; if (!ptrXcursorLibraryLoadCursor || !dpy) return cursor; - switch (cshape) { - case Qt::DragCopyCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); - break; - case Qt::DragMoveCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); - break; - case Qt::DragLinkCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); - break; - default: - break; - } - if (!cursor) { - cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + + for (const char *cursorName: cursorNames[cshape]) { + cursor = ptrXcursorLibraryLoadCursor(dpy, cursorName); + if (cursor != XCB_NONE) + break; } + return cursor; } #endif // QT_CONFIG(xcb_xlib) / QT_CONFIG(library) @@ -565,7 +556,6 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) #if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) if (cshape >= 0 && cshape <= Qt::LastCursor) { void *dpy = connection()->xlib_display(); - // special case for non-standard dnd-* cursors cursor = loadCursor(dpy, cshape); if (!cursor && !m_gtkCursorThemeInitialized && m_screen->xSettings()->initialized()) { QByteArray gtkCursorTheme = m_screen->xSettings()->setting("Gtk/CursorThemeName").toByteArray(); @@ -598,7 +588,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) } if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) { - const char *name = cursorNames[cshape]; + const char *name = cursorNames[cshape].front(); xcb_xfixes_set_cursor_name(conn, cursor, strlen(name), name); } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 9c34fccd48..5ea865f74a 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -644,6 +644,15 @@ void QXcbWindow::setGeometry(const QRect &rect) qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), }; xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)); + if (window()->parent() && !window()->transientParent()) { + // Wait for server reply for parented windows to ensure that a few window + // moves will come as a one event. This is important when native widget is + // moved a few times in X and Y directions causing native scroll. Widget + // must get single event to not trigger unwanted widget position changes + // and then expose events causing backingstore flushes with incorrect + // offset causing image crruption. + connection()->sync(); + } } xcb_flush(xcb_connection()); |