Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WirePlumber Module] Improvement, Added source node #1905

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions include/modules/wireplumber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,27 @@ class Wireplumber : public ALabel {
void loadRequiredApiModules();
void prepare();
void activatePlugins();
static void updateVolume(waybar::modules::Wireplumber* self);
static void updateNodeName(waybar::modules::Wireplumber* self);
static uint32_t getDefaultNodeId(waybar::modules::Wireplumber* self);
static void updateSinkVolume(waybar::modules::Wireplumber* self);
static void updateSourceVolume(waybar::modules::Wireplumber* self);
static void updateSinkNodeName(waybar::modules::Wireplumber* self);
static void updateSourceNodeName(waybar::modules::Wireplumber* self);
static uint32_t getDefaultSinkNodeId(waybar::modules::Wireplumber* self);
static uint32_t getDefaultSourceNodeId(waybar::modules::Wireplumber* self);
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
static void onObjectManagerInstalled(waybar::modules::Wireplumber* self);

WpCore* wp_core_;
GPtrArray* apis_;
WpObjectManager* om_;
uint32_t pending_plugins_;
bool muted_;
double volume_;
uint32_t node_id_{0};
std::string node_name_;
bool sinkmuted_;
double sinkvolume_;
uint32_t sinknode_id_{0};
bool sourcemuted_;
double sourcevolume_;
uint32_t sourcenode_id_{0};
std::string sinknode_name_;
std::string sourcenode_name_;
};

} // namespace waybar::modules
53 changes: 43 additions & 10 deletions man/waybar-wireplumber.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,34 @@ The *wireplumber* module displays the current volume reported by WirePlumber.

*format*: ++
typeof: string ++
default: *{volume}%* ++
default: *{format_sink} {format_source}* ++
The format, how information should be displayed. This format is used when other formats aren't specified.

*format-muted*: ++
*format-sink*: ++
typeof: string ++
This format is used when the sound is muted.
default: *OUT: {volume}%* ++
This format is used when the sink node is not muted.

*format-source*: ++
typeof: string ++
default: *IN: {volume}%* ++
This format is used when the source node is not muted.

*format-sink-muted*: ++
typeof: string ++
This format is used when the sink node is muted.

*format-source-muted*: ++
typeof: string ++
This format is used when the source node is muted.

*sinknode-id*: ++
typeof: integer ++
Force ID of the sink node

*sourcenode-id*: ++
typeof: integer ++
Force ID of the source node

*tooltip*: ++
typeof: bool ++
Expand All @@ -26,7 +48,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber.

*tooltip-format*: ++
typeof: string ++
default: *{node_name}* ++
default: *Sink: {sinknode_name} Source: {sourcenode_name}* ++
The format of information displayed in the tooltip.

*rotate*: ++
Expand Down Expand Up @@ -69,19 +91,30 @@ The *wireplumber* module displays the current volume reported by WirePlumber.

*{volume}*: Volume in percentage.

*{node_name}*: The node's nickname as reported by WirePlumber (*node.nick* property)
*{sinknode_name}*: The sink node's nickname as reported by WirePlumber (*node.nick* property)

*{sourcenode_name}*: The source node's nickname as reported by WirePlumber (*node.nick* property)

Only in *{format}*:
*{format_sink}*: Represents the field *format-sink* in config
*{format_source}*: Represents the field *format-source* in config

# EXAMPLES

```
"wireplumber": {
"format": "{volume}%",
"format-muted": "",
"on-click": "helvum"
}
"format": "{format_sink} {format_source}",
"format-sink": "OUT: {volume}%",
"format-sink-muted": "OUT: MUTED",
"format-source": "IN: {volume}%",
"format-source-muted": "IN: MUTED",
"tooltip": true,
"on-click": "qpwgraph",
},
```

# STYLE

- *#wireplumber*
- *#wireplumber.muted*
- *#wireplumber.sinkmuted*
- *#wireplumber.sourcemuted*
153 changes: 121 additions & 32 deletions src/modules/wireplumber.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#include "modules/wireplumber.hpp"

waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Value& config)
: ALabel(config, "wireplumber", id, "{volume}%"),
: ALabel(config, "wireplumber", id, "{format_sink} {format_source}"),
wp_core_(nullptr),
apis_(nullptr),
om_(nullptr),
pending_plugins_(0),
muted_(false),
volume_(0.0),
node_id_(0) {
sinkmuted_(false),
sinkvolume_(0.0),
sinknode_id_(0),
sourcemuted_(false),
sourcevolume_(0.0),
sourcenode_id_(0) {
wp_init(WP_INIT_ALL);
wp_core_ = wp_core_new(NULL, NULL);
apis_ = g_ptr_array_new_with_free_func(g_object_unref);
Expand All @@ -35,7 +38,7 @@ waybar::modules::Wireplumber::~Wireplumber() {
g_clear_object(&wp_core_);
}

uint32_t waybar::modules::Wireplumber::getDefaultNodeId(waybar::modules::Wireplumber* self) {
uint32_t waybar::modules::Wireplumber::getDefaultSinkNodeId(waybar::modules::Wireplumber* self) {
uint32_t id;
g_autoptr(WpPlugin) def_nodes_api = wp_plugin_find(self->wp_core_, "default-nodes-api");

Expand All @@ -53,13 +56,31 @@ uint32_t waybar::modules::Wireplumber::getDefaultNodeId(waybar::modules::Wireplu
return id;
}

void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self) {
uint32_t waybar::modules::Wireplumber::getDefaultSourceNodeId(waybar::modules::Wireplumber* self) {
uint32_t id;
g_autoptr(WpPlugin) def_nodes_api = wp_plugin_find(self->wp_core_, "default-nodes-api");

if (!def_nodes_api) {
throw std::runtime_error("Default nodes API is not loaded\n");
}

g_signal_emit_by_name(def_nodes_api, "get-default-node", "Audio/Source", &id);

if (id <= 0 || id >= G_MAXUINT32) {
auto err = fmt::format("'{}' is not a valid ID (returned by default-nodes-api)\n", id);
throw std::runtime_error(err);
}

return id;
}

void waybar::modules::Wireplumber::updateSinkNodeName(waybar::modules::Wireplumber* self) {
auto proxy = static_cast<WpProxy*>(
wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY,
"bound-id", "=u", self->node_id_, NULL));
"bound-id", "=u", self->sinknode_id_, NULL));

if (!proxy) {
throw std::runtime_error(fmt::format("Object '{}' not found\n", self->node_id_));
throw std::runtime_error(fmt::format("Object '{}' not found\n", self->sinknode_id_));
}

g_autoptr(WpProperties) properties =
Expand All @@ -72,36 +93,82 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber*
auto nick = wp_properties_get(properties, "node.nick");
auto description = wp_properties_get(properties, "node.description");

self->node_name_ = nick ? nick : description;
self->sinknode_name_ = nick ? nick : description;
}

void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self) {
void waybar::modules::Wireplumber::updateSourceNodeName(waybar::modules::Wireplumber* self) {
auto proxy = static_cast<WpProxy*>(
wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY,
"bound-id", "=u", self->sourcenode_id_, NULL));

if (!proxy) {
throw std::runtime_error(fmt::format("Object '{}' not found\n", self->sourcenode_id_));
}

g_autoptr(WpProperties) properties =
WP_IS_PIPEWIRE_OBJECT(proxy) ? wp_pipewire_object_get_properties(WP_PIPEWIRE_OBJECT(proxy))
: wp_properties_new_empty();
g_autoptr(WpProperties) global_p = wp_global_proxy_get_global_properties(WP_GLOBAL_PROXY(proxy));
properties = wp_properties_ensure_unique_owner(properties);
wp_properties_add(properties, global_p);
wp_properties_set(properties, "object.id", NULL);
auto nick = wp_properties_get(properties, "node.nick");
auto description = wp_properties_get(properties, "node.description");

self->sourcenode_name_ = nick ? nick : description;
}

void waybar::modules::Wireplumber::updateSinkVolume(waybar::modules::Wireplumber* self) {
double vol;
GVariant* variant = NULL;
g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api");
g_signal_emit_by_name(mixer_api, "get-volume", self->sinknode_id_, &variant);
if (!variant) {
auto err = fmt::format("Sink Node {} does not support volume\n", self->sinknode_id_);
throw std::runtime_error(err);
}

g_variant_lookup(variant, "volume", "d", &vol);
g_variant_lookup(variant, "mute", "b", &self->sinkmuted_);
g_clear_pointer(&variant, g_variant_unref);

self->sinkvolume_ = std::round(vol * 100.0F);
self->dp.emit();
}

void waybar::modules::Wireplumber::updateSourceVolume(waybar::modules::Wireplumber* self) {
double vol;
GVariant* variant = NULL;
g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api");
g_signal_emit_by_name(mixer_api, "get-volume", self->node_id_, &variant);
g_signal_emit_by_name(mixer_api, "get-volume", self->sourcenode_id_, &variant);
if (!variant) {
auto err = fmt::format("Node {} does not support volume\n", self->node_id_);
auto err = fmt::format("Source Node {} does not support volume\n", self->sourcenode_id_);
throw std::runtime_error(err);
}

g_variant_lookup(variant, "volume", "d", &vol);
g_variant_lookup(variant, "mute", "b", &self->muted_);
g_variant_lookup(variant, "mute", "b", &self->sourcemuted_);
g_clear_pointer(&variant, g_variant_unref);

self->volume_ = std::round(vol * 100.0F);
self->sourcevolume_ = std::round(vol * 100.0F);
self->dp.emit();
}

void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wireplumber* self) {
self->node_id_ =
self->config_["node-id"].isInt() ? self->config_["node-id"].asInt() : getDefaultNodeId(self);
self->sinknode_id_ = self->config_["sinknode-id"].isInt() ? self->config_["sinknode-id"].asInt()
: getDefaultSinkNodeId(self);
self->sourcenode_id_ = self->config_["sourcenode-id"].isInt()
? self->config_["sourcenode-id"].asInt()
: getDefaultSourceNodeId(self);

g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api");

updateVolume(self);
updateNodeName(self);
g_signal_connect_swapped(mixer_api, "changed", (GCallback)updateVolume, self);
updateSinkVolume(self);
updateSourceVolume(self);
updateSinkNodeName(self);
updateSourceNodeName(self);
g_signal_connect_swapped(mixer_api, "changed", (GCallback)updateSinkVolume, self);
g_signal_connect_swapped(mixer_api, "changed", (GCallback)updateSourceVolume, self);
}

void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult* res,
Expand Down Expand Up @@ -155,33 +222,55 @@ void waybar::modules::Wireplumber::loadRequiredApiModules() {
}

auto waybar::modules::Wireplumber::update() -> void {
auto format = format_;
auto format = config_["format"].isString() ? config_["format"].asString() : format_;
std::string sinkformat =
fmt::format(config_["format-sink"].isString() ? config_["format-sink"].asString()
: std::string("OUT: {volume}%"),
fmt::arg("volume", sinkvolume_), fmt::arg("sinknode_name", sinknode_name_),
fmt::arg("sourcenode_name", sourcenode_name_));
std::string sourceformat =
fmt::format(config_["format-source"].isString() ? config_["format-source"].asString()
: std::string("IN: {volume}%"),
fmt::arg("volume", sourcevolume_), fmt::arg("sinknode_name", sinknode_name_),
fmt::arg("sourcenode_name", sourcenode_name_));

std::string tooltip_format;

if (muted_) {
format = config_["format-muted"].isString() ? config_["format-muted"].asString() : format;
label_.get_style_context()->add_class("muted");
if (sinkmuted_) {
if (config_["format-sink-muted"].isString()) {
sinkformat = config_["format-sink-muted"].asString();
}
label_.get_style_context()->add_class("sinkmuted");
} else {
label_.get_style_context()->remove_class("muted");
label_.get_style_context()->remove_class("sinkmuted");
}

std::string markup = fmt::format(format, fmt::arg("node_name", node_name_),
fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_)));
if (sourcemuted_) {
if (config_["format-source-muted"].isString()) {
sourceformat = config_["format-source-muted"].asString();
}
label_.get_style_context()->add_class("sourcemuted");
} else {
label_.get_style_context()->remove_class("sourcemuted");
}
std::string markup = fmt::format(format, fmt::arg("format_sink", sinkformat),
fmt::arg("format_source", sourceformat));
label_.set_markup(markup);

getState(volume_);
getState(sinkvolume_);
getState(sourcevolume_);

if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}

if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("node_name", node_name_),
fmt::arg("volume", volume_),
fmt::arg("icon", getIcon(volume_))));
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("sinknode_name", sinknode_name_),
fmt::arg("sourcenode_name", sourcenode_name_),
fmt::arg("sinkvolume", sinkformat),
fmt::arg("sourcevolume", sourceformat)));
} else {
label_.set_tooltip_text(node_name_);
label_.set_tooltip_text(fmt::format("Sink: {} Source: {}", sinknode_name_, sourcenode_name_));
}
}

Expand Down