add volume widget

This commit is contained in:
2025-12-10 03:19:48 +01:00
parent a01bb7554b
commit 12546a36f8
8 changed files with 250 additions and 13 deletions

View File

@@ -0,0 +1,112 @@
#include "widgets/volumeWidget.hpp"
#include "helpers/systemHelper.hpp"
#include <sigc++/functors/mem_fun.h>
#include <regex>
#include <iostream>
#include <cmath>
VolumeWidget::VolumeWidget()
: Gtk::Box(Gtk::Orientation::HORIZONTAL)
{
set_valign(Gtk::Align::CENTER);
set_halign(Gtk::Align::CENTER);
m_label.set_halign(Gtk::Align::CENTER);
m_label.set_valign(Gtk::Align::CENTER);
m_label.set_text("Vol");
append(m_label);
// Click toggles mute using wpctl
m_click = Gtk::GestureClick::create();
m_click->set_button(GDK_BUTTON_PRIMARY);
// signal_released provides (int, double, double) — use lambda to ignore args
m_click->signal_released().connect([this](int /*n_press*/, double /*x*/, double /*y*/)
{
try
{
// Toggle mute then refresh
(void)SystemHelper::get_command_output("wpctl set-mute @DEFAULT_SINK@ toggle");
}
catch (const std::exception &ex)
{
std::cerr << "[VolumeWidget] failed to toggle mute: " << ex.what() << std::endl;
}
this->update();
});
add_controller(m_click);
// Initial read
update();
// Start polling every 1 second to keep the display up to date
m_timeoutConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &VolumeWidget::on_timeout), 100);
}
VolumeWidget::~VolumeWidget()
{
if (m_timeoutConn.connected())
m_timeoutConn.disconnect();
}
void VolumeWidget::update()
{
try
{
const std::string out = SystemHelper::get_command_output("wpctl get-volume @DEFAULT_SINK@");
// Attempt to parse a number (percentage or fraction)
std::smatch m;
std::regex r_percent(R"((\d+(?:\.\d+)?)%)");
std::regex r_number(R"((\d+(?:\.\d+)?))");
std::string text = out;
int percent = -1;
if (std::regex_search(text, m, r_percent))
{
percent = static_cast<int>(std::round(std::stod(m[1].str())));
}
else if (std::regex_search(text, m, r_number))
{
// If number looks like 0.8 treat as fraction
const double v = std::stod(m[1].str());
if (v <= 1.0)
percent = static_cast<int>(std::round(v * 100.0));
else
percent = static_cast<int>(std::round(v));
}
if (percent >= 0)
{
m_label.set_text(std::to_string(percent) + "%");
}
else
{
// Fallback to raw output (trimmed)
auto pos = text.find_first_not_of(" \t\n\r");
if (pos != std::string::npos)
{
auto end = text.find_last_not_of(" \t\n\r");
m_label.set_text(text.substr(pos, end - pos + 1));
}
else
{
m_label.set_text("?");
}
}
}
catch (const std::exception &ex)
{
std::cerr << "[VolumeWidget] failed to read volume: " << ex.what() << std::endl;
m_label.set_text("N/A");
}
}
bool VolumeWidget::on_timeout()
{
update();
return true; // keep timeout active
}