diff options
19 files changed, 288 insertions, 87 deletions
diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index ef55af365c..227dc8908a 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -497,21 +497,20 @@ UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags) // Make sure we keep the dependency order of libraries lflags[arch].removeAll(opt); lflags[arch].append(opt); - } else if (target_mode == TARG_MAC_MODE && opt.startsWith("-framework")) { - if (opt.length() > 10) { - opt = opt.mid(10).trimmed(); - } else { - opt = l.at(++lit); - if (opt.startsWith("-Xarch")) - opt = l.at(++lit); // The user has done the right thing and prefixed each part - } + } else if (target_mode == TARG_MAC_MODE + && (opt == "-framework" || opt == "-force_load")) { + // Handle space separated options + ProString dashOpt = opt; + opt = l.at(++lit); + if (opt.startsWith("-Xarch")) + opt = l.at(++lit); // The user has done the right thing and prefixed each part for(int x = 0; x < lflags[arch].size(); ++x) { - if (lflags[arch].at(x) == "-framework" && lflags[arch].at(++x) == opt) { + if (lflags[arch].at(x) == dashOpt && lflags[arch].at(++x) == opt) { lflags[arch].remove(x - 1, 2); break; } } - lflags[arch].append("-framework"); + lflags[arch].append(dashOpt); lflags[arch].append(opt); } else { lflags[arch].append(opt); diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp index 6f80b93307..37bf5e8669 100644 --- a/qmake/generators/win32/mingw_make.cpp +++ b/qmake/generators/win32/mingw_make.cpp @@ -127,7 +127,7 @@ QString MingwMakefileGenerator::installRoot() const return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)"); } -void createLdResponseFile(const QString &fileName, const ProStringList &objList) +static void createResponseFile(const QString &fileName, const ProStringList &objList) { QString filePath = Option::output_dir + QDir::separator() + fileName; QFile file(filePath); @@ -151,23 +151,6 @@ void createLdResponseFile(const QString &fileName, const ProStringList &objList) } } -void createArObjectScriptFile(const QString &fileName, const QString &target, const ProStringList &objList) -{ - QString filePath = Option::output_dir + QDir::separator() + fileName; - QFile file(filePath); - if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QTextStream t(&file); - // ### quoting? - t << "CREATE " << target << Qt::endl; - for (ProStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) { - t << "ADDMOD " << *it << Qt::endl; - } - t << "SAVE\n"; - t.flush(); - file.close(); - } -} - void MingwMakefileGenerator::writeMingwParts(QTextStream &t) { writeStandardParts(t); @@ -294,26 +277,25 @@ void MingwMakefileGenerator::writeObjectsPart(QTextStream &t) if (objmax.isEmpty() || project->values("OBJECTS").count() < objmax.toInt()) { objectsLinkLine = "$(OBJECTS)"; } else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { - QString ar_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); + QString ar_response_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); if (!var("BUILD_NAME").isEmpty()) { - ar_script_file += "." + var("BUILD_NAME"); + ar_response_file += "." + var("BUILD_NAME"); } if (!var("MAKEFILE").isEmpty()) - ar_script_file += "." + var("MAKEFILE"); + ar_response_file += "." + var("MAKEFILE"); // QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix. - // Strip off any options since the ar commands will be read from file. - QString ar_cmd = var("QMAKE_LIB").section(" ", 0, 0); + QString ar_cmd = var("QMAKE_LIB"); if (ar_cmd.isEmpty()) - ar_cmd = "ar"; - createArObjectScriptFile(ar_script_file, var("DEST_TARGET"), project->values("OBJECTS")); - objectsLinkLine = ar_cmd + " -M < " + escapeFilePath(ar_script_file); + ar_cmd = "ar -rc"; + createResponseFile(ar_response_file, project->values("OBJECTS")); + objectsLinkLine = ar_cmd + ' ' + var("DEST_TARGET") + " @" + escapeFilePath(ar_response_file); } else { QString ld_response_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); if (!var("BUILD_NAME").isEmpty()) ld_response_file += "." + var("BUILD_NAME"); if (!var("MAKEFILE").isEmpty()) ld_response_file += "." + var("MAKEFILE"); - createLdResponseFile(ld_response_file, project->values("OBJECTS")); + createResponseFile(ld_response_file, project->values("OBJECTS")); objectsLinkLine = "@" + escapeFilePath(ld_response_file); } Win32MakefileGenerator::writeObjectsPart(t); diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 0db27f3e25..0d73839f8d 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -167,19 +167,21 @@ QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd) /*! \internal */ -bool QFSFileEngine::processOpenModeFlags(QIODevice::OpenMode *mode) +ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode openMode) { - QIODevice::OpenMode &openMode = *mode; + ProcessOpenModeResult result; + result.ok = false; if ((openMode & QFile::NewOnly) && (openMode & QFile::ExistingOnly)) { qWarning("NewOnly and ExistingOnly are mutually exclusive"); - setError(QFile::OpenError, QLatin1String("NewOnly and ExistingOnly are mutually exclusive")); - return false; + result.error = QLatin1String("NewOnly and ExistingOnly are mutually exclusive"); + return result; } if ((openMode & QFile::ExistingOnly) && !(openMode & (QFile::ReadOnly | QFile::WriteOnly))) { qWarning("ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite"); - setError(QFile::OpenError, QLatin1String("ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite")); - return false; + result.error = QLatin1String( + "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite"); + return result; } // Either Append or NewOnly implies WriteOnly @@ -190,7 +192,9 @@ bool QFSFileEngine::processOpenModeFlags(QIODevice::OpenMode *mode) if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append | QFile::NewOnly))) openMode |= QFile::Truncate; - return true; + result.ok = true; + result.openMode = openMode; + return result; } /*! @@ -234,16 +238,19 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode) return false; } - if (!processOpenModeFlags(&openMode)) + const ProcessOpenModeResult res = processOpenModeFlags(openMode); + if (!res.ok) { + setError(QFileDevice::OpenError, res.error); return false; + } - d->openMode = openMode; + d->openMode = res.openMode; d->lastFlushFailed = false; d->tried_stat = 0; d->fh = nullptr; d->fd = -1; - return d->nativeOpen(openMode); + return d->nativeOpen(d->openMode); } /*! @@ -262,17 +269,20 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHand Q_D(QFSFileEngine); - if (!processOpenModeFlags(&openMode)) + const ProcessOpenModeResult res = processOpenModeFlags(openMode); + if (!res.ok) { + setError(QFileDevice::OpenError, res.error); return false; + } - d->openMode = openMode; + d->openMode = res.openMode; d->lastFlushFailed = false; d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); d->fileEntry.clear(); d->tried_stat = 0; d->fd = -1; - return d->openFh(openMode, fh); + return d->openFh(d->openMode, fh); } /*! @@ -321,10 +331,13 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandle { Q_D(QFSFileEngine); - if (!processOpenModeFlags(&openMode)) + const ProcessOpenModeResult res = processOpenModeFlags(openMode); + if (!res.ok) { + setError(QFileDevice::OpenError, res.error); return false; + } - d->openMode = openMode; + d->openMode = res.openMode; d->lastFlushFailed = false; d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); d->fileEntry.clear(); @@ -332,7 +345,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandle d->fd = -1; d->tried_stat = 0; - return d->openFd(openMode, fd); + return d->openFd(d->openMode, fd); } diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h index 0f416f4886..79593a3e1c 100644 --- a/src/corelib/io/qfsfileengine_p.h +++ b/src/corelib/io/qfsfileengine_p.h @@ -61,6 +61,13 @@ QT_BEGIN_NAMESPACE +struct ProcessOpenModeResult { + bool ok; + QIODevice::OpenMode openMode; + QString error; +}; +inline Q_CORE_EXPORT ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode mode); + class QFSFileEnginePrivate; class Q_CORE_EXPORT QFSFileEngine : public QAbstractFileEngine @@ -131,9 +138,6 @@ public: protected: QFSFileEngine(QFSFileEnginePrivate &dd); - -private: - inline bool processOpenModeFlags(QIODevice::OpenMode *mode); }; class Q_AUTOTEST_EXPORT QFSFileEnginePrivate : public QAbstractFileEnginePrivate diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 747f153f48..11d117c990 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -948,8 +948,10 @@ bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data) { + if (resourceGlobalData.isDestroyed()) + return false; QMutexLocker lock(resourceMutex()); - if (version >= 0x01 && version <= 0x3 && resourceList()) { + if (version >= 0x01 && version <= 0x3) { bool found = false; QResourceRoot res(version, tree, name, data); for(int i = 0; i < resourceList()->size(); ++i) { @@ -975,7 +977,7 @@ Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tre return false; QMutexLocker lock(resourceMutex()); - if (version >= 0x01 && version <= 0x3 && resourceList()) { + if (version >= 0x01 && version <= 0x3) { QResourceRoot res(version, tree, name, data); for(int i = 0; i < resourceList()->size(); ) { if(*resourceList()->at(i) == res) { diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index ee91dc20b3..53784407d8 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -135,8 +135,10 @@ static void q_loadCallback(val event) if (readyState == 4) { // done reply->setReplyAttributes(xhr["data-handler"].as<quintptr>(), status, statusText); - if (!responseString.isEmpty()) - reply->dataReceived(responseString.toUtf8(), responseString.size()); + if (!responseString.isEmpty()) { + QByteArray responseStringArray = responseString.toUtf8(); + reply->dataReceived(responseStringArray, responseStringArray.size()); + } } } if (status >= 400 && !statusText.isEmpty()) diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 0dbb2cefb1..487cac6d90 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -112,11 +112,39 @@ int get_signal_index() return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject); } -void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver, - QtPrivate::QSlotObjectBase *slotObj) +} + +/* + The calling thread is likely the one that executes the lookup via + QHostInfoRunnable. Unless we operate with a queued connection already, + posts the QHostInfo to a dedicated QHostInfoResult object that lives in + the same thread as the user-provided receiver, or (if there is none) in + the thread that made the call to lookupHost. That QHostInfoResult object + then calls the user code in the correct thread. + + The 'result' object deletes itself (via deleteLater) when the metacall + event is received. +*/ +void QHostInfoResult::postResultsReady(const QHostInfo &info) { + // queued connection will take care of dispatching to right thread + if (!slotObj) { + emitResultsReady(info); + return; + } static const int signal_index = get_signal_index(); + + // we used to have a context object, but it's already destroyed + if (withContextObject && !receiver) + return; + + /* QHostInfoResult c'tor moves the result object to the thread of receiver. + If we don't have a receiver, then the result object will not live in a + thread that runs an event loop - so move it to this' thread, which is the thread + that initiated the lookup, and required to have a running event loop. */ auto result = new QHostInfoResult(receiver, slotObj); + if (!receiver) + result->moveToThread(thread()); Q_CHECK_PTR(result); const int nargs = 2; auto types = reinterpret_cast<int *>(malloc(nargs * sizeof(int))); @@ -126,15 +154,13 @@ void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver, auto args = reinterpret_cast<void **>(malloc(nargs * sizeof(void *))); Q_CHECK_PTR(args); args[0] = 0; - args[1] = QMetaType::create(types[1], &hostInfo); + args[1] = QMetaType::create(types[1], &info); Q_CHECK_PTR(args[1]); auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs, types, args); Q_CHECK_PTR(metaCallEvent); qApp->postEvent(result, metaCallEvent); } -} - /*! \class QHostInfo \brief The QHostInfo class provides static functions for host name lookups. @@ -322,6 +348,10 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, ready, the \a functor is called with a QHostInfo argument. The QHostInfo object can then be inspected to get the results of the lookup. + + The \a functor will be run in the thread that makes the call to lookupHost; + that thread must have a running Qt event loop. + \note There is no guarantee on the order the signals will be emitted if you start multiple requests with lookupHost(). @@ -810,7 +840,8 @@ int QHostInfo::lookupHostImpl(const QString &name, QHostInfo hostInfo(id); hostInfo.setError(QHostInfo::HostNotFound); hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given")); - emit_results_ready(hostInfo, receiver, slotObj); + QHostInfoResult result(receiver, slotObj); + result.postResultsReady(hostInfo); return id; } @@ -824,7 +855,8 @@ int QHostInfo::lookupHostImpl(const QString &name, QHostInfo info = manager->cache.get(name, &valid); if (valid) { info.setLookupId(id); - emit_results_ready(info, receiver, slotObj); + QHostInfoResult result(receiver, slotObj); + result.postResultsReady(info); return id; } } @@ -882,7 +914,7 @@ void QHostInfoRunnable::run() // signal emission hostInfo.setLookupId(id); - resultEmitter.emitResultsReady(hostInfo); + resultEmitter.postResultsReady(hostInfo); #if QT_CONFIG(thread) // now also iterate through the postponed ones @@ -895,7 +927,7 @@ void QHostInfoRunnable::run() QHostInfoRunnable* postponed = *it; // we can now emit hostInfo.setLookupId(postponed->id); - postponed->resultEmitter.emitResultsReady(hostInfo); + postponed->resultEmitter.postResultsReady(hostInfo); delete postponed; } manager->postponedLookups.erase(partitionBegin, partitionEnd); diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index ee052b86d3..9a4657234e 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -84,12 +84,14 @@ class QHostInfoResult : public QObject QPointer<const QObject> receiver = nullptr; QtPrivate::QSlotObjectBase *slotObj = nullptr; + const bool withContextObject = false; public: QHostInfoResult() = default; QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) : receiver(receiver), - slotObj(slotObj) + slotObj(slotObj), + withContextObject(slotObj && receiver) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater); @@ -97,10 +99,15 @@ public: moveToThread(receiver->thread()); } + void postResultsReady(const QHostInfo &info); + public Q_SLOTS: inline void emitResultsReady(const QHostInfo &info) { if (slotObj) { + // we used to have a context object, but it's already destroyed + if (withContextObject && !receiver) + return; QHostInfo copy = info; void *args[2] = { nullptr, reinterpret_cast<void *>(©) }; slotObj->call(const_cast<QObject*>(receiver.data()), args); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 869d6d5959..1fcfdf9f16 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -882,8 +882,25 @@ static LoadedOpenSsl loadOpenSsl() // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third // attempt, _after_ <bundle>/Contents/Frameworks has been searched. // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place. +# if defined(Q_OS_ANDROID) + // OpenSSL 1.1.x must be suffixed otherwise it will use the system libcrypto.so libssl.so which on API-21 are OpenSSL 1.0 not 1.1 + auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) { + auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX"); + if (suffix.isEmpty()) + return defaultSuffix; + return suffix; + }; +# if QT_CONFIG(opensslv11) + static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1")); +# else + static QString suffix = QString::fromLatin1(openSSLSuffix()); +# endif + libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1); + libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1); +# else libssl->setFileNameAndVersion(QLatin1String("ssl"), -1); libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1); +# endif if (libcrypto->load() && libssl->load()) { // libssl.so.0 and libcrypto.so.0 found return result; diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 2398e6351e..9be6814ae7 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -49,7 +49,14 @@ QT_BEGIN_NAMESPACE -class QNSWindowBackingStore : public QRasterBackingStore +class QCocoaBackingStore : public QRasterBackingStore +{ +protected: + QCocoaBackingStore(QWindow *window); + QCFType<CGColorSpaceRef> colorSpace() const; +}; + +class QNSWindowBackingStore : public QCocoaBackingStore { public: QNSWindowBackingStore(QWindow *window); @@ -64,7 +71,7 @@ private: void redrawRoundedBottomCorners(CGRect) const; }; -class QCALayerBackingStore : public QPlatformBackingStore +class QCALayerBackingStore : public QCocoaBackingStore { public: QCALayerBackingStore(QWindow *window); diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 01b4894324..6015257f4e 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -48,11 +48,24 @@ QT_BEGIN_NAMESPACE -QNSWindowBackingStore::QNSWindowBackingStore(QWindow *window) +QCocoaBackingStore::QCocoaBackingStore(QWindow *window) : QRasterBackingStore(window) { } +QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const +{ + NSView *view = static_cast<QCocoaWindow *>(window()->handle())->view(); + return QCFType<CGColorSpaceRef>::constructFromGet(view.window.colorSpace.CGColorSpace); +} + +// ---------------------------------------------------------------------------- + +QNSWindowBackingStore::QNSWindowBackingStore(QWindow *window) + : QCocoaBackingStore(window) +{ +} + QNSWindowBackingStore::~QNSWindowBackingStore() { } @@ -175,11 +188,10 @@ void QNSWindowBackingStore::flush(QWindow *window, const QRegion ®ion, const Q_ASSERT_X(graphicsContext, "QCocoaBackingStore", "Focusing the view should give us a current graphics context"); - // Prevent potentially costly color conversion by assigning the display color space - // to the backingstore image. This does not copy the underlying image data. - CGColorSpaceRef displayColorSpace = view.window.screen.colorSpace.CGColorSpace; + // Tag backingstore image with color space based on the window. + // Note: This does not copy the underlying image data. QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace( - QCFType<CGImageRef>(m_image.toCGImage()), displayColorSpace); + QCFType<CGImageRef>(m_image.toCGImage()), colorSpace()); // Create temporary image to use for blitting, without copying image data NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease]; @@ -293,7 +305,7 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const // ---------------------------------------------------------------------------- QCALayerBackingStore::QCALayerBackingStore(QWindow *window) - : QPlatformBackingStore(window) + : QCocoaBackingStore(window) { qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window; m_buffers.resize(1); @@ -432,11 +444,7 @@ bool QCALayerBackingStore::recreateBackBufferIfNeeded() << "based on requested" << m_requestedSize << "and dpr =" << devicePixelRatio; static auto pixelFormat = QImage::toPixelFormat(QImage::Format_ARGB32_Premultiplied); - - NSView *view = static_cast<QCocoaWindow *>(window()->handle())->view(); - auto colorSpace = QCFType<CGColorSpaceRef>::constructFromGet(view.window.screen.colorSpace.CGColorSpace); - - m_buffers.back().reset(new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace)); + m_buffers.back().reset(new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace())); return true; } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 26df604f1c..8f3c192745 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1648,6 +1648,9 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel) [nsWindow setDepthLimit:NSWindowDepthTwentyfourBitRGB]; } + if (format().colorSpace() == QSurfaceFormat::sRGBColorSpace) + nsWindow.colorSpace = NSColorSpace.sRGBColorSpace; + return nsWindow; } diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 4b54051e0c..4f0f846749 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -1301,7 +1301,23 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, || code == Qt::Key_Control || code == Qt::Key_Meta || code == Qt::Key_Alt)) { - // Someone ate the key down event + + // Workaround for QTBUG-77153: + // The Surface Pen eraser button generates Meta+F18/19/20 keystrokes, + // but when it is not touching the screen the Fn Down is eaten and only + // a Fn Up with the previous state as "not pressed" is generated, which + // would be ignored. We detect this case and synthesize the expected events. + if ((msg.lParam & 0x40000000) == 0 && + Qt::KeyboardModifier(state) == Qt::NoModifier && + ((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) { + QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code, + Qt::MetaModifier, scancode, + quint32(msg.wParam), MetaLeft); + QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code, + Qt::NoModifier, scancode, + quint32(msg.wParam), 0); + result = true; + } } else { if (!code) code = asciiToKeycode(rec->ascii ? char(rec->ascii) : char(msg.wParam), state); diff --git a/src/plugins/platforms/winrt/qwinrtfileengine.cpp b/src/plugins/platforms/winrt/qwinrtfileengine.cpp index 3014b30c38..962e4ab938 100644 --- a/src/plugins/platforms/winrt/qwinrtfileengine.cpp +++ b/src/plugins/platforms/winrt/qwinrtfileengine.cpp @@ -43,6 +43,7 @@ #include <QtCore/QCoreApplication> #include <QtCore/QHash> #include <QtCore/qfunctions_winrt.h> +#include <QtCore/private/qfsfileengine_p.h> #include <wrl.h> #include <windows.storage.h> @@ -196,7 +197,19 @@ bool QWinRTFileEngine::open(QIODevice::OpenMode openMode) hr = QWinRTFunctions::await(op, d->stream.GetAddressOf()); RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false); - d->openMode = openMode; + const ProcessOpenModeResult res = processOpenModeFlags(openMode); + if (!res.ok) { + setError(QFileDevice::OpenError, res.error); + return false; + } + d->openMode = res.openMode; + if (d->openMode & QIODevice::Truncate) { + if (!setSize(0)) { + close(); + setError(QFileDevice::OpenError, QLatin1String("Could not truncate file")); + return false; + } + } return SUCCEEDED(hr); } @@ -257,6 +270,29 @@ qint64 QWinRTFileEngine::size() const return qint64(size); } +bool QWinRTFileEngine::setSize(qint64 size) +{ + Q_D(QWinRTFileEngine); + if (!d->stream) { + setError(QFileDevice::ResizeError, QLatin1String("File must be open to be resized")); + return false; + } + + if (size < 0) { + setError(QFileDevice::ResizeError, QLatin1String("File size cannot be negative")); + return false; + } + + HRESULT hr = d->stream->put_Size(static_cast<quint64>(size)); + RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ResizeError, false); + if (!flush()) { + setError(QFileDevice::ResizeError, QLatin1String("Could not flush file")); + return false; + } + + return true; +} + qint64 QWinRTFileEngine::pos() const { Q_D(const QWinRTFileEngine); diff --git a/src/plugins/platforms/winrt/qwinrtfileengine.h b/src/plugins/platforms/winrt/qwinrtfileengine.h index 4485917c9e..453565a95a 100644 --- a/src/plugins/platforms/winrt/qwinrtfileengine.h +++ b/src/plugins/platforms/winrt/qwinrtfileengine.h @@ -79,6 +79,7 @@ public: bool close() override; bool flush() override; qint64 size() const override; + bool setSize(qint64 size) override; qint64 pos() const override; bool seek(qint64 pos) override; bool remove() override; diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index c162cca048..968ca68619 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -5327,10 +5327,10 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const CGRect barRect = [cell barRectFlipped:hasTicks]; if (drawBar) { + [cell drawBarInside:barRect flipped:!verticalFlip]; // This ain't HIG kosher: force unfilled bar look. if (hasDoubleTicks) slider.numberOfTickMarks = numberOfTickMarks; - [cell drawBarInside:barRect flipped:!verticalFlip]; } if (hasTicks && drawTicks) { diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index f0230f4f32..5df78b92f3 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -6457,8 +6457,18 @@ void QWidget::setFocusProxy(QWidget * w) } } + QWidget *oldDeepestFocusProxy = d_func()->deepestFocusProxy(); + if (!oldDeepestFocusProxy) + oldDeepestFocusProxy = this; + const bool changingAppFocusWidget = (QApplicationPrivate::focus_widget == oldDeepestFocusProxy); + d->createExtra(); d->extra->focus_proxy = w; + + if (changingAppFocusWidget) { + QWidget *newDeepestFocusProxy = d_func()->deepestFocusProxy(); + QApplicationPrivate::focus_widget = newDeepestFocusProxy ? newDeepestFocusProxy : this; + } } diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp index da6e02210b..a603b2d879 100644 --- a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp @@ -92,6 +92,7 @@ private slots: void lookupIPv6(); void lookupConnectToFunctionPointer_data(); void lookupConnectToFunctionPointer(); + void lookupConnectToFunctionPointerDeleted(); void lookupConnectToLambda_data(); void lookupConnectToLambda(); void reverseLookup_data(); @@ -358,6 +359,17 @@ void tst_QHostInfo::lookupConnectToFunctionPointer() QCOMPARE(tmp.join(' '), expected.join(' ')); } +void tst_QHostInfo::lookupConnectToFunctionPointerDeleted() +{ + { + QObject contextObject; + QHostInfo::lookupHost("localhost", &contextObject, [](const QHostInfo){ + QFAIL("This should never be called!"); + }); + } + QTestEventLoop::instance().enterLoop(3); +} + void tst_QHostInfo::lookupConnectToLambda_data() { lookupIPv4_data(); @@ -464,7 +476,9 @@ void tst_QHostInfo::reverseLookup_data() QTest::newRow("dns.google") << QString("8.8.8.8") << reverseLookupHelper("8.8.8.8") << 0 << false; QTest::newRow("one.one.one.one") << QString("1.1.1.1") << reverseLookupHelper("1.1.1.1") << 0 << false; - QTest::newRow("bogus-name") << QString("1::2::3::4") << QStringList() << 1 << true; + QTest::newRow("dns.google IPv6") << QString("2001:4860:4860::8888") << reverseLookupHelper("2001:4860:4860::8888") << 0 << true; + QTest::newRow("cloudflare IPv6") << QString("2606:4700:4700::1111") << reverseLookupHelper("2606:4700:4700::1111") << 0 << true; + QTest::newRow("bogus-name IPv6") << QString("1::2::3::4") << QStringList() << 1 << true; } void tst_QHostInfo::reverseLookup() @@ -705,6 +719,7 @@ void tst_QHostInfo::cache() void tst_QHostInfo::resultsReady(const QHostInfo &hi) { + QVERIFY(QThread::currentThread() == thread()); lookupDone = true; lookupResults = hi; lookupsDoneCounter++; diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 0064eb2713..5465bd5518 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -182,6 +182,8 @@ private slots: void tabOrderWithCompoundWidgets(); void tabOrderNoChange(); void tabOrderNoChange2(); + void appFocusWidgetWithFocusProxyLater(); + void appFocusWidgetWhenLosingFocusProxy(); #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) void activation(); #endif @@ -2032,6 +2034,51 @@ void tst_QWidget::tabOrderNoChange2() QCOMPARE(focusChainBackward, getFocusChain(&w, false)); } +void tst_QWidget::appFocusWidgetWithFocusProxyLater() +{ + // Given a lineedit without a focus proxy + QWidget window; + window.setWindowTitle(QTest::currentTestFunction()); + QLineEdit *lineEditFocusProxy = new QLineEdit(&window); + QLineEdit *lineEdit = new QLineEdit(&window); + lineEdit->setFocus(); + window.show(); + QApplication::setActiveWindow(&window); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QCOMPARE(QApplication::focusWidget(), lineEdit); + + // When setting a focus proxy for the focus widget (like QWebEngineView does) + lineEdit->setFocusProxy(lineEditFocusProxy); + + // Then the focus widget should be updated + QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); + + // So that deleting the lineEdit and later the window, doesn't crash + delete lineEdit; + QCOMPARE(QApplication::focusWidget(), nullptr); +} + +void tst_QWidget::appFocusWidgetWhenLosingFocusProxy() +{ + // Given a lineedit with a focus proxy + QWidget window; + window.setWindowTitle(QTest::currentTestFunction()); + QLineEdit *lineEditFocusProxy = new QLineEdit(&window); + QLineEdit *lineEdit = new QLineEdit(&window); + lineEdit->setFocusProxy(lineEditFocusProxy); + lineEdit->setFocus(); + window.show(); + QApplication::setActiveWindow(&window); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); + + // When unsetting the focus proxy + lineEdit->setFocusProxy(nullptr); + + // Then the application focus widget should be back to the lineedit + QCOMPARE(QApplication::focusWidget(), lineEdit); +} + #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) void tst_QWidget::activation() { |