Waybar/src/util/pipewire/pipewire_backend.cpp

153 lines
4.8 KiB
C++

#include "util/pipewire/pipewire_backend.hpp"
#include "util/pipewire/privacy_node_info.hpp"
namespace waybar::util::PipewireBackend {
static void getNodeInfo(void *data_, const struct pw_node_info *info) {
auto *pNodeInfo = static_cast<PrivacyNodeInfo *>(data_);
pNodeInfo->handleNodeEventInfo(info);
static_cast<PipewireBackend *>(pNodeInfo->data)->privacy_nodes_changed_signal_event.emit();
}
static const struct pw_node_events NODE_EVENTS = {
.version = PW_VERSION_NODE_EVENTS,
.info = getNodeInfo,
};
static void proxyDestroy(void *data) {
static_cast<PrivacyNodeInfo *>(data)->handleProxyEventDestroy();
}
static const struct pw_proxy_events PROXY_EVENTS = {
.version = PW_VERSION_PROXY_EVENTS,
.destroy = proxyDestroy,
};
static void registryEventGlobal(void *_data, uint32_t id, uint32_t permissions, const char *type,
uint32_t version, const struct spa_dict *props) {
static_cast<PipewireBackend *>(_data)->handleRegistryEventGlobal(id, permissions, type, version,
props);
}
static void registryEventGlobalRemove(void *_data, uint32_t id) {
static_cast<PipewireBackend *>(_data)->handleRegistryEventGlobalRemove(id);
}
static const struct pw_registry_events REGISTRY_EVENTS = {
.version = PW_VERSION_REGISTRY_EVENTS,
.global = registryEventGlobal,
.global_remove = registryEventGlobalRemove,
};
PipewireBackend::PipewireBackend(PrivateConstructorTag tag)
: mainloop_(nullptr), context_(nullptr), core_(nullptr) {
pw_init(nullptr, nullptr);
mainloop_ = pw_thread_loop_new("waybar", nullptr);
if (mainloop_ == nullptr) {
throw std::runtime_error("pw_thread_loop_new() failed.");
}
pw_thread_loop_lock(mainloop_);
context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0);
if (context_ == nullptr) {
pw_thread_loop_unlock(mainloop_);
throw std::runtime_error("pa_context_new() failed.");
}
core_ = pw_context_connect(context_, nullptr, 0);
if (core_ == nullptr) {
pw_thread_loop_unlock(mainloop_);
throw std::runtime_error("pw_context_connect() failed");
}
registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0);
spa_zero(registryListener_);
pw_registry_add_listener(registry_, &registryListener_, &REGISTRY_EVENTS, this);
if (pw_thread_loop_start(mainloop_) < 0) {
pw_thread_loop_unlock(mainloop_);
throw std::runtime_error("pw_thread_loop_start() failed.");
}
pw_thread_loop_unlock(mainloop_);
}
PipewireBackend::~PipewireBackend() {
if (mainloop_ != nullptr) {
pw_thread_loop_lock(mainloop_);
}
if (registry_ != nullptr) {
pw_proxy_destroy((struct pw_proxy *)registry_);
}
spa_zero(registryListener_);
if (core_ != nullptr) {
pw_core_disconnect(core_);
}
if (context_ != nullptr) {
pw_context_destroy(context_);
}
if (mainloop_ != nullptr) {
pw_thread_loop_unlock(mainloop_);
pw_thread_loop_stop(mainloop_);
pw_thread_loop_destroy(mainloop_);
}
}
std::shared_ptr<PipewireBackend> PipewireBackend::getInstance() {
PrivateConstructorTag tag;
return std::make_shared<PipewireBackend>(tag);
}
void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permissions, const char *type,
uint32_t version, const struct spa_dict *props) {
if (props == nullptr || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return;
const char *lookupStr = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
if (lookupStr == nullptr) return;
std::string mediaClass = lookupStr;
enum PrivacyNodeType mediaType = PRIVACY_NODE_TYPE_NONE;
if (mediaClass == "Stream/Input/Video") {
mediaType = PRIVACY_NODE_TYPE_VIDEO_INPUT;
} else if (mediaClass == "Stream/Input/Audio") {
mediaType = PRIVACY_NODE_TYPE_AUDIO_INPUT;
} else if (mediaClass == "Stream/Output/Audio") {
mediaType = PRIVACY_NODE_TYPE_AUDIO_OUTPUT;
} else {
return;
}
auto *proxy = (pw_proxy *)pw_registry_bind(registry_, id, type, version, sizeof(PrivacyNodeInfo));
if (proxy == nullptr) return;
auto *pNodeInfo = (PrivacyNodeInfo *)pw_proxy_get_user_data(proxy);
pNodeInfo->id = id;
pNodeInfo->data = this;
pNodeInfo->type = mediaType;
pNodeInfo->media_class = mediaClass;
pw_proxy_add_listener(proxy, &pNodeInfo->proxy_listener, &PROXY_EVENTS, pNodeInfo);
pw_proxy_add_object_listener(proxy, &pNodeInfo->object_listener, &NODE_EVENTS, pNodeInfo);
privacy_nodes.insert_or_assign(id, pNodeInfo);
}
void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) {
mutex_.lock();
auto iter = privacy_nodes.find(id);
if (iter != privacy_nodes.end()) {
privacy_nodes.erase(id);
}
mutex_.unlock();
privacy_nodes_changed_signal_event.emit();
}
} // namespace waybar::util::PipewireBackend