Merge remote-tracking branch 'origin/5.14' into 5.15

Change-Id: Iebedaa967a263854f18cd403ce007d7965f26d2b
This commit is contained in:
Qt Forward Merge Bot 2019-10-25 01:00:16 +02:00
commit 375efdd0e1
58 changed files with 862 additions and 337 deletions

View File

@ -70,8 +70,8 @@ QMAKE_CFLAGS_THREAD = -D_REENTRANT
QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden
QMAKE_CFLAGS_NEON = -mfpu=neon
QMAKE_LFLAGS_APP = -Wl,--no-undefined -Wl,-z,noexecstack -shared
QMAKE_LFLAGS_SHLIB = -Wl,--no-undefined -Wl,-z,noexecstack -shared
QMAKE_LFLAGS_APP = -Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack -shared
QMAKE_LFLAGS_SHLIB = -Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack -shared
QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB
QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined
QMAKE_LFLAGS_RPATH = -Wl,-rpath=

View File

@ -4690,7 +4690,7 @@
The definitions above define a qmake target called \c mytarget, containing a
Makefile target called \c{.buildfile} which in turn is generated with the
\l{touchfunction}{touch()} function. Finally, the
\c touch command. Finally, the
\c{.depends} member specifies that \c mytarget depends on \c mytarget2,
another target that is defined afterwards. \c mytarget2 is a dummy target.
It is only defined to echo some text to the console.

View File

@ -327,9 +327,9 @@ public:
return pre;
}
static constexpr QSpecialInteger max()
static Q_DECL_CONSTEXPR QSpecialInteger max()
{ return QSpecialInteger(std::numeric_limits<T>::max()); }
static constexpr QSpecialInteger min()
static Q_DECL_CONSTEXPR QSpecialInteger min()
{ return QSpecialInteger(std::numeric_limits<T>::min()); }
};
@ -373,8 +373,8 @@ public:
QLEInteger &operator ++(int);
QLEInteger &operator --(int);
static constexpr QLEInteger max();
static constexpr QLEInteger min();
static Q_DECL_CONSTEXPR QLEInteger max();
static Q_DECL_CONSTEXPR QLEInteger min();
};
template<typename T>
@ -400,8 +400,8 @@ public:
QBEInteger &operator ++(int);
QBEInteger &operator --(int);
static constexpr QBEInteger max();
static constexpr QBEInteger min();
static Q_DECL_CONSTEXPR QBEInteger max();
static Q_DECL_CONSTEXPR QBEInteger min();
};
#else

View File

@ -114,8 +114,8 @@ extern "C" {
// without full system POSIX.
# pragma weak shm_area_password
# pragma weak shm_area_name
char *shm_area_password = "dummy";
char *shm_area_name = "dummy";
char shm_area_password[] = "dummy";
char shm_area_name[] = "dummy";
}
#endif

View File

@ -58,6 +58,36 @@
QT_BEGIN_NAMESPACE
#define Q_RETURN_ON_INVALID_FILENAME(message, result) \
{ \
QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).warning(message); \
errno = EINVAL; \
return (result); \
}
inline bool qIsFilenameBroken(const QByteArray &name)
{
return name.contains('\0');
}
inline bool qIsFilenameBroken(const QString &name)
{
return name.contains(QLatin1Char('\0'));
}
inline bool qIsFilenameBroken(const QFileSystemEntry &entry)
{
return qIsFilenameBroken(entry.nativeFilePath());
}
#define Q_CHECK_FILE_NAME(name, result) \
do { \
if (Q_UNLIKELY((name).isEmpty())) \
Q_RETURN_ON_INVALID_FILENAME("Empty filename passed to function", (result)); \
if (Q_UNLIKELY(qIsFilenameBroken(name))) \
Q_RETURN_ON_INVALID_FILENAME("Broken filename passed to function", (result)); \
} while (false)
class QFileSystemEngine
{
public:

View File

@ -118,13 +118,6 @@ enum {
#endif
};
#define emptyFileEntryWarning() emptyFileEntryWarning_(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC)
static void emptyFileEntryWarning_(const char *file, int line, const char *function)
{
QMessageLogger(file, line, function).warning("Empty filename passed to function");
errno = EINVAL;
}
#if defined(Q_OS_DARWIN)
static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
const QFileSystemEntry &entry,
@ -625,8 +618,7 @@ void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
//static
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
{
if (Q_UNLIKELY(link.isEmpty()))
return emptyFileEntryWarning(), link;
Q_CHECK_FILE_NAME(link, link);
QByteArray s = qt_readlink(link.nativeFilePath().constData());
if (s.length() > 0) {
@ -685,10 +677,7 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), entry;
if (entry.isRoot())
return entry;
Q_CHECK_FILE_NAME(entry, entry);
#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
// realpath(X,0) is not supported
@ -738,8 +727,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), entry;
Q_CHECK_FILE_NAME(entry, entry);
if (entry.isAbsolute() && entry.isClean())
return entry;
@ -773,8 +762,7 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
//static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), QByteArray();
Q_CHECK_FILE_NAME(entry, QByteArray());
QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
@ -887,8 +875,7 @@ QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry)
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(entry, false);
#if defined(Q_OS_DARWIN)
if (what & QFileSystemMetaData::BundleType) {
@ -1157,8 +1144,7 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{
QString dirName = entry.filePath();
if (Q_UNLIKELY(dirName.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(dirName, false);
// Darwin doesn't support trailing /'s, so remove for everyone
while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/')))
@ -1177,8 +1163,7 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
//static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(entry, false);
if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath());
@ -1203,8 +1188,9 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
//static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1233,8 +1219,9 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
{
QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(srcPath, false);
Q_CHECK_FILE_NAME(tgtPath, false);
#if defined(RENAME_NOREPLACE) && QT_CONFIG(renameat2)
if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
@ -1302,8 +1289,9 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1313,8 +1301,7 @@ bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, cons
//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(entry, false);
if (unlink(entry.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@ -1349,8 +1336,7 @@ static mode_t toMode_t(QFile::Permissions permissions)
//static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
{
if (Q_UNLIKELY(entry.isEmpty()))
return emptyFileEntryWarning(), false;
Q_CHECK_FILE_NAME(entry, false);
mode_t mode = toMode_t(permissions);
bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;

View File

@ -461,7 +461,9 @@ void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
QFileSystemMetaData &data)
{
if (data.missingFlags(QFileSystemMetaData::LinkType))
Q_CHECK_FILE_NAME(link, link);
if (data.missingFlags(QFileSystemMetaData::LinkType))
QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType);
QString target;
@ -480,6 +482,8 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
Q_CHECK_FILE_NAME(entry, entry);
if (data.missingFlags(QFileSystemMetaData::ExistsAttribute))
QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute);
@ -492,6 +496,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static
QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
{
Q_CHECK_FILE_NAME(path, QString());
// can be //server or //server/share
QString absPath;
QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1));
@ -527,6 +533,8 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path)
//static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{
Q_CHECK_FILE_NAME(entry, entry);
QString ret;
if (!entry.isRelative()) {
@ -609,6 +617,8 @@ QByteArray fileIdWin8(HANDLE handle)
//static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{
Q_CHECK_FILE_NAME(entry, QByteArray());
QByteArray result;
#ifndef Q_OS_WINRT
@ -999,6 +1009,7 @@ static bool isDirPath(const QString &dirPath, bool *existed);
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what)
{
Q_CHECK_FILE_NAME(entry, false);
what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags;
data.entryFlags &= ~what;
@ -1116,6 +1127,8 @@ static bool isDirPath(const QString &dirPath, bool *existed)
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{
QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
if (createParents) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
// We spefically search for / so \ would break it..
@ -1177,6 +1190,8 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{
QString dirName = entry.filePath();
Q_CHECK_FILE_NAME(dirName, false);
if (removeEmptyParents) {
dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
@ -1381,6 +1396,9 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
//static
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
#ifndef Q_OS_WINRT
bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(),
(wchar_t*)target.nativeFilePath().utf16()) != 0;
@ -1396,6 +1414,9 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
Q_CHECK_FILE_NAME(source, false);
Q_CHECK_FILE_NAME(target, false);
bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
MOVEFILE_REPLACE_EXISTING) != 0;
@ -1407,6 +1428,8 @@ bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, cons
//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
Q_CHECK_FILE_NAME(entry, false);
bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
if(!ret)
error = QSystemError(::GetLastError(), QSystemError::NativeError);
@ -1417,6 +1440,8 @@ bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data)
{
Q_CHECK_FILE_NAME(entry, false);
Q_UNUSED(data);
int mode = 0;

View File

@ -142,36 +142,45 @@ QString QStandardPaths::writableLocation(StandardLocation type)
}
case RuntimeLocation:
{
const uint myUid = uint(geteuid());
// http://standards.freedesktop.org/basedir-spec/latest/
const uint myUid = uint(geteuid());
// since the current user is the owner, set both xxxUser and xxxOwner
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
QFileInfo fileInfo;
QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
if (xdgRuntimeDir.isEmpty()) {
const QString userName = QFileSystemEngine::resolveUserName(myUid);
xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName;
fileInfo.setFile(xdgRuntimeDir);
if (!fileInfo.isDir()) {
if (!QDir().mkdir(xdgRuntimeDir)) {
qErrnoWarning("QStandardPaths: error creating runtime directory %ls",
qUtf16Printable(xdgRuntimeDir));
return QString();
}
}
#ifndef Q_OS_WASM
qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%ls'", qUtf16Printable(xdgRuntimeDir));
#endif
} else {
fileInfo.setFile(xdgRuntimeDir);
if (!fileInfo.exists()) {
qWarning("QStandardPaths: XDG_RUNTIME_DIR points to non-existing path '%ls', "
"please create it with 0700 permissions.", qUtf16Printable(xdgRuntimeDir));
return QString();
}
}
if (fileInfo.exists()) {
if (!fileInfo.isDir()) {
qWarning("QStandardPaths: XDG_RUNTIME_DIR points to '%ls' which is not a directory",
qUtf16Printable(xdgRuntimeDir));
return QString();
}
} else {
QFileSystemEntry entry(xdgRuntimeDir);
if (!QFileSystemEngine::createDirectory(entry, false)) {
if (errno != EEXIST) {
qErrnoWarning("QStandardPaths: error creating runtime directory %ls",
qUtf16Printable(xdgRuntimeDir));
return QString();
}
} else {
QSystemError error;
if (!QFileSystemEngine::setPermissions(entry, wantedPerms, error)) {
qWarning("QStandardPaths: could not set correct permissions on runtime directory %ls: %ls",
qUtf16Printable(xdgRuntimeDir), qUtf16Printable(error.toString()));
return QString();
}
}
}
// "The directory MUST be owned by the user"
if (fileInfo.ownerId() != myUid) {
@ -181,17 +190,12 @@ QString QStandardPaths::writableLocation(StandardLocation type)
return QString();
}
// "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700."
// since the current user is the owner, set both xxxUser and xxxOwner
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
if (fileInfo.permissions() != wantedPerms) {
QFile file(xdgRuntimeDir);
if (!file.setPermissions(wantedPerms)) {
qWarning("QStandardPaths: could not set correct permissions on runtime directory %ls: %ls",
qUtf16Printable(xdgRuntimeDir), qUtf16Printable(file.errorString()));
return QString();
}
qWarning("QStandardPaths: wrong permissions on runtime directory %ls, %x instead of %x",
qUtf16Printable(xdgRuntimeDir), uint(fileInfo.permissions()), uint(wantedPerms));
return QString();
}
return xdgRuntimeDir;
}
default:

View File

@ -1498,7 +1498,7 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta
case QStateMachine::StateMachineChildModeSetToParallelError:
Q_ASSERT(currentContext != nullptr);
errorString = QStateMachine::tr("Child mode of state machine '%1' is not 'ExclusiveStates'!")
errorString = QStateMachine::tr("Child mode of state machine '%1' is not 'ExclusiveStates'.")
.arg(currentContext->objectName());
break;
@ -2469,7 +2469,7 @@ QStateMachine::QStateMachine(QObject *parent)
and \a parent.
\warning Do not set the \a childMode to anything else than \l{ExclusiveStates}, otherwise the
state machine is invalid, and might work incorrectly!
state machine is invalid, and might work incorrectly.
*/
QStateMachine::QStateMachine(QState::ChildMode childMode, QObject *parent)
: QState(*new QStateMachinePrivate, /*parentState=*/0)

View File

@ -71,21 +71,6 @@ QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
// Macro QSHAREDPOINTER_VERIFY_AUTO_CAST
// generates a compiler error if the following construct isn't valid:
// T *ptr1;
// X *ptr2 = ptr1;
//
#ifdef QT_NO_DEBUG
# define QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X) qt_noop()
#else
template<typename T> inline void qt_sharedpointer_cast_check(T *) { }
# define QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X) \
qt_sharedpointer_cast_check<T>(static_cast<X *>(0))
#endif
//
// forward declarations
//
@ -299,6 +284,9 @@ template <class T> class QSharedPointer
{
typedef T *QSharedPointer:: *RestrictedBool;
typedef QtSharedPointer::ExternalRefCountData Data;
template <typename X>
using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;
public:
typedef T Type;
typedef T element_type;
@ -322,11 +310,11 @@ public:
Q_DECL_CONSTEXPR QSharedPointer(std::nullptr_t) noexcept : value(nullptr), d(nullptr) { }
template <class X>
template <class X, IfCompatible<X> = true>
inline explicit QSharedPointer(X *ptr) : value(ptr) // noexcept
{ internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }
template <class X, typename Deleter>
template <class X, typename Deleter, IfCompatible<X> = true>
inline QSharedPointer(X *ptr, Deleter deleter) : value(ptr) // throws
{ internalConstruct(ptr, deleter); }
@ -354,7 +342,7 @@ public:
return *this;
}
template <class X>
template <class X, IfCompatible<X> = true>
QSharedPointer(QSharedPointer<X> &&other) noexcept
: value(other.value), d(other.d)
{
@ -362,7 +350,7 @@ public:
other.value = nullptr;
}
template <class X>
template <class X, IfCompatible<X> = true>
QSharedPointer &operator=(QSharedPointer<X> &&other) noexcept
{
QSharedPointer moved(std::move(other));
@ -370,11 +358,11 @@ public:
return *this;
}
template <class X>
template <class X, IfCompatible<X> = true>
QSharedPointer(const QSharedPointer<X> &other) noexcept : value(other.value), d(other.d)
{ if (d) ref(); }
template <class X>
template <class X, IfCompatible<X> = true>
inline QSharedPointer &operator=(const QSharedPointer<X> &other)
{
QSharedPointer copy(other);
@ -382,11 +370,11 @@ public:
return *this;
}
template <class X>
template <class X, IfCompatible<X> = true>
inline QSharedPointer(const QWeakPointer<X> &other) : value(nullptr), d(nullptr)
{ *this = other; }
template <class X>
template <class X, IfCompatible<X> = true>
inline QSharedPointer<T> &operator=(const QWeakPointer<X> &other)
{ internalSet(other.d, other.value); return *this; }
@ -556,6 +544,8 @@ class QWeakPointer
{
typedef T *QWeakPointer:: *RestrictedBool;
typedef QtSharedPointer::ExternalRefCountData Data;
template <typename X>
using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;
public:
typedef T element_type;
@ -581,14 +571,14 @@ public:
#ifndef QT_NO_QOBJECT
// special constructor that is enabled only if X derives from QObject
#if QT_DEPRECATED_SINCE(5, 0)
template <class X>
template <class X, IfCompatible<X> = true>
QT_DEPRECATED inline QWeakPointer(X *ptr) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
{ }
#endif
#endif
#if QT_DEPRECATED_SINCE(5, 0)
template <class X>
template <class X, IfCompatible<X> = true>
QT_DEPRECATED inline QWeakPointer &operator=(X *ptr)
{ return *this = QWeakPointer(ptr); }
#endif
@ -624,11 +614,11 @@ public:
return *this;
}
template <class X>
template <class X, IfCompatible<X> = true>
inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr)
{ *this = o; }
template <class X>
template <class X, IfCompatible<X> = true>
inline QWeakPointer &operator=(const QWeakPointer<X> &o)
{
// conversion between X and T could require access to the virtual table
@ -645,14 +635,13 @@ public:
bool operator!=(const QWeakPointer<X> &o) const noexcept
{ return !(*this == o); }
template <class X>
template <class X, IfCompatible<X> = true>
inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr)
{ *this = o; }
template <class X>
template <class X, IfCompatible<X> = true>
inline QWeakPointer &operator=(const QSharedPointer<X> &o)
{
QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X); // if you get an error in this line, the cast is invalid
internalSet(o.d, o.data());
return *this;
}
@ -689,7 +678,7 @@ public:
{ return *this = QWeakPointer<X>(ptr, true); }
#ifndef QT_NO_QOBJECT
template <class X>
template <class X, IfCompatible<X> = true>
inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
{ }
#endif

View File

@ -4145,11 +4145,11 @@ QPaintEngine *QImage::paintEngine() const
if (!d->paintEngine) {
QPaintDevice *paintDevice = const_cast<QImage *>(this);
QPaintEngine *paintEngine = 0;
QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
if (platformIntegration)
paintEngine = platformIntegration->createImagePaintEngine(paintDevice);
d->paintEngine = paintEngine ? paintEngine : new QRasterPaintEngine(paintDevice);
d->paintEngine = platformIntegration->createImagePaintEngine(paintDevice);
if (!d->paintEngine)
d->paintEngine = new QRasterPaintEngine(paintDevice);
}
return d->paintEngine;

View File

@ -492,6 +492,8 @@ static const struct {
{ Qt::Key_LaunchD, QT_TRANSLATE_NOOP("QShortcut", "Launch (D)") },
{ Qt::Key_LaunchE, QT_TRANSLATE_NOOP("QShortcut", "Launch (E)") },
{ Qt::Key_LaunchF, QT_TRANSLATE_NOOP("QShortcut", "Launch (F)") },
{ Qt::Key_LaunchG, QT_TRANSLATE_NOOP("QShortcut", "Launch (G)") },
{ Qt::Key_LaunchH, QT_TRANSLATE_NOOP("QShortcut", "Launch (H)") },
{ Qt::Key_MonBrightnessUp, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Up") },
{ Qt::Key_MonBrightnessDown, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Down") },
{ Qt::Key_KeyboardLightOnOff, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Light On/Off") },
@ -518,9 +520,11 @@ static const struct {
{ Qt::Key_Book, QT_TRANSLATE_NOOP("QShortcut", "Book") },
{ Qt::Key_CD, QT_TRANSLATE_NOOP("QShortcut", "CD") },
{ Qt::Key_Calculator, QT_TRANSLATE_NOOP("QShortcut", "Calculator") },
{ Qt::Key_Calendar, QT_TRANSLATE_NOOP("QShortcut", "Calendar") },
{ Qt::Key_Clear, QT_TRANSLATE_NOOP("QShortcut", "Clear") },
{ Qt::Key_ClearGrab, QT_TRANSLATE_NOOP("QShortcut", "Clear Grab") },
{ Qt::Key_Close, QT_TRANSLATE_NOOP("QShortcut", "Close") },
{ Qt::Key_ContrastAdjust, QT_TRANSLATE_NOOP("QShortcut", "Adjust contrast") },
{ Qt::Key_Copy, QT_TRANSLATE_NOOP("QShortcut", "Copy") },
{ Qt::Key_Cut, QT_TRANSLATE_NOOP("QShortcut", "Cut") },
{ Qt::Key_Display, QT_TRANSLATE_NOOP("QShortcut", "Display") },
@ -534,6 +538,7 @@ static const struct {
{ Qt::Key_LogOff, QT_TRANSLATE_NOOP("QShortcut", "Logoff") },
{ Qt::Key_Market, QT_TRANSLATE_NOOP("QShortcut", "Market") },
{ Qt::Key_Meeting, QT_TRANSLATE_NOOP("QShortcut", "Meeting") },
{ Qt::Key_Memo, QT_TRANSLATE_NOOP("QShortcut", "Memo") },
{ Qt::Key_MenuKB, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Menu") },
{ Qt::Key_MenuPB, QT_TRANSLATE_NOOP("QShortcut", "Menu PB") },
{ Qt::Key_MySites, QT_TRANSLATE_NOOP("QShortcut", "My Sites") },
@ -554,6 +559,7 @@ static const struct {
{ Qt::Key_Support, QT_TRANSLATE_NOOP("QShortcut", "Support") },
{ Qt::Key_TaskPane, QT_TRANSLATE_NOOP("QShortcut", "Task Panel") },
{ Qt::Key_Terminal, QT_TRANSLATE_NOOP("QShortcut", "Terminal") },
{ Qt::Key_ToDoList, QT_TRANSLATE_NOOP("QShortcut", "To-do list") },
{ Qt::Key_Tools, QT_TRANSLATE_NOOP("QShortcut", "Tools") },
{ Qt::Key_Travel, QT_TRANSLATE_NOOP("QShortcut", "Travel") },
{ Qt::Key_Video, QT_TRANSLATE_NOOP("QShortcut", "Video") },

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_2_Compatibility : public QAbstractOpenGLFunctions
@ -5632,6 +5638,10 @@ inline void QOpenGLFunctions_4_2_Compatibility::glVertexAttribI1i(GLuint index,
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_2_Core : public QAbstractOpenGLFunctions
@ -3027,6 +3033,10 @@ inline void QOpenGLFunctions_4_2_Core::glDrawArraysInstancedBaseInstance(GLenum
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_3_Compatibility : public QAbstractOpenGLFunctions
@ -5839,6 +5845,10 @@ inline void QOpenGLFunctions_4_3_Compatibility::glVertexAttribI1i(GLuint index,
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -57,6 +57,13 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_3_Core : public QAbstractOpenGLFunctions
@ -3230,6 +3237,10 @@ inline void QOpenGLFunctions_4_3_Core::glClearBufferData(GLenum target, GLenum i
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -59,6 +59,12 @@
QT_BEGIN_NAMESPACE
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
class Q_GUI_EXPORT QOpenGLFunctions_4_4_Compatibility : public QAbstractOpenGLFunctions
{
public:
@ -5961,6 +5967,10 @@ inline void QOpenGLFunctions_4_4_Compatibility::glVertexP2ui(GLenum type, GLuint
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_4_Core : public QAbstractOpenGLFunctions
@ -3415,6 +3421,10 @@ inline void QOpenGLFunctions_4_4_Core::glBufferStorage(GLenum target, GLsizeiptr
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_5_Compatibility : public QAbstractOpenGLFunctions
@ -6679,6 +6685,10 @@ inline void QOpenGLFunctions_4_5_Compatibility::glGetnMapdv(GLenum target, GLenu
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -57,6 +57,12 @@
#include <QtGui/QOpenGLVersionFunctions>
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QOpenGLFunctions_4_5_Core : public QAbstractOpenGLFunctions
@ -4056,6 +4062,11 @@ inline void QOpenGLFunctions_4_5_Core::glClipControl(GLenum origin, GLenum depth
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
#endif

View File

@ -61,6 +61,12 @@
#include <QtCore/qpair.h>
#include <QtGui/qopengl.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class QOpenGLContext;
@ -1897,6 +1903,10 @@ public:
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL
#endif

View File

@ -2961,7 +2961,7 @@ bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
return checker.get(ctx)->isSupported();
}
static QOpenGLProgramBinaryCache qrhi_programBinaryCache;
Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
{
@ -2995,7 +2995,7 @@ QRhiGles2::DiskCacheResult QRhiGles2::tryLoadFromDiskCache(const QRhiShaderStage
}
diskCacheKey = binaryProgram.cacheKey();
if (qrhi_programBinaryCache.load(diskCacheKey, program)) {
if (qrhi_programBinaryCache()->load(diskCacheKey, program)) {
qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
program, diskCacheKey.constData());
result = QRhiGles2::DiskCacheHit;
@ -3013,7 +3013,7 @@ void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
if (isProgramBinaryDiskCacheEnabled()) {
qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
program, cacheKey.constData());
qrhi_programBinaryCache.save(cacheKey, program);
qrhi_programBinaryCache()->save(cacheKey, program);
}
}

View File

@ -35,8 +35,6 @@
****************************************************************************/
#include "qrhimetal_p_p.h"
#include "qshader_p.h"
#include "qshaderdescription_p.h"
#include <QGuiApplication>
#include <QWindow>
#include <qmath.h>
@ -143,8 +141,10 @@ struct QMetalShader
id<MTLLibrary> lib = nil;
id<MTLFunction> func = nil;
std::array<uint, 3> localSize;
QShader::NativeResourceBindingMap nativeResourceBindingMap;
void release() {
nativeResourceBindingMap.clear();
[lib release];
lib = nil;
[func release];
@ -164,7 +164,7 @@ struct QRhiMetalData
const QRhiDepthStencilClearValue &depthStencilClearValue,
int colorAttCount);
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint);
QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
struct DeferredReleaseEntry {
@ -653,18 +653,40 @@ QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings()
return new QMetalShaderResourceBindings(this);
}
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
enum class BindingType {
Buffer,
Texture,
Sampler
};
static inline int mapBinding(int binding,
int stageIndex,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
BindingType type)
{
const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
if (map) {
auto it = map->constFind(binding);
if (it != map->cend())
return type == BindingType::Sampler ? it->second : it->first;
}
return binding;
}
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
bool offsetOnlyChange)
bool offsetOnlyChange,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
{
static const int KNOWN_STAGES = 3;
struct {
QRhiBatchedBindings<id<MTLBuffer> > buffers;
QRhiBatchedBindings<NSUInteger> bufferOffsets;
QRhiBatchedBindings<id<MTLTexture> > textures;
QRhiBatchedBindings<id<MTLSamplerState> > samplers;
} res[KNOWN_STAGES];
} res[SUPPORTED_STAGES];
enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
const QRhiShaderResourceBinding::Data *b = binding.data();
@ -682,16 +704,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
res[0].buffers.feed(b->binding, mtlbuf);
res[0].bufferOffsets.feed(b->binding, offset);
res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[VERTEX].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
res[1].buffers.feed(b->binding, mtlbuf);
res[1].bufferOffsets.feed(b->binding, offset);
res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
res[2].buffers.feed(b->binding, mtlbuf);
res[2].bufferOffsets.feed(b->binding, offset);
res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[COMPUTE].bufferOffsets.feed(b->binding, offset);
}
}
break;
@ -700,16 +722,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
res[0].textures.feed(b->binding, texD->d->tex);
res[0].samplers.feed(b->binding, samplerD->d->samplerState);
res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
res[VERTEX].samplers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
res[1].textures.feed(b->binding, texD->d->tex);
res[1].samplers.feed(b->binding, samplerD->d->samplerState);
res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
res[FRAGMENT].samplers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
res[2].textures.feed(b->binding, texD->d->tex);
res[2].samplers.feed(b->binding, samplerD->d->samplerState);
res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
res[COMPUTE].samplers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
}
}
break;
@ -722,11 +744,11 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage))
res[0].textures.feed(b->binding, t);
res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), t);
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
res[1].textures.feed(b->binding, t);
res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), t);
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
res[2].textures.feed(b->binding, t);
res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), t);
}
break;
case QRhiShaderResourceBinding::BufferLoad:
@ -739,16 +761,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
id<MTLBuffer> mtlbuf = bufD->d->buf[0];
uint offset = uint(b->u.sbuf.offset);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
res[0].buffers.feed(b->binding, mtlbuf);
res[0].bufferOffsets.feed(b->binding, offset);
res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[VERTEX].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
res[1].buffers.feed(b->binding, mtlbuf);
res[1].bufferOffsets.feed(b->binding, offset);
res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
res[2].buffers.feed(b->binding, mtlbuf);
res[2].bufferOffsets.feed(b->binding, offset);
res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
res[COMPUTE].bufferOffsets.feed(b->binding, offset);
}
}
break;
@ -758,25 +780,30 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
for (int idx = 0; idx < KNOWN_STAGES; ++idx) {
res[idx].buffers.finish();
res[idx].bufferOffsets.finish();
for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == VERTEX || stage == FRAGMENT))
continue;
if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
continue;
for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) {
const auto &bufferBatch(res[idx].buffers.batches[i]);
const auto &offsetBatch(res[idx].bufferOffsets.batches[i]);
switch (idx) {
case 0:
res[stage].buffers.finish();
res[stage].bufferOffsets.finish();
for (int i = 0, ie = res[stage].buffers.batches.count(); i != ie; ++i) {
const auto &bufferBatch(res[stage].buffers.batches[i]);
const auto &offsetBatch(res[stage].bufferOffsets.batches[i]);
switch (stage) {
case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break;
case 1:
case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break;
case 2:
case COMPUTE:
[cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
@ -790,21 +817,21 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
if (offsetOnlyChange)
continue;
res[idx].textures.finish();
res[idx].samplers.finish();
res[stage].textures.finish();
res[stage].samplers.finish();
for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) {
const auto &batch(res[idx].textures.batches[i]);
switch (idx) {
case 0:
for (int i = 0, ie = res[stage].textures.batches.count(); i != ie; ++i) {
const auto &batch(res[stage].textures.batches[i]);
switch (stage) {
case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
case 1:
case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
case 2:
case COMPUTE:
[cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
@ -813,18 +840,18 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break;
}
}
for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) {
const auto &batch(res[idx].samplers.batches[i]);
switch (idx) {
case 0:
for (int i = 0, ie = res[stage].samplers.batches.count(); i != ie; ++i) {
const auto &batch(res[stage].samplers.batches[i]);
switch (stage) {
case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
case 1:
case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
case 2:
case COMPUTE:
[cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
@ -973,18 +1000,22 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
// dynamic uniform buffer offsets always trigger a rebind
if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr };
if (gfxPsD) {
cbD->currentGraphicsSrb = srb;
cbD->currentComputeSrb = nullptr;
resBindMaps[0] = &gfxPsD->d->vs.nativeResourceBindingMap;
resBindMaps[1] = &gfxPsD->d->fs.nativeResourceBindingMap;
} else {
cbD->currentGraphicsSrb = nullptr;
cbD->currentComputeSrb = srb;
resBindMaps[2] = &compPsD->d->cs.nativeResourceBindingMap;
}
cbD->currentSrbGeneration = srbD->generation;
cbD->currentResSlot = resSlot;
const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange);
enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
}
}
@ -3081,9 +3112,10 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
}
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
QString *error, QByteArray *entryPoint)
QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
{
QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant });
QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant };
QShaderCode mtllib = shader.shader(key);
if (!mtllib.shader().isEmpty()) {
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
size_t(mtllib.shader().size()),
@ -3094,6 +3126,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
dispatch_release(data);
if (!err) {
*entryPoint = mtllib.entryPoint();
*activeKey = key;
return lib;
} else {
const QString msg = QString::fromNSString(err.localizedDescription);
@ -3101,7 +3134,8 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
}
QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant });
key = { QShader::MslShader, 12, shaderVariant };
QShaderCode mslSource = shader.shader(key);
if (mslSource.shader().isEmpty()) {
qWarning() << "No MSL 1.2 code found in baked shader" << shader;
return nil;
@ -3122,6 +3156,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
*entryPoint = mslSource.entryPoint();
*activeKey = key;
return lib;
}
@ -3195,9 +3230,12 @@ bool QMetalGraphicsPipeline::build()
break;
}
} else {
const QShader shader = shaderStage.shader();
QString error;
QByteArray entryPoint;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
QShaderKey activeKey;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
&error, &entryPoint, &activeKey);
if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false;
@ -3218,6 +3256,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Vertex:
d->vs.lib = lib;
d->vs.func = func;
if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
d->vs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->vs);
[d->vs.lib retain];
[d->vs.func retain];
@ -3226,6 +3266,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Fragment:
d->fs.lib = lib;
d->fs.func = func;
if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
d->fs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->fs);
[d->fs.lib retain];
[d->fs.func retain];
@ -3360,8 +3402,9 @@ bool QMetalComputePipeline::build()
const QShader shader = m_shaderStage.shader();
QString error;
QByteArray entryPoint;
QShaderKey activeKey;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
&error, &entryPoint);
&error, &entryPoint, &activeKey);
if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false;
@ -3375,6 +3418,8 @@ bool QMetalComputePipeline::build()
d->cs.lib = lib;
d->cs.func = func;
d->cs.localSize = shader.description().computeShaderLocalSize();
if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
d->cs.nativeResourceBindingMap = *map;
if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
for (QMetalShader &s : rhiD->d->shaderCache)

View File

@ -433,10 +433,13 @@ public:
qsizetype *curOfs);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
static const int SUPPORTED_STAGES = 3;
void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
bool offsetOnlyChange);
bool offsetOnlyChange,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
int effectiveSampleCount(int sampleCount) const;
bool importedDevice = false;

View File

@ -214,7 +214,8 @@ QT_BEGIN_NAMESPACE
QShader, it indicates no shader code was found for the requested key.
*/
static const int QSB_VERSION = 1;
static const int QSB_VERSION = 2;
static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
/*!
Constructs a new, empty (and thus invalid) QShader instance.
@ -345,6 +346,14 @@ void QShader::removeShader(const QShaderKey &key)
d->shaders.erase(it);
}
static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
{
*ds << k.source();
*ds << k.sourceVersion().version();
*ds << k.sourceVersion().flags();
*ds << k.sourceVariant();
}
/*!
\return a serialized binary version of all the data held by the
QShader, suitable for writing to files or other I/O devices.
@ -365,18 +374,42 @@ QByteArray QShader::serialized() const
ds << d->shaders.count();
for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
ds << k.source();
ds << k.sourceVersion().version();
ds << k.sourceVersion().flags();
ds << k.sourceVariant();
writeShaderKey(&ds, k);
const QShaderCode &shader(d->shaders.value(k));
ds << shader.shader();
ds << shader.entryPoint();
}
ds << d->bindings.count();
for (auto it = d->bindings.cbegin(), itEnd = d->bindings.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
writeShaderKey(&ds, k);
const NativeResourceBindingMap &map(it.value());
ds << map.count();
for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
ds << mapIt.key();
ds << mapIt.value().first;
ds << mapIt.value().second;
}
}
return qCompress(buf.buffer());
}
static void readShaderKey(QDataStream *ds, QShaderKey *k)
{
int intVal;
*ds >> intVal;
k->setSource(QShader::Source(intVal));
QShaderVersion ver;
*ds >> intVal;
ver.setVersion(intVal);
*ds >> intVal;
ver.setFlags(QShaderVersion::Flags(intVal));
k->setSourceVersion(ver);
*ds >> intVal;
k->setSourceVariant(QShader::Variant(intVal));
}
/*!
Creates a new QShader instance from the given \a data.
@ -396,8 +429,11 @@ QShader QShader::fromSerialized(const QByteArray &data)
Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
int intVal;
ds >> intVal;
if (intVal != QSB_VERSION)
const int qsbVersion = intVal;
if (qsbVersion != QSB_VERSION && qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) {
qWarning("Attempted to deserialize QShader with unknown version %d.", qsbVersion);
return QShader();
}
ds >> intVal;
d->stage = Stage(intVal);
@ -408,16 +444,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> count;
for (int i = 0; i < count; ++i) {
QShaderKey k;
ds >> intVal;
k.setSource(Source(intVal));
QShaderVersion ver;
ds >> intVal;
ver.setVersion(intVal);
ds >> intVal;
ver.setFlags(QShaderVersion::Flags(intVal));
k.setSourceVersion(ver);
ds >> intVal;
k.setSourceVariant(Variant(intVal));
readShaderKey(&ds, &k);
QShaderCode shader;
QByteArray s;
ds >> s;
@ -427,6 +454,27 @@ QShader QShader::fromSerialized(const QByteArray &data)
d->shaders[k] = shader;
}
if (qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) {
ds >> count;
for (int i = 0; i < count; ++i) {
QShaderKey k;
readShaderKey(&ds, &k);
NativeResourceBindingMap map;
int mapSize;
ds >> mapSize;
for (int b = 0; b < mapSize; ++b) {
int binding;
ds >> binding;
int firstNativeBinding;
ds >> firstNativeBinding;
int secondNativeBinding;
ds >> secondNativeBinding;
map.insert(binding, { firstNativeBinding, secondNativeBinding });
}
d->bindings.insert(k, map);
}
}
return bs;
}
@ -460,7 +508,7 @@ bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
{
return lhs.d->stage == rhs.d->stage
&& lhs.d->shaders == rhs.d->shaders;
// do not bother with desc, if the shader code is the same, the description must match too
// do not bother with desc and bindings, if the shader code is the same, the description must match too
}
/*!
@ -586,4 +634,66 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
}
#endif // QT_NO_DEBUG_STREAM
/*!
\typedef QShader::NativeResourceBindingMap
Synonym for QHash<int, QPair<int, int>>.
The resource binding model QRhi assumes is based on SPIR-V. This means that
uniform buffers, storage buffers, combined image samplers, and storage
images share a common binding point space. The binding numbers in
QShaderDescription and QRhiShaderResourceBinding are expected to match the
\c binding layout qualifier in the Vulkan-compatible GLSL shader.
Graphics APIs other than Vulkan may use a resource binding model that is
not fully compatible with this. In addition, the generator of the shader
code translated from SPIR-V may choose not to take the SPIR-V binding
qualifiers into account, for various reasons. (this is the case with the
Metal backend of SPIRV-Cross, for example).
Therefore, a QShader may expose an additional map that describes what the
native binding point for a given SPIR-V binding is. The QRhi backends are
expected to use this map automatically, as appropriate. The value is a
pair, because combined image samplers may map to two native resources (a
texture and a sampler) in some shading languages. In that case the second
value refers to the sampler.
*/
/*!
\return the native binding map for \a key or null if no extra mapping is
available, or is not applicable.
*/
const QShader::NativeResourceBindingMap *QShader::nativeResourceBindingMap(const QShaderKey &key) const
{
auto it = d->bindings.constFind(key);
if (it == d->bindings.cend())
return nullptr;
return &it.value();
}
/*!
Stores the given native resource binding \a map associated with \a key.
\sa nativeResourceBindingMap()
*/
void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
{
detach();
d->bindings[key] = map;
}
/*!
Removes the native resource binding map for \a key.
*/
void QShader::removeResourceBindingMap(const QShaderKey &key)
{
auto it = d->bindings.find(key);
if (it == d->bindings.end())
return;
detach();
d->bindings.erase(it);
}
QT_END_NAMESPACE

View File

@ -149,6 +149,11 @@ public:
QByteArray serialized() const;
static QShader fromSerialized(const QByteArray &data);
using NativeResourceBindingMap = QHash<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
const NativeResourceBindingMap *nativeResourceBindingMap(const QShaderKey &key) const;
void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
void removeResourceBindingMap(const QShaderKey &key);
private:
QShaderPrivate *d;
friend struct QShaderPrivate;

View File

@ -66,7 +66,8 @@ struct Q_GUI_EXPORT QShaderPrivate
: ref(1),
stage(other->stage),
desc(other->desc),
shaders(other->shaders)
shaders(other->shaders),
bindings(other->bindings)
{
}
@ -77,6 +78,7 @@ struct Q_GUI_EXPORT QShaderPrivate
QShader::Stage stage = QShader::VertexStage;
QShaderDescription desc;
QHash<QShaderKey, QShaderCode> shaders;
QHash<QShaderKey, QShader::NativeResourceBindingMap> bindings;
};
QT_END_NAMESPACE

View File

@ -2088,8 +2088,12 @@ void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *pain
tl->draw(painter, offset, selections, context.clip.isValid() ? (context.clip & clipRect) : clipRect);
if ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
|| (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty())) {
// if the block is empty and it precedes a table, do not draw the cursor.
// the cursor is drawn later after the table has been drawn so no need
// to draw it here.
if (!isEmptyBlockBeforeTable(frameIteratorForTextPosition(blpos))
&& ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
|| (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty()))) {
int cpos = context.cursorPosition;
if (cpos < -1)
cpos = tl->preeditAreaPosition() - (cpos + 2);

View File

@ -60,6 +60,12 @@
#include "qopenglextensions.h"
#include <QtGui/qopenglcontext.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
QAbstractOpenGLExtension::~QAbstractOpenGLExtension()
@ -7720,3 +7726,6 @@ bool QOpenGLExtension_QCOM_tiled_rendering::initializeOpenGLFunctions()
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif

View File

@ -66,6 +66,12 @@
#include <QtGui/qopengl.h>
// MemoryBarrier is a macro on some architectures on Windows
#ifdef Q_OS_WIN
#pragma push_macro("MemoryBarrier")
#undef MemoryBarrier
#endif
QT_BEGIN_NAMESPACE
class QOpenGLContext;
@ -19473,6 +19479,10 @@ inline void QOpenGLExtension_QCOM_tiled_rendering::glEndTilingQCOM(GLbitfield pr
QT_END_NAMESPACE
#ifdef Q_OS_WIN
#pragma pop_macro("MemoryBarrier")
#endif
#endif // QT_NO_OPENGL
#endif

View File

@ -195,20 +195,6 @@ namespace QtAndroidInput
angleDelta);
}
void releaseMouse(int x, int y)
{
m_ignoreMouseEvents = true;
QPoint globalPos(x,y);
QWindow *tlw = topLevelWindowAt(globalPos);
QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos;
// Release left button
QWindowSystemInterface::handleMouseEvent(tlw,
localPos,
globalPos,
Qt::MouseButtons(Qt::NoButton));
}
static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
{
QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext();

View File

@ -61,8 +61,6 @@ namespace QtAndroidInput
void updateHandles(int handleCount, QPoint editMenuPos = QPoint(), uint32_t editButtons = 0, QPoint cursor = QPoint(), QPoint anchor = QPoint(), bool rtl = false);
bool registerNatives(JNIEnv *env);
void releaseMouse(int x, int y);
}
QT_END_NAMESPACE

View File

@ -827,9 +827,6 @@ void QAndroidInputContext::longPress(int x, int y)
focusObjectStopComposing();
// Release left button, otherwise the following events will cancel the menu popup
QtAndroidInput::releaseMouse(x, y);
const double pixelDensity =
QGuiApplication::focusWindow()
? QHighDpiScaling::factor(QGuiApplication::focusWindow())

View File

@ -50,6 +50,8 @@
QT_BEGIN_NAMESPACE
class QCocoaWindow;
class QCocoaGLContext : public QPlatformOpenGLContext
{
public:
@ -76,12 +78,12 @@ private:
static NSOpenGLPixelFormat *pixelFormatForSurfaceFormat(const QSurfaceFormat &format);
bool setDrawable(QPlatformSurface *surface);
void prepareDrawable(QCocoaWindow *platformWindow);
void updateSurfaceFormat();
NSOpenGLContext *m_context = nil;
NSOpenGLContext *m_shareContext = nil;
QSurfaceFormat m_format;
bool m_didCheckForSoftwareContext = false;
QVarLengthArray<QMacNotificationObserver, 3> m_updateObservers;
QAtomicInt m_needsUpdate = false;
};

View File

@ -223,12 +223,10 @@ NSOpenGLPixelFormat *QCocoaGLContext::pixelFormatForSurfaceFormat(const QSurface
attrs << NSOpenGLPFAAllowOfflineRenderers;
}
// FIXME: Pull this information out of the NSView
QByteArray useLayer = qgetenv("QT_MAC_WANTS_LAYER");
if (!useLayer.isEmpty() && useLayer.toInt() > 0) {
// Disable the software rendering fallback. This makes compositing
// OpenGL and raster NSViews using Core Animation layers possible.
attrs << NSOpenGLPFANoRecovery;
if (qGuiApp->testAttribute(Qt::AA_UseSoftwareOpenGL)) {
// kCGLRendererGenericFloatID is the modern software renderer on macOS,
// as opposed to kCGLRendererGenericID, which is deprecated.
attrs << NSOpenGLPFARendererID << kCGLRendererGenericFloatID;
}
attrs << 0; // 0-terminate array
@ -368,23 +366,6 @@ bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
[m_context makeCurrentContext];
if (surface->surface()->surfaceClass() == QSurface::Window) {
// Disable high-resolution surfaces when using the software renderer, which has the
// problem that the system silently falls back to a to using a low-resolution buffer
// when a high-resolution buffer is requested. This is not detectable using the NSWindow
// convertSizeToBacking and backingScaleFactor APIs. A typical result of this is that Qt
// will display a quarter of the window content when running in a virtual machine.
if (!m_didCheckForSoftwareContext) {
// FIXME: This ensures we check only once per context,
// but the context may be used for multiple surfaces.
m_didCheckForSoftwareContext = true;
const GLubyte* renderer = glGetString(GL_RENDERER);
if (qstrcmp((const char *)renderer, "Apple Software Renderer") == 0) {
NSView *view = static_cast<QCocoaWindow *>(surface)->m_view;
[view setWantsBestResolutionOpenGLSurface:NO];
}
}
if (m_needsUpdate.fetchAndStoreRelaxed(false))
update();
}
@ -413,11 +394,14 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
}
Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(surface)->view());
auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
QNSView *view = qnsview_cast(cocoaWindow->view());
if (view == m_context.view)
return true;
prepareDrawable(cocoaWindow);
// Setting the drawable may happen on a separate thread as a result of
// a call to makeCurrent, so we need to set up the observers before we
// associate the view with the context. That way we will guarantee that
@ -460,6 +444,30 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
return true;
}
void QCocoaGLContext::prepareDrawable(QCocoaWindow *platformWindow)
{
// We generally want high-DPI GL surfaces, unless the user has explicitly disabled them
bool prefersBestResolutionOpenGLSurface = qt_mac_resolveOption(YES,
platformWindow->window(), "_q_mac_wantsBestResolutionOpenGLSurface",
"QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
auto *view = platformWindow->view();
// The only case we have to opt out ourselves is when using the Apple software renderer
// in combination with surface-backed views, as these together do not support high-DPI.
if (prefersBestResolutionOpenGLSurface) {
int rendererID = 0;
[m_context getValues:&rendererID forParameter:NSOpenGLContextParameterCurrentRendererID];
bool isSoftwareRenderer = (rendererID & kCGLRendererIDMatchingMask) == kCGLRendererGenericFloatID;
if (isSoftwareRenderer && !view.layer) {
qCInfo(lcQpaOpenGLContext) << "Disabling high resolution GL surface due to software renderer";
prefersBestResolutionOpenGLSurface = false;
}
}
view.wantsBestResolutionOpenGLSurface = prefersBestResolutionOpenGLSurface;
}
// NSOpenGLContext is not re-entrant. Even when using separate contexts per thread,
// view, and window, calls into the API will still deadlock. For more information
// see https://openradar.appspot.com/37064579
@ -491,6 +499,21 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
return;
}
if (m_context.view.layer) {
// Flushing an NSOpenGLContext will hit the screen immediately, ignoring
// any Core Animation transactions in place. This may result in major
// visual artifacts if the flush happens out of sync with the size
// of the layer, view, and window reflected by other parts of the UI,
// e.g. if the application flushes in the resize event or a timer during
// window resizing, instead of in the expose event.
auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)."
<< "Skipping flush to avoid visual artifacts.";
return;
}
}
QMutexLocker locker(&s_reentrancyMutex);
[m_context flushBuffer];
}

View File

@ -44,17 +44,6 @@
- (void)initDrawing
{
[self updateLayerBacking];
// Enable high-DPI OpenGL for retina displays. Enabling has the side
// effect that Cocoa will start calling glViewport(0, 0, width, height),
// overriding any glViewport calls in application code. This is usually not a
// problem, except if the application wants to have a "custom" viewport.
// (like the hellogl example)
if (m_platformWindow->window()->supportsOpenGL()) {
self.wantsBestResolutionOpenGLSurface = qt_mac_resolveOption(YES, m_platformWindow->window(),
"_q_mac_wantsBestResolutionOpenGLSurface", "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
// See also QCocoaGLContext::makeCurrent for software renderer workarounds.
}
}
- (BOOL)isOpaque

View File

@ -59,6 +59,8 @@ QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode, const QString &devi
QString id = deviceId;
if (id.isEmpty())
id = QCocoaPrinterSupport().defaultPrintDeviceId();
else
setProperty(QPrintEngine::PPK_PrinterName, deviceId);
d->m_printDevice.reset(new QCocoaPrintDevice(id));
d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize());
d->initialize();

View File

@ -221,18 +221,18 @@ public:
int disconnectCount;
bool hasSQLFetchScroll;
bool isStmtHandleValid();
bool isStmtHandleValid() const;
void updateStmtHandleState();
};
bool QODBCResultPrivate::isStmtHandleValid()
bool QODBCResultPrivate::isStmtHandleValid() const
{
return disconnectCount == drv_d_func()->disconnectCount;
return drv_d_func() && disconnectCount == drv_d_func()->disconnectCount;
}
void QODBCResultPrivate::updateStmtHandleState()
{
disconnectCount = drv_d_func()->disconnectCount;
disconnectCount = drv_d_func() ? drv_d_func()->disconnectCount : 0;
}
static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0)
@ -975,7 +975,7 @@ QODBCResult::QODBCResult(const QODBCDriver *db)
QODBCResult::~QODBCResult()
{
Q_D(QODBCResult);
if (d->hStmt && d->isStmtHandleValid() && driver()->isOpen()) {
if (d->hStmt && d->isStmtHandleValid() && driver() && driver()->isOpen()) {
SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@ -296,3 +296,31 @@ QTest::keyClick(myWindow, Qt::Key_Escape, Qt::ShiftModifier, 200);
}
//! [30]
void TestQLocale::initTestCase_data()
{
QTest::addColumn<QLocale>("locale");
QTest::newRow("C") << QLocale::c();
QTest::newRow("UKish") << QLocale("en_GB");
QTest::newRow("USAish") << QLocale(QLocale::English);
}
void TestQLocale::roundTripInt_data()
{
QTest::addColumn<int>("number");
QTest::newRow("one") << 1;
QTest::newRow("two") << 2;
QTest::newRow("ten") << 10;
}
//! [30]
//! [31]
void TestQLocale::roundTripInt()
{
QFETCH_GLOBAL(QLocale, locale);
QFETCH(int, number);
bool ok;
QCOMPARE(locale.toInt(locale.toString(number), &ok), number);
QVERIFY(ok);
}
//! [31]

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@ -89,12 +89,14 @@
private slot is a test function in your test. QTest::qExec() can be used to execute
all test functions in the test object.
In addition, there are four private slots that are \e not treated as test functions.
They will be executed by the testing framework and can be used to initialize and
clean up either the entire test or the current test function.
In addition, you can define the following private slots that are \e not
treated as test functions. When present, they will be executed by the
testing framework and can be used to initialize and clean up either the
entire test or the current test function.
\list
\li \c{initTestCase()} will be called before the first test function is executed.
\li \c{initTestCase_data()} will be called to create a global test data table.
\li \c{cleanupTestCase()} will be called after the last test function was executed.
\li \c{init()} will be called before each test function is executed.
\li \c{cleanup()} will be called after every test function.
@ -358,18 +360,44 @@
counters can be obtained by running any benchmark executable with the
option \c -perfcounterlist.
\list
\li \b Notes:
\note
\list
\li Using the performance counter may require enabling access to non-privileged
applications.
\li Devices that do not support high-resolution timers default to
one-millisecond granularity.
\endlist
\endlist
See \l {Chapter 5: Writing a Benchmark}{Writing a Benchmark} in the Qt Test
Tutorial for more benchmarking examples.
\section1 Using Global Test Data
You can define \c{initTestCase_data()} to set up a global test data table.
Each test is run once for each row in the global test data table. When the
test function itself \l{Chapter 2: Data-driven Testing}{is data-driven},
it is run for each local data row, for each global data row. So, if there
are \c g rows in the global data table and \c d rows in the test's own
data-table, the number of runs of this test is \c g times \c d.
Global data is fetched from the table using the \l QFETCH_GLOBAL() macro.
The following are typical use cases for global test data:
\list
\li Selecting among the available database backends in QSql tests to run
every test against every database.
\li Doing all networking tests with and without SSL (HTTP versus HTTPS)
and proxying.
\li Testing a timer with a high precision clock and with a coarse one.
\li Selecting whether a parser shall read from a QByteArray or from a
QIODevice.
\endlist
For example, to test each number provided by \c {roundTripInt_data()} with
each locale provided by \c {initTestCase_data()}:
\snippet code/src_qtestlib_qtestcase.cpp 31
*/
/*!
@ -513,10 +541,9 @@
QTest::newRow() function. Each set of data will become a
separate row in the test table.
\l QTest::newRow() takes one argument: a name that will be
associated with the data set. If the test fails, the name will be
used in the test log, referencing the failed data. Then we
stream the data set into the new table row. First an arbitrary
\l QTest::newRow() takes one argument: a name that will be associated
with the data set and used in the test log to identify the data set.
Then we stream the data set into the new table row. First an arbitrary
string, and then the expected result of applying the
QString::toUpper() function to that string.
@ -548,6 +575,10 @@
\li HELLO
\endtable
When data is streamed into the row, each datum is asserted to match
the type of the column whose value it supplies. If any assertion fails,
the test is aborted.
\section1 Rewriting the Test Function
Our test function can now be rewritten:

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@ -48,7 +48,7 @@
\snippet code/doc_src_qsignalspy.cpp 1
\b {Note:} Non-standard data types need to be registered, using
\note Non-standard data types need to be registered, using
the qRegisterMetaType() function, before you can create a
QSignalSpy. For example:
@ -57,6 +57,18 @@
To retrieve the instance, you can use qvariant_cast:
\snippet code/doc_src_qsignalspy.cpp 3
\section1 Verifying Signal Emissions
The QSignalSpy class provides an elegant mechanism for capturing the list
of signals emitted by an object. However, you should verify its validity
after construction. The constructor does a number of sanity checks, such as
verifying that the signal to be spied upon actually exists. To make the
diagnosis of test failures easier, the results of these checks should be
checked by calling \c QVERIFY(spy.isValid()) before proceeding further with
a test.
\sa QVERIFY()
*/
/*! \fn QSignalSpy::QSignalSpy(const QObject *object, const char *signal)

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@ -220,8 +220,8 @@
\relates QTest
The fetch macro creates a local variable named \a name with the type \a type
on the stack. \a name has to match the element name from the test's data.
If no such element exists, the test will assert.
on the stack. The \a name and \a type must match a column from the test's
data table. This is asserted and the test will abort if the assertion fails.
Assuming a test has the following data:
@ -235,10 +235,37 @@
\c aString and \c expected are variables on the stack that are initialized with
the current test data.
\b {Note:} This macro can only be used in a test function that is invoked
\note This macro can only be used in a test function that is invoked
by the test framework. The test function must have a _data function.
*/
/*! \macro QFETCH_GLOBAL(type, name)
\relates QTest
This macro fetches a variable named \a name with the type \a type from
a row in the global data table. The \a name and \a type must match a
column in the global data table. This is asserted and the test will abort
if the assertion fails.
Assuming a test has the following data:
\snippet code/src_qtestlib_qtestcase.cpp 30
The test's own data is a single number per row. In this case,
\c initTestCase_data() also supplies a locale per row. Therefore,
this test will be run with every combination of locale from the
latter and number from the former.
\snippet code/src_qtestlib_qtestcase.cpp 31
The locale is read from the global data table using QFETCH_GLOBAL(),
and the number is read from the local data table using QFETCH().
\note This macro can only be used in test methods of a class with an
\c initTestCase_data() method.
*/
/*! \macro QWARN(message)
\relates QTest
@ -255,7 +282,7 @@
This macro can be used to force a test failure. The test stops
executing and the failure \a message is appended to the test log.
\b {Note:} This macro can only be used in a test function that is invoked
\note This macro can only be used in a test function that is invoked
by the test framework.
Example:
@ -332,7 +359,7 @@
\a mode is a \l QTest::TestFailMode and sets whether the test should
continue to execute or not.
\b {Note:} This macro can only be used in a test function that is invoked
\note This macro can only be used in a test function that is invoked
by the test framework.
Example 1:
@ -394,13 +421,13 @@
test has been installed, and regardless of whether the test's build tree
is equal to the test's source tree.
\b {Note:} reliable detection of testdata from the source directory requires
\note reliable detection of testdata from the source directory requires
either that qmake is used, or the \c{QT_TESTCASE_BUILDDIR} macro is defined to
point to the working directory from which the compiler is invoked, or only
absolute paths to the source files are passed to the compiler. Otherwise, the
absolute path of the source directory cannot be determined.
\b {Note:} For tests that use the \l QTEST_APPLESS_MAIN() macro to generate a
\note For tests that use the \l QTEST_APPLESS_MAIN() macro to generate a
\c{main()} function, \c{QFINDTESTDATA} will not attempt to find test data
relative to QCoreApplication::applicationDirPath(). In practice, this means that
tests using \c{QTEST_APPLESS_MAIN()} will fail to find their test data
@ -422,7 +449,7 @@
Similarly, if qmake is used and the configuration includes \c{QT += gui}, then
\c QT_GUI_LIB will be defined automatically.
\b {Note:} On platforms that have keypad navigation enabled by default,
\note On platforms that have keypad navigation enabled by default,
this macro will forcefully disable it if \c QT_WIDGETS_LIB is defined. This is done
to simplify the usage of key events when writing autotests. If you wish to write a
test case that uses keypad navigation, you should enable it either in the
@ -662,7 +689,7 @@
Simulates pressing a \a key with an optional \a modifier on a \a widget. If \a delay
is larger than 0, the test will wait for \a delay milliseconds before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease().
\note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick()
*/
@ -674,7 +701,7 @@
If \a delay is larger than 0, the test will wait for \a delay milliseconds
before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease().
\note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick()
*/
@ -686,7 +713,7 @@
Simulates pressing a \a key with an optional \a modifier on a \a window. If \a delay
is larger than 0, the test will wait for \a delay milliseconds before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease().
\note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick()
*/
@ -699,7 +726,7 @@
If \a delay is larger than 0, the test will wait for \a delay milliseconds
before pressing the key.
\b {Note:} At some point you should release the key using \l keyRelease().
\note At some point you should release the key using \l keyRelease().
\sa QTest::keyRelease(), QTest::keyClick()
*/
@ -920,12 +947,12 @@
You can add specializations or overloads of this function to your test to enable
verbose output.
\b {Note:} Starting with Qt 5.5, you should prefer to provide a toString() function
\note Starting with Qt 5.5, you should prefer to provide a toString() function
in the type's namespace instead of specializing this template.
If your code needs to continue to work with the QTestLib from Qt 5.4 or
earlier, you need to continue to use specialization.
\b {Note:} The caller of toString() must delete the returned data
\note The caller of toString() must delete the returned data
using \c{delete[]}. Your implementation should return a string
created with \c{new[]} or qstrdup(). The easiest way to do so is to
create a QByteArray or QString and calling QTest::toString() on it

View File

@ -1107,10 +1107,10 @@ QMenu::indicator:exclusive:checked:selected {
QMenuBar {
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 lightgray, stop:1 darkgray);
spacing: 3px; /* spacing between menu bar items */
}
QMenuBar::item {
spacing: 3px; /* spacing between menu bar items */
padding: 1px 4px;
background: transparent;
border-radius: 4px;

View File

@ -386,7 +386,15 @@ void QGraphicsScenePrivate::_q_emitUpdated()
// Notify the changes to anybody interested.
QList<QRectF> oldUpdatedRects;
oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects;
if (updateAll) {
oldUpdatedRects << q->sceneRect();
} else {
// Switch to a ranged constructor in Qt 6...
oldUpdatedRects.reserve(int(updatedRects.size()));
std::copy(updatedRects.cbegin(), updatedRects.cend(),
std::back_inserter(oldUpdatedRects));
}
updateAll = false;
updatedRects.clear();
emit q->changed(oldUpdatedRects);
@ -3219,8 +3227,7 @@ void QGraphicsScene::update(const QRectF &rect)
view->d_func()->updateRectF(rect);
}
} else {
if (!d->updatedRects.contains(rect))
d->updatedRects << rect;
d->updatedRects.insert(rect);
}
}

View File

@ -69,6 +69,9 @@
#include <QtWidgets/qstyle.h>
#include <QtWidgets/qstyleoption.h>
#include <set>
#include <tuple>
QT_REQUIRE_CONFIG(graphicsview);
QT_BEGIN_NAMESPACE
@ -122,7 +125,19 @@ public:
QRectF growingItemsBoundingRect;
void _q_emitUpdated();
QList<QRectF> updatedRects;
struct UpdatedRectsCmp
{
bool operator() (const QRectF &a, const QRectF &b) const noexcept
{
return std::make_tuple(a.y(), a.x(), a.height(), a.width())
< std::make_tuple(b.y(), b.x(), b.height(), b.width());
}
};
// std::set was used here instead of std::unordered_set due to requiring only a comparator and
// showing equivalent performance in empirical measurements within the ranges of interest...
std::set<QRectF, UpdatedRectsCmp> updatedRects;
QPainterPath selectionArea;
int selectionChanging;

View File

@ -514,15 +514,6 @@ void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,
QStyle *style = widget ? widget->style() : QApplication::style();
QRect geom = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget);
const int delta = qSmartMinSize(editor).width() - geom.width();
if (delta > 0) {
//we need to widen the geometry
if (editor->layoutDirection() == Qt::RightToLeft)
geom.adjust(-delta, 0, 0, 0);
else
geom.adjust(0, 0, delta, 0);
}
editor->setGeometry(geom);
}

View File

@ -3331,7 +3331,7 @@ void QTableViewPrivate::selectRow(int row, bool anchor)
if (q->selectionMode() != QTableView::SingleSelection
&& command.testFlag(QItemSelectionModel::Toggle)) {
if (anchor)
ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows().contains(index)
ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows(column).contains(index)
? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
command &= ~QItemSelectionModel::Toggle;
command |= ctrlDragSelectionFlag;

View File

@ -893,7 +893,7 @@ void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget
void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
{
auto *widget = findDnDTarget(m_widget, event->pos());
QPointer<QWidget> widget = findDnDTarget(m_widget, event->pos());
if (!widget) {
event->ignore();
if (m_dragTarget) { // Send DragLeave to previous
@ -916,14 +916,18 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event);
m_dragTarget = nullptr;
}
// Send DragEnter to new widget.
handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget);
// Handling 'DragEnter' should suffice for the application.
translated.setDropAction(event->dropAction());
translated.setAccepted(event->isAccepted());
// The drag enter event is always immediately followed by a drag move event,
// see QDragEnterEvent documentation.
QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
// widget might have been deleted when handling the leaveEvent
if (widget) {
// Send DragEnter to new widget.
handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget);
// Handling 'DragEnter' should suffice for the application.
translated.setDropAction(event->dropAction());
translated.setAccepted(event->isAccepted());
// The drag enter event is always immediately followed by a drag move event,
// see QDragEnterEvent documentation.
if (m_dragTarget)
QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
}
}
event->setAccepted(translated.isAccepted());
event->setDropAction(translated.dropAction());

View File

@ -550,6 +550,10 @@ void tst_QFile::exists()
QFile unc(uncPath);
QVERIFY2(unc.exists(), msgFileDoesNotExist(uncPath).constData());
#endif
QTest::ignoreMessage(QtWarningMsg, "Broken filename passed to function");
QVERIFY(!QFile::exists(QDir::currentPath() + QLatin1Char('/') +
QChar(QChar::Null) + QLatin1String("x/y")));
}
void tst_QFile::open_data()

View File

@ -465,16 +465,6 @@ void tst_qstandardpaths::testRuntimeDirectory()
#ifdef Q_XDG_PLATFORM
const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY(!runtimeDir.isEmpty());
// Check that it can automatically fix permissions
QFile file(runtimeDir);
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
const QFile::Permissions additionalPerms = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
QCOMPARE(file.permissions(), wantedPerms | additionalPerms);
QVERIFY(file.setPermissions(wantedPerms | QFile::ExeGroup));
const QString runtimeDirAgain = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QCOMPARE(runtimeDirAgain, runtimeDir);
QCOMPARE(QFile(runtimeDirAgain).permissions(), wantedPerms | additionalPerms);
#endif
}
@ -516,11 +506,27 @@ void tst_qstandardpaths::testCustomRuntimeDirectory()
const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY2(runtimeDir.isEmpty(), qPrintable(runtimeDir));
// When $XDG_RUNTIME_DIR points to a non-existing directory, QStandardPaths should warn (QTBUG-48771)
qputenv("XDG_RUNTIME_DIR", "does_not_exist");
QTest::ignoreMessage(QtWarningMsg, "QStandardPaths: XDG_RUNTIME_DIR points to non-existing path 'does_not_exist', please create it with 0700 permissions.");
// When $XDG_RUNTIME_DIR points to a directory with wrong permissions, QStandardPaths should warn
const QByteArray wrongPermissionFileName = "wrong_permissions";
QDir::current().mkdir(wrongPermissionFileName);
QFile wrongPermissionFile(wrongPermissionFileName);
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
QVERIFY(wrongPermissionFile.setPermissions(wantedPerms | QFile::ExeGroup));
qputenv("XDG_RUNTIME_DIR", wrongPermissionFileName);
QTest::ignoreMessage(QtWarningMsg,
qPrintable(QString::fromLatin1("QStandardPaths: wrong permissions on runtime directory " + wrongPermissionFileName + ", 7710 instead of 7700")));
const QString wrongPermissionRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY(wrongPermissionRuntimeDir.isEmpty());
QDir::current().rmdir(wrongPermissionFileName);
// When $XDG_RUNTIME_DIR points to a non-existing directory, QStandardPaths should create it first
const QByteArray nonExistingDir = "does_not_exist";
qputenv("XDG_RUNTIME_DIR", nonExistingDir);
const QString nonExistingRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY2(nonExistingRuntimeDir.isEmpty(), qPrintable(nonExistingRuntimeDir));
QVERIFY2(!nonExistingRuntimeDir.compare(nonExistingDir), qPrintable(nonExistingRuntimeDir));
QVERIFY(QDir::current().exists(nonExistingRuntimeDir));
QDir::current().rmdir(nonExistingRuntimeDir);
// When $XDG_RUNTIME_DIR points to a file, QStandardPaths should warn
const QString file = QFINDTESTDATA("tst_qstandardpaths.cpp");

View File

@ -338,7 +338,7 @@ void tst_QStateMachine::transitionToRootState()
machine.postEvent(new QEvent(QEvent::User));
QTest::ignoreMessage(QtWarningMsg,
"Unrecoverable error detected in running state machine: "
"Child mode of state machine 'machine' is not 'ExclusiveStates'!");
"Child mode of state machine 'machine' is not 'ExclusiveStates'.");
QCoreApplication::processEvents();
QVERIFY(machine.configuration().isEmpty());
QVERIFY(!machine.isRunning());
@ -1064,7 +1064,7 @@ void tst_QStateMachine::transitionToStateNotInGraph()
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
"Child mode of state machine '' is not 'ExclusiveStates'!");
"Child mode of state machine '' is not 'ExclusiveStates'.");
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
@ -2103,7 +2103,7 @@ void tst_QStateMachine::parallelRootState()
QVERIFY(finishedSpy.isValid());
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
"Child mode of state machine '' is not 'ExclusiveStates'!");
"Child mode of state machine '' is not 'ExclusiveStates'.");
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 4);
QVERIFY(machine.configuration().contains(s1));
@ -3316,7 +3316,7 @@ void tst_QStateMachine::targetStateWithNoParent()
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
"Child mode of state machine '' is not 'ExclusiveStates'!");
"Child mode of state machine '' is not 'ExclusiveStates'.");
TEST_ACTIVE_CHANGED(s1, 2);
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(machine.isRunning(), false);

View File

@ -40,6 +40,7 @@
#include "nontracked.h"
#include "wrapper.h"
#include <array>
#include <memory>
#include <stdlib.h>
#include <time.h>
@ -106,12 +107,15 @@ private slots:
void sharedFromThis();
void constructorThrow();
void overloads();
void threadStressTest_data();
void threadStressTest();
void validConstructs();
void invalidConstructs_data();
void invalidConstructs();
// let invalidConstructs be the last test, because it's the slowest;
// add new tests above this block
public slots:
@ -2383,6 +2387,11 @@ void tst_QSharedPointer::invalidConstructs_data()
QTest::newRow("incompatible-custom-lambda-deleter")
<< &QTest::QExternalTest::tryCompileFail
<< "QSharedPointer<Data> ptr(new Data, [](int *) {});\n";
QTest::newRow("incompatible-overload")
<< &QTest::QExternalTest::tryCompileFail
<< "void foo(QSharedPointer<DerivedData>) {}\n"
"void bar() { foo(QSharedPointer<Data>()); }\n";
}
void tst_QSharedPointer::invalidConstructs()
@ -2883,5 +2892,50 @@ void tst_QSharedPointer::reentrancyWhileDestructing()
ReentrancyWhileDestructing::A obj;
}
namespace {
struct Base1 {};
struct Base2 {};
struct Child1 : Base1 {};
struct Child2 : Base2 {};
template<template<typename> class SmartPtr>
struct Overloaded
{
std::array<int, 1> call(const SmartPtr<const Base1> &)
{
return {};
}
std::array<int, 2> call(const SmartPtr<const Base2> &)
{
return {};
}
static const Q_CONSTEXPR uint base1Called = sizeof(std::array<int, 1>);
static const Q_CONSTEXPR uint base2Called = sizeof(std::array<int, 2>);
void test()
{
#define QVERIFY_CALLS(expr, base) Q_STATIC_ASSERT(sizeof(call(expr)) == base##Called)
QVERIFY_CALLS(SmartPtr<Base1>{}, base1);
QVERIFY_CALLS(SmartPtr<Base2>{}, base2);
QVERIFY_CALLS(SmartPtr<const Base1>{}, base1);
QVERIFY_CALLS(SmartPtr<const Base2>{}, base2);
QVERIFY_CALLS(SmartPtr<Child1>{}, base1);
QVERIFY_CALLS(SmartPtr<Child2>{}, base2);
QVERIFY_CALLS(SmartPtr<const Child1>{}, base1);
QVERIFY_CALLS(SmartPtr<const Child2>{}, base2);
#undef QVERIFY_CALLS
}
};
}
void tst_QSharedPointer::overloads()
{
Overloaded<QSharedPointer> sharedOverloaded;
sharedOverloaded.test();
Overloaded<QWeakPointer> weakOverloaded;
weakOverloaded.test();
}
QTEST_MAIN(tst_QSharedPointer)
#include "tst_qsharedpointer.moc"

View File

@ -2132,6 +2132,12 @@ void tst_QImage::paintEngine()
QCOMPARE(engine, img.paintEngine());
QCOMPARE(img, expected);
{
QImage img1(16, 16, QImage::Format_ARGB32);
QImage img2 = img1;
QVERIFY(img2.paintEngine());
}
}
void tst_QImage::setAlphaChannelWhilePainting()

View File

@ -1131,7 +1131,7 @@ void tst_QDtls::handshakeReadyRead()
QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
Q_ASSERT(socket);
if (!socket->pendingDatagramSize())
if (socket->pendingDatagramSize() <= 0)
return;
const bool isServer = socket == &serverSocket;

View File

@ -352,7 +352,7 @@ void tst_QDtlsCookie::receiveMessage(QUdpSocket *socket, QByteArray *message,
{
Q_ASSERT(socket && message);
if (!socket->pendingDatagramSize())
if (socket->pendingDatagramSize() <= 0)
testLoop.enterLoopMSecs(handshakeTimeoutMS);
QVERIFY(!testLoop.timeout());
@ -377,7 +377,7 @@ void tst_QDtlsCookie::serverReadyRead()
{
Q_ASSERT(clientsToWait);
if (!serverSocket.pendingDatagramSize())
if (serverSocket.pendingDatagramSize() <= 0)
return;
QByteArray hello;
@ -410,7 +410,7 @@ void tst_QDtlsCookie::clientReadyRead()
QUdpSocket *clientSocket = qobject_cast<QUdpSocket *>(sender());
Q_ASSERT(clientSocket);
if (!clientSocket->pendingDatagramSize())
if (clientSocket->pendingDatagramSize() <= 0)
return;
QDtls *handshake = nullptr;

View File

@ -190,8 +190,7 @@ private slots:
void task_250026_data() { generic_data("QODBC"); }
void task_250026();
void task_205701_data() { generic_data("QMYSQL"); }
void task_205701();
void crashQueryOnCloseDatabase();
void task_233829_data() { generic_data("QPSQL"); }
void task_233829();
@ -305,6 +304,8 @@ void tst_QSqlQuery::init()
void tst_QSqlQuery::cleanup()
{
if (QTest::currentTestFunction() == QLatin1String("crashQueryOnCloseDatabase"))
return;
QFETCH( QString, dbName );
QSqlDatabase db = QSqlDatabase::database( dbName );
CHECK_DATABASE( db );
@ -3427,19 +3428,17 @@ void tst_QSqlQuery::task_250026()
QCOMPARE( q.value( 0 ).toString().length(), data1026.length() );
}
void tst_QSqlQuery::task_205701()
void tst_QSqlQuery::crashQueryOnCloseDatabase()
{
QSqlDatabase qsdb = QSqlDatabase::addDatabase("QMYSQL", "atest");
qsdb.setHostName("test");
qsdb.setDatabaseName("test");
qsdb.setUserName("test");
qsdb.setPassword("test");
qsdb.open();
// {
QSqlQuery query(qsdb);
// }
QSqlDatabase::removeDatabase("atest");
for (const auto &dbName : qAsConst(dbs.dbNames)) {
QSqlDatabase clonedDb = QSqlDatabase::cloneDatabase(
QSqlDatabase::database(dbName), "crashTest");
qDebug() << "Testing crash in sqlquery dtor for driver" << clonedDb.driverName();
QVERIFY(clonedDb.open());
QSqlQuery q(clonedDb);
clonedDb.close();
QSqlDatabase::removeDatabase("crashTest");
}
}
void tst_QSqlQuery::task_233829()

View File

@ -3747,8 +3747,6 @@ void tst_QGraphicsScene::changedSignal()
QCoreApplication::processEvents();
QCOMPARE(cl.changes.size(), 2);
QCOMPARE(cl.changes.at(1).size(), 2);
QCOMPARE(cl.changes.at(1).first(), QRectF(0, 0, 10, 10));
QCOMPARE(cl.changes.at(1).last(), QRectF(20, 0, 10, 10));
QCOMPARE(scene.sceneRect(), QRectF(0, 0, 30, 10));
}

View File

@ -418,6 +418,7 @@ private slots:
void taskQTBUG_10169_sizeHintForRow();
void taskQTBUG_30653_doItemsLayout();
void taskQTBUG_50171_selectRowAfterSwapColumns();
void deselectRow();
#if QT_CONFIG(wheelevent)
void mouseWheel_data();
@ -4519,6 +4520,31 @@ void tst_QTableView::taskQTBUG_50171_selectRowAfterSwapColumns()
}
}
class DeselectTableWidget : public QTableWidget
{
public:
using QTableWidget::QTableWidget;
QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &,
const QEvent * = nullptr) const override
{
return QItemSelectionModel::Toggle;
}
};
void tst_QTableView::deselectRow()
{
DeselectTableWidget tw(20, 20);
tw.show();
QVERIFY(QTest::qWaitForWindowExposed(&tw));
tw.hideColumn(0);
QVERIFY(tw.isColumnHidden(0));
tw.selectRow(1);
QVERIFY(tw.selectionModel()->isRowSelected(1, QModelIndex()));
tw.selectRow(1);
// QTBUG-79092 - deselection was not possible when column 0 was hidden
QVERIFY(!tw.selectionModel()->isRowSelected(1, QModelIndex()));
}
// This has nothing to do with QTableView, but it's convenient to reuse the QtTestTableModel
#if QT_CONFIG(textmarkdownwriter)