diff --git a/.clang-tidy b/.clang-tidy index 002c444d..ca6c9549 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,7 +7,6 @@ Checks: > -bugprone-easily-swappable-parameters, -bugprone-forward-declararion-namespace, -bugprone-forward-declararion-namespace, - -bugprone-return-const-ref-from-parameter, concurrency-*, cppcoreguidelines-*, -cppcoreguidelines-owning-memory, @@ -45,7 +44,6 @@ Checks: > -readability-container-data-pointer, -readability-implicit-bool-conversion, -readability-avoid-nested-conditional-operator, - -readability-math-missing-parentheses, tidyfox-*, CheckOptions: performance-for-range-copy.WarnOnAllAutoCopies: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a67e5f43..b176e982 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ jobs: name: Nix strategy: matrix: - qtver: [qt6.8.1, qt6.8.0, qt6.7.3, qt6.7.2, qt6.7.1, qt6.7.0, qt6.6.3, qt6.6.2, qt6.6.1, qt6.6.0] + qtver: [qt6.8.0, qt6.7.3, qt6.7.2, qt6.7.1, qt6.7.0, qt6.6.3, qt6.6.2, qt6.6.1, qt6.6.0] compiler: [clang, gcc] runs-on: ubuntu-latest steps: @@ -43,7 +43,6 @@ jobs: qt6-shadertools \ wayland-protocols \ wayland \ - libdrm \ libxcb \ libpipewire \ cli11 \ diff --git a/BUILD.md b/BUILD.md index 3172dbe3..cf6b3a03 100644 --- a/BUILD.md +++ b/BUILD.md @@ -130,24 +130,6 @@ which allows quickshell to be used as a session lock under compatible wayland co To disable: `-DWAYLAND_TOPLEVEL_MANAGEMENT=OFF` -#### Screencopy -Enables streaming video from monitors and toplevel windows through various protocols. - -To disable: `-DSCREENCOPY=OFF` - -Dependencies: -- `libdrm` -- `libgbm` - -Specific protocols can also be disabled: -- `DSCREENCOPY_ICC=OFF` - Disable screencopy via [ext-image-copy-capture-v1] -- `DSCREENCOPY_WLR=OFF` - Disable screencopy via [zwlr-screencopy-v1] -- `DSCREENCOPY_HYPRLAND_TOPLEVEL=OFF` - Disable screencopy via [hyprland-toplevel-export-v1] - -[ext-image-copy-capture-v1]:https://wayland.app/protocols/ext-image-copy-capture-v1 -[zwlr-screencopy-v1]: https://wayland.app/protocols/wlr-screencopy-unstable-v1 -[hyprland-toplevel-export-v1]: https://wayland.app/protocols/hyprland-toplevel-export-v1 - ### X11 This feature enables x11 support. Currently this implements panel windows for X11 similarly to the wlroots layershell above. diff --git a/CMakeLists.txt b/CMakeLists.txt index 846a280c..a4919952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,10 +56,6 @@ boption(HYPRLAND_IPC " Hyprland IPC" ON REQUIRES HYPRLAND) boption(HYPRLAND_GLOBAL_SHORTCUTS " Hyprland Global Shortcuts" ON REQUIRES HYPRLAND) boption(HYPRLAND_FOCUS_GRAB " Hyprland Focus Grabbing" ON REQUIRES HYPRLAND) boption(HYPRLAND_SURFACE_EXTENSIONS " Hyprland Surface Extensions" ON REQUIRES HYPRLAND) -boption(SCREENCOPY " Screencopy" ON REQUIRES WAYLAND) -boption(SCREENCOPY_ICC " Image Copy Capture" ON REQUIRES WAYLAND) -boption(SCREENCOPY_WLR " Wlroots Screencopy" ON REQUIRES WAYLAND) -boption(SCREENCOPY_HYPRLAND_TOPLEVEL " Hyprland Toplevel Export" ON REQUIRES WAYLAND) boption(X11 "X11" ON) boption(I3 "I3/Sway" ON) boption(I3_IPC " I3/Sway IPC" ON REQUIRES I3) @@ -74,7 +70,7 @@ boption(SERVICE_NOTIFICATIONS "Notifications" ON) include(cmake/install-qml-module.cmake) include(cmake/util.cmake) -add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension) +add_compile_options(-Wall -Wextra) # pipewire defines this, breaking PCH add_compile_definitions(_REENTRANT) diff --git a/Justfile b/Justfile index f60771aa..b4fe87ec 100644 --- a/Justfile +++ b/Justfile @@ -4,13 +4,13 @@ fmt: find src -type f \( -name "*.cpp" -o -name "*.hpp" \) -print0 | xargs -0 clang-format -i lint: - find src -type f -name "*.cpp" -print0 | parallel -j$(nproc) -q0 --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} + find src -type f -name "*.cpp" -print0 | parallel -q0 --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} lint-ci: - find src -type f -name "*.cpp" -print0 | parallel -j$(nproc) -q0 --no-notice --will-cite --tty clang-tidy --load={{ env_var("TIDYFOX") }} + find src -type f -name "*.cpp" -print0 | parallel -q0 --no-notice --will-cite --tty clang-tidy --load={{ env_var("TIDYFOX") }} lint-changed: - git diff --name-only HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} + git diff --name-only HEAD | grep "^.*\.cpp\$" | parallel --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} configure target='debug' *FLAGS='': cmake -GNinja -B {{builddir}} \ diff --git a/ci/nix-checkouts.nix b/ci/nix-checkouts.nix index f38d5fa4..3c99ab11 100644 --- a/ci/nix-checkouts.nix +++ b/ci/nix-checkouts.nix @@ -11,14 +11,9 @@ in { # For old qt versions, grab the commit before the version bump that has all the patches # instead of the bumped version. - qt6_8_1 = byCommit { - commit = "3df3c47c19dc90fec35359e89ffb52b34d2b0e94"; - sha256 = "1lhlm7czhwwys5ak6ngb5li6bxddilb9479k9nkss502kw8hwjyz"; - }; - qt6_8_0 = byCommit { - commit = "352f462ad9d2aa2cde75fdd8f1734e86402a3ff6"; - sha256 = "02zfgkr9fpd6iwfh6dcr3m6fnx61jppm3v081f3brvkqwmmz7zq1"; + commit = "23e89b7da85c3640bbc2173fe04f4bd114342367"; + sha256 = "1b2v6y3bja4br5ribh9lj6xzz2k81dggz708b2mib83rwb509wyb"; }; qt6_7_3 = byCommit { diff --git a/default.nix b/default.nix index 79c9b7a4..fab038a7 100644 --- a/default.nix +++ b/default.nix @@ -14,8 +14,6 @@ jemalloc, wayland, wayland-protocols, - libdrm, - libgbm ? null, xorg, pipewire, pam, @@ -66,7 +64,7 @@ ++ lib.optional withCrashReporter breakpad ++ lib.optional withJemalloc jemalloc ++ lib.optional withQtSvg qt6.qtsvg - ++ lib.optionals withWayland ([ qt6.qtwayland wayland ] ++ (if libgbm != null then [ libdrm libgbm ] else [])) + ++ lib.optionals withWayland [ qt6.qtwayland wayland ] ++ lib.optional withX11 xorg.libxcb ++ lib.optional withPam pam ++ lib.optional withPipewire pipewire; @@ -81,7 +79,6 @@ (lib.cmakeBool "CRASH_REPORTER" withCrashReporter) (lib.cmakeBool "USE_JEMALLOC" withJemalloc) (lib.cmakeBool "WAYLAND" withWayland) - (lib.cmakeBool "SCREENCOPY" (libgbm != null)) (lib.cmakeBool "SERVICE_PIPEWIRE" withPipewire) (lib.cmakeBool "SERVICE_PAM" withPam) (lib.cmakeBool "HYPRLAND" withHyprland) diff --git a/flake.lock b/flake.lock index df5aa3f9..ed928826 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1736012469, - "narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=", + "lastModified": 1732014248, + "narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev": "23e89b7da85c3640bbc2173fe04f4bd114342367", "type": "github" }, "original": { 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/desktopentry.cpp b/src/core/desktopentry.cpp index 75a088d9..3714df01 100644 --- a/src/core/desktopentry.cpp +++ b/src/core/desktopentry.cpp @@ -18,9 +18,7 @@ #include "model.hpp" -namespace { Q_LOGGING_CATEGORY(logDesktopEntry, "quickshell.desktopentry", QtWarningMsg); -} struct Locale { explicit Locale() = default; @@ -80,7 +78,6 @@ struct Locale { QString modifier; }; -// NOLINTNEXTLINE(misc-use-internal-linkage) QDebug operator<<(QDebug debug, const Locale& locale) { auto saver = QDebugStateSaver(debug); debug.nospace() << "Locale(language=" << locale.language << ", territory=" << locale.territory @@ -213,7 +210,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 +226,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()) { @@ -309,7 +306,7 @@ void DesktopEntryManager::scanPath(const QDir& dir, const QString& prefix) { auto entries = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); for (auto& entry: entries) { - if (entry.isDir()) this->scanPath(entry.absoluteFilePath(), prefix + dir.dirName() + "-"); + if (entry.isDir()) this->scanPath(entry.path(), prefix + dir.dirName() + "-"); else if (entry.isFile()) { auto path = entry.filePath(); if (!path.endsWith(".desktop")) { @@ -317,8 +314,9 @@ void DesktopEntryManager::scanPath(const QDir& dir, const QString& prefix) { continue; } - auto file = QFile(path); - if (!file.open(QFile::ReadOnly)) { + auto* file = new QFile(path); + + if (!file->open(QFile::ReadOnly)) { qCDebug(logDesktopEntry) << "Could not open file" << path; continue; } @@ -326,7 +324,7 @@ void DesktopEntryManager::scanPath(const QDir& dir, const QString& prefix) { auto id = prefix + entry.fileName().sliced(0, entry.fileName().length() - 8); auto lowerId = id.toLower(); - auto text = QString::fromUtf8(file.readAll()); + auto text = QString::fromUtf8(file->readAll()); auto* dentry = new DesktopEntry(id, this); dentry->parseEntry(text); diff --git a/src/core/iconimageprovider.cpp b/src/core/iconimageprovider.cpp index 43e00fd8..cf24d37d 100644 --- a/src/core/iconimageprovider.cpp +++ b/src/core/iconimageprovider.cpp @@ -1,5 +1,4 @@ #include "iconimageprovider.hpp" -#include #include #include @@ -50,8 +49,8 @@ IconImageProvider::requestPixmap(const QString& id, QSize* size, const QSize& re QPixmap IconImageProvider::missingPixmap(const QSize& size) { auto width = size.width() % 2 == 0 ? size.width() : size.width() + 1; auto height = size.height() % 2 == 0 ? size.height() : size.height() + 1; - width = std::max(width, 2); - height = std::max(height, 2); + if (width < 2) width = 2; + if (height < 2) height = 2; auto pixmap = QPixmap(width, height); pixmap.fill(QColorConstants::Black); diff --git a/src/core/imageprovider.cpp b/src/core/imageprovider.cpp index 47f284c7..cc81c47f 100644 --- a/src/core/imageprovider.cpp +++ b/src/core/imageprovider.cpp @@ -1,6 +1,5 @@ #include "imageprovider.hpp" -#include #include #include #include @@ -8,30 +7,17 @@ #include #include #include -#include -namespace { +static QMap liveImages; // NOLINT -namespace { -QMap liveImages; // NOLINT -quint32 handleIndex = 0; // NOLINT -} // namespace - -void parseReq(const QString& req, QString& target, QString& param) { - auto splitIdx = req.indexOf('/'); - if (splitIdx != -1) { - target = req.sliced(0, splitIdx); - param = req.sliced(splitIdx + 1); - } else { - target = req; +QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent) + : QObject(parent) + , type(type) { + { + auto dbg = QDebug(&this->id); + dbg.nospace() << static_cast(this); } -} -} // namespace - -QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type) - : type(type) - , id(QString::number(++handleIndex)) { liveImages.insert(this->id, this); } @@ -57,6 +43,16 @@ QPixmap QsImageHandle:: return QPixmap(); } +void parseReq(const QString& req, QString& target, QString& param) { + auto splitIdx = req.indexOf('/'); + if (splitIdx != -1) { + target = req.sliced(0, splitIdx); + param = req.sliced(splitIdx + 1); + } else { + target = req; + } +} + QImage QsImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize) { QString target; QString param; @@ -85,9 +81,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 c8b17ab9..831f561b 100644 --- a/src/core/module.md +++ b/src/core/module.md @@ -28,6 +28,5 @@ headers = [ "types.hpp", "qsmenuanchor.hpp", "clock.hpp", - "scriptmodel.hpp", ] ----- diff --git a/src/core/paths.cpp b/src/core/paths.cpp index e49a9d41..e108da03 100644 --- a/src/core/paths.cpp +++ b/src/core/paths.cpp @@ -15,9 +15,7 @@ #include "instanceinfo.hpp" -namespace { Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg); -} QsPaths* QsPaths::instance() { static auto* instance = new QsPaths(); // NOLINT diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index 0eb9a067..c6ceb13e 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -10,9 +10,16 @@ static QVector plugins; // NOLINT void QsEnginePlugin::registerPlugin(QsEnginePlugin& plugin) { plugins.push_back(&plugin); } void QsEnginePlugin::initPlugins() { - plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); }); + plugins.erase( + std::remove_if( + plugins.begin(), + plugins.end(), + [](QsEnginePlugin* plugin) { return !plugin->applies(); } + ), + plugins.end() + ); - std::ranges::sort(plugins, [](QsEnginePlugin* a, QsEnginePlugin* b) { + std::sort(plugins.begin(), plugins.end(), [](QsEnginePlugin* a, QsEnginePlugin* b) { return b->dependencies().contains(a->name()); }); diff --git a/src/core/popupanchor.cpp b/src/core/popupanchor.cpp index f72d5c59..2b5b1bae 100644 --- a/src/core/popupanchor.cpp +++ b/src/core/popupanchor.cpp @@ -1,5 +1,4 @@ #include "popupanchor.hpp" -#include #include #include @@ -277,7 +276,9 @@ void PopupPositioner::reposition(PopupAnchor* anchor, QWindow* window, bool only effectiveX = screenGeometry.right() - windowGeometry.width() + 1; } - effectiveX = std::max(effectiveX, screenGeometry.left()); + if (effectiveX < screenGeometry.left()) { + effectiveX = screenGeometry.left(); + } } if (adjustment.testFlag(PopupAdjustment::SlideY)) { @@ -285,7 +286,9 @@ void PopupPositioner::reposition(PopupAnchor* anchor, QWindow* window, bool only effectiveY = screenGeometry.bottom() - windowGeometry.height() + 1; } - effectiveY = std::max(effectiveY, screenGeometry.top()); + if (effectiveY < screenGeometry.top()) { + effectiveY = screenGeometry.top(); + } } if (adjustment.testFlag(PopupAdjustment::ResizeX)) { 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..69c0762d 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); @@ -44,7 +40,7 @@ class QuickshellScreenInfo: public QObject { /// The ratio between physical pixels and device-independent (scaled) pixels. Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY physicalPixelDensityChanged); Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged); - Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged); + Q_PROPERTY(Qt::ScreenOrientation primatyOrientation READ primaryOrientation NOTIFY primaryOrientationChanged); // clang-format on public: @@ -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/core/region.cpp b/src/core/region.cpp index 439cfbd2..47f15d4a 100644 --- a/src/core/region.cpp +++ b/src/core/region.cpp @@ -8,7 +8,6 @@ #include #include #include -#include PendingRegion::PendingRegion(QObject* parent): QObject(parent) { QObject::connect(this, &PendingRegion::shapeChanged, this, &PendingRegion::changed); @@ -106,19 +105,8 @@ QRegion PendingRegion::applyTo(QRegion& region) const { return region; } -QRegion PendingRegion::applyTo(const QRect& rect) const { - // if left as the default, dont combine it with the whole rect area, leave it as is. - if (this->mIntersection == Intersection::Combine) { - return this->build(); - } else { - auto baseRegion = QRegion(rect); - return this->applyTo(baseRegion); - } -} - void PendingRegion::regionsAppend(QQmlListProperty* prop, PendingRegion* region) { auto* self = static_cast(prop->object); // NOLINT - if (!region) return; QObject::connect(region, &QObject::destroyed, self, &PendingRegion::onChildDestroyed); QObject::connect(region, &PendingRegion::changed, self, &PendingRegion::childrenChanged); diff --git a/src/core/region.hpp b/src/core/region.hpp index 6637d7bd..02d7a26b 100644 --- a/src/core/region.hpp +++ b/src/core/region.hpp @@ -96,7 +96,6 @@ public: [[nodiscard]] bool empty() const; [[nodiscard]] QRegion build() const; [[nodiscard]] QRegion applyTo(QRegion& region) const; - [[nodiscard]] QRegion applyTo(const QRect& rect) const; RegionShape::Enum mShape = RegionShape::Rect; Intersection::Enum mIntersection = Intersection::Combine; @@ -110,11 +109,6 @@ signals: void widthChanged(); void heightChanged(); void childrenChanged(); - - /// Triggered when the region's geometry changes. - /// - /// In some cases the region does not update automatically. - /// In those cases you can emit this signal manually. void changed(); private slots: diff --git a/src/core/scriptmodel.hpp b/src/core/scriptmodel.hpp index 10a42d6b..b57456b3 100644 --- a/src/core/scriptmodel.hpp +++ b/src/core/scriptmodel.hpp @@ -36,8 +36,6 @@ /// delegate: // ... /// } /// ``` -/// [QAbstractItemModel]: https://doc.qt.io/qt-6/qabstractitemmodel.html -/// [Data Model]: https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html#qml-data-models class ScriptModel: public QAbstractListModel { Q_OBJECT; /// The list of values to reflect in the model. @@ -53,19 +51,8 @@ class ScriptModel: public QAbstractListModel { /// > } /// > ``` /// > - /// > Note that we are using @@ObjectModel.values because it will cause @@ScriptModel.values + /// > Note that we are using @@DesktopEntries.values because it will cause @@ScriptModel.values /// > to receive an update on change. - /// - /// > [!TIP] Most lists exposed by Quickshell are read-only. Some operations like `sort()` - /// > act on a list in-place and cannot be used directly on a list exposed by Quickshell. - /// > You can copy a list using spread syntax: `[...variable]` instead of `variable`. - /// > - /// > For example: - /// > ```qml - /// > ScriptModel { - /// > values: [...DesktopEntries.applications.values].sort(...) - /// > } - /// > ``` Q_PROPERTY(QVariantList values READ values WRITE setValues NOTIFY valuesChanged); QML_ELEMENT; diff --git a/src/core/stacklist.hpp b/src/core/stacklist.hpp deleted file mode 100644 index 41dc58ee..00000000 --- a/src/core/stacklist.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include - -template -class StackList { -public: - T& operator[](size_t i) { - if (i < N) { - return this->array[i]; - } else { - return this->vec[i - N]; - } - } - - const T& operator[](size_t i) const { - return const_cast*>(this)->operator[](i); // NOLINT - } - - void push(const T& value) { - if (this->size < N) { - this->array[this->size] = value; - } else { - this->vec.push_back(value); - } - - ++this->size; - } - - [[nodiscard]] size_t length() const { return this->size; } - [[nodiscard]] bool isEmpty() const { return this->size == 0; } - - [[nodiscard]] bool operator==(const StackList& other) const { - if (other.size != this->size) return false; - - for (size_t i = 0; i != this->size; ++i) { - if (this->operator[](i) != other[i]) return false; - } - - return true; - } - - [[nodiscard]] QList toList() const { - QList list; - list.reserve(this->size); - - for (const auto& entry: *this) { - list.push_back(entry); - } - - return list; - } - - template - struct BaseIterator { - using iterator_category = std::bidirectional_iterator_tag; - using difference_type = int64_t; - using value_type = IT; - using pointer = IT*; - using reference = IT&; - - BaseIterator() = default; - explicit BaseIterator(ListPtr list, size_t i): list(list), i(i) {} - - reference operator*() const { return this->list->operator[](this->i); } - pointer operator->() const { return &**this; } - - Self& operator++() { - ++this->i; - return *static_cast(this); - } - - Self& operator--() { - --this->i; - return *static_cast(this); - } - - Self operator++(int) { - auto v = *this; - this->operator++(); - return v; - } - - Self operator--(int) { - auto v = *this; - this->operator--(); - return v; - } - - difference_type operator-(const Self& other) { - return static_cast(this->i) - static_cast(other.i); - } - - Self& operator+(difference_type offset) { - return Self(this->list, static_cast(this->i) + offset); - } - - [[nodiscard]] bool operator==(const Self& other) const { - return this->list == other.list && this->i == other.i; - } - - [[nodiscard]] bool operator!=(const Self& other) const { return !(*this == other); } - - private: - ListPtr list = nullptr; - size_t i = 0; - }; - - struct Iterator: public BaseIterator*, T> { - Iterator() = default; - Iterator(StackList* list, size_t i) - : BaseIterator*, T>(list, i) {} - }; - - struct ConstIterator: public BaseIterator*, const T> { - ConstIterator() = default; - ConstIterator(const StackList* list, size_t i) - : BaseIterator*, const T>(list, i) {} - }; - - [[nodiscard]] Iterator begin() { return Iterator(this, 0); } - [[nodiscard]] Iterator end() { return Iterator(this, this->size); } - - [[nodiscard]] ConstIterator begin() const { return ConstIterator(this, 0); } - [[nodiscard]] ConstIterator end() const { return ConstIterator(this, this->size); } - - [[nodiscard]] bool isContiguous() const { return this->vec.empty(); } - [[nodiscard]] const T* pArray() const { return this->array.data(); } - [[nodiscard]] size_t dataLength() const { return this->size * sizeof(T); } - - const T* populateAlloc(void* alloc) const { - auto arraylen = std::min(this->size, N) * sizeof(T); - memcpy(alloc, this->array.data(), arraylen); - - if (!this->vec.empty()) { - memcpy( - static_cast(alloc) + arraylen, // NOLINT - this->vec.data(), - this->vec.size() * sizeof(T) - ); - } - - return static_cast(alloc); - } - -private: - std::array array {}; - std::vector vec; - size_t size = 0; -}; - -// might be incorrectly aligned depending on type -// #define STACKLIST_ALLOCA_VIEW(list) ((list).isContiguous() ? (list).pArray() : (list).populateAlloc(alloca((list).dataLength()))) - -// NOLINTBEGIN -#define STACKLIST_VLA_VIEW(type, list, var) \ - const type* var; \ - type var##Data[(list).length()]; \ - if ((list).isContiguous()) { \ - (var) = (list).pArray(); \ - } else { \ - (list).populateAlloc(var##Data); \ - (var) = var##Data; \ - } -// NOLINTEND diff --git a/src/core/test/CMakeLists.txt b/src/core/test/CMakeLists.txt index bb49192d..d38c2868 100644 --- a/src/core/test/CMakeLists.txt +++ b/src/core/test/CMakeLists.txt @@ -7,4 +7,3 @@ endfunction() qs_test(transformwatcher transformwatcher.cpp) qs_test(ringbuffer ringbuf.cpp) qs_test(scriptmodel scriptmodel.cpp) -qs_test(stacklist stacklist.cpp) diff --git a/src/core/test/scriptmodel.cpp b/src/core/test/scriptmodel.cpp index 66746832..bdf9c709 100644 --- a/src/core/test/scriptmodel.cpp +++ b/src/core/test/scriptmodel.cpp @@ -22,7 +22,6 @@ bool ModelOperation::operator==(const ModelOperation& other) const { && other.length == this->length && other.destIndex == this->destIndex; } -// NOLINTNEXTLINE(misc-use-internal-linkage) QDebug& operator<<(QDebug& debug, const ModelOperation& op) { auto saver = QDebugStateSaver(debug); debug.nospace(); @@ -44,7 +43,6 @@ QDebug& operator<<(QDebug& debug, const ModelOperation& op) { return debug; } -// NOLINTNEXTLINE(misc-use-internal-linkage) QDebug& operator<<(QDebug& debug, const QVariantList& list) { auto str = QString(); diff --git a/src/core/test/stacklist.cpp b/src/core/test/stacklist.cpp deleted file mode 100644 index 9b981729..00000000 --- a/src/core/test/stacklist.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "stacklist.hpp" -#include - -#include -#include -#include - -#include "../stacklist.hpp" - -void TestStackList::push() { - StackList list; - - list.push(1); - list.push(2); - - QCOMPARE_EQ(list.toList(), QList({1, 2})); - QCOMPARE_EQ(list.length(), 2); -} - -void TestStackList::pushAndGrow() { - StackList list; - - list.push(1); - list.push(2); - list.push(3); - list.push(4); - - QCOMPARE_EQ(list.toList(), QList({1, 2, 3, 4})); - QCOMPARE_EQ(list.length(), 4); -} - -void TestStackList::copy() { - StackList list; - - list.push(1); - list.push(2); - list.push(3); - list.push(4); - - QCOMPARE_EQ(list.toList(), QList({1, 2, 3, 4})); - QCOMPARE_EQ(list.length(), 4); - - auto list2 = list; - - QCOMPARE_EQ(list2.toList(), QList({1, 2, 3, 4})); - QCOMPARE_EQ(list2.length(), 4); - QCOMPARE_EQ(list2, list); -} - -void TestStackList::viewVla() { - StackList list; - - list.push(1); - list.push(2); - - QCOMPARE_EQ(list.toList(), QList({1, 2})); - QCOMPARE_EQ(list.length(), 2); - - STACKLIST_VLA_VIEW(int, list, listView); - - QList ql; - - for (size_t i = 0; i != list.length(); ++i) { - ql.push_back(listView[i]); // NOLINT - } - - QCOMPARE_EQ(ql, list.toList()); -} - -void TestStackList::viewVlaGrown() { - StackList list; - - list.push(1); - list.push(2); - list.push(3); - list.push(4); - - QCOMPARE_EQ(list.toList(), QList({1, 2, 3, 4})); - QCOMPARE_EQ(list.length(), 4); - - STACKLIST_VLA_VIEW(int, list, listView); - - QList ql; - - for (size_t i = 0; i != list.length(); ++i) { - ql.push_back(listView[i]); // NOLINT - } - - QCOMPARE_EQ(ql, list.toList()); -} - -QTEST_MAIN(TestStackList); diff --git a/src/core/test/stacklist.hpp b/src/core/test/stacklist.hpp deleted file mode 100644 index f582761d..00000000 --- a/src/core/test/stacklist.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -class TestStackList: public QObject { - Q_OBJECT; - -private slots: - static void push(); - static void pushAndGrow(); - static void copy(); - static void viewVla(); - static void viewVlaGrown(); -}; diff --git a/src/core/variants.cpp b/src/core/variants.cpp index a190e36d..6c8713b6 100644 --- a/src/core/variants.cpp +++ b/src/core/variants.cpp @@ -196,7 +196,7 @@ V* AwfulMap::get(const K& key) { } template -void AwfulMap::insert(const K& key, V value) { +void AwfulMap::insert(K key, V value) { this->values.push_back(QPair(key, value)); } diff --git a/src/core/variants.hpp b/src/core/variants.hpp index fa0333d3..ebf87ae1 100644 --- a/src/core/variants.hpp +++ b/src/core/variants.hpp @@ -20,8 +20,8 @@ class AwfulMap { public: [[nodiscard]] bool contains(const K& key) const; [[nodiscard]] V* get(const K& key); - void insert(const K& key, V value); // assumes no duplicates - bool remove(const K& key); // returns true if anything was removed + void insert(K key, V value); // assumes no duplicates + bool remove(const K& key); // returns true if anything was removed QList> values; }; diff --git a/src/crash/handler.cpp b/src/crash/handler.cpp index 8d9a8a71..1f300cc9 100644 --- a/src/crash/handler.cpp +++ b/src/crash/handler.cpp @@ -22,9 +22,7 @@ using namespace google_breakpad; namespace qs::crash { -namespace { Q_LOGGING_CATEGORY(logCrashHandler, "quickshell.crashhandler", QtWarningMsg); -} struct CrashHandlerPrivate { ExceptionHandler* exceptionHandler = nullptr; diff --git a/src/crash/main.cpp b/src/crash/main.cpp index 7c3bad73..1beb6749 100644 --- a/src/crash/main.cpp +++ b/src/crash/main.cpp @@ -22,10 +22,49 @@ #include "build.hpp" #include "interface.hpp" -namespace { - Q_LOGGING_CATEGORY(logCrashReporter, "quickshell.crashreporter", QtWarningMsg); +void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance); + +void qsCheckCrash(int argc, char** argv) { + auto fd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD"); + if (fd.isEmpty()) return; + auto app = QApplication(argc, argv); + + RelaunchInfo info; + + auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt(); + + { + auto infoFd = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD").toInt(); + + QFile file; + file.open(infoFd, QFile::ReadOnly, QFile::AutoCloseHandle); + file.seek(0); + + auto ds = QDataStream(&file); + ds >> info; + } + + LogManager::init( + !info.noColor, + info.timestamp, + info.sparseLogsOnly, + info.defaultLogLevel, + info.logRules + ); + + auto crashDir = QsPaths::crashDir(info.instance.instanceId); + + qCInfo(logCrashReporter) << "Starting crash reporter..."; + + recordCrashInfo(crashDir, info.instance); + + auto gui = CrashReporterGui(crashDir.path(), crashProc); + gui.show(); + exit(QApplication::exec()); // NOLINT +} + int tryDup(int fd, const QString& path) { QFile sourceFile; if (!sourceFile.open(fd, QFile::ReadOnly, QFile::AutoCloseHandle)) { @@ -145,44 +184,3 @@ void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance) { qCDebug(logCrashReporter) << "Recorded crash information."; } - -} // namespace - -void qsCheckCrash(int argc, char** argv) { - auto fd = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_FD"); - if (fd.isEmpty()) return; - auto app = QApplication(argc, argv); - - RelaunchInfo info; - - auto crashProc = qEnvironmentVariable("__QUICKSHELL_CRASH_DUMP_PID").toInt(); - - { - auto infoFd = qEnvironmentVariable("__QUICKSHELL_CRASH_INFO_FD").toInt(); - - QFile file; - file.open(infoFd, QFile::ReadOnly, QFile::AutoCloseHandle); - file.seek(0); - - auto ds = QDataStream(&file); - ds >> info; - } - - LogManager::init( - !info.noColor, - info.timestamp, - info.sparseLogsOnly, - info.defaultLogLevel, - info.logRules - ); - - auto crashDir = QsPaths::crashDir(info.instance.instanceId); - - qCInfo(logCrashReporter) << "Starting crash reporter..."; - - recordCrashInfo(crashDir, info.instance); - - auto gui = CrashReporterGui(crashDir.path(), crashProc); - gui.show(); - exit(QApplication::exec()); // NOLINT -} diff --git a/src/dbus/bus.cpp b/src/dbus/bus.cpp index dc6d21bf..6f560e9e 100644 --- a/src/dbus/bus.cpp +++ b/src/dbus/bus.cpp @@ -14,9 +14,7 @@ namespace qs::dbus { -namespace { Q_LOGGING_CATEGORY(logDbus, "quickshell.dbus", QtWarningMsg); -} void tryLaunchService( QObject* parent, diff --git a/src/dbus/dbusmenu/dbusmenu.cpp b/src/dbus/dbusmenu/dbusmenu.cpp index 2b633b76..e86b580d 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 @@ -368,9 +368,11 @@ void DBusMenu::updateLayoutRecursive( auto childrenChanged = false; auto iter = item->mChildren.begin(); while (iter != item->mChildren.end()) { - auto existing = std::ranges::find_if(layout.children, [&](const DBusMenuLayout& layout) { - return layout.id == *iter; - }); + auto existing = std::find_if( + layout.children.begin(), + layout.children.end(), + [&](const DBusMenuLayout& layout) { return layout.id == *iter; } + ); if (!item->mShowChildren || existing == layout.children.end()) { qCDebug(logDbusMenu) << "Removing missing layout item" << this->items.value(*iter) << "from" 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/dbus/properties.cpp b/src/dbus/properties.cpp index 52f50060..a9ef2d27 100644 --- a/src/dbus/properties.cpp +++ b/src/dbus/properties.cpp @@ -190,9 +190,11 @@ void DBusPropertyGroup::updateAllViaGetAll() { void DBusPropertyGroup::updatePropertySet(const QVariantMap& properties, bool complainMissing) { for (const auto [name, value]: properties.asKeyValueRange()) { - auto prop = std::ranges::find_if(this->properties, [&name](DBusPropertyCore* prop) { - return prop->nameRef() == name; - }); + auto prop = std::find_if( + this->properties.begin(), + this->properties.end(), + [&name](DBusPropertyCore* prop) { return prop->nameRef() == name; } + ); if (prop == this->properties.end()) { qCDebug(logDbusProperties) << "Ignoring untracked property update" << name << "for" @@ -310,9 +312,11 @@ void DBusPropertyGroup::onPropertiesChanged( << "Received property change set and invalidations for" << this->toString(); for (const auto& name: invalidatedProperties) { - auto prop = std::ranges::find_if(this->properties, [&name](DBusPropertyCore* prop) { - return prop->nameRef() == name; - }); + auto prop = std::find_if( + this->properties.begin(), + this->properties.end(), + [&name](DBusPropertyCore* prop) { return prop->nameRef() == name; } + ); if (prop == this->properties.end()) { qCDebug(logDbusProperties) << "Ignoring untracked property invalidation" << name << "for" diff --git a/src/dbus/properties.hpp b/src/dbus/properties.hpp index 6feaa43d..846f70f2 100644 --- a/src/dbus/properties.hpp +++ b/src/dbus/properties.hpp @@ -36,8 +36,8 @@ template class DBusResult { public: explicit DBusResult() = default; - DBusResult(T value): value(std::move(value)) {} - DBusResult(QDBusError error): error(std::move(error)) {} + explicit DBusResult(T value): value(std::move(value)) {} + explicit DBusResult(QDBusError error): error(std::move(error)) {} explicit DBusResult(T value, QDBusError error) : value(std::move(value)) , error(std::move(error)) {} @@ -66,7 +66,7 @@ template void asyncReadProperty( QDBusAbstractInterface& interface, const QString& property, - const std::function& callback + std::function callback ) { asyncReadPropertyInternal( QMetaType::fromType(), diff --git a/src/debug/lint.cpp b/src/debug/lint.cpp index eb0450f8..f9727968 100644 --- a/src/debug/lint.cpp +++ b/src/debug/lint.cpp @@ -13,12 +13,10 @@ namespace qs::debug { -namespace { Q_LOGGING_CATEGORY(logLint, "quickshell.linter", QtWarningMsg); void lintZeroSized(QQuickItem* item); bool isRenderable(QQuickItem* item); -} // namespace void lintObjectTree(QObject* object) { if (!logLint().isWarningEnabled()) return; @@ -43,8 +41,6 @@ void lintItemTree(QQuickItem* item) { } } -namespace { - void lintZeroSized(QQuickItem* item) { if (!item->isEnabled() || !item->isVisible()) return; if (item->childItems().isEmpty()) return; @@ -75,6 +71,4 @@ bool isRenderable(QQuickItem* item) { return std::ranges::any_of(item->childItems(), [](auto* item) { return isRenderable(item); }); } -} // namespace - } // namespace qs::debug diff --git a/src/io/fileview.cpp b/src/io/fileview.cpp index 23656d34..29e6971b 100644 --- a/src/io/fileview.cpp +++ b/src/io/fileview.cpp @@ -24,9 +24,7 @@ namespace qs::io { -namespace { Q_LOGGING_CATEGORY(logFileView, "quickshell.io.fileview", QtWarningMsg); -} QString FileViewError::toString(FileViewError::Enum value) { switch (value) { 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..93801113 100644 --- a/src/launch/command.cpp +++ b/src/launch/command.cpp @@ -34,310 +34,16 @@ namespace qs::launch { using qs::ipc::IpcClient; -namespace { - -int locateConfigFile(CommandState& cmd, QString& path) { - if (!cmd.config.path->isEmpty()) { - path = *cmd.config.path; - } else { - auto manifestPath = *cmd.config.manifest; - if (manifestPath.isEmpty()) { - auto configDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); - auto path = configDir.filePath("manifest.conf"); - if (QFileInfo(path).isFile()) manifestPath = path; - } - - if (!manifestPath.isEmpty()) { - auto file = QFile(manifestPath); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - auto stream = QTextStream(&file); - while (!stream.atEnd()) { - auto line = stream.readLine(); - if (line.trimmed().startsWith("#")) continue; - if (line.trimmed().isEmpty()) continue; - - auto split = line.split('='); - if (split.length() != 2) { - qCritical() << "Manifest line not in expected format 'name = relativepath':" << line; - return -1; - } - - if (split[0].trimmed() == *cmd.config.name) { - path = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed()); - break; - } - } - - if (path.isEmpty()) { - qCCritical(logBare) << "Configuration" << *cmd.config.name - << "not found when searching manifest" << manifestPath; - return -1; - } - } else { - qCCritical(logBare) << "Could not open maifest at path" << *cmd.config.manifest; - return -1; - } - } else { - auto configDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); - - if (cmd.config.name->isEmpty()) { - path = configDir.path(); - } else { - path = configDir.filePath(*cmd.config.name); - } - } - } - - if (QFileInfo(path).isDir()) { - path = QDir(path).filePath("shell.qml"); - } - - if (!QFileInfo(path).isFile()) { - qCCritical(logBare) << "Could not open config file at" << path; - return -1; - } - - path = QFileInfo(path).canonicalFilePath(); - - 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; - }); -}; - -int selectInstance(CommandState& cmd, InstanceLockInfo* instance) { - auto* basePath = QsPaths::instance()->baseRunDir(); - if (!basePath) return -1; - - QString path; - - if (cmd.instance.pid != -1) { - path = QDir(basePath->filePath("by-pid")).filePath(QString::number(cmd.instance.pid)); - if (!QsPaths::checkLock(path, instance)) { - qCInfo(logBare) << "No instance found for pid" << cmd.instance.pid; - return -1; - } - } else if (!cmd.instance.id->isEmpty()) { - path = basePath->filePath("by-pid"); - auto instances = QsPaths::collectInstances(path); - - instances.removeIf([&](const InstanceLockInfo& info) { - return !info.instance.instanceId.startsWith(*cmd.instance.id); - }); - - if (instances.isEmpty()) { - qCInfo(logBare) << "No running instances start with" << *cmd.instance.id; - return -1; - } else if (instances.length() != 1) { - qCInfo(logBare) << "More than one instance starts with" << *cmd.instance.id; - - for (auto& instance: instances) { - qCInfo(logBare).noquote() << " -" << instance.instance.instanceId; - } - - return -1; - } else { - *instance = instances.value(0); - } - } else { - QString configFilePath; - auto r = locateConfigFile(cmd, configFilePath); - if (r != 0) return r; - - auto pathId = - QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex(); - - path = QDir(basePath->filePath("by-path")).filePath(pathId); - - auto instances = QsPaths::collectInstances(path); - sortInstances(instances, cmd.config.newest); - - if (instances.isEmpty()) { - qCInfo(logBare) << "No running instances for" << configFilePath; - return -1; - } - - *instance = instances.value(0); - } - - return 0; -} - -int readLogFile(CommandState& cmd) { - auto path = *cmd.log.file; - - if (path.isEmpty()) { - InstanceLockInfo instance; - auto r = selectInstance(cmd, &instance); - if (r != 0) return r; - - path = QDir(QsPaths::basePath(instance.instance.instanceId)).filePath("log.qslog"); - } - - auto file = QFile(path); - if (!file.open(QFile::ReadOnly)) { - qCCritical(logBare) << "Failed to open log file" << path; - return -1; - } - - return qs::log::readEncodedLogs( - &file, - path, - cmd.log.timestamp, - cmd.log.tail, - cmd.log.follow, - *cmd.log.readoutRules - ) - ? 0 - : -1; -} - -int listInstances(CommandState& cmd) { - auto* basePath = QsPaths::instance()->baseRunDir(); - if (!basePath) return -1; // NOLINT - - QString path; - QString configFilePath; - if (cmd.instance.all) { - path = basePath->filePath("by-pid"); - } else { - auto r = locateConfigFile(cmd, configFilePath); - - if (r != 0) { - qCInfo(logBare) << "Use --all to list all instances."; - return r; - } - - auto pathId = - QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex(); - - path = QDir(basePath->filePath("by-path")).filePath(pathId); - } - - auto instances = QsPaths::collectInstances(path); - - if (instances.isEmpty()) { - if (cmd.instance.all) { - qCInfo(logBare) << "No running instances."; - } else { - qCInfo(logBare) << "No running instances for" << configFilePath; - qCInfo(logBare) << "Use --all to list all instances."; - } - } else { - sortInstances(instances, cmd.config.newest); - - if (cmd.output.json) { - auto array = QJsonArray(); - - for (auto& instance: instances) { - auto json = QJsonObject(); - - json["id"] = instance.instance.instanceId; - json["pid"] = instance.pid; - json["shell_id"] = instance.instance.shellId; - json["config_path"] = instance.instance.configPath; - json["launch_time"] = instance.instance.launchTime.toString(Qt::ISODate); - - array.push_back(json); - } - - auto document = QJsonDocument(array); - QTextStream(stdout) << document.toJson(QJsonDocument::Indented); - } else { - for (auto& instance: instances) { - auto launchTimeStr = instance.instance.launchTime.toString("yyyy-MM-dd hh:mm:ss"); - - auto runSeconds = instance.instance.launchTime.secsTo(QDateTime::currentDateTime()); - auto remSeconds = runSeconds % 60; - auto runMinutes = (runSeconds - remSeconds) / 60; - auto remMinutes = runMinutes % 60; - auto runHours = (runMinutes - remMinutes) / 60; - auto runtimeStr = QString("%1 hours, %2 minutes, %3 seconds") - .arg(runHours) - .arg(remMinutes) - .arg(remSeconds); - - qCInfo(logBare).noquote().nospace() - << "Instance " << instance.instance.instanceId << ":\n" - << " Process ID: " << instance.pid << '\n' - << " Shell ID: " << instance.instance.shellId << '\n' - << " Config path: " << instance.instance.configPath << '\n' - << " Launch time: " << launchTimeStr << " (running for " << runtimeStr << ")\n"; - } - } - } - - return 0; -} - -int killInstances(CommandState& cmd) { - InstanceLockInfo instance; - auto r = selectInstance(cmd, &instance); - if (r != 0) return r; - - return IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) { - client.kill(); - qCInfo(logBare).noquote() << "Killed" << instance.instance.instanceId; - }); -} - -int ipcCommand(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); - } 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 -1; - }); -} - -int launchFromCommand(CommandState& cmd, QCoreApplication* coreApplication) { - QString configPath; - - auto r = locateConfigFile(cmd, configPath); - if (r != 0) return r; - - { - InstanceLockInfo info; - if (cmd.misc.noDuplicate && selectInstance(cmd, &info) == 0) { - qCInfo(logBare) << "An instance of this configuration is already running."; - return 0; - } - } - - return launch( - { - .configPath = configPath, - .debugPort = cmd.debug.port, - .waitForDebug = cmd.debug.wait, - }, - cmd.exec.argv, - coreApplication - ); -} - -} // namespace +int readLogFile(CommandState& cmd); +int listInstances(CommandState& cmd); +int killInstances(CommandState& cmd); +int msgInstance(CommandState& cmd); +int launchFromCommand(CommandState& cmd, QCoreApplication* coreApplication); +int locateConfigFile(CommandState& cmd, QString& path); int runCommand(int argc, char** argv, QCoreApplication* coreApplication) { auto state = CommandState(); - if (auto ret = parseCommand(argc, argv, state); ret != 65535) return ret; + if (auto ret = parseCommand(argc, argv, state); ret != 0) return ret; if (state.misc.checkCompat) { if (strcmp(qVersion(), QT_VERSION_STR) != 0) { @@ -420,8 +126,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 @@ -436,4 +142,306 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) { return 0; } +int locateConfigFile(CommandState& cmd, QString& path) { + if (!cmd.config.path->isEmpty()) { + path = *cmd.config.path; + } else { + auto manifestPath = *cmd.config.manifest; + if (manifestPath.isEmpty()) { + auto configDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); + auto path = configDir.filePath("manifest.conf"); + if (QFileInfo(path).isFile()) manifestPath = path; + } + + if (!manifestPath.isEmpty()) { + auto file = QFile(manifestPath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + auto stream = QTextStream(&file); + while (!stream.atEnd()) { + auto line = stream.readLine(); + if (line.trimmed().startsWith("#")) continue; + if (line.trimmed().isEmpty()) continue; + + auto split = line.split('='); + if (split.length() != 2) { + qCritical() << "Manifest line not in expected format 'name = relativepath':" << line; + return -1; + } + + if (split[0].trimmed() == *cmd.config.name) { + path = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed()); + break; + } + } + + if (path.isEmpty()) { + qCCritical(logBare) << "Configuration" << *cmd.config.name + << "not found when searching manifest" << manifestPath; + return -1; + } + } else { + qCCritical(logBare) << "Could not open maifest at path" << *cmd.config.manifest; + return -1; + } + } else { + auto configDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); + + if (cmd.config.name->isEmpty()) { + path = configDir.path(); + } else { + path = configDir.filePath(*cmd.config.name); + } + } + } + + if (QFileInfo(path).isDir()) { + path = QDir(path).filePath("shell.qml"); + } + + if (!QFileInfo(path).isFile()) { + qCCritical(logBare) << "Could not open config file at" << path; + return -1; + } + + path = QFileInfo(path).canonicalFilePath(); + + return 0; +} + +void sortInstances(QVector& list) { + std::sort(list.begin(), list.end(), [](const InstanceLockInfo& a, const InstanceLockInfo& b) { + return a.instance.launchTime < b.instance.launchTime; + }); +}; + +int selectInstance(CommandState& cmd, InstanceLockInfo* instance) { + auto* basePath = QsPaths::instance()->baseRunDir(); + if (!basePath) return -1; + + QString path; + + if (cmd.instance.pid != -1) { + path = QDir(basePath->filePath("by-pid")).filePath(QString::number(cmd.instance.pid)); + if (!QsPaths::checkLock(path, instance)) { + qCInfo(logBare) << "No instance found for pid" << cmd.instance.pid; + return -1; + } + } else if (!cmd.instance.id->isEmpty()) { + path = basePath->filePath("by-pid"); + auto instances = QsPaths::collectInstances(path); + + auto itr = + std::remove_if(instances.begin(), instances.end(), [&](const InstanceLockInfo& info) { + return !info.instance.instanceId.startsWith(*cmd.instance.id); + }); + + instances.erase(itr, instances.end()); + + if (instances.isEmpty()) { + qCInfo(logBare) << "No running instances start with" << *cmd.instance.id; + return -1; + } else if (instances.length() != 1) { + qCInfo(logBare) << "More than one instance starts with" << *cmd.instance.id; + + for (auto& instance: instances) { + qCInfo(logBare).noquote() << " -" << instance.instance.instanceId; + } + + return -1; + } else { + *instance = instances.value(0); + } + } else { + QString configFilePath; + auto r = locateConfigFile(cmd, configFilePath); + if (r != 0) return r; + + auto pathId = + QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex(); + + path = QDir(basePath->filePath("by-path")).filePath(pathId); + + auto instances = QsPaths::collectInstances(path); + sortInstances(instances); + + if (instances.isEmpty()) { + qCInfo(logBare) << "No running instances for" << configFilePath; + return -1; + } + + *instance = instances.value(0); + } + + return 0; +} + +int readLogFile(CommandState& cmd) { + auto path = *cmd.log.file; + + if (path.isEmpty()) { + InstanceLockInfo instance; + auto r = selectInstance(cmd, &instance); + if (r != 0) return r; + + path = QDir(QsPaths::basePath(instance.instance.instanceId)).filePath("log.qslog"); + } + + auto file = QFile(path); + if (!file.open(QFile::ReadOnly)) { + qCCritical(logBare) << "Failed to open log file" << path; + return -1; + } + + return qs::log::readEncodedLogs( + &file, + path, + cmd.log.timestamp, + cmd.log.tail, + cmd.log.follow, + *cmd.log.readoutRules + ) + ? 0 + : -1; +} + +int listInstances(CommandState& cmd) { + auto* basePath = QsPaths::instance()->baseRunDir(); + if (!basePath) return -1; // NOLINT + + QString path; + QString configFilePath; + if (cmd.instance.all) { + path = basePath->filePath("by-pid"); + } else { + auto r = locateConfigFile(cmd, configFilePath); + + if (r != 0) { + qCInfo(logBare) << "Use --all to list all instances."; + return r; + } + + auto pathId = + QCryptographicHash::hash(configFilePath.toUtf8(), QCryptographicHash::Md5).toHex(); + + path = QDir(basePath->filePath("by-path")).filePath(pathId); + } + + auto instances = QsPaths::collectInstances(path); + + if (instances.isEmpty()) { + if (cmd.instance.all) { + qCInfo(logBare) << "No running instances."; + } else { + qCInfo(logBare) << "No running instances for" << configFilePath; + qCInfo(logBare) << "Use --all to list all instances."; + } + } else { + sortInstances(instances); + + if (cmd.output.json) { + auto array = QJsonArray(); + + for (auto& instance: instances) { + auto json = QJsonObject(); + + json["id"] = instance.instance.instanceId; + json["pid"] = instance.pid; + json["shell_id"] = instance.instance.shellId; + json["config_path"] = instance.instance.configPath; + json["launch_time"] = instance.instance.launchTime.toString(Qt::ISODate); + + array.push_back(json); + } + + auto document = QJsonDocument(array); + QTextStream(stdout) << document.toJson(QJsonDocument::Indented); + } else { + for (auto& instance: instances) { + auto launchTimeStr = instance.instance.launchTime.toString("yyyy-MM-dd hh:mm:ss"); + + auto runSeconds = instance.instance.launchTime.secsTo(QDateTime::currentDateTime()); + auto remSeconds = runSeconds % 60; + auto runMinutes = (runSeconds - remSeconds) / 60; + auto remMinutes = runMinutes % 60; + auto runHours = (runMinutes - remMinutes) / 60; + auto runtimeStr = QString("%1 hours, %2 minutes, %3 seconds") + .arg(runHours) + .arg(remMinutes) + .arg(remSeconds); + + qCInfo(logBare).noquote().nospace() + << "Instance " << instance.instance.instanceId << ":\n" + << " Process ID: " << instance.pid << '\n' + << " Shell ID: " << instance.instance.shellId << '\n' + << " Config path: " << instance.instance.configPath << '\n' + << " Launch time: " << launchTimeStr << " (running for " << runtimeStr << ")\n"; + } + } + } + + return 0; +} + +int killInstances(CommandState& cmd) { + InstanceLockInfo instance; + auto r = selectInstance(cmd, &instance); + if (r != 0) return r; + + return IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) { + client.kill(); + qCInfo(logBare).noquote() << "Killed" << instance.instance.instanceId; + }); +} + +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.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.function, + arguments + ); + } + + return -1; + }); +} + +int launchFromCommand(CommandState& cmd, QCoreApplication* coreApplication) { + QString configPath; + + auto r = locateConfigFile(cmd, configPath); + if (r != 0) return r; + + { + InstanceLockInfo info; + if (cmd.misc.noDuplicate && selectInstance(cmd, &info) == 0) { + qCInfo(logBare) << "An instance of this configuration is already running."; + return 0; + } + } + + return launch( + { + .configPath = configPath, + .debugPort = cmd.debug.port, + .waitForDebug = cmd.debug.wait, + }, + cmd.exec.argv, + coreApplication + ); +} + } // namespace qs::launch diff --git a/src/launch/launch.cpp b/src/launch/launch.cpp index 848da499..ec5863e9 100644 --- a/src/launch/launch.cpp +++ b/src/launch/launch.cpp @@ -32,8 +32,6 @@ namespace qs::launch { -namespace { - template QString base36Encode(T number) { const QString digits = "0123456789abcdefghijklmnopqrstuvwxyz"; @@ -54,8 +52,6 @@ QString base36Encode(T number) { return result; } -} // namespace - int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplication) { auto pathId = QCryptographicHash::hash(args.configPath.toUtf8(), QCryptographicHash::Md5).toHex(); auto shellId = QString(pathId); 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/main.cpp b/src/launch/main.cpp index 2bcbebd3..3a2b5822 100644 --- a/src/launch/main.cpp +++ b/src/launch/main.cpp @@ -22,7 +22,36 @@ namespace qs::launch { -namespace { +void checkCrashRelaunch(char** argv, QCoreApplication* coreApplication); + +int DAEMON_PIPE = -1; // NOLINT + +void exitDaemon(int code) { + if (DAEMON_PIPE == -1) return; + + if (write(DAEMON_PIPE, &code, sizeof(int)) == -1) { + qCritical().nospace() << "Failed to write daemon exit command with error code " << errno << ": " + << qt_error_string(); + } + + close(DAEMON_PIPE); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + if (open("/dev/null", O_RDONLY) != STDIN_FILENO) { // NOLINT + qFatal() << "Failed to open /dev/null on stdin"; + } + + if (open("/dev/null", O_WRONLY) != STDOUT_FILENO) { // NOLINT + qFatal() << "Failed to open /dev/null on stdout"; + } + + if (open("/dev/null", O_WRONLY) != STDERR_FILENO) { // NOLINT + qFatal() << "Failed to open /dev/null on stderr"; + } +} void checkCrashRelaunch(char** argv, QCoreApplication* coreApplication) { #if CRASH_REPORTER @@ -67,37 +96,6 @@ void checkCrashRelaunch(char** argv, QCoreApplication* coreApplication) { #endif } -} // namespace - -int DAEMON_PIPE = -1; // NOLINT - -void exitDaemon(int code) { - if (DAEMON_PIPE == -1) return; - - if (write(DAEMON_PIPE, &code, sizeof(int)) == -1) { - qCritical().nospace() << "Failed to write daemon exit command with error code " << errno << ": " - << qt_error_string(); - } - - close(DAEMON_PIPE); - - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - - if (open("/dev/null", O_RDONLY) != STDIN_FILENO) { // NOLINT - qFatal() << "Failed to open /dev/null on stdin"; - } - - if (open("/dev/null", O_WRONLY) != STDOUT_FILENO) { // NOLINT - qFatal() << "Failed to open /dev/null on stdout"; - } - - if (open("/dev/null", O_WRONLY) != STDERR_FILENO) { // NOLINT - qFatal() << "Failed to open /dev/null on stderr"; - } -} - int main(int argc, char** argv) { QCoreApplication::setApplicationName("quickshell"); diff --git a/src/launch/parsecommand.cpp b/src/launch/parsecommand.cpp index 1edbf01e..91f7dc04 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,78 +156,43 @@ 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; } CLI11_PARSE(*cli, argc, argv); - return 65535; + return 0; } } // namespace qs::launch diff --git a/src/services/greetd/connection.cpp b/src/services/greetd/connection.cpp index ecfd9a59..822df5fb 100644 --- a/src/services/greetd/connection.cpp +++ b/src/services/greetd/connection.cpp @@ -15,9 +15,7 @@ #include "../../core/generation.hpp" -namespace { Q_LOGGING_CATEGORY(logGreetd, "quickshell.service.greetd"); -} QString GreetdState::toString(GreetdState::Enum value) { switch (value) { diff --git a/src/services/mpris/player.cpp b/src/services/mpris/player.cpp index 4aac2826..642ca3bd 100644 --- a/src/services/mpris/player.cpp +++ b/src/services/mpris/player.cpp @@ -21,9 +21,7 @@ using namespace qs::dbus; namespace qs::service::mpris { -namespace { Q_LOGGING_CATEGORY(logMprisPlayer, "quickshell.service.mp.player", QtWarningMsg); -} QString MprisPlaybackState::toString(MprisPlaybackState::Enum status) { switch (status) { @@ -239,7 +237,7 @@ void MprisPlayer::setPosition(qreal position) { this->player->Seek(target - pos); } - this->setPosition(target); + this->bpPosition = target; } void MprisPlayer::onPositionUpdated() { @@ -250,16 +248,11 @@ void MprisPlayer::onPositionUpdated() { if (firstChange) emit this->positionSupportedChanged(); } -void MprisPlayer::setPosition(qlonglong position) { - this->bpPosition = position; - this->onPositionUpdated(); -} - void MprisPlayer::onExportedPositionChanged() { if (!this->lengthSupported()) emit this->lengthChanged(); } -void MprisPlayer::onSeek(qlonglong time) { this->setPosition(time); } +void MprisPlayer::onSeek(qlonglong time) { this->bpPosition = time; } qreal MprisPlayer::length() const { if (this->bInternalLength == -1) { diff --git a/src/services/mpris/player.hpp b/src/services/mpris/player.hpp index 2eda305d..7637c10a 100644 --- a/src/services/mpris/player.hpp +++ b/src/services/mpris/player.hpp @@ -391,8 +391,6 @@ private slots: private: void onMetadataChanged(); void onPositionUpdated(); - // call instead of setting bpPosition - void setPosition(qlonglong position); void requestPositionUpdate() { this->pPosition.requestUpdate(); }; // clang-format off @@ -459,7 +457,7 @@ private: QS_DBUS_PROPERTY_BINDING(MprisPlayer, pCanSeek, bpCanSeek, playerProperties, "CanSeek"); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pCanGoNext, bpCanGoNext, playerProperties, "CanGoNext"); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pCanGoPrevious, bpCanGoPrevious, playerProperties, "CanGoPrevious"); - QS_DBUS_PROPERTY_BINDING(MprisPlayer, qlonglong, pPosition, bpPosition, onPositionUpdated, playerProperties, "Position", false); + QS_DBUS_PROPERTY_BINDING(MprisPlayer, qlonglong, pPosition, bpPosition, playerProperties, "Position", false); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pVolume, bVolume, playerProperties, "Volume", false); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pMetadata, bpMetadata, playerProperties, "Metadata"); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pPlaybackStatus, bpPlaybackStatus, playerProperties, "PlaybackStatus"); @@ -468,6 +466,8 @@ private: QS_DBUS_PROPERTY_BINDING(MprisPlayer, pMinRate, bMinRate, playerProperties, "MinimumRate", false); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pMaxRate, bMaxRate, playerProperties, "MaximumRate", false); QS_DBUS_PROPERTY_BINDING(MprisPlayer, pShuffle, bShuffle, playerProperties, "Shuffle", false); + + QS_BINDING_SUBSCRIBE_METHOD(MprisPlayer, bpPosition, onPositionUpdated, onValueChanged); // clang-format on QDateTime lastPositionTimestamp; diff --git a/src/services/mpris/watcher.cpp b/src/services/mpris/watcher.cpp index 94619074..8a788933 100644 --- a/src/services/mpris/watcher.cpp +++ b/src/services/mpris/watcher.cpp @@ -14,9 +14,7 @@ namespace qs::service::mpris { -namespace { Q_LOGGING_CATEGORY(logMprisWatcher, "quickshell.service.mpris.watcher", QtWarningMsg); -} MprisWatcher::MprisWatcher() { qCDebug(logMprisWatcher) << "Starting MprisWatcher"; diff --git a/src/services/notifications/dbusimage.cpp b/src/services/notifications/dbusimage.cpp index 9c10e222..46a72a78 100644 --- a/src/services/notifications/dbusimage.cpp +++ b/src/services/notifications/dbusimage.cpp @@ -9,7 +9,6 @@ namespace qs::service::notifications { -// NOLINTNEXTLINE(misc-use-internal-linkage) Q_DECLARE_LOGGING_CATEGORY(logNotifications); // server.cpp QImage DBusNotificationImage::createImage() const { 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..b1bbdf3a 100644 --- a/src/services/notifications/notification.cpp +++ b/src/services/notifications/notification.cpp @@ -1,4 +1,5 @@ #include "notification.hpp" +#include #include #include @@ -17,7 +18,6 @@ namespace qs::service::notifications { -// NOLINTNEXTLINE(misc-use-internal-linkage) Q_DECLARE_LOGGING_CATEGORY(logNotifications); // server.cpp QString NotificationUrgency::toString(NotificationUrgency::Enum value) { @@ -116,12 +116,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 +130,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/notifications/server.cpp b/src/services/notifications/server.cpp index 0c41fb84..9a866f7c 100644 --- a/src/services/notifications/server.cpp +++ b/src/services/notifications/server.cpp @@ -19,7 +19,6 @@ namespace qs::service::notifications { -// NOLINTNEXTLINE(misc-use-internal-linkage) Q_LOGGING_CATEGORY(logNotifications, "quickshell.service.notifications"); NotificationServer::NotificationServer() { diff --git a/src/services/pipewire/core.cpp b/src/services/pipewire/core.cpp index 9c2a3dbf..c4b31ab5 100644 --- a/src/services/pipewire/core.cpp +++ b/src/services/pipewire/core.cpp @@ -10,28 +10,12 @@ #include #include #include -#include #include #include namespace qs::service::pipewire { -namespace { Q_LOGGING_CATEGORY(logLoop, "quickshell.service.pipewire.loop", QtWarningMsg); -} - -const pw_core_events PwCore::EVENTS = { - .version = PW_VERSION_CORE_EVENTS, - .info = nullptr, - .done = &PwCore::onSync, - .ping = nullptr, - .error = nullptr, - .remove_id = nullptr, - .bound_id = nullptr, - .add_mem = nullptr, - .remove_mem = nullptr, - .bound_props = nullptr, -}; PwCore::PwCore(QObject* parent): QObject(parent), notifier(QSocketNotifier::Read) { qCInfo(logLoop) << "Creating pipewire event loop."; @@ -56,8 +40,6 @@ PwCore::PwCore(QObject* parent): QObject(parent), notifier(QSocketNotifier::Read return; } - pw_core_add_listener(this->core, &this->listener.hook, &PwCore::EVENTS, this); - qCInfo(logLoop) << "Linking pipewire event loop."; // Tie the pw event loop into qt. auto fd = pw_loop_get_fd(this->loop); @@ -95,16 +77,6 @@ void PwCore::poll() { emit this->polled(); } -qint32 PwCore::sync(quint32 id) const { - // Seq param doesn't seem to do anything. Seq is instead the returned value. - return pw_core_sync(this->core, id, 0); -} - -void PwCore::onSync(void* data, quint32 id, qint32 seq) { - auto* self = static_cast(data); - emit self->synced(id, seq); -} - SpaHook::SpaHook() { // NOLINT spa_zero(this->hook); } diff --git a/src/services/pipewire/core.hpp b/src/services/pipewire/core.hpp index 262e2d31..49728eeb 100644 --- a/src/services/pipewire/core.hpp +++ b/src/services/pipewire/core.hpp @@ -14,14 +14,6 @@ namespace qs::service::pipewire { -class SpaHook { -public: - explicit SpaHook(); - - void remove(); - spa_hook hook; -}; - class PwCore: public QObject { Q_OBJECT; @@ -31,7 +23,6 @@ public: Q_DISABLE_COPY_MOVE(PwCore); [[nodiscard]] bool isValid() const; - [[nodiscard]] qint32 sync(quint32 id) const; pw_loop* loop = nullptr; pw_context* context = nullptr; @@ -39,18 +30,12 @@ public: signals: void polled(); - void synced(quint32 id, qint32 seq); private slots: void poll(); private: - static const pw_core_events EVENTS; - - static void onSync(void* data, quint32 id, qint32 seq); - QSocketNotifier notifier; - SpaHook listener; }; template @@ -64,4 +49,12 @@ public: T* object; }; +class SpaHook { +public: + explicit SpaHook(); + + void remove(); + spa_hook hook; +}; + } // namespace qs::service::pipewire diff --git a/src/services/pipewire/defaults.cpp b/src/services/pipewire/defaults.cpp index 0333c87f..cd018f9f 100644 --- a/src/services/pipewire/defaults.cpp +++ b/src/services/pipewire/defaults.cpp @@ -18,9 +18,7 @@ namespace qs::service::pipewire { -namespace { Q_LOGGING_CATEGORY(logDefaults, "quickshell.service.pipewire.defaults", QtWarningMsg); -} PwDefaultTracker::PwDefaultTracker(PwRegistry* registry): registry(registry) { QObject::connect(registry, &PwRegistry::metadataAdded, this, &PwDefaultTracker::onMetadataAdded); diff --git a/src/services/pipewire/device.cpp b/src/services/pipewire/device.cpp index 0a1e1b6a..06ed102a 100644 --- a/src/services/pipewire/device.cpp +++ b/src/services/pipewire/device.cpp @@ -23,9 +23,7 @@ namespace qs::service::pipewire { -namespace { Q_LOGGING_CATEGORY(logDevice, "quickshell.service.pipewire.device", QtWarningMsg); -} // https://github.com/PipeWire/wireplumber/blob/895c1c7286e8809fad869059179e53ab39c807e9/modules/module-mixer-api.c#L397 // https://github.com/PipeWire/pipewire/blob/48c2e9516585ccc791335bc7baf4af6952ec54a0/src/modules/module-protocol-pulse/pulse-server.c#L2743-L2743 diff --git a/src/services/pipewire/link.cpp b/src/services/pipewire/link.cpp index c6421af6..8370446b 100644 --- a/src/services/pipewire/link.cpp +++ b/src/services/pipewire/link.cpp @@ -14,9 +14,7 @@ namespace qs::service::pipewire { -namespace { Q_LOGGING_CATEGORY(logLink, "quickshell.service.pipewire.link", QtWarningMsg); -} QString PwLinkState::toString(Enum value) { return QString(pw_link_state_as_string(static_cast(value))); diff --git a/src/services/pipewire/metadata.cpp b/src/services/pipewire/metadata.cpp index ea79611a..930725c3 100644 --- a/src/services/pipewire/metadata.cpp +++ b/src/services/pipewire/metadata.cpp @@ -15,9 +15,7 @@ namespace qs::service::pipewire { -namespace { Q_LOGGING_CATEGORY(logMeta, "quickshell.service.pipewire.metadata", QtWarningMsg); -} void PwMetadata::bindHooks() { pw_metadata_add_listener(this->proxy(), &this->listener.hook, &PwMetadata::EVENTS, this); diff --git a/src/services/pipewire/node.cpp b/src/services/pipewire/node.cpp index 21815b82..b89bbff0 100644 --- a/src/services/pipewire/node.cpp +++ b/src/services/pipewire/node.cpp @@ -24,15 +24,11 @@ #include #include -#include "connection.hpp" -#include "core.hpp" #include "device.hpp" namespace qs::service::pipewire { -namespace { Q_LOGGING_CATEGORY(logNode, "quickshell.service.pipewire.node", QtWarningMsg); -} QString PwAudioChannel::toString(Enum value) { switch (value) { @@ -94,12 +90,6 @@ void PwNode::bindHooks() { } void PwNode::unbindHooks() { - if (this->ready) { - this->ready = false; - emit this->readyChanged(); - } - - this->syncSeq = 0; this->listener.remove(); this->routeDevice = -1; this->properties.clear(); @@ -209,20 +199,6 @@ void PwNode::onInfo(void* data, const pw_node_info* info) { if (self->boundData != nullptr) { self->boundData->onInfo(info); } - - if (!self->ready && !self->syncSeq) { - auto* core = PwConnection::instance()->registry.core; - QObject::connect(core, &PwCore::synced, self, &PwNode::onCoreSync); - self->syncSeq = core->sync(self->id); - } -} - -void PwNode::onCoreSync(quint32 id, qint32 seq) { - if (id != this->id || seq != this->syncSeq) return; - qCInfo(logNode) << "Completed initial sync for" << this; - this->ready = true; - this->syncSeq = 0; - emit this->readyChanged(); } void PwNode::onParam( diff --git a/src/services/pipewire/node.hpp b/src/services/pipewire/node.hpp index a18abccf..697a54e0 100644 --- a/src/services/pipewire/node.hpp +++ b/src/services/pipewire/node.hpp @@ -172,7 +172,6 @@ public: PwNodeType type = PwNodeType::Untracked; bool isSink = false; bool isStream = false; - bool ready = false; PwNodeBoundData* boundData = nullptr; @@ -181,10 +180,6 @@ public: signals: void propertiesChanged(); - void readyChanged(); - -private slots: - void onCoreSync(quint32 id, qint32 seq); private: static const pw_node_events EVENTS; @@ -192,7 +187,6 @@ private: static void onParam(void* data, qint32 seq, quint32 id, quint32 index, quint32 next, const spa_pod* param); - qint32 syncSeq = 0; SpaHook listener; }; diff --git a/src/services/pipewire/qml.cpp b/src/services/pipewire/qml.cpp index 6eef238b..be50ec6e 100644 --- a/src/services/pipewire/qml.cpp +++ b/src/services/pipewire/qml.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -88,16 +87,6 @@ Pipewire::Pipewire(QObject* parent): QObject(parent) { this, &Pipewire::defaultConfiguredAudioSourceChanged ); - - if (!connection->registry.isInitialized()) { - QObject::connect( - &connection->registry, - &PwRegistry::initialized, - this, - &Pipewire::readyChanged, - Qt::SingleShotConnection - ); - } } ObjectModel* Pipewire::nodes() { return &this->mNodes; } @@ -167,8 +156,6 @@ void Pipewire::setDefaultConfiguredAudioSource(PwNodeIface* node) { PwConnection::instance()->defaults.changeConfiguredSource(node ? node->node() : nullptr); } -bool Pipewire::isReady() { return PwConnection::instance()->registry.isInitialized(); } - PwNodeIface* PwNodeLinkTracker::node() const { return this->mNode; } void PwNodeLinkTracker::setNode(PwNodeIface* node) { @@ -311,7 +298,6 @@ void PwNodeAudioIface::setVolumes(const QVector& volumes) { PwNodeIface::PwNodeIface(PwNode* node): PwObjectIface(node), mNode(node) { QObject::connect(node, &PwNode::propertiesChanged, this, &PwNodeIface::propertiesChanged); - QObject::connect(node, &PwNode::readyChanged, this, &PwNodeIface::readyChanged); if (auto* audioBoundData = dynamic_cast(node->boundData)) { this->audioIface = new PwNodeAudioIface(audioBoundData, this); @@ -332,8 +318,6 @@ bool PwNodeIface::isSink() const { return this->mNode->isSink; } bool PwNodeIface::isStream() const { return this->mNode->isStream; } -bool PwNodeIface::isReady() const { return this->mNode->ready; } - QVariantMap PwNodeIface::properties() const { auto map = QVariantMap(); for (auto [k, v]: this->mNode->properties.asKeyValueRange()) { diff --git a/src/services/pipewire/qml.hpp b/src/services/pipewire/qml.hpp index 6313a42b..675b923b 100644 --- a/src/services/pipewire/qml.hpp +++ b/src/services/pipewire/qml.hpp @@ -116,13 +116,6 @@ class Pipewire: public QObject { /// /// See @@defaultAudioSource for the current default source, regardless of preference. Q_PROPERTY(qs::service::pipewire::PwNodeIface* preferredDefaultAudioSource READ defaultConfiguredAudioSource WRITE setDefaultConfiguredAudioSource NOTIFY defaultConfiguredAudioSourceChanged); - /// This property is true if quickshell has completed its initial sync with - /// the pipewire server. If true, nodes, links and sync/source preferences will be - /// in a good state. - /// - /// > [!NOTE] You can use the pipewire object before it is ready, but some nodes/links - /// > may be missing, and preference metadata may be null. - Q_PROPERTY(bool ready READ isReady NOTIFY readyChanged); // clang-format on QML_ELEMENT; QML_SINGLETON; @@ -143,8 +136,6 @@ public: [[nodiscard]] PwNodeIface* defaultConfiguredAudioSource() const; static void setDefaultConfiguredAudioSource(PwNodeIface* node); - [[nodiscard]] static bool isReady(); - signals: void defaultAudioSinkChanged(); void defaultAudioSourceChanged(); @@ -152,8 +143,6 @@ signals: void defaultConfiguredAudioSinkChanged(); void defaultConfiguredAudioSourceChanged(); - void readyChanged(); - private slots: void onNodeAdded(PwNode* node); void onNodeRemoved(QObject* object); @@ -305,11 +294,6 @@ class PwNodeIface: public PwObjectIface { /// The presence or absence of this property can be used to determine if a node /// manages audio, regardless of if it is bound. If non null, the node is an audio node. Q_PROPERTY(qs::service::pipewire::PwNodeAudioIface* audio READ audio CONSTANT); - /// True if the node is fully bound and ready to use. - /// - /// > [!NOTE] The node may be used before it is fully bound, but some data - /// > may be missing or incorrect. - Q_PROPERTY(bool ready READ isReady NOTIFY readyChanged); QML_NAMED_ELEMENT(PwNode); QML_UNCREATABLE("PwNodes cannot be created directly"); @@ -323,7 +307,6 @@ public: [[nodiscard]] QString nickname() const; [[nodiscard]] bool isSink() const; [[nodiscard]] bool isStream() const; - [[nodiscard]] bool isReady() const; [[nodiscard]] QVariantMap properties() const; [[nodiscard]] PwNodeAudioIface* audio() const; @@ -331,7 +314,6 @@ public: signals: void propertiesChanged(); - void readyChanged(); private: PwNode* mNode; diff --git a/src/services/pipewire/registry.cpp b/src/services/pipewire/registry.cpp index d2967d03..04bd9ace 100644 --- a/src/services/pipewire/registry.cpp +++ b/src/services/pipewire/registry.cpp @@ -126,29 +126,6 @@ void PwRegistry::init(PwCore& core) { this->core = &core; this->object = pw_core_get_registry(core.core, PW_VERSION_REGISTRY, 0); pw_registry_add_listener(this->object, &this->listener.hook, &PwRegistry::EVENTS, this); - - QObject::connect(this->core, &PwCore::synced, this, &PwRegistry::onCoreSync); - - qCDebug(logRegistry) << "Registry created. Sending core sync for initial object tracking."; - this->coreSyncSeq = this->core->sync(PW_ID_CORE); -} - -void PwRegistry::onCoreSync(quint32 id, qint32 seq) { - if (id != PW_ID_CORE || seq != this->coreSyncSeq) return; - - switch (this->initState) { - case InitState::SendingObjects: - qCDebug(logRegistry) << "Initial sync for objects received. Syncing for metadata binding."; - this->coreSyncSeq = this->core->sync(PW_ID_CORE); - this->initState = InitState::Binding; - break; - case InitState::Binding: - qCInfo(logRegistry) << "Initial state sync complete."; - this->initState = InitState::Done; - emit this->initialized(); - break; - default: break; - } } const pw_registry_events PwRegistry::EVENTS = { diff --git a/src/services/pipewire/registry.hpp b/src/services/pipewire/registry.hpp index f1ba9610..be282a20 100644 --- a/src/services/pipewire/registry.hpp +++ b/src/services/pipewire/registry.hpp @@ -116,8 +116,6 @@ class PwRegistry public: void init(PwCore& core); - [[nodiscard]] bool isInitialized() const { return this->initState == InitState::Done; } - //QHash clients; QHash metadata; QHash nodes; @@ -134,11 +132,9 @@ signals: void linkAdded(PwLink* link); void linkGroupAdded(PwLinkGroup* group); void metadataAdded(PwMetadata* metadata); - void initialized(); private slots: void onLinkGroupDestroyed(QObject* object); - void onCoreSync(quint32 id, qint32 seq); private: static const pw_registry_events EVENTS; @@ -156,13 +152,6 @@ private: void addLinkToGroup(PwLink* link); - enum class InitState : quint8 { - SendingObjects, - Binding, - Done - } initState = InitState::SendingObjects; - - qint32 coreSyncSeq = 0; SpaHook listener; }; diff --git a/src/services/status_notifier/item.cpp b/src/services/status_notifier/item.cpp index 42847399..347edbbd 100644 --- a/src/services/status_notifier/item.cpp +++ b/src/services/status_notifier/item.cpp @@ -35,6 +35,7 @@ using namespace qs::dbus::dbusmenu; using namespace qs::menu::platform; Q_LOGGING_CATEGORY(logStatusNotifierItem, "quickshell.service.sni.item", QtWarningMsg); +Q_LOGGING_CATEGORY(logSniMenu, "quickshell.service.sni.item.menu", QtWarningMsg); namespace qs::service::sni { @@ -282,7 +283,7 @@ void StatusNotifierItem::onGetAllFailed() const { } TrayImageHandle::TrayImageHandle(StatusNotifierItem* item) - : QsImageHandle(QQmlImageProviderBase::Pixmap) + : QsImageHandle(QQmlImageProviderBase::Pixmap, item) , item(item) {} QPixmap diff --git a/src/services/upower/CMakeLists.txt b/src/services/upower/CMakeLists.txt index b18ec155..ca87f6ae 100644 --- a/src/services/upower/CMakeLists.txt +++ b/src/services/upower/CMakeLists.txt @@ -21,7 +21,6 @@ qt_add_dbus_interface(DBUS_INTERFACES qt_add_library(quickshell-service-upower STATIC core.cpp device.cpp - powerprofiles.cpp ${DBUS_INTERFACES} ) diff --git a/src/services/upower/core.cpp b/src/services/upower/core.cpp index 9fe0e60c..750da5c9 100644 --- a/src/services/upower/core.cpp +++ b/src/services/upower/core.cpp @@ -20,9 +20,7 @@ namespace qs::service::upower { -namespace { Q_LOGGING_CATEGORY(logUPower, "quickshell.service.upower", QtWarningMsg); -} UPower::UPower() { qCDebug(logUPower) << "Starting UPower Service"; diff --git a/src/services/upower/core.hpp b/src/services/upower/core.hpp index c3878150..9ade8121 100644 --- a/src/services/upower/core.hpp +++ b/src/services/upower/core.hpp @@ -54,14 +54,6 @@ private: DBusUPowerService* service = nullptr; }; -///! Provides access to the UPower service. -/// An interface to the [UPower daemon], which can be used to -/// view battery and power statistics for your computer and -/// connected devices. -/// -/// > [!NOTE] The UPower daemon must be installed to use this service. -/// -/// [UPower daemon]: https://upower.freedesktop.org class UPowerQml: public QObject { Q_OBJECT; QML_NAMED_ELEMENT(UPower); diff --git a/src/services/upower/device.cpp b/src/services/upower/device.cpp index b7c61e12..d9c6268d 100644 --- a/src/services/upower/device.cpp +++ b/src/services/upower/device.cpp @@ -15,9 +15,7 @@ using namespace qs::dbus; namespace qs::service::upower { -namespace { Q_LOGGING_CATEGORY(logUPowerDevice, "quickshell.service.upower.device", QtWarningMsg); -} QString UPowerDeviceState::toString(UPowerDeviceState::Enum status) { switch (status) { @@ -116,7 +114,9 @@ DBusResult DBusDataTransform::fromWire(qreal wire) { DBusResult DBusDataTransform::fromWire(quint32 wire) { - if (wire >= UPowerDeviceState::Unknown && wire <= UPowerDeviceState::PendingDischarge) { + if (wire != UPowerDeviceType::Battery && wire >= UPowerDeviceState::Unknown + && wire <= UPowerDeviceState::PendingDischarge) + { return DBusResult(static_cast(wire)); } diff --git a/src/services/upower/device.hpp b/src/services/upower/device.hpp index 899ead93..8d1ac709 100644 --- a/src/services/upower/device.hpp +++ b/src/services/upower/device.hpp @@ -154,8 +154,6 @@ class UPowerDevice: public QObject { Q_PROPERTY(bool isLaptopBattery READ isLaptopBattery NOTIFY isLaptopBatteryChanged BINDABLE bindableIsLaptopBattery); /// Native path of the device specific to your OS. Q_PROPERTY(QString nativePath READ nativePath NOTIFY nativePathChanged BINDABLE bindableNativePath); - /// Model name of the device. Unlikely to be useful for internal devices. - Q_PROPERTY(QString model READ model NOTIFY modelChanged BINDABLE bindableModel); /// If device statistics have been queried for this device yet. /// This will be true for all devices returned from @@UPower.devices, but not the default /// device, which may be returned before it is ready to avoid returning null. @@ -188,7 +186,6 @@ public: QS_BINDABLE_GETTER(QString, bIconName, iconName, bindableIconName); QS_BINDABLE_GETTER(bool, bIsLaptopBattery, isLaptopBattery, bindableIsLaptopBattery); QS_BINDABLE_GETTER(QString, bNativePath, nativePath, bindableNativePath); - QS_BINDABLE_GETTER(QString, bModel, model, bindableModel); QS_BINDABLE_GETTER(bool, bReady, ready, bindableReady); signals: @@ -209,7 +206,6 @@ signals: void iconNameChanged(); void isLaptopBatteryChanged(); void nativePathChanged(); - void modelChanged(); private slots: void onGetAllFinished(); @@ -231,7 +227,6 @@ private: Q_OBJECT_BINDABLE_PROPERTY(UPowerDevice, QString, bIconName, &UPowerDevice::iconNameChanged); Q_OBJECT_BINDABLE_PROPERTY(UPowerDevice, bool, bIsLaptopBattery, &UPowerDevice::isLaptopBatteryChanged); Q_OBJECT_BINDABLE_PROPERTY(UPowerDevice, QString, bNativePath, &UPowerDevice::nativePathChanged); - Q_OBJECT_BINDABLE_PROPERTY(UPowerDevice, QString, bModel, &UPowerDevice::modelChanged); Q_OBJECT_BINDABLE_PROPERTY(UPowerDevice, bool, bReady, &UPowerDevice::readyChanged); QS_DBUS_BINDABLE_PROPERTY_GROUP(UPowerDevice, deviceProperties); @@ -248,7 +243,6 @@ private: QS_DBUS_PROPERTY_BINDING(UPowerDevice, pHealthPercentage, bHealthPercentage, deviceProperties, "Capacity"); QS_DBUS_PROPERTY_BINDING(UPowerDevice, pIconName, bIconName, deviceProperties, "IconName"); QS_DBUS_PROPERTY_BINDING(UPowerDevice, pNativePath, bNativePath, deviceProperties, "NativePath"); - QS_DBUS_PROPERTY_BINDING(UPowerDevice, pModel, bModel, deviceProperties, "Model"); // clang-format on DBusUPowerDevice* device = nullptr; diff --git a/src/services/upower/module.md b/src/services/upower/module.md index e1d697fe..99c7ece4 100644 --- a/src/services/upower/module.md +++ b/src/services/upower/module.md @@ -3,6 +3,5 @@ description = "UPower Service" headers = [ "core.hpp", "device.hpp", - "powerprofiles.hpp", ] ----- diff --git a/src/services/upower/powerprofiles.cpp b/src/services/upower/powerprofiles.cpp deleted file mode 100644 index b4b477b0..00000000 --- a/src/services/upower/powerprofiles.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include "powerprofiles.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../dbus/bus.hpp" -#include "../../dbus/properties.hpp" - -namespace qs::service::upower { - -namespace { -Q_LOGGING_CATEGORY(logPowerProfiles, "quickshell.service.powerprofiles", QtWarningMsg); -} - -QString PowerProfile::toString(PowerProfile::Enum profile) { - switch (profile) { - case PowerProfile::PowerSaver: return QStringLiteral("PowerSaver"); - case PowerProfile::Balanced: return QStringLiteral("Balanced"); - case PowerProfile::Performance: return QStringLiteral("Performance"); - default: return QStringLiteral("Invalid"); - } -} - -QString PerformanceDegradationReason::toString(PerformanceDegradationReason::Enum reason) { - switch (reason) { - case PerformanceDegradationReason::LapDetected: return QStringLiteral("LapDetected"); - case PerformanceDegradationReason::HighTemperature: return QStringLiteral("HighTemperature"); - default: return QStringLiteral("Invalid"); - } -} - -bool PowerProfileHold::operator==(const PowerProfileHold& other) const { - return other.profile == this->profile && other.applicationId == this->applicationId - && other.reason == this->reason; -} - -QDebug& operator<<(QDebug& debug, const PowerProfileHold& hold) { - auto saver = QDebugStateSaver(debug); - - debug.nospace(); - debug << "PowerProfileHold(profile=" << hold.profile << ", applicationId=" << hold.applicationId - << ", reason=" << hold.reason << ')'; - - return debug; -} - -PowerProfiles::PowerProfiles() { - qDBusRegisterMetaType>(); - - this->bHasPerformanceProfile.setBinding([this]() { - return this->bProfiles.value().contains(PowerProfile::Performance); - }); - - qCDebug(logPowerProfiles) << "Starting PowerProfiles Service."; - - auto bus = QDBusConnection::systemBus(); - - if (!bus.isConnected()) { - qCWarning(logPowerProfiles - ) << "Could not connect to DBus. PowerProfiles services will not work."; - } - - this->service = new QDBusInterface( - "org.freedesktop.UPower.PowerProfiles", - "/org/freedesktop/UPower/PowerProfiles", - "org.freedesktop.UPower.PowerProfiles", - bus, - this - ); - - if (!this->service->isValid()) { - qCDebug(logPowerProfiles - ) << "PowerProfilesDaemon is not currently running, attempting to start it."; - - dbus::tryLaunchService(this, bus, "org.freedesktop.UPower.PowerProfiles", [this](bool success) { - if (success) { - qCDebug(logPowerProfiles) << "Successfully launched PowerProfiles service."; - this->init(); - } else { - qCWarning(logPowerProfiles) - << "Could not start PowerProfilesDaemon. The PowerProfiles service will not work."; - } - }); - } else { - this->init(); - } -} - -void PowerProfiles::init() { - this->properties.setInterface(this->service); - this->properties.updateAllViaGetAll(); -} - -void PowerProfiles::setProfile(PowerProfile::Enum profile) { - if (profile == PowerProfile::Performance && !this->bHasPerformanceProfile) { - qCCritical(logPowerProfiles - ) << "Cannot request performance profile as it is not present for this device."; - return; - } else if (profile < PowerProfile::PowerSaver || profile > PowerProfile::Performance) { - qCCritical(logPowerProfiles) << "Tried to request invalid power profile" << profile; - return; - } - - this->bProfile = profile; - this->pProfile.write(); -} - -PowerProfiles* PowerProfiles::instance() { - static auto* instance = new PowerProfiles(); // NOLINT - return instance; -} - -PowerProfilesQml::PowerProfilesQml(QObject* parent): QObject(parent) { - auto* instance = PowerProfiles::instance(); - - this->bProfile.setBinding([instance]() { return instance->bProfile.value(); }); - - this->bHasPerformanceProfile.setBinding([instance]() { - return instance->bHasPerformanceProfile.value(); - }); - - this->bDegradationReason.setBinding([instance]() { return instance->bDegradationReason.value(); } - ); - - this->bHolds.setBinding([instance]() { return instance->bHolds.value(); }); -} - -} // namespace qs::service::upower - -namespace qs::dbus { - -using namespace qs::service::upower; - -DBusResult DBusDataTransform::fromWire(const Wire& wire) { - if (wire == QStringLiteral("power-saver")) { - return PowerProfile::PowerSaver; - } else if (wire == QStringLiteral("balanced")) { - return PowerProfile::Balanced; - } else if (wire == QStringLiteral("performance")) { - return PowerProfile::Performance; - } else { - return QDBusError(QDBusError::InvalidArgs, QString("Invalid PowerProfile: %1").arg(wire)); - } -} - -QString DBusDataTransform::toWire(Data data) { - switch (data) { - case PowerProfile::PowerSaver: return QStringLiteral("power-saver"); - case PowerProfile::Balanced: return QStringLiteral("balanced"); - case PowerProfile::Performance: return QStringLiteral("performance"); - } -} - -DBusResult> -DBusDataTransform>::fromWire(const Wire& wire) { - QList profiles; - - for (const auto& entry: wire) { - auto profile = - DBusDataTransform::fromWire(entry.value("Profile").value()); - - if (!profile.isValid()) return profile.error; - profiles.append(profile.value); - } - - return profiles; -} - -DBusResult -DBusDataTransform::fromWire(const Wire& wire) { - if (wire.isEmpty()) { - return PerformanceDegradationReason::None; - } else if (wire == QStringLiteral("lap-detected")) { - return PerformanceDegradationReason::LapDetected; - } else if (wire == QStringLiteral("high-operating-temperature")) { - return PerformanceDegradationReason::HighTemperature; - } else { - return QDBusError( - QDBusError::InvalidArgs, - QString("Invalid PerformanceDegradationReason: %1").arg(wire) - ); - } -} - -DBusResult> -DBusDataTransform>::fromWire(const Wire& wire) { - QList holds; - - for (const auto& entry: wire) { - auto profile = - DBusDataTransform::fromWire(entry.value("Profile").value()); - - if (!profile.isValid()) return profile.error; - - auto applicationId = entry.value("ApplicationId").value(); - auto reason = entry.value("Reason").value(); - - holds.append(PowerProfileHold(profile.value, applicationId, reason)); - } - - return holds; -} - -} // namespace qs::dbus diff --git a/src/services/upower/powerprofiles.hpp b/src/services/upower/powerprofiles.hpp deleted file mode 100644 index b7340328..00000000 --- a/src/services/upower/powerprofiles.hpp +++ /dev/null @@ -1,237 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../dbus/properties.hpp" - -namespace qs::service::upower { - -///! Power profile exposed by the PowerProfiles service. -/// See @@PowerProfiles. -class PowerProfile: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - /// This profile will limit system performance in order to save power. - PowerSaver = 0, - /// This profile is the default, and will attempt to strike a balance - /// between performance and power consumption. - Balanced = 1, - /// This profile will maximize performance at the cost of power consumption. - Performance = 2, - }; - Q_ENUM(Enum); - - Q_INVOKABLE static QString toString(qs::service::upower::PowerProfile::Enum profile); -}; - -///! Reason for performance degradation exposed by the PowerProfiles service. -/// See @@PowerProfiles.degradationReason for more information. -class PerformanceDegradationReason: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - /// Performance has not been degraded in a way power-profiles-daemon can detect. - None = 0, - /// Performance has been reduced due to the computer's lap detection function, - /// which attempts to keep the computer from getting too hot while on your lap. - LapDetected = 1, - /// Performance has been reduced due to high system temperatures. - HighTemperature = 2, - }; - Q_ENUM(Enum); - - // clang-format off - Q_INVOKABLE static QString toString(qs::service::upower::PerformanceDegradationReason::Enum reason); - // clang-format on -}; - -class PowerProfileHold; - -} // namespace qs::service::upower - -namespace qs::dbus { - -template <> -struct DBusDataTransform { - using Wire = QString; - using Data = qs::service::upower::PowerProfile::Enum; - static DBusResult fromWire(const Wire& wire); - static Wire toWire(Data data); -}; - -template <> -struct DBusDataTransform> { - using Wire = QList; - using Data = QList; - static DBusResult fromWire(const Wire& wire); -}; - -template <> -struct DBusDataTransform { - using Wire = QString; - using Data = qs::service::upower::PerformanceDegradationReason::Enum; - static DBusResult fromWire(const Wire& wire); -}; - -template <> -struct DBusDataTransform> { - using Wire = QList; - using Data = QList; - static DBusResult fromWire(const Wire& wire); -}; - -} // namespace qs::dbus - -namespace qs::service::upower { - -// docgen can't hit gadgets yet -class PowerProfileHold { - Q_GADGET; - QML_VALUE_TYPE(powerProfileHold); - Q_PROPERTY(qs::service::upower::PowerProfile::Enum profile MEMBER profile CONSTANT); - Q_PROPERTY(QString applicationId MEMBER applicationId CONSTANT); - Q_PROPERTY(QString reason MEMBER reason CONSTANT); - -public: - explicit PowerProfileHold() = default; - explicit PowerProfileHold(PowerProfile::Enum profile, QString applicationId, QString reason) - : profile(profile) - , applicationId(std::move(applicationId)) - , reason(std::move(reason)) {} - - PowerProfile::Enum profile = PowerProfile::Balanced; - QString applicationId; - QString reason; - - [[nodiscard]] bool operator==(const PowerProfileHold& other) const; -}; - -QDebug& operator<<(QDebug& debug, const PowerProfileHold& hold); - -class PowerProfiles: public QObject { - Q_OBJECT; - -public: - void setProfile(PowerProfile::Enum profile); - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(PowerProfiles, PowerProfile::Enum, bProfile, PowerProfile::Balanced); - Q_OBJECT_BINDABLE_PROPERTY(PowerProfiles, bool, bHasPerformanceProfile); - Q_OBJECT_BINDABLE_PROPERTY(PowerProfiles, PerformanceDegradationReason::Enum, bDegradationReason); - Q_OBJECT_BINDABLE_PROPERTY(PowerProfiles, QList, bHolds); - // clang-format on - - static PowerProfiles* instance(); - -private: - explicit PowerProfiles(); - void init(); - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(PowerProfiles, QList, bProfiles); - - QS_DBUS_BINDABLE_PROPERTY_GROUP(PowerProfiles, properties); - QS_DBUS_PROPERTY_BINDING(PowerProfiles, pProfile, bProfile, properties, "ActiveProfile"); - QS_DBUS_PROPERTY_BINDING(PowerProfiles, pProfiles, bProfiles, properties, "Profiles"); - QS_DBUS_PROPERTY_BINDING(PowerProfiles, pPerformanceDegraded, bDegradationReason, properties, "PerformanceDegraded"); - QS_DBUS_PROPERTY_BINDING(PowerProfiles, pHolds, bHolds, properties, "ActiveProfileHolds"); - // clang-format on - - QDBusInterface* service = nullptr; -}; - -///! Provides access to the Power Profiles service. -/// An interface to the UPower [power profiles daemon], which can be -/// used to view and manage power profiles. -/// -/// > [!NOTE] The power profiles daemon must be installed to use this service. -/// > Installing UPower does not necessarily install the power profiles daemon. -/// -/// [power profiles daemon]: https://gitlab.freedesktop.org/upower/power-profiles-daemon -class PowerProfilesQml: public QObject { - Q_OBJECT; - QML_NAMED_ELEMENT(PowerProfiles); - QML_SINGLETON; - // clang-format off - /// The current power profile. - /// - /// This property may be set to change the system's power profile, however - /// it cannot be set to `Performance` unless @@hasPerformanceProfile is true. - Q_PROPERTY(qs::service::upower::PowerProfile::Enum profile READ default WRITE setProfile NOTIFY profileChanged BINDABLE bindableProfile); - /// If the system has a performance profile. - /// - /// If this property is false, your system does not have a performance - /// profile known to power-profiles-daemon. - Q_PROPERTY(bool hasPerformanceProfile READ default NOTIFY hasPerformanceProfileChanged BINDABLE bindableHasPerformanceProfile); - /// If power-profiles-daemon detects degraded system performance, the reason - /// for the degradation will be present here. - Q_PROPERTY(qs::service::upower::PerformanceDegradationReason::Enum degradationReason READ default NOTIFY degradationReasonChanged BINDABLE bindableDegradationReason); - /// Power profile holds created by other applications. - /// - /// This property returns a `powerProfileHold` object, which has the following properties. - /// - `profile` - The @@PowerProfile held by the application. - /// - `applicationId` - A string identifying the application - /// - `reason` - The reason the application has given for holding the profile. - /// - /// Applications may "hold" a power profile in place for their lifetime, such - /// as a game holding Performance mode or a system daemon holding Power Saver mode - /// when reaching a battery threshold. If the user selects a different profile explicitly - /// (e.g. by setting @@profile$) all holds will be removed. - /// - /// Multiple applications may hold a power profile, however if multiple applications request - /// profiles than `PowerSaver` will win over `Performance`. Only `Performance` and `PowerSaver` - /// profiles may be held. - Q_PROPERTY(QList holds READ default NOTIFY holdsChanged BINDABLE bindableHolds); - // clang-format on - -signals: - void profileChanged(); - void hasPerformanceProfileChanged(); - void degradationReasonChanged(); - void holdsChanged(); - -public: - explicit PowerProfilesQml(QObject* parent = nullptr); - - [[nodiscard]] QBindable bindableProfile() const { return &this->bProfile; } - - static void setProfile(PowerProfile::Enum profile) { - PowerProfiles::instance()->setProfile(profile); - } - - [[nodiscard]] QBindable bindableHasPerformanceProfile() const { - return &this->bHasPerformanceProfile; - } - - [[nodiscard]] QBindable bindableDegradationReason() const { - return &this->bDegradationReason; - } - - [[nodiscard]] QBindable> bindableHolds() const { return &this->bHolds; } - -private: - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(PowerProfilesQml, PowerProfile::Enum, bProfile, &PowerProfilesQml::profileChanged); - Q_OBJECT_BINDABLE_PROPERTY(PowerProfilesQml, bool, bHasPerformanceProfile, &PowerProfilesQml::hasPerformanceProfileChanged); - Q_OBJECT_BINDABLE_PROPERTY(PowerProfilesQml, PerformanceDegradationReason::Enum, bDegradationReason, &PowerProfilesQml::degradationReasonChanged); - Q_OBJECT_BINDABLE_PROPERTY(PowerProfilesQml, QList, bHolds, &PowerProfilesQml::holdsChanged); - // clang-format on -}; - -} // namespace qs::service::upower diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index 3b3d08ae..db03cf16 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -28,7 +28,7 @@ qs_add_pchset(wayland-protocol ) -function (wl_proto target name dir) +function (wl_proto target name path) set(PROTO_BUILD_PATH ${CMAKE_CURRENT_BINARY_DIR}/wl-proto/${name}) make_directory(${PROTO_BUILD_PATH}) @@ -36,38 +36,39 @@ function (wl_proto target name dir) set(WS_CLIENT_CODE "${PROTO_BUILD_PATH}/wayland-${name}.c") set(QWS_CLIENT_HEADER "${PROTO_BUILD_PATH}/qwayland-${name}.h") set(QWS_CLIENT_CODE "${PROTO_BUILD_PATH}/qwayland-${name}.cpp") - set(PATH "${dir}/${name}.xml") add_custom_command( OUTPUT "${WS_CLIENT_HEADER}" - COMMAND Wayland::Scanner client-header "${PATH}" "${WS_CLIENT_HEADER}" - DEPENDS Wayland::Scanner "${PATH}" + COMMAND Wayland::Scanner client-header "${path}" "${WS_CLIENT_HEADER}" + DEPENDS Wayland::Scanner "${path}" ) add_custom_command( OUTPUT "${WS_CLIENT_CODE}" - COMMAND Wayland::Scanner private-code "${PATH}" "${WS_CLIENT_CODE}" - DEPENDS Wayland::Scanner "${PATH}" + COMMAND Wayland::Scanner private-code "${path}" "${WS_CLIENT_CODE}" + DEPENDS Wayland::Scanner "${path}" ) add_custom_command( OUTPUT "${QWS_CLIENT_HEADER}" - COMMAND Qt6::qtwaylandscanner client-header "${PATH}" > "${QWS_CLIENT_HEADER}" - DEPENDS Qt6::qtwaylandscanner "${PATH}" + COMMAND Qt6::qtwaylandscanner client-header "${path}" > "${QWS_CLIENT_HEADER}" + DEPENDS Qt6::qtwaylandscanner "${path}" ) add_custom_command( OUTPUT "${QWS_CLIENT_CODE}" - COMMAND Qt6::qtwaylandscanner client-code "${PATH}" > "${QWS_CLIENT_CODE}" - DEPENDS Qt6::qtwaylandscanner "${PATH}" + COMMAND Qt6::qtwaylandscanner client-code "${path}" > "${QWS_CLIENT_CODE}" + DEPENDS Qt6::qtwaylandscanner "${path}" ) add_library(wl-proto-${name}-wl STATIC ${WS_CLIENT_HEADER} ${WS_CLIENT_CODE}) - add_library(${target} STATIC ${QWS_CLIENT_HEADER} ${QWS_CLIENT_CODE}) + add_library(wl-proto-${name} STATIC ${QWS_CLIENT_HEADER} ${QWS_CLIENT_CODE}) - target_include_directories(${target} INTERFACE ${PROTO_BUILD_PATH}) - target_link_libraries(${target} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate) - qs_pch(${target} SET wayland-protocol) + target_include_directories(wl-proto-${name} INTERFACE ${PROTO_BUILD_PATH}) + target_link_libraries(wl-proto-${name} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate) + qs_pch(wl-proto-${name} SET wayland-protocol) + + target_link_libraries(${target} PRIVATE wl-proto-${name}) endfunction() # ----- @@ -103,13 +104,6 @@ if (WAYLAND_TOPLEVEL_MANAGEMENT) list(APPEND WAYLAND_MODULES Quickshell.Wayland._ToplevelManagement) endif() -if (SCREENCOPY) - add_subdirectory(buffer) - add_subdirectory(screencopy) - list(APPEND WAYLAND_MODULES Quickshell.Wayland._Screencopy) -endif() - - if (HYPRLAND) add_subdirectory(hyprland) endif() diff --git a/src/wayland/buffer/CMakeLists.txt b/src/wayland/buffer/CMakeLists.txt deleted file mode 100644 index f80c53a3..00000000 --- a/src/wayland/buffer/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -find_package(PkgConfig REQUIRED) -pkg_check_modules(dmabuf-deps REQUIRED IMPORTED_TARGET libdrm gbm egl) - -qt_add_library(quickshell-wayland-buffer STATIC - manager.cpp - dmabuf.cpp - shm.cpp -) - -wl_proto(wlp-linux-dmabuf linux-dmabuf-v1 "${WAYLAND_PROTOCOLS}/stable/linux-dmabuf") - -target_link_libraries(quickshell-wayland-buffer PRIVATE - Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - PkgConfig::dmabuf-deps - wlp-linux-dmabuf -) - -qs_pch(quickshell-wayland-buffer SET large) diff --git a/src/wayland/buffer/dmabuf.cpp b/src/wayland/buffer/dmabuf.cpp deleted file mode 100644 index 09abc15f..00000000 --- a/src/wayland/buffer/dmabuf.cpp +++ /dev/null @@ -1,609 +0,0 @@ -#include "dmabuf.hpp" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/stacklist.hpp" -#include "manager.hpp" -#include "manager_p.hpp" - -namespace qs::wayland::buffer::dmabuf { - -namespace { - -Q_LOGGING_CATEGORY(logDmabuf, "quickshell.wayland.buffer.dmabuf", QtWarningMsg); - -LinuxDmabufManager* MANAGER = nullptr; // NOLINT - -} // namespace - -QDebug& operator<<(QDebug& debug, const FourCCStr& fourcc) { - debug << fourcc.cStr(); - return debug; -} - -QDebug& operator<<(QDebug& debug, const FourCCModStr& fourcc) { - debug << fourcc.cStr(); - return debug; -} - -QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer) { - auto saver = QDebugStateSaver(debug); - debug.nospace(); - - if (buffer) { - debug << "WlDmaBuffer(" << static_cast(buffer) << ", size=" << buffer->width << 'x' - << buffer->height << ", format=" << FourCCStr(buffer->format) << ", modifier=`" - << FourCCModStr(buffer->modifier) << "`)"; - } else { - debug << "WlDmaBuffer(0x0)"; - } - - return debug; -} - -GbmDeviceHandle::~GbmDeviceHandle() { - if (device) { - MANAGER->unrefGbmDevice(this->device); - } -} - -// This will definitely backfire later -void LinuxDmabufFormatSelection::ensureSorted() { - if (this->sorted) return; - auto beginIter = this->formats.begin(); - - auto xrgbIter = std::ranges::find_if(this->formats, [](const auto& format) { - return format.first == DRM_FORMAT_XRGB8888; - }); - - if (xrgbIter != this->formats.end()) { - std::swap(*beginIter, *xrgbIter); - ++beginIter; - } - - auto argbIter = std::ranges::find_if(this->formats, [](const auto& format) { - return format.first == DRM_FORMAT_ARGB8888; - }); - - if (argbIter != this->formats.end()) std::swap(*beginIter, *argbIter); - - this->sorted = true; -} - -LinuxDmabufFeedback::LinuxDmabufFeedback(::zwp_linux_dmabuf_feedback_v1* feedback) - : zwp_linux_dmabuf_feedback_v1(feedback) {} - -LinuxDmabufFeedback::~LinuxDmabufFeedback() { this->destroy(); } - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_format_table(int32_t fd, uint32_t size) { - this->formatTableSize = size; - - this->formatTable = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); - - if (this->formatTable == MAP_FAILED) { - this->formatTable = nullptr; - qCFatal(logDmabuf) << "Failed to mmap format table."; - } - - qCDebug(logDmabuf) << "Got format table"; -} - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_main_device(wl_array* device) { - if (device->size != sizeof(dev_t)) { - qCFatal(logDmabuf) << "The size of dev_t used by the compositor and quickshell is mismatched. " - "Try recompiling both."; - } - - this->mainDevice = *reinterpret_cast(device->data); - qCDebug(logDmabuf) << "Got main device id" << this->mainDevice; -} - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_tranche_target_device(wl_array* device) { - if (device->size != sizeof(dev_t)) { - qCFatal(logDmabuf) << "The size of dev_t used by the compositor and quickshell is mismatched. " - "Try recompiling both."; - } - - auto& tranche = this->tranches.emplaceBack(); - tranche.device = *reinterpret_cast(device->data); - qCDebug(logDmabuf) << "Got target device id" << this->mainDevice; -} - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_tranche_flags(uint32_t flags) { - this->tranches.back().flags = flags; - qCDebug(logDmabuf) << "Got target device flags" << flags; -} - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_tranche_formats(wl_array* indices) { - struct FormatTableEntry { - uint32_t format; - uint32_t padding; - uint64_t modifier; - }; - - static_assert(sizeof(FormatTableEntry) == 16, "Format table entry was not packed to 16 bytes."); - - if (this->formatTable == nullptr) { - qCFatal(logDmabuf) << "Received tranche formats before format table."; - } - - auto& tranche = this->tranches.back(); - - auto* table = reinterpret_cast(this->formatTable); - auto* indexTable = reinterpret_cast(indices->data); - auto indexTableLength = indices->size / sizeof(uint16_t); - - uint32_t lastFormat = 0; - LinuxDmabufModifiers* modifiers = nullptr; - - for (uint16_t ti = 0; ti != indexTableLength; ++ti) { - auto i = indexTable[ti]; // NOLINT - const auto& entry = table[i]; // NOLINT - - // Compositors usually send a single format's modifiers as a block. - if (!modifiers || entry.format != lastFormat) { - lastFormat = entry.format; - - auto modifiersIter = std::ranges::find_if(tranche.formats.formats, [&](const auto& pair) { - return pair.first == entry.format; - }); - - if (modifiersIter == tranche.formats.formats.end()) { - tranche.formats.formats.push(qMakePair(entry.format, LinuxDmabufModifiers())); - modifiers = &(--tranche.formats.formats.end())->second; - } else { - modifiers = &modifiersIter->second; - } - } - - if (entry.modifier == DRM_FORMAT_MOD_INVALID) { - modifiers->implicit = true; - } else { - modifiers->modifiers.push(entry.modifier); - } - } -} - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_tranche_done() { - qCDebug(logDmabuf) << "Got tranche end."; -} - -void LinuxDmabufFeedback::zwp_linux_dmabuf_feedback_v1_done() { - qCDebug(logDmabuf) << "Got feedback done."; - - if (this->formatTable) { - munmap(this->formatTable, this->formatTableSize); - this->formatTable = nullptr; - } - - if (logDmabuf().isDebugEnabled()) { - qCDebug(logDmabuf) << "Dmabuf tranches:"; - - for (auto& tranche: this->tranches) { - qCDebug(logDmabuf) << " Tranche on device" << tranche.device; - - // will be sorted on first use otherwise - tranche.formats.ensureSorted(); - - for (auto& [format, modifiers]: tranche.formats.formats) { - qCDebug(logDmabuf) << " Format" << FourCCStr(format); - - if (modifiers.implicit) { - qCDebug(logDmabuf) << " Implicit Modifier"; - } - - for (const auto& modifier: modifiers.modifiers) { - qCDebug(logDmabuf) << " Explicit Modifier" << FourCCModStr(modifier); - } - } - } - } - - // Copy tranches to the manager. If the compositor ever updates - // our tranches, we'll start from a clean slate. - MANAGER->tranches = this->tranches; - this->tranches.clear(); - - MANAGER->feedbackDone(); -} - -LinuxDmabufManager::LinuxDmabufManager(WlBufferManagerPrivate* manager) - : QWaylandClientExtensionTemplate(5) - , manager(manager) { - MANAGER = this; - this->initialize(); - - if (this->isActive()) { - qCDebug(logDmabuf) << "Requesting default dmabuf feedback..."; - new LinuxDmabufFeedback(this->get_default_feedback()); - } -} - -void LinuxDmabufManager::feedbackDone() { this->manager->dmabufReady(); } - -GbmDeviceHandle LinuxDmabufManager::getGbmDevice(dev_t handle) { - struct DrmFree { - static void cleanup(drmDevice* d) { drmFreeDevice(&d); } - }; - - std::string renderNodeStorage; - std::string* renderNode = nullptr; - - auto sharedDevice = std::ranges::find_if(this->gbmDevices, [&](const SharedGbmDevice& d) { - return d.handle == handle; - }); - - if (sharedDevice != this->gbmDevices.end()) { - renderNode = &sharedDevice->renderNode; - } else { - drmDevice* drmDevPtr = nullptr; - if (auto error = drmGetDeviceFromDevId(handle, 0, &drmDevPtr); error != 0) { - qCWarning(logDmabuf) << "Failed to get drm device information from handle:" - << qt_error_string(error); - return nullptr; - } - - auto drmDev = QScopedPointer(drmDevPtr); - - if (!(drmDev->available_nodes & (1 << DRM_NODE_RENDER))) { - qCDebug(logDmabuf) << "Cannot create GBM device: DRM device does not have render node."; - return nullptr; - } - - renderNodeStorage = drmDev->nodes[DRM_NODE_RENDER]; // NOLINT - renderNode = &renderNodeStorage; - sharedDevice = std::ranges::find_if(this->gbmDevices, [&](const SharedGbmDevice& d) { - return d.renderNode == renderNodeStorage; - }); - } - - if (sharedDevice != this->gbmDevices.end()) { - qCDebug(logDmabuf) << "Used existing GBM device on render node" << *renderNode; - ++sharedDevice->refcount; - return sharedDevice->device; - } else { - auto fd = open(renderNode->c_str(), O_RDWR | O_CLOEXEC); - if (fd < 0) { - qCDebug(logDmabuf) << "Could not open render node" << *renderNode << ":" - << qt_error_string(fd); - return nullptr; - } - - auto* device = gbm_create_device(fd); - - if (!device) { - qCDebug(logDmabuf) << "Failed to create GBM device from render node" << *renderNode; - close(fd); - return nullptr; - } - - qCDebug(logDmabuf) << "Created GBM device on render node" << *renderNode; - - this->gbmDevices.push_back({ - .handle = handle, - .renderNode = std::move(renderNodeStorage), - .device = device, - .refcount = 1, - }); - - return device; - } -} - -void LinuxDmabufManager::unrefGbmDevice(gbm_device* device) { - auto iter = std::ranges::find_if(this->gbmDevices, [device](const SharedGbmDevice& d) { - return d.device == device; - }); - if (iter == this->gbmDevices.end()) return; - - qCDebug(logDmabuf) << "Lost reference to GBM device" << device; - - if (--iter->refcount == 0) { - auto fd = gbm_device_get_fd(iter->device); - gbm_device_destroy(iter->device); - close(fd); - - this->gbmDevices.erase(iter); - qCDebug(logDmabuf) << "Destroyed GBM device" << device; - } -} - -GbmDeviceHandle LinuxDmabufManager::dupHandle(const GbmDeviceHandle& handle) { - if (!handle) return GbmDeviceHandle(); - - auto iter = std::ranges::find_if(this->gbmDevices, [&handle](const SharedGbmDevice& d) { - return d.device == *handle; - }); - if (iter == this->gbmDevices.end()) return GbmDeviceHandle(); - - qCDebug(logDmabuf) << "Duplicated GBM device handle" << *handle; - ++iter->refcount; - return GbmDeviceHandle(*handle); -} - -WlBuffer* LinuxDmabufManager::createDmabuf(const WlBufferRequest& request) { - for (auto& tranche: this->tranches) { - if (request.dmabuf.device != 0 && tranche.device != request.dmabuf.device) { - continue; - } - - LinuxDmabufFormatSelection formats; - for (const auto& format: request.dmabuf.formats) { - if (!format.modifiers.isEmpty()) { - formats.formats.push( - qMakePair(format.format, LinuxDmabufModifiers {.modifiers = format.modifiers}) - ); - } else { - for (const auto& trancheFormat: tranche.formats.formats) { - if (trancheFormat.first == format.format) { - formats.formats.push(trancheFormat); - } - } - } - } - - if (formats.formats.isEmpty()) continue; - formats.ensureSorted(); - - auto gbmDevice = this->getGbmDevice(tranche.device); - - if (!gbmDevice) { - qCWarning(logDmabuf) << "Hit unusable tranche device while trying to create dmabuf."; - continue; - } - - for (const auto& [format, modifiers]: formats.formats) { - if (auto* buf = - this->createDmabuf(gbmDevice, format, modifiers, request.width, request.height)) - { - return buf; - } - } - } - - qCWarning(logDmabuf) << "Unable to create dmabuf for request: No matching formats."; - return nullptr; -} - -WlBuffer* LinuxDmabufManager::createDmabuf( - GbmDeviceHandle& device, - uint32_t format, - const LinuxDmabufModifiers& modifiers, - uint32_t width, - uint32_t height -) { - auto buffer = std::unique_ptr(new WlDmaBuffer()); - auto& bo = buffer->bo; - - const uint32_t flags = GBM_BO_USE_RENDERING; - - if (modifiers.modifiers.isEmpty()) { - if (!modifiers.implicit) { - qCritical(logDmabuf - ) << "Failed to create gbm_bo: format supports no implicit OR explicit modifiers."; - return nullptr; - } - - qCDebug(logDmabuf) << "Creating gbm_bo without modifiers..."; - bo = gbm_bo_create(*device, width, height, format, flags); - } else { - qCDebug(logDmabuf) << "Creating gbm_bo with modifiers..."; - - STACKLIST_VLA_VIEW(uint64_t, modifiers.modifiers, modifiersData); - - bo = gbm_bo_create_with_modifiers2( - *device, - width, - height, - format, - modifiersData, - modifiers.modifiers.length(), - flags - ); - } - - if (!bo) { - qCritical(logDmabuf) << "Failed to create gbm_bo."; - return nullptr; - } - - buffer->planeCount = gbm_bo_get_plane_count(bo); - buffer->planes = new WlDmaBuffer::Plane[buffer->planeCount](); - buffer->modifier = gbm_bo_get_modifier(bo); - - auto params = QtWayland::zwp_linux_buffer_params_v1(this->create_params()); - - for (auto i = 0; i < buffer->planeCount; ++i) { - auto& plane = buffer->planes[i]; // NOLINT - plane.fd = gbm_bo_get_fd_for_plane(bo, i); - - if (plane.fd < 0) { - qCritical(logDmabuf) << "Failed to get gbm_bo fd for plane" << i << qt_error_string(plane.fd); - params.destroy(); - gbm_bo_destroy(bo); - return nullptr; - } - - plane.stride = gbm_bo_get_stride_for_plane(bo, i); - plane.offset = gbm_bo_get_offset(bo, i); - - params.add( - plane.fd, - i, - plane.offset, - plane.stride, - buffer->modifier >> 32, - buffer->modifier & 0xffffffff - ); - } - - buffer->mBuffer = - params.create_immed(static_cast(width), static_cast(height), format, 0); - params.destroy(); - - buffer->device = this->dupHandle(device); - buffer->width = width; - buffer->height = height; - buffer->format = format; - - qCDebug(logDmabuf) << "Created dmabuf" << buffer.get(); - return buffer.release(); -} - -WlDmaBuffer::WlDmaBuffer(WlDmaBuffer&& other) noexcept - : device(std::move(other.device)) - , bo(other.bo) - , mBuffer(other.mBuffer) - , planes(other.planes) { - other.mBuffer = nullptr; - other.bo = nullptr; - other.planeCount = 0; -} - -WlDmaBuffer& WlDmaBuffer::operator=(WlDmaBuffer&& other) noexcept { - this->~WlDmaBuffer(); - new (this) WlDmaBuffer(std::move(other)); - return *this; -} - -WlDmaBuffer::~WlDmaBuffer() { - if (this->mBuffer) { - wl_buffer_destroy(this->mBuffer); - } - - if (this->bo) { - gbm_bo_destroy(this->bo); - qCDebug(logDmabuf) << "Destroyed" << this << "freeing bo" << this->bo; - } - - for (auto i = 0; i < this->planeCount; ++i) { - const auto& plane = this->planes[i]; // NOLINT - if (plane.fd) close(plane.fd); - } - - delete[] this->planes; -} - -bool WlDmaBuffer::isCompatible(const WlBufferRequest& request) const { - if (request.width != this->width || request.height != this->height) return false; - - auto matchingFormat = std::ranges::find_if(request.dmabuf.formats, [&](const auto& format) { - return format.format == this->format - && (format.modifiers.isEmpty() - || std::ranges::find(format.modifiers, this->modifier) != format.modifiers.end()); - }); - - return matchingFormat != request.dmabuf.formats.end(); -} - -WlBufferQSGTexture* WlDmaBuffer::createQsgTexture(QQuickWindow* window) const { - static auto* glEGLImageTargetTexture2DOES = []() { - auto* fn = reinterpret_cast( - eglGetProcAddress("glEGLImageTargetTexture2DOES") - ); - - if (!fn) { - qCFatal(logDmabuf) << "Failed to create QSG texture from WlDmaBuffer: " - "glEGLImageTargetTexture2DOES is missing."; - } - - return fn; - }(); - - auto* context = QOpenGLContext::currentContext(); - if (!context) { - qCFatal(logDmabuf) << "Failed to create QSG texture from WlDmaBuffer: No GL context."; - } - - auto* qEglContext = context->nativeInterface(); - if (!qEglContext) { - qCFatal(logDmabuf) << "Failed to create QSG texture from WlDmaBuffer: No EGL context."; - } - - auto* display = qEglContext->display(); - - // clang-format off - auto attribs = std::array { - EGL_WIDTH, this->width, - EGL_HEIGHT, this->height, - EGL_LINUX_DRM_FOURCC_EXT, this->format, - EGL_DMA_BUF_PLANE0_FD_EXT, this->planes[0].fd, // NOLINT - EGL_DMA_BUF_PLANE0_OFFSET_EXT, this->planes[0].offset, // NOLINT - EGL_DMA_BUF_PLANE0_PITCH_EXT, this->planes[0].stride, // NOLINT - EGL_NONE - }; - // clang-format on - - auto* eglImage = - eglCreateImage(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); - - if (eglImage == EGL_NO_IMAGE) { - qFatal() << "failed to make egl image" << eglGetError(); - return nullptr; - } - - window->beginExternalCommands(); - GLuint glTexture = 0; - glGenTextures(1, &glTexture); - - glBindTexture(GL_TEXTURE_2D, glTexture); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage); - glBindTexture(GL_TEXTURE_2D, 0); - window->endExternalCommands(); - - auto* qsgTexture = QNativeInterface::QSGOpenGLTexture::fromNative( - glTexture, - window, - QSize(static_cast(this->width), static_cast(this->height)) - ); - - auto* tex = new WlDmaBufferQSGTexture(eglImage, glTexture, qsgTexture); - qCDebug(logDmabuf) << "Created WlDmaBufferQSGTexture" << tex << "from" << this; - return tex; -} - -WlDmaBufferQSGTexture::~WlDmaBufferQSGTexture() { - auto* context = QOpenGLContext::currentContext(); - auto* display = context->nativeInterface()->display(); - - if (this->glTexture) glDeleteTextures(1, &this->glTexture); - if (this->eglImage) eglDestroyImage(display, this->eglImage); - delete this->qsgTexture; - - qCDebug(logDmabuf) << "WlDmaBufferQSGTexture" << this << "destroyed."; -} - -} // namespace qs::wayland::buffer::dmabuf diff --git a/src/wayland/buffer/dmabuf.hpp b/src/wayland/buffer/dmabuf.hpp deleted file mode 100644 index a05e82a1..00000000 --- a/src/wayland/buffer/dmabuf.hpp +++ /dev/null @@ -1,235 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "manager.hpp" -#include "qsg.hpp" - -namespace qs::wayland::buffer { -class WlBufferManagerPrivate; -} - -namespace qs::wayland::buffer::dmabuf { - -class LinuxDmabufManager; -class FourCCStr { -public: - explicit FourCCStr(uint32_t code) - : chars( - {static_cast(code >> 0 & 0xff), - static_cast(code >> 8 & 0xff), - static_cast(code >> 16 & 0xff), - static_cast(code >> 24 & 0xff), - '\0'} - ) { - for (auto i = 3; i != 0; i--) { - if (chars[i] == ' ') chars[i] = '\0'; - else break; - } - } - - [[nodiscard]] const char* cStr() const { return this->chars.data(); } - -private: - std::array chars {}; -}; - -class FourCCModStr { -public: - explicit FourCCModStr(uint64_t code): drmStr(drmGetFormatModifierName(code)) {} - ~FourCCModStr() { - if (this->drmStr) drmFree(this->drmStr); - } - - Q_DISABLE_COPY_MOVE(FourCCModStr); - - [[nodiscard]] const char* cStr() const { return this->drmStr; } - -private: - char* drmStr; -}; - -QDebug& operator<<(QDebug& debug, const FourCCStr& fourcc); -QDebug& operator<<(QDebug& debug, const FourCCModStr& fourcc); - -class GbmDeviceHandle { -public: - GbmDeviceHandle() = default; - GbmDeviceHandle(gbm_device* device): device(device) {} - - GbmDeviceHandle(GbmDeviceHandle&& other) noexcept: device(other.device) { - other.device = nullptr; - } - - ~GbmDeviceHandle(); - Q_DISABLE_COPY(GbmDeviceHandle); - - GbmDeviceHandle& operator=(GbmDeviceHandle&& other) noexcept { - this->device = other.device; - other.device = nullptr; - return *this; - } - - [[nodiscard]] gbm_device* operator*() const { return this->device; } - [[nodiscard]] operator bool() const { return this->device; } - -private: - gbm_device* device = nullptr; -}; - -class WlDmaBufferQSGTexture: public WlBufferQSGTexture { -public: - ~WlDmaBufferQSGTexture() override; - Q_DISABLE_COPY_MOVE(WlDmaBufferQSGTexture); - - [[nodiscard]] QSGTexture* texture() const override { return this->qsgTexture; } - -private: - WlDmaBufferQSGTexture(EGLImage eglImage, GLuint glTexture, QSGTexture* qsgTexture) - : eglImage(eglImage) - , glTexture(glTexture) - , qsgTexture(qsgTexture) {} - - EGLImage eglImage; - GLuint glTexture; - QSGTexture* qsgTexture; - - friend class WlDmaBuffer; -}; - -class WlDmaBuffer: public WlBuffer { -public: - ~WlDmaBuffer() override; - Q_DISABLE_COPY(WlDmaBuffer); - WlDmaBuffer(WlDmaBuffer&& other) noexcept; - WlDmaBuffer& operator=(WlDmaBuffer&& other) noexcept; - - [[nodiscard]] wl_buffer* buffer() const override { return this->mBuffer; } - - [[nodiscard]] QSize size() const override { - return QSize(static_cast(this->width), static_cast(this->height)); - } - - [[nodiscard]] bool isCompatible(const WlBufferRequest& request) const override; - [[nodiscard]] WlBufferQSGTexture* createQsgTexture(QQuickWindow* window) const override; - -private: - WlDmaBuffer() noexcept = default; - - struct Plane { - int fd = 0; - uint32_t offset = 0; - uint32_t stride = 0; - }; - - GbmDeviceHandle device; - gbm_bo* bo = nullptr; - wl_buffer* mBuffer = nullptr; - int planeCount = 0; - Plane* planes = nullptr; - uint32_t format = 0; - uint64_t modifier = 0; - uint32_t width = 0; - uint32_t height = 0; - - friend class LinuxDmabufManager; - friend QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer); -}; - -QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer); - -struct LinuxDmabufModifiers { - StackList modifiers; - bool implicit = false; -}; - -struct LinuxDmabufFormatSelection { - bool sorted = false; - StackList, 2> formats; - void ensureSorted(); -}; - -struct LinuxDmabufTranche { - dev_t device = 0; - uint32_t flags = 0; - LinuxDmabufFormatSelection formats; -}; - -class LinuxDmabufFeedback: public QtWayland::zwp_linux_dmabuf_feedback_v1 { -public: - explicit LinuxDmabufFeedback(::zwp_linux_dmabuf_feedback_v1* feedback); - ~LinuxDmabufFeedback() override; - Q_DISABLE_COPY_MOVE(LinuxDmabufFeedback); - -protected: - void zwp_linux_dmabuf_feedback_v1_main_device(wl_array* device) override; - void zwp_linux_dmabuf_feedback_v1_format_table(int32_t fd, uint32_t size) override; - void zwp_linux_dmabuf_feedback_v1_tranche_target_device(wl_array* device) override; - void zwp_linux_dmabuf_feedback_v1_tranche_flags(uint32_t flags) override; - void zwp_linux_dmabuf_feedback_v1_tranche_formats(wl_array* indices) override; - void zwp_linux_dmabuf_feedback_v1_tranche_done() override; - void zwp_linux_dmabuf_feedback_v1_done() override; - -private: - dev_t mainDevice = 0; - QList tranches; - void* formatTable = nullptr; - uint32_t formatTableSize = 0; -}; - -class LinuxDmabufManager - : public QWaylandClientExtensionTemplate - , public QtWayland::zwp_linux_dmabuf_v1 { -public: - explicit LinuxDmabufManager(WlBufferManagerPrivate* manager); - - [[nodiscard]] WlBuffer* createDmabuf(const WlBufferRequest& request); - - [[nodiscard]] WlBuffer* createDmabuf( - GbmDeviceHandle& device, - uint32_t format, - const LinuxDmabufModifiers& modifiers, - uint32_t width, - uint32_t height - ); - -private: - struct SharedGbmDevice { - dev_t handle = 0; - std::string renderNode; - gbm_device* device = nullptr; - qsizetype refcount = 0; - }; - - void feedbackDone(); - - GbmDeviceHandle getGbmDevice(dev_t handle); - void unrefGbmDevice(gbm_device* device); - GbmDeviceHandle dupHandle(const GbmDeviceHandle& handle); - - QList tranches; - QList gbmDevices; - WlBufferManagerPrivate* manager; - - friend class LinuxDmabufFeedback; - friend class GbmDeviceHandle; -}; - -} // namespace qs::wayland::buffer::dmabuf diff --git a/src/wayland/buffer/manager.cpp b/src/wayland/buffer/manager.cpp deleted file mode 100644 index 4c2e267e..00000000 --- a/src/wayland/buffer/manager.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "manager.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dmabuf.hpp" -#include "manager_p.hpp" -#include "qsg.hpp" -#include "shm.hpp" - -namespace qs::wayland::buffer { - -namespace { -Q_LOGGING_CATEGORY(logBuffer, "quickshell.wayland.buffer", QtWarningMsg); -} - -WlBuffer* WlBufferSwapchain::createBackbuffer(const WlBufferRequest& request, bool* newBuffer) { - auto& buffer = this->presentSecondBuffer ? this->buffer1 : this->buffer2; - - if (!buffer || !buffer->isCompatible(request)) { - buffer.reset(WlBufferManager::instance()->createBuffer(request)); - if (newBuffer) *newBuffer = true; - } - - return buffer.get(); -} - -WlBufferManager::WlBufferManager(): p(new WlBufferManagerPrivate(this)) {} - -WlBufferManager::~WlBufferManager() { delete this->p; } - -WlBufferManager* WlBufferManager::instance() { - static auto* instance = new WlBufferManager(); - return instance; -} - -bool WlBufferManager::isReady() const { return this->p->mReady; } - -[[nodiscard]] WlBuffer* WlBufferManager::createBuffer(const WlBufferRequest& request) { - static const bool dmabufDisabled = qEnvironmentVariableIsSet("QS_DISABLE_DMABUF"); - - qCDebug(logBuffer).nospace() << "Creating buffer from request at " << request.width << 'x' - << request.height; - qCDebug(logBuffer).nospace() << " Dmabuf requests on device " << request.dmabuf.device - << " (disabled: " << dmabufDisabled << ')'; - - for (const auto& [format, modifiers]: request.dmabuf.formats) { - qCDebug(logBuffer) << " Format" << dmabuf::FourCCStr(format); - - for (const auto& modifier: modifiers) { - qCDebug(logBuffer) << " Explicit Modifier" << dmabuf::FourCCModStr(modifier); - } - } - - qCDebug(logBuffer).nospace() << " Shm requests"; - - for (const auto& format: request.shm.formats) { - qCDebug(logBuffer) << " Format" << format; - } - - if (!dmabufDisabled) { - if (auto* buf = this->p->dmabuf.createDmabuf(request)) return buf; - qCWarning(logBuffer) << "DMA buffer creation failed, falling back to SHM."; - } - - return shm::ShmbufManager::createShmbuf(request); -} - -WlBufferManagerPrivate::WlBufferManagerPrivate(WlBufferManager* manager) - : manager(manager) - , dmabuf(this) {} - -void WlBufferManagerPrivate::dmabufReady() { - this->mReady = true; - emit this->manager->ready(); -} - -WlBufferQSGDisplayNode::WlBufferQSGDisplayNode(QQuickWindow* window) - : window(window) - , imageNode(window->createImageNode()) { - this->appendChildNode(this->imageNode); -} - -void WlBufferQSGDisplayNode::setRect(const QRectF& rect) { - const auto* buffer = (this->presentSecondBuffer ? this->buffer2 : this->buffer1).first; - if (!buffer) return; - - auto matrix = QMatrix4x4(); - auto center = rect.center(); - auto centerX = static_cast(center.x()); - auto centerY = static_cast(center.y()); - matrix.translate(centerX, centerY); - buffer->transform.apply(matrix); - matrix.translate(-centerX, -centerY); - - auto viewRect = matrix.mapRect(rect); - auto bufferSize = buffer->size().toSizeF(); - - bufferSize.scale(viewRect.width(), viewRect.height(), Qt::KeepAspectRatio); - this->imageNode->setRect( - viewRect.x() + viewRect.width() / 2 - bufferSize.width() / 2, - viewRect.y() + viewRect.height() / 2 - bufferSize.height() / 2, - bufferSize.width(), - bufferSize.height() - ); - - this->setMatrix(matrix); -} - -void WlBufferQSGDisplayNode::syncSwapchain(const WlBufferSwapchain& swapchain) { - auto* buffer = swapchain.frontbuffer(); - auto& texture = swapchain.presentSecondBuffer ? this->buffer2 : this->buffer1; - - if (swapchain.presentSecondBuffer == this->presentSecondBuffer && texture.first == buffer) { - return; - } - - this->presentSecondBuffer = swapchain.presentSecondBuffer; - - if (texture.first == buffer) { - texture.second->sync(texture.first, this->window); - } else { - texture.first = buffer; - texture.second.reset(buffer->createQsgTexture(this->window)); - } - - this->imageNode->setTexture(texture.second->texture()); -} - -} // namespace qs::wayland::buffer diff --git a/src/wayland/buffer/manager.hpp b/src/wayland/buffer/manager.hpp deleted file mode 100644 index c3f62a0d..00000000 --- a/src/wayland/buffer/manager.hpp +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/stacklist.hpp" - -class QQuickWindow; - -namespace qs::wayland::buffer { - -class WlBufferManagerPrivate; -class WlBufferQSGTexture; - -struct WlBufferTransform { - enum Transform : uint8_t { - Normal0 = 0, - Normal90 = 1, - Normal180 = 2, - Normal270 = 3, - Flipped0 = 4, - Flipped90 = 5, - Flipped180 = 6, - Flipped270 = 7, - } transform = Normal0; - - WlBufferTransform() = default; - WlBufferTransform(uint8_t transform): transform(static_cast(transform)) {} - - [[nodiscard]] int degrees() const { return 90 * (this->transform & 0b11111011); } - [[nodiscard]] bool flip() const { return this->transform & 0b00000100; } - - void apply(QMatrix4x4& matrix) const { - matrix.rotate(this->flip() ? 180 : 0, 0, 1, 0); - matrix.rotate(static_cast(this->degrees()), 0, 0, 1); - } -}; - -struct WlBufferRequest { - uint32_t width = 0; - uint32_t height = 0; - - struct DmaFormat { - DmaFormat() = default; - DmaFormat(uint32_t format): format(format) {} - - uint32_t format = 0; - StackList modifiers; - }; - - struct { - StackList formats; - } shm; - - struct { - dev_t device = 0; - StackList formats; - } dmabuf; -}; - -class WlBuffer { -public: - virtual ~WlBuffer() = default; - Q_DISABLE_COPY_MOVE(WlBuffer); - - [[nodiscard]] virtual wl_buffer* buffer() const = 0; - [[nodiscard]] virtual QSize size() const = 0; - [[nodiscard]] virtual bool isCompatible(const WlBufferRequest& request) const = 0; - [[nodiscard]] operator bool() const { return this->buffer(); } - - // Must be called from render thread. - [[nodiscard]] virtual WlBufferQSGTexture* createQsgTexture(QQuickWindow* window) const = 0; - - WlBufferTransform transform; - -protected: - explicit WlBuffer() = default; -}; - -class WlBufferSwapchain { -public: - [[nodiscard]] WlBuffer* - createBackbuffer(const WlBufferRequest& request, bool* newBuffer = nullptr); - - void swapBuffers() { this->presentSecondBuffer = !this->presentSecondBuffer; } - - [[nodiscard]] WlBuffer* backbuffer() const { - return this->presentSecondBuffer ? this->buffer1.get() : this->buffer2.get(); - } - - [[nodiscard]] WlBuffer* frontbuffer() const { - return this->presentSecondBuffer ? this->buffer2.get() : this->buffer1.get(); - } - -private: - std::unique_ptr buffer1; - std::unique_ptr buffer2; - bool presentSecondBuffer = false; - - friend class WlBufferQSGDisplayNode; -}; - -class WlBufferManager: public QObject { - Q_OBJECT; - -public: - ~WlBufferManager() override; - Q_DISABLE_COPY_MOVE(WlBufferManager); - - static WlBufferManager* instance(); - - [[nodiscard]] bool isReady() const; - [[nodiscard]] WlBuffer* createBuffer(const WlBufferRequest& request); - -signals: - void ready(); - -private: - explicit WlBufferManager(); - - WlBufferManagerPrivate* p; -}; - -} // namespace qs::wayland::buffer diff --git a/src/wayland/buffer/manager_p.hpp b/src/wayland/buffer/manager_p.hpp deleted file mode 100644 index 55f5e667..00000000 --- a/src/wayland/buffer/manager_p.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "dmabuf.hpp" -#include "manager.hpp" - -namespace qs::wayland::buffer { - -class WlBufferManagerPrivate { -public: - explicit WlBufferManagerPrivate(WlBufferManager* manager); - - void dmabufReady(); - - WlBufferManager* manager; - dmabuf::LinuxDmabufManager dmabuf; - - bool mReady = false; -}; - -} // namespace qs::wayland::buffer diff --git a/src/wayland/buffer/qsg.hpp b/src/wayland/buffer/qsg.hpp deleted file mode 100644 index c230cfee..00000000 --- a/src/wayland/buffer/qsg.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include "manager.hpp" - -namespace qs::wayland::buffer { - -// Interact only from QSG thread. -class WlBufferQSGTexture { -public: - virtual ~WlBufferQSGTexture() = default; - Q_DISABLE_COPY_MOVE(WlBufferQSGTexture); - - [[nodiscard]] virtual QSGTexture* texture() const = 0; - virtual void sync(const WlBuffer* /*buffer*/, QQuickWindow* /*window*/) {} - -protected: - WlBufferQSGTexture() = default; -}; - -// Interact only from QSG thread. -class WlBufferQSGDisplayNode: public QSGTransformNode { -public: - explicit WlBufferQSGDisplayNode(QQuickWindow* window); - - void syncSwapchain(const WlBufferSwapchain& swapchain); - void setRect(const QRectF& rect); - -private: - QQuickWindow* window; - QSGImageNode* imageNode; - QPair> buffer1; - QPair> buffer2; - bool presentSecondBuffer = false; -}; - -} // namespace qs::wayland::buffer diff --git a/src/wayland/buffer/shm.cpp b/src/wayland/buffer/shm.cpp deleted file mode 100644 index 59a8e914..00000000 --- a/src/wayland/buffer/shm.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "shm.hpp" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "manager.hpp" - -namespace qs::wayland::buffer::shm { - -namespace { -Q_LOGGING_CATEGORY(logShm, "quickshell.wayland.buffer.shm", QtWarningMsg); -} - -bool WlShmBuffer::isCompatible(const WlBufferRequest& request) const { - if (QSize(static_cast(request.width), static_cast(request.height)) != this->size()) { - return false; - } - - auto matchingFormat = std::ranges::find(request.shm.formats, this->format); - return matchingFormat != request.shm.formats.end(); -} - -QDebug& operator<<(QDebug& debug, const WlShmBuffer* buffer) { - auto saver = QDebugStateSaver(debug); - debug.nospace(); - - if (buffer) { - auto fmt = QtWaylandClient::QWaylandShm::formatFrom( - static_cast<::wl_shm_format>(buffer->format) // NOLINT - ); - - debug << "WlShmBuffer(" << static_cast(buffer) << ", size=" << buffer->size() - << ", format=" << fmt << ')'; - } else { - debug << "WlShmBuffer(0x0)"; - } - - return debug; -} - -WlShmBuffer::~WlShmBuffer() { qCDebug(logShm) << "Destroyed" << this; } - -WlBufferQSGTexture* WlShmBuffer::createQsgTexture(QQuickWindow* window) const { - auto* texture = new WlShmBufferQSGTexture(); - - // If the QWaylandShmBuffer is destroyed before the QSGTexture, we'll hit a UAF - // in the render thread. - texture->shmBuffer = this->shmBuffer; - - texture->qsgTexture.reset(window->createTextureFromImage(*this->shmBuffer->image())); - texture->sync(this, window); - return texture; -} - -void WlShmBufferQSGTexture::sync(const WlBuffer* /*unused*/, QQuickWindow* window) { - // This is both dumb and expensive. We should use an RHI texture and render images into - // it more intelligently, but shm buffers are already a horribly slow fallback path, - // to the point where it barely matters. - this->qsgTexture.reset(window->createTextureFromImage(*this->shmBuffer->image())); -} - -WlBuffer* ShmbufManager::createShmbuf(const WlBufferRequest& request) { - if (request.shm.formats.isEmpty()) return nullptr; - - static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance(); - auto* display = waylandIntegration->display(); - - // Its probably fine... - auto format = request.shm.formats[0]; - - auto* buffer = new WlShmBuffer( - new QtWaylandClient::QWaylandShmBuffer( - display, - QSize(static_cast(request.width), static_cast(request.height)), - QtWaylandClient::QWaylandShm::formatFrom(static_cast<::wl_shm_format>(format)) // NOLINT - ), - format - ); - - qCDebug(logShm) << "Created shmbuf" << buffer; - return buffer; -} -} // namespace qs::wayland::buffer::shm diff --git a/src/wayland/buffer/shm.hpp b/src/wayland/buffer/shm.hpp deleted file mode 100644 index f3597cbc..00000000 --- a/src/wayland/buffer/shm.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include "manager.hpp" -#include "qsg.hpp" - -namespace qs::wayland::buffer::shm { - -class WlShmBuffer: public WlBuffer { -public: - ~WlShmBuffer() override; - Q_DISABLE_COPY_MOVE(WlShmBuffer); - - [[nodiscard]] wl_buffer* buffer() const override { return this->shmBuffer->buffer(); } - [[nodiscard]] QSize size() const override { return this->shmBuffer->size(); } - [[nodiscard]] bool isCompatible(const WlBufferRequest& request) const override; - [[nodiscard]] WlBufferQSGTexture* createQsgTexture(QQuickWindow* window) const override; - -private: - WlShmBuffer(QtWaylandClient::QWaylandShmBuffer* shmBuffer, uint32_t format) - : shmBuffer(shmBuffer) - , format(format) {} - - std::shared_ptr shmBuffer; - uint32_t format; - - friend class WlShmBufferQSGTexture; - friend class ShmbufManager; - friend QDebug& operator<<(QDebug& debug, const WlShmBuffer* buffer); -}; - -QDebug& operator<<(QDebug& debug, const WlShmBuffer* buffer); - -class WlShmBufferQSGTexture: public WlBufferQSGTexture { -public: - [[nodiscard]] QSGTexture* texture() const override { return this->qsgTexture.get(); } - void sync(const WlBuffer* buffer, QQuickWindow* window) override; - -private: - WlShmBufferQSGTexture() = default; - - std::shared_ptr shmBuffer; - std::unique_ptr qsgTexture; - - friend class WlShmBuffer; -}; - -class ShmbufManager { -public: - [[nodiscard]] static WlBuffer* createShmbuf(const WlBufferRequest& request); -}; - -} // namespace qs::wayland::buffer::shm diff --git a/src/wayland/hyprland/focus_grab/CMakeLists.txt b/src/wayland/hyprland/focus_grab/CMakeLists.txt index 95825628..04b6e0a9 100644 --- a/src/wayland/hyprland/focus_grab/CMakeLists.txt +++ b/src/wayland/hyprland/focus_grab/CMakeLists.txt @@ -14,11 +14,13 @@ qs_add_module_deps_light(quickshell-hyprland-focus-grab Quickshell) install_qml_module(quickshell-hyprland-focus-grab) -wl_proto(wlp-hyprland-focus-grab hyprland-focus-grab-v1 "${CMAKE_CURRENT_SOURCE_DIR}") +wl_proto(quickshell-hyprland-focus-grab + hyprland-focus-grab-v1 + "${CMAKE_CURRENT_SOURCE_DIR}/hyprland-focus-grab-v1.xml" +) target_link_libraries(quickshell-hyprland-focus-grab PRIVATE Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - wlp-hyprland-focus-grab ) qs_module_pch(quickshell-hyprland-focus-grab SET large) diff --git a/src/wayland/hyprland/global_shortcuts/CMakeLists.txt b/src/wayland/hyprland/global_shortcuts/CMakeLists.txt index ece0037e..986f2d8e 100644 --- a/src/wayland/hyprland/global_shortcuts/CMakeLists.txt +++ b/src/wayland/hyprland/global_shortcuts/CMakeLists.txt @@ -12,12 +12,14 @@ qt_add_qml_module(quickshell-hyprland-global-shortcuts install_qml_module(quickshell-hyprland-global-shortcuts) -wl_proto(wlp-hyprland-shortcuts hyprland-global-shortcuts-v1 "${CMAKE_CURRENT_SOURCE_DIR}") +wl_proto(quickshell-hyprland-global-shortcuts + hyprland-global-shortcuts-v1 + "${CMAKE_CURRENT_SOURCE_DIR}/hyprland-global-shortcuts-v1.xml" +) target_link_libraries(quickshell-hyprland-global-shortcuts PRIVATE Qt::Qml Qt::WaylandClient Qt::WaylandClientPrivate wayland-client Qt::Quick # pch - wlp-hyprland-shortcuts ) qs_module_pch(quickshell-hyprland-global-shortcuts) diff --git a/src/wayland/hyprland/ipc/connection.cpp b/src/wayland/hyprland/ipc/connection.cpp index 794ecff6..c33ebd60 100644 --- a/src/wayland/hyprland/ipc/connection.cpp +++ b/src/wayland/hyprland/ipc/connection.cpp @@ -26,10 +26,8 @@ namespace qs::hyprland::ipc { -namespace { Q_LOGGING_CATEGORY(logHyprlandIpc, "quickshell.hyprland.ipc", QtWarningMsg); Q_LOGGING_CATEGORY(logHyprlandIpcEvents, "quickshell.hyprland.ipc.events", QtWarningMsg); -} // namespace HyprlandIpc::HyprlandIpc() { auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE"); @@ -243,8 +241,9 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { const auto& mList = this->mMonitors.valueList(); auto name = QString::fromUtf8(event->data); - auto monitorIter = - std::ranges::find_if(mList, [name](const HyprlandMonitor* m) { return m->name() == name; }); + auto monitorIter = std::find_if(mList.begin(), mList.end(), [name](const HyprlandMonitor* m) { + return m->name() == name; + }); if (monitorIter == mList.end()) { qCWarning(logHyprlandIpc) << "Got removal for monitor" << name @@ -293,8 +292,9 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { const auto& mList = this->mWorkspaces.valueList(); - auto workspaceIter = - std::ranges::find_if(mList, [id](const HyprlandWorkspace* m) { return m->id() == id; }); + auto workspaceIter = std::find_if(mList.begin(), mList.end(), [id](const HyprlandWorkspace* m) { + return m->id() == id; + }); if (workspaceIter == mList.end()) { qCWarning(logHyprlandIpc) << "Got removal for workspace id" << id << "name" << name @@ -352,22 +352,6 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) { auto* monitor = this->findMonitorByName(monitorName, true); workspace->setMonitor(monitor); - } else if (event->name == "renameworkspace") { - auto args = event->parseView(2); - auto id = args.at(0).toInt(); - auto name = QString::fromUtf8(args.at(1)); - - const auto& mList = this->mWorkspaces.valueList(); - - auto workspaceIter = - std::ranges::find_if(mList, [id](const HyprlandWorkspace* m) { return m->id() == id; }); - - if (workspaceIter == mList.end()) return; - - qCDebug(logHyprlandIpc) << "Workspace with id" << id << "renamed from" - << (*workspaceIter)->name() << "to" << name; - - (*workspaceIter)->setName(name); } } @@ -375,8 +359,9 @@ HyprlandWorkspace* HyprlandIpc::findWorkspaceByName(const QString& name, bool createIfMissing, qint32 id) { const auto& mList = this->mWorkspaces.valueList(); - auto workspaceIter = - std::ranges::find_if(mList, [name](const HyprlandWorkspace* m) { return m->name() == name; }); + auto workspaceIter = std::find_if(mList.begin(), mList.end(), [name](const HyprlandWorkspace* m) { + return m->name() == name; + }); if (workspaceIter != mList.end()) { return *workspaceIter; @@ -410,9 +395,10 @@ void HyprlandIpc::refreshWorkspaces(bool canCreate) { auto object = entry.toObject().toVariantMap(); auto name = object.value("name").toString(); - auto workspaceIter = std::ranges::find_if(mList, [name](const HyprlandWorkspace* m) { - return m->name() == name; - }); + auto workspaceIter = + std::find_if(mList.begin(), mList.end(), [name](const HyprlandWorkspace* m) { + return m->name() == name; + }); auto* workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter; auto existed = workspace != nullptr; @@ -450,8 +436,9 @@ HyprlandMonitor* HyprlandIpc::findMonitorByName(const QString& name, bool createIfMissing, qint32 id) { const auto& mList = this->mMonitors.valueList(); - auto monitorIter = - std::ranges::find_if(mList, [name](const HyprlandMonitor* m) { return m->name() == name; }); + auto monitorIter = std::find_if(mList.begin(), mList.end(), [name](const HyprlandMonitor* m) { + return m->name() == name; + }); if (monitorIter != mList.end()) { return *monitorIter; @@ -519,7 +506,7 @@ void HyprlandIpc::refreshMonitors(bool canCreate) { auto object = entry.toObject().toVariantMap(); auto name = object.value("name").toString(); - auto monitorIter = std::ranges::find_if(mList, [name](const HyprlandMonitor* m) { + auto monitorIter = std::find_if(mList.begin(), mList.end(), [name](const HyprlandMonitor* m) { return m->name() == name; }); diff --git a/src/wayland/hyprland/ipc/workspace.cpp b/src/wayland/hyprland/ipc/workspace.cpp index 153dea6b..fbf8477f 100644 --- a/src/wayland/hyprland/ipc/workspace.cpp +++ b/src/wayland/hyprland/ipc/workspace.cpp @@ -11,15 +11,7 @@ namespace qs::hyprland::ipc { qint32 HyprlandWorkspace::id() const { return this->mId; } - QString HyprlandWorkspace::name() const { return this->mName; } - -void HyprlandWorkspace::setName(QString name) { - if (name == this->mName) return; - this->mName = std::move(name); - emit this->nameChanged(); -} - QVariantMap HyprlandWorkspace::lastIpcObject() const { return this->mLastIpcObject; } void HyprlandWorkspace::updateInitial(qint32 id, QString name) { diff --git a/src/wayland/hyprland/ipc/workspace.hpp b/src/wayland/hyprland/ipc/workspace.hpp index 5eedfe22..dab01eb3 100644 --- a/src/wayland/hyprland/ipc/workspace.hpp +++ b/src/wayland/hyprland/ipc/workspace.hpp @@ -32,14 +32,11 @@ public: void updateFromObject(QVariantMap object); [[nodiscard]] qint32 id() const; - [[nodiscard]] QString name() const; - void setName(QString name); - [[nodiscard]] QVariantMap lastIpcObject() const; - [[nodiscard]] HyprlandMonitor* monitor() const; void setMonitor(HyprlandMonitor* monitor); + [[nodiscard]] HyprlandMonitor* monitor() const; signals: void idChanged(); diff --git a/src/wayland/hyprland/surface/CMakeLists.txt b/src/wayland/hyprland/surface/CMakeLists.txt index 7f889c96..04fa5c58 100644 --- a/src/wayland/hyprland/surface/CMakeLists.txt +++ b/src/wayland/hyprland/surface/CMakeLists.txt @@ -12,11 +12,13 @@ qt_add_qml_module(quickshell-hyprland-surface-extensions install_qml_module(quickshell-hyprland-surface-extensions) -wl_proto(wlp-hyprland-surface hyprland-surface-v1 "${CMAKE_CURRENT_SOURCE_DIR}") +wl_proto(quickshell-hyprland-surface-extensions + hyprland-surface-v1 + "${CMAKE_CURRENT_SOURCE_DIR}/hyprland-surface-v1.xml" +) target_link_libraries(quickshell-hyprland-surface-extensions PRIVATE Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - wlp-hyprland-surface ) qs_module_pch(quickshell-hyprland-surface-extensions) 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..8477de55 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" @@ -31,6 +28,7 @@ HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) { } } + qDebug() << "hlwindow for" << proxyWindow; if (!proxyWindow) return nullptr; return new HyprlandWindow(proxyWindow); } @@ -43,15 +41,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 +61,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->mWaylandWindow); } 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 +87,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 +123,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->mWaylandWindow); } } @@ -245,9 +145,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/init.cpp b/src/wayland/init.cpp index 3f2a18b4..7c024c42 100644 --- a/src/wayland/init.cpp +++ b/src/wayland/init.cpp @@ -10,8 +10,8 @@ #include "wlr_layershell.hpp" #endif -void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage) -void installPopupPositioner(); // NOLINT(misc-use-internal-linkage) +void installPlatformMenuHook(); +void installPopupPositioner(); namespace { diff --git a/src/wayland/module.md b/src/wayland/module.md index db9bfb5a..d6376e39 100644 --- a/src/wayland/module.md +++ b/src/wayland/module.md @@ -5,6 +5,5 @@ headers = [ "wlr_layershell.hpp", "session_lock.hpp", "toplevel_management/qml.hpp", - "screencopy/view.hpp", ] ----- diff --git a/src/wayland/platformmenu.cpp b/src/wayland/platformmenu.cpp index 117d028d..e64e8880 100644 --- a/src/wayland/platformmenu.cpp +++ b/src/wayland/platformmenu.cpp @@ -12,8 +12,6 @@ using namespace qs::menu::platform; -namespace { - // fixes positioning of submenus when hitting screen edges void platformMenuHook(PlatformMenuQMenu* menu) { auto* window = menu->windowHandle(); @@ -64,6 +62,4 @@ void platformMenuHook(PlatformMenuQMenu* menu) { window->setProperty("_q_waylandPopupConstraintAdjustment", constraintAdjustment); } -} // namespace - void installPlatformMenuHook() { PlatformMenuEntry::registerCreationHook(&platformMenuHook); } diff --git a/src/wayland/screencopy/CMakeLists.txt b/src/wayland/screencopy/CMakeLists.txt deleted file mode 100644 index 97c4209e..00000000 --- a/src/wayland/screencopy/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -qt_add_library(quickshell-wayland-screencopy STATIC - manager.cpp - view.cpp -) - -qt_add_qml_module(quickshell-wayland-screencopy - URI Quickshell.Wayland._Screencopy - VERSION 0.1 - DEPENDENCIES QtQuick -) - -install_qml_module(quickshell-wayland-screencopy) - -set(SCREENCOPY_MODULES) - -if (SCREENCOPY_ICC) - add_subdirectory(image_copy_capture) - list(APPEND SCREENCOPY_MODULES quickshell-wayland-screencopy-icc) -endif() - -if (SCREENCOPY_WLR) - add_subdirectory(wlr_screencopy) - list(APPEND SCREENCOPY_MODULES quickshell-wayland-screencopy-wlr) -endif() - -if (SCREENCOPY_HYPRLAND_TOPLEVEL) - add_subdirectory(hyprland_screencopy) - list(APPEND SCREENCOPY_MODULES quickshell-wayland-screencopy-hyprland) -endif() - -configure_file(build.hpp.in build.hpp @ONLY) -target_include_directories(quickshell-wayland-screencopy PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -target_link_libraries(quickshell-wayland-screencopy PRIVATE - Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - quickshell-wayland-buffer - ${SCREENCOPY_MODULES} -) - -qs_module_pch(quickshell-wayland-screencopy SET large) - -target_link_libraries(quickshell PRIVATE quickshell-wayland-screencopyplugin) diff --git a/src/wayland/screencopy/build.hpp.in b/src/wayland/screencopy/build.hpp.in deleted file mode 100644 index 9276daaa..00000000 --- a/src/wayland/screencopy/build.hpp.in +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -// NOLINTBEGIN -#cmakedefine01 SCREENCOPY_ICC -#cmakedefine01 SCREENCOPY_WLR -#cmakedefine01 SCREENCOPY_HYPRLAND_TOPLEVEL -// NOLINTEND diff --git a/src/wayland/screencopy/hyprland_screencopy/CMakeLists.txt b/src/wayland/screencopy/hyprland_screencopy/CMakeLists.txt deleted file mode 100644 index d06a91e5..00000000 --- a/src/wayland/screencopy/hyprland_screencopy/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -qt_add_library(quickshell-wayland-screencopy-hyprland STATIC - hyprland_screencopy.cpp -) - -wl_proto(wlp-hyprland-screencopy hyprland-toplevel-export-v1 "${CMAKE_CURRENT_SOURCE_DIR}") - -target_link_libraries(quickshell-wayland-screencopy-hyprland PRIVATE - Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - Qt::Quick # for pch -) - -target_link_libraries(quickshell-wayland-screencopy-hyprland PUBLIC - wlp-hyprland-screencopy wlp-foreign-toplevel -) - -qs_pch(quickshell-wayland-screencopy-hyprland SET large) diff --git a/src/wayland/screencopy/hyprland_screencopy/hyprland-toplevel-export-v1.xml b/src/wayland/screencopy/hyprland_screencopy/hyprland-toplevel-export-v1.xml deleted file mode 100644 index b1185aa5..00000000 --- a/src/wayland/screencopy/hyprland_screencopy/hyprland-toplevel-export-v1.xml +++ /dev/null @@ -1,228 +0,0 @@ - - - - Copyright © 2022 Vaxry - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - - This protocol allows clients to ask for exporting another toplevel's - surface(s) to a buffer. - - Particularly useful for sharing a single window. - - - - - This object is a manager which offers requests to start capturing from a - source. - - - - - Capture the next frame of a toplevel. (window) - - The captured frame will not contain any server-side decorations and will - ignore the compositor-set geometry, like e.g. rounded corners. - - It will contain all the subsurfaces and popups, however the latter will be clipped - to the geometry of the base surface. - - The handle parameter refers to the address of the window as seen in `hyprctl clients`. - For example, for d161e7b0 it would be 3512854448. - - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - Same as capture_toplevel, but with a zwlr_foreign_toplevel_handle_v1 handle. - - - - - - - - - - - This object represents a single frame. - - When created, a series of buffer events will be sent, each representing a - supported buffer type. The "buffer_done" event is sent afterwards to - indicate that all supported buffer types have been enumerated. The client - will then be able to send a "copy" request. If the capture is successful, - the compositor will send a "flags" followed by a "ready" event. - - wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. - - If the capture failed, the "failed" event is sent. This can happen anytime - before the "ready" event. - - Once either a "ready" or a "failed" event is received, the client should - destroy the frame. - - - - - Provides information about wl_shm buffer parameters that need to be - used for this frame. This event is sent once after the frame is created - if wl_shm buffers are supported. - - - - - - - - - - Copy the frame to the supplied buffer. The buffer must have the - correct size, see hyprland_toplevel_export_frame_v1.buffer and - hyprland_toplevel_export_frame_v1.linux_dmabuf. The buffer needs to have a - supported format. - - If the frame is successfully copied, a "flags" and a "ready" event is - sent. Otherwise, a "failed" event is sent. - - This event will wait for appropriate damage to be copied, unless the ignore_damage - arg is set to a non-zero value. - - - - - - - - This event is sent right before the ready event when ignore_damage was - not set. It may be generated multiple times for each copy - request. - - The arguments describe a box around an area that has changed since the - last copy request that was derived from the current screencopy manager - instance. - - The union of all regions received between the call to copy - and a ready event is the total damage since the prior ready event. - - - - - - - - - - - - - - - - - - - Provides flags about the frame. This event is sent once before the - "ready" event. - - - - - - - Called as soon as the frame is copied, indicating it is available - for reading. This event includes the time at which presentation happened - at. - - The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, - each component being an unsigned 32-bit value. Whole seconds are in - tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, - and the additional fractional part in tv_nsec as nanoseconds. Hence, - for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part - may have an arbitrary offset at start. - - After receiving this event, the client should destroy the object. - - - - - - - - - This event indicates that the attempted frame copy has failed. - - After receiving this event, the client should destroy the object. - - - - - - Destroys the frame. This request can be sent at any time by the client. - - - - - - Provides information about linux-dmabuf buffer parameters that need to - be used for this frame. This event is sent once after the frame is - created if linux-dmabuf buffers are supported. - - - - - - - - - This event is sent once after all buffer events have been sent. - - The client should proceed to create a buffer of one of the supported - types, and send a "copy" request. - - - - diff --git a/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.cpp b/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.cpp deleted file mode 100644 index 457f1055..00000000 --- a/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "hyprland_screencopy.hpp" -#include - -#include -#include -#include -#include -#include -#include - -#include "../../toplevel_management/handle.hpp" -#include "../manager.hpp" -#include "hyprland_screencopy_p.hpp" - -namespace qs::wayland::screencopy::hyprland { - -namespace { -Q_LOGGING_CATEGORY(logScreencopy, "quickshell.wayland.screencopy.hyprland", QtWarningMsg); -} - -HyprlandScreencopyManager::HyprlandScreencopyManager(): QWaylandClientExtensionTemplate(2) { - this->initialize(); -} - -HyprlandScreencopyManager* HyprlandScreencopyManager::instance() { - static auto* instance = new HyprlandScreencopyManager(); - return instance; -} - -ScreencopyContext* HyprlandScreencopyManager::captureToplevel( - toplevel_management::impl::ToplevelHandle* handle, - bool paintCursors -) { - return new HyprlandScreencopyContext(this, handle, paintCursors); -} - -HyprlandScreencopyContext::HyprlandScreencopyContext( - HyprlandScreencopyManager* manager, - toplevel_management::impl::ToplevelHandle* handle, - bool paintCursors -) - : manager(manager) - , handle(handle) - , paintCursors(paintCursors) { - QObject::connect( - handle, - &QObject::destroyed, - this, - &HyprlandScreencopyContext::onToplevelDestroyed - ); -} - -HyprlandScreencopyContext::~HyprlandScreencopyContext() { - if (this->object()) this->destroy(); -} - -void HyprlandScreencopyContext::onToplevelDestroyed() { - qCWarning(logScreencopy) << "Toplevel destroyed while recording. Stopping" << this; - if (this->object()) this->destroy(); - emit this->stopped(); -} - -void HyprlandScreencopyContext::captureFrame() { - if (this->object()) return; - - this->init(this->manager->capture_toplevel_with_wlr_toplevel_handle( - this->paintCursors ? 1 : 0, - this->handle->object() - )); -} - -void HyprlandScreencopyContext::hyprland_toplevel_export_frame_v1_buffer( - uint32_t format, - uint32_t width, - uint32_t height, - uint32_t /*stride*/ -) { - // While different sizes can technically be requested, that would be insane. - this->request.width = width; - this->request.height = height; - this->request.shm.formats.push(format); -} - -void HyprlandScreencopyContext::hyprland_toplevel_export_frame_v1_linux_dmabuf( - uint32_t format, - uint32_t width, - uint32_t height -) { - // While different sizes can technically be requested, that would be insane. - this->request.width = width; - this->request.height = height; - this->request.dmabuf.formats.push(format); -} - -void HyprlandScreencopyContext::hyprland_toplevel_export_frame_v1_flags(uint32_t flags) { - if (flags & HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FLAGS_Y_INVERT) { - this->mSwapchain.backbuffer()->transform = buffer::WlBufferTransform::Flipped180; - } -} - -void HyprlandScreencopyContext::hyprland_toplevel_export_frame_v1_buffer_done() { - auto* backbuffer = this->mSwapchain.createBackbuffer(this->request); - this->copy(backbuffer->buffer(), this->copiedFirstFrame ? 0 : 1); -} - -void HyprlandScreencopyContext::hyprland_toplevel_export_frame_v1_ready( - uint32_t /*tvSecHi*/, - uint32_t /*tvSecLo*/, - uint32_t /*tvNsec*/ -) { - this->destroy(); - this->copiedFirstFrame = true; - this->mSwapchain.swapBuffers(); - emit this->frameCaptured(); -} - -void HyprlandScreencopyContext::hyprland_toplevel_export_frame_v1_failed() { - qCWarning(logScreencopy) << "Ending recording due to screencopy failure for" << this; - emit this->stopped(); -} - -} // namespace qs::wayland::screencopy::hyprland diff --git a/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.hpp b/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.hpp deleted file mode 100644 index fbd08c54..00000000 --- a/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -#include "../../toplevel_management/handle.hpp" -#include "../manager.hpp" - -namespace qs::wayland::screencopy::hyprland { - -class HyprlandScreencopyManager - : public QWaylandClientExtensionTemplate - , public QtWayland::hyprland_toplevel_export_manager_v1 { -public: - ScreencopyContext* - captureToplevel(toplevel_management::impl::ToplevelHandle* handle, bool paintCursors); - - static HyprlandScreencopyManager* instance(); - -private: - explicit HyprlandScreencopyManager(); - - friend class HyprlandScreencopyContext; -}; - -} // namespace qs::wayland::screencopy::hyprland diff --git a/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy_p.hpp b/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy_p.hpp deleted file mode 100644 index 199390ec..00000000 --- a/src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy_p.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include -#include - -#include "../../toplevel_management/handle.hpp" -#include "../manager.hpp" - -namespace qs::wayland::screencopy::hyprland { - -class HyprlandScreencopyManager; - -class HyprlandScreencopyContext - : public ScreencopyContext - , public QtWayland::hyprland_toplevel_export_frame_v1 { -public: - explicit HyprlandScreencopyContext( - HyprlandScreencopyManager* manager, - toplevel_management::impl::ToplevelHandle* handle, - bool paintCursors - ); - - ~HyprlandScreencopyContext() override; - Q_DISABLE_COPY_MOVE(HyprlandScreencopyContext); - - void captureFrame() override; - -protected: - // clang-format off - void hyprland_toplevel_export_frame_v1_buffer(uint32_t format, uint32_t width, uint32_t height, uint32_t stride) override; - void hyprland_toplevel_export_frame_v1_linux_dmabuf(uint32_t format, uint32_t width, uint32_t height) override; - void hyprland_toplevel_export_frame_v1_flags(uint32_t flags) override; - void hyprland_toplevel_export_frame_v1_buffer_done() override; - void hyprland_toplevel_export_frame_v1_ready(uint32_t tvSecHi, uint32_t tvSecLo, uint32_t tvNsec) override; - void hyprland_toplevel_export_frame_v1_failed() override; - // clang-format on - -private slots: - void onToplevelDestroyed(); - -private: - HyprlandScreencopyManager* manager; - buffer::WlBufferRequest request; - bool copiedFirstFrame = false; - - toplevel_management::impl::ToplevelHandle* handle; - bool paintCursors; -}; - -} // namespace qs::wayland::screencopy::hyprland diff --git a/src/wayland/screencopy/image_copy_capture/CMakeLists.txt b/src/wayland/screencopy/image_copy_capture/CMakeLists.txt deleted file mode 100644 index 954fdda3..00000000 --- a/src/wayland/screencopy/image_copy_capture/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -qt_add_library(quickshell-wayland-screencopy-icc STATIC - image_copy_capture.cpp -) - -wl_proto(wlp-ext-foreign-toplevel ext-foreign-toplevel-list-v1 "${WAYLAND_PROTOCOLS}/staging/ext-foreign-toplevel-list") -wl_proto(wlp-image-copy-capture ext-image-copy-capture-v1 "${WAYLAND_PROTOCOLS}/staging/ext-image-copy-capture") -wl_proto(wlp-image-capture-source ext-image-capture-source-v1 "${WAYLAND_PROTOCOLS}/staging/ext-image-capture-source") - -target_link_libraries(quickshell-wayland-screencopy-icc PRIVATE - Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - Qt::Quick # for pch -) - -target_link_libraries(quickshell-wayland-screencopy-icc PUBLIC - wlp-image-copy-capture wlp-image-capture-source - wlp-ext-foreign-toplevel # required for capture source to build -) - -qs_pch(quickshell-wayland-screencopy-icc SET large) diff --git a/src/wayland/screencopy/image_copy_capture/image_copy_capture.cpp b/src/wayland/screencopy/image_copy_capture/image_copy_capture.cpp deleted file mode 100644 index 649b111b..00000000 --- a/src/wayland/screencopy/image_copy_capture/image_copy_capture.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "image_copy_capture.hpp" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../manager.hpp" -#include "image_copy_capture_p.hpp" - -namespace qs::wayland::screencopy::icc { - -namespace { -Q_LOGGING_CATEGORY(logIcc, "quickshell.wayland.screencopy.icc", QtWarningMsg); -} - -using IccCaptureSession = QtWayland::ext_image_copy_capture_session_v1; -using IccCaptureFrame = QtWayland::ext_image_copy_capture_frame_v1; - -IccScreencopyContext::IccScreencopyContext(::ext_image_copy_capture_session_v1* session) - : IccCaptureSession(session) {} - -IccScreencopyContext::~IccScreencopyContext() { - if (this->IccCaptureSession::object()) { - this->IccCaptureSession::destroy(); - } - - if (this->IccCaptureFrame::object()) { - this->IccCaptureFrame::destroy(); - } -} - -void IccScreencopyContext::captureFrame() { - if (this->IccCaptureFrame::object() || this->capturePending) return; - - if (this->statePending) this->capturePending = true; - else this->doCapture(); -} - -void IccScreencopyContext::ext_image_copy_capture_session_v1_buffer_size( - uint32_t width, - uint32_t height -) { - this->clearOldState(); - - this->request.width = width; - this->request.height = height; -} - -void IccScreencopyContext::ext_image_copy_capture_session_v1_shm_format(uint32_t format) { - this->clearOldState(); - - this->request.shm.formats.push(format); -} - -void IccScreencopyContext::ext_image_copy_capture_session_v1_dmabuf_device(wl_array* device) { - this->clearOldState(); - - if (device->size != sizeof(dev_t)) { - qCFatal(logIcc) << "The size of dev_t used by the compositor and quickshell is mismatched. Try " - "recompiling both."; - } - - this->request.dmabuf.device = *reinterpret_cast(device->data); -} - -void IccScreencopyContext::ext_image_copy_capture_session_v1_dmabuf_format( - uint32_t format, - wl_array* modifiers -) { - this->clearOldState(); - - auto* modifierArray = reinterpret_cast(modifiers->data); - auto modifierCount = modifiers->size / sizeof(uint64_t); - - auto reqFormat = buffer::WlBufferRequest::DmaFormat(format); - - for (uint16_t i = 0; i != modifierCount; i++) { - reqFormat.modifiers.push(modifierArray[i]); // NOLINT - } - - this->request.dmabuf.formats.push(reqFormat); -} - -void IccScreencopyContext::ext_image_copy_capture_session_v1_done() { - this->statePending = false; - - if (this->capturePending) { - this->doCapture(); - } -} - -void IccScreencopyContext::ext_image_copy_capture_session_v1_stopped() { - qCInfo(logIcc) << "Ending recording due to screencopy stop for" << this; - emit this->stopped(); -} - -void IccScreencopyContext::clearOldState() { - if (!this->statePending) { - this->request = buffer::WlBufferRequest(); - this->statePending = true; - } -} - -void IccScreencopyContext::doCapture() { - this->capturePending = false; - - auto newBuffer = false; - auto* backbuffer = this->mSwapchain.createBackbuffer(this->request, &newBuffer); - - this->IccCaptureFrame::init(this->IccCaptureSession::create_frame()); - this->IccCaptureFrame::attach_buffer(backbuffer->buffer()); - - if (newBuffer) { - // If the buffer was replaced, it will be blank and the compositor needs - // to repaint the whole thing. - this->IccCaptureFrame::damage_buffer( - 0, - 0, - static_cast(this->request.width), - static_cast(this->request.height) - ); - - // We don't care about partial damage if the whole buffer was replaced. - this->lastDamage = QRect(); - } else if (!this->lastDamage.isEmpty()) { - // If buffers were swapped between the last frame and the current one, request a repaint - // of the backbuffer in the same places that changes to the frontbuffer were recorded. - this->IccCaptureFrame::damage_buffer( - this->lastDamage.x(), - this->lastDamage.y(), - this->lastDamage.width(), - this->lastDamage.height() - ); - - // We don't need to do this more than once per buffer swap. - this->lastDamage = QRect(); - } - - this->IccCaptureFrame::capture(); -} - -void IccScreencopyContext::ext_image_copy_capture_frame_v1_transform(uint32_t transform) { - this->mSwapchain.backbuffer()->transform = transform; -} - -void IccScreencopyContext::ext_image_copy_capture_frame_v1_damage( - int32_t x, - int32_t y, - int32_t width, - int32_t height -) { - this->damage = this->damage.united(QRect(x, y, width, height)); -} - -void IccScreencopyContext::ext_image_copy_capture_frame_v1_ready() { - this->IccCaptureFrame::destroy(); - - this->mSwapchain.swapBuffers(); - this->lastDamage = this->damage; - this->damage = QRect(); - - emit this->frameCaptured(); -} - -void IccScreencopyContext::ext_image_copy_capture_frame_v1_failed(uint32_t reason) { - switch (static_cast(reason)) { - case IccCaptureFrame::failure_reason_buffer_constraints: - qFatal(logIcc) << "Got a buffer_constraints failure, however the buffer matches the last sent " - "size. There is a bug in quickshell or your compositor."; - break; - case IccCaptureFrame::failure_reason_stopped: - // Handled in the ExtCaptureSession handler. - break; - case IccCaptureFrame::failure_reason_unknown: - qCWarning(logIcc) << "Ending recording due to screencopy failure for" << this; - emit this->stopped(); - break; - } -} - -IccManager::IccManager(): QWaylandClientExtensionTemplate(1) { this->initialize(); } - -IccManager* IccManager::instance() { - static auto* instance = new IccManager(); - return instance; -} - -ScreencopyContext* -IccManager::createSession(::ext_image_capture_source_v1* source, bool paintCursors) { - auto* session = this->create_session( - source, - paintCursors ? QtWayland::ext_image_copy_capture_manager_v1::options_paint_cursors : 0 - ); - return new IccScreencopyContext(session); -} - -IccOutputSourceManager::IccOutputSourceManager(): QWaylandClientExtensionTemplate(1) { - this->initialize(); -} - -IccOutputSourceManager* IccOutputSourceManager::instance() { - static auto* instance = new IccOutputSourceManager(); - return instance; -} - -ScreencopyContext* IccOutputSourceManager::captureOutput(QScreen* screen, bool paintCursors) { - auto* waylandScreen = dynamic_cast(screen->handle()); - if (!waylandScreen) return nullptr; - - return IccManager::instance()->createSession( - this->create_source(waylandScreen->output()), - paintCursors - ); -} - -} // namespace qs::wayland::screencopy::icc diff --git a/src/wayland/screencopy/image_copy_capture/image_copy_capture.hpp b/src/wayland/screencopy/image_copy_capture/image_copy_capture.hpp deleted file mode 100644 index 93ba36c3..00000000 --- a/src/wayland/screencopy/image_copy_capture/image_copy_capture.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "../manager.hpp" - -namespace qs::wayland::screencopy::icc { - -class IccManager - : public QWaylandClientExtensionTemplate - , public QtWayland::ext_image_copy_capture_manager_v1 { -public: - ScreencopyContext* createSession(::ext_image_capture_source_v1* source, bool paintCursors); - - static IccManager* instance(); - -private: - explicit IccManager(); -}; - -class IccOutputSourceManager - : public QWaylandClientExtensionTemplate - , public QtWayland::ext_output_image_capture_source_manager_v1 { -public: - ScreencopyContext* captureOutput(QScreen* screen, bool paintCursors); - - static IccOutputSourceManager* instance(); - -private: - explicit IccOutputSourceManager(); -}; - -} // namespace qs::wayland::screencopy::icc diff --git a/src/wayland/screencopy/image_copy_capture/image_copy_capture_p.hpp b/src/wayland/screencopy/image_copy_capture/image_copy_capture_p.hpp deleted file mode 100644 index e89af4a5..00000000 --- a/src/wayland/screencopy/image_copy_capture/image_copy_capture_p.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include "../manager.hpp" - -namespace qs::wayland::screencopy::icc { - -class IccScreencopyContext - : public ScreencopyContext - , public QtWayland::ext_image_copy_capture_session_v1 - , public QtWayland::ext_image_copy_capture_frame_v1 { - -public: - IccScreencopyContext(::ext_image_copy_capture_session_v1* session); - ~IccScreencopyContext() override; - Q_DISABLE_COPY_MOVE(IccScreencopyContext); - - void captureFrame() override; - -protected: - // clang-format off - void ext_image_copy_capture_session_v1_buffer_size(uint32_t width, uint32_t height) override; - void ext_image_copy_capture_session_v1_shm_format(uint32_t format) override; - void ext_image_copy_capture_session_v1_dmabuf_device(wl_array* device) override; - void ext_image_copy_capture_session_v1_dmabuf_format(uint32_t format, wl_array* modifiers) override; - void ext_image_copy_capture_session_v1_done() override; - void ext_image_copy_capture_session_v1_stopped() override; - - void ext_image_copy_capture_frame_v1_transform(uint32_t transform) override; - void ext_image_copy_capture_frame_v1_damage(int32_t x, int32_t y, int32_t width, int32_t height) override; - void ext_image_copy_capture_frame_v1_ready() override; - void ext_image_copy_capture_frame_v1_failed(uint32_t reason) override; - // clang-format on - -private: - void clearOldState(); - void doCapture(); - - buffer::WlBufferRequest request; - bool statePending = true; - bool capturePending = false; - QRect damage; - QRect lastDamage; -}; - -} // namespace qs::wayland::screencopy::icc diff --git a/src/wayland/screencopy/manager.cpp b/src/wayland/screencopy/manager.cpp deleted file mode 100644 index 8345e314..00000000 --- a/src/wayland/screencopy/manager.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "manager.hpp" - -#include - -#include "build.hpp" - -#if SCREENCOPY_ICC || SCREENCOPY_WLR -#include "../../core/qmlscreen.hpp" -#endif - -#if SCREENCOPY_ICC -#include "image_copy_capture/image_copy_capture.hpp" -#endif - -#if SCREENCOPY_WLR -#include "wlr_screencopy/wlr_screencopy.hpp" -#endif - -#if SCREENCOPY_HYPRLAND_TOPLEVEL -#include "../toplevel_management/qml.hpp" -#include "hyprland_screencopy/hyprland_screencopy.hpp" -#endif - -namespace qs::wayland::screencopy { - -ScreencopyContext* ScreencopyManager::createContext(QObject* object, bool paintCursors) { - if (auto* screen = qobject_cast(object)) { -#if SCREENCOPY_ICC - { - auto* manager = icc::IccOutputSourceManager::instance(); - if (manager->isActive()) { - return manager->captureOutput(screen->screen, paintCursors); - } - } -#endif -#if SCREENCOPY_WLR - { - auto* manager = wlr::WlrScreencopyManager::instance(); - if (manager->isActive()) { - return manager->captureOutput(screen->screen, paintCursors); - } - } -#endif -#if SCREENCOPY_HYPRLAND_TOPLEVEL - } else if (auto* toplevel = qobject_cast(object)) { - auto* manager = hyprland::HyprlandScreencopyManager::instance(); - if (manager->isActive()) { - return manager->captureToplevel(toplevel->implHandle(), paintCursors); - } -#endif - } - - return nullptr; -} - -} // namespace qs::wayland::screencopy diff --git a/src/wayland/screencopy/manager.hpp b/src/wayland/screencopy/manager.hpp deleted file mode 100644 index f58e0052..00000000 --- a/src/wayland/screencopy/manager.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../buffer/manager.hpp" - -namespace qs::wayland::screencopy { - -class ScreencopyContext: public QObject { - Q_OBJECT; - -public: - [[nodiscard]] buffer::WlBufferSwapchain& swapchain() { return this->mSwapchain; } - virtual void captureFrame() = 0; - -signals: - void frameCaptured(); - void stopped(); - -protected: - ScreencopyContext() = default; - - buffer::WlBufferSwapchain mSwapchain; -}; - -class ScreencopyManager { -public: - static ScreencopyContext* createContext(QObject* object, bool paintCursors); -}; - -} // namespace qs::wayland::screencopy diff --git a/src/wayland/screencopy/view.cpp b/src/wayland/screencopy/view.cpp deleted file mode 100644 index fe744fd8..00000000 --- a/src/wayland/screencopy/view.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "view.hpp" - -#include -#include -#include -#include -#include - -#include "../buffer/manager.hpp" -#include "../buffer/qsg.hpp" -#include "manager.hpp" - -namespace qs::wayland::screencopy { - -void ScreencopyView::setCaptureSource(QObject* captureSource) { - if (captureSource == this->mCaptureSource) return; - auto hadContext = this->context != nullptr; - this->destroyContext(false); - - this->mCaptureSource = captureSource; - - if (captureSource) { - QObject::connect( - captureSource, - &QObject::destroyed, - this, - &ScreencopyView::onCaptureSourceDestroyed - ); - - if (this->completed) this->createContext(); - } - - if (!this->context && hadContext) this->update(); - emit this->captureSourceChanged(); -} - -void ScreencopyView::onCaptureSourceDestroyed() { - this->mCaptureSource = nullptr; - this->destroyContext(); -} - -void ScreencopyView::setPaintCursors(bool paintCursors) { - if (paintCursors == this->mPaintCursors) return; - this->mPaintCursors = paintCursors; - if (this->completed && this->context) this->createContext(); - emit this->paintCursorsChanged(); -} - -void ScreencopyView::setLive(bool live) { - if (live == this->mLive) return; - - if (live && !this->mLive && this->context) { - this->context->captureFrame(); - } - - this->mLive = live; - emit this->liveChanged(); -} - -void ScreencopyView::createContext() { - this->destroyContext(false); - this->context = ScreencopyManager::createContext(this->mCaptureSource, this->mPaintCursors); - - if (!this->context) { - qmlWarning(this) << "Capture source set to non captureable object."; - return; - } - - this->context->setParent(this); - - QObject::connect( - this->context, - &ScreencopyContext::stopped, - this, - &ScreencopyView::destroyContextWithUpdate - ); - - QObject::connect( - this->context, - &ScreencopyContext::frameCaptured, - this, - &ScreencopyView::onFrameCaptured - ); - - this->context->captureFrame(); -} - -void ScreencopyView::destroyContext(bool update) { - auto hadContext = this->context != nullptr; - delete this->context; - this->context = nullptr; - this->bHasContent = false; - this->bSourceSize = QSize(); - if (hadContext && update) this->update(); -} - -void ScreencopyView::captureFrame() { - if (this->context) this->context->captureFrame(); - else qmlWarning(this) << "Cannot capture frame, as no recording context is ready."; -} - -void ScreencopyView::onFrameCaptured() { - this->setFlag(QQuickItem::ItemHasContents); - this->update(); - this->bHasContent = true; - this->bSourceSize = this->context->swapchain().frontbuffer()->size(); -} - -void ScreencopyView::componentComplete() { - this->QQuickItem::componentComplete(); - - auto* bufManager = buffer::WlBufferManager::instance(); - if (!bufManager->isReady()) { - QObject::connect( - bufManager, - &buffer::WlBufferManager::ready, - this, - &ScreencopyView::onBuffersReady - ); - } else { - this->onBuffersReady(); - } -} - -void ScreencopyView::onBuffersReady() { - this->completed = true; - if (this->mCaptureSource) this->createContext(); -} - -QSGNode* ScreencopyView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* /*unused*/) { - if (!this->context || !this->bHasContent) { - delete oldNode; - this->setFlag(QQuickItem::ItemHasContents, false); - return nullptr; - } - - auto* node = static_cast(oldNode); // NOLINT - - if (!node) { - node = new buffer::WlBufferQSGDisplayNode(this->window()); - } - - auto& swapchain = this->context->swapchain(); - node->syncSwapchain(swapchain); - node->setRect(this->boundingRect()); - - if (this->mLive) this->context->captureFrame(); - return node; -} - -} // namespace qs::wayland::screencopy diff --git a/src/wayland/screencopy/view.hpp b/src/wayland/screencopy/view.hpp deleted file mode 100644 index 53f42398..00000000 --- a/src/wayland/screencopy/view.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "manager.hpp" - -namespace qs::wayland::screencopy { - -///! Displays a video stream from other windows or a monitor. -/// ScreencopyView displays live video streams or single captured frames from valid -/// capture sources. See @@captureSource for details on which objects are accepted. -class ScreencopyView: public QQuickItem { - Q_OBJECT; - QML_ELEMENT; - // clang-format off - /// The object to capture from. Accepts any of the following: - /// - `null` - Clears the displayed image. - /// - @@Quickshell.ShellScreen - A monitor. - /// Requires a compositor that supports `wlr-screencopy-unstable` - /// or both `ext-image-copy-capture-v1` and `ext-capture-source-v1`. - /// - @@Quickshell.Wayland.Toplevel - A toplevel window. - /// Requires a compositor that supports `hyprland-toplevel-export-v1`. - Q_PROPERTY(QObject* captureSource READ captureSource WRITE setCaptureSource NOTIFY captureSourceChanged); - /// If true, the system cursor will be painted on the image. Defaults to false. - Q_PROPERTY(bool paintCursor READ paintCursors WRITE setPaintCursors NOTIFY paintCursorsChanged); - /// If true, a live video feed from the capture source will be displayed instead of a still image. - /// Defaults to false. - Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged); - /// If true, the view has content ready to display. Content is not always immediately available, - /// and this property can be used to avoid displaying it until ready. - Q_PROPERTY(bool hasContent READ default NOTIFY hasContentChanged BINDABLE bindableHasContent); - /// The size of the source image. Valid when @@hasContent is true. - Q_PROPERTY(QSize sourceSize READ default NOTIFY sourceSizeChanged BINDABLE bindableSourceSize); - // clang-format on - -public: - explicit ScreencopyView(QQuickItem* parent = nullptr): QQuickItem(parent) {} - - void componentComplete() override; - - /// Capture a single frame. Has no effect if @@live is true. - Q_INVOKABLE void captureFrame(); - - [[nodiscard]] QObject* captureSource() const { return this->mCaptureSource; } - void setCaptureSource(QObject* captureSource); - - [[nodiscard]] bool paintCursors() const { return this->mPaintCursors; } - void setPaintCursors(bool paintCursors); - - [[nodiscard]] bool live() const { return this->mLive; } - void setLive(bool live); - - [[nodiscard]] QBindable bindableHasContent() { return &this->bHasContent; } - [[nodiscard]] QBindable bindableSourceSize() { return &this->bSourceSize; } - -signals: - /// The compositor has ended the video stream. Attempting to restart it may or may not work. - void stopped(); - - void captureSourceChanged(); - void paintCursorsChanged(); - void liveChanged(); - void hasContentChanged(); - void sourceSizeChanged(); - -protected: - QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* data) override; - -private slots: - void onCaptureSourceDestroyed(); - void onFrameCaptured(); - void destroyContextWithUpdate() { this->destroyContext(); } - void onBuffersReady(); - -private: - void destroyContext(bool update = true); - void createContext(); - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(ScreencopyView, bool, bHasContent, &ScreencopyView::hasContentChanged); - Q_OBJECT_BINDABLE_PROPERTY(ScreencopyView, QSize, bSourceSize, &ScreencopyView::sourceSizeChanged); - // clang-format on - - QObject* mCaptureSource = nullptr; - bool mPaintCursors = false; - bool mLive = false; - ScreencopyContext* context = nullptr; - bool completed = false; -}; - -} // namespace qs::wayland::screencopy diff --git a/src/wayland/screencopy/wlr_screencopy/CMakeLists.txt b/src/wayland/screencopy/wlr_screencopy/CMakeLists.txt deleted file mode 100644 index 5829d915..00000000 --- a/src/wayland/screencopy/wlr_screencopy/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -qt_add_library(quickshell-wayland-screencopy-wlr STATIC - wlr_screencopy.cpp -) - -wl_proto(wlp-wlr-screencopy wlr-screencopy-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}") - -target_link_libraries(quickshell-wayland-screencopy-wlr PRIVATE - Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - Qt::Quick # for pch -) - -target_link_libraries(quickshell-wayland-screencopy-wlr PUBLIC wlp-wlr-screencopy) - -qs_pch(quickshell-wayland-screencopy-wlr SET large) diff --git a/src/wayland/screencopy/wlr_screencopy/wlr-screencopy-unstable-v1.xml b/src/wayland/screencopy/wlr_screencopy/wlr-screencopy-unstable-v1.xml deleted file mode 100644 index 50b1b7d2..00000000 --- a/src/wayland/screencopy/wlr_screencopy/wlr-screencopy-unstable-v1.xml +++ /dev/null @@ -1,232 +0,0 @@ - - - - Copyright © 2018 Simon Ser - Copyright © 2019 Andri Yngvason - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol allows clients to ask the compositor to copy part of the - screen content to a client buffer. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This object is a manager which offers requests to start capturing from a - source. - - - - - Capture the next frame of an entire output. - - - - - - - - - Capture the next frame of an output's region. - - The region is given in output logical coordinates, see - xdg_output.logical_size. The region will be clipped to the output's - extents. - - - - - - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This object represents a single frame. - - When created, a series of buffer events will be sent, each representing a - supported buffer type. The "buffer_done" event is sent afterwards to - indicate that all supported buffer types have been enumerated. The client - will then be able to send a "copy" request. If the capture is successful, - the compositor will send a "flags" followed by a "ready" event. - - For objects version 2 or lower, wl_shm buffers are always supported, ie. - the "buffer" event is guaranteed to be sent. - - If the capture failed, the "failed" event is sent. This can happen anytime - before the "ready" event. - - Once either a "ready" or a "failed" event is received, the client should - destroy the frame. - - - - - Provides information about wl_shm buffer parameters that need to be - used for this frame. This event is sent once after the frame is created - if wl_shm buffers are supported. - - - - - - - - - - Copy the frame to the supplied buffer. The buffer must have a the - correct size, see zwlr_screencopy_frame_v1.buffer and - zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a - supported format. - - If the frame is successfully copied, a "flags" and a "ready" events are - sent. Otherwise, a "failed" event is sent. - - - - - - - - - - - - - - - - Provides flags about the frame. This event is sent once before the - "ready" event. - - - - - - - Called as soon as the frame is copied, indicating it is available - for reading. This event includes the time at which presentation happened - at. - - The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, - each component being an unsigned 32-bit value. Whole seconds are in - tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, - and the additional fractional part in tv_nsec as nanoseconds. Hence, - for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part - may have an arbitrary offset at start. - - After receiving this event, the client should destroy the object. - - - - - - - - - This event indicates that the attempted frame copy has failed. - - After receiving this event, the client should destroy the object. - - - - - - Destroys the frame. This request can be sent at any time by the client. - - - - - - - Same as copy, except it waits until there is damage to copy. - - - - - - - This event is sent right before the ready event when copy_with_damage is - requested. It may be generated multiple times for each copy_with_damage - request. - - The arguments describe a box around an area that has changed since the - last copy request that was derived from the current screencopy manager - instance. - - The union of all regions received between the call to copy_with_damage - and a ready event is the total damage since the prior ready event. - - - - - - - - - - - Provides information about linux-dmabuf buffer parameters that need to - be used for this frame. This event is sent once after the frame is - created if linux-dmabuf buffers are supported. - - - - - - - - - This event is sent once after all buffer events have been sent. - - The client should proceed to create a buffer of one of the supported - types, and send a "copy" request. - - - - diff --git a/src/wayland/screencopy/wlr_screencopy/wlr_screencopy.cpp b/src/wayland/screencopy/wlr_screencopy/wlr_screencopy.cpp deleted file mode 100644 index 8cc89bca..00000000 --- a/src/wayland/screencopy/wlr_screencopy/wlr_screencopy.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "wlr_screencopy.hpp" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../buffer/manager.hpp" -#include "../manager.hpp" -#include "wlr_screencopy_p.hpp" - -namespace qs::wayland::screencopy::wlr { - -namespace { -Q_LOGGING_CATEGORY(logScreencopy, "quickshell.wayland.screencopy.wlr", QtWarningMsg); -} - -WlrScreencopyManager::WlrScreencopyManager(): QWaylandClientExtensionTemplate(3) { - this->initialize(); -} - -WlrScreencopyManager* WlrScreencopyManager::instance() { - static auto* instance = new WlrScreencopyManager(); - return instance; -} - -ScreencopyContext* -WlrScreencopyManager::captureOutput(QScreen* screen, bool paintCursors, QRect region) { - if (!dynamic_cast(screen->handle())) return nullptr; - return new WlrScreencopyContext(this, screen, paintCursors, region); -} - -WlrScreencopyContext::WlrScreencopyContext( - WlrScreencopyManager* manager, - QScreen* screen, - bool paintCursors, - QRect region -) - : manager(manager) - , screen(dynamic_cast(screen->handle())) - , paintCursors(paintCursors) - , region(region) { - QObject::connect(screen, &QObject::destroyed, this, &WlrScreencopyContext::onScreenDestroyed); -} - -WlrScreencopyContext::~WlrScreencopyContext() { - if (this->object()) this->destroy(); -} - -void WlrScreencopyContext::onScreenDestroyed() { - qCWarning(logScreencopy) << "Screen destroyed while recording. Stopping" << this; - if (this->object()) this->destroy(); - emit this->stopped(); -} - -void WlrScreencopyContext::captureFrame() { - if (this->object()) return; - - if (this->region.isEmpty()) { - this->init(manager->capture_output(this->paintCursors ? 1 : 0, screen->output())); - } else { - this->init(manager->capture_output_region( - this->paintCursors ? 1 : 0, - screen->output(), - this->region.x(), - this->region.y(), - this->region.width(), - this->region.height() - )); - } -} - -void WlrScreencopyContext::zwlr_screencopy_frame_v1_buffer( - uint32_t format, - uint32_t width, - uint32_t height, - uint32_t /*stride*/ -) { - // While different sizes can technically be requested, that would be insane. - this->request.width = width; - this->request.height = height; - this->request.shm.formats.push(format); -} - -void WlrScreencopyContext::zwlr_screencopy_frame_v1_linux_dmabuf( - uint32_t format, - uint32_t width, - uint32_t height -) { - // While different sizes can technically be requested, that would be insane. - this->request.width = width; - this->request.height = height; - this->request.dmabuf.formats.push(format); -} - -void WlrScreencopyContext::zwlr_screencopy_frame_v1_flags(uint32_t flags) { - if (flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT) { - this->mSwapchain.backbuffer()->transform = buffer::WlBufferTransform::Flipped180; - } -} - -void WlrScreencopyContext::zwlr_screencopy_frame_v1_buffer_done() { - auto* backbuffer = this->mSwapchain.createBackbuffer(this->request); - - if (this->copiedFirstFrame) { - this->copy_with_damage(backbuffer->buffer()); - } else { - this->copy(backbuffer->buffer()); - } -} - -void WlrScreencopyContext::zwlr_screencopy_frame_v1_ready( - uint32_t /*tvSecHi*/, - uint32_t /*tvSecLo*/, - uint32_t /*tvNsec*/ -) { - this->destroy(); - this->copiedFirstFrame = true; - this->mSwapchain.swapBuffers(); - emit this->frameCaptured(); -} - -void WlrScreencopyContext::zwlr_screencopy_frame_v1_failed() { - qCWarning(logScreencopy) << "Ending recording due to screencopy failure for" << this; - emit this->stopped(); -} - -} // namespace qs::wayland::screencopy::wlr diff --git a/src/wayland/screencopy/wlr_screencopy/wlr_screencopy.hpp b/src/wayland/screencopy/wlr_screencopy/wlr_screencopy.hpp deleted file mode 100644 index bea17332..00000000 --- a/src/wayland/screencopy/wlr_screencopy/wlr_screencopy.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../manager.hpp" - -namespace qs::wayland::screencopy::wlr { - -class WlrScreencopyManager - : public QWaylandClientExtensionTemplate - , public QtWayland::zwlr_screencopy_manager_v1 { -public: - ScreencopyContext* captureOutput(QScreen* screen, bool paintCursors, QRect region = QRect()); - - static WlrScreencopyManager* instance(); - -private: - explicit WlrScreencopyManager(); - - friend class WlrScreencopyContext; -}; - -} // namespace qs::wayland::screencopy::wlr diff --git a/src/wayland/screencopy/wlr_screencopy/wlr_screencopy_p.hpp b/src/wayland/screencopy/wlr_screencopy/wlr_screencopy_p.hpp deleted file mode 100644 index 7bdbafb7..00000000 --- a/src/wayland/screencopy/wlr_screencopy/wlr_screencopy_p.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../manager.hpp" - -namespace qs::wayland::screencopy::wlr { - -class WlrScreencopyManager; - -class WlrScreencopyContext - : public ScreencopyContext - , public QtWayland::zwlr_screencopy_frame_v1 { -public: - explicit WlrScreencopyContext( - WlrScreencopyManager* manager, - QScreen* screen, - bool paintCursors, - QRect region - ); - ~WlrScreencopyContext() override; - Q_DISABLE_COPY_MOVE(WlrScreencopyContext); - - void captureFrame() override; - -protected: - // clang-format off - void zwlr_screencopy_frame_v1_buffer(uint32_t format, uint32_t width, uint32_t height, uint32_t stride) override; - void zwlr_screencopy_frame_v1_linux_dmabuf(uint32_t format, uint32_t width, uint32_t height) override; - void zwlr_screencopy_frame_v1_flags(uint32_t flags) override; - void zwlr_screencopy_frame_v1_buffer_done() override; - void zwlr_screencopy_frame_v1_ready(uint32_t tvSecHi, uint32_t tvSecLo, uint32_t tvNsec) override; - void zwlr_screencopy_frame_v1_failed() override; - // clang-format on - -private slots: - void onScreenDestroyed(); - -private: - WlrScreencopyManager* manager; - buffer::WlBufferRequest request; - bool copiedFirstFrame = false; - - QtWaylandClient::QWaylandScreen* screen; - bool paintCursors; - QRect region; -}; - -} // namespace qs::wayland::screencopy::wlr diff --git a/src/wayland/session_lock/CMakeLists.txt b/src/wayland/session_lock/CMakeLists.txt index d157fc13..245d1f25 100644 --- a/src/wayland/session_lock/CMakeLists.txt +++ b/src/wayland/session_lock/CMakeLists.txt @@ -6,11 +6,10 @@ qt_add_library(quickshell-wayland-sessionlock STATIC session_lock.cpp ) -wl_proto(wlp-session-lock ext-session-lock-v1 "${WAYLAND_PROTOCOLS}/staging/ext-session-lock") +wl_proto(quickshell-wayland-sessionlock ext-session-lock-v1 "${WAYLAND_PROTOCOLS}/staging/ext-session-lock/ext-session-lock-v1.xml") target_link_libraries(quickshell-wayland-sessionlock PRIVATE Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - wlp-session-lock ) qs_pch(quickshell-wayland-sessionlock SET large) diff --git a/src/wayland/toplevel_management/CMakeLists.txt b/src/wayland/toplevel_management/CMakeLists.txt index 97f8c7a4..0db82aae 100644 --- a/src/wayland/toplevel_management/CMakeLists.txt +++ b/src/wayland/toplevel_management/CMakeLists.txt @@ -16,11 +16,13 @@ qs_add_module_deps_light(quickshell-wayland-toplevel-management install_qml_module(quickshell-wayland-toplevel-management) -wl_proto(wlp-foreign-toplevel wlr-foreign-toplevel-management-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}") +wl_proto(quickshell-wayland-toplevel-management + wlr-foreign-toplevel-management-unstable-v1 + "${CMAKE_CURRENT_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1.xml" +) target_link_libraries(quickshell-wayland-toplevel-management PRIVATE Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - wlp-foreign-toplevel ) qs_module_pch(quickshell-wayland-toplevel-management SET large) diff --git a/src/wayland/toplevel_management/qml.hpp b/src/wayland/toplevel_management/qml.hpp index 20347343..127b4c8f 100644 --- a/src/wayland/toplevel_management/qml.hpp +++ b/src/wayland/toplevel_management/qml.hpp @@ -13,7 +13,7 @@ namespace qs::wayland::toplevel_management { namespace impl { -class ToplevelManager; // NOLINT +class ToplevelManager; class ToplevelHandle; } // namespace impl @@ -80,8 +80,6 @@ public: [[nodiscard]] bool fullscreen() const; void setFullscreen(bool fullscreen); - [[nodiscard]] impl::ToplevelHandle* implHandle() const { return this->handle; } - signals: void closed(); void appIdChanged(); diff --git a/src/wayland/util.cpp b/src/wayland/util.cpp index abcf8a4d..6bce2621 100644 --- a/src/wayland/util.cpp +++ b/src/wayland/util.cpp @@ -1,9 +1,17 @@ #include "util.hpp" -#include "../window/proxywindow.hpp" +#include +#include namespace qs::wayland::util { -void scheduleCommit(ProxyWindowBase* window) { window->schedulePolish(); } +void scheduleCommit(QtWaylandClient::QWaylandWindow* window) { + // This seems to be one of the less offensive ways to force Qt to send a wl_surface.commit on its own terms. + // Ideally we would trigger the commit more directly. + QWindowSystemInterface::handleExposeEvent( + window->window(), + QRect(QPoint(), window->geometry().size()) + ); +} } // namespace qs::wayland::util diff --git a/src/wayland/util.hpp b/src/wayland/util.hpp index aadf44f1..7967fadc 100644 --- a/src/wayland/util.hpp +++ b/src/wayland/util.hpp @@ -1,9 +1,9 @@ #pragma once -#include "../window/proxywindow.hpp" +#include namespace qs::wayland::util { -void scheduleCommit(ProxyWindowBase* window); +void scheduleCommit(QtWaylandClient::QWaylandWindow* window); } diff --git a/src/wayland/wlr_layershell.cpp b/src/wayland/wlr_layershell.cpp index a649603c..9b4f32f2 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 @@ -192,12 +192,10 @@ WaylandPanelInterface::WaylandPanelInterface(QObject* parent) QObject::connect(this->layer, &ProxyWindowBase::backerVisibilityChanged, this, &WaylandPanelInterface::backingWindowVisibleChanged); QObject::connect(this->layer, &ProxyWindowBase::heightChanged, this, &WaylandPanelInterface::heightChanged); QObject::connect(this->layer, &ProxyWindowBase::widthChanged, this, &WaylandPanelInterface::widthChanged); - QObject::connect(this->layer, &ProxyWindowBase::devicePixelRatioChanged, this, &WaylandPanelInterface::devicePixelRatioChanged); QObject::connect(this->layer, &ProxyWindowBase::screenChanged, this, &WaylandPanelInterface::screenChanged); QObject::connect(this->layer, &ProxyWindowBase::windowTransformChanged, this, &WaylandPanelInterface::windowTransformChanged); QObject::connect(this->layer, &ProxyWindowBase::colorChanged, this, &WaylandPanelInterface::colorChanged); QObject::connect(this->layer, &ProxyWindowBase::maskChanged, this, &WaylandPanelInterface::maskChanged); - QObject::connect(this->layer, &ProxyWindowBase::surfaceFormatChanged, this, &WaylandPanelInterface::surfaceFormatChanged); // panel specific QObject::connect(this->layer, &WlrLayershell::anchorsChanged, this, &WaylandPanelInterface::anchorsChanged); @@ -219,13 +217,10 @@ void WaylandPanelInterface::onReload(QObject* oldInstance) { QQmlListProperty WaylandPanelInterface::data() { return this->layer->data(); } ProxyWindowBase* WaylandPanelInterface::proxyWindow() const { return this->layer; } QQuickItem* WaylandPanelInterface::contentItem() const { return this->layer->contentItem(); } - bool WaylandPanelInterface::isBackingWindowVisible() const { return this->layer->isVisibleDirect(); } -qreal WaylandPanelInterface::devicePixelRatio() const { return this->layer->devicePixelRatio(); } - // NOLINTBEGIN #define proxyPair(type, get, set) \ type WaylandPanelInterface::get() const { return this->layer->get(); } \ @@ -237,7 +232,6 @@ proxyPair(qint32, height, setHeight); proxyPair(QuickshellScreenInfo*, screen, setScreen); proxyPair(QColor, color, setColor); proxyPair(PendingRegion*, mask, setMask); -proxyPair(QsSurfaceFormat, surfaceFormat, setSurfaceFormat); // panel specific proxyPair(Anchors, anchors, setAnchors); diff --git a/src/wayland/wlr_layershell.hpp b/src/wayland/wlr_layershell.hpp index c3448706..f6f6988a 100644 --- a/src/wayland/wlr_layershell.hpp +++ b/src/wayland/wlr_layershell.hpp @@ -146,8 +146,6 @@ public: [[nodiscard]] qint32 height() const override; void setHeight(qint32 height) override; - [[nodiscard]] virtual qreal devicePixelRatio() const override; - [[nodiscard]] QuickshellScreenInfo* screen() const override; void setScreen(QuickshellScreenInfo* screen) override; @@ -157,9 +155,6 @@ public: [[nodiscard]] PendingRegion* mask() const override; void setMask(PendingRegion* mask) override; - [[nodiscard]] QsSurfaceFormat surfaceFormat() const override; - void setSurfaceFormat(QsSurfaceFormat mask) override; - [[nodiscard]] QQmlListProperty data() override; // panel specific diff --git a/src/wayland/wlr_layershell/CMakeLists.txt b/src/wayland/wlr_layershell/CMakeLists.txt index 69b24e92..f22ee0ba 100644 --- a/src/wayland/wlr_layershell/CMakeLists.txt +++ b/src/wayland/wlr_layershell/CMakeLists.txt @@ -14,14 +14,13 @@ qs_add_module_deps_light(quickshell-wayland-layershell Quickshell Quickshell.Way install_qml_module(quickshell-wayland-layershell) -wl_proto(wlp-layer-shell wlr-layer-shell-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}") +wl_proto(quickshell-wayland-layershell wlr-layer-shell-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}/wlr-layer-shell-unstable-v1.xml") # link dependency of wlr-layer-shell's codegen -wl_proto(wlp-xdg-shell xdg-shell "${WAYLAND_PROTOCOLS}/stable/xdg-shell") +wl_proto(quickshell-wayland-layershell xdg-shell "${WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml") target_link_libraries(quickshell-wayland-layershell PRIVATE Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client - wlp-layer-shell wlp-xdg-shell ) qs_module_pch(quickshell-wayland-layershell SET large) diff --git a/src/wayland/wlr_layershell/surface.cpp b/src/wayland/wlr_layershell/surface.cpp index 3c0338c7..25b58ff8 100644 --- a/src/wayland/wlr_layershell/surface.cpp +++ b/src/wayland/wlr_layershell/surface.cpp @@ -22,51 +22,12 @@ #include #endif -namespace { - -[[nodiscard]] QtWayland::zwlr_layer_shell_v1::layer toWaylandLayer(const WlrLayer::Enum& layer -) noexcept { - switch (layer) { - case WlrLayer::Background: return QtWayland::zwlr_layer_shell_v1::layer_background; - case WlrLayer::Bottom: return QtWayland::zwlr_layer_shell_v1::layer_bottom; - case WlrLayer::Top: return QtWayland::zwlr_layer_shell_v1::layer_top; - case WlrLayer::Overlay: return QtWayland::zwlr_layer_shell_v1::layer_overlay; - } - - return QtWayland::zwlr_layer_shell_v1::layer_top; -} - -[[nodiscard]] QtWayland::zwlr_layer_surface_v1::anchor toWaylandAnchors(const Anchors& anchors -) noexcept { - quint32 wl = 0; - if (anchors.mLeft) wl |= QtWayland::zwlr_layer_surface_v1::anchor_left; - if (anchors.mRight) wl |= QtWayland::zwlr_layer_surface_v1::anchor_right; - if (anchors.mTop) wl |= QtWayland::zwlr_layer_surface_v1::anchor_top; - if (anchors.mBottom) wl |= QtWayland::zwlr_layer_surface_v1::anchor_bottom; - return static_cast(wl); -} - -[[nodiscard]] QtWayland::zwlr_layer_surface_v1::keyboard_interactivity -toWaylandKeyboardFocus(const WlrKeyboardFocus::Enum& focus) noexcept { - switch (focus) { - case WlrKeyboardFocus::None: return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_none; - case WlrKeyboardFocus::Exclusive: - return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_exclusive; - case WlrKeyboardFocus::OnDemand: - return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_on_demand; - } - - return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_none; -} - -[[nodiscard]] QSize constrainedSize(const Anchors& anchors, const QSize& size) noexcept { - return QSize( - anchors.horizontalConstraint() ? 0 : size.width(), - anchors.verticalConstraint() ? 0 : size.height() - ); -} - -} // namespace +// clang-format off +[[nodiscard]] QtWayland::zwlr_layer_shell_v1::layer toWaylandLayer(const WlrLayer::Enum& layer) noexcept; +[[nodiscard]] QtWayland::zwlr_layer_surface_v1::anchor toWaylandAnchors(const Anchors& anchors) noexcept; +[[nodiscard]] QtWayland::zwlr_layer_surface_v1::keyboard_interactivity toWaylandKeyboardFocus(const WlrKeyboardFocus::Enum& focus) noexcept; +[[nodiscard]] QSize constrainedSize(const Anchors& anchors, const QSize& size) noexcept; +// clang-format on QSWaylandLayerSurface::QSWaylandLayerSurface( QSWaylandLayerShellIntegration* shell, @@ -187,6 +148,46 @@ void QSWaylandLayerSurface::updateKeyboardFocus() { this->window()->waylandSurface()->commit(); } +QtWayland::zwlr_layer_shell_v1::layer toWaylandLayer(const WlrLayer::Enum& layer) noexcept { + switch (layer) { + case WlrLayer::Background: return QtWayland::zwlr_layer_shell_v1::layer_background; + case WlrLayer::Bottom: return QtWayland::zwlr_layer_shell_v1::layer_bottom; + case WlrLayer::Top: return QtWayland::zwlr_layer_shell_v1::layer_top; + case WlrLayer::Overlay: return QtWayland::zwlr_layer_shell_v1::layer_overlay; + } + + return QtWayland::zwlr_layer_shell_v1::layer_top; +} + +QtWayland::zwlr_layer_surface_v1::anchor toWaylandAnchors(const Anchors& anchors) noexcept { + quint32 wl = 0; + if (anchors.mLeft) wl |= QtWayland::zwlr_layer_surface_v1::anchor_left; + if (anchors.mRight) wl |= QtWayland::zwlr_layer_surface_v1::anchor_right; + if (anchors.mTop) wl |= QtWayland::zwlr_layer_surface_v1::anchor_top; + if (anchors.mBottom) wl |= QtWayland::zwlr_layer_surface_v1::anchor_bottom; + return static_cast(wl); +} + +QtWayland::zwlr_layer_surface_v1::keyboard_interactivity +toWaylandKeyboardFocus(const WlrKeyboardFocus::Enum& focus) noexcept { + switch (focus) { + case WlrKeyboardFocus::None: return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_none; + case WlrKeyboardFocus::Exclusive: + return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_exclusive; + case WlrKeyboardFocus::OnDemand: + return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_on_demand; + } + + return QtWayland::zwlr_layer_surface_v1::keyboard_interactivity_none; +} + +QSize constrainedSize(const Anchors& anchors, const QSize& size) noexcept { + return QSize( + anchors.horizontalConstraint() ? 0 : size.width(), + anchors.verticalConstraint() ? 0 : size.height() + ); +} + void QSWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* popup) { std::any role = popup->surfaceRole(); diff --git a/src/window/floatingwindow.cpp b/src/window/floatingwindow.cpp index 761bc2d4..a1d23f54 100644 --- a/src/window/floatingwindow.cpp +++ b/src/window/floatingwindow.cpp @@ -32,12 +32,10 @@ FloatingWindowInterface::FloatingWindowInterface(QObject* parent) QObject::connect(this->window, &ProxyWindowBase::backerVisibilityChanged, this, &FloatingWindowInterface::backingWindowVisibleChanged); QObject::connect(this->window, &ProxyWindowBase::heightChanged, this, &FloatingWindowInterface::heightChanged); QObject::connect(this->window, &ProxyWindowBase::widthChanged, this, &FloatingWindowInterface::widthChanged); - QObject::connect(this->window, &ProxyWindowBase::devicePixelRatioChanged, this, &FloatingWindowInterface::devicePixelRatioChanged); QObject::connect(this->window, &ProxyWindowBase::screenChanged, this, &FloatingWindowInterface::screenChanged); QObject::connect(this->window, &ProxyWindowBase::windowTransformChanged, this, &FloatingWindowInterface::windowTransformChanged); QObject::connect(this->window, &ProxyWindowBase::colorChanged, this, &FloatingWindowInterface::colorChanged); QObject::connect(this->window, &ProxyWindowBase::maskChanged, this, &FloatingWindowInterface::maskChanged); - QObject::connect(this->window, &ProxyWindowBase::surfaceFormatChanged, this, &FloatingWindowInterface::surfaceFormatChanged); // clang-format on } @@ -51,13 +49,10 @@ void FloatingWindowInterface::onReload(QObject* oldInstance) { QQmlListProperty FloatingWindowInterface::data() { return this->window->data(); } ProxyWindowBase* FloatingWindowInterface::proxyWindow() const { return this->window; } QQuickItem* FloatingWindowInterface::contentItem() const { return this->window->contentItem(); } - bool FloatingWindowInterface::isBackingWindowVisible() const { return this->window->isVisibleDirect(); } -qreal FloatingWindowInterface::devicePixelRatio() const { return this->window->devicePixelRatio(); } - // NOLINTBEGIN #define proxyPair(type, get, set) \ type FloatingWindowInterface::get() const { return this->window->get(); } \ @@ -69,7 +64,6 @@ proxyPair(qint32, height, setHeight); proxyPair(QuickshellScreenInfo*, screen, setScreen); proxyPair(QColor, color, setColor); proxyPair(PendingRegion*, mask, setMask); -proxyPair(QsSurfaceFormat, surfaceFormat, setSurfaceFormat); #undef proxyPair // NOLINTEND diff --git a/src/window/floatingwindow.hpp b/src/window/floatingwindow.hpp index 7dd0d4ed..def1183a 100644 --- a/src/window/floatingwindow.hpp +++ b/src/window/floatingwindow.hpp @@ -4,7 +4,6 @@ #include #include "proxywindow.hpp" -#include "windowinterface.hpp" class ProxyFloatingWindow: public ProxyWindowBase { Q_OBJECT; @@ -42,8 +41,6 @@ public: [[nodiscard]] qint32 height() const override; void setHeight(qint32 height) override; - [[nodiscard]] virtual qreal devicePixelRatio() const override; - [[nodiscard]] QuickshellScreenInfo* screen() const override; void setScreen(QuickshellScreenInfo* screen) override; @@ -53,9 +50,6 @@ public: [[nodiscard]] PendingRegion* mask() const override; void setMask(PendingRegion* mask) override; - [[nodiscard]] QsSurfaceFormat surfaceFormat() const override; - void setSurfaceFormat(QsSurfaceFormat mask) override; - [[nodiscard]] QQmlListProperty data() override; // NOLINTEND diff --git a/src/window/panelinterface.hpp b/src/window/panelinterface.hpp index b9664ff9..6f8d1ec1 100644 --- a/src/window/panelinterface.hpp +++ b/src/window/panelinterface.hpp @@ -13,8 +13,7 @@ class Anchors { Q_PROPERTY(bool right MEMBER mRight); Q_PROPERTY(bool top MEMBER mTop); Q_PROPERTY(bool bottom MEMBER mBottom); - QML_VALUE_TYPE(panelAnchors); - QML_STRUCTURED_VALUE; + QML_VALUE_TYPE(anchors); public: [[nodiscard]] bool horizontalConstraint() const noexcept { return this->mLeft && this->mRight; } @@ -41,8 +40,7 @@ class Margins { Q_PROPERTY(qint32 right MEMBER mRight); Q_PROPERTY(qint32 top MEMBER mTop); Q_PROPERTY(qint32 bottom MEMBER mBottom); - QML_VALUE_TYPE(panelMargins); - QML_STRUCTURED_VALUE; + QML_VALUE_TYPE(margins); public: [[nodiscard]] bool operator==(const Margins& other) const noexcept { diff --git a/src/window/proxywindow.cpp b/src/window/proxywindow.cpp index 8ce80c20..6af1dcc3 100644 --- a/src/window/proxywindow.cpp +++ b/src/window/proxywindow.cpp @@ -1,20 +1,15 @@ #include "proxywindow.hpp" #include -#include #include -#include #include #include #include #include -#include #include #include #include #include -#include -#include #include #include #include @@ -30,16 +25,15 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent) : Reloadable(parent) - , mContentItem(new ProxyWindowContentItem()) { + , mContentItem(new QQuickItem()) { QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership); this->mContentItem->setParent(this); // clang-format off - QObject::connect(this->mContentItem, &ProxyWindowContentItem::polished, this, &ProxyWindowBase::onPolished); - QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged); QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onHeightChanged); + QObject::connect(this, &ProxyWindowBase::maskChanged, this, &ProxyWindowBase::onMaskChanged); QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged); QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged); @@ -56,7 +50,7 @@ ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); } void ProxyWindowBase::onReload(QObject* oldInstance) { this->window = this->retrieveWindow(oldInstance); auto wasVisible = this->window != nullptr && this->window->isVisible(); - this->ensureQWindow(); + if (this->window == nullptr) this->window = this->createQQuickWindow(); // The qml engine will leave the WindowInterface as owner of everything // nested in an item, so we have to make sure the interface's children @@ -91,55 +85,10 @@ void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); } ProxiedWindow* ProxyWindowBase::createQQuickWindow() { return new ProxiedWindow(this); } -void ProxyWindowBase::ensureQWindow() { - auto format = QSurfaceFormat::defaultFormat(); - - { - // match QtQuick's default format, including env var controls - static const auto useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); - static const auto useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER"); - static const auto enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG"); - static const auto disableVSync = qEnvironmentVariableIsSet("QSG_NO_VSYNC"); - - if (useDepth && format.depthBufferSize() == -1) format.setDepthBufferSize(24); - else if (!useDepth) format.setDepthBufferSize(0); - - if (useStencil && format.stencilBufferSize() == -1) format.setStencilBufferSize(8); - else if (!useStencil) format.setStencilBufferSize(0); - - auto opaque = this->qsSurfaceFormat.opaqueModified ? this->qsSurfaceFormat.opaque - : this->mColor.alpha() >= 255; - - if (opaque) format.setAlphaBufferSize(0); - else format.setAlphaBufferSize(8); - - if (enableDebug) format.setOption(QSurfaceFormat::DebugContext); - if (disableVSync) format.setSwapInterval(0); - - format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - format.setRedBufferSize(8); - format.setGreenBufferSize(8); - format.setBlueBufferSize(8); - } - - this->mSurfaceFormat = format; - - auto useOldWindow = this->window != nullptr; - - if (useOldWindow) { - if (this->window->requestedFormat() != format) { - useOldWindow = false; - } - } - - if (useOldWindow) return; - delete this->window; - this->window = this->createQQuickWindow(); - this->window->setFormat(format); -} - void ProxyWindowBase::createWindow() { - this->ensureQWindow(); + if (this->window != nullptr) return; + this->window = this->createQQuickWindow(); + this->connectWindow(); this->completeWindow(); emit this->windowConnected(); @@ -193,7 +142,6 @@ void ProxyWindowBase::connectWindow() { QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged); QObject::connect(this->window, &QQuickWindow::colorChanged, this, &ProxyWindowBase::colorChanged); QObject::connect(this->window, &ProxiedWindow::exposed, this, &ProxyWindowBase::runLints); - QObject::connect(this->window, &ProxiedWindow::devicePixelRatioChanged, this, &ProxyWindowBase::devicePixelRatioChanged); // clang-format on } @@ -201,6 +149,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); @@ -213,7 +164,6 @@ void ProxyWindowBase::completeWindow() { emit this->yChanged(); emit this->widthChanged(); emit this->heightChanged(); - emit this->devicePixelRatioChanged(); this->mContentItem->setParentItem(this->window->contentItem()); this->mContentItem->setWidth(this->width()); @@ -266,12 +216,6 @@ void ProxyWindowBase::setVisibleDirect(bool visible) { } } -void ProxyWindowBase::schedulePolish() { - if (this->isVisibleDirect()) { - this->mContentItem->polish(); - } -} - void ProxyWindowBase::polishItems() { // Due to QTBUG-126704, layouts in invisible windows don't update their dimensions. // Usually this isn't an issue, but it is when the size of a window is based on the size @@ -325,39 +269,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; } @@ -376,8 +320,6 @@ void ProxyWindowBase::setColor(QColor color) { ); this->window->setColor(premultiplied); - // setColor also modifies the alpha buffer size of the surface format - this->window->setFormat(this->mSurfaceFormat); } } @@ -393,44 +335,37 @@ void ProxyWindowBase::setMask(PendingRegion* mask) { this->mMask = mask; if (mask != nullptr) { + mask->setParent(this); QObject::connect(mask, &QObject::destroyed, this, &ProxyWindowBase::onMaskDestroyed); - QObject::connect(mask, &PendingRegion::changed, this, &ProxyWindowBase::onMaskChanged); + QObject::connect(mask, &PendingRegion::changed, this, &ProxyWindowBase::maskChanged); } - this->onMaskChanged(); emit this->maskChanged(); } -void ProxyWindowBase::setSurfaceFormat(QsSurfaceFormat format) { - if (format == this->qsSurfaceFormat) return; - if (this->window != nullptr) { - qmlWarning(this) << "Cannot set window surface format."; - return; - } - - this->qsSurfaceFormat = format; - emit this->surfaceFormatChanged(); -} - -qreal ProxyWindowBase::devicePixelRatio() const { - if (this->window != nullptr) return this->window->devicePixelRatio(); - if (this->mScreen != nullptr) return this->mScreen->devicePixelRatio(); - return 1.0; -} - void ProxyWindowBase::onMaskChanged() { if (this->window != nullptr) this->updateMask(); } void ProxyWindowBase::onMaskDestroyed() { this->mMask = nullptr; - this->onMaskChanged(); emit this->maskChanged(); } void ProxyWindowBase::updateMask() { - this->pendingPolish.inputMask = true; - this->schedulePolish(); + QRegion mask; + if (this->mMask != nullptr) { + // if left as the default, dont combine it with the whole window area, leave it as is. + if (this->mMask->mIntersection == Intersection::Combine) { + mask = this->mMask->build(); + } else { + auto windowRegion = QRegion(QRect(0, 0, this->width(), this->height())); + mask = this->mMask->applyTo(windowRegion); + } + } + + this->window->setFlag(Qt::WindowTransparentForInput, this->mMask != nullptr && mask.isEmpty()); + this->window->setMask(mask); } QQmlListProperty ProxyWindowBase::data() { @@ -463,33 +398,7 @@ void ProxyWindowAttached::setWindow(ProxyWindowBase* window) { emit this->windowChanged(); } -bool ProxiedWindow::event(QEvent* event) { - if (event->type() == QEvent::DevicePixelRatioChange) { - emit this->devicePixelRatioChanged(); - } - - return this->QQuickWindow::event(event); -} - void ProxiedWindow::exposeEvent(QExposeEvent* event) { this->QQuickWindow::exposeEvent(event); emit this->exposed(); } - -void ProxyWindowContentItem::updatePolish() { emit this->polished(); } - -void ProxyWindowBase::onPolished() { - if (this->pendingPolish.inputMask) { - QRegion mask; - if (this->mMask != nullptr) { - mask = this->mMask->applyTo(QRect(0, 0, this->width(), this->height())); - } - - this->window->setFlag(Qt::WindowTransparentForInput, this->mMask != nullptr && mask.isEmpty()); - this->window->setMask(mask); - - this->pendingPolish.inputMask = false; - } - - emit this->polished(); -} diff --git a/src/window/proxywindow.hpp b/src/window/proxywindow.hpp index 92a85f4f..14c90339 100644 --- a/src/window/proxywindow.hpp +++ b/src/window/proxywindow.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -20,7 +19,6 @@ #include "windowinterface.hpp" class ProxiedWindow; -class ProxyWindowContentItem; // Proxy to an actual window exposing a limited property set with the ability to // transfer it to a new window. @@ -31,7 +29,6 @@ class ProxyWindowContentItem; /// [FloatingWindow]: ../floatingwindow class ProxyWindowBase: public Reloadable { Q_OBJECT; - // clang-format off /// The QtQuick window backing this window. /// /// > [!WARNING] Do not expect values set via this property to work correctly. @@ -44,15 +41,12 @@ class ProxyWindowBase: public Reloadable { Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); - Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged); Q_PROPERTY(QuickshellScreenInfo* screen READ screen WRITE setScreen NOTIFY screenChanged); Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged); Q_PROPERTY(PendingRegion* mask READ mask WRITE setMask NOTIFY maskChanged); Q_PROPERTY(QObject* windowTransform READ windowTransform NOTIFY windowTransformChanged); Q_PROPERTY(bool backingWindowVisible READ isVisibleDirect NOTIFY backerVisibilityChanged); - Q_PROPERTY(QsSurfaceFormat surfaceFormat READ surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged); Q_PROPERTY(QQmlListProperty data READ data); - // clang-format on Q_CLASSINFO("DefaultProperty", "data"); public: @@ -65,7 +59,6 @@ public: void operator=(ProxyWindowBase&&) = delete; void onReload(QObject* oldInstance) override; - void ensureQWindow(); void createWindow(); void deleteWindow(bool keepItemOwnership = false); @@ -87,8 +80,6 @@ public: virtual void setVisible(bool visible); virtual void setVisibleDirect(bool visible); - void schedulePolish(); - [[nodiscard]] virtual qint32 x() const; [[nodiscard]] virtual qint32 y() const; @@ -98,10 +89,7 @@ public: [[nodiscard]] virtual qint32 height() const; virtual void setHeight(qint32 height); - [[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; @@ -110,9 +98,6 @@ public: [[nodiscard]] PendingRegion* mask() const; virtual void setMask(PendingRegion* mask); - [[nodiscard]] QsSurfaceFormat surfaceFormat() const { return this->qsSurfaceFormat; } - void setSurfaceFormat(QsSurfaceFormat format); - [[nodiscard]] QObject* windowTransform() const { return nullptr; } // NOLINT [[nodiscard]] QQmlListProperty data(); @@ -126,13 +111,10 @@ signals: void yChanged(); void widthChanged(); void heightChanged(); - void devicePixelRatioChanged(); void windowTransformChanged(); void screenChanged(); void colorChanged(); void maskChanged(); - void surfaceFormatChanged(); - void polished(); protected slots: virtual void onWidthChanged(); @@ -140,7 +122,6 @@ protected slots: void onMaskChanged(); void onMaskDestroyed(); void onScreenDestroyed(); - void onPolished(); void runLints(); protected: @@ -151,15 +132,9 @@ protected: QColor mColor = Qt::white; PendingRegion* mMask = nullptr; ProxiedWindow* window = nullptr; - ProxyWindowContentItem* mContentItem = nullptr; + QQuickItem* mContentItem = nullptr; bool reloadComplete = false; bool ranLints = false; - QsSurfaceFormat qsSurfaceFormat; - QSurfaceFormat mSurfaceFormat; - - struct { - bool inputMask : 1 = false; - } pendingPolish; private: void polishItems(); @@ -197,22 +172,10 @@ public: signals: void exposed(); - void devicePixelRatioChanged(); protected: - bool event(QEvent* event) override; void exposeEvent(QExposeEvent* event) override; private: ProxyWindowBase* mProxy; }; - -class ProxyWindowContentItem: public QQuickItem { - Q_OBJECT; - -signals: - void polished(); - -protected: - void updatePolish() override; -}; diff --git a/src/window/windowinterface.hpp b/src/window/windowinterface.hpp index 2aca7a76..c969a21d 100644 --- a/src/window/windowinterface.hpp +++ b/src/window/windowinterface.hpp @@ -15,26 +15,6 @@ class ProxyWindowBase; class QsWindowAttached; -class QsSurfaceFormat { - Q_GADGET; - QML_VALUE_TYPE(surfaceFormat); - QML_STRUCTURED_VALUE; - Q_PROPERTY(bool opaque MEMBER opaque WRITE setOpaque); - -public: - bool opaque = false; - bool opaqueModified = false; - - void setOpaque(bool opaque) { - this->opaque = opaque; - this->opaqueModified = true; - } - - [[nodiscard]] bool operator==(const QsSurfaceFormat& other) const { - return other.opaqueModified == this->opaqueModified && other.opaque == this->opaque; - } -}; - ///! Base class of Quickshell windows /// Base class of Quickshell windows /// ### Attached properties @@ -55,12 +35,6 @@ class WindowInterface: public Reloadable { Q_PROPERTY(bool backingWindowVisible READ isBackingWindowVisible NOTIFY backingWindowVisibleChanged); Q_PROPERTY(qint32 width READ width WRITE setWidth NOTIFY widthChanged); Q_PROPERTY(qint32 height READ height WRITE setHeight NOTIFY heightChanged); - /// The ratio between logical pixels and monitor pixels. - /// - /// Qt's coordinate system works in logical pixels, which equal N monitor pixels - /// depending on scale factor. This property returns the amount of monitor pixels - /// in a logical pixel for the current window. - Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged); /// The screen that the window currently occupies. /// /// This may be modified to move the window to the given screen. @@ -72,10 +46,6 @@ class WindowInterface: public Reloadable { /// along with map[To|From]Item (which is not reactive). Q_PROPERTY(QObject* windowTransform READ windowTransform NOTIFY windowTransformChanged); /// The background color of the window. Defaults to white. - /// - /// > [!WARNING] If the window color is opaque before it is made visible, - /// > it will not be able to become transparent later unless @@surfaceFormat$.opaque - /// > is false. Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged); /// The clickthrough mask. Defaults to null. /// @@ -120,16 +90,6 @@ class WindowInterface: public Reloadable { /// } /// ``` Q_PROPERTY(PendingRegion* mask READ mask WRITE setMask NOTIFY maskChanged); - /// Set the surface format to request from the system. - /// - /// - `opaque` - If the requested surface should be opaque. Opaque windows allow - /// the operating system to avoid drawing things behind them, or blending the window - /// with those behind it, saving power and GPU load. If unset, this property defaults to - /// true if @@color is opaque, or false if not. *You should not need to modify this - /// property unless you create a surface that starts opaque and later becomes transparent.* - /// - /// > [!NOTE] The surface format cannot be changed after the window is created. - Q_PROPERTY(QsSurfaceFormat surfaceFormat READ surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged); Q_PROPERTY(QQmlListProperty data READ data); // clang-format on Q_CLASSINFO("DefaultProperty", "data"); @@ -153,8 +113,6 @@ public: [[nodiscard]] virtual qint32 height() const = 0; virtual void setHeight(qint32 height) = 0; - [[nodiscard]] virtual qreal devicePixelRatio() const = 0; - [[nodiscard]] virtual QuickshellScreenInfo* screen() const = 0; virtual void setScreen(QuickshellScreenInfo* screen) = 0; @@ -166,9 +124,6 @@ public: [[nodiscard]] virtual PendingRegion* mask() const = 0; virtual void setMask(PendingRegion* mask) = 0; - [[nodiscard]] virtual QsSurfaceFormat surfaceFormat() const = 0; - virtual void setSurfaceFormat(QsSurfaceFormat format) = 0; - [[nodiscard]] virtual QQmlListProperty data() = 0; static QsWindowAttached* qmlAttachedProperties(QObject* object); @@ -179,12 +134,10 @@ signals: void backingWindowVisibleChanged(); void widthChanged(); void heightChanged(); - void devicePixelRatioChanged(); void screenChanged(); void windowTransformChanged(); void colorChanged(); void maskChanged(); - void surfaceFormatChanged(); }; class QsWindowAttached: public QObject { diff --git a/src/x11/i3/ipc/connection.cpp b/src/x11/i3/ipc/connection.cpp index b6aa1513..8ce78228 100644 --- a/src/x11/i3/ipc/connection.cpp +++ b/src/x11/i3/ipc/connection.cpp @@ -28,12 +28,10 @@ #include "monitor.hpp" #include "workspace.hpp" -namespace qs::i3::ipc { - -namespace { Q_LOGGING_CATEGORY(logI3Ipc, "quickshell.I3.ipc", QtWarningMsg); Q_LOGGING_CATEGORY(logI3IpcEvents, "quickshell.I3.ipc.events", QtWarningMsg); -} // namespace + +namespace qs::i3::ipc { void I3Ipc::makeRequest(const QByteArray& request) { if (!this->valid) { @@ -264,8 +262,9 @@ void I3Ipc::handleGetWorkspacesEvent(I3IpcEvent* event) { auto object = entry.toObject().toVariantMap(); auto name = object["name"].toString(); - auto workspaceIter = - std::ranges::find_if(mList, [name](const I3Workspace* m) { return m->name() == name; }); + auto workspaceIter = std::find_if(mList.begin(), mList.end(), [name](const I3Workspace* m) { + return m->name() == name; + }); auto* workspace = workspaceIter == mList.end() ? nullptr : *workspaceIter; auto existed = workspace != nullptr; @@ -320,8 +319,9 @@ void I3Ipc::handleGetOutputsEvent(I3IpcEvent* event) { auto object = elem.toObject().toVariantMap(); auto name = object["name"].toString(); - auto monitorIter = - std::ranges::find_if(mList, [name](const I3Monitor* m) { return m->name() == name; }); + auto monitorIter = std::find_if(mList.begin(), mList.end(), [name](const I3Monitor* m) { + return m->name() == name; + }); auto* monitor = monitorIter == mList.end() ? nullptr : *monitorIter; auto existed = monitor != nullptr; @@ -477,23 +477,25 @@ I3Monitor* I3Ipc::monitorFor(QuickshellScreenInfo* screen) { I3Workspace* I3Ipc::findWorkspaceByID(qint32 id) { auto list = this->mWorkspaces.valueList(); auto workspaceIter = - std::ranges::find_if(list, [id](const I3Workspace* m) { return m->id() == id; }); + std::find_if(list.begin(), list.end(), [id](const I3Workspace* m) { return m->id() == id; }); return workspaceIter == list.end() ? nullptr : *workspaceIter; } I3Workspace* I3Ipc::findWorkspaceByName(const QString& name) { auto list = this->mWorkspaces.valueList(); - auto workspaceIter = - std::ranges::find_if(list, [name](const I3Workspace* m) { return m->name() == name; }); + auto workspaceIter = std::find_if(list.begin(), list.end(), [name](const I3Workspace* m) { + return m->name() == name; + }); return workspaceIter == list.end() ? nullptr : *workspaceIter; } I3Monitor* I3Ipc::findMonitorByName(const QString& name) { auto list = this->mMonitors.valueList(); - auto monitorIter = - std::ranges::find_if(list, [name](const I3Monitor* m) { return m->name() == name; }); + auto monitorIter = std::find_if(list.begin(), list.end(), [name](const I3Monitor* m) { + return m->name() == name; + }); return monitorIter == list.end() ? nullptr : *monitorIter; } diff --git a/src/x11/panel_window.cpp b/src/x11/panel_window.cpp index ac182f76..c133abd3 100644 --- a/src/x11/panel_window.cpp +++ b/src/x11/panel_window.cpp @@ -478,12 +478,10 @@ XPanelInterface::XPanelInterface(QObject* parent) QObject::connect(this->panel, &ProxyWindowBase::backerVisibilityChanged, this, &XPanelInterface::backingWindowVisibleChanged); QObject::connect(this->panel, &ProxyWindowBase::heightChanged, this, &XPanelInterface::heightChanged); QObject::connect(this->panel, &ProxyWindowBase::widthChanged, this, &XPanelInterface::widthChanged); - QObject::connect(this->panel, &ProxyWindowBase::devicePixelRatioChanged, this, &XPanelInterface::devicePixelRatioChanged); QObject::connect(this->panel, &ProxyWindowBase::screenChanged, this, &XPanelInterface::screenChanged); QObject::connect(this->panel, &ProxyWindowBase::windowTransformChanged, this, &XPanelInterface::windowTransformChanged); QObject::connect(this->panel, &ProxyWindowBase::colorChanged, this, &XPanelInterface::colorChanged); QObject::connect(this->panel, &ProxyWindowBase::maskChanged, this, &XPanelInterface::maskChanged); - QObject::connect(this->panel, &ProxyWindowBase::surfaceFormatChanged, this, &XPanelInterface::surfaceFormatChanged); // panel specific QObject::connect(this->panel, &XPanelWindow::anchorsChanged, this, &XPanelInterface::anchorsChanged); @@ -506,7 +504,6 @@ QQmlListProperty XPanelInterface::data() { return this->panel->data(); ProxyWindowBase* XPanelInterface::proxyWindow() const { return this->panel; } QQuickItem* XPanelInterface::contentItem() const { return this->panel->contentItem(); } bool XPanelInterface::isBackingWindowVisible() const { return this->panel->isVisibleDirect(); } -qreal XPanelInterface::devicePixelRatio() const { return this->panel->devicePixelRatio(); } // NOLINTBEGIN #define proxyPair(type, get, set) \ @@ -519,7 +516,6 @@ proxyPair(qint32, height, setHeight); proxyPair(QuickshellScreenInfo*, screen, setScreen); proxyPair(QColor, color, setColor); proxyPair(PendingRegion*, mask, setMask); -proxyPair(QsSurfaceFormat, surfaceFormat, setSurfaceFormat); // panel specific proxyPair(Anchors, anchors, setAnchors); diff --git a/src/x11/panel_window.hpp b/src/x11/panel_window.hpp index 11041f22..b37c9c50 100644 --- a/src/x11/panel_window.hpp +++ b/src/x11/panel_window.hpp @@ -127,8 +127,6 @@ public: [[nodiscard]] qint32 height() const override; void setHeight(qint32 height) override; - [[nodiscard]] virtual qreal devicePixelRatio() const override; - [[nodiscard]] QuickshellScreenInfo* screen() const override; void setScreen(QuickshellScreenInfo* screen) override; @@ -138,9 +136,6 @@ public: [[nodiscard]] PendingRegion* mask() const override; void setMask(PendingRegion* mask) override; - [[nodiscard]] QsSurfaceFormat surfaceFormat() const override; - void setSurfaceFormat(QsSurfaceFormat mask) override; - [[nodiscard]] QQmlListProperty data() override; // panel specific