mirror of
				https://github.com/rad4day/Waybar.git
				synced 2025-10-31 16:02:43 +01:00 
			
		
		
		
	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:
		| @@ -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" | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								include/modules/sndio.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								include/modules/sndio.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										83
									
								
								man/waybar-sndio.5.scd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								man/waybar-sndio.5.scd
									
									
									
									
									
										Normal file
									
								
							| @@ -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]); | ||||||
|   | |||||||
							
								
								
									
										167
									
								
								src/modules/sndio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/modules/sndio.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 */ | ||||||
		Reference in New Issue
	
	Block a user
	 Érico Rolim
					Érico Rolim