sndio: Add module.
- can control sndio: change volume, toggle mute - appearance is somewhat dynamic: takes muted status into account - uses polling inside sleeper thread to update values - uses sioctl_* functions, requires sndio>=1.7.0.
This commit is contained in:
parent
93afe5113a
commit
e4427cb017
|
@ -67,6 +67,7 @@ libnl [Network module]
|
||||||
libappindicator-gtk3 [Tray module]
|
libappindicator-gtk3 [Tray module]
|
||||||
libdbusmenu-gtk3 [Tray module]
|
libdbusmenu-gtk3 [Tray module]
|
||||||
libmpdclient [MPD module]
|
libmpdclient [MPD module]
|
||||||
|
libsndio [sndio module]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Build dependencies**
|
**Build dependencies**
|
||||||
|
|
|
@ -39,6 +39,9 @@
|
||||||
#ifdef HAVE_LIBMPDCLIENT
|
#ifdef HAVE_LIBMPDCLIENT
|
||||||
#include "modules/mpd.hpp"
|
#include "modules/mpd.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBSNDIO
|
||||||
|
#include "modules/sndio.hpp"
|
||||||
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sndio.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "ALabel.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
class Sndio : public ALabel {
|
||||||
|
public:
|
||||||
|
Sndio(const std::string&, const Json::Value&);
|
||||||
|
~Sndio();
|
||||||
|
auto update() -> void;
|
||||||
|
auto set_desc(struct sioctl_desc *, unsigned int) -> void;
|
||||||
|
auto put_val(unsigned int, unsigned int) -> void;
|
||||||
|
bool handleScroll(GdkEventScroll *);
|
||||||
|
bool handleToggle(GdkEventButton* const&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
util::SleeperThread thread_;
|
||||||
|
struct sioctl_hdl *hdl_;
|
||||||
|
std::vector<struct pollfd> pfds_;
|
||||||
|
unsigned int addr_;
|
||||||
|
unsigned int volume_, old_volume_, maxval_;
|
||||||
|
bool muted_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules
|
|
@ -0,0 +1,83 @@
|
||||||
|
waybar-sndio(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - sndio module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *sndio* module displays the current volume reported by sndio(7).
|
||||||
|
|
||||||
|
Additionally, you can control the volume by scrolling *up* or *down* while the
|
||||||
|
cursor is over the module, and clicking on the module toggles mute.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {volume}% ++
|
||||||
|
The format for how information should be displayed.
|
||||||
|
|
||||||
|
*rotate*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Positive value to rotate the text label.
|
||||||
|
|
||||||
|
*max-length*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
|
*scroll-step*: ++
|
||||||
|
typeof: int ++
|
||||||
|
default: 5 ++
|
||||||
|
The speed in which to change the volume when scrolling.
|
||||||
|
|
||||||
|
*on-click*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when clicked on the module.
|
||||||
|
This replaces the default behaviour of toggling mute.
|
||||||
|
|
||||||
|
*on-click-middle*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when middle-clicked on the module using mousewheel.
|
||||||
|
|
||||||
|
*on-click-right*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when you right clicked on the module.
|
||||||
|
|
||||||
|
*on-update*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when the module is updated.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module.
|
||||||
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module.
|
||||||
|
This replaces the default behaviour of volume control.
|
||||||
|
|
||||||
|
*smooth-scrolling-threshold*: ++
|
||||||
|
typeof: double ++
|
||||||
|
Threshold to be used when scrolling.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{volume}*: Volume in percentage.
|
||||||
|
|
||||||
|
*{raw_value}*: Volume as value reported by sndio.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"sndio": {
|
||||||
|
"format": "{raw_value} 🎜",
|
||||||
|
"scroll-step": 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#sndio*
|
||||||
|
- *#sndio.muted*
|
20
meson.build
20
meson.build
|
@ -96,6 +96,19 @@ libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
||||||
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
||||||
libudev = dependency('libudev', required: get_option('libudev'))
|
libudev = dependency('libudev', required: get_option('libudev'))
|
||||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||||
|
|
||||||
|
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||||
|
if libsndio.found()
|
||||||
|
if not compiler.has_function('sioctl_open', prefix: '#include <sndio.h>', dependencies: libsndio)
|
||||||
|
if get_option('sndio').enabled()
|
||||||
|
error('libsndio is too old, required >=1.7.0')
|
||||||
|
else
|
||||||
|
warning('libsndio is too old, required >=1.7.0')
|
||||||
|
libsndio = dependency('', required: false)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
gtk_layer_shell = dependency('gtk-layer-shell-0',
|
||||||
required: get_option('gtk-layer-shell'),
|
required: get_option('gtk-layer-shell'),
|
||||||
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
|
||||||
|
@ -207,6 +220,11 @@ if gtk_layer_shell.found()
|
||||||
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if libsndio.found()
|
||||||
|
add_project_arguments('-DHAVE_LIBSNDIO', language: 'cpp')
|
||||||
|
src_files += 'src/modules/sndio.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
if get_option('rfkill').enabled()
|
if get_option('rfkill').enabled()
|
||||||
if is_linux
|
if is_linux
|
||||||
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
||||||
|
@ -241,6 +259,7 @@ executable(
|
||||||
libepoll,
|
libepoll,
|
||||||
libmpdclient,
|
libmpdclient,
|
||||||
gtk_layer_shell,
|
gtk_layer_shell,
|
||||||
|
libsndio,
|
||||||
tz_dep
|
tz_dep
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
|
@ -292,6 +311,7 @@ if scdoc.found()
|
||||||
'waybar-states.5.scd',
|
'waybar-states.5.scd',
|
||||||
'waybar-wlr-taskbar.5.scd',
|
'waybar-wlr-taskbar.5.scd',
|
||||||
'waybar-bluetooth.5.scd',
|
'waybar-bluetooth.5.scd',
|
||||||
|
'waybar-sndio.5.scd',
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach file : man_files
|
foreach file : man_files
|
||||||
|
|
|
@ -8,3 +8,4 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and i
|
||||||
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
|
||||||
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')
|
||||||
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL')
|
||||||
|
option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio')
|
||||||
|
|
|
@ -76,6 +76,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
if (ref == "mpd") {
|
if (ref == "mpd") {
|
||||||
return new waybar::modules::MPD(id, config_[name]);
|
return new waybar::modules::MPD(id, config_[name]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIBSNDIO
|
||||||
|
if (ref == "sndio") {
|
||||||
|
return new waybar::modules::Sndio(id, config_[name]);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ref == "temperature") {
|
if (ref == "temperature") {
|
||||||
return new waybar::modules::Temperature(id, config_[name]);
|
return new waybar::modules::Temperature(id, config_[name]);
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
#include "modules/sndio.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace waybar::modules {
|
||||||
|
|
||||||
|
void ondesc(void *arg, struct sioctl_desc *d, int curval) {
|
||||||
|
auto self = static_cast<Sndio*>(arg);
|
||||||
|
if (d == NULL) {
|
||||||
|
// d is NULL when the list is done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->set_desc(d, curval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onval(void *arg, unsigned int addr, unsigned int val) {
|
||||||
|
auto self = static_cast<Sndio*>(arg);
|
||||||
|
self->put_val(addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sndio::Sndio(const std::string &id, const Json::Value &config)
|
||||||
|
: ALabel(config, "sndio", id, "{volume}%"),
|
||||||
|
hdl_(nullptr),
|
||||||
|
pfds_(0),
|
||||||
|
addr_(0),
|
||||||
|
volume_(0),
|
||||||
|
old_volume_(0),
|
||||||
|
maxval_(0),
|
||||||
|
muted_(false) {
|
||||||
|
hdl_ = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0);
|
||||||
|
if (hdl_ == nullptr) {
|
||||||
|
throw std::runtime_error("sioctl_open() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sioctl_ondesc(hdl_, ondesc, this) == 0) {
|
||||||
|
throw std::runtime_error("sioctl_ondesc() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
sioctl_onval(hdl_, onval, this);
|
||||||
|
|
||||||
|
pfds_.reserve(sioctl_nfds(hdl_));
|
||||||
|
|
||||||
|
event_box_.show();
|
||||||
|
|
||||||
|
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK | Gdk::BUTTON_PRESS_MASK);
|
||||||
|
event_box_.signal_scroll_event().connect(
|
||||||
|
sigc::mem_fun(*this, &Sndio::handleScroll));
|
||||||
|
event_box_.signal_button_press_event().connect(
|
||||||
|
sigc::mem_fun(*this, &Sndio::handleToggle));
|
||||||
|
|
||||||
|
thread_ = [this] {
|
||||||
|
dp.emit();
|
||||||
|
|
||||||
|
int nfds = sioctl_pollfd(hdl_, pfds_.data(), POLLIN);
|
||||||
|
if (nfds == 0) {
|
||||||
|
throw std::runtime_error("sioctl_pollfd() failed.");
|
||||||
|
}
|
||||||
|
while (poll(pfds_.data(), nfds, -1) < 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
throw std::runtime_error("poll() failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int revents = sioctl_revents(hdl_, pfds_.data());
|
||||||
|
if (revents & POLLHUP) {
|
||||||
|
throw std::runtime_error("disconnected!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Sndio::~Sndio() {
|
||||||
|
sioctl_close(hdl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::update() -> void {
|
||||||
|
auto format = format_;
|
||||||
|
unsigned int vol = 100. * static_cast<double>(volume_) / static_cast<double>(maxval_);
|
||||||
|
|
||||||
|
if (volume_ == 0) {
|
||||||
|
label_.get_style_context()->add_class("muted");
|
||||||
|
} else {
|
||||||
|
label_.get_style_context()->remove_class("muted");
|
||||||
|
}
|
||||||
|
|
||||||
|
label_.set_markup(fmt::format(format,
|
||||||
|
fmt::arg("volume", vol),
|
||||||
|
fmt::arg("raw_value", volume_)));
|
||||||
|
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::set_desc(struct sioctl_desc *d, unsigned int val) -> void {
|
||||||
|
std::string name{d->func};
|
||||||
|
std::string node_name{d->node0.name};
|
||||||
|
|
||||||
|
if (name == "level" && node_name == "output" && d->type == SIOCTL_NUM) {
|
||||||
|
// store addr for output.level value, used in put_val
|
||||||
|
addr_ = d->addr;
|
||||||
|
maxval_ = d->maxval;
|
||||||
|
volume_ = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Sndio::put_val(unsigned int addr, unsigned int val) -> void {
|
||||||
|
if (addr == addr_) {
|
||||||
|
volume_ = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sndio::handleScroll(GdkEventScroll *e) {
|
||||||
|
// change the volume only when no user provided
|
||||||
|
// events are configured
|
||||||
|
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
|
||||||
|
return AModule::handleScroll(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = AModule::getScrollDir(e);
|
||||||
|
if (dir == SCROLL_DIR::NONE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int step = 5;
|
||||||
|
if (config_["scroll-step"].isInt()) {
|
||||||
|
step = config_["scroll-step"].asInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_volume = volume_;
|
||||||
|
if (muted_) {
|
||||||
|
new_volume = old_volume_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == SCROLL_DIR::UP) {
|
||||||
|
new_volume += step;
|
||||||
|
} else if (dir == SCROLL_DIR::DOWN) {
|
||||||
|
new_volume -= step;
|
||||||
|
}
|
||||||
|
new_volume = std::clamp(new_volume, 0, static_cast<int>(maxval_));
|
||||||
|
|
||||||
|
// quits muted mode if volume changes
|
||||||
|
muted_ = false;
|
||||||
|
|
||||||
|
sioctl_setval(hdl_, addr_, new_volume);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sndio::handleToggle(GdkEventButton* const& e) {
|
||||||
|
// toggle mute only when no user provided events are configured
|
||||||
|
if (config_["on-click"].isString()) {
|
||||||
|
return AModule::handleToggle(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
muted_ = !muted_;
|
||||||
|
if (muted_) {
|
||||||
|
// store old volume to be able to restore it later
|
||||||
|
old_volume_ = volume_;
|
||||||
|
sioctl_setval(hdl_, addr_, 0);
|
||||||
|
} else {
|
||||||
|
sioctl_setval(hdl_, addr_, old_volume_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace waybar::modules */
|
Loading…
Reference in New Issue