diff --git a/changelog/next.md b/changelog/next.md index 0feffe1..ef63323 100644 --- a/changelog/next.md +++ b/changelog/next.md @@ -50,6 +50,7 @@ set shell id. - Fixed ClippingRectangle related crashes. - Fixed crashes when monitors are unplugged. - Fixed crashes when default pipewire devices are lost. +- Desktop action order is now preserved. ## Packaging Changes diff --git a/default.nix b/default.nix index 59e68b0..02b8659 100644 --- a/default.nix +++ b/default.nix @@ -19,6 +19,7 @@ wayland-protocols, wayland-scanner, xorg, + libxcb ? xorg.libxcb, libdrm, libgbm ? null, vulkan-headers, @@ -88,7 +89,7 @@ ++ lib.optional (withWayland && lib.strings.compareVersions qt6.qtbase.version "6.10.0" == -1) qt6.qtwayland ++ lib.optionals withWayland [ wayland wayland-protocols ] ++ lib.optionals (withWayland && libgbm != null) [ libdrm libgbm vulkan-headers ] - ++ lib.optional withX11 xorg.libxcb + ++ lib.optional withX11 libxcb ++ lib.optional withPam pam ++ lib.optional withPipewire pipewire ++ lib.optionals withPolkit [ polkit glib ]; diff --git a/src/core/desktopentry.cpp b/src/core/desktopentry.cpp index 2dbafea..637f758 100644 --- a/src/core/desktopentry.cpp +++ b/src/core/desktopentry.cpp @@ -107,7 +107,10 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString& auto groupName = QString(); auto entries = QHash>(); - auto finishCategory = [&data, &groupName, &entries]() { + auto actionOrder = QStringList(); + auto pendingActions = QHash(); + + auto finishCategory = [&data, &groupName, &entries, &actionOrder, &pendingActions]() { if (groupName == "Desktop Entry") { if (entries.value("Type").second != "Application") return; @@ -129,9 +132,10 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString& else if (key == "Terminal") data.terminal = value == "true"; else if (key == "Categories") data.categories = value.split(u';', Qt::SkipEmptyParts); else if (key == "Keywords") data.keywords = value.split(u';', Qt::SkipEmptyParts); + else if (key == "Actions") actionOrder = value.split(u';', Qt::SkipEmptyParts); } } else if (groupName.startsWith("Desktop Action ")) { - auto actionName = groupName.sliced(16); + auto actionName = groupName.sliced(15); DesktopActionData action; action.id = actionName; @@ -147,7 +151,7 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString& } } - data.actions.insert(actionName, action); + pendingActions.insert(actionName, action); } entries.clear(); @@ -193,6 +197,13 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString& } finishCategory(); + + for (const auto& actionId: actionOrder) { + if (pendingActions.contains(actionId)) { + data.actions.append(pendingActions.value(actionId)); + } + } + return data; } @@ -216,17 +227,18 @@ void DesktopEntry::updateState(const ParsedDesktopEntryData& newState) { this->updateActions(newState.actions); } -void DesktopEntry::updateActions(const QHash& newActions) { +void DesktopEntry::updateActions(const QVector& newActions) { auto old = this->mActions; + this->mActions.clear(); - for (const auto& [key, d]: newActions.asKeyValueRange()) { + for (const auto& d: newActions) { DesktopAction* act = nullptr; - if (auto found = old.find(key); found != old.end()) { - act = found.value(); + auto found = std::ranges::find(old, d.id, &DesktopAction::mId); + if (found != old.end()) { + act = *found; old.erase(found); } else { act = new DesktopAction(d.id, this); - this->mActions.insert(key, act); } Qt::beginPropertyUpdateGroup(); @@ -237,6 +249,7 @@ void DesktopEntry::updateActions(const QHash& newAct Qt::endPropertyUpdateGroup(); act->mEntries = d.entries; + this->mActions.append(act); } for (auto* leftover: old) { @@ -250,7 +263,7 @@ void DesktopEntry::execute() const { bool DesktopEntry::isValid() const { return !this->bName.value().isEmpty(); } -QVector DesktopEntry::actions() const { return this->mActions.values(); } +QVector DesktopEntry::actions() const { return this->mActions; } QVector DesktopEntry::parseExecString(const QString& execString) { QVector arguments; diff --git a/src/core/desktopentry.hpp b/src/core/desktopentry.hpp index 623019d..0d1eff2 100644 --- a/src/core/desktopentry.hpp +++ b/src/core/desktopentry.hpp @@ -43,7 +43,7 @@ struct ParsedDesktopEntryData { QVector categories; QVector keywords; QHash entries; - QHash actions; + QVector actions; }; /// A desktop entry. See @@DesktopEntries for details. @@ -164,10 +164,10 @@ public: // clang-format on private: - void updateActions(const QHash& newActions); + void updateActions(const QVector& newActions); ParsedDesktopEntryData state; - QHash mActions; + QVector mActions; friend class DesktopAction; }; diff --git a/src/services/pipewire/core.cpp b/src/services/pipewire/core.cpp index e40bc54..5077abe 100644 --- a/src/services/pipewire/core.cpp +++ b/src/services/pipewire/core.cpp @@ -143,12 +143,17 @@ void PwCore::onSync(void* data, quint32 id, qint32 seq) { void PwCore::onError(void* data, quint32 id, qint32 /*seq*/, qint32 res, const char* message) { auto* self = static_cast(data); - if (message != nullptr) { - qCWarning(logLoop) << "Fatal pipewire error on object" << id << "with code" << res << message; - } else { - qCWarning(logLoop) << "Fatal pipewire error on object" << id << "with code" << res; + // Pipewire's documentation describes the error event as being fatal, however it isn't. + // We're not sure what causes these ENOENTs on device removal, presumably something in + // the teardown sequence, but they're harmless. Attempting to handle them as a fatal + // error causes unnecessary triggers for shells. + if (res == -ENOENT) { + qCDebug(logLoop) << "Pipewire ENOENT on object" << id << "with code" << res << message; + return; } + qCWarning(logLoop) << "Pipewire error on object" << id << "with code" << res << message; + emit self->fatalError(); }