diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index eca7270d..6778e984 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -37,7 +37,6 @@ qt_add_library(quickshell-core STATIC common.cpp iconprovider.cpp scriptmodel.cpp - colorquantizer.cpp ) qt_add_qml_module(quickshell-core diff --git a/src/core/clock.cpp b/src/core/clock.cpp index 90938d21..ebb7e92a 100644 --- a/src/core/clock.cpp +++ b/src/core/clock.cpp @@ -6,6 +6,8 @@ #include #include +#include "util.hpp" + SystemClock::SystemClock(QObject* parent): QObject(parent) { QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::onTimeout); this->update(); @@ -46,16 +48,19 @@ void SystemClock::update() { void SystemClock::setTime(const QDateTime& targetTime) { auto currentTime = QDateTime::currentDateTime(); auto offset = currentTime.msecsTo(targetTime); - this->currentTime = offset > -500 && offset < 500 ? targetTime : currentTime; + auto dtime = offset > -500 && offset < 500 ? targetTime : currentTime; + auto time = dtime.time(); - auto time = this->currentTime.time(); - this->currentTime.setTime(QTime( - this->mPrecision >= SystemClock::Hours ? time.hour() : 0, - this->mPrecision >= SystemClock::Minutes ? time.minute() : 0, - this->mPrecision >= SystemClock::Seconds ? time.second() : 0 - )); + auto secondPrecision = this->mPrecision >= SystemClock::Seconds; + auto secondChanged = this->setSeconds(secondPrecision ? time.second() : 0); - emit this->dateChanged(); + auto minutePrecision = this->mPrecision >= SystemClock::Minutes; + auto minuteChanged = this->setMinutes(minutePrecision ? time.minute() : 0); + + auto hourPrecision = this->mPrecision >= SystemClock::Hours; + auto hourChanged = this->setHours(hourPrecision ? time.hour() : 0); + + DropEmitter::call(secondChanged, minuteChanged, hourChanged); } void SystemClock::schedule(const QDateTime& targetTime) { @@ -71,11 +76,11 @@ void SystemClock::schedule(const QDateTime& targetTime) { auto nextTime = offset > 0 && offset < 500 ? targetTime : currentTime; auto baseTimeT = nextTime.time(); - nextTime.setTime(QTime( - hourPrecision ? baseTimeT.hour() : 0, - minutePrecision ? baseTimeT.minute() : 0, - secondPrecision ? baseTimeT.second() : 0 - )); + nextTime.setTime( + {hourPrecision ? baseTimeT.hour() : 0, + minutePrecision ? baseTimeT.minute() : 0, + secondPrecision ? baseTimeT.second() : 0} + ); if (secondPrecision) nextTime = nextTime.addSecs(1); else if (minutePrecision) nextTime = nextTime.addSecs(60); @@ -86,3 +91,7 @@ void SystemClock::schedule(const QDateTime& targetTime) { this->timer.start(static_cast(delay)); this->targetTime = nextTime; } + +DEFINE_MEMBER_GETSET(SystemClock, hours, setHours); +DEFINE_MEMBER_GETSET(SystemClock, minutes, setMinutes); +DEFINE_MEMBER_GETSET(SystemClock, seconds, setSeconds); diff --git a/src/core/clock.hpp b/src/core/clock.hpp index 67461911..3e669589 100644 --- a/src/core/clock.hpp +++ b/src/core/clock.hpp @@ -7,26 +7,9 @@ #include #include +#include "util.hpp" + ///! System clock accessor. -/// SystemClock is a view into the system's clock. -/// It updates at hour, minute, or second intervals depending on @@precision. -/// -/// # Examples -/// ```qml -/// SystemClock { -/// id: clock -/// precision: SystemClock.Seconds -/// } -/// -/// @@QtQuick.Text { -/// text: Qt.formatDateTime(clock.date, "hh:mm:ss - yyyy-MM-dd") -/// } -/// ``` -/// -/// > [!WARNING] Clock updates will trigger within 50ms of the system clock changing, -/// > however this can be either before or after the clock changes (+-50ms). If you -/// > need a date object, use @@date instead of constructing a new one, or the time -/// > of the constructed object could be off by up to a second. class SystemClock: public QObject { Q_OBJECT; /// If the clock should update. Defaults to true. @@ -35,17 +18,12 @@ class SystemClock: public QObject { Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); /// The precision the clock should measure at. Defaults to `SystemClock.Seconds`. Q_PROPERTY(SystemClock::Enum precision READ precision WRITE setPrecision NOTIFY precisionChanged); - /// The current date and time. - /// - /// > [!TIP] You can use @@QtQml.Qt.formatDateTime() to get the time as a string in - /// > your format of choice. - Q_PROPERTY(QDateTime date READ date NOTIFY dateChanged); /// The current hour. - Q_PROPERTY(quint32 hours READ hours NOTIFY dateChanged); + Q_PROPERTY(quint32 hours READ hours NOTIFY hoursChanged); /// The current minute, or 0 if @@precision is `SystemClock.Hours`. - Q_PROPERTY(quint32 minutes READ minutes NOTIFY dateChanged); + Q_PROPERTY(quint32 minutes READ minutes NOTIFY minutesChanged); /// The current second, or 0 if @@precision is `SystemClock.Hours` or `SystemClock.Minutes`. - Q_PROPERTY(quint32 seconds READ seconds NOTIFY dateChanged); + Q_PROPERTY(quint32 seconds READ seconds NOTIFY secondsChanged); QML_ELEMENT; public: @@ -65,15 +43,12 @@ public: [[nodiscard]] SystemClock::Enum precision() const; void setPrecision(SystemClock::Enum precision); - [[nodiscard]] QDateTime date() const { return this->currentTime; } - [[nodiscard]] quint32 hours() const { return this->currentTime.time().hour(); } - [[nodiscard]] quint32 minutes() const { return this->currentTime.time().minute(); } - [[nodiscard]] quint32 seconds() const { return this->currentTime.time().second(); } - signals: void enabledChanged(); void precisionChanged(); - void dateChanged(); + void hoursChanged(); + void minutesChanged(); + void secondsChanged(); private slots: void onTimeout(); @@ -81,11 +56,17 @@ private slots: private: bool mEnabled = true; SystemClock::Enum mPrecision = SystemClock::Seconds; + quint32 mHours = 0; + quint32 mMinutes = 0; + quint32 mSeconds = 0; QTimer timer; - QDateTime currentTime; QDateTime targetTime; void update(); void setTime(const QDateTime& targetTime); void schedule(const QDateTime& targetTime); + + DECLARE_PRIVATE_MEMBER(SystemClock, hours, setHours, mHours, hoursChanged); + DECLARE_PRIVATE_MEMBER(SystemClock, minutes, setMinutes, mMinutes, minutesChanged); + DECLARE_PRIVATE_MEMBER(SystemClock, seconds, setSeconds, mSeconds, secondsChanged); }; diff --git a/src/core/colorquantizer.cpp b/src/core/colorquantizer.cpp deleted file mode 100644 index d08f87b4..00000000 --- a/src/core/colorquantizer.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "colorquantizer.hpp" - -#include -#include -#include -#include -#include - -ColorQuantizerOperation::ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize) - : source(source) - , maxDepth(depth) - , rescaleSize(rescaleSize) { - mColors = QList(); -} - -void ColorQuantizerOperation::run() { - quantizeImage(); - - emit done(mColors); -} - -void ColorQuantizerOperation::quantizeImage() { - mColors.clear(); - - if (source->isEmpty()) { - return; - } - - QImage image(source->toLocalFile()); - - if ((image.width() > rescaleSize || image.height() > rescaleSize) && rescaleSize > 0) { - image = image.scaled( - static_cast(rescaleSize), - static_cast(rescaleSize), - Qt::KeepAspectRatio, - Qt::SmoothTransformation - ); - } - - if (image.isNull()) { - qWarning() << "Failed to load image from" << source; - return; - } - - QList pixels; - for (int y = 0; y < image.height(); ++y) { - for (int x = 0; x < image.width(); ++x) { - QRgb pixel = image.pixel(x, y); - if (qAlpha(pixel) == 0) { - continue; - } - - pixels.append(QColor::fromRgb(pixel)); - } - } - - QDateTime startTime = QDateTime::currentDateTime(); - - mColors = quantization(pixels, 0); - - QDateTime endTime = QDateTime::currentDateTime(); - qint64 milliseconds = startTime.msecsTo(endTime); - qDebug() << "Color Quantization took: " << milliseconds << "ms"; -} - -QList ColorQuantizerOperation::quantization(QList& rgbValues, qreal depth) { - if (depth >= maxDepth || rgbValues.isEmpty()) { - if (rgbValues.isEmpty()) { - return QList(); - } - - int totalR = 0; - int totalG = 0; - int totalB = 0; - - for (const QColor& color: rgbValues) { - totalR += color.red(); - totalG += color.green(); - totalB += color.blue(); - } - - QColor avgColor( - qRound(totalR / static_cast(rgbValues.size())), - qRound(totalG / static_cast(rgbValues.size())), - qRound(totalB / static_cast(rgbValues.size())) - ); - - return QList() << avgColor; - } - - QString dominantChannel = findBiggestColorRange(rgbValues); - std::ranges::sort(rgbValues, [dominantChannel](const QColor& a, const QColor& b) { - if (dominantChannel == "r") return a.red() < b.red(); - else if (dominantChannel == "g") return a.green() < b.green(); - return a.blue() < b.blue(); - }); - - qsizetype mid = rgbValues.size() / 2; - - QList leftHalf = rgbValues.mid(0, mid); - QList rightHalf = rgbValues.mid(mid + 1); - - QList result; - result.append(quantization(leftHalf, depth + 1)); - result.append(quantization(rightHalf, depth + 1)); - - return result; -} - -QString ColorQuantizerOperation::findBiggestColorRange(const QList& rgbValues) { - if (rgbValues.isEmpty()) return "r"; - - int rMin = 255; - int gMin = 255; - int bMin = 255; - int rMax = 0; - int gMax = 0; - int bMax = 0; - - for (const QColor& color: rgbValues) { - rMin = qMin(rMin, color.red()); - gMin = qMin(gMin, color.green()); - bMin = qMin(bMin, color.blue()); - - rMax = qMax(rMax, color.red()); - gMax = qMax(gMax, color.green()); - bMax = qMax(bMax, color.blue()); - } - - int rRange = rMax - rMin; - int gRange = gMax - gMin; - int bRange = bMax - bMin; - - int biggestRange = qMax(rRange, qMax(gRange, bRange)); - if (biggestRange == rRange) { - return "r"; - } else if (biggestRange == gRange) { - return "g"; - } else { - return "b"; - } -} - -QList ColorQuantizer::colors() { return mColors; } - -void ColorQuantizer::setSource(const QUrl& source) { - if (mSource != source) { - mSource = source; - emit sourceChanged(); - quantizeAsync(); - } -} - -void ColorQuantizer::setDepth(qreal depth) { - if (mDepth != depth) { - mDepth = depth; - emit depthChanged(); - } -} - -void ColorQuantizer::setRescaleSize(int rescaleSize) { - if (mRescaleSize != rescaleSize) { - mRescaleSize = rescaleSize; - emit rescaleSizeChanged(); - } -} - -void ColorQuantizer::operationFinished(const QList& result) { - mColors = result; - emit colorsChanged(); - isProcessing = false; -} - -void ColorQuantizer::quantizeAsync() { - if (isProcessing) return; - - qDebug() << "Starting color quantization asynchronously"; - isProcessing = true; - auto* task = new ColorQuantizerOperation(&mSource, mDepth, mRescaleSize); - QObject::connect(task, &ColorQuantizerOperation::done, this, &ColorQuantizer::operationFinished); - QThreadPool::globalInstance()->start(task); -} diff --git a/src/core/colorquantizer.hpp b/src/core/colorquantizer.hpp deleted file mode 100644 index 1c0435de..00000000 --- a/src/core/colorquantizer.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -class ColorQuantizerOperation - : public QObject - , public QRunnable { - Q_OBJECT; - -public: - ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize); - - void run() override; - -signals: - void done(QList colors); - -private: - QList mColors; - QUrl* source; - qreal maxDepth; - qreal rescaleSize; - - void quantizeImage(); - QList quantization(QList& rgbValues, qreal depth); - static QString findBiggestColorRange(const QList& rgbValues); -}; - -///! Color Quantization Utility -/// A color quantization utility used for getting prevalent colors in an image, by -/// averaging out the image's color data recursively. -/// -/// #### Example -/// ```qml -/// ColorQuantizer { -/// id: colorQuantizer -/// source: Qt.resolvedUrl("./yourImage.png") -/// depth: 3 // Will produce 8 colors (2³) -/// rescaleSize: 64 // Rescale to 64x64 for faster processing -/// } -/// ``` - -class ColorQuantizer: public QObject { - Q_OBJECT; - /// Access the colors resulting from the color quantization performed. - /// > [!NOTE] The amount of colors returned from the quantization is determined by - /// > the property depth, specifically 2ⁿ where n is the depth. - Q_PROPERTY(QList colors READ colors NOTIFY colorsChanged); - - /// Path to the image you'd like to run the color quantization on. - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged); - - /// Max depth for the color quantization. Each level of depth represents another - /// binary split of the color space - Q_PROPERTY(qreal depth READ depth WRITE setDepth NOTIFY depthChanged); - - /// The size to rescale the image to, when rescaleSize is 0 then no scaling will be done. - /// > [!NOTE] Results from color quantization doesn't suffer much when rescaling, it's - /// > reccommended to rescale, otherwise the quantization process will take much longer. - Q_PROPERTY(qreal rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged); - QML_ELEMENT; - -public: - QList colors(); - [[nodiscard]] QUrl source() const { return mSource; } - void setSource(const QUrl& source); - [[nodiscard]] qreal depth() const { return mDepth; } - void setDepth(qreal depth); - [[nodiscard]] qreal rescaleSize() const { return mRescaleSize; } - void setRescaleSize(int rescaleSize); - -signals: - void colorsChanged(); - void sourceChanged(); - void depthChanged(); - void rescaleSizeChanged(); - -public slots: - void operationFinished(const QList& result); - -private: - bool isProcessing = false; - QList mColors; - QUrl mSource; - qreal mDepth = 0; - qreal mRescaleSize = 0; - - void quantizeAsync(); -}; diff --git a/src/core/desktopentry.cpp b/src/core/desktopentry.cpp index 75a088d9..063aacd6 100644 --- a/src/core/desktopentry.cpp +++ b/src/core/desktopentry.cpp @@ -213,7 +213,7 @@ QVector DesktopEntry::parseExecString(const QString& execString) { currentArgument += c; escape = 0; - } else if (c == u'"' || c == u'\'') { + } else if (c == u'"') { parsingString = false; } else { currentArgument += c; @@ -229,7 +229,7 @@ QVector DesktopEntry::parseExecString(const QString& execString) { percent = false; } else if (c == '%') { percent = true; - } else if (c == u'"' || c == u'\'') { + } else if (c == u'"') { parsingString = true; } else if (c == u' ') { if (!currentArgument.isEmpty()) { diff --git a/src/core/imageprovider.cpp b/src/core/imageprovider.cpp index 47f284c7..256faaed 100644 --- a/src/core/imageprovider.cpp +++ b/src/core/imageprovider.cpp @@ -1,6 +1,5 @@ #include "imageprovider.hpp" -#include #include #include #include @@ -8,14 +7,10 @@ #include #include #include -#include namespace { -namespace { QMap liveImages; // NOLINT -quint32 handleIndex = 0; // NOLINT -} // namespace void parseReq(const QString& req, QString& target, QString& param) { auto splitIdx = req.indexOf('/'); @@ -29,9 +24,14 @@ void parseReq(const QString& req, QString& target, QString& param) { } // namespace -QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type) - : type(type) - , id(QString::number(++handleIndex)) { +QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent) + : QObject(parent) + , type(type) { + { + auto dbg = QDebug(&this->id); + dbg.nospace() << static_cast(this); + } + liveImages.insert(this->id, this); } @@ -85,9 +85,3 @@ QsPixmapProvider::requestPixmap(const QString& id, QSize* size, const QSize& req return QPixmap(); } } - -QString QsIndexedImageHandle::url() const { - return this->QsImageHandle::url() % '/' % QString::number(this->changeIndex); -} - -void QsIndexedImageHandle::imageChanged() { ++this->changeIndex; } diff --git a/src/core/imageprovider.hpp b/src/core/imageprovider.hpp index 8568d4f7..5ea7843d 100644 --- a/src/core/imageprovider.hpp +++ b/src/core/imageprovider.hpp @@ -20,13 +20,15 @@ public: QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override; }; -class QsImageHandle { +class QsImageHandle: public QObject { + Q_OBJECT; + public: - explicit QsImageHandle(QQmlImageProviderBase::ImageType type); - virtual ~QsImageHandle(); + explicit QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent = nullptr); + ~QsImageHandle() override; Q_DISABLE_COPY_MOVE(QsImageHandle); - [[nodiscard]] virtual QString url() const; + [[nodiscard]] QString url() const; virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize); virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize); @@ -35,14 +37,3 @@ private: QQmlImageProviderBase::ImageType type; QString id; }; - -class QsIndexedImageHandle: public QsImageHandle { -public: - explicit QsIndexedImageHandle(QQmlImageProviderBase::ImageType type): QsImageHandle(type) {} - - [[nodiscard]] QString url() const override; - void imageChanged(); - -private: - quint32 changeIndex = 0; -}; diff --git a/src/core/module.md b/src/core/module.md index b9404ea9..c8b17ab9 100644 --- a/src/core/module.md +++ b/src/core/module.md @@ -29,6 +29,5 @@ headers = [ "qsmenuanchor.hpp", "clock.hpp", "scriptmodel.hpp", - "colorquantizer.hpp", ] ----- diff --git a/src/core/qmlscreen.cpp b/src/core/qmlscreen.cpp index 105b4f01..34588b77 100644 --- a/src/core/qmlscreen.cpp +++ b/src/core/qmlscreen.cpp @@ -42,24 +42,6 @@ QString QuickshellScreenInfo::name() const { return this->screen->name(); } -QString QuickshellScreenInfo::model() const { - if (this->screen == nullptr) { - this->warnDangling(); - return "{ NULL SCREEN }"; - } - - return this->screen->model(); -} - -QString QuickshellScreenInfo::serialNumber() const { - if (this->screen == nullptr) { - this->warnDangling(); - return "{ NULL SCREEN }"; - } - - return this->screen->serialNumber(); -} - qint32 QuickshellScreenInfo::x() const { if (this->screen == nullptr) { this->warnDangling(); diff --git a/src/core/qmlscreen.hpp b/src/core/qmlscreen.hpp index 5e978bc0..c74d3b2c 100644 --- a/src/core/qmlscreen.hpp +++ b/src/core/qmlscreen.hpp @@ -29,10 +29,6 @@ class QuickshellScreenInfo: public QObject { /// /// Usually something like `DP-1`, `HDMI-1`, `eDP-1`. Q_PROPERTY(QString name READ name CONSTANT); - /// The model of the screen as seen by the operating system. - Q_PROPERTY(QString model READ model CONSTANT); - /// The serial number of the screen as seen by the operating system. - Q_PROPERTY(QString serialNumber READ serialNumber CONSTANT); Q_PROPERTY(qint32 x READ x NOTIFY geometryChanged); Q_PROPERTY(qint32 y READ y NOTIFY geometryChanged); Q_PROPERTY(qint32 width READ width NOTIFY geometryChanged); @@ -53,8 +49,6 @@ public: bool operator==(QuickshellScreenInfo& other) const; [[nodiscard]] QString name() const; - [[nodiscard]] QString model() const; - [[nodiscard]] QString serialNumber() const; [[nodiscard]] qint32 x() const; [[nodiscard]] qint32 y() const; [[nodiscard]] qint32 width() const; diff --git a/src/dbus/dbusmenu/dbusmenu.cpp b/src/dbus/dbusmenu/dbusmenu.cpp index 2b633b76..0267af8e 100644 --- a/src/dbus/dbusmenu/dbusmenu.cpp +++ b/src/dbus/dbusmenu/dbusmenu.cpp @@ -59,8 +59,8 @@ QString DBusMenuItem::icon() const { this->iconName, this->menu->iconThemePath.value().join(':') ); - } else if (this->image.hasData()) { - return this->image.url(); + } else if (this->image != nullptr) { + return this->image->url(); } else return nullptr; } @@ -113,7 +113,7 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString auto originalEnabled = this->mEnabled; auto originalVisible = this->visible; auto originalIconName = this->iconName; - auto imageChanged = false; + auto* originalImage = this->image; auto originalIsSeparator = this->mSeparator; auto originalButtonType = this->mButtonType; auto originalToggleState = this->mCheckState; @@ -173,16 +173,12 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString if (iconData.canConvert()) { auto data = iconData.value(); if (data.isEmpty()) { - imageChanged = this->image.hasData(); - this->image.data.clear(); - } else if (!this->image.hasData() || this->image.data != data) { - imageChanged = true; - this->image.data = data; - this->image.imageChanged(); + this->image = nullptr; + } else if (this->image == nullptr || this->image->data != data) { + this->image = new DBusMenuPngImage(data, this); } } else if (removed.isEmpty() || removed.contains("icon-data")) { - imageChanged = this->image.hasData(); - image.data.clear(); + this->image = nullptr; } auto type = properties.value("type"); @@ -243,13 +239,17 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString if (this->mSeparator != originalIsSeparator) emit this->isSeparatorChanged(); if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged(); - if (this->iconName != originalIconName || imageChanged) { + if (this->iconName != originalIconName || this->image != originalImage) { + if (this->image != originalImage) { + delete originalImage; + } + emit this->iconChanged(); } qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mText << ", enabled=" << this->mEnabled << ", visible=" << this->visible - << ", iconName=" << this->iconName << ", iconData=" << &this->image + << ", iconName=" << this->iconName << ", iconData=" << this->image << ", separator=" << this->mSeparator << ", toggleType=" << this->mButtonType << ", toggleState=" << this->mCheckState diff --git a/src/dbus/dbusmenu/dbusmenu.hpp b/src/dbus/dbusmenu/dbusmenu.hpp index 1a8b399e..35afa98e 100644 --- a/src/dbus/dbusmenu/dbusmenu.hpp +++ b/src/dbus/dbusmenu/dbusmenu.hpp @@ -30,17 +30,7 @@ namespace qs::dbus::dbusmenu { using menu::QsMenuEntry; class DBusMenu; -class DBusMenuItem; - -class DBusMenuPngImage: public QsIndexedImageHandle { -public: - explicit DBusMenuPngImage(): QsIndexedImageHandle(QQuickImageProvider::Image) {} - - [[nodiscard]] bool hasData() const { return !data.isEmpty(); } - QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; - - QByteArray data; -}; +class DBusMenuPngImage; ///! Menu item shared by an external program. /// Menu item shared by an external program via the @@ -103,7 +93,7 @@ private: bool visible = true; bool mSeparator = false; QString iconName; - DBusMenuPngImage image; + DBusMenuPngImage* image = nullptr; menu::QsMenuButtonType::Enum mButtonType = menu::QsMenuButtonType::None; Qt::CheckState mCheckState = Qt::Unchecked; bool displayChildren = false; @@ -166,6 +156,17 @@ private: QDebug operator<<(QDebug debug, DBusMenu* menu); +class DBusMenuPngImage: public QsImageHandle { +public: + explicit DBusMenuPngImage(QByteArray data, DBusMenuItem* parent) + : QsImageHandle(QQuickImageProvider::Image, parent) + , data(std::move(data)) {} + + QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; + + QByteArray data; +}; + class DBusMenuHandle; QDebug operator<<(QDebug debug, const DBusMenuHandle* handle); diff --git a/src/io/ipc.cpp b/src/io/ipc.cpp index 768299ed..37a37eb3 100644 --- a/src/io/ipc.cpp +++ b/src/io/ipc.cpp @@ -1,12 +1,9 @@ #include "ipc.hpp" -#include #include #include #include #include -#include -#include namespace qs::io::ipc { @@ -17,12 +14,6 @@ const BoolIpcType BoolIpcType::INSTANCE {}; const DoubleIpcType DoubleIpcType::INSTANCE {}; const ColorIpcType ColorIpcType::INSTANCE {}; -void* IpcType::copyStorage(const void* data) const { - auto* storage = this->createStorage(); - memcpy(storage, data, this->size()); - return storage; -} - const IpcType* IpcType::ipcType(const QMetaType& metaType) { if (metaType.id() == QMetaType::Void) return &VoidIpcType::INSTANCE; if (metaType.id() == QMetaType::QString) return &StringIpcType::INSTANCE; @@ -79,18 +70,12 @@ void IpcTypeSlot::replace(void* value) { this->storage = value; } -void IpcTypeSlot::replace(const QVariant& value) { - this->replace(this->mType->copyStorage(value.constData())); -} - const char* VoidIpcType::name() const { return "void"; } const char* VoidIpcType::genericArgumentName() const { return "void"; } -qsizetype VoidIpcType::size() const { return 0; } // string const char* StringIpcType::name() const { return "string"; } const char* StringIpcType::genericArgumentName() const { return "QString"; } -qsizetype StringIpcType::size() const { return sizeof(QString); } void* StringIpcType::fromString(const QString& string) const { return new QString(string); } QString StringIpcType::toString(void* slot) const { return *static_cast(slot); } void* StringIpcType::createStorage() const { return new QString(); } @@ -99,7 +84,6 @@ void StringIpcType::destroyStorage(void* slot) const { delete static_cast(slo // bool const char* BoolIpcType::name() const { return "bool"; } const char* BoolIpcType::genericArgumentName() const { return "bool"; } -qsizetype BoolIpcType::size() const { return sizeof(bool); } void* BoolIpcType::fromString(const QString& string) const { if (string == "true") return new bool(true); @@ -138,7 +121,6 @@ void BoolIpcType::destroyStorage(void* slot) const { delete static_cast(s // double const char* DoubleIpcType::name() const { return "real"; } const char* DoubleIpcType::genericArgumentName() const { return "double"; } -qsizetype DoubleIpcType::size() const { return sizeof(double); } void* DoubleIpcType::fromString(const QString& string) const { auto ok = false; @@ -157,7 +139,6 @@ void DoubleIpcType::destroyStorage(void* slot) const { delete static_castname % '(' % paramString % "): " % this->returnType; } -QString WirePropertyDefinition::toString() const { - return "property " % this->name % ": " % this->type; -} - QString WireTargetDefinition::toString() const { QString accum = "target " % this->name; @@ -197,10 +174,6 @@ QString WireTargetDefinition::toString() const { accum += "\n " % func.toString(); } - for (const auto& prop: this->properties) { - accum += "\n " % prop.toString(); - } - return accum; } diff --git a/src/io/ipc.hpp b/src/io/ipc.hpp index d2b865a2..924f045e 100644 --- a/src/io/ipc.hpp +++ b/src/io/ipc.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "../ipc/ipc.hpp" @@ -22,12 +21,10 @@ public: [[nodiscard]] virtual const char* name() const = 0; [[nodiscard]] virtual const char* genericArgumentName() const = 0; - [[nodiscard]] virtual qsizetype size() const = 0; [[nodiscard]] virtual void* fromString(const QString& /*string*/) const { return nullptr; } [[nodiscard]] virtual QString toString(void* /*slot*/) const { return ""; } [[nodiscard]] virtual void* createStorage() const { return nullptr; } virtual void destroyStorage(void* /*slot*/) const {} - void* copyStorage(const void* data) const; static const IpcType* ipcType(const QMetaType& metaType); }; @@ -46,7 +43,6 @@ public: [[nodiscard]] QGenericReturnArgument asGenericReturnArgument(); void replace(void* value); - void replace(const QVariant& value); private: const IpcType* mType = nullptr; @@ -57,7 +53,6 @@ class VoidIpcType: public IpcType { public: [[nodiscard]] const char* name() const override; [[nodiscard]] const char* genericArgumentName() const override; - [[nodiscard]] qsizetype size() const override; static const VoidIpcType INSTANCE; }; @@ -66,7 +61,6 @@ class StringIpcType: public IpcType { public: [[nodiscard]] const char* name() const override; [[nodiscard]] const char* genericArgumentName() const override; - [[nodiscard]] qsizetype size() const override; [[nodiscard]] void* fromString(const QString& string) const override; [[nodiscard]] QString toString(void* slot) const override; [[nodiscard]] void* createStorage() const override; @@ -79,7 +73,6 @@ class IntIpcType: public IpcType { public: [[nodiscard]] const char* name() const override; [[nodiscard]] const char* genericArgumentName() const override; - [[nodiscard]] qsizetype size() const override; [[nodiscard]] void* fromString(const QString& string) const override; [[nodiscard]] QString toString(void* slot) const override; [[nodiscard]] void* createStorage() const override; @@ -92,7 +85,6 @@ class BoolIpcType: public IpcType { public: [[nodiscard]] const char* name() const override; [[nodiscard]] const char* genericArgumentName() const override; - [[nodiscard]] qsizetype size() const override; [[nodiscard]] void* fromString(const QString& string) const override; [[nodiscard]] QString toString(void* slot) const override; [[nodiscard]] void* createStorage() const override; @@ -105,7 +97,6 @@ class DoubleIpcType: public IpcType { public: [[nodiscard]] const char* name() const override; [[nodiscard]] const char* genericArgumentName() const override; - [[nodiscard]] qsizetype size() const override; [[nodiscard]] void* fromString(const QString& string) const override; [[nodiscard]] QString toString(void* slot) const override; [[nodiscard]] void* createStorage() const override; @@ -118,7 +109,6 @@ class ColorIpcType: public IpcType { public: [[nodiscard]] const char* name() const override; [[nodiscard]] const char* genericArgumentName() const override; - [[nodiscard]] qsizetype size() const override; [[nodiscard]] void* fromString(const QString& string) const override; [[nodiscard]] QString toString(void* slot) const override; [[nodiscard]] void* createStorage() const override; @@ -137,23 +127,13 @@ struct WireFunctionDefinition { DEFINE_SIMPLE_DATASTREAM_OPS(WireFunctionDefinition, data.name, data.returnType, data.arguments); -struct WirePropertyDefinition { - QString name; - QString type; - - [[nodiscard]] QString toString() const; -}; - -DEFINE_SIMPLE_DATASTREAM_OPS(WirePropertyDefinition, data.name, data.type); - struct WireTargetDefinition { QString name; QVector functions; - QVector properties; [[nodiscard]] QString toString() const; }; -DEFINE_SIMPLE_DATASTREAM_OPS(WireTargetDefinition, data.name, data.functions, data.properties); +DEFINE_SIMPLE_DATASTREAM_OPS(WireTargetDefinition, data.name, data.functions); } // namespace qs::io::ipc diff --git a/src/io/ipccomm.cpp b/src/io/ipccomm.cpp index 7203a307..f2a9118a 100644 --- a/src/io/ipccomm.cpp +++ b/src/io/ipccomm.cpp @@ -21,17 +21,16 @@ namespace qs::io::ipc::comm { struct NoCurrentGeneration: std::monostate {}; struct TargetNotFound: std::monostate {}; -struct EntryNotFound: std::monostate {}; +struct FunctionNotFound: std::monostate {}; using QueryResponse = std::variant< std::monostate, NoCurrentGeneration, TargetNotFound, - EntryNotFound, + FunctionNotFound, QVector, WireTargetDefinition, - WireFunctionDefinition, - WirePropertyDefinition>; + WireFunctionDefinition>; void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const { auto resp = conn->responseStream(); @@ -45,24 +44,16 @@ void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const { auto* handler = registry->findHandler(this->target); if (handler) { - if (this->name.isEmpty()) { + if (this->function.isEmpty()) { resp << handler->wireDef(); } else { - auto* func = handler->findFunction(this->name); + auto* func = handler->findFunction(this->function); if (func) { resp << func->wireDef(); - return; + } else { + resp << FunctionNotFound(); } - - auto* prop = handler->findProperty(this->name); - - if (prop) { - resp << prop->wireDef(); - return; - } - - resp << EntryNotFound(); } } else { resp << TargetNotFound(); @@ -73,8 +64,8 @@ void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const { } } -int queryMetadata(IpcClient* client, const QString& target, const QString& name) { - client->sendMessage(IpcCommand(QueryMetadataCommand {.target = target, .name = name})); +int queryMetadata(IpcClient* client, const QString& target, const QString& function) { + client->sendMessage(IpcCommand(QueryMetadataCommand {.target = target, .function = function})); QueryResponse slot; if (!client->waitForResponse(slot)) return -1; @@ -91,11 +82,9 @@ int queryMetadata(IpcClient* client, const QString& target, const QString& name) qCInfo(logBare).noquote() << std::get(slot).toString(); } else if (std::holds_alternative(slot)) { qCInfo(logBare).noquote() << std::get(slot).toString(); - } else if (std::holds_alternative(slot)) { - qCInfo(logBare).noquote() << std::get(slot).toString(); } else if (std::holds_alternative(slot)) { qCCritical(logBare) << "Target not found."; - } else if (std::holds_alternative(slot)) { + } else if (std::holds_alternative(slot)) { qCCritical(logBare) << "Function not found."; } else if (std::holds_alternative(slot)) { qCCritical(logBare) << "Not ready to accept queries yet."; @@ -130,7 +119,7 @@ using StringCallResponse = std::variant< std::monostate, NoCurrentGeneration, TargetNotFound, - EntryNotFound, + FunctionNotFound, ArgParseFailed, Completed>; @@ -148,7 +137,7 @@ void StringCallCommand::exec(qs::ipc::IpcServerConnection* conn) const { auto* func = handler->findFunction(this->function); if (!func) { - resp << EntryNotFound(); + resp << FunctionNotFound(); return; } @@ -234,7 +223,7 @@ int callFunction( qCCritical(logBare).noquote() << "Function definition:" << error.definition.toString(); } else if (std::holds_alternative(slot)) { qCCritical(logBare) << "Target not found."; - } else if (std::holds_alternative(slot)) { + } else if (std::holds_alternative(slot)) { qCCritical(logBare) << "Function not found."; } else if (std::holds_alternative(slot)) { qCCritical(logBare) << "Not ready to accept queries yet."; @@ -244,74 +233,4 @@ int callFunction( return -1; } - -struct PropertyValue { - QString value; -}; - -DEFINE_SIMPLE_DATASTREAM_OPS(PropertyValue, data.value); - -using StringPropReadResponse = - std::variant; - -void StringPropReadCommand::exec(qs::ipc::IpcServerConnection* conn) const { - auto resp = conn->responseStream(); - - if (auto* generation = EngineGeneration::currentGeneration()) { - auto* registry = IpcHandlerRegistry::forGeneration(generation); - - auto* handler = registry->findHandler(this->target); - if (!handler) { - resp << TargetNotFound(); - return; - } - - auto* prop = handler->findProperty(this->property); - if (!prop) { - resp << EntryNotFound(); - return; - } - - auto slot = IpcTypeSlot(prop->type); - prop->read(handler, slot); - - resp << PropertyValue { - .value = slot.type()->toString(slot.get()), - }; - } else { - conn->respond(StringCallResponse(NoCurrentGeneration())); - } -} - -int getProperty(IpcClient* client, const QString& target, const QString& property) { - if (target.isEmpty()) { - qCCritical(logBare) << "Target required to send message."; - return -1; - } else if (property.isEmpty()) { - qCCritical(logBare) << "Property required to send message."; - return -1; - } - - client->sendMessage(IpcCommand(StringPropReadCommand {.target = target, .property = property})); - - StringPropReadResponse slot; - if (!client->waitForResponse(slot)) return -1; - - if (std::holds_alternative(slot)) { - auto& result = std::get(slot); - QTextStream(stdout) << result.value << Qt::endl; - return 0; - } else if (std::holds_alternative(slot)) { - qCCritical(logBare) << "Target not found."; - } else if (std::holds_alternative(slot)) { - qCCritical(logBare) << "Property not found."; - } else if (std::holds_alternative(slot)) { - qCCritical(logBare) << "Not ready to accept queries yet."; - } else { - qCCritical(logIpc) << "Received invalid IPC response from" << client; - } - - return -1; -} - } // namespace qs::io::ipc::comm diff --git a/src/io/ipccomm.hpp b/src/io/ipccomm.hpp index bc7dbf97..69463983 100644 --- a/src/io/ipccomm.hpp +++ b/src/io/ipccomm.hpp @@ -2,7 +2,6 @@ #include #include -#include #include "../ipc/ipc.hpp" @@ -10,12 +9,12 @@ namespace qs::io::ipc::comm { struct QueryMetadataCommand { QString target; - QString name; + QString function; void exec(qs::ipc::IpcServerConnection* conn) const; }; -DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.name); +DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.function); struct StringCallCommand { QString target; @@ -28,7 +27,7 @@ struct StringCallCommand { DEFINE_SIMPLE_DATASTREAM_OPS(StringCallCommand, data.target, data.function, data.arguments); void handleMsg(qs::ipc::IpcServerConnection* conn); -int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& name); +int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& function); int callFunction( qs::ipc::IpcClient* client, @@ -37,15 +36,4 @@ int callFunction( const QVector& arguments ); -struct StringPropReadCommand { - QString target; - QString property; - - void exec(qs::ipc::IpcServerConnection* conn) const; -}; - -DEFINE_SIMPLE_DATASTREAM_OPS(StringPropReadCommand, data.target, data.property); - -int getProperty(qs::ipc::IpcClient* client, const QString& target, const QString& property); - } // namespace qs::io::ipc::comm diff --git a/src/io/ipchandler.cpp b/src/io/ipchandler.cpp index 517e4505..510b2055 100644 --- a/src/io/ipchandler.cpp +++ b/src/io/ipchandler.cpp @@ -107,32 +107,6 @@ WireFunctionDefinition IpcFunction::wireDef() const { return wire; } -bool IpcProperty::resolve(QString& error) { - this->type = IpcType::ipcType(this->property.metaType()); - - if (!this->type) { - error = QString("Type %1 cannot be used across IPC.").arg(this->property.metaType().name()); - return false; - } - - return true; -} - -void IpcProperty::read(QObject* target, IpcTypeSlot& slot) const { - slot.replace(this->property.read(target)); -} - -QString IpcProperty::toString() const { - return QString("property ") % this->property.name() % ": " % this->type->name(); -} - -WirePropertyDefinition IpcProperty::wireDef() const { - WirePropertyDefinition wire; - wire.name = this->property.name(); - wire.type = this->type->name(); - return wire; -} - IpcCallStorage::IpcCallStorage(const IpcFunction& function): returnSlot(function.returnType) { for (const auto& arg: function.argumentTypes) { this->argumentSlots.emplace_back(arg); @@ -179,21 +153,6 @@ void IpcHandler::onPostReload() { } } - for (auto i = smeta.propertyCount(); i != meta->propertyCount(); i++) { - const auto& property = meta->property(i); - if (!property.isReadable() || !property.hasNotifySignal()) continue; - - auto ipcProp = IpcProperty(property); - QString error; - - if (!ipcProp.resolve(error)) { - qmlWarning(this).nospace().noquote() - << "Error parsing property \"" << property.name() << "\": " << error; - } else { - this->propertyMap.insert(property.name(), ipcProp); - } - } - this->complete = true; this->updateRegistration(); @@ -311,10 +270,6 @@ WireTargetDefinition IpcHandler::wireDef() const { wire.functions += func.wireDef(); } - for (const auto& prop: this->propertyMap.values()) { - wire.properties += prop.wireDef(); - } - return wire; } @@ -352,13 +307,6 @@ IpcFunction* IpcHandler::findFunction(const QString& name) { else return &*itr; } -IpcProperty* IpcHandler::findProperty(const QString& name) { - auto itr = this->propertyMap.find(name); - - if (itr == this->propertyMap.end()) return nullptr; - else return &*itr; -} - IpcHandler* IpcHandlerRegistry::findHandler(const QString& target) { return this->handlers.value(target); } diff --git a/src/io/ipchandler.hpp b/src/io/ipchandler.hpp index e6b24ba1..cc4ee5f4 100644 --- a/src/io/ipchandler.hpp +++ b/src/io/ipchandler.hpp @@ -53,28 +53,14 @@ private: friend class IpcFunction; }; -class IpcProperty { -public: - explicit IpcProperty(QMetaProperty property): property(property) {} - - bool resolve(QString& error); - void read(QObject* target, IpcTypeSlot& slot) const; - - [[nodiscard]] QString toString() const; - [[nodiscard]] WirePropertyDefinition wireDef() const; - - QMetaProperty property; - const IpcType* type = nullptr; -}; - class IpcHandlerRegistry; ///! Handler for IPC message calls. /// Each IpcHandler is registered into a per-instance map by its unique @@target. -/// Functions and properties defined on the IpcHandler can be accessed via `qs ipc`. +/// Functions defined on the IpcHandler can be called by `qs msg`. /// /// #### Handler Functions -/// IPC handler functions can be called by `qs ipc call` as long as they have at most 10 +/// IPC handler functions can be called by `qs msg` as long as they have at most 10 /// arguments, and all argument types along with the return type are listed below. /// /// **Argument and return types must be explicitly specified or they will not @@ -126,9 +112,9 @@ class IpcHandlerRegistry; /// } /// } /// ``` -/// The list of registered targets can be inspected using `qs ipc show`. +/// The list of registered targets can be inspected using `qs msg -s`. /// ```sh -/// $ qs ipc show +/// $ qs msg -s /// target rect /// function setColor(color: color): void /// function getColor(): color @@ -138,22 +124,18 @@ class IpcHandlerRegistry; /// function getRadius(): int /// ``` /// -/// and then invoked using `qs ipc call`. +/// and then invoked using `qs msg`. /// ```sh -/// $ qs ipc call rect setColor orange -/// $ qs ipc call rect setAngle 40.5 -/// $ qs ipc call rect setRadius 30 -/// $ qs ipc call rect getColor +/// $ qs msg rect setColor orange +/// $ qs msg rect setAngle 40.5 +/// $ qs msg rect setRadius 30 +/// $ qs msg rect getColor /// #ffffa500 -/// $ qs ipc call rect getAngle +/// $ qs msg rect getAngle /// 40.5 -/// $ qs ipc call rect getRadius +/// $ qs msg rect getRadius /// 30 /// ``` -/// -/// #### Properties -/// Properties of an IpcHanlder can be read using `qs ipc prop get` as long as they are -/// of an IPC compatible type. See the table above for compatible types. class IpcHandler : public QObject , public PostReloadHook { @@ -180,16 +162,12 @@ public: QString listMembers(qsizetype indent); [[nodiscard]] IpcFunction* findFunction(const QString& name); - [[nodiscard]] IpcProperty* findProperty(const QString& name); [[nodiscard]] WireTargetDefinition wireDef() const; signals: void enabledChanged(); void targetChanged(); -private slots: - //void handleIpcPropertyChange(); - private: void updateRegistration(bool destroying = false); @@ -205,7 +183,6 @@ private: bool complete = false; QHash functionMap; - QHash propertyMap; friend class IpcHandlerRegistry; }; diff --git a/src/ipc/ipccommand.hpp b/src/ipc/ipccommand.hpp index b221b460..c2e5059f 100644 --- a/src/ipc/ipccommand.hpp +++ b/src/ipc/ipccommand.hpp @@ -15,7 +15,6 @@ using IpcCommand = std::variant< std::monostate, IpcKillCommand, qs::io::ipc::comm::QueryMetadataCommand, - qs::io::ipc::comm::StringCallCommand, - qs::io::ipc::comm::StringPropReadCommand>; + qs::io::ipc::comm::StringCallCommand>; } // namespace qs::ipc diff --git a/src/launch/command.cpp b/src/launch/command.cpp index 00ad613e..eb27df74 100644 --- a/src/launch/command.cpp +++ b/src/launch/command.cpp @@ -102,10 +102,9 @@ int locateConfigFile(CommandState& cmd, QString& path) { return 0; } -void sortInstances(QVector& list, bool newestFirst) { - std::ranges::sort(list, [=](const InstanceLockInfo& a, const InstanceLockInfo& b) { - auto r = a.instance.launchTime < b.instance.launchTime; - return newestFirst ? !r : r; +void sortInstances(QVector& list) { + std::ranges::sort(list, [](const InstanceLockInfo& a, const InstanceLockInfo& b) { + return a.instance.launchTime < b.instance.launchTime; }); }; @@ -154,7 +153,7 @@ int selectInstance(CommandState& cmd, InstanceLockInfo* instance) { path = QDir(basePath->filePath("by-path")).filePath(pathId); auto instances = QsPaths::collectInstances(path); - sortInstances(instances, cmd.config.newest); + sortInstances(instances); if (instances.isEmpty()) { qCInfo(logBare) << "No running instances for" << configFilePath; @@ -228,7 +227,7 @@ int listInstances(CommandState& cmd) { qCInfo(logBare) << "Use --all to list all instances."; } } else { - sortInstances(instances, cmd.config.newest); + sortInstances(instances); if (cmd.output.json) { auto array = QJsonArray(); @@ -285,23 +284,26 @@ int killInstances(CommandState& cmd) { }); } -int ipcCommand(CommandState& cmd) { +int msgInstance(CommandState& cmd) { InstanceLockInfo instance; auto r = selectInstance(cmd, &instance); if (r != 0) return r; return IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) { - if (*cmd.ipc.show || cmd.ipc.showOld) { - return qs::io::ipc::comm::queryMetadata(&client, *cmd.ipc.target, *cmd.ipc.name); - } else if (*cmd.ipc.getprop) { - return qs::io::ipc::comm::getProperty(&client, *cmd.ipc.target, *cmd.ipc.name); + if (cmd.ipc.info) { + return qs::io::ipc::comm::queryMetadata(&client, *cmd.ipc.target, *cmd.ipc.function); } else { QVector arguments; for (auto& arg: cmd.ipc.arguments) { arguments += *arg; } - return qs::io::ipc::comm::callFunction(&client, *cmd.ipc.target, *cmd.ipc.name, arguments); + return qs::io::ipc::comm::callFunction( + &client, + *cmd.ipc.target, + *cmd.ipc.function, + arguments + ); } return -1; @@ -420,8 +422,8 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) { return listInstances(state); } else if (*state.subcommand.kill) { return killInstances(state); - } else if (*state.subcommand.msg || *state.ipc.ipc) { - return ipcCommand(state); + } else if (*state.subcommand.msg) { + return msgInstance(state); } else { if (strcmp(qVersion(), QT_VERSION_STR) != 0) { qWarning() << "\033[31mQuickshell was built against Qt" << QT_VERSION_STR diff --git a/src/launch/launch_p.hpp b/src/launch/launch_p.hpp index 77808450..d752edbc 100644 --- a/src/launch/launch_p.hpp +++ b/src/launch/launch_p.hpp @@ -49,7 +49,6 @@ struct CommandState { QStringOption path; QStringOption manifest; QStringOption name; - bool newest = false; } config; struct { @@ -68,13 +67,9 @@ struct CommandState { } output; struct { - CLI::App* ipc = nullptr; - CLI::App* show = nullptr; - CLI::App* call = nullptr; - CLI::App* getprop = nullptr; - bool showOld = false; + bool info = false; QStringOption target; - QStringOption name; + QStringOption function; std::vector arguments; } ipc; diff --git a/src/launch/parsecommand.cpp b/src/launch/parsecommand.cpp index 1edbf01e..bee9dd00 100644 --- a/src/launch/parsecommand.cpp +++ b/src/launch/parsecommand.cpp @@ -16,7 +16,7 @@ int parseCommand(int argc, char** argv, CommandState& state) { .argv = argv, }; - auto addConfigSelection = [&](CLI::App* cmd, bool withNewestOption = false) { + auto addConfigSelection = [&](CLI::App* cmd) { auto* group = cmd->add_option_group("Config Selection") ->description("If no options in this group are specified,\n" "$XDG_CONFIG_HOME/quickshell/shell.qml will be used."); @@ -37,11 +37,6 @@ int parseCommand(int argc, char** argv, CommandState& state) { "otherwise it is the name of a folder in $XDG_CONFIG_HOME/quickshell.") ->envname("QS_CONFIG_NAME"); - if (withNewestOption) { - group->add_flag("-n,--newest", state.config.newest) - ->description("Operate on the most recently launched instance instead of the oldest"); - } - return group; }; @@ -135,7 +130,7 @@ int parseCommand(int argc, char** argv, CommandState& state) { ->description("Rules to apply to the log being read, in the format of QT_LOGGING_RULES."); auto* instance = addInstanceSelection(sub)->excludes(file); - addConfigSelection(sub, true)->excludes(instance)->excludes(file); + addConfigSelection(sub)->excludes(instance)->excludes(file); addLoggingOptions(sub, false); state.subcommand.log = sub; @@ -151,7 +146,7 @@ int parseCommand(int argc, char** argv, CommandState& state) { sub->add_flag("-j,--json", state.output.json, "Output the list as a json."); - addConfigSelection(sub, true)->excludes(all); + addConfigSelection(sub)->excludes(all); addLoggingOptions(sub, false, true); state.subcommand.list = sub; @@ -161,72 +156,37 @@ int parseCommand(int argc, char** argv, CommandState& state) { auto* sub = cli->add_subcommand("kill", "Kill quickshell instances."); //sub->add_flag("-a,--all", "Kill all matching instances instead of just one."); auto* instance = addInstanceSelection(sub); - addConfigSelection(sub, true)->excludes(instance); + addConfigSelection(sub)->excludes(instance); addLoggingOptions(sub, false, true); state.subcommand.kill = sub; } { - auto* sub = cli->add_subcommand("ipc", "Communicate with other Quickshell instances.") - ->require_subcommand(); - state.ipc.ipc = sub; + auto* sub = cli->add_subcommand("msg", "Send messages to IpcHandlers.")->require_option(); - auto* instance = addInstanceSelection(sub); - addConfigSelection(sub, true)->excludes(instance); - addLoggingOptions(sub, false, true); + auto* target = sub->add_option("target", state.ipc.target, "The target to message."); - { - auto* show = sub->add_subcommand("show", "Print information about available IPC targets."); - state.ipc.show = show; - } + auto* function = sub->add_option("function", state.ipc.function) + ->description("The function to call in the target.") + ->needs(target); - { - auto* call = sub->add_subcommand("call", "Call an IpcHandler function."); - state.ipc.call = call; + auto* arguments = sub->add_option("arguments", state.ipc.arguments) + ->description("Arguments to the called function.") + ->needs(function) + ->allow_extra_args(); - call->add_option("target", state.ipc.target, "The target to message."); - - call->add_option("function", state.ipc.name) - ->description("The function to call in the target."); - - call->add_option("arguments", state.ipc.arguments) - ->description("Arguments to the called function.") - ->allow_extra_args(); - } - - { - auto* prop = - sub->add_subcommand("prop", "Manipulate IpcHandler properties.")->require_subcommand(); - - { - auto* get = prop->add_subcommand("get", "Read the value of a property."); - state.ipc.getprop = get; - get->add_option("target", state.ipc.target, "The target to read the property of."); - get->add_option("property", state.ipc.name)->description("The property to read."); - } - } - } - - { - auto* sub = cli->add_subcommand("msg", "[DEPRECATED] Moved to `ipc call`.")->require_option(); - - sub->add_option("target", state.ipc.target, "The target to message."); - - sub->add_option("function", state.ipc.name)->description("The function to call in the target."); - - sub->add_option("arguments", state.ipc.arguments) - ->description("Arguments to the called function.") - ->allow_extra_args(); - - sub->add_flag("-s,--show", state.ipc.showOld) + sub->add_flag("-s,--show", state.ipc.info) ->description("Print information about a function or target if given, or all available " - "targets if not."); + "targets if not.") + ->excludes(arguments); auto* instance = addInstanceSelection(sub); - addConfigSelection(sub, true)->excludes(instance); + addConfigSelection(sub)->excludes(instance); addLoggingOptions(sub, false, true); + sub->require_option(); + state.subcommand.msg = sub; } diff --git a/src/services/notifications/dbusimage.hpp b/src/services/notifications/dbusimage.hpp index c310d95e..d81d1e74 100644 --- a/src/services/notifications/dbusimage.hpp +++ b/src/services/notifications/dbusimage.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -21,22 +23,14 @@ struct DBusNotificationImage { const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap); const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap); -class NotificationImage: public QsIndexedImageHandle { +class NotificationImage: public QsImageHandle { public: - explicit NotificationImage(): QsIndexedImageHandle(QQuickAsyncImageProvider::Image) {} - - [[nodiscard]] bool hasData() const { return !this->image.data.isEmpty(); } - void clear() { this->image.data.clear(); } - - [[nodiscard]] DBusNotificationImage& writeImage() { - this->imageChanged(); - return this->image; - } + explicit NotificationImage(DBusNotificationImage image, QObject* parent) + : QsImageHandle(QQuickAsyncImageProvider::Image, parent) + , image(std::move(image)) {} QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; -private: DBusNotificationImage image; }; - } // namespace qs::service::notifications diff --git a/src/services/notifications/notification.cpp b/src/services/notifications/notification.cpp index 2703cf6d..51a64154 100644 --- a/src/services/notifications/notification.cpp +++ b/src/services/notifications/notification.cpp @@ -1,4 +1,5 @@ #include "notification.hpp" +#include #include #include @@ -116,12 +117,13 @@ void Notification::updateProperties( QString imagePath; - if (imageDataName.isEmpty()) { - this->mImagePixmap.clear(); - } else { + if (!imageDataName.isEmpty()) { auto value = hints.value(imageDataName).value(); - value >> this->mImagePixmap.writeImage(); - imagePath = this->mImagePixmap.url(); + DBusNotificationImage image; + value >> image; + if (this->mImagePixmap) this->mImagePixmap->deleteLater(); + this->mImagePixmap = new NotificationImage(std::move(image), this); + imagePath = this->mImagePixmap->url(); } // don't store giant byte arrays longer than necessary @@ -129,7 +131,7 @@ void Notification::updateProperties( hints.remove("image_data"); hints.remove("icon_data"); - if (!this->mImagePixmap.hasData()) { + if (!this->mImagePixmap) { QString imagePathName; if (hints.contains("image-path")) imagePathName = "image-path"; else if (hints.contains("image_path")) imagePathName = "image_path"; diff --git a/src/services/notifications/notification.hpp b/src/services/notifications/notification.hpp index 85fe023d..25b9e330 100644 --- a/src/services/notifications/notification.hpp +++ b/src/services/notifications/notification.hpp @@ -12,10 +12,11 @@ #include "../../core/retainable.hpp" #include "../../core/util.hpp" -#include "dbusimage.hpp" namespace qs::service::notifications { +class NotificationImage; + ///! The urgency level of a Notification. /// See @@Notification.urgency. class NotificationUrgency: public QObject { @@ -186,7 +187,7 @@ private: quint32 mId; NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed; bool mLastGeneration = false; - NotificationImage mImagePixmap; + NotificationImage* mImagePixmap = nullptr; QList mActions; // clang-format off diff --git a/src/services/status_notifier/item.cpp b/src/services/status_notifier/item.cpp index 42847399..d145c451 100644 --- a/src/services/status_notifier/item.cpp +++ b/src/services/status_notifier/item.cpp @@ -282,7 +282,7 @@ void StatusNotifierItem::onGetAllFailed() const { } TrayImageHandle::TrayImageHandle(StatusNotifierItem* item) - : QsImageHandle(QQmlImageProviderBase::Pixmap) + : QsImageHandle(QQmlImageProviderBase::Pixmap, item) , item(item) {} QPixmap diff --git a/src/wayland/hyprland/surface/hyprland-surface-v1.xml b/src/wayland/hyprland/surface/hyprland-surface-v1.xml index c4b1424f..2f683365 100644 --- a/src/wayland/hyprland/surface/hyprland-surface-v1.xml +++ b/src/wayland/hyprland/surface/hyprland-surface-v1.xml @@ -34,7 +34,7 @@ This protocol exposes hyprland-specific wl_surface properties. - + This interface allows a client to create hyprland surface objects. @@ -63,7 +63,7 @@ - + This interface allows access to hyprland-specific properties of a wl_surface. @@ -96,31 +96,5 @@ - - - - This request sets the region of the surface that contains visible content. - Visible content refers to content that has an alpha value greater than zero. - - The visible region is an optimization hint for the compositor that lets it - avoid drawing parts of the surface that are not visible. Setting a visible region - that does not contain all content in the surface may result in missing content - not being drawn. - - The visible region is specified in buffer-local coordinates. - - The compositor ignores the parts of the visible region that fall outside of the surface. - When all parts of the region fall outside of the buffer geometry, the compositor may - avoid rendering the surface entirely. - - The initial value for the visible region is empty. Setting the - visible region has copy semantics, and the wl_region object can be destroyed immediately. - A NULL wl_region causes the visible region to be set to empty. - - Does not take effect until wl_surface.commit is called. - - - - diff --git a/src/wayland/hyprland/surface/manager.cpp b/src/wayland/hyprland/surface/manager.cpp index 6354255e..31829bb6 100644 --- a/src/wayland/hyprland/surface/manager.cpp +++ b/src/wayland/hyprland/surface/manager.cpp @@ -7,13 +7,13 @@ namespace qs::hyprland::surface::impl { -HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(2) { +HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(1) { this->initialize(); } HyprlandSurface* HyprlandSurfaceManager::createHyprlandExtension(QtWaylandClient::QWaylandWindow* surface) { - return new HyprlandSurface(this->get_hyprland_surface(surface->surface()), surface); + return new HyprlandSurface(this->get_hyprland_surface(surface->surface())); } HyprlandSurfaceManager* HyprlandSurfaceManager::instance() { diff --git a/src/wayland/hyprland/surface/qml.cpp b/src/wayland/hyprland/surface/qml.cpp index b00ee33e..5150487f 100644 --- a/src/wayland/hyprland/surface/qml.cpp +++ b/src/wayland/hyprland/surface/qml.cpp @@ -1,20 +1,17 @@ #include "qml.hpp" #include -#include #include #include #include #include -#include #include #include -#include #include -#include "../../../core/region.hpp" #include "../../../window/proxywindow.hpp" #include "../../../window/windowinterface.hpp" +#include "../../util.hpp" #include "manager.hpp" #include "surface.hpp" @@ -43,15 +40,6 @@ HyprlandWindow::HyprlandWindow(ProxyWindowBase* window): QObject(nullptr), proxy &HyprlandWindow::onWindowConnected ); - QObject::connect(window, &ProxyWindowBase::polished, this, &HyprlandWindow::onWindowPolished); - - QObject::connect( - window, - &ProxyWindowBase::devicePixelRatioChanged, - this, - &HyprlandWindow::updateVisibleMask - ); - QObject::connect(window, &QObject::destroyed, this, &HyprlandWindow::onProxyWindowDestroyed); if (window->backingWindow()) { @@ -72,76 +60,14 @@ void HyprlandWindow::setOpacity(qreal opacity) { this->mOpacity = opacity; - if (this->surface && this->proxyWindow) { - this->pendingPolish.opacity = true; - this->proxyWindow->schedulePolish(); + if (this->surface) { + this->surface->setOpacity(opacity); + qs::wayland::util::scheduleCommit(this->proxyWindow); } emit this->opacityChanged(); } -PendingRegion* HyprlandWindow::visibleMask() const { return this->mVisibleMask; } - -void HyprlandWindow::setVisibleMask(PendingRegion* mask) { - if (mask == this->mVisibleMask) return; - - if (this->mVisibleMask) { - QObject::disconnect(this->mVisibleMask, nullptr, this, nullptr); - } - - this->mVisibleMask = mask; - - if (mask) { - QObject::connect(mask, &QObject::destroyed, this, &HyprlandWindow::onVisibleMaskDestroyed); - QObject::connect(mask, &PendingRegion::changed, this, &HyprlandWindow::updateVisibleMask); - } - - this->updateVisibleMask(); - emit this->visibleMaskChanged(); -} - -void HyprlandWindow::onVisibleMaskDestroyed() { - this->mVisibleMask = nullptr; - this->updateVisibleMask(); - emit this->visibleMaskChanged(); -} - -void HyprlandWindow::updateVisibleMask() { - if (!this->surface || !this->proxyWindow) return; - - this->pendingPolish.visibleMask = true; - this->proxyWindow->schedulePolish(); -} - -void HyprlandWindow::onWindowPolished() { - if (!this->surface) return; - - if (this->pendingPolish.opacity) { - this->surface->setOpacity(this->mOpacity); - this->pendingPolish.opacity = false; - } - - if (this->pendingPolish.visibleMask) { - QRegion mask; - if (this->mVisibleMask != nullptr) { - mask = - this->mVisibleMask->applyTo(QRect(0, 0, this->mWindow->width(), this->mWindow->height())); - } - - auto dpr = this->proxyWindow->devicePixelRatio(); - if (dpr != 1.0) { - mask = QHighDpi::scale(mask, dpr); - } - - if (mask.isEmpty() && this->mVisibleMask) { - mask = QRect(-1, -1, 1, 1); - } - - this->surface->setVisibleRegion(mask); - this->pendingPolish.visibleMask = false; - } -} - void HyprlandWindow::onWindowConnected() { this->mWindow = this->proxyWindow->backingWindow(); // disconnected by destructor @@ -160,46 +86,33 @@ void HyprlandWindow::onWindowVisibleChanged() { if (!this->mWindow->handle()) { this->mWindow->create(); } - } - auto* window = dynamic_cast(this->mWindow->handle()); - if (window == this->mWaylandWindow) return; + this->mWaylandWindow = dynamic_cast(this->mWindow->handle()); - if (this->mWaylandWindow) { - QObject::disconnect(this->mWaylandWindow, nullptr, this, nullptr); - } + if (this->mWaylandWindow) { + // disconnected by destructor - this->mWaylandWindow = window; - if (!window) return; + QObject::connect( + this->mWaylandWindow, + &QWaylandWindow::surfaceCreated, + this, + &HyprlandWindow::onWaylandSurfaceCreated + ); - QObject::connect( - this->mWaylandWindow, - &QObject::destroyed, - this, - &HyprlandWindow::onWaylandWindowDestroyed - ); + QObject::connect( + this->mWaylandWindow, + &QWaylandWindow::surfaceDestroyed, + this, + &HyprlandWindow::onWaylandSurfaceDestroyed + ); - QObject::connect( - this->mWaylandWindow, - &QWaylandWindow::surfaceCreated, - this, - &HyprlandWindow::onWaylandSurfaceCreated - ); - - QObject::connect( - this->mWaylandWindow, - &QWaylandWindow::surfaceDestroyed, - this, - &HyprlandWindow::onWaylandSurfaceDestroyed - ); - - if (this->mWaylandWindow->surface()) { - this->onWaylandSurfaceCreated(); + if (this->mWaylandWindow->surface()) { + this->onWaylandSurfaceCreated(); + } + } } } -void HyprlandWindow::onWaylandWindowDestroyed() { this->mWaylandWindow = nullptr; } - void HyprlandWindow::onWaylandSurfaceCreated() { auto* manager = impl::HyprlandSurfaceManager::instance(); @@ -209,26 +122,12 @@ void HyprlandWindow::onWaylandSurfaceCreated() { return; } - auto v = this->mWaylandWindow->property("hyprland_window_ext"); - if (v.canConvert()) { - auto* windowExt = v.value(); - if (windowExt != this && windowExt->surface) { - this->surface.swap(windowExt->surface); - } - } + auto* ext = manager->createHyprlandExtension(this->mWaylandWindow); + this->surface = std::unique_ptr(ext); - if (!this->surface) { - auto* ext = manager->createHyprlandExtension(this->mWaylandWindow); - this->surface = std::unique_ptr(ext); - } - - this->mWaylandWindow->setProperty("hyprland_window_ext", QVariant::fromValue(this)); - - this->pendingPolish.opacity = this->mOpacity != 1.0; - this->pendingPolish.visibleMask = this->mVisibleMask; - - if (this->pendingPolish.opacity || this->pendingPolish.visibleMask) { - this->proxyWindow->schedulePolish(); + if (this->mOpacity != 1.0) { + this->surface->setOpacity(this->mOpacity); + qs::wayland::util::scheduleCommit(this->proxyWindow); } } @@ -245,9 +144,8 @@ void HyprlandWindow::onProxyWindowDestroyed() { // Deleting it when the proxy window is deleted will cause a full opacity frame between the destruction of the // hyprland_surface_v1 and wl_surface objects. - this->proxyWindow = nullptr; - if (this->surface == nullptr) { + this->proxyWindow = nullptr; this->deleteLater(); } } diff --git a/src/wayland/hyprland/surface/qml.hpp b/src/wayland/hyprland/surface/qml.hpp index 157b8f32..ce32a967 100644 --- a/src/wayland/hyprland/surface/qml.hpp +++ b/src/wayland/hyprland/surface/qml.hpp @@ -9,7 +9,6 @@ #include #include -#include "../../../core/region.hpp" #include "../../../window/proxywindow.hpp" #include "surface.hpp" @@ -32,18 +31,11 @@ namespace qs::hyprland::surface { /// [hyprland-surface-v1]: https://github.com/hyprwm/hyprland-protocols/blob/main/protocols/hyprland-surface-v1.xml class HyprlandWindow: public QObject { Q_OBJECT; - // clang-format off /// A multiplier for the window's overall opacity, ranging from 1.0 to 0.0. Overall opacity includes the opacity of /// both the window content *and* visual effects such as blur that apply to it. /// /// Default: 1.0 Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged); - /// A hint to the compositor that only certain regions of the surface should be rendered. - /// This can be used to avoid rendering large empty regions of a window which can increase - /// performance, especially if the window is blurred. The mask should include all pixels - /// of the window that do not have an alpha value of 0. - Q_PROPERTY(PendingRegion* visibleMask READ visibleMask WRITE setVisibleMask NOTIFY visibleMaskChanged); - // clang-format on QML_ELEMENT; QML_UNCREATABLE("HyprlandWindow can only be used as an attached object."); QML_ATTACHED(HyprlandWindow); @@ -56,25 +48,17 @@ public: [[nodiscard]] qreal opacity() const; void setOpacity(qreal opacity); - [[nodiscard]] PendingRegion* visibleMask() const; - virtual void setVisibleMask(PendingRegion* mask); - static HyprlandWindow* qmlAttachedProperties(QObject* object); signals: void opacityChanged(); - void visibleMaskChanged(); private slots: void onWindowConnected(); void onWindowVisibleChanged(); - void onWaylandWindowDestroyed(); void onWaylandSurfaceCreated(); void onWaylandSurfaceDestroyed(); void onProxyWindowDestroyed(); - void onVisibleMaskDestroyed(); - void onWindowPolished(); - void updateVisibleMask(); private: void disconnectWaylandWindow(); @@ -83,13 +67,7 @@ private: QWindow* mWindow = nullptr; QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr; - struct { - bool opacity : 1 = false; - bool visibleMask : 1 = false; - } pendingPolish; - qreal mOpacity = 1.0; - PendingRegion* mVisibleMask = nullptr; std::unique_ptr surface; }; diff --git a/src/wayland/hyprland/surface/surface.cpp b/src/wayland/hyprland/surface/surface.cpp index 487da40b..d1aa24fb 100644 --- a/src/wayland/hyprland/surface/surface.cpp +++ b/src/wayland/hyprland/surface/surface.cpp @@ -1,53 +1,19 @@ #include "surface.hpp" -#include -#include -#include -#include -#include #include #include -#include #include #include namespace qs::hyprland::surface::impl { -HyprlandSurface::HyprlandSurface( - ::hyprland_surface_v1* surface, - QtWaylandClient::QWaylandWindow* backer -) - : QtWayland::hyprland_surface_v1(surface) - , backer(backer) - , backerSurface(backer->surface()) {} +HyprlandSurface::HyprlandSurface(::hyprland_surface_v1* surface) + : QtWayland::hyprland_surface_v1(surface) {} HyprlandSurface::~HyprlandSurface() { this->destroy(); } -bool HyprlandSurface::surfaceEq(wl_surface* surface) const { - return surface == this->backerSurface; -} - void HyprlandSurface::setOpacity(qreal opacity) { this->set_opacity(wl_fixed_from_double(opacity)); } -void HyprlandSurface::setVisibleRegion(const QRegion& region) { - if (this->version() < HYPRLAND_SURFACE_V1_SET_VISIBLE_REGION_SINCE_VERSION) { - qWarning() << "Cannot set hyprland surface visible region: compositor does not support " - "hyprland_surface_v1.set_visible_region"; - return; - } - - if (region.isEmpty()) { - this->set_visible_region(nullptr); - } else { - static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance(); - auto* display = waylandIntegration->display(); - - auto* wlRegion = display->createRegion(region); - this->set_visible_region(wlRegion); - wl_region_destroy(wlRegion); // NOLINT(misc-include-cleaner) - } -} - } // namespace qs::hyprland::surface::impl diff --git a/src/wayland/hyprland/surface/surface.hpp b/src/wayland/hyprland/surface/surface.hpp index 1c8b5486..a27e50e3 100644 --- a/src/wayland/hyprland/surface/surface.hpp +++ b/src/wayland/hyprland/surface/surface.hpp @@ -1,31 +1,21 @@ #pragma once -#include #include -#include #include #include #include #include -#include #include namespace qs::hyprland::surface::impl { class HyprlandSurface: public QtWayland::hyprland_surface_v1 { public: - explicit HyprlandSurface(::hyprland_surface_v1* surface, QtWaylandClient::QWaylandWindow* backer); + explicit HyprlandSurface(::hyprland_surface_v1* surface); ~HyprlandSurface() override; Q_DISABLE_COPY_MOVE(HyprlandSurface); - [[nodiscard]] bool surfaceEq(wl_surface* surface) const; - void setOpacity(qreal opacity); - void setVisibleRegion(const QRegion& region); - -private: - QtWaylandClient::QWaylandWindow* backer; - wl_surface* backerSurface = nullptr; }; } // namespace qs::hyprland::surface::impl diff --git a/src/wayland/wlr_layershell.cpp b/src/wayland/wlr_layershell.cpp index a649603c..1a338522 100644 --- a/src/wayland/wlr_layershell.cpp +++ b/src/wayland/wlr_layershell.cpp @@ -90,8 +90,8 @@ void WlrLayershell::setHeight(qint32 height) { } void WlrLayershell::setScreen(QuickshellScreenInfo* screen) { - this->ext->setUseWindowScreen(screen != nullptr); this->ProxyWindowBase::setScreen(screen); + this->ext->setUseWindowScreen(screen != nullptr); } // NOLINTBEGIN diff --git a/src/window/proxywindow.cpp b/src/window/proxywindow.cpp index 8ce80c20..f75fc641 100644 --- a/src/window/proxywindow.cpp +++ b/src/window/proxywindow.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -201,6 +200,9 @@ void ProxyWindowBase::completeWindow() { if (this->mScreen != nullptr && this->window->screen() != this->mScreen) { if (this->window->isVisible()) this->window->setVisible(false); this->window->setScreen(this->mScreen); + } else if (this->mScreen == nullptr) { + this->mScreen = this->window->screen(); + QObject::connect(this->mScreen, &QObject::destroyed, this, &ProxyWindowBase::onScreenDestroyed); } this->setWidth(this->mWidth); @@ -325,39 +327,39 @@ void ProxyWindowBase::setHeight(qint32 height) { void ProxyWindowBase::setScreen(QuickshellScreenInfo* screen) { auto* qscreen = screen == nullptr ? nullptr : screen->screen; - auto newMScreen = this->mScreen != qscreen; + if (qscreen == this->mScreen) return; - if (this->mScreen && newMScreen) { + if (this->mScreen != nullptr) { QObject::disconnect(this->mScreen, nullptr, this, nullptr); } - if (this->qscreen() != qscreen) { - this->mScreen = qscreen; - if (this->window == nullptr) { - emit this->screenChanged(); - } else if (qscreen) { - auto reshow = this->isVisibleDirect(); - if (reshow) this->setVisibleDirect(false); - if (this->window != nullptr) this->window->setScreen(qscreen); - if (reshow) this->setVisibleDirect(true); - } + if (this->window == nullptr) { + emit this->screenChanged(); + } else { + auto reshow = this->isVisibleDirect(); + if (reshow) this->setVisibleDirect(false); + if (this->window != nullptr) this->window->setScreen(qscreen); + if (reshow) this->setVisibleDirect(true); } - if (qscreen && newMScreen) { - QObject::connect(this->mScreen, &QObject::destroyed, this, &ProxyWindowBase::onScreenDestroyed); - } + if (qscreen) this->mScreen = qscreen; + else this->mScreen = this->window->screen(); + + QObject::connect(this->mScreen, &QObject::destroyed, this, &ProxyWindowBase::onScreenDestroyed); } void ProxyWindowBase::onScreenDestroyed() { this->mScreen = nullptr; } -QScreen* ProxyWindowBase::qscreen() const { - if (this->window) return this->window->screen(); - if (this->mScreen) return this->mScreen; - return QGuiApplication::primaryScreen(); -} - QuickshellScreenInfo* ProxyWindowBase::screen() const { - return QuickshellTracked::instance()->screenInfo(this->qscreen()); + QScreen* qscreen = nullptr; + + if (this->window == nullptr) { + if (this->mScreen != nullptr) qscreen = this->mScreen; + } else { + qscreen = this->window->screen(); + } + + return QuickshellTracked::instance()->screenInfo(qscreen); } QColor ProxyWindowBase::color() const { return this->mColor; } diff --git a/src/window/proxywindow.hpp b/src/window/proxywindow.hpp index 92a85f4f..6f02e05a 100644 --- a/src/window/proxywindow.hpp +++ b/src/window/proxywindow.hpp @@ -100,8 +100,7 @@ public: [[nodiscard]] qreal devicePixelRatio() const; - [[nodiscard]] QScreen* qscreen() const; - [[nodiscard]] QuickshellScreenInfo* screen() const; + [[nodiscard]] virtual QuickshellScreenInfo* screen() const; virtual void setScreen(QuickshellScreenInfo* screen); [[nodiscard]] QColor color() const;