Merge branch 'master' into fix/zjeffer/thread-sanitizer-warning
This commit is contained in:
@ -63,7 +63,8 @@ std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto data_dirs = Glib::get_system_data_dirs();
|
||||
auto data_dirs = Glib::get_system_data_dirs();
|
||||
data_dirs.insert(data_dirs.begin(), Glib::get_user_data_dir());
|
||||
for (const auto& data_dir : data_dirs) {
|
||||
const auto data_app_dir = data_dir + "/applications/";
|
||||
auto desktop_file_suffix = app_identifier + ".desktop";
|
||||
|
@ -36,11 +36,12 @@ AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const
|
||||
box_.set_spacing(spacing);
|
||||
|
||||
bool swap_icon_label = false;
|
||||
if (config_.isMember("swap-icon-label")) {
|
||||
if (!config_["swap-icon-label"].isBool())
|
||||
spdlog::warn("'swap-icon-label' must be a bool.");
|
||||
else
|
||||
swap_icon_label = config_["swap-icon-label"].asBool();
|
||||
if (config_["swap-icon-label"].isNull()) {
|
||||
} else if (config_["swap-icon-label"].isBool()) {
|
||||
swap_icon_label = config_["swap-icon-label"].asBool();
|
||||
} else {
|
||||
spdlog::warn("'swap-icon-label' must be a bool, found '{}'. Using default value (false).",
|
||||
config_["swap-icon-label"].asString());
|
||||
}
|
||||
|
||||
if ((rot == 0 || rot == 3) ^ swap_icon_label) {
|
||||
|
@ -17,10 +17,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
|
||||
config["format-alt"].isString() || config["menu"].isString() || enable_click,
|
||||
enable_scroll),
|
||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
||||
|
||||
// Leave the default option outside of the std::max(1L, ...), because the zero value
|
||||
// (default) is used in modules/custom.cpp to make the difference between
|
||||
// two types of custom scripts. Fixes #4521.
|
||||
interval_(config_["interval"] == "once"
|
||||
? std::chrono::seconds::max()
|
||||
: std::chrono::seconds(
|
||||
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
|
||||
? std::chrono::milliseconds::max()
|
||||
: std::chrono::milliseconds(
|
||||
(config_["interval"].isNumeric()
|
||||
? std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(config_["interval"].asDouble()) * 1000)
|
||||
: 1000 * (long)interval))),
|
||||
default_format_(format_) {
|
||||
label_.set_name(name);
|
||||
if (!id.empty()) {
|
||||
|
@ -172,6 +172,10 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
|
||||
// Popup the menu
|
||||
gtk_widget_show_all(GTK_WIDGET(menu_));
|
||||
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
|
||||
// Manually reset prelight to make sure the module doesn't stay in a hover state
|
||||
if (auto* module = event_box_.get_child(); module != nullptr) {
|
||||
module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second call user scripts
|
||||
|
13
src/bar.cpp
13
src/bar.cpp
@ -433,7 +433,18 @@ void waybar::Bar::onMap(GdkEventAny* /*unused*/) {
|
||||
/*
|
||||
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
|
||||
*/
|
||||
auto* gdk_window = window.get_window()->gobj();
|
||||
auto gdk_window_ref = window.get_window();
|
||||
if (!gdk_window_ref) {
|
||||
spdlog::warn("Failed to get GDK window during onMap, deferring surface initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* gdk_window = gdk_window_ref->gobj();
|
||||
if (!gdk_window) {
|
||||
spdlog::warn("GDK window object is null during onMap, deferring surface initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
||||
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window));
|
||||
|
||||
|
@ -106,12 +106,24 @@ void Config::setupConfig(Json::Value &dst, const std::string &config_file, int d
|
||||
mergeConfig(dst, tmp_config);
|
||||
}
|
||||
|
||||
std::optional<std::string> Config::findIncludePath(const std::string &name) {
|
||||
std::vector<std::string> Config::findIncludePath(const std::string &name,
|
||||
const std::vector<std::string> &dirs) {
|
||||
auto match1 = tryExpandPath(name, "");
|
||||
if (!match1.empty()) {
|
||||
return match1.front();
|
||||
return match1;
|
||||
}
|
||||
return findConfigPath({name});
|
||||
if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) {
|
||||
if (auto res = tryExpandPath(dir, name); !res.empty()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
for (const auto &dir : dirs) {
|
||||
if (auto res = tryExpandPath(dir, name); !res.empty()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
||||
@ -119,18 +131,22 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) {
|
||||
if (includes.isArray()) {
|
||||
for (const auto &include : includes) {
|
||||
spdlog::info("Including resource file: {}", include.asString());
|
||||
auto match = findIncludePath(include.asString());
|
||||
if (match.has_value()) {
|
||||
setupConfig(config, match.value(), depth + 1);
|
||||
auto matches = findIncludePath(include.asString());
|
||||
if (!matches.empty()) {
|
||||
for (const auto &match : matches) {
|
||||
setupConfig(config, match, depth + 1);
|
||||
}
|
||||
} else {
|
||||
spdlog::warn("Unable to find resource file: {}", include.asString());
|
||||
}
|
||||
}
|
||||
} else if (includes.isString()) {
|
||||
spdlog::info("Including resource file: {}", includes.asString());
|
||||
auto match = findIncludePath(includes.asString());
|
||||
if (match.has_value()) {
|
||||
setupConfig(config, match.value(), depth + 1);
|
||||
auto matches = findIncludePath(includes.asString());
|
||||
if (!matches.empty()) {
|
||||
for (const auto &match : matches) {
|
||||
setupConfig(config, match, depth + 1);
|
||||
}
|
||||
} else {
|
||||
spdlog::warn("Unable to find resource file: {}", includes.asString());
|
||||
}
|
||||
|
@ -109,7 +109,7 @@
|
||||
#include "modules/wireplumber.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAVA
|
||||
#include "modules/cava.hpp"
|
||||
#include "modules/cava/cava.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
#include "modules/systemd_failed_units.hpp"
|
||||
@ -343,7 +343,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAVA
|
||||
if (ref == "cava") {
|
||||
return new waybar::modules::Cava(id, config_[name]);
|
||||
return new waybar::modules::cava::Cava(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD_MONITOR
|
||||
|
@ -1,211 +0,0 @@
|
||||
#include "modules/cava.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "cava", id, "{}", 60, false, false, false) {
|
||||
// Load waybar module config
|
||||
char cfgPath[PATH_MAX];
|
||||
cfgPath[0] = '\0';
|
||||
|
||||
if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data());
|
||||
// Load cava config
|
||||
error_.length = 0;
|
||||
|
||||
if (!load_config(cfgPath, &prm_, false, &error_)) {
|
||||
spdlog::error("Error loading config. {0}", error_.message);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Override cava parameters by the user config
|
||||
prm_.inAtty = 0;
|
||||
prm_.output = cava::output_method::OUTPUT_RAW;
|
||||
strcpy(prm_.data_format, "ascii");
|
||||
strcpy(prm_.raw_target, "/dev/stdout");
|
||||
prm_.ascii_range = config_["format-icons"].size() - 1;
|
||||
|
||||
prm_.bar_width = 2;
|
||||
prm_.bar_spacing = 0;
|
||||
prm_.bar_height = 32;
|
||||
prm_.bar_width = 1;
|
||||
prm_.orientation = cava::ORIENT_TOP;
|
||||
prm_.xaxis = cava::xaxis_scale::NONE;
|
||||
prm_.mono_opt = cava::AVERAGE;
|
||||
prm_.autobars = 0;
|
||||
prm_.gravity = 0;
|
||||
prm_.integral = 1;
|
||||
|
||||
if (config_["framerate"].isInt()) prm_.framerate = config_["framerate"].asInt();
|
||||
if (config_["autosens"].isInt()) prm_.autosens = config_["autosens"].asInt();
|
||||
if (config_["sensitivity"].isInt()) prm_.sens = config_["sensitivity"].asInt();
|
||||
if (config_["bars"].isInt()) prm_.fixedbars = config_["bars"].asInt();
|
||||
if (config_["lower_cutoff_freq"].isNumeric())
|
||||
prm_.lower_cut_off = config_["lower_cutoff_freq"].asLargestInt();
|
||||
if (config_["higher_cutoff_freq"].isNumeric())
|
||||
prm_.upper_cut_off = config_["higher_cutoff_freq"].asLargestInt();
|
||||
if (config_["sleep_timer"].isInt()) prm_.sleep_timer = config_["sleep_timer"].asInt();
|
||||
if (config_["method"].isString())
|
||||
prm_.input = cava::input_method_by_name(config_["method"].asString().c_str());
|
||||
if (config_["source"].isString()) prm_.audio_source = config_["source"].asString().data();
|
||||
if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt();
|
||||
if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();
|
||||
if (config_["stereo"].isBool()) prm_.stereo = config_["stereo"].asBool();
|
||||
if (config_["reverse"].isBool()) prm_.reverse = config_["reverse"].asBool();
|
||||
if (config_["bar_delimiter"].isInt()) prm_.bar_delim = config_["bar_delimiter"].asInt();
|
||||
if (config_["monstercat"].isBool()) prm_.monstercat = config_["monstercat"].asBool();
|
||||
if (config_["waves"].isBool()) prm_.waves = config_["waves"].asBool();
|
||||
if (config_["noise_reduction"].isDouble())
|
||||
prm_.noise_reduction = config_["noise_reduction"].asDouble();
|
||||
if (config_["input_delay"].isInt())
|
||||
fetch_input_delay_ = std::chrono::seconds(config_["input_delay"].asInt());
|
||||
if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool();
|
||||
if (config_["format_silent"].isString()) format_silent_ = config_["format_silent"].asString();
|
||||
// Make cava parameters configuration
|
||||
plan_ = new cava::cava_plan{};
|
||||
|
||||
audio_raw_.height = prm_.ascii_range;
|
||||
audio_data_.format = -1;
|
||||
audio_data_.source = new char[1 + strlen(prm_.audio_source)];
|
||||
audio_data_.source[0] = '\0';
|
||||
strcpy(audio_data_.source, prm_.audio_source);
|
||||
|
||||
audio_data_.rate = 0;
|
||||
audio_data_.samples_counter = 0;
|
||||
audio_data_.channels = 2;
|
||||
audio_data_.IEEE_FLOAT = 0;
|
||||
|
||||
audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels;
|
||||
audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8;
|
||||
|
||||
audio_data_.cava_in = new double[audio_data_.cava_buffer_size]{0.0};
|
||||
|
||||
audio_data_.terminate = 0;
|
||||
audio_data_.suspendFlag = false;
|
||||
input_source_ = get_input(&audio_data_, &prm_);
|
||||
|
||||
if (!input_source_) {
|
||||
spdlog::error("cava API didn't provide input audio source method");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// Calculate delay for Update() thread
|
||||
frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate));
|
||||
|
||||
// Init cava plan, audio_raw structure
|
||||
audio_raw_init(&audio_data_, &audio_raw_, &prm_, plan_);
|
||||
if (!plan_) spdlog::error("cava plan is not provided");
|
||||
audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message
|
||||
// Read audio source trough cava API. Cava orginizes this process via infinity loop
|
||||
thread_fetch_input_ = [this] {
|
||||
thread_fetch_input_.sleep_for(fetch_input_delay_);
|
||||
input_source_(&audio_data_);
|
||||
};
|
||||
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(frame_time_milsec_);
|
||||
};
|
||||
}
|
||||
|
||||
waybar::modules::Cava::~Cava() {
|
||||
thread_fetch_input_.stop();
|
||||
thread_.stop();
|
||||
delete plan_;
|
||||
plan_ = nullptr;
|
||||
}
|
||||
|
||||
void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta == std::chrono::seconds{0}) {
|
||||
delta += std::chrono::seconds{1};
|
||||
delay += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta > std::chrono::seconds{0}) {
|
||||
delay -= delta;
|
||||
delta -= std::chrono::seconds{1};
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::Cava::update() -> void {
|
||||
if (audio_data_.suspendFlag) return;
|
||||
silence_ = true;
|
||||
|
||||
for (int i{0}; i < audio_data_.input_buffer_size; ++i) {
|
||||
if (audio_data_.cava_in[i]) {
|
||||
silence_ = false;
|
||||
sleep_counter_ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (silence_ && prm_.sleep_timer != 0) {
|
||||
if (sleep_counter_ <=
|
||||
(int)(std::chrono::milliseconds(prm_.sleep_timer * 1s) / frame_time_milsec_)) {
|
||||
++sleep_counter_;
|
||||
silence_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!silence_ || prm_.sleep_timer == 0) {
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
// Process: execute cava
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out,
|
||||
plan_);
|
||||
if (audio_data_.samples_counter > 0) audio_data_.samples_counter = 0;
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
|
||||
// Do transformation under raw data
|
||||
audio_raw_fetch(&audio_raw_, &prm_, &rePaint_, plan_);
|
||||
|
||||
if (rePaint_ == 1) {
|
||||
text_.clear();
|
||||
|
||||
for (int i{0}; i < audio_raw_.number_of_bars; ++i) {
|
||||
audio_raw_.previous_frame[i] = audio_raw_.bars[i];
|
||||
text_.append(
|
||||
getIcon((audio_raw_.bars[i] > prm_.ascii_range) ? prm_.ascii_range : audio_raw_.bars[i],
|
||||
"", prm_.ascii_range + 1));
|
||||
if (prm_.bar_delim != 0) text_.push_back(prm_.bar_delim);
|
||||
}
|
||||
|
||||
label_.set_markup(text_);
|
||||
label_.show();
|
||||
ALabel::update();
|
||||
label_.get_style_context()->add_class("updated");
|
||||
}
|
||||
|
||||
label_.get_style_context()->remove_class("silent");
|
||||
} else {
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
if (hide_on_silence_)
|
||||
label_.hide();
|
||||
else if (config_["format_silent"].isString())
|
||||
label_.set_markup(format_silent_);
|
||||
|
||||
label_.get_style_context()->add_class("silent");
|
||||
label_.get_style_context()->remove_class("updated");
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::Cava::doAction(const std::string& name) -> void {
|
||||
if ((actionMap_[name])) {
|
||||
(this->*actionMap_[name])();
|
||||
} else
|
||||
spdlog::error("Cava. Unsupported action \"{0}\"", name);
|
||||
}
|
||||
|
||||
// Cava actions
|
||||
void waybar::modules::Cava::pause_resume() {
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
if (audio_data_.suspendFlag) {
|
||||
audio_data_.suspendFlag = false;
|
||||
pthread_cond_broadcast(&audio_data_.resumeCond);
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
} else {
|
||||
audio_data_.suspendFlag = true;
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
}
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
}
|
51
src/modules/cava/cava.cpp
Normal file
51
src/modules/cava/cava.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "modules/cava/cava.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
waybar::modules::cava::Cava::Cava(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "cava", id, "{}", 60, false, false, false),
|
||||
backend_{waybar::modules::cava::CavaBackend::inst(config)} {
|
||||
if (config_["hide_on_silence"].isBool()) hide_on_silence_ = config_["hide_on_silence"].asBool();
|
||||
if (config_["format_silent"].isString()) format_silent_ = config_["format_silent"].asString();
|
||||
|
||||
ascii_range_ = backend_->getAsciiRange();
|
||||
backend_->signal_update().connect(sigc::mem_fun(*this, &Cava::onUpdate));
|
||||
backend_->signal_silence().connect(sigc::mem_fun(*this, &Cava::onSilence));
|
||||
backend_->Update();
|
||||
}
|
||||
|
||||
auto waybar::modules::cava::Cava::doAction(const std::string& name) -> void {
|
||||
if ((actionMap_[name])) {
|
||||
(this->*actionMap_[name])();
|
||||
} else
|
||||
spdlog::error("Cava. Unsupported action \"{0}\"", name);
|
||||
}
|
||||
|
||||
// Cava actions
|
||||
void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
|
||||
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
|
||||
if (silence_) {
|
||||
label_.get_style_context()->remove_class("silent");
|
||||
label_.get_style_context()->add_class("updated");
|
||||
}
|
||||
label_text_.clear();
|
||||
for (auto& ch : input)
|
||||
label_text_.append(getIcon((ch > ascii_range_) ? ascii_range_ : ch, "", ascii_range_ + 1));
|
||||
|
||||
label_.set_markup(label_text_);
|
||||
label_.show();
|
||||
ALabel::update();
|
||||
silence_ = false;
|
||||
}
|
||||
auto waybar::modules::cava::Cava::onSilence() -> void {
|
||||
if (!silence_) {
|
||||
label_.get_style_context()->remove_class("updated");
|
||||
|
||||
if (hide_on_silence_)
|
||||
label_.hide();
|
||||
else if (config_["format_silent"].isString())
|
||||
label_.set_markup(format_silent_);
|
||||
silence_ = true;
|
||||
label_.get_style_context()->add_class("silent");
|
||||
}
|
||||
}
|
223
src/modules/cava/cava_backend.cpp
Normal file
223
src/modules/cava/cava_backend.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include "modules/cava/cava_backend.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
std::shared_ptr<waybar::modules::cava::CavaBackend> waybar::modules::cava::CavaBackend::inst(
|
||||
const Json::Value& config) {
|
||||
static auto* backend = new CavaBackend(config);
|
||||
static std::shared_ptr<CavaBackend> backend_ptr{backend};
|
||||
return backend_ptr;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::CavaBackend(const Json::Value& config) {
|
||||
// Load waybar module config
|
||||
char cfgPath[PATH_MAX];
|
||||
cfgPath[0] = '\0';
|
||||
|
||||
if (config["cava_config"].isString()) strcpy(cfgPath, config["cava_config"].asString().data());
|
||||
// Load cava config
|
||||
error_.length = 0;
|
||||
|
||||
if (!load_config(cfgPath, &prm_, false, &error_)) {
|
||||
spdlog::error("cava backend. Error loading config. {0}", error_.message);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Override cava parameters by the user config
|
||||
prm_.inAtty = 0;
|
||||
prm_.output = ::cava::output_method::OUTPUT_RAW;
|
||||
strcpy(prm_.data_format, "ascii");
|
||||
strcpy(prm_.raw_target, "/dev/stdout");
|
||||
prm_.ascii_range = config["format-icons"].size() - 1;
|
||||
|
||||
prm_.bar_width = 2;
|
||||
prm_.bar_spacing = 0;
|
||||
prm_.bar_height = 32;
|
||||
prm_.bar_width = 1;
|
||||
prm_.orientation = ::cava::ORIENT_TOP;
|
||||
prm_.xaxis = ::cava::xaxis_scale::NONE;
|
||||
prm_.mono_opt = ::cava::AVERAGE;
|
||||
prm_.autobars = 0;
|
||||
prm_.gravity = 0;
|
||||
prm_.integral = 1;
|
||||
|
||||
if (config["framerate"].isInt()) prm_.framerate = config["framerate"].asInt();
|
||||
// Calculate delay for Update() thread
|
||||
frame_time_milsec_ = std::chrono::milliseconds((int)(1e3 / prm_.framerate));
|
||||
if (config["autosens"].isInt()) prm_.autosens = config["autosens"].asInt();
|
||||
if (config["sensitivity"].isInt()) prm_.sens = config["sensitivity"].asInt();
|
||||
if (config["bars"].isInt()) prm_.fixedbars = config["bars"].asInt();
|
||||
if (config["lower_cutoff_freq"].isNumeric())
|
||||
prm_.lower_cut_off = config["lower_cutoff_freq"].asLargestInt();
|
||||
if (config["higher_cutoff_freq"].isNumeric())
|
||||
prm_.upper_cut_off = config["higher_cutoff_freq"].asLargestInt();
|
||||
if (config["sleep_timer"].isInt()) prm_.sleep_timer = config["sleep_timer"].asInt();
|
||||
if (config["method"].isString())
|
||||
prm_.input = ::cava::input_method_by_name(config["method"].asString().c_str());
|
||||
if (config["source"].isString()) prm_.audio_source = config["source"].asString().data();
|
||||
if (config["sample_rate"].isNumeric()) prm_.samplerate = config["sample_rate"].asLargestInt();
|
||||
if (config["sample_bits"].isInt()) prm_.samplebits = config["sample_bits"].asInt();
|
||||
if (config["stereo"].isBool()) prm_.stereo = config["stereo"].asBool();
|
||||
if (config["reverse"].isBool()) prm_.reverse = config["reverse"].asBool();
|
||||
if (config["bar_delimiter"].isInt()) prm_.bar_delim = config["bar_delimiter"].asInt();
|
||||
if (config["monstercat"].isBool()) prm_.monstercat = config["monstercat"].asBool();
|
||||
if (config["waves"].isBool()) prm_.waves = config["waves"].asBool();
|
||||
if (config["noise_reduction"].isDouble())
|
||||
prm_.noise_reduction = config["noise_reduction"].asDouble();
|
||||
if (config["input_delay"].isInt())
|
||||
fetch_input_delay_ = std::chrono::seconds(config["input_delay"].asInt());
|
||||
|
||||
// Make cava parameters configuration
|
||||
plan_ = new ::cava::cava_plan{};
|
||||
|
||||
audio_raw_.height = prm_.ascii_range;
|
||||
audio_data_.format = -1;
|
||||
audio_data_.source = new char[1 + strlen(prm_.audio_source)];
|
||||
audio_data_.source[0] = '\0';
|
||||
strcpy(audio_data_.source, prm_.audio_source);
|
||||
|
||||
audio_data_.rate = 0;
|
||||
audio_data_.samples_counter = 0;
|
||||
audio_data_.channels = 2;
|
||||
audio_data_.IEEE_FLOAT = 0;
|
||||
|
||||
audio_data_.input_buffer_size = BUFFER_SIZE * audio_data_.channels;
|
||||
audio_data_.cava_buffer_size = audio_data_.input_buffer_size * 8;
|
||||
|
||||
audio_data_.cava_in = new double[audio_data_.cava_buffer_size]{0.0};
|
||||
|
||||
audio_data_.terminate = 0;
|
||||
audio_data_.suspendFlag = false;
|
||||
input_source_ = get_input(&audio_data_, &prm_);
|
||||
|
||||
if (!input_source_) {
|
||||
spdlog::error("cava backend API didn't provide input audio source method");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Init cava plan, audio_raw structure
|
||||
audio_raw_init(&audio_data_, &audio_raw_, &prm_, plan_);
|
||||
if (!plan_) spdlog::error("cava backend plan is not provided");
|
||||
audio_raw_.previous_frame[0] = -1; // For first Update() call need to rePaint text message
|
||||
// Read audio source trough cava API. Cava orginizes this process via infinity loop
|
||||
read_thread_ = [this] {
|
||||
try {
|
||||
input_source_(&audio_data_);
|
||||
} catch (const std::runtime_error& e) {
|
||||
spdlog::warn("Cava backend. Read source error: {0}", e.what());
|
||||
}
|
||||
read_thread_.sleep_for(fetch_input_delay_);
|
||||
};
|
||||
|
||||
thread_ = [this] {
|
||||
doUpdate();
|
||||
thread_.sleep_for(frame_time_milsec_);
|
||||
};
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::~CavaBackend() {
|
||||
thread_.stop();
|
||||
read_thread_.stop();
|
||||
delete plan_;
|
||||
plan_ = nullptr;
|
||||
}
|
||||
|
||||
static void upThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta == std::chrono::seconds{0}) {
|
||||
delta += std::chrono::seconds{1};
|
||||
delay += delta;
|
||||
}
|
||||
}
|
||||
|
||||
static void downThreadDelay(std::chrono::milliseconds& delay, std::chrono::seconds& delta) {
|
||||
if (delta > std::chrono::seconds{0}) {
|
||||
delay -= delta;
|
||||
delta -= std::chrono::seconds{1};
|
||||
}
|
||||
}
|
||||
|
||||
bool waybar::modules::cava::CavaBackend::isSilence() {
|
||||
for (int i{0}; i < audio_data_.input_buffer_size; ++i) {
|
||||
if (audio_data_.cava_in[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int waybar::modules::cava::CavaBackend::getAsciiRange() { return prm_.ascii_range; }
|
||||
|
||||
// Process: execute cava
|
||||
void waybar::modules::cava::CavaBackend::invoke() {
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
::cava::cava_execute(audio_data_.cava_in, audio_data_.samples_counter, audio_raw_.cava_out,
|
||||
plan_);
|
||||
if (audio_data_.samples_counter > 0) audio_data_.samples_counter = 0;
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
}
|
||||
|
||||
// Do transformation under raw data
|
||||
void waybar::modules::cava::CavaBackend::execute() {
|
||||
invoke();
|
||||
audio_raw_fetch(&audio_raw_, &prm_, &re_paint_, plan_);
|
||||
|
||||
if (re_paint_ == 1) {
|
||||
output_.clear();
|
||||
for (int i{0}; i < audio_raw_.number_of_bars; ++i) {
|
||||
audio_raw_.previous_frame[i] = audio_raw_.bars[i];
|
||||
output_.push_back(audio_raw_.bars[i]);
|
||||
if (prm_.bar_delim != 0) output_.push_back(prm_.bar_delim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaBackend::doPauseResume() {
|
||||
pthread_mutex_lock(&audio_data_.lock);
|
||||
if (audio_data_.suspendFlag) {
|
||||
audio_data_.suspendFlag = false;
|
||||
pthread_cond_broadcast(&audio_data_.resumeCond);
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
} else {
|
||||
audio_data_.suspendFlag = true;
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
}
|
||||
pthread_mutex_unlock(&audio_data_.lock);
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_update
|
||||
waybar::modules::cava::CavaBackend::signal_update() {
|
||||
return m_signal_update_;
|
||||
}
|
||||
|
||||
waybar::modules::cava::CavaBackend::type_signal_silence
|
||||
waybar::modules::cava::CavaBackend::signal_silence() {
|
||||
return m_signal_silence_;
|
||||
}
|
||||
|
||||
void waybar::modules::cava::CavaBackend::Update() { doUpdate(true); }
|
||||
|
||||
void waybar::modules::cava::CavaBackend::doUpdate(bool force) {
|
||||
if (audio_data_.suspendFlag && !force) return;
|
||||
|
||||
silence_ = isSilence();
|
||||
if (!silence_) sleep_counter_ = 0;
|
||||
|
||||
if (silence_ && prm_.sleep_timer != 0) {
|
||||
if (sleep_counter_ <=
|
||||
(int)(std::chrono::milliseconds(prm_.sleep_timer * 1s) / frame_time_milsec_)) {
|
||||
++sleep_counter_;
|
||||
silence_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!silence_ || prm_.sleep_timer == 0) {
|
||||
downThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
execute();
|
||||
if (re_paint_ == 1 || force) m_signal_update_.emit(output_);
|
||||
} else {
|
||||
upThreadDelay(frame_time_milsec_, suspend_silence_delay_);
|
||||
if (silence_ != silence_prev_ || force) m_signal_silence_.emit();
|
||||
}
|
||||
silence_prev_ = silence_;
|
||||
}
|
@ -30,6 +30,9 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
cldMonShift_{year(1900) / January},
|
||||
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
|
||||
tzCurrIdx_{0},
|
||||
tzTooltipFormat_{config_["timezone-tooltip-format"].isString()
|
||||
? config_["timezone-tooltip-format"].asString()
|
||||
: ""},
|
||||
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
|
||||
m_tlpText_ = m_tlpFmt_;
|
||||
|
||||
@ -63,8 +66,8 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
if (cldInTooltip_) {
|
||||
if (config_[kCldPlaceholder]["mode"].isString()) {
|
||||
const std::string cfgMode{config_[kCldPlaceholder]["mode"].asString()};
|
||||
const std::map<std::string_view, const CldMode&> monthModes{{"month", CldMode::MONTH},
|
||||
{"year", CldMode::YEAR}};
|
||||
const std::map<std::string, CldMode> monthModes{{"month", CldMode::MONTH},
|
||||
{"year", CldMode::YEAR}};
|
||||
if (monthModes.find(cfgMode) != monthModes.end())
|
||||
cldMode_ = monthModes.at(cfgMode);
|
||||
else
|
||||
@ -73,6 +76,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
"using instead",
|
||||
cfgMode);
|
||||
}
|
||||
|
||||
if (config_[kCldPlaceholder]["iso8601"].isBool()) {
|
||||
iso8601Calendar_ = config_[kCldPlaceholder]["iso8601"].asBool();
|
||||
}
|
||||
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].isString()) {
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "left") cldWPos_ = WS::LEFT;
|
||||
if (config_[kCldPlaceholder]["weeks-pos"].asString() == "right") cldWPos_ = WS::RIGHT;
|
||||
@ -92,23 +100,25 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
fmtMap_.insert({2, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["today"].isString()) {
|
||||
fmtMap_.insert({3, config_[kCldPlaceholder]["format"]["today"].asString()});
|
||||
cldBaseDay_ =
|
||||
year_month_day{
|
||||
floor<days>(zoned_time{local_zone(), system_clock::now()}.get_local_time())}
|
||||
.day();
|
||||
auto local_time = zoned_time{local_zone(), system_clock::now()}.get_local_time();
|
||||
cldBaseDay_ = year_month_day{floor<days>(local_time)}.day();
|
||||
} else
|
||||
fmtMap_.insert({3, "{}"});
|
||||
if (config_[kCldPlaceholder]["format"]["weeks"].isString() && cldWPos_ != WS::HIDDEN) {
|
||||
const auto defaultFmt =
|
||||
iso8601Calendar_ ? "{:%V}" : ((first_day_of_week() == Monday) ? "{:%W}" : "{:%U}");
|
||||
fmtMap_.insert({4, std::regex_replace(config_[kCldPlaceholder]["format"]["weeks"].asString(),
|
||||
std::regex("\\{\\}"),
|
||||
(first_day_of_week() == Monday) ? "{:%W}" : "{:%U}")});
|
||||
std::regex("\\{\\}"), defaultFmt)});
|
||||
Glib::ustring tmp{std::regex_replace(fmtMap_[4], std::regex("</?[^>]+>|\\{.*\\}"), "")};
|
||||
cldWnLen_ += tmp.size();
|
||||
} else {
|
||||
if (cldWPos_ != WS::HIDDEN)
|
||||
fmtMap_.insert({4, (first_day_of_week() == Monday) ? "{:%W}" : "{:%U}"});
|
||||
else
|
||||
if (cldWPos_ != WS::HIDDEN) {
|
||||
const auto defaultFmt =
|
||||
iso8601Calendar_ ? "{:%V}" : ((first_day_of_week() == Monday) ? "{:%W}" : "{:%U}");
|
||||
fmtMap_.insert({4, defaultFmt});
|
||||
} else {
|
||||
cldWnLen_ = 0;
|
||||
}
|
||||
}
|
||||
if (config_[kCldPlaceholder]["mode-mon-col"].isInt()) {
|
||||
cldMonCols_ = config_[kCldPlaceholder]["mode-mon-col"].asInt();
|
||||
@ -188,11 +198,26 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string {
|
||||
if (tzList_.size() == 1) return "";
|
||||
|
||||
std::stringstream os;
|
||||
bool first = true;
|
||||
for (size_t tz_idx{0}; tz_idx < tzList_.size(); ++tz_idx) {
|
||||
if (static_cast<int>(tz_idx) == tzCurrIdx_) continue;
|
||||
const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone();
|
||||
// Skip local timezone (nullptr) - never show it in tooltip
|
||||
if (tzList_[tz_idx] == nullptr) continue;
|
||||
|
||||
// Skip current timezone unless timezone-tooltip-format is specified
|
||||
if (static_cast<int>(tz_idx) == tzCurrIdx_ && tzTooltipFormat_.empty()) continue;
|
||||
|
||||
const auto* tz = tzList_[tz_idx];
|
||||
auto zt{zoned_time{tz, now}};
|
||||
os << fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(zt)) << '\n';
|
||||
|
||||
// Add newline before each entry except the first
|
||||
if (!first) {
|
||||
os << '\n';
|
||||
}
|
||||
first = false;
|
||||
|
||||
// Use timezone-tooltip-format if specified, otherwise use format_
|
||||
const std::string& fmt = tzTooltipFormat_.empty() ? format_ : tzTooltipFormat_;
|
||||
os << fmt_lib::vformat(m_locale_, fmt, fmt_lib::make_format_args(zt));
|
||||
}
|
||||
|
||||
return os.str();
|
||||
@ -204,9 +229,11 @@ const unsigned cldRowsInMonth(const year_month& ym, const weekday& firstdow) {
|
||||
|
||||
auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsigned line)
|
||||
-> const year_month_weekday {
|
||||
unsigned index{line - 2};
|
||||
if (weekday{ym / 1} == firstdow) ++index;
|
||||
return ym / firstdow[index];
|
||||
const unsigned idx = line - 2;
|
||||
const std::chrono::weekday_indexed indexed_first_day_of_week =
|
||||
weekday{ym / 1} == firstdow ? firstdow[idx + 1] : firstdow[idx];
|
||||
|
||||
return ym / indexed_first_day_of_week;
|
||||
}
|
||||
|
||||
auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line,
|
||||
@ -265,7 +292,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const
|
||||
}
|
||||
// Print non-first week
|
||||
default: {
|
||||
auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
||||
const auto ymdTmp{cldGetWeekForLine(ym, firstdow, line)};
|
||||
if (ymdTmp.ok()) {
|
||||
auto d{year_month_day{ymdTmp}.day()};
|
||||
const auto dlast{(ym / last).day()};
|
||||
@ -356,8 +383,9 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
|
||||
: static_cast<const zoned_seconds&&>(zoned_seconds{
|
||||
tz, local_days{cldGetWeekForLine(ymTmp, firstdow, line)}})))
|
||||
<< ' ';
|
||||
} else
|
||||
} else {
|
||||
os << pads;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,6 +509,9 @@ using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
|
||||
|
||||
// Computations done similarly to Linux cal utility.
|
||||
auto waybar::modules::Clock::first_day_of_week() -> weekday {
|
||||
if (iso8601Calendar_) {
|
||||
return Monday;
|
||||
}
|
||||
#ifdef HAVE_LANGINFO_1STDAY
|
||||
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
|
||||
newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)};
|
||||
|
@ -89,9 +89,11 @@ void waybar::modules::Custom::continuousWorker() {
|
||||
dp.emit();
|
||||
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
|
||||
}
|
||||
if (config_["restart-interval"].isUInt()) {
|
||||
if (config_["restart-interval"].isNumeric()) {
|
||||
pid_ = -1;
|
||||
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
|
||||
thread_.sleep_for(std::chrono::milliseconds(
|
||||
std::max(1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(config_["restart-interval"].asDouble() * 1000))));
|
||||
fp_ = util::command::open(cmd, pid_, output_name_);
|
||||
if (!fp_) {
|
||||
throw std::runtime_error("Unable to open " + cmd);
|
||||
|
@ -349,11 +349,11 @@ Workspace::Workspace(const Json::Value &config, WorkspaceManager &manager,
|
||||
}
|
||||
const bool config_on_click_middle = config["on-click-middle"].isString();
|
||||
if (config_on_click_middle) {
|
||||
on_click_middle_action_ = config["on-click"].asString();
|
||||
on_click_middle_action_ = config["on-click-middle"].asString();
|
||||
}
|
||||
const bool config_on_click_right = config["on-click-right"].isString();
|
||||
if (config_on_click_right) {
|
||||
on_click_right_action_ = config["on-click"].asString();
|
||||
on_click_right_action_ = config["on-click-right"].asString();
|
||||
}
|
||||
|
||||
// setup UI
|
||||
@ -377,16 +377,19 @@ Workspace::~Workspace() {
|
||||
}
|
||||
|
||||
void Workspace::update() {
|
||||
if (!needs_updating_) {
|
||||
return;
|
||||
}
|
||||
const auto style_context = button_.get_style_context();
|
||||
|
||||
// update style and visibility
|
||||
|
||||
const auto style_context = button_.get_style_context();
|
||||
style_context->remove_class("active");
|
||||
style_context->remove_class("urgent");
|
||||
style_context->remove_class("hidden");
|
||||
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||
style_context->remove_class("active");
|
||||
}
|
||||
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_URGENT)) {
|
||||
style_context->remove_class("urgent");
|
||||
}
|
||||
if (!has_state(EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN)) {
|
||||
style_context->remove_class("hidden");
|
||||
}
|
||||
|
||||
if (has_state(EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
|
||||
button_.set_visible(true);
|
||||
@ -408,34 +411,26 @@ void Workspace::update() {
|
||||
label_.set_markup(fmt::format(fmt::runtime(format_), fmt::arg("name", name_),
|
||||
fmt::arg("id", workspace_id_),
|
||||
fmt::arg("icon", with_icon_ ? icon() : "")));
|
||||
|
||||
needs_updating_ = false;
|
||||
}
|
||||
|
||||
void Workspace::handle_id(const std::string &id) {
|
||||
spdlog::debug("[ext/workspaces]: ID for workspace {}: {}", id_, id);
|
||||
workspace_id_ = id;
|
||||
needs_updating_ = true;
|
||||
workspace_manager_.set_needs_sorting();
|
||||
}
|
||||
|
||||
void Workspace::handle_name(const std::string &name) {
|
||||
spdlog::debug("[ext/workspaces]: Name for workspace {}: {}", id_, name);
|
||||
name_ = name;
|
||||
needs_updating_ = true;
|
||||
workspace_manager_.set_needs_sorting();
|
||||
}
|
||||
|
||||
void Workspace::handle_coordinates(const std::vector<uint32_t> &coordinates) {
|
||||
coordinates_ = coordinates;
|
||||
needs_updating_ = true;
|
||||
workspace_manager_.set_needs_sorting();
|
||||
}
|
||||
|
||||
void Workspace::handle_state(uint32_t state) {
|
||||
state_ = state;
|
||||
needs_updating_ = true;
|
||||
}
|
||||
void Workspace::handle_state(uint32_t state) { state_ = state; }
|
||||
|
||||
void Workspace::handle_capabilities(uint32_t capabilities) {
|
||||
spdlog::debug("[ext/workspaces]: Capabilities for workspace {}:", id_);
|
||||
@ -451,7 +446,6 @@ void Workspace::handle_capabilities(uint32_t capabilities) {
|
||||
if ((capabilities & EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN) == capabilities) {
|
||||
spdlog::debug("[ext/workspaces]: - assign");
|
||||
}
|
||||
needs_updating_ = true;
|
||||
}
|
||||
|
||||
void Workspace::handle_removed() {
|
||||
@ -475,6 +469,8 @@ bool Workspace::handle_clicked(const GdkEventButton *button) const {
|
||||
|
||||
if (action == "activate") {
|
||||
ext_workspace_handle_v1_activate(ext_handle_);
|
||||
} else if (action == "deactivate") {
|
||||
ext_workspace_handle_v1_deactivate(ext_handle_);
|
||||
} else if (action == "close") {
|
||||
ext_workspace_handle_v1_remove(ext_handle_);
|
||||
} else {
|
||||
|
@ -53,7 +53,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
||||
if (config_["icon-spacing"].isUInt()) {
|
||||
iconSpacing = config_["icon-spacing"].asUInt();
|
||||
}
|
||||
box_.set_spacing(iconSpacing);
|
||||
|
||||
// Whether to use icon or not
|
||||
if (config_["use-icon"].isBool()) {
|
||||
@ -64,7 +63,6 @@ Gamemode::Gamemode(const std::string& id, const Json::Value& config)
|
||||
if (config_["icon-size"].isUInt()) {
|
||||
iconSize = config_["icon-size"].asUInt();
|
||||
}
|
||||
icon_.set_pixel_size(iconSize);
|
||||
|
||||
// Format
|
||||
if (config_["format"].isString()) {
|
||||
@ -228,6 +226,11 @@ auto Gamemode::update() -> void {
|
||||
iconName = DEFAULT_ICON_NAME;
|
||||
}
|
||||
icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID);
|
||||
box_.set_spacing(iconSpacing);
|
||||
icon_.set_pixel_size(iconSize);
|
||||
} else {
|
||||
box_.set_spacing(0);
|
||||
icon_.set_pixel_size(0);
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
|
@ -46,9 +46,14 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
|
||||
IPC::IPC() {
|
||||
// will start IPC and relay events to parseIPC
|
||||
ipcThread_ = std::thread([this]() { socketListener(); });
|
||||
socketOwnerPid_ = getpid();
|
||||
}
|
||||
|
||||
IPC::~IPC() {
|
||||
// Do no stop Hyprland IPC if a child process (with successful fork() but
|
||||
// failed exec()) exits.
|
||||
if (getpid() != socketOwnerPid_) return;
|
||||
|
||||
running_ = false;
|
||||
spdlog::info("Hyprland IPC stopping...");
|
||||
if (socketfd_ != -1) {
|
||||
|
@ -104,8 +104,25 @@ void Workspace::initializeWindowMap(const Json::Value &clients_data) {
|
||||
}
|
||||
|
||||
void Workspace::setActiveWindow(WindowAddress const &addr) {
|
||||
for (auto &window : m_windowMap) {
|
||||
window.setActive(window.address == addr);
|
||||
std::optional<long> activeIdx;
|
||||
for (size_t i = 0; i < m_windowMap.size(); ++i) {
|
||||
auto &window = m_windowMap[i];
|
||||
bool isActive = (window.address == addr);
|
||||
window.setActive(isActive);
|
||||
if (isActive) {
|
||||
activeIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
auto activeWindowPos = m_workspaceManager.activeWindowPosition();
|
||||
if (activeIdx.has_value() && activeWindowPos != Workspaces::ActiveWindowPosition::NONE) {
|
||||
auto window = std::move(m_windowMap[*activeIdx]);
|
||||
m_windowMap.erase(m_windowMap.begin() + *activeIdx);
|
||||
if (activeWindowPos == Workspaces::ActiveWindowPosition::FIRST) {
|
||||
m_windowMap.insert(m_windowMap.begin(), std::move(window));
|
||||
} else if (activeWindowPos == Workspaces::ActiveWindowPosition::LAST) {
|
||||
m_windowMap.emplace_back(std::move(window));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +268,17 @@ void Workspace::update(const std::string &workspace_icon) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Workspace::isEmpty() const {
|
||||
auto ignore_list = m_workspaceManager.getIgnoredWindows();
|
||||
if (ignore_list.empty()) {
|
||||
return m_windows == 0;
|
||||
}
|
||||
// If there are windows but they are all ignored, consider the workspace empty
|
||||
return std::all_of(
|
||||
m_windowMap.begin(), m_windowMap.end(),
|
||||
[this, &ignore_list](const auto &window_repr) { return shouldSkipWindow(window_repr); });
|
||||
}
|
||||
|
||||
void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
for (auto child : m_content.get_children()) {
|
||||
if (child != &m_labelBefore) {
|
||||
@ -259,9 +287,9 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
}
|
||||
|
||||
bool isFirst = true;
|
||||
for (const auto &window_repr : m_windowMap) {
|
||||
auto processWindow = [&](const WindowRepr &window_repr) {
|
||||
if (shouldSkipWindow(window_repr)) {
|
||||
continue;
|
||||
return; // skip
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
@ -270,6 +298,7 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
m_content.pack_start(*windowSeparator, false, false);
|
||||
windowSeparator->show();
|
||||
}
|
||||
|
||||
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
||||
window_box->set_tooltip_text(window_repr.window_title);
|
||||
window_box->get_style_context()->add_class("taskbar-window");
|
||||
@ -307,6 +336,16 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
|
||||
|
||||
m_content.pack_start(*event_box, true, false);
|
||||
event_box->show_all();
|
||||
};
|
||||
|
||||
if (m_workspaceManager.taskbarReverseDirection()) {
|
||||
for (auto it = m_windowMap.rbegin(); it != m_windowMap.rend(); ++it) {
|
||||
processWindow(*it);
|
||||
}
|
||||
} else {
|
||||
for (const auto &window_repr : m_windowMap) {
|
||||
processWindow(window_repr);
|
||||
}
|
||||
}
|
||||
|
||||
auto formatAfter = m_workspaceManager.formatAfter();
|
||||
|
@ -727,6 +727,7 @@ auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> vo
|
||||
|
||||
populateBoolConfig(workspaceTaskbar, "enable", m_enableTaskbar);
|
||||
populateBoolConfig(workspaceTaskbar, "update-active-window", m_updateActiveWindow);
|
||||
populateBoolConfig(workspaceTaskbar, "reverse-direction", m_taskbarReverseDirection);
|
||||
|
||||
if (workspaceTaskbar["format"].isString()) {
|
||||
/* The user defined a format string, use it */
|
||||
@ -774,6 +775,18 @@ auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> vo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (workspaceTaskbar["active-window-position"].isString()) {
|
||||
auto posStr = workspaceTaskbar["active-window-position"].asString();
|
||||
try {
|
||||
m_activeWindowPosition =
|
||||
m_activeWindowEnumParser.parseStringToEnum(posStr, m_activeWindowPositionMap);
|
||||
} catch (const std::invalid_argument &e) {
|
||||
spdlog::warn(
|
||||
"Invalid string representation for active-window-position. Falling back to 'none'.");
|
||||
m_activeWindowPosition = ActiveWindowPosition::NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
||||
@ -1128,9 +1141,9 @@ std::optional<int> Workspaces::parseWorkspaceId(std::string const &workspaceIdSt
|
||||
try {
|
||||
return workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr);
|
||||
} catch (std::exception const &e) {
|
||||
spdlog::error("Failed to parse workspace ID: {}", e.what());
|
||||
spdlog::debug("Workspace \"{}\" is not bound to an id: {}", workspaceIdStr, e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
@ -14,14 +14,20 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
|
||||
size_ = config["size"].asInt();
|
||||
|
||||
interval_ = config_["interval"].asInt();
|
||||
interval_ = config_["interval"] == "once"
|
||||
? std::chrono::milliseconds::max()
|
||||
: std::chrono::milliseconds(std::max(
|
||||
1L, // Minimum 1ms due to millisecond precision
|
||||
static_cast<long>(
|
||||
(config_["interval"].isNumeric() ? config_["interval"].asDouble() : 0) *
|
||||
1000)));
|
||||
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
|
||||
if (interval_ == 0) {
|
||||
interval_ = INT_MAX;
|
||||
if (interval_.count() == 0) {
|
||||
interval_ = std::chrono::milliseconds::max();
|
||||
}
|
||||
|
||||
delayWorker();
|
||||
@ -30,8 +36,7 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
void waybar::modules::Image::delayWorker() {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
auto interval = std::chrono::seconds(interval_);
|
||||
thread_.sleep_for(interval);
|
||||
thread_.sleep_for(interval_);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -333,18 +333,23 @@ auto waybar::modules::Network::update() -> void {
|
||||
fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_),
|
||||
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
|
||||
fmt::arg("icon", getIcon(signal_strength_, state_)),
|
||||
fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
|
||||
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
|
||||
fmt::arg("bandwidthTotalBits",
|
||||
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")),
|
||||
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
|
||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
|
||||
fmt::arg("bandwidthDownBits",
|
||||
pow_format(bandwidth_down * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||
fmt::arg("bandwidthUpBits",
|
||||
pow_format(bandwidth_up * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||
fmt::arg(
|
||||
"bandwidthTotalBits",
|
||||
pow_format((bandwidth_up + bandwidth_down) * 8ull / (interval_.count() / 1000.0), "b/s")),
|
||||
fmt::arg("bandwidthDownOctets",
|
||||
pow_format(bandwidth_down / (interval_.count() / 1000.0), "o/s")),
|
||||
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / (interval_.count() / 1000.0), "o/s")),
|
||||
fmt::arg("bandwidthTotalOctets",
|
||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")),
|
||||
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
|
||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
||||
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "o/s")),
|
||||
fmt::arg("bandwidthDownBytes",
|
||||
pow_format(bandwidth_down / (interval_.count() / 1000.0), "B/s")),
|
||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / (interval_.count() / 1000.0), "B/s")),
|
||||
fmt::arg("bandwidthTotalBytes",
|
||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
|
||||
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "B/s")));
|
||||
if (text.compare(label_.get_label()) != 0) {
|
||||
label_.set_markup(text);
|
||||
if (text.empty()) {
|
||||
|
@ -58,6 +58,16 @@ void Language::doUpdate() {
|
||||
spdlog::debug("niri language update with short description {}", layout.short_description);
|
||||
spdlog::debug("niri language update with variant {}", layout.variant);
|
||||
|
||||
if (!last_short_name_.empty()) {
|
||||
label_.get_style_context()->remove_class(last_short_name_);
|
||||
}
|
||||
if (!layout.short_name.empty()) {
|
||||
label_.get_style_context()->add_class(layout.short_name);
|
||||
last_short_name_ = layout.short_name;
|
||||
} else {
|
||||
last_short_name_.clear();
|
||||
}
|
||||
|
||||
std::string layoutName = std::string{};
|
||||
if (config_.isMember("format-" + layout.short_description + "-" + layout.variant)) {
|
||||
const auto propName = "format-" + layout.short_description + "-" + layout.variant;
|
||||
|
@ -150,11 +150,7 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
|
||||
button.show();
|
||||
}
|
||||
|
||||
struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
|
||||
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
|
||||
|
||||
zriver_status_manager_v1_destroy(status_manager_);
|
||||
box_.signal_show().connect(sigc::mem_fun(*this, &Tags::handle_show));
|
||||
}
|
||||
|
||||
Tags::~Tags() {
|
||||
@ -165,6 +161,19 @@ Tags::~Tags() {
|
||||
if (control_) {
|
||||
zriver_control_v1_destroy(control_);
|
||||
}
|
||||
|
||||
if (status_manager_) {
|
||||
zriver_status_manager_v1_destroy(status_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
void Tags::handle_show() {
|
||||
struct wl_output *output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
|
||||
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
|
||||
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
|
||||
|
||||
zriver_status_manager_v1_destroy(status_manager_);
|
||||
status_manager_ = nullptr;
|
||||
}
|
||||
|
||||
void Tags::handle_primary_clicked(uint32_t tag) {
|
||||
|
@ -74,6 +74,13 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
|
||||
cancellable_, interface);
|
||||
}
|
||||
|
||||
Item::~Item() {
|
||||
if (this->gtk_menu != nullptr) {
|
||||
this->gtk_menu->popdown();
|
||||
this->gtk_menu->detach();
|
||||
}
|
||||
}
|
||||
|
||||
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
|
||||
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
return false;
|
||||
@ -443,6 +450,9 @@ void Item::makeMenu() {
|
||||
gtk_menu->attach_to_widget(event_box);
|
||||
}
|
||||
}
|
||||
// Manually reset prelight to make sure the tray item doesn't stay in a hover state even though
|
||||
// the menu is focused
|
||||
event_box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
|
||||
}
|
||||
|
||||
bool Item::handleClick(GdkEventButton* const& ev) {
|
||||
|
@ -74,12 +74,14 @@ auto waybar::modules::Temperature::update() -> void {
|
||||
if (critical) {
|
||||
format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
|
||||
label_.get_style_context()->add_class("critical");
|
||||
} else if (warning) {
|
||||
format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format;
|
||||
label_.get_style_context()->add_class("warning");
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("critical");
|
||||
label_.get_style_context()->remove_class("warning");
|
||||
if (warning) {
|
||||
format = config_["format-warning"].isString() ? config_["format-warning"].asString() : format;
|
||||
label_.get_style_context()->add_class("warning");
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("warning");
|
||||
}
|
||||
}
|
||||
|
||||
if (format.empty()) {
|
||||
|
@ -41,6 +41,7 @@ EnumType EnumParser<EnumType>::parseStringToEnum(const std::string& str,
|
||||
// Explicit instantiations for specific EnumType types you intend to use
|
||||
// Add explicit instantiations for all relevant EnumType types
|
||||
template struct EnumParser<modules::hyprland::Workspaces::SortMethod>;
|
||||
template struct EnumParser<modules::hyprland::Workspaces::ActiveWindowPosition>;
|
||||
template struct EnumParser<util::KillSignalAction>;
|
||||
|
||||
} // namespace waybar::util
|
||||
|
Reference in New Issue
Block a user