Merge remote-tracking branch 'origin/master' into 7FM/master
# Conflicts: # include/modules/pulseaudio_slider.hpp # include/util/audio_backend.hpp # man/waybar-pulseaudio-slider.5.scd # src/modules/pulseaudio_slider.cpp # src/util/audio_backend.cpp
This commit is contained in:
+40
-14
@@ -100,15 +100,17 @@ void AudioBackend::contextStateCb(pa_context* c, void* data) {
|
||||
nullptr, nullptr);
|
||||
break;
|
||||
case PA_CONTEXT_FAILED:
|
||||
// When pulseaudio server restarts, the connection is "failed". Try to reconnect.
|
||||
// pa_threaded_mainloop_lock is already acquired in callback threads.
|
||||
// So there is no need to lock it again.
|
||||
if (backend->context_ != nullptr) {
|
||||
pa_context_disconnect(backend->context_);
|
||||
pa_context_unref(backend->context_);
|
||||
backend->context_ = nullptr;
|
||||
if (pa_context_errno(c) != PA_ERR_CONNECTIONREFUSED) {
|
||||
// When pulseaudio server restarts, the connection is "failed". Try to reconnect.
|
||||
// pa_threaded_mainloop_lock is already acquired in callback threads.
|
||||
// So there is no need to lock it again.
|
||||
if (backend->context_ != nullptr) {
|
||||
pa_context_disconnect(backend->context_);
|
||||
pa_context_unref(backend->context_);
|
||||
backend->context_ = nullptr;
|
||||
}
|
||||
backend->connectContext();
|
||||
}
|
||||
backend->connectContext();
|
||||
break;
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
@@ -278,15 +280,23 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo
|
||||
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
||||
}
|
||||
|
||||
uint16_t AudioBackend::getVolume(PulseaudioTarget target) const {
|
||||
if (target == PulseaudioTarget::Source) {
|
||||
return source_volume_;
|
||||
} else {
|
||||
return volume_;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume,
|
||||
AudioTarget target) {
|
||||
PulseaudioTarget target) {
|
||||
// Early return if context is not ready
|
||||
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
|
||||
spdlog::error("PulseAudio context not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_source = target == AudioTarget::Source;
|
||||
bool is_source = target == PulseaudioTarget::Source;
|
||||
|
||||
// Select the appropriate stored volume structure
|
||||
auto& ref_volume = is_source ? pa_source_volume_ : pa_volume_;
|
||||
@@ -326,14 +336,14 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m
|
||||
}
|
||||
|
||||
void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume,
|
||||
AudioTarget target) {
|
||||
PulseaudioTarget target) {
|
||||
// Early return if context is not ready
|
||||
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
|
||||
spdlog::error("PulseAudio context not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_source = target == AudioTarget::Source;
|
||||
bool is_source = target == PulseaudioTarget::Source;
|
||||
|
||||
// Select the appropriate stored volume structure and current volume
|
||||
auto& ref_volume = is_source ? pa_source_volume_ : pa_volume_;
|
||||
@@ -361,8 +371,8 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
||||
// No need to continue with volume change if we had to create a new structure
|
||||
pa_threaded_mainloop_lock(mainloop_);
|
||||
if (is_source) {
|
||||
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume,
|
||||
sourceVolumeModifyCb, this);
|
||||
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
|
||||
this);
|
||||
} else {
|
||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
||||
}
|
||||
@@ -415,6 +425,14 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
||||
pa_threaded_mainloop_unlock(mainloop_);
|
||||
}
|
||||
|
||||
bool AudioBackend::getMuted(PulseaudioTarget target) const {
|
||||
if (target == PulseaudioTarget::Source) {
|
||||
return source_muted_;
|
||||
} else {
|
||||
return muted_;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBackend::toggleSinkMute() {
|
||||
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
|
||||
muted_ = !muted_;
|
||||
@@ -451,6 +469,14 @@ void AudioBackend::toggleSourceMute(bool mute) {
|
||||
pa_threaded_mainloop_unlock(mainloop_);
|
||||
}
|
||||
|
||||
void AudioBackend::unmute(PulseaudioTarget target) {
|
||||
if (target == PulseaudioTarget::Source) {
|
||||
toggleSourceMute(false);
|
||||
} else {
|
||||
toggleSinkMute(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioBackend::isBluetooth() {
|
||||
return monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio
|
||||
monitor_.find("a2dp-sink") != std::string::npos || // PipeWire
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace {
|
||||
const std::regex IMPORT_REGEX(R"(@import\s+(?:url\()?(?:"|')([^"')]+)(?:"|')\)?;)");
|
||||
}
|
||||
|
||||
waybar::CssReloadHelper::CssReloadHelper(std::string cssFile, std::function<void()> callback)
|
||||
waybar::CssReloadHelper::CssReloadHelper(std::string cssFile,
|
||||
std::function<void(const std::string&)> callback)
|
||||
: m_cssFile(std::move(cssFile)), m_callback(std::move(callback)) {}
|
||||
|
||||
std::string waybar::CssReloadHelper::getFileContents(const std::string& filename) {
|
||||
@@ -88,6 +89,12 @@ void waybar::CssReloadHelper::monitorChanges() {
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::CssReloadHelper::changeCssFile(const std::string& newCssFile) {
|
||||
m_fileMonitors.clear();
|
||||
m_cssFile = newCssFile;
|
||||
monitorChanges();
|
||||
}
|
||||
|
||||
void waybar::CssReloadHelper::handleFileChange(Glib::RefPtr<Gio::File> const& file,
|
||||
Glib::RefPtr<Gio::File> const& other_type,
|
||||
Gio::FileMonitorEvent event_type) {
|
||||
@@ -95,7 +102,7 @@ void waybar::CssReloadHelper::handleFileChange(Glib::RefPtr<Gio::File> const& fi
|
||||
// fire for one
|
||||
if (event_type == Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
|
||||
spdlog::debug("Reloading style, file changed: {}", file->get_path());
|
||||
m_callback();
|
||||
m_callback(m_cssFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ std::string rewriteString(const std::string& value, const Json::Value& rules) {
|
||||
// malformated regexes will cause an exception.
|
||||
// in this case, log error and try the next rule.
|
||||
const std::regex rule{it.key().asString(), std::regex_constants::icase};
|
||||
if (std::regex_match(value, rule)) {
|
||||
if (std::regex_match(res, rule)) {
|
||||
res = std::regex_replace(res, rule, it->asString());
|
||||
}
|
||||
} catch (const std::regex_error& e) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct TransformResult {
|
||||
std::string css;
|
||||
bool was_transformed;
|
||||
};
|
||||
|
||||
TransformResult transform_8bit_to_hex(const std::string& file_path) {
|
||||
std::ifstream f(file_path, std::ios::in | std::ios::binary);
|
||||
const auto size = fs::file_size(file_path);
|
||||
std::string result(size, '\0');
|
||||
if (!f.is_open() || !f.good()) {
|
||||
throw std::runtime_error("Cannot open file: " + file_path);
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return {.css = result, .was_transformed = false};
|
||||
}
|
||||
|
||||
f.read(result.data(), size);
|
||||
|
||||
static std::regex pattern(
|
||||
R"(\#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2}))");
|
||||
std::string final_output;
|
||||
|
||||
auto it = std::sregex_iterator(result.begin(), result.end(), pattern);
|
||||
auto eof = std::sregex_iterator();
|
||||
|
||||
if (it == eof) {
|
||||
return {.css = result, .was_transformed = false};
|
||||
}
|
||||
|
||||
std::smatch match;
|
||||
while (it != eof) {
|
||||
match = *it;
|
||||
|
||||
final_output += match.prefix().str();
|
||||
|
||||
int r = stoi(match[1].str(), nullptr, 16);
|
||||
int g = stoi(match[2].str(), nullptr, 16);
|
||||
int b = stoi(match[3].str(), nullptr, 16);
|
||||
double a = (stoi(match[4].str(), nullptr, 16) / 255.0);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "rgba(" << r << "," << g << "," << b << "," << std::fixed << std::setprecision(2) << a
|
||||
<< ")";
|
||||
final_output += ss.str();
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
final_output += match.suffix().str();
|
||||
|
||||
return {.css = final_output, .was_transformed = true};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include <string>
|
||||
#include <util/utf8_string.hpp>
|
||||
|
||||
namespace waybar::util {
|
||||
|
||||
namespace {
|
||||
// Wide characters count as two, zero-width characters count as zero
|
||||
// Modifies str in-place (unless width = std::string::npos)
|
||||
// Returns the total width of the string pre-truncating
|
||||
size_t measure_and_truncate(std::string& str, size_t width = std::string::npos) {
|
||||
if (str.length() == 0) return 0;
|
||||
|
||||
const gchar* trunc_end = nullptr;
|
||||
|
||||
size_t total_width = 0;
|
||||
|
||||
for (gchar *data = str.data(), *end = data + str.size(); data != nullptr;) {
|
||||
gunichar c = g_utf8_get_char_validated(data, end - data);
|
||||
if (c == -1U || c == -2U) {
|
||||
// invalid unicode, treat string as ascii
|
||||
if (width != std::string::npos && str.length() > width) str.resize(width);
|
||||
return str.length();
|
||||
} else if (g_unichar_iswide(c)) {
|
||||
total_width += 2;
|
||||
} else if (!g_unichar_iszerowidth(c) && c != 0xAD) { // neither zero-width nor soft hyphen
|
||||
total_width += 1;
|
||||
}
|
||||
|
||||
data = g_utf8_find_next_char(data, end);
|
||||
if (width != std::string::npos && total_width <= width && !g_unichar_isspace(c))
|
||||
trunc_end = data;
|
||||
}
|
||||
|
||||
if (trunc_end) str.resize(trunc_end - str.data());
|
||||
|
||||
return total_width;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
size_t utf8_width(const std::string& str) {
|
||||
return measure_and_truncate(const_cast<std::string&>(str));
|
||||
}
|
||||
|
||||
void utf8_truncate(std::string& s, const std::string& ellipsis, size_t max_len) {
|
||||
if (max_len == 0) {
|
||||
s.resize(0);
|
||||
return;
|
||||
}
|
||||
size_t len = measure_and_truncate(s, max_len);
|
||||
if (len > max_len) {
|
||||
size_t ellipsis_len = utf8_width(ellipsis);
|
||||
if (max_len >= ellipsis_len) {
|
||||
if (ellipsis_len) measure_and_truncate(s, max_len - ellipsis_len);
|
||||
s += ellipsis;
|
||||
} else {
|
||||
s.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace waybar::util
|
||||
Reference in New Issue
Block a user