diff options
Diffstat (limited to 'src')
47 files changed, 820 insertions, 165 deletions
diff --git a/src/corelib/io/qdirentryinfo_p.h b/src/corelib/io/qdirentryinfo_p.h index 7ed5391ff04..8864255c425 100644 --- a/src/corelib/io/qdirentryinfo_p.h +++ b/src/corelib/io/qdirentryinfo_p.h @@ -148,7 +148,7 @@ private: QFileSystemEntry entry; QFileSystemMetaData metaData; - std::optional<QFileInfo> fileInfoOpt; + std::optional<QFileInfo> fileInfoOpt = std::nullopt; }; QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index b48922daeab..2356611a5cf 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -2362,7 +2362,7 @@ QList<QByteArray> QMetaMethod::parameterNames() const \note In Qt 7, this function will return a null pointer for constructors. - \sa returnType(), QMetaType::type() + \sa returnType(), QMetaType::name() */ const char *QMetaMethod::typeName() const { diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 0ec95c3bb99..d31566b4e95 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -2103,6 +2103,9 @@ public: while (begin != end && is_space(*(end - 1))) end--; + if (begin == end) + return len; + // Convert 'char const *' into 'const char *'. Start at index 1, // not 0, because 'const char *' is already OK. const char *cst = begin + 1; diff --git a/src/corelib/kernel/qobject_p_p.h b/src/corelib/kernel/qobject_p_p.h index 1b05f20b827..fdc0f1e8392 100644 --- a/src/corelib/kernel/qobject_p_p.h +++ b/src/corelib/kernel/qobject_p_p.h @@ -143,7 +143,7 @@ struct QObjectPrivate::ConnectionData QAtomicPointer<SignalVector> signalVector; Connection *senders = nullptr; Sender *currentSender = nullptr; // object currently activating the object - std::atomic<TaggedSignalVector> orphaned = {}; + std::atomic<TaggedSignalVector> orphaned = {nullptr}; ~ConnectionData() { diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index f4e7942e947..a3c4e109adf 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -299,7 +299,7 @@ public: auto This = static_cast<QPropertyChangeHandler<Functor>*>(self); This->m_handler(); }) - , m_handler(handler) + , m_handler(std::move(handler)) { } @@ -310,7 +310,7 @@ public: auto This = static_cast<QPropertyChangeHandler<Functor>*>(self); This->m_handler(); }) - , m_handler(handler) + , m_handler(std::move(handler)) { setSource(property); } @@ -329,7 +329,7 @@ public: auto This = static_cast<QPropertyNotifier *>(self); This->m_handler(); }) - , m_handler(handler) + , m_handler(std::move(handler)) { } @@ -341,7 +341,7 @@ public: auto This = static_cast<QPropertyNotifier *>(self); This->m_handler(); }) - , m_handler(handler) + , m_handler(std::move(handler)) { setSource(property); } @@ -516,7 +516,7 @@ public: QPropertyChangeHandler<Functor> onValueChanged(Functor f) { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); - return QPropertyChangeHandler<Functor>(*this, f); + return QPropertyChangeHandler<Functor>(*this, std::move(f)); } template<typename Functor> @@ -524,14 +524,14 @@ public: { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); f(); - return onValueChanged(f); + return onValueChanged(std::move(f)); } template<typename Functor> QPropertyNotifier addNotifier(Functor f) { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); - return QPropertyNotifier(*this, f); + return QPropertyNotifier(*this, std::move(f)); } const QtPrivate::QPropertyBindingData &bindingData() const { return d; } @@ -785,7 +785,7 @@ public: template<typename Functor> QPropertyChangeHandler<Functor> onValueChanged(Functor f) const { - QPropertyChangeHandler<Functor> handler(f); + QPropertyChangeHandler<Functor> handler(std::move(f)); observe(&handler); return handler; } @@ -794,13 +794,13 @@ public: QPropertyChangeHandler<Functor> subscribe(Functor f) const { f(); - return onValueChanged(f); + return onValueChanged(std::move(f)); } template<typename Functor> QPropertyNotifier addNotifier(Functor f) { - QPropertyNotifier handler(f); + QPropertyNotifier handler(std::move(f)); observe(&handler); return handler; } @@ -1046,19 +1046,19 @@ public: template<typename Functor> QPropertyChangeHandler<Functor> onValueChanged(Functor f) { - return QBindable<T>(aliasedProperty(), iface).onValueChanged(f); + return QBindable<T>(aliasedProperty(), iface).onValueChanged(std::move(f)); } template<typename Functor> QPropertyChangeHandler<Functor> subscribe(Functor f) { - return QBindable<T>(aliasedProperty(), iface).subscribe(f); + return QBindable<T>(aliasedProperty(), iface).subscribe(std::move(f)); } template<typename Functor> QPropertyNotifier addNotifier(Functor f) { - return QBindable<T>(aliasedProperty(), iface).addNotifier(f); + return QBindable<T>(aliasedProperty(), iface).addNotifier(std::move(f)); } bool isValid() const @@ -1238,7 +1238,7 @@ public: QPropertyChangeHandler<Functor> onValueChanged(Functor f) { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); - return QPropertyChangeHandler<Functor>(*this, f); + return QPropertyChangeHandler<Functor>(*this, std::move(f)); } template<typename Functor> @@ -1246,14 +1246,14 @@ public: { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); f(); - return onValueChanged(f); + return onValueChanged(std::move(f)); } template<typename Functor> QPropertyNotifier addNotifier(Functor f) { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); - return QPropertyNotifier(*this, f); + return QPropertyNotifier(*this, std::move(f)); } const QtPrivate::QPropertyBindingData &bindingData() const @@ -1386,7 +1386,7 @@ public: QPropertyChangeHandler<Functor> onValueChanged(Functor f) { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); - return QPropertyChangeHandler<Functor>(*this, f); + return QPropertyChangeHandler<Functor>(*this, std::move(f)); } template<typename Functor> @@ -1394,14 +1394,14 @@ public: { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); f(); - return onValueChanged(f); + return onValueChanged(std::move(f)); } template<typename Functor> QPropertyNotifier addNotifier(Functor f) { static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters"); - return QPropertyNotifier(*this, f); + return QPropertyNotifier(*this, std::move(f)); } QtPrivate::QPropertyBindingData &bindingData() const diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 3a2dcb4a12d..e91797a3ffe 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -321,11 +321,6 @@ static QVariant::Private clonePrivate(const QVariant::Private &other) \compares equality - Because C++ forbids unions from including types that have - non-default constructors or destructors, most interesting Qt - classes cannot be used in unions. Without QVariant, this would be - a problem for QObject::property() and for database work, etc. - A QVariant object holds a single value of a single typeId() at a time. (Some types are multi-valued, for example a string list.) You can find out what type, T, the variant holds, convert it to a diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index ad9e1089bfe..b4751d1324f 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -1081,10 +1081,12 @@ void QXmlStreamReaderPrivate::parseEntity(const QString &value) inline void QXmlStreamReaderPrivate::reallocateStack() { stack_size <<= 1; - sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); - Q_CHECK_PTR(sym_stack); - state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); - Q_CHECK_PTR(state_stack); + void *p = realloc(sym_stack, stack_size * sizeof(Value)); + Q_CHECK_PTR(p); + sym_stack = static_cast<Value*>(p); + p = realloc(state_stack, stack_size * sizeof(int)); + Q_CHECK_PTR(p); + state_stack = static_cast<int*>(p); } @@ -2036,7 +2038,7 @@ void QXmlStreamReaderPrivate::startDocument() // unspecified (i.e. System) encoding. QString buf = decoder(QByteArrayView(rawReadBuffer).first(nbytesread)); if (!decoder.hasError()) - readBuffer = buf; + readBuffer = std::move(buf); } } } diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h index 4bba6bcc765..bd8770200db 100644 --- a/src/corelib/serialization/qxmlstream_p.h +++ b/src/corelib/serialization/qxmlstream_p.h @@ -140,8 +140,8 @@ public: if (tos + extraCapacity + 1 > cap) { cap = qMax(tos + extraCapacity + 1, cap << 1 ); void *ptr = realloc(static_cast<void *>(data), cap * sizeof(T)); + Q_CHECK_PTR(ptr); data = reinterpret_cast<T *>(ptr); - Q_CHECK_PTR(data); } } diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index d518a2f28f2..91550056101 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -1889,6 +1889,15 @@ QByteArray::QByteArray(qsizetype size, Qt::Initialization) } /*! + \fn QByteArray::QByteArray(QByteArrayView v) + \since 6.8 + + Constructs a byte array initialized with the byte array view's data. + + The QByteArray will be null if and only if \a v is null. +*/ + +/*! Sets the size of the byte array to \a size bytes. If \a size is greater than the current size, the byte array is @@ -4924,7 +4933,7 @@ QByteArray QByteArray::fromEcmaUint8Array(emscripten::val uint8array) \since 6.5 \ingroup platform-type-conversions - \sa toEcmaUint8Array() + \sa fromEcmaUint8Array() */ emscripten::val QByteArray::toEcmaUint8Array() { diff --git a/src/corelib/text/qstringalgorithms_p.h b/src/corelib/text/qstringalgorithms_p.h index a017ec58d49..c873858c59b 100644 --- a/src/corelib/text/qstringalgorithms_p.h +++ b/src/corelib/text/qstringalgorithms_p.h @@ -149,7 +149,10 @@ template <typename StringType> struct QStringAlgorithms return src.size() + adjust; } - static inline void replace_detaching(StringType &src, qsizetype bsize, + // Instead of detaching, i.e. copying the whole data array then performing the + // replacement, create a new buffer, copy `src` and `after` to it and swap it + // with `src`. + static inline void replace_into_copy(StringType &src, qsizetype bsize, ViewType after, QSpan<const qsizetype> indices, qsizetype newlen) { @@ -237,7 +240,7 @@ template <typename StringType> struct QStringAlgorithms // Instead of detaching (which would copy the whole data array) then // performing the replacement, allocate a new string and copy the data // over from `src` and `after` as needed. - replace_detaching(src, bsize, after, indices, newlen); + replace_into_copy(src, bsize, after, indices, newlen); return; } diff --git a/src/corelib/text/qstringlist.cpp b/src/corelib/text/qstringlist.cpp index bb9cf2f0c9f..e6f85849f27 100644 --- a/src/corelib/text/qstringlist.cpp +++ b/src/corelib/text/qstringlist.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE \reentrant - QStringList inherits from QList<QString>. Like QList, QStringList is + QStringList is actually just a QList<QString>. Like QList, QStringList is \l{implicitly shared}. It provides fast index-based access as well as fast insertions and removals. Passing string lists as value parameters is both fast and safe. diff --git a/src/corelib/text/qutf8stringview.qdoc b/src/corelib/text/qutf8stringview.qdoc index 6261927f682..fea4ae97761 100644 --- a/src/corelib/text/qutf8stringview.qdoc +++ b/src/corelib/text/qutf8stringview.qdoc @@ -108,7 +108,7 @@ QBasicUtf8StringView. Please do not use the template class's name in your source code. - \sa QAnyStringView, QUtf8StringView, QString + \sa QAnyStringView, QStringView, QLatin1StringView, QString */ /*! diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp index 6154af00950..e0cd5aee25c 100644 --- a/src/gui/accessible/linux/atspiadaptor.cpp +++ b/src/gui/accessible/linux/atspiadaptor.cpp @@ -1462,9 +1462,12 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect // handle properties like regular functions if (interface == "org.freedesktop.DBus.Properties"_L1) { - interface = message.arguments().at(0).toString(); - // Get/Set + Name - function = message.member() + message.arguments().at(1).toString(); + const auto arguments = message.arguments(); + if (arguments.size() > 0) { + interface = arguments.at(0).toString(); + if (arguments.size() > 1) // e.g. Get/Set + Name + function = function + arguments.at(1).toString(); + } } // switch interface to call diff --git a/src/gui/itemmodels/qfileinfogatherer.cpp b/src/gui/itemmodels/qfileinfogatherer.cpp index a0192c99828..df43f53050a 100644 --- a/src/gui/itemmodels/qfileinfogatherer.cpp +++ b/src/gui/itemmodels/qfileinfogatherer.cpp @@ -149,6 +149,8 @@ void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStr while ((loc = this->path.lastIndexOf(path, loc - 1)) != -1) { if (this->files.at(loc) == files) return; + if (loc == 0) + break; } #if QT_CONFIG(thread) diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp index edbe507e93d..b4dccfc42fe 100644 --- a/src/gui/math3d/qquaternion.cpp +++ b/src/gui/math3d/qquaternion.cpp @@ -421,19 +421,20 @@ QQuaternion QQuaternion::fromAxisAndAngle \since 5.5 Calculates roll, pitch, and yaw Euler angles (in degrees) - that corresponds to this quaternion. + that correspond to this quaternion. \sa fromEulerAngles() */ /*! - \fn QQuaternion QQuaternion::fromEulerAngles(const QVector3D &eulerAngles) + \fn QQuaternion QQuaternion::fromEulerAngles(const QVector3D &angles) \since 5.5 \overload - Creates a quaternion that corresponds to a rotation of \a eulerAngles: - eulerAngles.z() degrees around the z axis, eulerAngles.x() degrees around the x axis, - and eulerAngles.y() degrees around the y axis (in that order). + Creates a quaternion that corresponds to a rotation of \a angles: + angles.\l{QVector3D::}{z()} degrees around the z axis, + angles.\l{QVector3D::}{x()} degrees around the x axis, and + angles.\l{QVector3D::}{y()} degrees around the y axis (in that order). \sa toEulerAngles() */ @@ -585,9 +586,9 @@ QMatrix3x3 QQuaternion::toRotationMatrix() const /*! \since 5.5 - Creates a quaternion that corresponds to a rotation matrix \a rot3x3. + Creates a quaternion that corresponds to the rotation matrix \a rot3x3. - \note If a given rotation matrix is not normalized, + \note If the given rotation matrix is not normalized, the resulting quaternion will contain scaling information. \sa toRotationMatrix(), fromAxes() diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h index 7fb153063c5..0ea844ae41f 100644 --- a/src/gui/math3d/qquaternion.h +++ b/src/gui/math3d/qquaternion.h @@ -109,7 +109,7 @@ QT_WARNING_POP #ifndef QT_NO_VECTOR3D inline QVector3D toEulerAngles() const; - static inline QQuaternion fromEulerAngles(const QVector3D &eulerAngles); + static inline QQuaternion fromEulerAngles(const QVector3D &angles); #endif QT7_ONLY(Q_GUI_EXPORT) void getEulerAngles(float *pitch, float *yaw, float *roll) const; QT7_ONLY(Q_GUI_EXPORT) static QQuaternion fromEulerAngles(float pitch, float yaw, float roll); @@ -311,23 +311,23 @@ inline QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) return quaternion.rotatedVector(vec); } -inline void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const +void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const { float aX, aY, aZ; getAxisAndAngle(&aX, &aY, &aZ, angle); *axis = QVector3D(aX, aY, aZ); } -inline QVector3D QQuaternion::toEulerAngles() const +QVector3D QQuaternion::toEulerAngles() const { float pitch, yaw, roll; getEulerAngles(&pitch, &yaw, &roll); return QVector3D(pitch, yaw, roll); } -inline QQuaternion QQuaternion::fromEulerAngles(const QVector3D &eulerAngles) +QQuaternion QQuaternion::fromEulerAngles(const QVector3D &angles) { - return QQuaternion::fromEulerAngles(eulerAngles.x(), eulerAngles.y(), eulerAngles.z()); + return QQuaternion::fromEulerAngles(angles.x(), angles.y(), angles.z()); } #endif // QT_NO_VECTOR3D diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index c7474dc57a3..4872853514b 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -87,8 +87,9 @@ public: capacity = 1; while (capacity < size) capacity *= 2; - buffer = (Type*) QtPrivate::fittedRealloc(static_cast<void*>(buffer), 0, &capacity, sizeof(Type)); - Q_CHECK_PTR(buffer); + auto ptr = QtPrivate::fittedRealloc(static_cast<void*>(buffer), 0, &capacity, sizeof(Type)); + Q_CHECK_PTR(ptr); + buffer = static_cast<Type*>(ptr); } } @@ -96,8 +97,9 @@ public: Q_ASSERT(capacity >= size); if (size) { capacity = size; - buffer = (Type*) QtPrivate::fittedRealloc(static_cast<void*>(buffer), 0, &capacity, sizeof(Type)); - Q_CHECK_PTR(buffer); + const auto ptr = QtPrivate::fittedRealloc(static_cast<void*>(buffer), 0, &capacity, sizeof(Type)); + Q_CHECK_PTR(ptr); + buffer = static_cast<Type*>(ptr); siz = std::min(siz, size); } else { QtPrivate::sizedFree(buffer, capacity, sizeof(Type)); diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index d436afc5c77..1a60afbdd8e 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -361,7 +361,7 @@ inline void QClipData::appendSpan(int x, int length, int y, int coverage) if (count == allocated) { allocated *= 2; - m_spans = (QT_FT_Span *)realloc(m_spans, allocated*sizeof(QT_FT_Span)); + m_spans = static_cast<QT_FT_Span*>(q_check_ptr(realloc(m_spans, allocated * sizeof(QT_FT_Span)))); } m_spans[count].x = x; m_spans[count].len = length; @@ -378,7 +378,7 @@ inline void QClipData::appendSpans(const QT_FT_Span *s, int num) do { allocated *= 2; } while (count + num > allocated); - m_spans = (QT_FT_Span *)realloc(m_spans, allocated*sizeof(QT_FT_Span)); + m_spans = static_cast<QT_FT_Span*>(q_check_ptr(realloc(m_spans, allocated * sizeof(QT_FT_Span)))); } memcpy(m_spans+count, s, num*sizeof(QT_FT_Span)); count += num; diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 06ae00123f0..379fb70a81a 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -2245,6 +2245,17 @@ void QTextCharFormat::setFont(const QFont &font, FontPropertiesInheritanceBehavi /*! Returns the font for this character format. + + This function takes into account the format's font attributes (such as fontWeight() + and fontPointSize()) and resolves them on top of the default font, defined as follows. + If the format is part of a document, that is the document's default font. + Otherwise the properties are resolved on top of a default constructed QFont. + + For example, if this format's font size hasn't been changed from the default font, + fontPointSize() returns 0, while \c {font().pointSize()} returns the actual + size used for drawing. + + \sa QTextDocument::defaultFont() */ QFont QTextCharFormat::font() const { diff --git a/src/platformsupport/devicediscovery/qdevicediscovery_p.h b/src/platformsupport/devicediscovery/qdevicediscovery_p.h index 59ce3cd8896..61b0c705898 100644 --- a/src/platformsupport/devicediscovery/qdevicediscovery_p.h +++ b/src/platformsupport/devicediscovery/qdevicediscovery_p.h @@ -58,6 +58,7 @@ public: signals: void deviceDetected(const QString &deviceNode); void deviceRemoved(const QString &deviceNode); + void deviceChanged(const QString &deviceNode); protected: QDeviceDiscovery(QDeviceTypes types, QObject *parent) : QObject(parent), m_types(types) { } diff --git a/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp b/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp index edb3fc58a22..e4d69101f75 100644 --- a/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp +++ b/src/platformsupport/devicediscovery/qdevicediscovery_udev.cpp @@ -175,6 +175,9 @@ void QDeviceDiscoveryUDev::handleUDevNotification() if (qstrcmp(action, "remove") == 0) emit deviceRemoved(devNode); + if (qstrcmp(action, "change") == 0) + emit deviceChanged(devNode); + cleanup: udev_device_unref(dev); } diff --git a/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/src/platformsupport/kmsconvenience/qkmsdevice.cpp index cdd2ac2d572..7b2637bfffe 100644 --- a/src/platformsupport/kmsconvenience/qkmsdevice.cpp +++ b/src/platformsupport/kmsconvenience/qkmsdevice.cpp @@ -151,17 +151,45 @@ static inline void assignPlane(QKmsOutput *output, QKmsPlane *plane) output->eglfs_plane = plane; } -QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, - drmModeConnectorPtr connector, - ScreenInfo *vinfo) +static bool orderedScreenLessThan(const QKmsDevice::OrderedScreen &a, + const QKmsDevice::OrderedScreen &b) +{ + return a.vinfo.virtualIndex < b.vinfo.virtualIndex; +} + +QKmsDevice::OrderedScreen::OrderedScreen() : screen(nullptr) { } + +QKmsDevice::OrderedScreen::OrderedScreen(QPlatformScreen *screen, + const QKmsDevice::ScreenInfo &vinfo) + : screen(screen), vinfo(vinfo) +{ +} + +QDebug operator<<(QDebug dbg, const QPlatformScreen *screen) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QPlatformScreen=" << (const void *)screen << " (" + << (screen ? screen->name() : QString()) << ")"; + return dbg; +} + +QDebug operator<<(QDebug dbg, const QKmsDevice::OrderedScreen &s) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "OrderedScreen(" << s.screen << ") : " << s.vinfo.virtualIndex << " / " + << s.vinfo.virtualPos << " / primary: " << s.vinfo.isPrimary << ")"; + return dbg; +} + +bool QKmsDevice::createScreenInfoForConnector(drmModeResPtr resources, + drmModeConnectorPtr connector, ScreenInfo &vinfo) { - Q_ASSERT(vinfo); const QByteArray connectorName = nameForConnector(connector); const int crtc = crtcForConnector(resources, connector); if (crtc < 0) { qWarning() << "No usable crtc/encoder pair for connector" << connectorName; - return nullptr; + return false; } OutputConfiguration configuration; @@ -195,45 +223,54 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, configuration = OutputConfigPreferred; } - *vinfo = ScreenInfo(); - vinfo->virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt(); + vinfo.virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt(); if (userConnectorConfig.contains(QStringLiteral("virtualPos"))) { const QByteArray vpos = userConnectorConfig.value(QStringLiteral("virtualPos")).toByteArray(); const QByteArrayList vposComp = vpos.split(','); - if (vposComp.size() == 2) - vinfo->virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt()); + if (vposComp.count() == 2) { + vinfo.virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt()); + qCDebug(qLcKmsDebug) << "Parsing virtualPos to: " << vinfo.virtualPos; + } else { + vinfo.virtualPos = QPoint(-1, -1); + qCDebug(qLcKmsDebug) << "Could not parse virtualPos," + << "will be calculated based on virtualIndex"; + } + } else { + vinfo.virtualPos = QPoint(-1, -1); } + if (userConnectorConfig.value(QStringLiteral("primary")).toBool()) - vinfo->isPrimary = true; + vinfo.isPrimary = true; const uint32_t crtc_id = resources->crtcs[crtc]; if (configuration == OutputConfigOff) { qCDebug(qLcKmsDebug) << "Turning off output" << connectorName; drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, nullptr); - return nullptr; + return false; } // Skip disconnected output if (configuration == OutputConfigPreferred && connector->connection == DRM_MODE_DISCONNECTED) { qCDebug(qLcKmsDebug) << "Skipping disconnected output" << connectorName; - return nullptr; + return false; } if (configuration == OutputConfigSkip) { qCDebug(qLcKmsDebug) << "Skipping output" << connectorName; - return nullptr; + return false; } // Get the current mode on the current crtc drmModeModeInfo crtc_mode; memset(&crtc_mode, 0, sizeof crtc_mode); if (drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoder_id)) { + drmModeCrtcPtr crtc = drmModeGetCrtc(m_dri_fd, encoder->crtc_id); drmModeFreeEncoder(encoder); if (!crtc) - return nullptr; + return false; if (crtc->mode_valid) crtc_mode = crtc->mode; @@ -303,7 +340,7 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, if (selected_mode < 0) { qWarning() << "No modes available for output" << connectorName; - return nullptr; + return false; } else { int width = modes[selected_mode].hdisplay; int height = modes[selected_mode].vdisplay; @@ -504,9 +541,8 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, m_crtc_allocator |= (1 << output.crtc_index); - vinfo->output = output; - - return createScreen(output); + vinfo.output = output; + return true; } drmModePropertyPtr QKmsDevice::connectorProperty(drmModeConnectorPtr connector, const QByteArray &name) @@ -566,29 +602,233 @@ QKmsDevice::~QKmsDevice() #endif } -struct OrderedScreen +void QKmsDevice::checkConnectedScreens() { - OrderedScreen() : screen(nullptr) { } - OrderedScreen(QPlatformScreen *screen, const QKmsDevice::ScreenInfo &vinfo) - : screen(screen), vinfo(vinfo) { } - QPlatformScreen *screen; - QKmsDevice::ScreenInfo vinfo; -}; + if (m_screenConfig->headless()) + return; -QDebug operator<<(QDebug dbg, const OrderedScreen &s) -{ - QDebugStateSaver saver(dbg); - dbg.nospace() << "OrderedScreen(QPlatformScreen=" << s.screen << " (" << s.screen->name() << ") : " - << s.vinfo.virtualIndex - << " / " << s.vinfo.virtualPos - << " / primary: " << s.vinfo.isPrimary - << ")"; - return dbg; + drmModeResPtr resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qErrnoWarning(errno, "drmModeGetResources failed"); + return; + } + + QList<uint32_t> newConnects; + QList<uint32_t> newDisconnects; + const QMap<QString, QVariantMap> userConfig = m_screenConfig->outputSettings(); + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) { + qErrnoWarning(errno, "drmModeGetConnector failed"); + continue; + } + + const uint32_t id = connector->connector_id; + + const QByteArray connectorName = nameForConnector(connector); + const QVariantMap userCConfig = userConfig.value(QString::fromUtf8(connectorName)); + const QByteArray mode = userCConfig.value(QStringLiteral("mode")).toByteArray().toLower(); + if (mode == "off" || mode == "skip") + continue; + + if (connector->connection == DRM_MODE_CONNECTED) { + if (!m_registeredScreens.contains(id)) + newConnects.append(id); + else + qCDebug(qLcKmsDebug) << "Connected screen already registered: connector id=" << id; + } + + if (connector->connection == DRM_MODE_DISCONNECTED) { + if (m_registeredScreens.contains(id)) + newDisconnects.append(id); + else + qCDebug(qLcKmsDebug) << "Disconnected screen not registered: connector id=" << id; + } + + drmModeFreeConnector(connector); + } + + if (newConnects.isEmpty() && newDisconnects.isEmpty()) { + qCDebug(qLcKmsDebug) << "EGLFS/KMS: KMS-device-change but no new connects or disconnects " + << "to process - exiting"; + return; + } else { + qCDebug(qLcKmsDebug) << "EGLFS/KMS: KMS-device-change, new connects:" << newConnects + << ", and disconnected: " << newDisconnects; + } + + const int remainingScreenCount = m_registeredScreens.count() - newDisconnects.count(); + if (remainingScreenCount == 0 && m_headlessScreen == nullptr) { + qCDebug(qLcKmsDebug) << "EGLFS/KMS: creating headless screen before" + << "unregistering screens to avoid having no screens"; + m_headlessScreen = createHeadlessScreen(); + registerScreen(m_headlessScreen, true, QPoint(), + QList<QPlatformScreen *>() << m_headlessScreen); + } + + for (uint32_t connectorId : newDisconnects) { + OrderedScreen orderedScreen = m_registeredScreens.take(connectorId); + QPlatformScreen *screen = orderedScreen.screen; + + // Clear active crtc of the plane associated with the screen output + // and, if applicable, disassociate it from the eglfs plane. + uint32_t crtcId = (orderedScreen.vinfo.output.eglfs_plane != nullptr) // if we have an assigned plan + ? orderedScreen.vinfo.output.eglfs_plane->activeCrtcId // we use the active crtc_id to disable everything + : orderedScreen.vinfo.output.crtc_id; // if not, we use the default crtc_id + + if (orderedScreen.vinfo.output.eglfs_plane != nullptr) + orderedScreen.vinfo.output.eglfs_plane->activeCrtcId = 0; + + // Clear crtc allocator bit for screen + const int crtcIdx = orderedScreen.vinfo.output.crtc_index; + m_crtc_allocator &= ~(1 << crtcIdx); + + const int ret = drmModeSetCrtc(m_dri_fd, crtcId, 0, 0, 0, nullptr, 0, nullptr); + + if (ret != 0) { + qCWarning(qLcKmsDebug) << "Could not disable CRTC" << crtcId + << "on connector" << connectorId << "removal:" << ret; + } else { + qCDebug(qLcKmsDebug) << "Disabled CRTC" << crtcId + << "for connector " << connectorId << "disconnected"; + } + + // As we've already turned the crtc off, we don't want to restore the saved_crtc + if (orderedScreen.vinfo.output.saved_crtc) { + drmModeFreeCrtc(orderedScreen.vinfo.output.saved_crtc); + orderedScreen.vinfo.output.saved_crtc = nullptr; + updateScreenOutput(orderedScreen.screen, orderedScreen.vinfo.output); + } + + unregisterScreen(screen); + } + + for (uint32_t connectorId : newConnects) { + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, connectorId); + if (!connector) { + qErrnoWarning(errno, "drmModeGetConnector failed"); + continue; + } + + ScreenInfo vinfo; + bool succ = createScreenInfoForConnector(resources, connector, vinfo); + drmModeFreeConnector(connector); + if (!succ) + continue; + + QPlatformScreen *screen = createScreen(vinfo.output); + if (!screen) + continue; + + OrderedScreen orderedScreen(screen, vinfo); + m_registeredScreens[connectorId] = orderedScreen; + } + + drmModeFreeResources(resources); + + registerScreens(newConnects); } -static bool orderedScreenLessThan(const OrderedScreen &a, const OrderedScreen &b) +void QKmsDevice::updateScreens() { - return a.vinfo.virtualIndex < b.vinfo.virtualIndex; + if (m_screenConfig->headless()) + return; + + drmModeResPtr resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qErrnoWarning(errno, "drmModeGetResources failed"); + return; + } + + QList<uint32_t> newConnects; + QList<OrderedScreen> newDisconnects; + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) + continue; + + if (m_registeredScreens.contains(connector->connector_id)) { + OrderedScreen &os = m_registeredScreens[connector->connector_id]; + + // As we're currently *re*creating the information of an used connector, + // we have to "fake" it being not in use at two places: + // (note: the only thing we'll restore is, in case of failure, the eglfs_plane + // probably not necessary but good practice) + + // 1) crtc_allocator for the crtc + const int crtcIdx = os.vinfo.output.crtc_index; + m_crtc_allocator &= ~(1 << crtcIdx); + + // 2) the plane itself + if (os.vinfo.output.eglfs_plane) + os.vinfo.output.eglfs_plane->activeCrtcId = 0; + + // We also save the saved crtc to restore it in case of success + // (otherwise QKmsOutput::restoreMode would restore to a second-latest crtc, + // rather then the original one) + drmModeCrtcPtr saved_saved_crtc = nullptr; + if (os.vinfo.output.saved_crtc) + saved_saved_crtc = os.vinfo.output.saved_crtc; + + ScreenInfo vinfo; + bool succ = createScreenInfoForConnector(resources, connector, vinfo); + if (!succ) { + // Here we either failed the recreate, or the config turns the screen off. + // In either case, we'll treat it as a disconnect + + // Either this connector is disconnected, broken or turned off + // In all those cases we don't need or want to restore the previous mode + if (os.vinfo.output.saved_crtc) { + drmModeFreeCrtc(os.vinfo.output.saved_crtc); + os.vinfo.output.saved_crtc = nullptr; + updateScreenOutput(os.screen, os.vinfo.output); + } + + // move from one container to another - we don't want registerScreens + // to deal with this, but need to call registerScreens before the disconnects + newDisconnects.append(m_registeredScreens.take(connector->connector_id)); + drmModeFreeConnector(connector); + continue; + } + drmModeFreeConnector(connector); + + drmModeFreeCrtc(vinfo.output.saved_crtc); + vinfo.output.saved_crtc = saved_saved_crtc; // This is vital as config changes should + // never override the original saved_crtc + os.vinfo = vinfo; + updateScreenOutput(os.screen, os.vinfo.output); + + } else { + ScreenInfo vinfo; + bool succ = createScreenInfoForConnector(resources, connector, vinfo); + if (!succ) // If we fail here we do nothing, as there is nothing to restore or cleanup + continue; + + QPlatformScreen *screen = createScreen(vinfo.output); + OrderedScreen orderedScreen(screen, vinfo); + m_registeredScreens[connector->connector_id] = orderedScreen; + newConnects.append(connector->connector_id); + } + } + + // In case we end up with zero screen, we do the fallback first + if (m_registeredScreens.count() == 0 && m_headlessScreen == nullptr) { + // Create headless screen before unregistering screens to avoid having no screens + m_headlessScreen = createHeadlessScreen(); + registerScreen(m_headlessScreen, true, QPoint(), + QList<QPlatformScreen *>() << m_headlessScreen); + } + + // Register new and updates existing screens + registerScreens(newConnects); + + // Last we unregister the disconncted ones + for (const OrderedScreen &os : newDisconnects) + unregisterScreen(os.screen); + + drmModeFreeResources(resources); } void QKmsDevice::createScreens() @@ -599,7 +839,8 @@ void QKmsDevice::createScreens() QPlatformScreen *screen = createHeadlessScreen(); if (screen) { qCDebug(qLcKmsDebug, "Headless mode enabled"); - registerScreen(screen, true, QPoint(0, 0), QList<QPlatformScreen *>()); + registerScreen(screen, true, QPoint(0, 0), + QList<QPlatformScreen *>() << screen); return; } else { qWarning("QKmsDevice: Requested headless mode without support in the backend. Request is ignored."); @@ -630,8 +871,7 @@ void QKmsDevice::createScreens() discoverPlanes(); - QList<OrderedScreen> screens; - + QList<uint32_t> newConnects; int wantedConnectorIndex = -1; bool ok; int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_CONNECTOR_INDEX", &ok); @@ -647,19 +887,47 @@ void QKmsDevice::createScreens() continue; drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); - if (!connector) + if (!connector) { + qErrnoWarning(errno, "drmModeGetConnector failed"); continue; + } ScreenInfo vinfo; - QPlatformScreen *screen = createScreenForConnector(resources, connector, &vinfo); - if (screen) - screens.append(OrderedScreen(screen, vinfo)); - + bool succ = createScreenInfoForConnector(resources, connector, vinfo); + uint32_t connectorId = connector->connector_id; drmModeFreeConnector(connector); + if (!succ) + continue; + + QPlatformScreen *screen = createScreen(vinfo.output); + if (!screen) + continue; + + OrderedScreen orderedScreen(screen, vinfo); + m_registeredScreens[connectorId] = orderedScreen; + newConnects.append(connectorId); } drmModeFreeResources(resources); + if (!qEnvironmentVariable("QT_QPA_EGLFS_HOTPLUG_ENABLED").isEmpty() + && newConnects.empty() && m_headlessScreen == nullptr) { + qCDebug(qLcKmsDebug) << "'QT_QPA_EGLFS_HOTPLUG_ENABLED' was set and no screen was connected/found during start-up." + << "In order for Qt to operate properly a qt_headless screen will be created." + << "It will be automatically removed as soon as the first screen is connected"; + // Create headless screen before unregistering screens to avoid having no screens + m_headlessScreen = createHeadlessScreen(); + registerScreen(m_headlessScreen, true, QPoint(), + QList<QPlatformScreen *>() << m_headlessScreen); + } + + registerScreens(newConnects); +} + +void QKmsDevice::registerScreens(QList<uint32_t> newConnects) +{ + QList<OrderedScreen> screens = m_registeredScreens.values(); + // Use stable sort to preserve the original (DRM connector) order // for outputs with unspecified indices. std::stable_sort(screens.begin(), screens.end(), orderedScreenLessThan); @@ -692,44 +960,129 @@ void QKmsDevice::createScreens() // Figure out the virtual desktop and register the screens to QPA/QGuiApplication. QPoint pos(0, 0); - QList<QPlatformScreen *> siblings; + QList<OrderedScreen> siblings; QList<QPoint> virtualPositions; int primarySiblingIdx = -1; + QRegion deskRegion; for (const OrderedScreen &orderedScreen : screens) { QPlatformScreen *s = orderedScreen.screen; QPoint virtualPos(0, 0); // set up a horizontal or vertical virtual desktop - if (orderedScreen.vinfo.virtualPos.isNull()) { - virtualPos = pos; - if (m_screenConfig->virtualDesktopLayout() == QKmsScreenConfig::VirtualDesktopLayoutVertical) - pos.ry() += s->geometry().height(); - else - pos.rx() += s->geometry().width(); + if (orderedScreen.vinfo.virtualPos.x() == -1 || orderedScreen.vinfo.virtualPos.y() == -1) { + if (orderedScreen.vinfo.output.clone_source.isEmpty()) { + virtualPos = pos; + if (m_screenConfig->virtualDesktopLayout() == QKmsScreenConfig::VirtualDesktopLayoutVertical) + pos.ry() += s->geometry().height(); + else + pos.rx() += s->geometry().width(); + } else { + for (int i = 0; i < screens.count(); i++) { + const OrderedScreen &os = screens[i]; + if (os.vinfo.output.name == orderedScreen.vinfo.output.clone_source) { + if (i >= virtualPositions.count()) { + qCWarning(qLcKmsDebug) + << "WARNING: When using clone on kms config," + << "you have to either order your screens (virtualIndex)," + << "so clones come after their source," + << "or specify 'virtualPos' for each clone." + << "Otherwise desktop-geomerty might not work properly!"; + virtualPos = pos; + } else { + virtualPos = virtualPositions[i]; + } + break; + } + } + } } else { virtualPos = orderedScreen.vinfo.virtualPos; } - qCDebug(qLcKmsDebug) << "Adding QPlatformScreen" << s << "(" << s->name() << ")" - << "to QPA with geometry" << s->geometry() - << "and isPrimary=" << orderedScreen.vinfo.isPrimary; + // The order in qguiapp's screens list will match the order set by // virtualIndex. This is not only handy but also required since for instance // evdevtouch relies on it when performing touch device - screen mapping. if (!m_screenConfig->separateScreens()) { - qCDebug(qLcKmsDebug) << " virtual position is" << virtualPos; - siblings.append(s); + siblings.append(orderedScreen); virtualPositions.append(virtualPos); if (orderedScreen.vinfo.isPrimary) primarySiblingIdx = siblings.size() - 1; } else { - registerScreen(s, orderedScreen.vinfo.isPrimary, virtualPos, QList<QPlatformScreen *>() << s); + const bool isNewScreen = newConnects.contains(orderedScreen.vinfo.output.connector_id); + if (isNewScreen) { + qCDebug(qLcKmsDebug) << "Adding QPlatformScreen" << s << "(" << s->name() << ")" + << "to QPA with geometry" << s->geometry() + << ", virtual position" << virtualPos + << "and isPrimary=" << orderedScreen.vinfo.isPrimary; + registerScreen(s, orderedScreen.vinfo.isPrimary, virtualPos, + QList<QPlatformScreen *>() << s); + deskRegion += s->geometry(); + } else { + qCDebug(qLcKmsDebug) << "Updating QPlatformScreen" << s << "(" << s->name() << ")" + << "to QPA with geometry" << s->geometry() + << ", virtual position" << virtualPos + << "and isPrimary=" << orderedScreen.vinfo.isPrimary; + updateScreen(s, virtualPos, QList<QPlatformScreen *>() << s); + deskRegion += s->geometry(); + } } } if (!m_screenConfig->separateScreens()) { + QList<QPlatformScreen *> platformScreenSiblings; + for (int i = 0; i < siblings.count(); ++i) { + platformScreenSiblings.append(siblings[i].screen); + } + // enable the virtual desktop - for (int i = 0; i < siblings.size(); ++i) - registerScreen(siblings[i], i == primarySiblingIdx, virtualPositions[i], siblings); + for (int i = 0; i < siblings.count(); ++i) { + QPlatformScreen *screen = platformScreenSiblings[i]; + const OrderedScreen &orderedScreen = siblings[i]; + const bool isNewScreen = newConnects.contains(orderedScreen.vinfo.output.connector_id); + if (isNewScreen) { + qCDebug(qLcKmsDebug) << "Adding QPlatformScreen" << screen + << "(" << screen->name() << ")" + << "to QPA with geometry" << screen->geometry() + << ", virtual position" << virtualPositions[i] + << "and isPrimary=" << orderedScreen.vinfo.isPrimary; + registerScreen(screen, i == primarySiblingIdx, virtualPositions[i], + platformScreenSiblings); + deskRegion += screen->geometry(); + } else { + qCDebug(qLcKmsDebug) << "Updating QPlatformScreen" << screen + << "(" << screen->name() << ")" + << "to QPA with geometry" << screen->geometry() + << ", virtual position" << virtualPositions[i] + << "and isPrimary=" << orderedScreen.vinfo.isPrimary; + updateScreen(screen, virtualPositions[i], platformScreenSiblings); + deskRegion += screen->geometry(); + } + } + } + + // Remove headless screen if other screens have become available + if (!m_registeredScreens.empty() && m_headlessScreen) { + unregisterScreen(m_headlessScreen); + m_headlessScreen = nullptr; + } + + // Due to layout changes it's possible that we have to reset/bound + // the cursor into the available space (otherwise the cursor might vanish) + QPoint currCPos = QCursor::pos(); + if (!deskRegion.contains(currCPos)) { + + // We try boudingRect first + QRect deskRect = deskRegion.boundingRect(); + currCPos.setX(qMin(currCPos.x(), deskRect.width()) - 1); + currCPos.setY(qMin(currCPos.y(), deskRect.height()) - 1); + + // If boudingRect isn't good enough, we go to 0 + if (!deskRegion.contains(currCPos)) + currCPos = QPoint(0,0); + + qCDebug(qLcKmsDebug) << "Due to desktop layout change, overriding cursor pos." + << "Is: " << QCursor::pos() << ", will be: " << currCPos; + QCursor::setPos(currCPos); } } @@ -749,6 +1102,25 @@ void QKmsDevice::registerScreenCloning(QPlatformScreen *screen, Q_UNUSED(screensCloningThisScreen); } +void QKmsDevice::unregisterScreen(QPlatformScreen *screen) +{ + Q_UNUSED(screen); +} + +void QKmsDevice::updateScreen(QPlatformScreen *screen, const QPoint &virtualPos, + const QList<QPlatformScreen *> &virtualSiblings) +{ + Q_UNUSED(screen); + Q_UNUSED(virtualPos); + Q_UNUSED(virtualSiblings); +} + +void QKmsDevice::updateScreenOutput(QPlatformScreen *screen, const QKmsOutput &output) +{ + Q_UNUSED(screen); + Q_UNUSED(output); +} + // drm_property_type_is is not available in old headers static inline bool propTypeIs(drmModePropertyPtr prop, uint32_t type) { @@ -1002,6 +1374,12 @@ QKmsScreenConfig::QKmsScreenConfig() { } +void QKmsScreenConfig::refreshConfig() +{ + m_outputSettings.clear(); + loadConfig(); +} + void QKmsScreenConfig::loadConfig() { QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG"); @@ -1039,6 +1417,11 @@ void QKmsScreenConfig::loadConfig() m_headless = false; } + const QString headlessSizeStr = object.value(QLatin1String("headlessSize")).toString(); + if (sscanf(headlessSizeStr.toUtf8().constData(), "%dx%d", &headlessSize.rwidth(), + &headlessSize.rheight()) == 2) + m_headlessSize = headlessSize; + m_hwCursor = object.value("hwcursor"_L1).toBool(m_hwCursor); m_pbuffers = object.value("pbuffers"_L1).toBool(m_pbuffers); m_devicePath = object.value("device"_L1).toString(); diff --git a/src/platformsupport/kmsconvenience/qkmsdevice_p.h b/src/platformsupport/kmsconvenience/qkmsdevice_p.h index 050d836cb18..3e6ec108175 100644 --- a/src/platformsupport/kmsconvenience/qkmsdevice_p.h +++ b/src/platformsupport/kmsconvenience/qkmsdevice_p.h @@ -88,11 +88,12 @@ public: QMap<QString, QVariantMap> outputSettings() const { return m_outputSettings; } virtual void loadConfig(); + void refreshConfig(); protected: QString m_devicePath; bool m_headless; - QSize m_headlessSize; + QSize m_headlessSize{ 1024, 768 }; bool m_hwCursor; bool m_separateScreens; bool m_pbuffers; @@ -196,6 +197,14 @@ public: QKmsOutput output; }; + struct OrderedScreen + { + OrderedScreen(); + OrderedScreen(QPlatformScreen *screen, const ScreenInfo &vinfo); + QPlatformScreen *screen = nullptr; + ScreenInfo vinfo; + }; + QKmsDevice(QKmsScreenConfig *screenConfig, const QString &path = QString()); virtual ~QKmsDevice(); @@ -210,6 +219,8 @@ public: bool threadLocalAtomicCommit(void *user_data); void threadLocalAtomicReset(); #endif + void checkConnectedScreens(); + void updateScreens(); void createScreens(); int fd() const; @@ -218,6 +229,7 @@ public: QKmsScreenConfig *screenConfig() const; protected: + void registerScreens(QList<uint32_t> newConnects = QList<uint32_t>()); virtual QPlatformScreen *createScreen(const QKmsOutput &output) = 0; virtual QPlatformScreen *createHeadlessScreen(); virtual void registerScreenCloning(QPlatformScreen *screen, @@ -227,12 +239,15 @@ protected: bool isPrimary, const QPoint &virtualPos, const QList<QPlatformScreen *> &virtualSiblings) = 0; + virtual void unregisterScreen(QPlatformScreen *screen); + virtual void updateScreen(QPlatformScreen *screen, const QPoint &virtualPos, + const QList<QPlatformScreen *> &virtualSiblings); + virtual void updateScreenOutput(QPlatformScreen *screen, const QKmsOutput &output); void setFd(int fd); int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); - QPlatformScreen *createScreenForConnector(drmModeResPtr resources, - drmModeConnectorPtr connector, - ScreenInfo *vinfo); + bool createScreenInfoForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, + ScreenInfo &vinfo); drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name); drmModePropertyBlobPtr connectorPropertyBlob(drmModeConnectorPtr connector, const QByteArray &name); typedef std::function<void(drmModePropertyPtr, quint64)> PropCallback; @@ -257,6 +272,8 @@ protected: quint32 m_crtc_allocator; QList<QKmsPlane> m_planes; + QMap<uint32_t, OrderedScreen> m_registeredScreens; + QPlatformScreen *m_headlessScreen = nullptr; private: Q_DISABLE_COPY(QKmsDevice) diff --git a/src/plugins/platforms/eglfs/api/qeglfscontext.cpp b/src/plugins/platforms/eglfs/api/qeglfscontext.cpp index 9c10c1a998c..0b9db8039f1 100644 --- a/src/plugins/platforms/eglfs/api/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfscontext.cpp @@ -79,8 +79,10 @@ void QEglFSContext::swapBuffers(QPlatformSurface *surface) // draw the cursor if (surface->surface()->surfaceClass() == QSurface::Window) { QPlatformWindow *window = static_cast<QPlatformWindow *>(surface); - if (QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(window->screen()->cursor())) - cursor->paintOnScreen(); + if (QPlatformScreen *screen = window->screen()) { + if (QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor())) + cursor->paintOnScreen(); + } } qt_egl_device_integration()->waitForVSync(surface); diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp index 4abe948117e..2f278a474e0 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp @@ -154,6 +154,7 @@ QPlatformBackingStore *QEglFSIntegration::createPlatformBackingStore(QWindow *wi if (!window->handle()) window->create(); static_cast<QEglFSWindow *>(window->handle())->setBackingStore(bs); + m_bs = bs; return bs; #else Q_UNUSED(window); @@ -175,6 +176,9 @@ QPlatformWindow *QEglFSIntegration::createPlatformWindow(QWindow *window) const if (window->type() != Qt::ToolTip && window->screen() == QGuiApplication::primaryScreen()) w->requestActivateWindow(); + if (window->isTopLevel()) + w->setBackingStore(static_cast<QOpenGLCompositorBackingStore *>(m_bs)); + return w; } diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h index 2359b7f29f1..3865b7130b7 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h @@ -112,6 +112,7 @@ private: QScopedPointer<QFbVtHandler> m_vtHandler; QPointer<QWindow> m_pointerWindow; bool m_disableInputHandlers; + mutable QPlatformBackingStore *m_bs = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor_p.h index a0f78bb3103..cca9097e2f0 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor_p.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor_p.h @@ -62,6 +62,8 @@ public: void reevaluateVisibilityForScreens() { setPos(pos()); } + QEglFSKmsGbmScreen *screen() const { return m_screen; } + private: void initCursorAtlas(); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp index a7592ed55e4..9f19e649f85 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp @@ -113,13 +113,27 @@ QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output) { QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output, false); - createGlobalCursor(screen); + + // On some platforms (e.g. rpi4), you'll get a kernel warning/error + // if the cursor is created 'at the same time' as the screen is created. + // (drmModeMoveCursor is the specific call that causes the issue) + // When this issue is triggered, the screen's connector is unusable until reboot + // + // Below is a work-around (without negative implications for other platforms). + // + // interval of 0 and QMetaObject::invokeMethod (w/o Qt::QueuedConnection) + // do no help / will still trigger issue + QTimer::singleShot(1, [screen, this](){ + createGlobalCursor(screen); + }); return screen; } QPlatformScreen *QEglFSKmsGbmDevice::createHeadlessScreen() { + destroyGlobalCursor(); + return new QEglFSKmsGbmScreen(this, QKmsOutput(), true); } @@ -127,9 +141,6 @@ void QEglFSKmsGbmDevice::registerScreenCloning(QPlatformScreen *screen, QPlatformScreen *screenThisScreenClones, const QList<QPlatformScreen *> &screensCloningThisScreen) { - if (!screenThisScreenClones && screensCloningThisScreen.isEmpty()) - return; - QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen); gbmScreen->initCloning(screenThisScreenClones, screensCloningThisScreen); } @@ -144,6 +155,32 @@ void QEglFSKmsGbmDevice::registerScreen(QPlatformScreen *screen, m_globalCursor->reevaluateVisibilityForScreens(); } +void QEglFSKmsGbmDevice::unregisterScreen(QPlatformScreen *screen) +{ + // The global cursor holds a pointer to a QEglFSKmsGbmScreen. + // If that screen is being unregistered, + // this will recreate the global cursor with the first sibling screen. + if (m_globalCursor && screen == m_globalCursor->screen()) { + qCDebug(qLcEglfsKmsDebug) << "Destroying global GBM mouse cursor due to unregistering" + << "it's screen - will probably be recreated right away"; + delete m_globalCursor; + m_globalCursor = nullptr; + + QList<QPlatformScreen *> siblings = screen->virtualSiblings(); + siblings.removeOne(screen); + if (siblings.count() > 0) { + QEglFSKmsGbmScreen *kmsScreen = static_cast<QEglFSKmsGbmScreen *>(siblings.first()); + m_globalCursor = new QEglFSKmsGbmCursor(kmsScreen); + qCDebug(qLcEglfsKmsDebug) << "Creating new global GBM mouse cursor on sibling screen"; + } else { + qCWarning(qLcEglfsKmsDebug) << "Couldn't find a sibling to recreate" + << "the GBM mouse cursor - it might vanish"; + } + } + + QEglFSKmsDevice::unregisterScreen(screen); +} + bool QEglFSKmsGbmDevice::usesEventReader() const { static const bool eventReaderThreadDisabled = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_NO_EVENT_READER_THREAD"); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice_p.h index e00992ed291..0ffed0ec4ef 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice_p.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice_p.h @@ -51,6 +51,7 @@ public: bool isPrimary, const QPoint &virtualPos, const QList<QPlatformScreen *> &virtualSiblings) override; + void unregisterScreen(QPlatformScreen *screen) override; bool usesEventReader() const; QEglFSKmsEventReader *eventReader() { return &m_eventReader; } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp index 05ffb3b212e..eb61de3c534 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp @@ -11,6 +11,7 @@ #include "private/qeglfscursor_p.h" #include <QtCore/QLoggingCategory> +#include <QtCore/QFileSystemWatcher> #include <QtGui/QScreen> #include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> @@ -23,6 +24,10 @@ QEglFSKmsGbmIntegration::QEglFSKmsGbmIntegration() qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via GBM integration created"); } +QEglFSKmsGbmIntegration::~QEglFSKmsGbmIntegration() +{ +} + #ifndef EGL_EXT_platform_base typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list); #endif @@ -94,14 +99,16 @@ void QEglFSKmsGbmIntegration::presentBuffer(QPlatformSurface *surface) QKmsDevice *QEglFSKmsGbmIntegration::createDevice() { + + m_deviceDiscovery = std::unique_ptr<QDeviceDiscovery>(QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask)); + m_kmsConfigWatcher = std::unique_ptr<QFileSystemWatcher>(new QFileSystemWatcher()); + QString path = screenConfig()->devicePath(); if (!path.isEmpty()) { qCDebug(qLcEglfsKmsDebug) << "GBM: Using DRM device" << path << "specified in config file"; } else { - QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask); - const QStringList devices = d->scanConnectedDevices(); + const QStringList devices = m_deviceDiscovery->scanConnectedDevices(); qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices; - d->deleteLater(); if (Q_UNLIKELY(devices.isEmpty())) qFatal("Could not find DRM device!"); @@ -110,6 +117,35 @@ QKmsDevice *QEglFSKmsGbmIntegration::createDevice() qCDebug(qLcEglfsKmsDebug) << "Using" << path; } + bool hotreload = !qEnvironmentVariable("QT_QPA_EGLFS_HOTPLUG_ENABLED").isEmpty(); + if (hotreload) { + qCWarning(qLcEglfsKmsDebug) << "EGLFS/KMS: Hot-Reload on KMS-events enabled, be aware that" + << "this requires actions in UI code for proper functionallity" + << "(e.g. close/open windows on screen's disconnect/connect)"; + QObject::connect(m_deviceDiscovery.get(), &QDeviceDiscovery::deviceChanged, + m_deviceDiscovery.get(), [this](const QString &deviceNode) { + qCDebug(qLcEglfsKmsDebug) << "KMS device changed:" << deviceNode; + m_device->checkConnectedScreens(); + }); + } + + QString json = qEnvironmentVariable("QT_QPA_EGLFS_KMS_CONFIG"); + if (json.isEmpty()) + json = qEnvironmentVariable("QT_QPA_KMS_CONFIG"); + + if (!json.isEmpty()) { + m_kmsConfigWatcher->addPath(json); + QObject::connect(m_kmsConfigWatcher.get(), &QFileSystemWatcher::fileChanged, + m_kmsConfigWatcher.get(), [this, json]() { + qCDebug(qLcEglfsKmsDebug) << "KMS config-file has changed! path:" + << json; + m_screenConfig->refreshConfig(); + m_device->updateScreens(); + m_kmsConfigWatcher->addPath(json); // as per QFileSystemWatcher doc we have to re-add + // the path in case it's a new file + }); + } + return new QEglFSKmsGbmDevice(screenConfig(), path); } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration_p.h index fb118438d25..7c2c2a474d7 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration_p.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration_p.h @@ -24,11 +24,14 @@ QT_BEGIN_NAMESPACE class QEglFSKmsDevice; +class QDeviceDiscovery; +class QFileSystemWatcher; class Q_EGLFS_EXPORT QEglFSKmsGbmIntegration : public QEglFSKmsIntegration { public: QEglFSKmsGbmIntegration(); + ~QEglFSKmsGbmIntegration() override; EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay) override; EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) override; @@ -42,6 +45,8 @@ protected: QKmsDevice *createDevice() override; private: + std::unique_ptr<QDeviceDiscovery> m_deviceDiscovery; + std::unique_ptr<QFileSystemWatcher> m_kmsConfigWatcher; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp index 00fecb87f1f..332030f03f2 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp @@ -20,7 +20,7 @@ QT_BEGIN_NAMESPACE -QMutex QEglFSKmsGbmScreen::m_nonThreadedFlipMutex; +QMutex QEglFSKmsGbmScreen::s_nonThreadedFlipMutex; static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat) { @@ -92,9 +92,26 @@ QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QEglFSKmsDevice *device, const QKmsOutput QEglFSKmsGbmScreen::~QEglFSKmsGbmScreen() { const int remainingScreenCount = qGuiApp->screens().count(); - qCDebug(qLcEglfsKmsDebug, "Screen dtor. Remaining screens: %d", remainingScreenCount); + qCDebug(qLcEglfsKmsDebug, "Screen dtor. %p Remaining screens: %d", this, remainingScreenCount); if (!remainingScreenCount && !device()->screenConfig()->separateScreens()) static_cast<QEglFSKmsGbmDevice *>(device())->destroyGlobalCursor(); + + if (m_cloneSource) { + // Remove this screen from the screen that has it as a clone destination + QList<CloneDestination> &dests = m_cloneSource->m_cloneDests; + auto newEnd = std::remove_if(dests.begin(), dests.end(), + [this](CloneDestination &dest) { + return dest.screen == this; + }); + dests.erase(newEnd, dests.end()); + } + + // Other screens can no longer have this screen as a clone source + for (CloneDestination &dest : m_cloneDests) { + dest.screen->m_cloneSource = nullptr; + // Mode must be set again before flipping + dest.screen->m_output.mode_set = false; + } } QPlatformCursor *QEglFSKmsGbmScreen::cursor() const @@ -206,9 +223,12 @@ void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones, if (clonesAnother) { m_cloneSource = static_cast<QEglFSKmsGbmScreen *>(screenThisScreenClones); qCDebug(qLcEglfsKmsDebug, "Screen %s clones %s", qPrintable(name()), qPrintable(m_cloneSource->name())); + } else { + m_cloneSource = nullptr; } // clone sources need to know their additional destinations + m_cloneDests.clear(); for (QPlatformScreen *s : screensCloningThisScreen) { CloneDestination d; d.screen = static_cast<QEglFSKmsGbmScreen *>(s); @@ -271,8 +291,11 @@ void QEglFSKmsGbmScreen::nonThreadedPageFlipHandler(int fd, // note that with cloning involved this callback is called also for screens that clone another one Q_UNUSED(fd); QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(user_data); - screen->flipFinished(); - screen->pageFlipped(sequence, tv_sec, tv_usec); + // The screen might have been deleted when DRM calls this handler + if (QEglFSKmsScreen::isScreenKnown(screen)) { + screen->flipFinished(); + screen->pageFlipped(sequence, tv_sec, tv_usec); + } } void QEglFSKmsGbmScreen::waitForFlipWithEventReader(QEglFSKmsGbmScreen *screen) @@ -280,7 +303,21 @@ void QEglFSKmsGbmScreen::waitForFlipWithEventReader(QEglFSKmsGbmScreen *screen) m_flipMutex.lock(); QEglFSKmsGbmDevice *dev = static_cast<QEglFSKmsGbmDevice *>(device()); dev->eventReader()->startWaitFlip(screen, &m_flipMutex, &m_flipCond); - m_flipCond.wait(&m_flipMutex); + + // We should only wait forever on this screen, clones should have a timeout + // (e.g. I clone might have been created just before the flip, + // we might wait for it but it might not know about waking us up) + bool succ = false; + if (screen == this) + succ = m_flipCond.wait(&m_flipMutex); + else + succ = m_flipCond.wait(&m_flipMutex, 300); + + if (!succ) + qCWarning(qLcEglfsKmsDebug) << "timeout on waitForFlipWithEventReader, screen to wait for:" + << screen << ", screen waiting (shouldn't be the same screen):" + << this; + m_flipMutex.unlock(); screen->flipFinished(); } @@ -306,7 +343,7 @@ void QEglFSKmsGbmScreen::waitForFlip() waitForFlipWithEventReader(d.screen); } } else { - QMutexLocker lock(&m_nonThreadedFlipMutex); + QMutexLocker lock(&s_nonThreadedFlipMutex); while (m_gbm_bo_next) { drmEventContext drmEvent; memset(&drmEvent, 0, sizeof(drmEvent)); @@ -359,15 +396,10 @@ static void addAtomicFlip(drmModeAtomicReq *request, const QKmsOutput &output, u void QEglFSKmsGbmScreen::flip() { - // For headless screen just return silently. It is not necessarily an error + // For headless or cloned screen just return silently. It is not necessarily an error // to end up here, so show no warnings. - if (m_headless) - return; - - if (m_cloneSource) { - qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name())); + if (m_headless || m_cloneSource) return; - } if (!m_gbm_surface) { qWarning("Cannot sync before platform init!"); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen_p.h index aca34fcae21..65625a3c1cd 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen_p.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen_p.h @@ -67,7 +67,7 @@ protected: QMutex m_flipMutex; QWaitCondition m_flipCond; - static QMutex m_nonThreadedFlipMutex; + static QMutex s_nonThreadedFlipMutex; QScopedPointer<QEglFSKmsGbmCursor> m_cursor; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp index ece19f46a49..ff4921c2b15 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp @@ -213,9 +213,13 @@ QEglFSWindow *QEglFSKmsEglDeviceIntegration::createWindow(QWindow *window) const QEglFSKmsEglDeviceWindow *eglWindow = new QEglFSKmsEglDeviceWindow(window, this); m_funcs->initialize(eglWindow->screen()->display()); - if (Q_UNLIKELY(!(m_funcs->has_egl_output_base && m_funcs->has_egl_output_drm && m_funcs->has_egl_stream && - m_funcs->has_egl_stream_producer_eglsurface && m_funcs->has_egl_stream_consumer_egloutput))) + if (Q_UNLIKELY(!(m_funcs->has_egl_output_base && m_funcs->has_egl_output_drm + && m_funcs->has_egl_stream && m_funcs->has_egl_stream_producer_eglsurface + && m_funcs->has_egl_stream_consumer_egloutput))) { + qCDebug(qLcEglfsKmsDebug, "EGL_EXTENSIONS %s", + eglQueryString(eglWindow->screen()->display(), EGL_EXTENSIONS)); qFatal("Required extensions missing!"); + } return eglWindow; } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp index 5af45e63a2f..5775ac3607a 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp @@ -71,7 +71,7 @@ QEglFSKmsEglDeviceScreen::~QEglFSKmsEglDeviceScreen() } const int remainingScreenCount = qGuiApp->screens().size(); - qCDebug(qLcEglfsKmsDebug, "Screen dtor. Remaining screens: %d", remainingScreenCount); + qCDebug(qLcEglfsKmsDebug, "Screen dtor. %p Remaining screens: %d", this, remainingScreenCount); if (!remainingScreenCount && !device()->screenConfig()->separateScreens()) static_cast<QEglFSKmsEglDevice *>(device())->destroyGlobalCursor(); } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp index 037b26f023e..59ca53355d6 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp @@ -25,4 +25,39 @@ void QEglFSKmsDevice::registerScreen(QPlatformScreen *screen, QWindowSystemInterface::handleScreenAdded(s, isPrimary); } +void QEglFSKmsDevice::unregisterScreen(QPlatformScreen *screen) +{ + QEglFSKmsScreen *s = static_cast<QEglFSKmsScreen *>(screen); + for (QPlatformScreen *sibling : s->virtualSiblings()) + static_cast<QEglFSKmsScreen *>(sibling)->removeSibling(s); + + QWindowSystemInterface::handleScreenRemoved(screen); +} + +void QEglFSKmsDevice::updateScreen(QPlatformScreen *screen, const QPoint &virtualPos, + const QList<QPlatformScreen *> &virtualSiblings) +{ + QEglFSKmsScreen *s = static_cast<QEglFSKmsScreen *>(screen); + QRect before = s->geometry(); + s->setVirtualPosition(virtualPos); + s->setVirtualSiblings(virtualSiblings); + QRect after = s->geometry(); + + if (before != after) + QWindowSystemInterface::handleScreenGeometryChange(s->screen(), after, + s->availableGeometry()); +} + +void QEglFSKmsDevice::updateScreenOutput(QPlatformScreen *screen, const QKmsOutput &output) +{ + QEglFSKmsScreen *s = static_cast<QEglFSKmsScreen *>(screen); + QRect before = s->geometry(); + s->updateOutput(output); + QRect after = s->geometry(); + + if (before != after) + QWindowSystemInterface::handleScreenGeometryChange(s->screen(), after, + s->availableGeometry()); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice_p.h index 6e11953a699..49b82d8baad 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice_p.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice_p.h @@ -30,6 +30,13 @@ public: bool isPrimary, const QPoint &virtualPos, const QList<QPlatformScreen *> &virtualSiblings) override; + + void unregisterScreen(QPlatformScreen *screen) override; + + void updateScreen(QPlatformScreen *screen, const QPoint &virtualPos, + const QList<QPlatformScreen *> &virtualSiblings) override; + + void updateScreenOutput(QPlatformScreen *screen, const QKmsOutput &output) override; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp index c0c96554962..fa735388bc0 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp @@ -20,7 +20,10 @@ static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, t->eventHost()->handlePageFlipCompleted(user_data); QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data); - screen->pageFlipped(sequence, tv_sec, tv_usec); + if (QEglFSKmsScreen::isScreenKnown(screen)) + screen->pageFlipped(sequence, tv_sec, tv_usec); + else + qWarning("Deleted screen got it's pageFlipHandler called; Dead pointer: %p", user_data); } class RegisterWaitFlipEvent : public QEvent diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp index cc7381fb701..a40287bdfed 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp @@ -16,6 +16,8 @@ QT_BEGIN_NAMESPACE +QSet<QEglFSKmsScreen *> QEglFSKmsScreen::s_screens; + class QEglFSKmsInterruptHandler : public QObject { public: @@ -59,10 +61,14 @@ QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsDevice *device, const QKmsOutput &outp } else { qCDebug(qLcEglfsKmsDebug) << "No EDID data for output" << name(); } + + s_screens.insert(this); } QEglFSKmsScreen::~QEglFSKmsScreen() { + s_screens.remove(this); + m_output.cleanup(m_device); delete m_interruptHandler; } @@ -166,6 +172,11 @@ void QEglFSKmsScreen::waitForFlip() { } +void QEglFSKmsScreen::updateOutput(QKmsOutput output) +{ + m_output = output; +} + void QEglFSKmsScreen::restoreMode() { m_output.restoreMode(m_device); @@ -180,6 +191,11 @@ qreal QEglFSKmsScreen::refreshRate() const return refresh > 0 ? refresh : 60; } +void QEglFSKmsScreen::removeSibling(QPlatformScreen *screen) +{ + m_siblings.removeAll(screen); +} + QList<QPlatformScreen::Mode> QEglFSKmsScreen::modes() const { QList<QPlatformScreen::Mode> list; @@ -227,4 +243,9 @@ void QEglFSKmsScreen::pageFlipped(unsigned int sequence, unsigned int tv_sec, un Q_UNUSED(tv_usec); } +bool QEglFSKmsScreen::isScreenKnown(QEglFSKmsScreen *s) +{ + return s_screens.contains(s); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h index 6fb1f9a1348..2dc49152a97 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h @@ -58,6 +58,7 @@ public: QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; } void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; } + void removeSibling(QPlatformScreen *screen); QList<QPlatformScreen::Mode> modes() const override; @@ -68,6 +69,7 @@ public: virtual void waitForFlip(); + void updateOutput(QKmsOutput output); QKmsOutput &output() { return m_output; } void restoreMode(); @@ -80,6 +82,8 @@ public: void setCursorOutOfRange(bool b) { m_cursorOutOfRange = b; } virtual void pageFlipped(unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec); + static bool isScreenKnown(QEglFSKmsScreen *s); + protected: QEglFSKmsDevice *m_device; @@ -95,6 +99,8 @@ protected: QEglFSKmsInterruptHandler *m_interruptHandler; bool m_headless; + + static QSet<QEglFSKmsScreen *> s_screens; }; QT_END_NAMESPACE diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp index 7179109505b..3f40dea5eb9 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -1662,7 +1662,7 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op } case CE_ItemViewItem: { if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { - if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) { + if (qobject_cast<const QAbstractItemView *>(widget)) { QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); @@ -1764,10 +1764,10 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); - if (!view || !view->isPersistentEditorOpen(vopt->index)) { - painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() : option->palette.text().color()); - d->viewItemDrawText(painter, vopt, textRect); - } + painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() + : vopt->palette.text().color()); + d->viewItemDrawText(painter, vopt, textRect); + // paint a vertical marker for QListView if (vopt->state & State_Selected) { if (const QListView *lv = qobject_cast<const QListView *>(widget); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 14280712154..64fd334d467 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -259,6 +259,7 @@ bool Moc::parseEnum(EnumDef *def, ClassDef *containingClass) return false; // anonymous enum isTypdefEnum = true; } + def->lineNumber = symbol().lineNum; if (test(COLON)) { // C++11 strongly typed enum // enum Foo : unsigned long { ... }; def->type = normalizeType(parseType().name); @@ -464,6 +465,8 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro) } next(LPAREN, "Not a signal or slot declaration"); def->name = tempType.name; + def->lineNumber = symbol().lineNum; + scopedFunctionName = tempType.isScoped; if (!test(RPAREN)) { @@ -1366,6 +1369,7 @@ void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::Proper { propDef.location = index; propDef.relativeIndex = propertyIndex; + propDef.lineNumber = symbol().lineNum; Type t = parseType(); QByteArray type = t.name; @@ -2156,6 +2160,7 @@ QJsonObject FunctionDef::toJson(int index) const if (revision > 0) fdef["revision"_L1] = revision; + fdef["lineNumber"_L1] = lineNumber; if (wasCloned) fdef["isCloned"_L1] = true; @@ -2220,6 +2225,7 @@ QJsonObject PropertyDef::toJson() const prop["final"_L1] = final; prop["required"_L1] = required; prop["index"_L1] = relativeIndex; + prop["lineNumber"_L1] = lineNumber; if (revision > 0) prop["revision"_L1] = revision; @@ -2231,6 +2237,7 @@ QJsonObject EnumDef::toJson(const ClassDef &cdef) const QJsonObject def; uint flags = this->flags | cdef.enumDeclarations.value(name); def["name"_L1] = QString::fromUtf8(name); + def["lineNumber"_L1] = lineNumber; if (!enumName.isEmpty()) def["alias"_L1] = QString::fromUtf8(enumName); if (!type.isEmpty()) diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index f08edb3f0d2..fcf000c655f 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -58,6 +58,7 @@ struct EnumDef QFlags<QtMocConstants::EnumFlags> flags = {}; QJsonObject toJson(const ClassDef &cdef) const; QByteArray qualifiedType(const ClassDef *cdef) const; + int lineNumber = 0; }; Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE); @@ -84,6 +85,7 @@ struct FunctionDef enum Access { Private, Protected, Public }; Access access = Private; int revision = 0; + int lineNumber = 0; bool isConst = false; bool isVirtual = false; @@ -130,6 +132,7 @@ struct PropertyDef bool final = false; bool required = false; int relativeIndex = -1; // property index in current metaobject + int lineNumber = 0; qsizetype location = -1; // token index, used for error reporting diff --git a/src/widgets/accessible/simplewidgets.cpp b/src/widgets/accessible/simplewidgets.cpp index 39da1538376..190c7b624f6 100644 --- a/src/widgets/accessible/simplewidgets.cpp +++ b/src/widgets/accessible/simplewidgets.cpp @@ -289,19 +289,32 @@ QToolButton *QAccessibleToolButton::toolButton() const bool QAccessibleToolButton::isSplitButton() const { #if QT_CONFIG(menu) - return toolButton()->menu() && toolButton()->popupMode() == QToolButton::MenuButtonPopup; + return menu() && toolButton()->popupMode() == QToolButton::MenuButtonPopup; #else return false; #endif } +#if QT_CONFIG(menu) +QMenu *QAccessibleToolButton::menu() const +{ + if (QMenu *menu = toolButton()->menu()) + return menu; + + if (QAction *defaultAction = toolButton()->defaultAction()) + return defaultAction->menu(); + + return nullptr; +} +#endif + QAccessible::State QAccessibleToolButton::state() const { QAccessible::State st = QAccessibleButton::state(); if (toolButton()->autoRaise()) st.hotTracked = true; #if QT_CONFIG(menu) - if (toolButton()->menu()) + if (menu()) st.hasPopup = true; #endif return st; @@ -315,9 +328,8 @@ int QAccessibleToolButton::childCount() const QAccessible::Role QAccessibleToolButton::role() const { #if QT_CONFIG(menu) - QAbstractButton *ab = button(); - QToolButton *tb = qobject_cast<QToolButton*>(ab); - if (!tb->menu()) + QToolButton *tb = toolButton(); + if (!menu()) return tb->isCheckable() ? QAccessible::CheckBox : QAccessible::PushButton; else if (tb->popupMode() == QToolButton::DelayedPopup) return QAccessible::ButtonDropDown; @@ -329,10 +341,8 @@ QAccessible::Role QAccessibleToolButton::role() const QAccessibleInterface *QAccessibleToolButton::child(int index) const { #if QT_CONFIG(menu) - if (index == 0 && toolButton()->menu()) - { - return QAccessible::queryAccessibleInterface(toolButton()->menu()); - } + if (index == 0 && menu()) + return QAccessible::queryAccessibleInterface(menu()); #else Q_UNUSED(index); #endif diff --git a/src/widgets/accessible/simplewidgets_p.h b/src/widgets/accessible/simplewidgets_p.h index 02c23f8cba7..356ef9101b2 100644 --- a/src/widgets/accessible/simplewidgets_p.h +++ b/src/widgets/accessible/simplewidgets_p.h @@ -27,6 +27,7 @@ class QAbstractButton; class QLineEdit; class QToolButton; class QGroupBox; +class QMenu; class QMessageBox; class QProgressBar; @@ -71,6 +72,9 @@ protected: QToolButton *toolButton() const; bool isSplitButton() const; +#if QT_CONFIG(menu) + QMenu *menu() const; +#endif }; #endif // QT_CONFIG(toolbutton) diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index ed6f6f6193b..87c33932a8d 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -147,7 +147,7 @@ QDebug operator<<(QDebug debug, const QDockAreaLayout &layout) QDebug operator<<(QDebug debug, const QMainWindowLayout *layout) { if (layout) - return debug << layout->layoutState.dockAreaLayout; + return std::move(debug) << layout->layoutState.dockAreaLayout; return debug << "QMainWindowLayout(0x0)"; } diff --git a/src/widgets/widgets/qscrollbar.cpp b/src/widgets/widgets/qscrollbar.cpp index 431faf9e24d..af7a2e011b8 100644 --- a/src/widgets/widgets/qscrollbar.cpp +++ b/src/widgets/widgets/qscrollbar.cpp @@ -382,7 +382,7 @@ QMenu *QScrollBar::createStandardContextMenu(QPoint position) { #if QT_CONFIG(menu) const bool horiz = HORIZONTAL; - QMenu *menu = new QMenu(); + QMenu *menu = new QMenu(this); menu->setObjectName("qt_scrollbar_menu"_L1); if (window() && window()->windowHandle()) { |