diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-09 01:00:54 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-09 01:00:55 +0100 |
commit | 261c0dedac1d97b5960d27eedaa017d45050b654 (patch) | |
tree | f91c8e2a770f4c456f6465f28aecefc8c2bbeccf /src | |
parent | 830e06a3f8173621a99426e7da6ad9704eb701b3 (diff) | |
parent | caa74f16d41ebe65e1edbea219f799cf246d6067 (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I056b658ffe9390dfcbe2787e2bddc7f4e9b389dd
Diffstat (limited to 'src')
46 files changed, 816 insertions, 634 deletions
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java index a4d134d0fa..1cbb8fe117 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -82,6 +82,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.PopupMenu; +import android.hardware.display.DisplayManager; import java.io.BufferedReader; import java.io.DataOutputStream; @@ -667,6 +668,28 @@ public class QtActivityDelegate } catch (Exception e) { e.printStackTrace(); } + + DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { } + + @Override + public void onDisplayChanged(int displayId) { + m_currentRotation = m_activity.getWindowManager().getDefaultDisplay().getRotation(); + QtNative.handleOrientationChanged(m_currentRotation, m_nativeOrientation); + } + + @Override + public void onDisplayRemoved(int displayId) { } + }; + + try { + DisplayManager displayManager = (DisplayManager) m_activity.getSystemService(Context.DISPLAY_SERVICE); + displayManager.registerDisplayListener(displayListener, null); + } catch (Exception e) { + e.printStackTrace(); + } + m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir); return m_mainLib != null; } @@ -856,13 +879,6 @@ public class QtActivityDelegate } catch (Exception e) { e.printStackTrace(); } - - int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation(); - if (rotation != m_currentRotation) { - QtNative.handleOrientationChanged(rotation, m_nativeOrientation); - } - - m_currentRotation = rotation; } public void onDestroy() diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 1077e31797..e7d739b4dc 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -286,7 +286,7 @@ static inline QStringList *resourceSearchPaths() decompress, use the \c{ZSTD_decompress} function from the zstd library. - \sa compressionAlgorithm(), isCopressed() + \sa compressionAlgorithm(), isCompressed() */ class QResourcePrivate { @@ -558,7 +558,7 @@ bool QResource::isValid() const check compressionAlgorithm() to verify what algorithm to use to decompress the data. - \sa data(), compressionType(), isFile() + \sa data(), compressionAlgorithm(), isFile() */ bool QResource::isCompressed() const diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 7e3be9ef36..35bb465a04 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -912,16 +912,20 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file) file.open(QIODevice::ReadOnly); //dump data QTemporaryFile *ret = new QTemporaryFile; - ret->open(); - file.seek(0); - char buffer[1024]; - while(true) { - qint64 len = file.read(buffer, 1024); - if(len < 1) - break; - ret->write(buffer, len); + if (ret->open()) { + file.seek(0); + char buffer[1024]; + while (true) { + qint64 len = file.read(buffer, 1024); + if (len < 1) + break; + ret->write(buffer, len); + } + ret->seek(0); + } else { + delete ret; + ret = nullptr; } - ret->seek(0); //restore if(wasOpen) file.seek(old_off); diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp index bbfe2dce16..a20024f468 100644 --- a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp +++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp @@ -103,7 +103,7 @@ QConcatenateTablesProxyModelPrivate::QConcatenateTablesProxyModelPrivate() \since 5.13 \class QConcatenateTablesProxyModel \inmodule QtCore - \brief The QConcatenateTablesProxyModel class proxies multiple source models, concatenating their rows + \brief The QConcatenateTablesProxyModel class proxies multiple source models, concatenating their rows. \ingroup model-view @@ -163,7 +163,7 @@ QModelIndex QConcatenateTablesProxyModel::mapFromSource(const QModelIndex &sourc } /*! - Returns the source index for a given proxy index. + Returns the source index for a given \a proxyIndex. */ QModelIndex QConcatenateTablesProxyModel::mapToSource(const QModelIndex &proxyIndex) const { @@ -232,8 +232,8 @@ bool QConcatenateTablesProxyModel::setItemData(const QModelIndex &proxyIndex, co /*! Returns the flags for the given index. - If the index is valid, the flags come from the source model for this index. - If the index is invalid (as used to determine if dropping onto an empty area + If the \a index is valid, the flags come from the source model for this \a index. + If the \a index is invalid (as used to determine if dropping onto an empty area in the view is allowed, for instance), the flags from the first model are returned. */ Qt::ItemFlags QConcatenateTablesProxyModel::flags(const QModelIndex &index) const diff --git a/src/corelib/itemmodels/qtransposeproxymodel.cpp b/src/corelib/itemmodels/qtransposeproxymodel.cpp index f15435739c..d4f379bc64 100644 --- a/src/corelib/itemmodels/qtransposeproxymodel.cpp +++ b/src/corelib/itemmodels/qtransposeproxymodel.cpp @@ -162,8 +162,9 @@ void QTransposeProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &source /*! \since 5.13 \class QTransposeProxyModel - \brief This proxy transposes the source model - \details This model will make the rows of the source model become columns of the proxy model and vice-versa. + \brief This proxy transposes the source model. + + This model will make the rows of the source model become columns of the proxy model and vice-versa. If the model is a tree, the parents will be transposed as well. For example, if an index in the source model had parent `index(2,0)`, it will have parent `index(0,2)` in the proxy. */ diff --git a/src/corelib/kernel/qcore_mac.cpp b/src/corelib/kernel/qcore_mac.cpp index e78b2d1171..b048576f5b 100644 --- a/src/corelib/kernel/qcore_mac.cpp +++ b/src/corelib/kernel/qcore_mac.cpp @@ -65,6 +65,19 @@ QCFString::operator CFStringRef() const #if defined(QT_USE_APPLE_UNIFIED_LOGGING) +bool AppleUnifiedLogger::willMirrorToStderr() +{ + // When running under Xcode or LLDB, one or more of these variables will + // be set, which triggers libsystem_trace.dyld to log messages to stderr + // as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables + // is not an option, as that would silence normal NSLog or os_log calls, + // so instead we skip our own stderr output. See rdar://36919139. + static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE") + || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR") + || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR"); + return willMirror; +} + QT_MAC_WEAK_IMPORT(_os_log_default); bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message, const QString &optionalSubsystem) @@ -103,15 +116,7 @@ bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogCont // system from redacting our log message. os_log_with_type(log, logType, "%{public}s", qPrintable(message)); - // When running under Xcode or LLDB, one or more of these variables will - // be set, which triggers libsystem_trace.dyld to log messages to stderr - // as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables - // is not an option, as that would silence normal NSLog or os_log calls, - // so instead we skip our own stderr output. See rdar://36919139. - static bool mirroredToStderr = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE") - || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR") - || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR"); - return mirroredToStderr; + return willMirrorToStderr(); } os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType) diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index acb87f8a3c..f96e7358a2 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -202,6 +202,7 @@ class Q_CORE_EXPORT AppleUnifiedLogger public: static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message, const QString &subsystem = QString()); + static bool willMirrorToStderr(); private: static os_log_type_t logTypeForMessageType(QtMsgType msgType); static os_log_t cachedLog(const QString &subsystem, const QString &category); diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 52c1b8e555..63c5a9ad73 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -178,7 +178,7 @@ public: #ifndef QT_NO_REGEXP #if QT_DEPRECATED_SINCE(5, 13) template<typename T> - QT_DEPRECATED_X("Use findChildren(const RegularExpression &, ...) instead.") + QT_DEPRECATED_X("Use findChildren(const QRegularExpression &, ...) instead.") inline QList<T> findChildren(const QRegExp &re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const { typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType; diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 05bc064005..b023ae9ed2 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -818,6 +818,16 @@ void QThread::quit() } +void QThread::exit(int returnCode) +{ + Q_D(QThread); + d->data->quitNow = true; + for (int i = 0; i < d->data->eventLoops.size(); ++i) { + QEventLoop *eventLoop = d->data->eventLoops.at(i); + eventLoop->exit(returnCode); + } +} + bool QThread::wait(unsigned long time) { Q_UNUSED(time); diff --git a/src/gui/Qt5GuiConfigExtras.cmake.in b/src/gui/Qt5GuiConfigExtras.cmake.in index 07869efd7d..84dbbfebd4 100644 --- a/src/gui/Qt5GuiConfigExtras.cmake.in +++ b/src/gui/Qt5GuiConfigExtras.cmake.in @@ -91,16 +91,29 @@ macro(_qt5gui_find_extra_libs Name Libs LibDir IncDirs) endforeach() !!ENDIF foreach(_lib ${Libs}) - string(REGEX REPLACE "[^_A-Za-z0-9]" "_" _cmake_lib_name ${_lib}) + if (IS_ABSOLUTE ${_lib}) + get_filename_component(_libFile ${_lib} NAME_WE) + if (_libFile MATCHES \"^${CMAKE_SHARED_LIBRARY_PREFIX}(.*)\") + set(_libFile ${CMAKE_MATCH_1}) + endif() + else() + set(_libFile ${_lib}) + endif() + + string(REGEX REPLACE "[^_A-Za-z0-9]" "_" _cmake_lib_name ${_libFile}) if (NOT TARGET Qt5::Gui_${_cmake_lib_name} AND NOT _Qt5Gui_${_cmake_lib_name}_LIBRARY_DONE) - find_library(Qt5Gui_${_cmake_lib_name}_LIBRARY ${_lib} + if (IS_ABSOLUTE ${_lib}) + set(Qt5Gui_${_cmake_lib_name}_LIBRARY ${_lib}) + else() + find_library(Qt5Gui_${_cmake_lib_name}_LIBRARY ${_lib} !!IF !isEmpty(CROSS_COMPILE) - PATHS \"${LibDir}\" + PATHS \"${LibDir}\" !!IF !mac - NO_DEFAULT_PATH + NO_DEFAULT_PATH !!ENDIF !!ENDIF - ) + ) + endif() !!IF mac set(Qt5Gui_${_cmake_lib_name}_LIBRARY "${Qt5Gui_${_cmake_lib_name}_LIBRARY}/${_lib}") if (NOT EXISTS "${Qt5Gui_${_cmake_lib_name}_LIBRARY}") diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index e508cc9413..18125460c7 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -2259,16 +2259,16 @@ bool QImage::reinterpretAsFormat(Format format) \sa convertToFormat() */ -void QImage::convertTo(Format f, Qt::ImageConversionFlags flags) +void QImage::convertTo(Format format, Qt::ImageConversionFlags flags) { - if (!d || f == QImage::Format_Invalid) + if (!d || format == QImage::Format_Invalid) return; detach(); - if (convertToFormat_inplace(f, flags)) + if (convertToFormat_inplace(format, flags)) return; - *this = convertToFormat_helper(f, flags); + *this = convertToFormat_helper(format, flags); } /*! diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 2a7393225e..13c81f18d0 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -95,9 +95,9 @@ public: void fill(const QColor &fillColor = Qt::white); #if QT_DEPRECATED_SINCE(5, 13) - QT_DEPRECATED_X(" Use QPainter or the fill(QColor)") + QT_DEPRECATED_X("Use QPainter or fill(QColor)") void fill(const QPaintDevice *device, const QPoint &ofs); - QT_DEPRECATED_X(" Use QPainter or the fill(QColor)") + QT_DEPRECATED_X("Use QPainter or fill(QColor)") void fill(const QPaintDevice *device, int xofs, int yofs); #endif diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp index 90253546c8..b8b532b3d0 100644 --- a/src/gui/opengl/qopengltextureuploader.cpp +++ b/src/gui/opengl/qopengltextureuploader.cpp @@ -114,6 +114,18 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag externalFormat = GL_BGRA; internalFormat = GL_RGBA; pixelType = GL_UNSIGNED_INT_8_8_8_8_REV; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian: + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) { + // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. + externalFormat = internalFormat = GL_BGRA; + pixelType = GL_UNSIGNED_BYTE; + } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { + // Is only allowed as an external format like OpenGL. + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; +#endif } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA }; @@ -125,25 +137,8 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag externalFormat = internalFormat = GL_RGBA; pixelType = GL_UNSIGNED_BYTE; } else { -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian. - if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) { - // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. - externalFormat = internalFormat = GL_BGRA; - pixelType = GL_UNSIGNED_BYTE; - } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { - // Is only allowed as an external format like OpenGL. - externalFormat = GL_BGRA; - internalFormat = GL_RGBA; - pixelType = GL_UNSIGNED_BYTE; - } else { - // No support for direct ARGB32 upload. - break; - } -#else - // Big endian requires GL_UNSIGNED_INT_8_8_8_8_REV for ARGB to match BGRA + // No support for direct ARGB32 upload. break; -#endif } targetFormat = image.format(); break; diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index a3dcb02e07..ddd1d997f2 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -333,7 +333,9 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr *shifted = QBezier::fromPoints(points_shifted[map[0]], points_shifted[map[1]], points_shifted[map[2]], points_shifted[map[3]]); - return good_offset(orig, shifted, offset, threshold); + if (np > 2) + return good_offset(orig, shifted, offset, threshold); + return Ok; } // This value is used to determine the length of control point vectors @@ -432,7 +434,6 @@ redo: } else if (res == Ok) { ++o; --b; - continue; } else if (res == Circle && maxSegments - (o - curveSegments) >= 2) { // add semi circle if (addCircle(b, offset, o)) diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 3bae6d4eb7..4a2985f74c 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -1421,7 +1421,7 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt) \fn void QTextCharFormat::setFontStyleName(const QString &styleName) \since 5.13 - Sets the text format's font \a style name. + Sets the text format's font \a styleName. \sa setFont(), QFont::setStyleName() */ diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp index a90ee72d52..fddb5feca5 100644 --- a/src/network/access/http2/hpacktable.cpp +++ b/src/network/access/http2/hpacktable.cpp @@ -42,6 +42,7 @@ #include <QtCore/qdebug.h> #include <algorithm> +#include <cstddef> #include <cstring> #include <limits> @@ -61,7 +62,7 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value) // for counting the number of references to the name and value would have // 32 octets of overhead." - const unsigned sum = unsigned(name.size()) + value.size(); + const unsigned sum = unsigned(name.size() + value.size()); if (std::numeric_limits<unsigned>::max() - 32 < sum) return HeaderSize(); return HeaderSize(true, quint32(sum + 32)); @@ -75,7 +76,7 @@ int compare(const QByteArray &lhs, const QByteArray &rhs) if (const int minLen = std::min(lhs.size(), rhs.size())) { // We use memcmp, since strings in headers are allowed // to contain '\0'. - const int cmp = std::memcmp(lhs.constData(), rhs.constData(), minLen); + const int cmp = std::memcmp(lhs.constData(), rhs.constData(), std::size_t(minLen)); if (cmp) return cmp; } @@ -138,82 +139,6 @@ bool FieldLookupTable::SearchEntry::operator < (const SearchEntry &rhs)const return offset > rhs.offset; } -// This data is from HPACK's specs and it's quite -// conveniently sorted == works with binary search as it is. -// Later this can probably change and instead of simple -// vector we'll just reuse FieldLookupTable. -// TODO: it makes sense to generate this table while ... -// configuring/building Qt (some script downloading/parsing/generating -// would be quite handy). -const std::vector<HeaderField> &staticTable() -{ - static std::vector<HeaderField> table = { - {":authority", ""}, - {":method", "GET"}, - {":method", "POST"}, - {":path", "/"}, - {":path", "/index.html"}, - {":scheme", "http"}, - {":scheme", "https"}, - {":status", "200"}, - {":status", "204"}, - {":status", "206"}, - {":status", "304"}, - {":status", "400"}, - {":status", "404"}, - {":status", "500"}, - {"accept-charset", ""}, - {"accept-encoding", "gzip, deflate"}, - {"accept-language", ""}, - {"accept-ranges", ""}, - {"accept", ""}, - {"access-control-allow-origin", ""}, - {"age", ""}, - {"allow", ""}, - {"authorization", ""}, - {"cache-control", ""}, - {"content-disposition", ""}, - {"content-encoding", ""}, - {"content-language", ""}, - {"content-length", ""}, - {"content-location", ""}, - {"content-range", ""}, - {"content-type", ""}, - {"cookie", ""}, - {"date", ""}, - {"etag", ""}, - {"expect", ""}, - {"expires", ""}, - {"from", ""}, - {"host", ""}, - {"if-match", ""}, - {"if-modified-since", ""}, - {"if-none-match", ""}, - {"if-range", ""}, - {"if-unmodified-since", ""}, - {"last-modified", ""}, - {"link", ""}, - {"location", ""}, - {"max-forwards", ""}, - {"proxy-authenticate", ""}, - {"proxy-authorization", ""}, - {"range", ""}, - {"referer", ""}, - {"refresh", ""}, - {"retry-after", ""}, - {"server", ""}, - {"set-cookie", ""}, - {"strict-transport-security", ""}, - {"transfer-encoding", ""}, - {"user-agent", ""}, - {"vary", ""}, - {"via", ""}, - {"www-authenticate", ""} - }; - - return table; -} - FieldLookupTable::FieldLookupTable(quint32 maxSize, bool use) : maxTableSize(maxSize), tableCapacity(maxSize), @@ -296,12 +221,12 @@ void FieldLookupTable::evictEntry() quint32 FieldLookupTable::numberOfEntries() const { - return quint32(staticTable().size()) + nDynamic; + return quint32(staticPart().size()) + nDynamic; } quint32 FieldLookupTable::numberOfStaticEntries() const { - return quint32(staticTable().size()); + return quint32(staticPart().size()); } quint32 FieldLookupTable::numberOfDynamicEntries() const @@ -326,24 +251,18 @@ void FieldLookupTable::clearDynamicTable() bool FieldLookupTable::indexIsValid(quint32 index) const { - return index && index <= staticTable().size() + nDynamic; + return index && index <= staticPart().size() + nDynamic; } quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &value)const { // Start from the static part first: - const auto &table = staticTable(); + const auto &table = staticPart(); const HeaderField field(name, value); - const auto staticPos = std::lower_bound(table.begin(), table.end(), field, - [](const HeaderField &lhs, const HeaderField &rhs) { - int cmp = compare(lhs.name, rhs.name); - if (cmp) - return cmp < 0; - return compare(lhs.value, rhs.value) < 0; - }); + const auto staticPos = findInStaticPart(field, CompareMode::nameAndValue); if (staticPos != table.end()) { if (staticPos->name == name && staticPos->value == value) - return staticPos - table.begin() + 1; + return quint32(staticPos - table.begin() + 1); } // Now we have to lookup in our dynamic part ... @@ -366,15 +285,12 @@ quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &valu quint32 FieldLookupTable::indexOf(const QByteArray &name) const { // Start from the static part first: - const auto &table = staticTable(); + const auto &table = staticPart(); const HeaderField field(name, QByteArray()); - const auto staticPos = std::lower_bound(table.begin(), table.end(), field, - [](const HeaderField &lhs, const HeaderField &rhs) { - return compare(lhs.name, rhs.name) < 0; - }); + const auto staticPos = findInStaticPart(field, CompareMode::nameOnly); if (staticPos != table.end()) { if (staticPos->name == name) - return staticPos - table.begin() + 1; + return quint32(staticPos - table.begin() + 1); } // Now we have to lookup in our dynamic part ... @@ -402,7 +318,7 @@ bool FieldLookupTable::field(quint32 index, QByteArray *name, QByteArray *value) if (!indexIsValid(index)) return false; - const auto &table = staticTable(); + const auto &table = staticPart(); if (index - 1 < table.size()) { *name = table[index - 1].name; *value = table[index - 1].value; @@ -477,7 +393,7 @@ quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const Q_ASSERT(offset < ChunkSize); Q_ASSERT(chunkIndex || offset >= begin); - return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticTable().size()); + return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticPart().size()); } FieldLookupTable::SearchEntry FieldLookupTable::frontKey() const @@ -526,6 +442,103 @@ void FieldLookupTable::setMaxDynamicTableSize(quint32 size) updateDynamicTableSize(size); } +// This data is from the HPACK's specs and it's quite conveniently sorted, +// except ... 'accept' is in the wrong position, see how we handle it below. +const std::vector<HeaderField> &FieldLookupTable::staticPart() +{ + static std::vector<HeaderField> table = { + {":authority", ""}, + {":method", "GET"}, + {":method", "POST"}, + {":path", "/"}, + {":path", "/index.html"}, + {":scheme", "http"}, + {":scheme", "https"}, + {":status", "200"}, + {":status", "204"}, + {":status", "206"}, + {":status", "304"}, + {":status", "400"}, + {":status", "404"}, + {":status", "500"}, + {"accept-charset", ""}, + {"accept-encoding", "gzip, deflate"}, + {"accept-language", ""}, + {"accept-ranges", ""}, + {"accept", ""}, + {"access-control-allow-origin", ""}, + {"age", ""}, + {"allow", ""}, + {"authorization", ""}, + {"cache-control", ""}, + {"content-disposition", ""}, + {"content-encoding", ""}, + {"content-language", ""}, + {"content-length", ""}, + {"content-location", ""}, + {"content-range", ""}, + {"content-type", ""}, + {"cookie", ""}, + {"date", ""}, + {"etag", ""}, + {"expect", ""}, + {"expires", ""}, + {"from", ""}, + {"host", ""}, + {"if-match", ""}, + {"if-modified-since", ""}, + {"if-none-match", ""}, + {"if-range", ""}, + {"if-unmodified-since", ""}, + {"last-modified", ""}, + {"link", ""}, + {"location", ""}, + {"max-forwards", ""}, + {"proxy-authenticate", ""}, + {"proxy-authorization", ""}, + {"range", ""}, + {"referer", ""}, + {"refresh", ""}, + {"retry-after", ""}, + {"server", ""}, + {"set-cookie", ""}, + {"strict-transport-security", ""}, + {"transfer-encoding", ""}, + {"user-agent", ""}, + {"vary", ""}, + {"via", ""}, + {"www-authenticate", ""} + }; + + return table; +} + +std::vector<HeaderField>::const_iterator FieldLookupTable::findInStaticPart(const HeaderField &field, CompareMode mode) +{ + const auto &table = staticPart(); + const auto acceptPos = table.begin() + 18; + if (field.name == "accept") { + if (mode == CompareMode::nameAndValue && field.value != "") + return table.end(); + return acceptPos; + } + + auto predicate = [mode](const HeaderField &lhs, const HeaderField &rhs) { + const int cmp = compare(lhs.name, rhs.name); + if (cmp) + return cmp < 0; + else if (mode == CompareMode::nameAndValue) + return compare(lhs.value, rhs.value) < 0; + return false; + }; + + const auto staticPos = std::lower_bound(table.begin(), acceptPos, field, predicate); + if (staticPos != acceptPos) + return staticPos; + + return std::lower_bound(acceptPos + 1, table.end(), field, predicate); +} + } QT_END_NAMESPACE diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h index 5eaccbffce..587d86f09c 100644 --- a/src/network/access/http2/hpacktable_p.h +++ b/src/network/access/http2/hpacktable_p.h @@ -173,6 +173,8 @@ public: bool updateDynamicTableSize(quint32 size); void setMaxDynamicTableSize(quint32 size); + static const std::vector<HeaderField> &staticPart(); + private: // Table's maximum size is controlled // by SETTINGS_HEADER_TABLE_SIZE (HTTP/2, 6.5.2). @@ -225,9 +227,16 @@ private: quint32 indexOfChunk(const Chunk *chunk) const; quint32 keyToIndex(const SearchEntry &key) const; + enum class CompareMode { + nameOnly, + nameAndValue + }; + + static std::vector<HeaderField>::const_iterator findInStaticPart(const HeaderField &field, CompareMode mode); + mutable QByteArray dummyDst; - Q_DISABLE_COPY_MOVE(FieldLookupTable); + Q_DISABLE_COPY_MOVE(FieldLookupTable) }; } diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index df4e034d97..b1e9853a50 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -265,7 +265,7 @@ qint64 QNetworkReplyWasmImpl::readData(char *data, qint64 maxlen) Q_D(QNetworkReplyWasmImpl); qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size() - d->downloadBufferReadPosition)); - memcpy(data, d->downloadBuffer.constData(), howMuch); + memcpy(data, d->downloadBuffer.constData() + d->downloadBufferReadPosition, howMuch); d->downloadBufferReadPosition += howMuch; return howMuch; diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp index 496979913b..1f2de44859 100644 --- a/src/network/ssl/qocspresponse.cpp +++ b/src/network/ssl/qocspresponse.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE /*! \class QOcspResponse - \brief This class represents Online Certificate Status Protocol response + \brief This class represents Online Certificate Status Protocol response. \since 5.13 \ingroup network @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE received by the client-side socket during the TLS handshake. QSslSocket must be configured with OCSP stapling enabled. - \sa QSslSocket, QSslSocket::ocspResponse(), certificateStatus(), + \sa QSslSocket, QSslSocket::ocspResponses(), certificateStatus(), revocationReason(), responder(), subject(), QOcspCertificateStatus, QOcspRevocationReason, QSslConfiguration::setOcspStaplingEnabled(), QSslConfiguration::ocspStaplingEnabled(), QSslConfiguration::peerCertificate() @@ -126,14 +126,14 @@ QOcspResponse::QOcspResponse() Creates a new response, the copy of \a other. */ -QOcspResponse::QOcspResponse(const QOcspResponse &other) = default; +QOcspResponse::QOcspResponse(const QOcspResponse &) = default; /*! \since 5.13 Move-constructs a QOcspResponse instance from \a other. */ -QOcspResponse::QOcspResponse(QOcspResponse &&other) Q_DECL_NOTHROW = default; +QOcspResponse::QOcspResponse(QOcspResponse &&) Q_DECL_NOTHROW = default; /*! \since 5.13 @@ -147,14 +147,14 @@ QOcspResponse::~QOcspResponse() = default; Assigns \a other to the response and returns a reference to this response. */ -QOcspResponse &QOcspResponse::operator=(const QOcspResponse &other) = default; +QOcspResponse &QOcspResponse::operator=(const QOcspResponse &) = default; /*! \since 5.13 Move-assigns \a other to this QOcspResponse instance. */ -QOcspResponse &QOcspResponse::operator=(QOcspResponse &&other) Q_DECL_NOTHROW = default; +QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) Q_DECL_NOTHROW = default; /*! \fn void QOcspResponse::swap(QOcspResponse &other) diff --git a/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/src/platformsupport/services/genericunix/qgenericunixservices.cpp index 7fff50b2a1..734bdcaf75 100644 --- a/src/platformsupport/services/genericunix/qgenericunixservices.cpp +++ b/src/platformsupport/services/genericunix/qgenericunixservices.cpp @@ -179,7 +179,15 @@ static inline bool checkNeedPortalSupport() return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP"); } -static inline bool xdgDesktopPortalOpenFile(const QUrl &url) +static inline bool isPortalReturnPermanent(const QDBusError &error) +{ + // A service unknown error isn't permanent, it just indicates that we + // should fall back to the regular way. This check includes + // QDBusError::NoError. + return error.type() != QDBusError::ServiceUnknown; +} + +static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) { // DBus signature: // OpenFile (IN s parent_window, @@ -198,23 +206,22 @@ static inline bool xdgDesktopPortalOpenFile(const QUrl &url) QLatin1String("org.freedesktop.portal.OpenURI"), QLatin1String("OpenFile")); - QDBusUnixFileDescriptor descriptor(fd); - qt_safe_close(fd); + QDBusUnixFileDescriptor descriptor; + descriptor.giveFileDescriptor(fd); // FIXME parent_window_id and handle writable option message << QString() << QVariant::fromValue(descriptor) << QVariantMap(); - QDBusPendingReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(message); - return !reply.isError(); + return QDBusConnection::sessionBus().call(message); } #else Q_UNUSED(url) #endif - return false; + return QDBusMessage::createError(QDBusError::InternalError, qt_error_string()); } -static inline bool xdgDesktopPortalOpenUrl(const QUrl &url) +static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url) { // DBus signature: // OpenURI (IN s parent_window, @@ -234,11 +241,10 @@ static inline bool xdgDesktopPortalOpenUrl(const QUrl &url) // FIXME parent_window_id and handle writable option message << QString() << url.toString() << QVariantMap(); - QDBusPendingReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(message); - return !reply.isError(); + return QDBusConnection::sessionBus().call(message); } -static inline bool xdgDesktopPortalSendEmail(const QUrl &url) +static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) { // DBus signature: // ComposeEmail (IN s parent_window, @@ -281,8 +287,7 @@ static inline bool xdgDesktopPortalSendEmail(const QUrl &url) // FIXME parent_window_id message << QString() << options; - QDBusPendingReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(message); - return !reply.isError(); + return QDBusConnection::sessionBus().call(message); } #endif // QT_CONFIG(dbus) @@ -296,15 +301,23 @@ bool QGenericUnixServices::openUrl(const QUrl &url) { if (url.scheme() == QLatin1String("mailto")) { #if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) - return xdgDesktopPortalSendEmail(url); + if (checkNeedPortalSupport()) { + QDBusError error = xdgDesktopPortalSendEmail(url); + if (isPortalReturnPermanent(error)) + return !error.isValid(); + + // service not running, fall back + } #endif return openDocument(url); } #if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) - return xdgDesktopPortalOpenUrl(url); + if (checkNeedPortalSupport()) { + QDBusError error = xdgDesktopPortalOpenUrl(url); + if (isPortalReturnPermanent(error)) + return !error.isValid(); + } #endif if (m_webBrowser.isEmpty() && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) { @@ -317,8 +330,11 @@ bool QGenericUnixServices::openUrl(const QUrl &url) bool QGenericUnixServices::openDocument(const QUrl &url) { #if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) - return xdgDesktopPortalOpenFile(url); + if (checkNeedPortalSupport()) { + QDBusError error = xdgDesktopPortalOpenFile(url); + if (isPortalReturnPermanent(error)) + return !error.isValid(); + } #endif if (m_documentLauncher.isEmpty() && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) { diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp index 6b9687c22d..57fe7c2fa2 100644 --- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp @@ -70,7 +70,9 @@ void QComposeInputContext::ensureInitialized() } m_initialized = true; - const char *const locale = setlocale(LC_CTYPE, ""); + const char *locale = setlocale(LC_CTYPE, ""); + if (!locale) + locale = setlocale(LC_CTYPE, nullptr); qCDebug(lcXkbCompose) << "detected locale (LC_CTYPE):" << locale; m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index d86e935788..9c705616ba 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing"); -Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse"); +Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg); Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen"); // diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 17063f6e92..5309449dce 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -137,22 +137,13 @@ { if ((self = [super initWithFrame:NSZeroRect])) { m_platformWindow = platformWindow; - m_buttons = Qt::NoButton; - m_acceptedMouseDowns = Qt::NoButton; - m_frameStrutButtons = Qt::NoButton; m_sendKeyEvent = false; - m_sendUpAsRightButton = false; m_inputSource = nil; - m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self]; m_resendKeyEvent = false; - m_scrolling = false; m_updatingDrag = false; m_currentlyInterpretedKeyEvent = nil; - m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, platformWindow->window(), - "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); self.focusRingType = NSFocusRingTypeNone; - self.cursor = nil; self.previousSuperview = nil; self.previousWindow = nil; diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm index 1c38c5326c..002cb3279e 100644 --- a/src/plugins/platforms/cocoa/qnsview_dragging.mm +++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm @@ -57,9 +57,9 @@ NSFilesPromisePboardType, NSInkTextPboardType, NSMultipleTextSelectionPboardType, mimeTypeGeneric]]; - // Add custom types supported by the application. + // Add custom types supported by the application for (const QString &customType : qt_mac_enabledDraggedTypes()) - [supportedTypes addObject:customType.toNSString()]; + [supportedTypes addObject:customType.toNSString()]; [self registerForDraggedTypes:supportedTypes]; } @@ -79,11 +79,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin return target->mapFromGlobal(source->mapToGlobal(point)); } -- (NSDragOperation)draggingSession:(NSDraggingSession *)session - sourceOperationMaskForDraggingContext:(NSDraggingContext)context +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { Q_UNUSED(session); Q_UNUSED(context); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions()); } @@ -134,30 +134,29 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (pixmapCursor.isNull()) { switch (response.acceptedAction()) { - case Qt::CopyAction: - nativeCursor = [NSCursor dragCopyCursor]; - break; - case Qt::LinkAction: - nativeCursor = [NSCursor dragLinkCursor]; - break; - case Qt::IgnoreAction: - // Uncomment the next lines if forbiden cursor wanted on non droppable targets. - /*nativeCursor = [NSCursor operationNotAllowedCursor]; - break;*/ - case Qt::MoveAction: - default: - nativeCursor = [NSCursor arrowCursor]; - break; + case Qt::CopyAction: + nativeCursor = [NSCursor dragCopyCursor]; + break; + case Qt::LinkAction: + nativeCursor = [NSCursor dragLinkCursor]; + break; + case Qt::IgnoreAction: + // Uncomment the next lines if forbidden cursor is wanted on undroppable targets. + /*nativeCursor = [NSCursor operationNotAllowedCursor]; + break;*/ + case Qt::MoveAction: + default: + nativeCursor = [NSCursor arrowCursor]; + break; } - } - else { + } else { NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; [nsimage release]; } - // change the cursor + // Change the cursor [nativeCursor set]; // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor @@ -169,39 +168,33 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (m_updatingDrag) return; - const QPoint mousePos(QCursor::pos()); - CGEventRef moveEvent(CGEventCreateMouseEvent( - NULL, kCGEventMouseMoved, - CGPointMake(mousePos.x(), mousePos.y()), + QCFType<CGEventRef> moveEvent = CGEventCreateMouseEvent( + nullptr, kCGEventMouseMoved, QCursor::pos().toCGPoint(), kCGMouseButtonLeft // ignored - )); + ); CGEventPost(kCGHIDEventTap, moveEvent); - CFRelease(moveEvent); } -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { - return [self handleDrag : sender]; + return [self handleDrag:(QEvent::DragEnter) sender:sender]; } -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { - m_updatingDrag = true; - const NSDragOperation ret([self handleDrag : sender]); - m_updatingDrag = false; - - return ret; + QScopedValueRollback<bool> rollback(m_updatingDrag, true); + return [self handleDrag:(QEvent::DragMove) sender:sender]; } // Sends drag update to Qt, return the action -- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender +- (NSDragOperation)handleDrag:(QEvent::Type)dragType sender:(id<NSDraggingInfo>)sender { if (!m_platformWindow) return NSDragOperationNone; - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint(); + + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask); QWindow *target = findEventTargetWindow(m_platformWindow->window()); if (!target) @@ -209,7 +202,12 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags]; const auto buttons = currentlyPressedMouseButtons(); - const auto point = mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint); + const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint); + + if (dragType == QEvent::DragEnter) + qCDebug(lcQpaMouse) << dragType << self << "at" << windowPoint; + else + qCDebug(lcQpaMouse) << dragType << "at" << windowPoint << "with" << buttons; QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); @@ -219,7 +217,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin point, qtAllowed, buttons, modifiers); [self updateCursorFromDragResponse:response drag:nativeDrag]; } else { - QCocoaDropData mimeData([sender draggingPasteboard]); + QCocoaDropData mimeData(sender.draggingPasteboard); response = QWindowSystemInterface::handleDrag(target, &mimeData, point, qtAllowed, buttons, modifiers); } @@ -227,7 +225,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin return qt_mac_mapDropAction(response.acceptedAction()); } -- (void)draggingExited:(id <NSDraggingInfo>)sender +- (void)draggingExited:(id<NSDraggingInfo>)sender { if (!m_platformWindow) return; @@ -236,17 +234,18 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (!target) return; - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint(); + + qCDebug(lcQpaMouse) << QEvent::DragLeave << self << "at" << windowPoint; // Send 0 mime data to indicate drag exit QWindowSystemInterface::handleDrag(target, nullptr, - mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), + mapWindowCoordinates(m_platformWindow->window(), target, windowPoint), Qt::IgnoreAction, Qt::NoButton, Qt::NoModifier); } -// called on drop, send the drop to Qt and return if it was accepted. -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender +// Called on drop, send the drop to Qt and return if it was accepted +- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { if (!m_platformWindow) return false; @@ -255,31 +254,31 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (!target) return false; - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint(); + + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask); QPlatformDropQtResponse response(false, Qt::IgnoreAction); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags]; const auto buttons = currentlyPressedMouseButtons(); - const auto point = mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint); + const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint); + + qCDebug(lcQpaMouse) << QEvent::Drop << "at" << windowPoint << "with" << buttons; if (nativeDrag->currentDrag()) { // The drag was started from within the application response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), point, qtAllowed, buttons, modifiers); } else { - QCocoaDropData mimeData([sender draggingPasteboard]); + QCocoaDropData mimeData(sender.draggingPasteboard); response = QWindowSystemInterface::handleDrop(target, &mimeData, point, qtAllowed, buttons, modifiers); } return response.isAccepted(); } -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint - operation:(NSDragOperation)operation +- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { Q_UNUSED(session); Q_UNUSED(screenPoint); @@ -295,6 +294,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation)); m_buttons = currentlyPressedMouseButtons(); + + qCDebug(lcQpaMouse) << "Drag session" << session << "ended, with" << m_buttons; } @end diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm index 49c94f18db..6e3cff2b48 100644 --- a/src/plugins/platforms/cocoa/qnsview_mouse.mm +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -93,6 +93,7 @@ - (void)resetMouseButtons { + qCDebug(lcQpaMouse) << "Reseting mouse buttons"; m_buttons = Qt::NoButton; m_frameStrutButtons = Qt::NoButton; } @@ -145,6 +146,9 @@ QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); ulong timestamp = [theEvent timestamp] * 1000; + + auto eventType = cocoaEvent2QtMouseEvent(theEvent); + qCInfo(lcQpaMouse) << "Frame-strut" << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window; QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); } @end @@ -153,6 +157,19 @@ - (void)initMouse { + m_buttons = Qt::NoButton; + m_acceptedMouseDowns = Qt::NoButton; + m_frameStrutButtons = Qt::NoButton; + + m_scrolling = false; + self.cursor = nil; + + m_sendUpAsRightButton = false; + m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, m_platformWindow->window(), + "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); + + m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self]; + NSUInteger trackingOptions = NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingCursorUpdate; @@ -241,6 +258,11 @@ button = Qt::RightButton; const auto eventType = cocoaEvent2QtMouseEvent(theEvent); + if (eventType == QEvent::MouseMove) + qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons; + else + qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons; + QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, buttons, button, eventType, modifiers); @@ -446,16 +468,16 @@ - (void)cursorUpdate:(NSEvent *)theEvent { - qCDebug(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor; - // Note: We do not get this callback when moving from a subview that // uses the legacy cursorRect API, so the cursor is reset to the arrow // cursor. See rdar://34183708 - if (self.cursor) + if (self.cursor && self.cursor != NSCursor.currentCursor) { + qCInfo(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor; [self.cursor set]; - else + } else { [super cursorUpdate:theEvent]; + } } - (void)mouseMovedImpl:(NSEvent *)theEvent @@ -510,6 +532,8 @@ QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); + + qCInfo(lcQpaMouse) << QEvent::Enter << self << "at" << windowPoint << "with" << currentlyPressedMouseButtons(); QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); } @@ -528,6 +552,7 @@ if (!m_platformWindow->isContentView()) return; + qCInfo(lcQpaMouse) << QEvent::Leave << self; QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); m_platformWindow->m_enterLeaveTargetWindow = 0; } @@ -626,8 +651,10 @@ // "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective. bool isInverted = [theEvent isDirectionInvertedFromDevice]; - qCDebug(lcQpaMouse) << "scroll wheel @ window pos" << qt_windowPoint << "delta px" << pixelDelta - << "angle" << angleDelta << "phase" << phase << (isInverted ? "inverted" : ""); + qCInfo(lcQpaMouse).nospace() << phase << " at " << qt_windowPoint + << " pixelDelta=" << pixelDelta << " angleDelta=" << angleDelta + << (isInverted ? " inverted=true" : ""); + QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, m_currentWheelModifiers, phase, source, isInverted); } diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index 84694c7b9f..049eb1c35e 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -50,6 +50,7 @@ // External mode.usage: // // var config = { +// canvasElements : [$("canvas-id")], // showLoader: function() { // loader.style.display = 'block' // canvas.style.display = 'hidden' @@ -69,6 +70,8 @@ // One or more HTML elements. QtLoader will display loader elements // on these while loading the applicaton, and replace the loader with a // canvas on load complete. +// canvasElements : [canvas-element, ...] +// One or more canvas elements. // showLoader : function(status, containerElement) // Optional loading element constructor function. Implement to create // a custom loading screen. This function may be called multiple times, @@ -146,8 +149,25 @@ function QtLoader(config) while (element.firstChild) element.removeChild(element.firstChild); } - // Set default state handler functions if needed + function createCanvas() { + var canvas = document.createElement("canvas"); + canvas.className = "QtCanvas"; + canvas.style.height = "100%"; + canvas.style.width = "100%"; + + // Set contentEditable in order to enable clipboard events; hide the resulting focus frame. + canvas.contentEditable = true; + canvas.style.outline = "0px solid transparent"; + canvas.style.cursor = "default"; + + return canvas; + } + + // Set default state handler functions and create canvases if needed if (config.containerElements !== undefined) { + + config.canvasElements = config.containerElements.map(createCanvas); + config.showError = config.showError || function(errorText, container) { removeChildren(container); var errorTextElement = document.createElement("text"); @@ -164,12 +184,8 @@ function QtLoader(config) return loadingText; }; - config.showCanvas = config.showCanvas || function(container) { + config.showCanvas = config.showCanvas || function(canvas, container) { removeChildren(container); - var canvas = document.createElement("canvas"); - canvas.className = "QtCanvas" - canvas.style = "height: 100%; width: 100%;" - return canvas; } config.showExit = config.showExit || function(crashed, exitCode, container) { @@ -384,6 +400,8 @@ function QtLoader(config) Module.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'}); + Module.qtCanvasElements = config.canvasElements; + config.restart = function() { // Restart by reloading the page. This will wipe all state which means @@ -438,19 +456,17 @@ function QtLoader(config) } function setCanvasContent() { - var firstCanvas; if (config.containerElements === undefined) { - firstCanvas = config.showCanvas(); - } else { - for (container of config.containerElements) { - var canvasElement = config.showCanvas(container); - container.appendChild(canvasElement); - } - firstCanvas = config.containerElements[0].firstChild; + if (config.showCanvas !== undefined) + config.showCanvas(); + return; } - if (Module.canvas === undefined) { - Module.canvas = firstCanvas; + for (var i = 0; i < config.containerElements.length; ++i) { + var container = config.containerElements[i]; + var canvas = config.canvasElements[i]; + config.showCanvas(canvas, container); + container.appendChild(canvas); } } diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index ec058f05dd..7a7b253b19 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -67,23 +67,42 @@ static void qClipboardPromiseResolve(emscripten::val something) pasteClipboardData(emscripten::val("text/plain"), something); } -static void qClipboardCopyTo(val event) +static void qClipboardCutTo(val event) { - val target = event["target"]; - val clipboard = event["clipboardData"]; + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) { + // Send synthetic Ctrl+X to make the app cut data to Qt's clipboard + QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( + 0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X"); + } val module = val::global("Module"); val clipdata = module.call<val>("getClipboardData"); val clipFormat = module.call<val>("getClipboardFormat"); - clipboard.call<void>("setData", clipFormat, clipdata); - target.call<void>("preventDefault"); + event["clipboardData"].call<void>("setData", clipFormat, clipdata); + event.call<void>("preventDefault"); } -static void qClipboardPasteTo(val event) +static void qClipboardCopyTo(val event) { - val target = event["clipboardData"]; + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) { + // Send synthetic Ctrl+C to make the app copy data to Qt's clipboard + QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( + 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C"); + } + val module = val::global("Module"); val clipdata = module.call<val>("getClipboardData"); + val clipFormat = module.call<val>("getClipboardFormat"); + event["clipboardData"].call<void>("setData", clipFormat, clipdata); + event.call<void>("preventDefault"); +} + +static void qClipboardPasteTo(val event) +{ + bool hasClipboardApi = QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi; + val clipdata = hasClipboardApi ? + val::global("Module").call<val>("getClipboardData") : + event["clipboardData"].call<val>("getData", std::string("text")); const std::string data = clipdata.as<std::string>(); if (data.length() > 0) { @@ -99,13 +118,16 @@ EMSCRIPTEN_BINDINGS(clipboard_module) { function("getClipboardFormat", &getClipboardFormat); function("pasteClipboardData", &pasteClipboardData); function("qClipboardPromiseResolve", &qClipboardPromiseResolve); + function("qClipboardCutTo", &qClipboardCutTo); function("qClipboardCopyTo", &qClipboardCopyTo); function("qClipboardPasteTo", &qClipboardPasteTo); } -QWasmClipboard::QWasmClipboard() : - hasClipboardApi(false) +QWasmClipboard::QWasmClipboard() { + val clipboard = val::global("navigator")["clipboard"]; + hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined()); + initClipboardEvents(); } @@ -157,29 +179,32 @@ void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData) void QWasmClipboard::initClipboardEvents() { - val navigator = val::global("navigator"); - val permissions = navigator["permissions"]; - val clipboard = navigator["clipboard"]; + if (!hasClipboardApi) + return; - hasClipboardApi = (!clipboard.isUndefined()); - if (hasClipboardApi) { - val readPermissionsMap = val::object(); - readPermissionsMap.set("name", val("clipboard-read")); - permissions.call<val>("query", readPermissionsMap); + val permissions = val::global("navigator")["permissions"]; + val readPermissionsMap = val::object(); + readPermissionsMap.set("name", val("clipboard-read")); + permissions.call<val>("query", readPermissionsMap); - val writePermissionsMap = val::object(); - writePermissionsMap.set("name", val("clipboard-write")); - permissions.call<val>("query", writePermissionsMap); - - } else { + val writePermissionsMap = val::object(); + writePermissionsMap.set("name", val("clipboard-write")); + permissions.call<val>("query", writePermissionsMap); +} - val window = val::global("window"); - window.call<void>("addEventListener", std::string("paste"), - val::module_property("qClipboardPasteTo")); +void QWasmClipboard::installEventHandlers(const QString &canvasId) +{ + if (hasClipboardApi) + return; - window.call<void>("addEventListener", std::string("copy"), - val::module_property("qClipboardCopyTo")); - } + // Fallback path for browsers which do not support direct clipboard access + val canvas = val::global(canvasId.toUtf8().constData()); + canvas.call<void>("addEventListener", std::string("cut"), + val::module_property("qClipboardCutTo")); + canvas.call<void>("addEventListener", std::string("copy"), + val::module_property("qClipboardCopyTo")); + canvas.call<void>("addEventListener", std::string("paste"), + val::module_property("qClipboardPasteTo")); } void QWasmClipboard::readTextFromClipboard() diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h index e64b2e5007..00aae8fead 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.h +++ b/src/plugins/platforms/wasm/qwasmclipboard.h @@ -51,6 +51,7 @@ public: static void qWasmClipboardPaste(QMimeData *mData); void initClipboardEvents(); + void installEventHandlers(const QString &canvasId); bool hasClipboardApi; void readTextFromClipboard(); void writeTextToClipboard(); diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 3dc6b7d2f3..90cc20789d 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -56,8 +56,9 @@ QWasmCompositedWindow::QWasmCompositedWindow() { } -QWasmCompositor::QWasmCompositor() - : m_frameBuffer(nullptr) +QWasmCompositor::QWasmCompositor(QWasmScreen *screen) + :QObject(screen) + , m_frameBuffer(nullptr) , m_blitter(new QOpenGLTextureBlitter) , m_needComposit(false) , m_inFlush(false) @@ -107,11 +108,6 @@ void QWasmCompositor::removeWindow(QWasmWindow *window) notifyTopWindowChanged(window); } -void QWasmCompositor::setScreen(QWasmScreen *screen) -{ - m_screen = screen; -} - void QWasmCompositor::setVisible(QWasmWindow *window, bool visible) { QWasmCompositedWindow &compositedWindow = m_compositedWindows[window]; @@ -654,7 +650,7 @@ void QWasmCompositor::frame() m_needComposit = false; - if (m_windowStack.empty() || !m_screen) + if (m_windowStack.empty() || !screen()) return; QWasmWindow *someWindow = nullptr; @@ -673,7 +669,7 @@ void QWasmCompositor::frame() if (m_context.isNull()) { m_context.reset(new QOpenGLContext()); //mContext->setFormat(mScreen->format()); - m_context->setScreen(m_screen->screen()); + m_context->setScreen(screen()->screen()); m_context->create(); } @@ -682,8 +678,8 @@ void QWasmCompositor::frame() if (!m_blitter->isCreated()) m_blitter->create(); - qreal dpr = m_screen->devicePixelRatio(); - glViewport(0, 0, m_screen->geometry().width() * dpr, m_screen->geometry().height() * dpr); + qreal dpr = screen()->devicePixelRatio(); + glViewport(0, 0, screen()->geometry().width() * dpr, screen()->geometry().height() * dpr); m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0); m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -697,7 +693,7 @@ void QWasmCompositor::frame() if (!compositedWindow.visible) continue; - drawWindow(m_blitter.data(), m_screen, window); + drawWindow(m_blitter.data(), screen(), window); } m_blitter->release(); @@ -719,3 +715,8 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window) requestRedraw(); QWindowSystemInterface::handleWindowActivated(window->window()); } + +QWasmScreen *QWasmCompositor::screen() +{ + return static_cast<QWasmScreen *>(parent()); +} diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 4e5ed46cec..ed6facdcc3 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -62,7 +62,7 @@ class QWasmCompositor : public QObject { Q_OBJECT public: - QWasmCompositor(); + QWasmCompositor(QWasmScreen *screen); ~QWasmCompositor(); enum QWasmSubControl { @@ -103,7 +103,6 @@ public: void addWindow(QWasmWindow *window, QWasmWindow *parentWindow = nullptr); void removeWindow(QWasmWindow *window); - void setScreen(QWasmScreen *screen); void setVisible(QWasmWindow *window, bool visible); void raise(QWasmWindow *window); @@ -129,8 +128,7 @@ private slots: void frame(); private: - void createFrameBuffer(); - void flushCompletedCallback(int32_t); + QWasmScreen *screen(); void notifyTopWindowChanged(QWasmWindow *window); void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); @@ -142,7 +140,6 @@ private: QImage *m_frameBuffer; QScopedPointer<QOpenGLContext> m_context; QScopedPointer<QOpenGLTextureBlitter> m_blitter; - QWasmScreen *m_screen; QHash<QWasmWindow *, QWasmCompositedWindow> m_compositedWindows; QList<QWasmWindow *> m_windowStack; diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp index 744b160dd1..2b3f37300d 100644 --- a/src/plugins/platforms/wasm/qwasmcursor.cpp +++ b/src/plugins/platforms/wasm/qwasmcursor.cpp @@ -28,20 +28,21 @@ ****************************************************************************/ #include "qwasmcursor.h" +#include "qwasmscreen.h" #include <QtCore/qdebug.h> +#include <QtGui/qwindow.h> #include <emscripten/emscripten.h> #include <emscripten/bind.h> void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window) { - if (windowCursor == nullptr) + if (!windowCursor || !window) + return; + QScreen *screen = window->screen(); + if (!screen) return; - - // FIXME: The HTML5 plugin sets the cursor on the native canvas; when using multiple windows - // multiple cursors need to be managed taking mouse postion and stacking into account. - Q_UNUSED(window); // Bitmap and custom cursors are not implemented (will fall back to "auto") if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor) @@ -52,8 +53,9 @@ void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window) if (htmlCursorName.isEmpty()) htmlCursorName = "auto"; - // Set cursor on the main canvas - emscripten::val canvasStyle = emscripten::val::module_property("canvas")["style"]; + // Set cursor on the canvas + QString canvasId = QWasmScreen::get(screen)->canvasId(); + emscripten::val canvasStyle = emscripten::val::global(canvasId.toUtf8().constData())["style"]; canvasStyle.set("cursor", emscripten::val(htmlCursorName.constData())); } diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 3fc8f600a1..14222da807 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -320,41 +320,35 @@ EMSCRIPTEN_BINDINGS(mouse_module) { function("mouseWheelEvent", &mouseWheelEvent); } -QWasmEventTranslator::QWasmEventTranslator(QObject *parent) - : QObject(parent) +QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen) + : QObject(screen) , draggedWindow(nullptr) , lastWindow(nullptr) , pressedButtons(Qt::NoButton) , resizeMode(QWasmWindow::ResizeNone) { - emscripten_set_keydown_callback(0, (void *)this, 1, &keyboard_cb); - emscripten_set_keyup_callback(0, (void *)this, 1, &keyboard_cb); - - emscripten_set_mousedown_callback(0, (void *)this, 1, &mouse_cb); - emscripten_set_mouseup_callback(0, (void *)this, 1, &mouse_cb); - emscripten_set_mousemove_callback(0, (void *)this, 1, &mouse_cb); - - emscripten_set_focus_callback(0, (void *)this, 1, &focus_cb); - - emscripten_set_wheel_callback(0, (void *)this, 1, &wheel_cb); - touchDevice = new QTouchDevice; touchDevice->setType(QTouchDevice::TouchScreen); touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition); QWindowSystemInterface::registerTouchDevice(touchDevice); - emscripten_set_touchstart_callback("#canvas", (void *)this, 1, &touchCallback); - emscripten_set_touchend_callback("#canvas", (void *)this, 1, &touchCallback); - emscripten_set_touchmove_callback("#canvas", (void *)this, 1, &touchCallback); - emscripten_set_touchcancel_callback("#canvas", (void *)this, 1, &touchCallback); + initEventHandlers(); +} + +void QWasmEventTranslator::initEventHandlers() +{ + qDebug() << "QWasmEventTranslator::initEventHandlers"; + + QByteArray _canvasId = screen()->canvasId().toUtf8(); + const char *canvasId = _canvasId.constData(); // The Platform Detect: expand coverage and move as needed enum Platform { GenericPlatform, MacOSPlatform }; - Platform platform = Platform(emscripten::val::global("navigator")["platform"] - .call<bool>("includes", emscripten::val("Mac"))); + Platform platform = Platform(emscripten::val::global("navigator")["platform"] + .call<bool>("includes", emscripten::val("Mac"))); g_usePlatformMacCtrlMetaSwitching = (platform == MacOSPlatform); if (platform == MacOSPlatform) { @@ -362,11 +356,30 @@ QWasmEventTranslator::QWasmEventTranslator(QObject *parent) if (emscripten::val::global("window")["safari"].isUndefined()) { - emscripten::val::global("canvas").call<void>("addEventListener", + emscripten::val::global(canvasId).call<void>("addEventListener", std::string("wheel"), val::module_property("mouseWheelEvent")); } } + + emscripten_set_keydown_callback(canvasId, (void *)this, 1, &keyboard_cb); + emscripten_set_keyup_callback(canvasId, (void *)this, 1, &keyboard_cb); + + emscripten_set_mousedown_callback(canvasId, (void *)this, 1, &mouse_cb); + emscripten_set_mouseup_callback(canvasId, (void *)this, 1, &mouse_cb); + emscripten_set_mousemove_callback(canvasId, (void *)this, 1, &mouse_cb); + + emscripten_set_focus_callback(canvasId, (void *)this, 1, &focus_cb); + + emscripten_set_wheel_callback(canvasId, (void *)this, 1, &wheel_cb); + + emscripten_set_touchstart_callback(canvasId, (void *)this, 1, &touchCallback); + emscripten_set_touchend_callback(canvasId, (void *)this, 1, &touchCallback); + emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback); + emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback); + + emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize + } template <typename Event> @@ -415,6 +428,11 @@ int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEve return accepted ? 1 : 0; } +QWasmScreen *QWasmEventTranslator::screen() +{ + return static_cast<QWasmScreen *>(parent()); +} + Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey) { Qt::Key qtKey = Qt::Key_unknown; @@ -531,14 +549,14 @@ void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode, void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent) { auto timestamp = mouseEvent->timestamp; - QPoint point(mouseEvent->canvasX, mouseEvent->canvasY); + QPoint point(mouseEvent->targetX, mouseEvent->targetY); QEvent::Type buttonEventType = QEvent::None; Qt::MouseButton button = translateMouseButton(mouseEvent->button); Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent); - QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5); + QWindow *window2 = screen()->compositor()->windowAt(point, 5); if (window2 != nullptr) lastWindow = window2; @@ -635,6 +653,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh { Q_UNUSED(eventType) + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData); EmscriptenMouseEvent mouseEvent = wheelEvent->mouse; int scrollFactor = 0; @@ -658,7 +677,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh auto timestamp = mouseEvent.timestamp; QPoint globalPoint(mouseEvent.canvasX, mouseEvent.canvasY); - QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5); + QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5); QPoint localPoint(globalPoint.x() - window2->geometry().x(), globalPoint.y() - window2->geometry().y()); @@ -676,6 +695,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) { + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData); QList<QWindowSystemInterface::TouchPoint> touchPointList; touchPointList.reserve(touchEvent->numTouches); QWindow *window2; @@ -685,7 +705,7 @@ int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEven const EmscriptenTouchPoint *touches = &touchEvent->touches[i]; QPoint point(touches->canvasX, touches->canvasY); - window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5); + window2 = eventTranslator->screen()->compositor()->windowAt(point, 5); QWindowSystemInterface::TouchPoint touchPoint; @@ -832,6 +852,14 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa return 0; QFlags<Qt::KeyboardModifier> mods = translateKeyboardEventModifier(keyEvent); + + // Clipboard fallback path: cut/copy/paste are handled by clipboard event + // handlers if direct clipboard access is not available. + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier && + (qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) { + return 0; + } + bool accepted = false; if (keyType == QEvent::KeyPress && @@ -857,4 +885,19 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa return accepted; } +int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData) +{ + Q_UNUSED(e) + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData); + + if (eventType == EMSCRIPTEN_EVENT_RESIZE) { + // This resize event is called when the HTML window is resized. Depending + // on the page layout the the canvas might also have been resized, so we + // update the Qt screen size (and canvas render size). + eventTranslator->screen()->updateQScreenAndCanvasRenderSize(); + } + + return 0; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h index 25e2a630b6..ba08dce946 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.h +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h @@ -48,7 +48,7 @@ class QWasmEventTranslator : public QObject public: - explicit QWasmEventTranslator(QObject *parent = 0); + explicit QWasmEventTranslator(QWasmScreen *screen); static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); @@ -57,12 +57,15 @@ public: static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); + static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); + void processEvents(); + void initEventHandlers(); Q_SIGNALS: void getWindowAt(const QPoint &point, QWindow **window); private: - + QWasmScreen *screen(); Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey); template <typename Event> QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event); diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 6f96ec69da..1964cefdad 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -70,29 +70,35 @@ QWasmIntegration *QWasmIntegration::s_instance; QWasmIntegration::QWasmIntegration() : m_fontDb(nullptr), - m_compositor(new QWasmCompositor), - m_screen(new QWasmScreen(m_compositor)), m_eventDispatcher(nullptr), m_clipboard(new QWasmClipboard) { s_instance = this; - updateQScreenAndCanvasRenderSize(); - screenAdded(m_screen); - emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb); - - m_eventTranslator = new QWasmEventTranslator; + // We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases. + // Also check Module.canvas, which may be set if the emscripen or a custom loader is used. + emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements"); + emscripten::val canvas = val::module_property("canvas"); + + if (!qtCanvaseElements.isUndefined()) { + int screenCount = qtCanvaseElements["length"].as<int>(); + for (int i = 0; i < screenCount; ++i) { + emscripten::val canvas = qtCanvaseElements[i].as<emscripten::val>(); + QString canvasId = QString::fromStdString(canvas["id"].as<std::string>()); + addScreen(canvasId); + } + } else if (!canvas.isUndefined()){ + QString canvasId = QString::fromStdString(canvas["id"].as<std::string>()); + addScreen(canvasId); + } emscripten::val::global("window").set("onbeforeunload", val::module_property("browserBeforeUnload")); - } QWasmIntegration::~QWasmIntegration() { - delete m_compositor; - destroyScreen(m_screen); delete m_fontDb; - delete m_eventTranslator; + qDeleteAll(m_screens); s_instance = nullptr; } @@ -117,13 +123,15 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const { - return new QWasmWindow(window, m_compositor, m_backingStores.value(window)); + QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); + return new QWasmWindow(window, compositor, m_backingStores.value(window)); } QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const { #ifndef QT_NO_OPENGL - QWasmBackingStore *backingStore = new QWasmBackingStore(m_compositor, window); + QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); + QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window); m_backingStores.insert(window, backingStore); return backingStore; #else @@ -168,55 +176,22 @@ QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const return QPlatformIntegration::createPlatformTheme(name); } -int QWasmIntegration::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData) -{ - Q_UNUSED(e) - Q_UNUSED(userData) - - if (eventType == EMSCRIPTEN_EVENT_RESIZE) { - // This resize event is called when the HTML window is resized. Depending - // on the page layout the the canvas might also have been resized, so we - // update the Qt screen size (and canvas render size). - updateQScreenAndCanvasRenderSize(); - } - - return 0; -} - -static void set_canvas_size(double width, double height) +QPlatformClipboard* QWasmIntegration::clipboard() const { - emscripten::val canvas = emscripten::val::global("canvas"); - canvas.set("width", width); - canvas.set("height", height); + return m_clipboard; } -void QWasmIntegration::updateQScreenAndCanvasRenderSize() +QVector<QWasmScreen *> QWasmIntegration::screens() { - // The HTML canvas has two sizes: the CSS size and the canvas render size. - // The CSS size is determined according to standard CSS rules, while the - // render size is set using the "width" and "height" attributes. The render - // size must be set manually and is not auto-updated on CSS size change. - // Setting the render size to a value larger than the CSS size enables high-dpi - // rendering. - - double css_width; - double css_height; - emscripten_get_element_css_size(0, &css_width, &css_height); - QSizeF cssSize(css_width, css_height); - - QWasmScreen *screen = QWasmIntegration::get()->m_screen; - QSizeF canvasSize = cssSize * screen->devicePixelRatio(); - - set_canvas_size(canvasSize.width(), canvasSize.height()); - screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize())); - QWasmIntegration::get()->m_compositor->redrawWindowContent(); + return m_screens; } -QPlatformClipboard* QWasmIntegration::clipboard() const +void QWasmIntegration::addScreen(const QString &canvasId) { - if (!m_clipboard) - m_clipboard = new QWasmClipboard; - return m_clipboard; + QWasmScreen *screen = new QWasmScreen(canvasId); + m_clipboard->installEventHandlers(canvasId); + m_screens.append(screen); + screenAdded(screen); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 4c5aeb4ebc..5c0ac0b297 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -71,24 +71,20 @@ public: QPlatformTheme *createPlatformTheme(const QString &name) const override; QPlatformClipboard *clipboard() const override; - QWasmScreen *screen() { return m_screen; } - QWasmCompositor *compositor() { return m_compositor; } - QWasmEventTranslator *eventTranslator() { return m_eventTranslator; } + QVector<QWasmScreen *>screens(); QWasmClipboard *getWasmClipboard() { return m_clipboard; } static QWasmIntegration *get() { return s_instance; } static void QWasmBrowserExit(); - static void updateQScreenAndCanvasRenderSize(); private: + void addScreen(const QString &canvasId); + mutable QWasmFontDatabase *m_fontDb; - QWasmCompositor *m_compositor; - mutable QWasmScreen *m_screen; - mutable QWasmEventTranslator *m_eventTranslator; mutable QWasmEventDispatcher *m_eventDispatcher; - static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores; + QVector<QWasmScreen *> m_screens; mutable QWasmClipboard *m_clipboard; static QWasmIntegration *s_instance; }; diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 73af3d1878..ae43e2ebf0 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -28,7 +28,7 @@ ****************************************************************************/ #include "qwasmopenglcontext.h" - +#include "qwasmintegration.h" #include <EGL/egl.h> QT_BEGIN_NAMESPACE @@ -57,7 +57,7 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac emscripten_webgl_destroy_context(m_context); // Create new context - const char *canvasId = 0; // (use default canvas) FIXME: get the actual canvas from the surface. + const QString canvasId = QWasmScreen::get(surface->screen())->canvasId(); m_context = createEmscriptenContext(canvasId, m_requestedFormat); // Register context-lost callback. @@ -73,11 +73,11 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac return true; }; bool capture = true; - emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback); + emscripten_set_webglcontextlost_callback(canvasId.toLocal8Bit().constData(), this, capture, callback); } } -EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const char *canvasId, QSurfaceFormat format) +EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format) { EmscriptenWebGLContextAttributes attributes; emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes @@ -96,7 +96,7 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons attributes.depth = format.depthBufferSize() > 0; attributes.stencil = format.stencilBufferSize() > 0; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId, &attributes); + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId.toLocal8Bit().constData(), &attributes); return context; } diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index 9123100479..126b596a7e 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -51,7 +51,7 @@ public: private: void maybeRecreateEmscriptenContext(QPlatformSurface *surface); - static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const char *canvasId, QSurfaceFormat format); + static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format); bool m_contextLost = false; QSurfaceFormat m_requestedFormat; diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 37f1efadc6..a26cafa900 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -29,8 +29,10 @@ #include "qwasmscreen.h" #include "qwasmwindow.h" +#include "qwasmeventtranslator.h" #include "qwasmcompositor.h" #include <emscripten/bind.h> +#include <emscripten/val.h> #include <QtEglSupport/private/qeglconvenience_p.h> #ifndef QT_NO_OPENGL @@ -44,12 +46,13 @@ QT_BEGIN_NAMESPACE -QWasmScreen::QWasmScreen(QWasmCompositor *compositor) - : m_compositor(compositor) - , m_depth(32) - , m_format(QImage::Format_RGB32) +QWasmScreen::QWasmScreen(const QString &canvasId) + : m_canvasId(canvasId) + { - m_compositor->setScreen(this); + m_compositor = new QWasmCompositor(this); + m_eventTranslator = new QWasmEventTranslator(this); + updateQScreenAndCanvasRenderSize(); } QWasmScreen::~QWasmScreen() @@ -57,6 +60,31 @@ QWasmScreen::~QWasmScreen() } +QWasmScreen *QWasmScreen::get(QPlatformScreen *screen) +{ + return static_cast<QWasmScreen *>(screen); +} + +QWasmScreen *QWasmScreen::get(QScreen *screen) +{ + return get(screen->handle()); +} + +QWasmCompositor *QWasmScreen::compositor() +{ + return m_compositor; +} + +QWasmEventTranslator *QWasmScreen::eventTranslator() +{ + return m_eventTranslator; +} + +QString QWasmScreen::canvasId() const +{ + return m_canvasId; +} + QRect QWasmScreen::geometry() const { return m_geometry; @@ -82,6 +110,11 @@ qreal QWasmScreen::devicePixelRatio() const return qreal(htmlWindowDpr); } +QString QWasmScreen::name() const +{ + return m_canvasId; +} + QPlatformCursor *QWasmScreen::cursor() const { return const_cast<QWasmCursor *>(&m_cursor); @@ -114,4 +147,31 @@ void QWasmScreen::setGeometry(const QRect &rect) resizeMaximizedWindows(); } +void QWasmScreen::updateQScreenAndCanvasRenderSize() +{ + // The HTML canvas has two sizes: the CSS size and the canvas render size. + // The CSS size is determined according to standard CSS rules, while the + // render size is set using the "width" and "height" attributes. The render + // size must be set manually and is not auto-updated on CSS size change. + // Setting the render size to a value larger than the CSS size enables high-dpi + // rendering. + + QByteArray canvasId = m_canvasId.toUtf8(); + double css_width; + double css_height; + emscripten_get_element_css_size(canvasId.constData(), &css_width, &css_height); + QSizeF cssSize(css_width, css_height); + + QSizeF canvasSize = cssSize * devicePixelRatio(); + emscripten::val canvas = emscripten::val::global(canvasId.constData()); + canvas.set("width", canvasSize.width()); + canvas.set("height", canvasSize.height()); + + emscripten::val rect = canvas.call<emscripten::val>("getBoundingClientRect"); + QPoint position(rect["left"].as<int>(), rect["top"].as<int>()); + + setGeometry(QRect(position, cssSize.toSize())); + m_compositor->redrawWindowContent(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 3891db77bb..82d2a83edb 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -43,20 +43,28 @@ class QPlatformOpenGLContext; class QWasmWindow; class QWasmBackingStore; class QWasmCompositor; +class QWasmEventTranslator; class QOpenGLContext; class QWasmScreen : public QObject, public QPlatformScreen { Q_OBJECT public: - - QWasmScreen(QWasmCompositor *compositor); + QWasmScreen(const QString &canvasId); ~QWasmScreen(); + static QWasmScreen *get(QPlatformScreen *screen); + static QWasmScreen *get(QScreen *screen); + QString canvasId() const; + + QWasmCompositor *compositor(); + QWasmEventTranslator *eventTranslator(); + QRect geometry() const override; int depth() const override; QImage::Format format() const override; qreal devicePixelRatio() const override; + QString name() const override; QPlatformCursor *cursor() const override; void resizeMaximizedWindows(); @@ -64,17 +72,18 @@ public: QWindow *topLevelAt(const QPoint &p) const override; void invalidateSize(); + void updateQScreenAndCanvasRenderSize(); public slots: void setGeometry(const QRect &rect); -protected: private: - QWasmCompositor *m_compositor; - + QString m_canvasId; + QWasmCompositor *m_compositor = nullptr; + QWasmEventTranslator *m_eventTranslator = nullptr; QRect m_geometry = QRect(0, 0, 100, 100); - int m_depth; - QImage::Format m_format; + int m_depth = 32; + QImage::Format m_format = QImage::Format_RGB32; QWasmCursor m_cursor; }; diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html index 67bfcdfbdc..39bb711b6b 100644 --- a/src/plugins/platforms/wasm/wasm_shell.html +++ b/src/plugins/platforms/wasm/wasm_shell.html @@ -7,27 +7,32 @@ <style> html, body { padding: 0; margin : 0; overflow:hidden; height: 100% } /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ - canvas { border: 0px none; background-color: white; height:100%; width:100%; } + canvas { border: 0px none; background-color: white; height:100%; width:100%; } + /* The contenteditable property is set to true for the canvas in order to support + clipboard events. Hide the resulting focus frame and set the cursor back to + the default cursor. */ + canvas { outline: 0px solid transparent; cursor:default } </style> </head> <body onload="init()"> - <figure style="overflow:visible;" id="spinner"> + <figure style="overflow:visible;" id="qtspinner"> <center style="margin-top:1.5em; line-height:150%"> <img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img> <strong>Qt for WebAssembly: APPNAME</strong> - <div id="status"></div> + <div id="qtstatus"></div> <noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript> </center> </figure> - <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas> + <canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas> <script type='text/javascript'> function init() { - var spinner = document.getElementById('spinner'); - var canvas = document.getElementById('canvas'); - var status = document.getElementById('status') + var spinner = document.getElementById('qtspinner'); + var canvas = document.getElementById('qtcanvas'); + var status = document.getElementById('qtstatus') var qtLoader = QtLoader({ + canvasElements : [canvas], showLoader: function(loaderStatus) { spinner.style.display = 'block'; canvas.style.display = 'none'; @@ -50,7 +55,6 @@ showCanvas: function() { spinner.style.display = 'none'; canvas.style.display = 'block'; - return canvas; }, }); qtLoader.loadEmscriptenModule("APPNAME"); diff --git a/src/testlib/qappletestlogger.cpp b/src/testlib/qappletestlogger.cpp index 8e75da88f8..959ff6cf64 100644 --- a/src/testlib/qappletestlogger.cpp +++ b/src/testlib/qappletestlogger.cpp @@ -55,9 +55,8 @@ bool QAppleTestLogger::debugLoggingEnabled() return os_log_type_enabled(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG); } -QAppleTestLogger::QAppleTestLogger(QAbstractTestLogger *logger) +QAppleTestLogger::QAppleTestLogger() : QAbstractTestLogger(nullptr) - , m_logger(logger) { } @@ -65,6 +64,8 @@ static QAppleLogActivity testFunctionActivity; void QAppleTestLogger::enterTestFunction(const char *function) { + Q_UNUSED(function); + // Re-create activity each time testFunctionActivity = QT_APPLE_LOG_ACTIVITY("Running test function").enter(); @@ -73,15 +74,12 @@ void QAppleTestLogger::enterTestFunction(const char *function) QString identifier = QString::fromLatin1(testIdentifier.data()); QMessageLogContext context(nullptr, 0, nullptr, "qt.test.enter"); QString message = identifier; - if (AppleUnifiedLogger::messageHandler(QtDebugMsg, context, message, identifier)) - return; // AUL already printed to stderr - m_logger->enterTestFunction(function); + AppleUnifiedLogger::messageHandler(QtDebugMsg, context, message, identifier); } void QAppleTestLogger::leaveTestFunction() { - m_logger->leaveTestFunction(); testFunctionActivity.leave(); } @@ -134,18 +132,12 @@ void QAppleTestLogger::addIncident(IncidentTypes type, const char *description, if (qstrlen(description)) message += QLatin1Char('\n') % QString::fromLatin1(description); - if (AppleUnifiedLogger::messageHandler(incidentClassification.first, context, message, subsystem)) - return; // AUL already printed to stderr - - m_logger->addIncident(type, description, file, line); + AppleUnifiedLogger::messageHandler(incidentClassification.first, context, message, subsystem); } void QAppleTestLogger::addMessage(QtMsgType type, const QMessageLogContext &context, const QString &message) { - if (AppleUnifiedLogger::messageHandler(type, context, message)) - return; // AUL already printed to stderr - - m_logger->addMessage(type, context, message); + AppleUnifiedLogger::messageHandler(type, context, message); } #endif // QT_USE_APPLE_UNIFIED_LOGGING diff --git a/src/testlib/qappletestlogger_p.h b/src/testlib/qappletestlogger_p.h index 5a45fad7a0..4217f4e6a2 100644 --- a/src/testlib/qappletestlogger_p.h +++ b/src/testlib/qappletestlogger_p.h @@ -63,12 +63,7 @@ class QAppleTestLogger : public QAbstractTestLogger public: static bool debugLoggingEnabled(); - QAppleTestLogger(QAbstractTestLogger *logger); - - void startLogging() override - { m_logger->startLogging(); } - void stopLogging() override - { m_logger->stopLogging(); } + QAppleTestLogger(); void enterTestFunction(const char *function) override; void leaveTestFunction() override; @@ -77,16 +72,12 @@ public: const char *file = 0, int line = 0) override; void addMessage(QtMsgType, const QMessageLogContext &, const QString &) override; - - void addBenchmarkResult(const QBenchmarkResult &result) override - { m_logger->addBenchmarkResult(result); } - void addMessage(MessageTypes type, const QString &message, const char *file = 0, int line = 0) override - { m_logger->addMessage(type, message, file, line); } + { Q_UNUSED(type); Q_UNUSED(message); Q_UNUSED(file); Q_UNUSED(line); Q_UNREACHABLE(); } -private: - QScopedPointer<QAbstractTestLogger> m_logger; + void addBenchmarkResult(const QBenchmarkResult &result) override + { Q_UNUSED(result); } }; #endif diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 0911c64e57..a010ea467b 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -78,6 +78,10 @@ #include <QtTest/private/qtestutil_macos_p.h> #endif +#if defined(Q_OS_DARWIN) +#include <QtTest/private/qappletestlogger_p.h> +#endif + #include <cmath> #include <numeric> #include <algorithm> @@ -511,7 +515,7 @@ static int qToInt(const char *str) Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml) { - QTestLog::LogMode logFormat = QTestLog::Plain; + int logFormat = -1; // Not set const char *logFilename = 0; QTest::testFunctions.clear(); @@ -679,7 +683,7 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool fprintf(stderr, "only one logger can log to stdout\n"); exit(1); } - QTestLog::addLogger(logFormat, filename); + QTestLog::addLogger(QTestLog::LogMode(logFormat), filename); } delete [] filename; delete [] format; @@ -841,10 +845,25 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool QTestLog::setInstalledTestCoverage(installedTestCoverage); // If no loggers were created by the long version of the -o command-line - // option, create a logger using whatever filename and format were - // set using the old-style command-line options. - if (QTestLog::loggerCount() == 0) - QTestLog::addLogger(logFormat, logFilename); + // option, but a logger was requested via the old-style option, add it. + const bool explicitLoggerRequested = logFormat != -1; + if (QTestLog::loggerCount() == 0 && explicitLoggerRequested) + QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename); + + bool addFallbackLogger = !explicitLoggerRequested; + +#if defined(QT_USE_APPLE_UNIFIED_LOGGING) + // Any explicitly requested loggers will be added by now, so we can check if they use stdout + const bool safeToAddAppleLogger = !AppleUnifiedLogger::willMirrorToStderr() || !QTestLog::loggerUsingStdout(); + if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) { + QTestLog::addLogger(QTestLog::Apple, nullptr); + if (AppleUnifiedLogger::willMirrorToStderr() && !logFilename) + addFallbackLogger = false; // Prevent plain test logger fallback below + } +#endif + + if (addFallbackLogger) + QTestLog::addLogger(QTestLog::Plain, logFilename); } // Temporary, backwards compatibility, until qtdeclarative's use of it is converted @@ -2171,13 +2190,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co if (found.isEmpty()) { const char *testObjectName = QTestResult::currentTestObjectName(); if (testObjectName) { - QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath); - QString candidate = QString::fromLatin1("%1/%2/%3") + const QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath); + const QString candidate = QString::fromLatin1("%1/%2/%3") .arg(testsPath, QFile::decodeName(testObjectName).toLower(), base); if (QFileInfo::exists(candidate)) { found = candidate; - } - else if (QTestLog::verboseLevel() >= 2) { + } else if (QTestLog::verboseLevel() >= 2) { QTestLog::info(qPrintable( QString::fromLatin1("testdata %1 not found in tests install path [%2]; " "checking next location") @@ -2199,11 +2217,10 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co } const QString canonicalPath = srcdir.canonicalFilePath(); - QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base); + const QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base); if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) { found = candidate; - } - else if (QTestLog::verboseLevel() >= 2) { + } else if (QTestLog::verboseLevel() >= 2) { QTestLog::info(qPrintable( QString::fromLatin1("testdata %1 not found relative to source path [%2]") .arg(base, QDir::toNativeSeparators(candidate))), @@ -2213,31 +2230,48 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co // 4. Try resources if (found.isEmpty()) { - QString candidate = QString::fromLatin1(":/%1").arg(base); - if (QFileInfo::exists(candidate)) + const QString candidate = QString::fromLatin1(":/%1").arg(base); + if (QFileInfo::exists(candidate)) { found = candidate; + } else if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(qPrintable( + QString::fromLatin1("testdata %1 not found in resources [%2]") + .arg(base, QDir::toNativeSeparators(candidate))), + file, line); + } } // 5. Try current directory if (found.isEmpty()) { const QString candidate = QDir::currentPath() + QLatin1Char('/') + base; - if (QFileInfo::exists(candidate)) + if (QFileInfo::exists(candidate)) { found = candidate; + } else if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(qPrintable( + QString::fromLatin1("testdata %1 not found in current directory [%2]") + .arg(base, QDir::toNativeSeparators(candidate))), + file, line); + } } // 6. Try main source directory if (found.isEmpty()) { - QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base; - if (QFileInfo::exists(candidate)) + const QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base; + if (QFileInfo::exists(candidate)) { found = candidate; + } else if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(qPrintable( + QString::fromLatin1("testdata %1 not found in main source directory [%2]") + .arg(base, QDir::toNativeSeparators(candidate))), + file, line); + } } if (found.isEmpty()) { QTest::qWarn(qPrintable( QString::fromLatin1("testdata %1 could not be located!").arg(base)), file, line); - } - else if (QTestLog::verboseLevel() >= 1) { + } else if (QTestLog::verboseLevel() >= 1) { QTestLog::info(qPrintable( QString::fromLatin1("testdata %1 was located at %2").arg(base, QDir::toNativeSeparators(found))), file, line); diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 32be7f6f10..faef3912c4 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -60,6 +60,7 @@ #include <QtCore/qbytearray.h> #include <QtCore/QElapsedTimer> #include <QtCore/QVariant> +#include <QtCore/qvector.h> #if QT_CONFIG(regularexpression) #include <QtCore/QRegularExpression> #endif @@ -98,6 +99,8 @@ static void saveCoverageTool(const char * appname, bool testfailed, bool install static QElapsedTimer elapsedFunctionTime; static QElapsedTimer elapsedTotalTime; +#define FOREACH_TEST_LOGGER for (QAbstractTestLogger *logger : QTest::loggers) + namespace QTest { int fails = 0; @@ -165,109 +168,7 @@ namespace QTest { static IgnoreResultList *ignoreResultList = 0; - struct LoggerList - { - QAbstractTestLogger *logger; - LoggerList *next; - }; - - class TestLoggers - { - public: - static void addLogger(QAbstractTestLogger *logger) - { - LoggerList *l = new LoggerList; - l->logger = logger; - l->next = loggers; - loggers = l; - } - - static void destroyLoggers() - { - while (loggers) { - LoggerList *l = loggers; - loggers = loggers->next; - delete l->logger; - delete l; - } - } - -#define FOREACH_LOGGER(operation) \ - LoggerList *l = loggers; \ - while (l) { \ - QAbstractTestLogger *logger = l->logger; \ - Q_UNUSED(logger); \ - operation; \ - l = l->next; \ - } - - static void startLogging() - { - FOREACH_LOGGER(logger->startLogging()); - } - - static void stopLogging() - { - FOREACH_LOGGER(logger->stopLogging()); - } - - static void enterTestFunction(const char *function) - { - FOREACH_LOGGER(logger->enterTestFunction(function)); - } - - static void leaveTestFunction() - { - FOREACH_LOGGER(logger->leaveTestFunction()); - } - - static void enterTestData(QTestData *data) - { - FOREACH_LOGGER(logger->enterTestData(data)); - } - - static void addIncident(QAbstractTestLogger::IncidentTypes type, const char *description, - const char *file = 0, int line = 0) - { - FOREACH_LOGGER(logger->addIncident(type, description, file, line)); - } - - static void addBenchmarkResult(const QBenchmarkResult &result) - { - FOREACH_LOGGER(logger->addBenchmarkResult(result)); - } - - static void addMessage(QtMsgType type, const QMessageLogContext &context, - const QString &message) - { - FOREACH_LOGGER(logger->addMessage(type, context, message)); - } - - static void addMessage(QAbstractTestLogger::MessageTypes type, const QString &message, - const char *file = 0, int line = 0) - { - FOREACH_LOGGER(logger->addMessage(type, message, file, line)); - } - - static void outputString(const char *msg) - { - FOREACH_LOGGER(logger->outputString(msg)); - } - - static int loggerCount() - { - int count = 0; - FOREACH_LOGGER(++count); - return count; - } - - private: - static LoggerList *loggers; - }; - -#undef FOREACH_LOGGER - - LoggerList *TestLoggers::loggers = 0; + static QVector<QAbstractTestLogger*> loggers; static bool loggerUsingStdout = false; static int verbosity = 0; @@ -306,10 +207,10 @@ namespace QTest { { static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(QTest::maxWarnings); - if (QTest::TestLoggers::loggerCount() == 0) { + if (QTestLog::loggerCount() == 0) { // if this goes wrong, something is seriously broken. qInstallMessageHandler(oldMessageHandler); - QTEST_ASSERT(QTest::TestLoggers::loggerCount() != 0); + QTEST_ASSERT(QTestLog::loggerCount() != 0); } if (handleIgnoredMessage(type, message)) { @@ -322,13 +223,16 @@ namespace QTest { return; if (!counter.deref()) { - QTest::TestLoggers::addMessage(QAbstractTestLogger::QSystem, + FOREACH_TEST_LOGGER { + logger->addMessage(QAbstractTestLogger::QSystem, QStringLiteral("Maximum amount of warnings exceeded. Use -maxwarnings to override.")); + } return; } } - QTest::TestLoggers::addMessage(type, context, message); + FOREACH_TEST_LOGGER + logger->addMessage(type, context, message); if (type == QtFatalMsg) { /* Right now, we're inside the custom message handler and we're @@ -351,13 +255,16 @@ void QTestLog::enterTestFunction(const char* function) QTEST_ASSERT(function); - QTest::TestLoggers::enterTestFunction(function); + FOREACH_TEST_LOGGER + logger->enterTestFunction(function); } void QTestLog::enterTestData(QTestData *data) { QTEST_ASSERT(data); - QTest::TestLoggers::enterTestData(data); + + FOREACH_TEST_LOGGER + logger->enterTestData(data); } int QTestLog::unhandledIgnoreMessages() @@ -376,7 +283,8 @@ void QTestLog::leaveTestFunction() if (printAvailableTags) return; - QTest::TestLoggers::leaveTestFunction(); + FOREACH_TEST_LOGGER + logger->leaveTestFunction(); } void QTestLog::printUnhandledIgnoreMessages() @@ -391,7 +299,8 @@ void QTestLog::printUnhandledIgnoreMessages() message = QStringLiteral("Did not receive any message matching: \"") + list->pattern.toRegularExpression().pattern() + QLatin1Char('"'); #endif } - QTest::TestLoggers::addMessage(QAbstractTestLogger::Info, message); + FOREACH_TEST_LOGGER + logger->addMessage(QAbstractTestLogger::Info, message); list = list->next; } @@ -411,7 +320,8 @@ void QTestLog::addPass(const char *msg) ++QTest::passes; - QTest::TestLoggers::addIncident(QAbstractTestLogger::Pass, msg); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::Pass, msg); } void QTestLog::addFail(const char *msg, const char *file, int line) @@ -420,7 +330,8 @@ void QTestLog::addFail(const char *msg, const char *file, int line) ++QTest::fails; - QTest::TestLoggers::addIncident(QAbstractTestLogger::Fail, msg, file, line); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::Fail, msg, file, line); } void QTestLog::addXFail(const char *msg, const char *file, int line) @@ -428,7 +339,8 @@ void QTestLog::addXFail(const char *msg, const char *file, int line) QTEST_ASSERT(msg); QTEST_ASSERT(file); - QTest::TestLoggers::addIncident(QAbstractTestLogger::XFail, msg, file, line); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::XFail, msg, file, line); } void QTestLog::addXPass(const char *msg, const char *file, int line) @@ -438,7 +350,8 @@ void QTestLog::addXPass(const char *msg, const char *file, int line) ++QTest::fails; - QTest::TestLoggers::addIncident(QAbstractTestLogger::XPass, msg, file, line); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::XPass, msg, file, line); } void QTestLog::addBPass(const char *msg) @@ -447,7 +360,8 @@ void QTestLog::addBPass(const char *msg) ++QTest::blacklists; - QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedPass, msg); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::BlacklistedPass, msg); } void QTestLog::addBFail(const char *msg, const char *file, int line) @@ -457,7 +371,8 @@ void QTestLog::addBFail(const char *msg, const char *file, int line) ++QTest::blacklists; - QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line); } void QTestLog::addBXPass(const char *msg, const char *file, int line) @@ -467,7 +382,8 @@ void QTestLog::addBXPass(const char *msg, const char *file, int line) ++QTest::blacklists; - QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line); } void QTestLog::addBXFail(const char *msg, const char *file, int line) @@ -477,7 +393,8 @@ void QTestLog::addBXFail(const char *msg, const char *file, int line) ++QTest::blacklists; - QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line); + FOREACH_TEST_LOGGER + logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line); } void QTestLog::addSkip(const char *msg, const char *file, int line) @@ -487,27 +404,33 @@ void QTestLog::addSkip(const char *msg, const char *file, int line) ++QTest::skips; - QTest::TestLoggers::addMessage(QAbstractTestLogger::Skip, QString::fromUtf8(msg), file, line); + FOREACH_TEST_LOGGER + logger->addMessage(QAbstractTestLogger::Skip, QString::fromUtf8(msg), file, line); } void QTestLog::addBenchmarkResult(const QBenchmarkResult &result) { - QTest::TestLoggers::addBenchmarkResult(result); + FOREACH_TEST_LOGGER + logger->addBenchmarkResult(result); } void QTestLog::startLogging() { elapsedTotalTime.start(); elapsedFunctionTime.start(); - QTest::TestLoggers::startLogging(); + FOREACH_TEST_LOGGER + logger->startLogging(); QTest::oldMessageHandler = qInstallMessageHandler(QTest::messageHandler); } void QTestLog::stopLogging() { qInstallMessageHandler(QTest::oldMessageHandler); - QTest::TestLoggers::stopLogging(); - QTest::TestLoggers::destroyLoggers(); + FOREACH_TEST_LOGGER { + logger->stopLogging(); + delete logger; + } + QTest::loggers.clear(); QTest::loggerUsingStdout = false; saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage()); } @@ -542,6 +465,11 @@ void QTestLog::addLogger(LogMode mode, const char *filename) case QTestLog::TAP: logger = new QTapTestLogger(filename); break; +#if defined(QT_USE_APPLE_UNIFIED_LOGGING) + case QTestLog::Apple: + logger = new QAppleTestLogger; + break; +#endif #if defined(HAVE_XCTEST) case QTestLog::XCTest: logger = new QXcodeTestLogger; @@ -549,21 +477,13 @@ void QTestLog::addLogger(LogMode mode, const char *filename) #endif } -#if defined(QT_USE_APPLE_UNIFIED_LOGGING) - // Logger that also feeds messages to AUL. It needs to wrap the existing - // logger, as it needs to be able to short circuit the existing logger - // in case AUL prints to stderr. - if (QAppleTestLogger::debugLoggingEnabled()) - logger = new QAppleTestLogger(logger); -#endif - QTEST_ASSERT(logger); - QTest::TestLoggers::addLogger(logger); + QTest::loggers.append(logger); } int QTestLog::loggerCount() { - return QTest::TestLoggers::loggerCount(); + return QTest::loggers.size(); } bool QTestLog::loggerUsingStdout() @@ -575,15 +495,16 @@ void QTestLog::warn(const char *msg, const char *file, int line) { QTEST_ASSERT(msg); - if (QTest::TestLoggers::loggerCount() > 0) - QTest::TestLoggers::addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line); + FOREACH_TEST_LOGGER + logger->addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line); } void QTestLog::info(const char *msg, const char *file, int line) { QTEST_ASSERT(msg); - QTest::TestLoggers::addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line); + FOREACH_TEST_LOGGER + logger->addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line); } void QTestLog::setVerboseLevel(int level) diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h index 0bdd6290e1..e63e89a78e 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -53,6 +53,10 @@ #include <QtTest/qttestglobal.h> +#if defined(Q_OS_DARWIN) +#include <QtCore/private/qcore_mac_p.h> +#endif + QT_BEGIN_NAMESPACE class QBenchmarkResult; @@ -63,9 +67,12 @@ class Q_TESTLIB_EXPORT QTestLog { public: enum LogMode { - Plain = 0, XML, LightXML, XunitXML, CSV, TeamCity, TAP, + Plain = 0, XML, LightXML, XunitXML, CSV, TeamCity, TAP +#if defined(QT_USE_APPLE_UNIFIED_LOGGING) + , Apple +#endif #if defined(HAVE_XCTEST) - XCTest + , XCTest #endif }; diff --git a/src/widgets/dialogs/qdialog.cpp b/src/widgets/dialogs/qdialog.cpp index f8fc18d13d..45d987aa91 100644 --- a/src/widgets/dialogs/qdialog.cpp +++ b/src/widgets/dialogs/qdialog.cpp @@ -148,7 +148,7 @@ bool QDialogPrivate::canBeNativeDialog() const /*! \internal - Properly hides dialog and sets the \p resultCode + Properly hides dialog and sets the \a resultCode. */ void QDialogPrivate::hide(int resultCode) { @@ -164,8 +164,8 @@ void QDialogPrivate::hide(int resultCode) /*! \internal - Emits finished() signal with \p resultCode. If the \p dialogCode - is equal to 0 emits rejected(), if the \p dialogCode is equal to + Emits finished() signal with \a resultCode. If the \a dialogCode + is equal to 0 emits rejected(), if the \a dialogCode is equal to 1 emits accepted(). */ void QDialogPrivate::finalize(int resultCode, int dialogCode) |