Merge branch 'master' into fix/zjeffer/thread-sanitizer-warning

This commit is contained in:
Alexis Rouillard
2025-10-05 10:51:47 +02:00
committed by GitHub
61 changed files with 875 additions and 453 deletions

View File

@ -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";

View File

@ -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) {

View File

@ -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()) {

View File

@ -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

View File

@ -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));

View File

@ -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());
}

View File

@ -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

View File

@ -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
View 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");
}
}

View 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_;
}

View File

@ -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)};

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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();

View File

@ -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

View File

@ -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_);
};
}

View File

@ -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()) {

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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()) {

View File

@ -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