Skip to content

Commit

Permalink
Added tooltips
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikReider committed Oct 28, 2023
1 parent 46e36c0 commit d32da91
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 42 deletions.
15 changes: 11 additions & 4 deletions include/modules/privacy/privacy_item.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
#include "gtkmm/revealer.h"
#include "util/pipewire/privacy_node_info.hpp"

using waybar::util::PipewireBackend::PrivacyNodeInfo;
using waybar::util::PipewireBackend::PrivacyNodeType;

namespace waybar::modules::privacy {

class PrivacyItem : public Gtk::Revealer {
public:
PrivacyItem(const Json::Value&, enum util::PipewireBackend::PrivacyNodeType privacy_type_,
const std::string& pos);
PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_,
std::list<PrivacyNodeInfo *> *nodes, const std::string &pos);

bool is_enabled();

Expand All @@ -26,9 +29,10 @@ class PrivacyItem : public Gtk::Revealer {
void set_icon_size(uint size);

private:
enum util::PipewireBackend::PrivacyNodeType privacy_type;
enum PrivacyNodeType privacy_type;
std::list<PrivacyNodeInfo *> *nodes;

std::mutex mutex_;
Gtk::Box tooltip_window;

bool init = false;
bool in_use = false;
Expand All @@ -37,12 +41,15 @@ class PrivacyItem : public Gtk::Revealer {
// Config
bool enabled = true;
std::string iconName = "image-missing-symbolic";
bool tooltip = true;
uint tooltipIconSize = 24;

Gtk::Box box_;
Gtk::Image icon_;

void on_child_revealed_changed();
void on_map_changed();
void update_tooltip();
};

} // namespace waybar::modules::privacy
2 changes: 1 addition & 1 deletion include/util/pipewire/pipewire_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PipewireBackend {

sigc::signal<void> privacy_nodes_changed_signal_event;

std::unordered_map<uint32_t, PrivacyNodeInfo*> privacy_nodes;
std::unordered_map<uint32_t, PrivacyNodeInfo> privacy_nodes;

static std::shared_ptr<PipewireBackend> getInstance();

Expand Down
35 changes: 33 additions & 2 deletions include/util/pipewire/privacy_node_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <string>

#include "util/gtk_icon.hpp"

namespace waybar::util::PipewireBackend {

enum PrivacyNodeType {
Expand All @@ -22,15 +24,44 @@ class PrivacyNodeInfo {
std::string media_class;
std::string media_name;
std::string node_name;
std::string application_name;

std::string pipewire_access_portal_app_id;
std::string application_icon_name;

struct spa_hook node_listener;

bool changed = false;

void* data;
void *data;

PrivacyNodeInfo(uint32_t id_, void* data_) : id(id_), data(data_) {}
PrivacyNodeInfo(uint32_t id_, void *data_) : id(id_), data(data_) {}

~PrivacyNodeInfo() { spa_hook_remove(&node_listener); }

std::string get_name() {
const std::vector<std::string *> names{&application_name, &node_name};
std::string name = "Unknown Application";
for (auto &name_ : names) {
if (name_ != nullptr && name_->length() > 0) {
name = *name_;
name[0] = toupper(name[0]);
break;
}
}
return name;
}

std::string get_icon_name() {
const std::vector<std::string *> names{&application_icon_name, &pipewire_access_portal_app_id,
&application_name, &node_name};
std::string name = "application-x-executable-symbolic";
for (auto &name_ : names) {
if (name_ != nullptr && name_->length() > 0 && DefaultGtkIconThemeWrapper::has_icon(*name_)) {
return *name_;
}
}
return name;
}
};
} // namespace waybar::util::PipewireBackend
30 changes: 15 additions & 15 deletions src/modules/privacy/privacy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, const std::st
nodes_screenshare(),
nodes_audio_in(),
nodes_audio_out(),
privacy_item_screenshare(config["screenshare"], PRIVACY_NODE_TYPE_VIDEO_INPUT, pos),
privacy_item_audio_input(config["audio-in"], PRIVACY_NODE_TYPE_AUDIO_INPUT, pos),
privacy_item_audio_output(config["audio-out"], PRIVACY_NODE_TYPE_AUDIO_OUTPUT, pos),
privacy_item_screenshare(config["screenshare"], PRIVACY_NODE_TYPE_VIDEO_INPUT,
&nodes_screenshare, pos),
privacy_item_audio_input(config["audio-in"], PRIVACY_NODE_TYPE_AUDIO_INPUT, &nodes_audio_in,
pos),
privacy_item_audio_output(config["audio-out"], PRIVACY_NODE_TYPE_AUDIO_OUTPUT,
&nodes_audio_out, pos),
visibility_conn(),
box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
box_.set_name(name_);
Expand Down Expand Up @@ -74,25 +77,18 @@ void Privacy::onPrivacyNodesChanged() {
nodes_audio_in.clear();
nodes_screenshare.clear();

bool screenshare = false;
bool audio_in = false;
bool audio_out = false;
for (auto& node : backend->privacy_nodes) {
if (screenshare && audio_in && audio_out) break;
switch (node.second->state) {
switch (node.second.state) {
case PW_NODE_STATE_RUNNING:
switch (node.second->type) {
switch (node.second.type) {
case PRIVACY_NODE_TYPE_VIDEO_INPUT:
screenshare = true;
nodes_screenshare.push_back(node.second);
nodes_screenshare.push_back(&node.second);
break;
case PRIVACY_NODE_TYPE_AUDIO_INPUT:
audio_in = true;
nodes_audio_in.push_back(node.second);
nodes_audio_in.push_back(&node.second);
break;
case PRIVACY_NODE_TYPE_AUDIO_OUTPUT:
audio_out = true;
nodes_audio_out.push_back(node.second);
nodes_audio_out.push_back(&node.second);
break;
case PRIVACY_NODE_TYPE_NONE:
continue;
Expand All @@ -108,13 +104,15 @@ void Privacy::onPrivacyNodesChanged() {
}

auto Privacy::update() -> void {
mutex_.lock();
bool screenshare = !nodes_screenshare.empty();
bool audio_in = !nodes_audio_in.empty();
bool audio_out = !nodes_audio_out.empty();

privacy_item_screenshare.set_in_use(screenshare);
privacy_item_audio_input.set_in_use(audio_in);
privacy_item_audio_output.set_in_use(audio_out);
mutex_.unlock();

// Hide the whole widget if none are in use
bool is_visible = screenshare || audio_in || audio_out;
Expand All @@ -130,9 +128,11 @@ auto Privacy::update() -> void {
visibility_conn = Glib::signal_timeout().connect(
sigc::track_obj(
[this] {
mutex_.lock();
bool screenshare = !nodes_screenshare.empty();
bool audio_in = !nodes_audio_in.empty();
bool audio_out = !nodes_audio_out.empty();
mutex_.unlock();
event_box_.set_visible(screenshare || audio_in || audio_out);
return false;
},
Expand Down
63 changes: 53 additions & 10 deletions src/modules/privacy/privacy_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

namespace waybar::modules::privacy {

PrivacyItem::PrivacyItem(const Json::Value& config_,
enum util::PipewireBackend::PrivacyNodeType privacy_type_,
const std::string& pos)
PrivacyItem::PrivacyItem(const Json::Value &config_, enum PrivacyNodeType privacy_type_,
std::list<PrivacyNodeInfo *> *nodes_, const std::string &pos)
: Gtk::Revealer(),
privacy_type(privacy_type_),
mutex_(),
nodes(nodes_),
tooltip_window(Gtk::ORIENTATION_VERTICAL, 0),
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
icon_() {
switch (privacy_type) {
Expand Down Expand Up @@ -74,6 +74,26 @@ PrivacyItem::PrivacyItem(const Json::Value& config_,
}
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);

// Tooltip Icon Size
if (config_["tooltip-icon-size"].isUInt()) {
tooltipIconSize = config_["tooltip-icon-size"].asUInt();
}
// Tooltip
if (config_["tooltip"].isString()) {
tooltip = config_["tooltip"].asBool();
}
set_has_tooltip(tooltip);
if (tooltip) {
// Sets the window to use when showing the tooltip
update_tooltip();
this->signal_query_tooltip().connect(sigc::track_obj(
[this](int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip> &tooltip) {
tooltip->set_custom(tooltip_window);
return true;
},
*this));
}

property_child_revealed().signal_changed().connect(
sigc::mem_fun(*this, &PrivacyItem::on_child_revealed_changed));
signal_map().connect(sigc::mem_fun(*this, &PrivacyItem::on_map_changed));
Expand All @@ -83,6 +103,31 @@ PrivacyItem::PrivacyItem(const Json::Value& config_,
set_visible(false);
}

void PrivacyItem::update_tooltip() {
// Removes all old nodes
for (auto child : tooltip_window.get_children()) {
delete child;
}

for (auto *node : *nodes) {
Gtk::Box *box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4);

// Set device icon
Gtk::Image *node_icon = new Gtk::Image();
node_icon->set_pixel_size(tooltipIconSize);
node_icon->set_from_icon_name(node->get_icon_name(), Gtk::ICON_SIZE_INVALID);
box->add(*node_icon);

// Set model
Gtk::Label *node_name = new Gtk::Label(node->get_name());
box->add(*node_name);

tooltip_window.add(*box);
}

tooltip_window.show_all();
}

bool PrivacyItem::is_enabled() { return enabled; }

void PrivacyItem::on_child_revealed_changed() {
Expand All @@ -98,12 +143,12 @@ void PrivacyItem::on_map_changed() {
}

void PrivacyItem::set_in_use(bool in_use) {
mutex_.lock();
if (this->in_use == in_use && init) {
mutex_.unlock();
return;
if (in_use) {
update_tooltip();
}

if (this->in_use == in_use && init) return;

if (init) {
this->in_use = in_use;
if (this->in_use) {
Expand Down Expand Up @@ -136,8 +181,6 @@ void PrivacyItem::set_in_use(bool in_use) {
get_style_context()->add_class(status);
}
lastStatus = status;

mutex_.unlock();
}

void PrivacyItem::set_icon_size(uint size) { icon_.set_pixel_size(size); }
Expand Down
18 changes: 8 additions & 10 deletions src/util/pipewire_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,25 @@ static void get_node_info(void *data_, const struct pw_node_info *info) {
p_node_info->media_name = item->value;
} else if (strcmp(item->key, PW_KEY_NODE_NAME) == 0) {
p_node_info->node_name = item->value;
} else if (strcmp(item->key, PW_KEY_APP_NAME) == 0) {
p_node_info->application_name = item->value;
} else if (strcmp(item->key, "pipewire.access.portal.app_id") == 0) {
p_node_info->pipewire_access_portal_app_id = item->value;
} else if (strcmp(item->key, PW_KEY_APP_ICON_NAME) == 0) {
p_node_info->application_icon_name = item->value;
}
}

if (p_node_info->type != PRIVACY_NODE_TYPE_NONE) {
backend->mutex_.lock();
p_node_info->changed = true;
backend->privacy_nodes.insert_or_assign(info->id, p_node_info);
backend->privacy_nodes.insert_or_assign(info->id, *p_node_info);
backend->mutex_.unlock();

backend->privacy_nodes_changed_signal_event.emit();
} else {
if (p_node_info->changed) {
backend->mutex_.lock();
PrivacyNodeInfo *node = backend->privacy_nodes.at(info->id);
delete node;
backend->privacy_nodes.erase(info->id);
backend->mutex_.unlock();

Expand All @@ -64,7 +68,7 @@ static void registry_event_global(void *_data, uint32_t id, uint32_t permissions
PrivacyNodeInfo *p_node_info;
backend->mutex_.lock();
if (backend->privacy_nodes.contains(id)) {
p_node_info = backend->privacy_nodes.at(id);
p_node_info = &backend->privacy_nodes.at(id);
} else {
p_node_info = new PrivacyNodeInfo(id, backend);
}
Expand All @@ -78,8 +82,6 @@ static void registry_event_global_remove(void *_data, uint32_t id) {

backend->mutex_.lock();
if (backend->privacy_nodes.contains(id)) {
PrivacyNodeInfo *node_info = backend->privacy_nodes.at(id);
delete node_info;
backend->privacy_nodes.erase(id);
}
backend->mutex_.unlock();
Expand Down Expand Up @@ -118,10 +120,6 @@ PipewireBackend::PipewireBackend(private_constructor_tag tag)
}

PipewireBackend::~PipewireBackend() {
for (auto &node : privacy_nodes) {
delete node.second;
}

if (registry != nullptr) {
pw_proxy_destroy((struct pw_proxy *)registry);
}
Expand Down

0 comments on commit d32da91

Please sign in to comment.