mirror of https://github.com/polybar/polybar.git
Compare commits
7 Commits
f3f945afa7
...
efc11eba54
Author | SHA1 | Date |
---|---|---|
Vincent Bernat | efc11eba54 | |
Roddy Rappaport | 11b522c313 | |
Roddy Rappaport | 82a81ce07c | |
Roddy Rappaport | c2dd279bf7 | |
patrick96 | 174ce34285 | |
Vincent Bernat | 36b4c3a8af | |
Vincent Bernat | 7d3151eb42 |
|
@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- An option `unmute-on-scroll` for `internal/pulseaudio` and `internal/alsa` to unmute audio when the user scrolls on the widget.
|
||||||
|
|
||||||
## [3.7.1] - 2023-11-27
|
## [3.7.1] - 2023-11-27
|
||||||
### Build
|
### Build
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
sphinx~=7.2.6
|
sphinx~=7.2.6
|
||||||
sphinx-rtd-theme~=2.0.0rc2
|
sphinx-rtd-theme~=2.0.0rc2
|
||||||
sphinx-notfound-page~=1.0.0
|
sphinx-notfound-page~=1.0.0
|
||||||
readthedocs-sphinx-search~=0.3.1
|
readthedocs-sphinx-search~=0.3.2
|
||||||
|
|
|
@ -153,6 +153,9 @@ internal/alsa
|
||||||
``interval`` is the config setting in the module. Volume changed like this
|
``interval`` is the config setting in the module. Volume changed like this
|
||||||
will never go above 100%.
|
will never go above 100%.
|
||||||
|
|
||||||
|
if ``unmute-on-scroll`` is turned on, the sound will also be unmuted when
|
||||||
|
this action is called.
|
||||||
|
|
||||||
:``toggle``:
|
:``toggle``:
|
||||||
Toggles between muted and unmuted.
|
Toggles between muted and unmuted.
|
||||||
|
|
||||||
|
@ -165,6 +168,9 @@ internal/pulseaudio
|
||||||
will never go above ~153% (if ``use-ui-max`` is set to ``true``) or 100% (if
|
will never go above ~153% (if ``use-ui-max`` is set to ``true``) or 100% (if
|
||||||
not).
|
not).
|
||||||
|
|
||||||
|
if ``unmute-on-scroll`` is turned on, the sound will also be unmuted when
|
||||||
|
this action is called.
|
||||||
|
|
||||||
:``toggle``:
|
:``toggle``:
|
||||||
Toggles between muted and unmuted.
|
Toggles between muted and unmuted.
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@ class pulseaudio {
|
||||||
using queue = std::queue<evtype>;
|
using queue = std::queue<evtype>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit pulseaudio(const logger& logger, string&& sink_name, bool m_max_volume);
|
enum class devicetype { SINK = 0, SOURCE };
|
||||||
|
explicit pulseaudio(const logger& logger, devicetype device_type, string&& device_name, bool m_max_volume);
|
||||||
~pulseaudio();
|
~pulseaudio();
|
||||||
|
|
||||||
pulseaudio(const pulseaudio& o) = delete;
|
pulseaudio(const pulseaudio& o) = delete;
|
||||||
|
@ -40,23 +41,29 @@ class pulseaudio {
|
||||||
|
|
||||||
int get_volume();
|
int get_volume();
|
||||||
double get_decibels();
|
double get_decibels();
|
||||||
void set_volume(float percentage);
|
|
||||||
void inc_volume(int delta_perc);
|
void inc_volume(int delta_perc);
|
||||||
void set_mute(bool mode);
|
void set_mute(bool mode);
|
||||||
void toggle_mute();
|
void toggle_mute();
|
||||||
bool is_muted();
|
bool is_muted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update_volume(pa_operation* o);
|
template<typename T>
|
||||||
static void check_mute_callback(pa_context* context, const pa_sink_info* info, int eol, void* userdata);
|
static void get_volume_callback(pa_context* context, const T* info, int is_last, void* userdata);
|
||||||
static void get_sink_volume_callback(pa_context* context, const pa_sink_info* info, int is_last, void* userdata);
|
|
||||||
static void subscribe_callback(pa_context* context, pa_subscription_event_type_t t, uint32_t idx, void* userdata);
|
static void subscribe_callback(pa_context* context, pa_subscription_event_type_t t, uint32_t idx, void* userdata);
|
||||||
static void simple_callback(pa_context* context, int success, void* userdata);
|
static void simple_callback(pa_context* context, int success, void* userdata);
|
||||||
static void sink_info_callback(pa_context* context, const pa_sink_info* info, int eol, void* userdata);
|
template<typename T>
|
||||||
|
static void info_callback(pa_context* context, const T* info, int eol, void* userdata);
|
||||||
static void context_state_callback(pa_context* context, void* userdata);
|
static void context_state_callback(pa_context* context, void* userdata);
|
||||||
|
|
||||||
inline void wait_loop(pa_operation* op, pa_threaded_mainloop* loop);
|
inline void wait_loop(pa_operation* op, pa_threaded_mainloop* loop);
|
||||||
|
|
||||||
|
/* Abstraction for sink/source */
|
||||||
|
const string get_device_type();
|
||||||
|
void device_update_info(string device_name);
|
||||||
|
void device_update_info();
|
||||||
|
void device_set_volume();
|
||||||
|
void device_set_mute(bool mode);
|
||||||
|
|
||||||
const logger& m_log;
|
const logger& m_log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,13 +77,15 @@ class pulseaudio {
|
||||||
bool muted{false};
|
bool muted{false};
|
||||||
// default sink name
|
// default sink name
|
||||||
static constexpr auto DEFAULT_SINK = "@DEFAULT_SINK@";
|
static constexpr auto DEFAULT_SINK = "@DEFAULT_SINK@";
|
||||||
|
static constexpr auto DEFAULT_SOURCE = "@DEFAULT_SOURCE@";
|
||||||
|
|
||||||
pa_context* m_context{nullptr};
|
pa_context* m_context{nullptr};
|
||||||
pa_threaded_mainloop* m_mainloop{nullptr};
|
pa_threaded_mainloop* m_mainloop{nullptr};
|
||||||
|
|
||||||
queue m_events;
|
queue m_events;
|
||||||
|
|
||||||
// specified sink name
|
// specified sink/source
|
||||||
|
devicetype m_device_type;
|
||||||
string spec_s_name;
|
string spec_s_name;
|
||||||
string s_name;
|
string s_name;
|
||||||
uint32_t m_index{0};
|
uint32_t m_index{0};
|
||||||
|
|
|
@ -67,6 +67,7 @@ namespace modules {
|
||||||
map<control, control_t> m_ctrl;
|
map<control, control_t> m_ctrl;
|
||||||
int m_headphoneid{0};
|
int m_headphoneid{0};
|
||||||
bool m_mapped{false};
|
bool m_mapped{false};
|
||||||
|
bool m_unmute_on_scroll{false};
|
||||||
int m_interval{5};
|
int m_interval{5};
|
||||||
atomic<bool> m_muted{false};
|
atomic<bool> m_muted{false};
|
||||||
atomic<bool> m_headphones{false};
|
atomic<bool> m_headphones{false};
|
||||||
|
|
|
@ -51,6 +51,7 @@ namespace modules {
|
||||||
pulseaudio_t m_pulseaudio;
|
pulseaudio_t m_pulseaudio;
|
||||||
|
|
||||||
int m_interval{5};
|
int m_interval{5};
|
||||||
|
bool m_unmute_on_scroll{false};
|
||||||
atomic<bool> m_muted{false};
|
atomic<bool> m_muted{false};
|
||||||
atomic<int> m_volume{0};
|
atomic<int> m_volume{0};
|
||||||
atomic<double> m_decibels{0};
|
atomic<double> m_decibels{0};
|
||||||
|
|
|
@ -7,8 +7,9 @@ POLYBAR_NS
|
||||||
/**
|
/**
|
||||||
* Construct pulseaudio object
|
* Construct pulseaudio object
|
||||||
*/
|
*/
|
||||||
pulseaudio::pulseaudio(const logger& logger, string&& sink_name, bool max_volume)
|
pulseaudio::pulseaudio(const logger& logger, devicetype device_type, string&& device_name, bool max_volume)
|
||||||
: m_log(logger), spec_s_name(sink_name) {
|
: m_log(logger), spec_s_name(device_name) {
|
||||||
|
m_device_type = device_type;
|
||||||
m_mainloop = pa_threaded_mainloop_new();
|
m_mainloop = pa_threaded_mainloop_new();
|
||||||
if (!m_mainloop) {
|
if (!m_mainloop) {
|
||||||
throw pulseaudio_error("Could not create pulseaudio threaded mainloop.");
|
throw pulseaudio_error("Could not create pulseaudio threaded mainloop.");
|
||||||
|
@ -65,30 +66,38 @@ pulseaudio::pulseaudio(const logger& logger, string&& sink_name, bool max_volume
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_operation* op{nullptr};
|
pa_operation* op{nullptr};
|
||||||
if (!sink_name.empty()) {
|
if (!device_name.empty()) {
|
||||||
op = pa_context_get_sink_info_by_name(m_context, sink_name.c_str(), sink_info_callback, this);
|
device_update_info(device_name);
|
||||||
wait_loop(op, m_mainloop);
|
|
||||||
}
|
}
|
||||||
if (s_name.empty()) {
|
if (s_name.empty()) {
|
||||||
// get the sink index
|
// get the index
|
||||||
op = pa_context_get_sink_info_by_name(m_context, DEFAULT_SINK, sink_info_callback, this);
|
device_update_info(""s);
|
||||||
wait_loop(op, m_mainloop);
|
m_log.notice("pulseaudio: using default %s %s", get_device_type(), s_name);
|
||||||
m_log.notice("pulseaudio: using default sink %s", s_name);
|
|
||||||
} else {
|
} else {
|
||||||
m_log.trace("pulseaudio: using sink %s", s_name);
|
m_log.trace("pulseaudio: using %s %s", get_device_type(), s_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_max_volume = max_volume ? PA_VOLUME_UI_MAX : PA_VOLUME_NORM;
|
m_max_volume = max_volume ? PA_VOLUME_UI_MAX : PA_VOLUME_NORM;
|
||||||
|
|
||||||
auto event_types = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SERVER);
|
pa_subscription_mask_t event_types;
|
||||||
|
switch (m_device_type) {
|
||||||
|
case devicetype::SINK:
|
||||||
|
event_types = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SERVER);
|
||||||
|
break;
|
||||||
|
case devicetype::SOURCE:
|
||||||
|
event_types = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw pulseaudio_error("unreachable");
|
||||||
|
}
|
||||||
op = pa_context_subscribe(m_context, event_types, simple_callback, this);
|
op = pa_context_subscribe(m_context, event_types, simple_callback, this);
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw pulseaudio_error("Failed to subscribe to sink.");
|
throw pulseaudio_error("Failed to subscribe to device.");
|
||||||
}
|
}
|
||||||
pa_context_set_subscribe_callback(m_context, subscribe_callback, this);
|
pa_context_set_subscribe_callback(m_context, subscribe_callback, this);
|
||||||
|
|
||||||
update_volume(op);
|
device_update_info();
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
}
|
}
|
||||||
|
@ -110,6 +119,87 @@ const string& pulseaudio::get_name() {
|
||||||
return s_name;
|
return s_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get device type as string
|
||||||
|
*/
|
||||||
|
const string pulseaudio::get_device_type() {
|
||||||
|
switch (m_device_type) {
|
||||||
|
case devicetype::SINK:
|
||||||
|
return "sink"s;
|
||||||
|
case devicetype::SOURCE:
|
||||||
|
return "source"s;
|
||||||
|
}
|
||||||
|
throw pulseaudio_error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around pa_context_get_XXX_info_by_name().
|
||||||
|
*/
|
||||||
|
void pulseaudio::device_update_info(string device_name) {
|
||||||
|
pa_operation *op{nullptr};
|
||||||
|
switch (m_device_type) {
|
||||||
|
case devicetype::SINK:
|
||||||
|
op = pa_context_get_sink_info_by_name(m_context,
|
||||||
|
device_name.empty()?DEFAULT_SINK:device_name.c_str(),
|
||||||
|
info_callback<pa_sink_info>, this);
|
||||||
|
break;
|
||||||
|
case devicetype::SOURCE:
|
||||||
|
op = pa_context_get_source_info_by_name(m_context,
|
||||||
|
device_name.empty()?DEFAULT_SOURCE:device_name.c_str(),
|
||||||
|
info_callback<pa_source_info>, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wait_loop(op, m_mainloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around pa_context_get_XXX_info_by_index().
|
||||||
|
*/
|
||||||
|
void pulseaudio::device_update_info() {
|
||||||
|
pa_operation *op{nullptr};
|
||||||
|
switch (m_device_type) {
|
||||||
|
case devicetype::SINK:
|
||||||
|
op = pa_context_get_sink_info_by_index(m_context, m_index, get_volume_callback<pa_sink_info>, this);
|
||||||
|
break;
|
||||||
|
case devicetype::SOURCE:
|
||||||
|
op = pa_context_get_source_info_by_index(m_context, m_index, get_volume_callback<pa_source_info>, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wait_loop(op, m_mainloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around pa_context_set_XXX_volume_by_index().
|
||||||
|
*/
|
||||||
|
void pulseaudio::device_set_volume() {
|
||||||
|
pa_operation *op{nullptr};
|
||||||
|
switch (m_device_type) {
|
||||||
|
case devicetype::SINK:
|
||||||
|
op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
||||||
|
break;
|
||||||
|
case devicetype::SOURCE:
|
||||||
|
op = pa_context_set_source_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wait_loop(op, m_mainloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around pa_context_set_XXX_mute_by_index().
|
||||||
|
*/
|
||||||
|
void pulseaudio::device_set_mute(bool mode) {
|
||||||
|
pa_operation *op{nullptr};
|
||||||
|
switch (m_device_type) {
|
||||||
|
case devicetype::SINK:
|
||||||
|
op = pa_context_set_sink_mute_by_index(m_context, m_index, mode, simple_callback, this);
|
||||||
|
break;
|
||||||
|
case devicetype::SOURCE:
|
||||||
|
op = pa_context_set_source_mute_by_index(m_context, m_index, mode, simple_callback, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wait_loop(op, m_mainloop);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for events
|
* Wait for events
|
||||||
*/
|
*/
|
||||||
|
@ -123,37 +213,34 @@ bool pulseaudio::wait() {
|
||||||
int pulseaudio::process_events() {
|
int pulseaudio::process_events() {
|
||||||
int ret = m_events.size();
|
int ret = m_events.size();
|
||||||
pa_threaded_mainloop_lock(m_mainloop);
|
pa_threaded_mainloop_lock(m_mainloop);
|
||||||
pa_operation* o{nullptr};
|
|
||||||
// clear the queue
|
// clear the queue
|
||||||
while (!m_events.empty()) {
|
while (!m_events.empty()) {
|
||||||
switch (m_events.front()) {
|
switch (m_events.front()) {
|
||||||
// try to get specified sink
|
// try to get specified sink/source
|
||||||
case evtype::NEW:
|
case evtype::NEW:
|
||||||
// redundant if already using specified sink
|
// redundant if already using specified sink/source
|
||||||
if (!spec_s_name.empty()) {
|
if (!spec_s_name.empty()) {
|
||||||
o = pa_context_get_sink_info_by_name(m_context, spec_s_name.c_str(), sink_info_callback, this);
|
device_update_info(spec_s_name);
|
||||||
wait_loop(o, m_mainloop);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// FALLTHRU
|
// FALLTHRU
|
||||||
case evtype::SERVER:
|
case evtype::SERVER:
|
||||||
// don't fallthrough only if always using default sink
|
// don't fallthrough only if always using default sink/source
|
||||||
if (!spec_s_name.empty()) {
|
if (!spec_s_name.empty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// FALLTHRU
|
// FALLTHRU
|
||||||
// get default sink
|
// get default sink/source
|
||||||
case evtype::REMOVE:
|
case evtype::REMOVE:
|
||||||
o = pa_context_get_sink_info_by_name(m_context, DEFAULT_SINK, sink_info_callback, this);
|
device_update_info(""s);
|
||||||
wait_loop(o, m_mainloop);
|
|
||||||
if (spec_s_name != s_name) {
|
if (spec_s_name != s_name) {
|
||||||
m_log.notice("pulseaudio: using default sink %s", s_name);
|
m_log.notice("pulseaudio: using default %s %s", get_device_type(), s_name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
update_volume(o);
|
device_update_info();
|
||||||
m_events.pop();
|
m_events.pop();
|
||||||
}
|
}
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
|
@ -175,20 +262,6 @@ double pulseaudio::get_decibels() {
|
||||||
return pa_sw_volume_to_dB(pa_cvolume_max(&cv));
|
return pa_sw_volume_to_dB(pa_cvolume_max(&cv));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set volume to given percentage
|
|
||||||
*/
|
|
||||||
void pulseaudio::set_volume(float percentage) {
|
|
||||||
pa_threaded_mainloop_lock(m_mainloop);
|
|
||||||
pa_volume_t vol = math_util::percentage_to_value<pa_volume_t>(percentage, PA_VOLUME_MUTED, PA_VOLUME_NORM);
|
|
||||||
pa_cvolume_scale(&cv, vol);
|
|
||||||
pa_operation* op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
|
||||||
wait_loop(op, m_mainloop);
|
|
||||||
if (!success)
|
|
||||||
throw pulseaudio_error("Failed to set sink volume.");
|
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment or decrement volume by given percentage (prevents accumulation of rounding errors from get_volume)
|
* Increment or decrement volume by given percentage (prevents accumulation of rounding errors from get_volume)
|
||||||
*/
|
*/
|
||||||
|
@ -207,10 +280,9 @@ void pulseaudio::inc_volume(int delta_perc) {
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
pa_cvolume_dec(&cv, vol);
|
pa_cvolume_dec(&cv, vol);
|
||||||
pa_operation* op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
device_set_volume();
|
||||||
wait_loop(op, m_mainloop);
|
|
||||||
if (!success)
|
if (!success)
|
||||||
throw pulseaudio_error("Failed to set sink volume.");
|
throw pulseaudio_error("Failed to set device volume.");
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,10 +291,9 @@ void pulseaudio::inc_volume(int delta_perc) {
|
||||||
*/
|
*/
|
||||||
void pulseaudio::set_mute(bool mode) {
|
void pulseaudio::set_mute(bool mode) {
|
||||||
pa_threaded_mainloop_lock(m_mainloop);
|
pa_threaded_mainloop_lock(m_mainloop);
|
||||||
pa_operation* op = pa_context_set_sink_mute_by_index(m_context, m_index, mode, simple_callback, this);
|
device_set_mute(mode);
|
||||||
wait_loop(op, m_mainloop);
|
|
||||||
if (!success)
|
if (!success)
|
||||||
throw pulseaudio_error("Failed to mute sink.");
|
throw pulseaudio_error("Failed to mute device.");
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,18 +311,11 @@ bool pulseaudio::is_muted() {
|
||||||
return muted;
|
return muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update local volume cache
|
|
||||||
*/
|
|
||||||
void pulseaudio::update_volume(pa_operation* o) {
|
|
||||||
o = pa_context_get_sink_info_by_index(m_context, m_index, get_sink_volume_callback, this);
|
|
||||||
wait_loop(o, m_mainloop);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when getting volume
|
* Callback when getting volume
|
||||||
*/
|
*/
|
||||||
void pulseaudio::get_sink_volume_callback(pa_context*, const pa_sink_info* info, int, void* userdata) {
|
template <typename T>
|
||||||
|
void pulseaudio::get_volume_callback(pa_context*, const T* info, int, void* userdata) {
|
||||||
pulseaudio* This = static_cast<pulseaudio*>(userdata);
|
pulseaudio* This = static_cast<pulseaudio*>(userdata);
|
||||||
if (info) {
|
if (info) {
|
||||||
This->cv = info->volume;
|
This->cv = info->volume;
|
||||||
|
@ -274,6 +338,7 @@ void pulseaudio::subscribe_callback(pa_context*, pa_subscription_event_type_t t,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PA_SUBSCRIPTION_EVENT_SINK:
|
case PA_SUBSCRIPTION_EVENT_SINK:
|
||||||
|
case PA_SUBSCRIPTION_EVENT_SOURCE:
|
||||||
switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
|
switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
|
||||||
case PA_SUBSCRIPTION_EVENT_NEW:
|
case PA_SUBSCRIPTION_EVENT_NEW:
|
||||||
This->m_events.emplace(evtype::NEW);
|
This->m_events.emplace(evtype::NEW);
|
||||||
|
@ -302,10 +367,11 @@ void pulseaudio::simple_callback(pa_context*, int success, void* userdata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when getting sink info & existence
|
* Callback when getting sink/source info & existence
|
||||||
*/
|
*/
|
||||||
void pulseaudio::sink_info_callback(pa_context*, const pa_sink_info* info, int eol, void* userdata) {
|
template <typename T>
|
||||||
pulseaudio* This = static_cast<pulseaudio*>(userdata);
|
void pulseaudio::info_callback(pa_context*, const T* info, int eol, void* userdata) {
|
||||||
|
pulseaudio *This = static_cast<pulseaudio*>(userdata);
|
||||||
if (!eol && info) {
|
if (!eol && info) {
|
||||||
This->m_index = info->index;
|
This->m_index = info->index;
|
||||||
This->s_name = info->name;
|
This->s_name = info->name;
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace modules {
|
||||||
// Load configuration values
|
// Load configuration values
|
||||||
m_mapped = m_conf.get(name(), "mapped", m_mapped);
|
m_mapped = m_conf.get(name(), "mapped", m_mapped);
|
||||||
m_interval = m_conf.get(name(), "interval", m_interval);
|
m_interval = m_conf.get(name(), "interval", m_interval);
|
||||||
|
m_unmute_on_scroll = m_conf.get(name(), "unmute-on-scroll", m_unmute_on_scroll);
|
||||||
|
|
||||||
auto master_mixer_name = m_conf.get(name(), "master-mixer", "Master"s);
|
auto master_mixer_name = m_conf.get(name(), "master-mixer", "Master"s);
|
||||||
auto speaker_mixer_name = m_conf.get(name(), "speaker-mixer", ""s);
|
auto speaker_mixer_name = m_conf.get(name(), "speaker-mixer", ""s);
|
||||||
|
@ -261,6 +262,9 @@ namespace modules {
|
||||||
}
|
}
|
||||||
const auto& mixers = get_mixers();
|
const auto& mixers = get_mixers();
|
||||||
for (auto&& mixer : mixers) {
|
for (auto&& mixer : mixers) {
|
||||||
|
if (m_unmute_on_scroll) {
|
||||||
|
mixer->set_mute(true);
|
||||||
|
}
|
||||||
m_mapped ? mixer->set_normalized_volume(math_util::cap<float>(mixer->get_normalized_volume() + interval, 0, 100))
|
m_mapped ? mixer->set_normalized_volume(math_util::cap<float>(mixer->get_normalized_volume() + interval, 0, 100))
|
||||||
: mixer->set_volume(math_util::cap<float>(mixer->get_volume() + interval, 0, 100));
|
: mixer->set_volume(math_util::cap<float>(mixer->get_volume() + interval, 0, 100));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,13 +23,28 @@ namespace modules {
|
||||||
|
|
||||||
// Load configuration values
|
// Load configuration values
|
||||||
m_interval = m_conf.get(name(), "interval", m_interval);
|
m_interval = m_conf.get(name(), "interval", m_interval);
|
||||||
|
m_unmute_on_scroll = m_conf.get(name(), "unmute-on-scroll", m_unmute_on_scroll);
|
||||||
|
|
||||||
auto sink_name = m_conf.get(name(), "sink", ""s);
|
auto sink_name = m_conf.get(name(), "sink", ""s);
|
||||||
|
auto source_name = m_conf.get(name(), "source", ""s);
|
||||||
|
string device_name;
|
||||||
bool m_max_volume = m_conf.get(name(), "use-ui-max", true);
|
bool m_max_volume = m_conf.get(name(), "use-ui-max", true);
|
||||||
m_reverse_scroll = m_conf.get(name(), "reverse-scroll", false);
|
m_reverse_scroll = m_conf.get(name(), "reverse-scroll", false);
|
||||||
|
|
||||||
|
if (!sink_name.empty() && !source_name.empty()) {
|
||||||
|
throw module_error("Use either source or sink, not both");
|
||||||
|
}
|
||||||
|
pulseaudio::devicetype device_type;
|
||||||
|
if (!source_name.empty()) {
|
||||||
|
device_type = pulseaudio::devicetype::SOURCE;
|
||||||
|
device_name = move(source_name);
|
||||||
|
} else {
|
||||||
|
device_type = pulseaudio::devicetype::SINK;
|
||||||
|
device_name = move(sink_name);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m_pulseaudio = std::make_unique<pulseaudio>(m_log, move(sink_name), m_max_volume);
|
m_pulseaudio = std::make_unique<pulseaudio>(m_log, device_type, move(device_name), m_max_volume);
|
||||||
} catch (const pulseaudio_error& err) {
|
} catch (const pulseaudio_error& err) {
|
||||||
throw module_error(err.what());
|
throw module_error(err.what());
|
||||||
}
|
}
|
||||||
|
@ -84,7 +99,7 @@ namespace modules {
|
||||||
m_muted = m_muted || m_pulseaudio->is_muted();
|
m_muted = m_muted || m_pulseaudio->is_muted();
|
||||||
}
|
}
|
||||||
} catch (const pulseaudio_error& err) {
|
} catch (const pulseaudio_error& err) {
|
||||||
m_log.err("%s: Failed to query pulseaudio sink (%s)", name(), err.what());
|
m_log.err("%s: Failed to query pulseaudio (%s)", name(), err.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace label tokens
|
// Replace label tokens
|
||||||
|
@ -108,6 +123,17 @@ namespace modules {
|
||||||
}
|
}
|
||||||
|
|
||||||
string pulseaudio_module::get_output() {
|
string pulseaudio_module::get_output() {
|
||||||
|
// Exclude monitors and auto_null
|
||||||
|
if (!m_pulseaudio) {
|
||||||
|
return ""s;
|
||||||
|
}
|
||||||
|
auto s_name = m_pulseaudio->get_name();
|
||||||
|
if (s_name.empty() ||
|
||||||
|
s_name == "auto_null" ||
|
||||||
|
(s_name.size() >= 8 && s_name.compare(s_name.size() - 8, s_name.size(), ".monitor") == 0)) {
|
||||||
|
return ""s;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the module output early so that
|
// Get the module output early so that
|
||||||
// the format prefix/suffix also gets wrapper
|
// the format prefix/suffix also gets wrapper
|
||||||
// with the cmd handlers
|
// with the cmd handlers
|
||||||
|
@ -156,10 +182,16 @@ namespace modules {
|
||||||
}
|
}
|
||||||
|
|
||||||
void pulseaudio_module::action_inc() {
|
void pulseaudio_module::action_inc() {
|
||||||
|
if (m_unmute_on_scroll) {
|
||||||
|
m_pulseaudio->set_mute(false);
|
||||||
|
}
|
||||||
m_pulseaudio->inc_volume(m_interval);
|
m_pulseaudio->inc_volume(m_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pulseaudio_module::action_dec() {
|
void pulseaudio_module::action_dec() {
|
||||||
|
if (m_unmute_on_scroll) {
|
||||||
|
m_pulseaudio->set_mute(false);
|
||||||
|
}
|
||||||
m_pulseaudio->inc_volume(-m_interval);
|
m_pulseaudio->inc_volume(-m_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue