Compare commits

...

2 commits

Author SHA1 Message Date
outfoxxed
e7cd1e9982
core: add env and isEnvSet functions to pragma context
Some checks failed
Build / Nix-6 (push) Has been cancelled
Build / Nix-7 (push) Has been cancelled
Build / Nix-8 (push) Has been cancelled
Build / Nix-9 (push) Has been cancelled
Build / Nix-10 (push) Has been cancelled
Build / Nix-11 (push) Has been cancelled
Build / Nix-12 (push) Has been cancelled
Build / Nix-13 (push) Has been cancelled
Build / Nix-14 (push) Has been cancelled
Build / Nix-15 (push) Has been cancelled
Build / Nix-16 (push) Has been cancelled
Build / Nix-17 (push) Has been cancelled
Build / Nix-18 (push) Has been cancelled
Build / Nix-19 (push) Has been cancelled
Build / Nix-20 (push) Has been cancelled
Build / Nix-21 (push) Has been cancelled
Build / Nix-22 (push) Has been cancelled
Build / Nix-23 (push) Has been cancelled
Build / Nix-24 (push) Has been cancelled
Build / Nix-25 (push) Has been cancelled
Build / Nix-26 (push) Has been cancelled
Build / Nix-27 (push) Has been cancelled
Build / Nix-28 (push) Has been cancelled
Build / Nix-29 (push) Has been cancelled
Build / Nix-30 (push) Has been cancelled
Build / Nix-31 (push) Has been cancelled
Build / Nix-32 (push) Has been cancelled
Build / Nix-33 (push) Has been cancelled
Build / Archlinux (push) Has been cancelled
Lint / Lint (push) Has been cancelled
2026-02-21 21:11:45 -08:00
Andrei
afbc5ffd4e
services/pipewire: use node volume control when device missing
Some outputs which present a pipewire device object do not present
routes, instead expecting volume to be set on the associated pipewire node.
2026-02-21 20:39:03 -08:00
7 changed files with 47 additions and 14 deletions

View file

@ -36,6 +36,7 @@ set shell id.
- Fixed volume control breaking with pipewire pro audio mode. - Fixed volume control breaking with pipewire pro audio mode.
- Fixed volume control breaking with bluez streams and potentially others. - Fixed volume control breaking with bluez streams and potentially others.
- Fixed volume control breaking for devices without route definitions.
- Fixed escape sequence handling in desktop entries. - Fixed escape sequence handling in desktop entries.
- Fixed volumes not initializing if a pipewire device was already loaded before its node. - Fixed volumes not initializing if a pipewire device was already loaded before its node.
- Fixed hyprland active toplevel not resetting after window closes. - Fixed hyprland active toplevel not resetting after window closes.

View file

@ -1,6 +1,7 @@
#include "scanenv.hpp" #include "scanenv.hpp"
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qtenvironmentvariables.h>
#include "build.hpp" #include "build.hpp"
@ -19,4 +20,12 @@ bool PreprocEnv::hasVersion(int major, int minor, const QStringList& features) {
return QS_VERSION_MAJOR == major && QS_VERSION_MINOR == minor; return QS_VERSION_MAJOR == major && QS_VERSION_MINOR == minor;
} }
QString PreprocEnv::env(const QString& variable) {
return qEnvironmentVariable(variable.toStdString().c_str());
}
bool PreprocEnv::isEnvSet(const QString& variable) {
return qEnvironmentVariableIsSet(variable.toStdString().c_str());
}
} // namespace qs::scan::env } // namespace qs::scan::env

View file

@ -12,6 +12,9 @@ class PreprocEnv: public QObject {
public: public:
Q_INVOKABLE static bool Q_INVOKABLE static bool
hasVersion(int major, int minor, const QStringList& features = QStringList()); hasVersion(int major, int minor, const QStringList& features = QStringList());
Q_INVOKABLE static QString env(const QString& variable);
Q_INVOKABLE static bool isEnvSet(const QString& variable);
}; };
} // namespace qs::scan::env } // namespace qs::scan::env

View file

@ -141,6 +141,10 @@ bool PwDevice::tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps
return true; return true;
} }
bool PwDevice::hasRouteDevice(qint32 routeDevice) const {
return this->routeDeviceIndexes.contains(routeDevice);
}
void PwDevice::polled() { void PwDevice::polled() {
// It is far more likely that the list content has not come in yet than it having no entries, // It is far more likely that the list content has not come in yet than it having no entries,
// and there isn't a way to check in the case that there *aren't* actually any entries. // and there isn't a way to check in the case that there *aren't* actually any entries.

View file

@ -12,13 +12,15 @@
#include <spa/pod/builder.h> #include <spa/pod/builder.h>
#include "core.hpp" #include "core.hpp"
#include "node.hpp"
#include "registry.hpp" #include "registry.hpp"
namespace qs::service::pipewire { namespace qs::service::pipewire {
class PwDevice; class PwDevice;
// Forward declare to avoid circular dependency with node.hpp
struct PwVolumeProps;
class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE> { class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE> {
Q_OBJECT; Q_OBJECT;
@ -33,6 +35,7 @@ public:
[[nodiscard]] bool waitingForDevice() const; [[nodiscard]] bool waitingForDevice() const;
[[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps); [[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps);
[[nodiscard]] bool hasRouteDevice(qint32 routeDevice) const;
signals: signals:
void deviceReady(); void deviceReady();

View file

@ -429,6 +429,10 @@ void PwNodeBoundAudio::setMuted(bool muted) {
} }
float PwNodeBoundAudio::averageVolume() const { float PwNodeBoundAudio::averageVolume() const {
if (this->mVolumes.isEmpty()) {
return 0.0f;
}
float total = 0; float total = 0;
for (auto volume: this->mVolumes) { for (auto volume: this->mVolumes) {
@ -572,9 +576,8 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
const auto* muteProp = spa_pod_find_prop(param, nullptr, SPA_PROP_mute); const auto* muteProp = spa_pod_find_prop(param, nullptr, SPA_PROP_mute);
const auto* volumeStepProp = spa_pod_find_prop(param, nullptr, SPA_PROP_volumeStep); const auto* volumeStepProp = spa_pod_find_prop(param, nullptr, SPA_PROP_volumeStep);
if (volumesProp) {
const auto* volumes = reinterpret_cast<const spa_pod_array*>(&volumesProp->value); const auto* volumes = reinterpret_cast<const spa_pod_array*>(&volumesProp->value);
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
spa_pod* iter = nullptr; spa_pod* iter = nullptr;
SPA_POD_ARRAY_FOREACH(volumes, iter) { SPA_POD_ARRAY_FOREACH(volumes, iter) {
// Cubing behavior found in MPD source, and appears to corrospond to everyone else's measurements correctly. // Cubing behavior found in MPD source, and appears to corrospond to everyone else's measurements correctly.
@ -582,12 +585,19 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
auto visual = std::cbrt(linear); auto visual = std::cbrt(linear);
props.volumes.push_back(visual); props.volumes.push_back(visual);
} }
}
if (channelsProp) {
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
spa_pod* iter = nullptr;
SPA_POD_ARRAY_FOREACH(channels, iter) { SPA_POD_ARRAY_FOREACH(channels, iter) {
props.channels.push_back(*reinterpret_cast<PwAudioChannel::Enum*>(iter)); props.channels.push_back(*reinterpret_cast<PwAudioChannel::Enum*>(iter));
} }
}
if (muteProp) {
spa_pod_get_bool(&muteProp->value, &props.mute); spa_pod_get_bool(&muteProp->value, &props.mute);
}
if (volumeStepProp) { if (volumeStepProp) {
spa_pod_get_float(&volumeStepProp->value, &props.volumeStep); spa_pod_get_float(&volumeStepProp->value, &props.volumeStep);

View file

@ -15,6 +15,7 @@
#include <spa/pod/pod.h> #include <spa/pod/pod.h>
#include "core.hpp" #include "core.hpp"
#include "device.hpp"
#include "registry.hpp" #include "registry.hpp"
namespace qs::service::pipewire { namespace qs::service::pipewire {
@ -249,7 +250,9 @@ public:
bool proAudio = false; bool proAudio = false;
[[nodiscard]] bool shouldUseDevice() const { [[nodiscard]] bool shouldUseDevice() const {
return this->device && !this->proAudio && this->routeDevice != -1; if (!this->device || this->proAudio || this->routeDevice == -1) return false;
// Only use device control if the device actually has route indexes for this routeDevice
return this->device->hasRouteDevice(this->routeDevice);
} }
signals: signals: