diff --git a/include/client.hpp b/include/client.hpp index f2eafb15..5965f7cd 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -19,6 +19,7 @@ class Client { public: static Client *inst(); int main(int argc, char *argv[]); + void reset(); Glib::RefPtr gtk_app; Glib::RefPtr gdk_display; diff --git a/include/factory.hpp b/include/factory.hpp index 853e6b09..1cae68c9 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,7 +1,11 @@ #pragma once #include +#ifdef HAVE_LIBDATE #include "modules/clock.hpp" +#else +#include "modules/simpleclock.hpp" +#endif #ifdef HAVE_SWAY #include "modules/sway/mode.hpp" #include "modules/sway/window.hpp" diff --git a/include/modules/simpleclock.hpp b/include/modules/simpleclock.hpp new file mode 100644 index 00000000..aa9a0a22 --- /dev/null +++ b/include/modules/simpleclock.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class Clock : public ALabel { + public: + Clock(const std::string&, const Json::Value&); + ~Clock() = default; + auto update() -> void; + + private: + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/man/waybar-river-tags.5.scd b/man/waybar-river-tags.5.scd index a02ddeb3..0f027249 100644 --- a/man/waybar-river-tags.5.scd +++ b/man/waybar-river-tags.5.scd @@ -17,6 +17,10 @@ Addressed by *river/tags* default: 9 ++ The number of tags that should be displayed. +*tag-labels*: ++ + typeof: array ++ + The label to display for each tag. + # EXAMPLE ``` diff --git a/man/waybar-temperature.5.scd b/man/waybar-temperature.5.scd index 86497367..7810a59b 100644 --- a/man/waybar-temperature.5.scd +++ b/man/waybar-temperature.5.scd @@ -50,6 +50,11 @@ Addressed by *temperature* typeof: array ++ Based on the current temperature (Celsius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*. +*tooltip-format*: ++ + typeof: string ++ + default: {temperatureC}°C ++ + The format for the tooltip + *rotate*: ++ typeof: integer ++ Positive value to rotate the text label. diff --git a/meson.build b/meson.build index 839c1dc4..49e71f26 100644 --- a/meson.build +++ b/meson.build @@ -112,7 +112,11 @@ gtk_layer_shell = dependency('gtk-layer-shell-0', required: get_option('gtk-layer-shell'), fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep']) systemd = dependency('systemd', required: get_option('systemd')) -tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], modules : [ 'date::date', 'date::date-tz' ], fallback: [ 'date', 'tz_dep' ]) +tz_dep = dependency('date', + required: false, + default_options : [ 'use_system_tzdb=true' ], + modules : [ 'date::date', 'date::date-tz' ], + fallback: [ 'date', 'tz_dep' ]) prefix = get_option('prefix') sysconfdir = get_option('sysconfdir') @@ -136,7 +140,6 @@ src_files = files( 'src/factory.cpp', 'src/AModule.cpp', 'src/ALabel.cpp', - 'src/modules/clock.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', @@ -236,6 +239,13 @@ if get_option('rfkill').enabled() endif endif +if tz_dep.found() + add_project_arguments('-DHAVE_LIBDATE', language: 'cpp') + src_files += 'src/modules/clock.cpp' +else + src_files += 'src/modules/simpleclock.cpp' +endif + subdir('protocol') executable( diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 3a4063d9..9371a0e7 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -60,14 +60,14 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_ std::string ALabel::getIcon(uint16_t percentage, std::vector& alts, uint16_t max) { auto format_icons = config_["format-icons"]; if (format_icons.isObject()) { + std::string _alt = "default"; for (const auto& alt : alts) { if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) { - format_icons = format_icons[alt]; + _alt = alt; break; - } else { - format_icons = format_icons["default"]; } } + format_icons = format_icons[_alt]; } if (format_icons.isArray()) { auto size = format_icons.size(); diff --git a/src/client.cpp b/src/client.cpp index ad281d59..24984a47 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -303,10 +303,9 @@ int waybar::Client::main(int argc, char *argv[]) { gtk_app->hold(); gtk_app->run(); bars.clear(); - zxdg_output_manager_v1_destroy(xdg_output_manager); - zwlr_layer_shell_v1_destroy(layer_shell); - zwp_idle_inhibit_manager_v1_destroy(idle_inhibit_manager); - wl_registry_destroy(registry); - wl_display_disconnect(wl_display); return 0; } + +void waybar::Client::reset() { + gtk_app->quit(); +} diff --git a/src/main.cpp b/src/main.cpp index 19a8de1e..13a25670 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ std::mutex reap_mtx; std::list reap; +volatile bool reload; void* signalThread(void* args) { int err, signum; @@ -70,12 +71,19 @@ void startSignalThread(void) { int main(int argc, char* argv[]) { try { auto client = waybar::Client::inst(); + std::signal(SIGUSR1, [](int /*signal*/) { for (auto& bar : waybar::Client::inst()->bars) { bar->toggle(); } }); + std::signal(SIGUSR2, [](int /*signal*/) { + spdlog::info("Reloading..."); + reload = true; + waybar::Client::inst()->reset(); + }); + for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { std::signal(sig, [](int sig) { for (auto& bar : waybar::Client::inst()->bars) { @@ -85,7 +93,12 @@ int main(int argc, char* argv[]) { } startSignalThread(); - auto ret = client->main(argc, argv); + auto ret = 0; + do { + reload = false; + ret = client->main(argc, argv); + } while (reload); + delete client; return ret; } catch (const std::exception& e) { diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index f2204cde..e86d10a0 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -15,8 +15,19 @@ auto waybar::modules::Cpu::update() -> void { if (tooltipEnabled()) { label_.set_tooltip_text(tooltip); } - label_.set_markup(fmt::format(format_, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage))); - getState(cpu_usage); + auto format = format_; + auto state = getState(cpu_usage); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format(format, fmt::arg("load", cpu_load), fmt::arg("usage", cpu_usage))); + } + // Call parent update ALabel::update(); } diff --git a/src/modules/disk.cpp b/src/modules/disk.cpp index 83d612b1..e63db475 100644 --- a/src/modules/disk.cpp +++ b/src/modules/disk.cpp @@ -49,15 +49,27 @@ auto waybar::modules::Disk::update() -> void { auto total = pow_format(stats.f_blocks * stats.f_frsize, "B", true); auto percentage_used = (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks; - label_.set_markup(fmt::format(format_ - , stats.f_bavail * 100 / stats.f_blocks - , fmt::arg("free", free) - , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) - , fmt::arg("used", used) - , fmt::arg("percentage_used", percentage_used) - , fmt::arg("total", total) - , fmt::arg("path", path_) - )); + auto format = format_; + auto state = getState(percentage_used); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format(format + , stats.f_bavail * 100 / stats.f_blocks + , fmt::arg("free", free) + , fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks) + , fmt::arg("used", used) + , fmt::arg("percentage_used", percentage_used) + , fmt::arg("total", total) + , fmt::arg("path", path_) + )); + } + if (tooltipEnabled()) { std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)"; if (config_["tooltip-format"].isString()) { @@ -73,8 +85,6 @@ auto waybar::modules::Disk::update() -> void { , fmt::arg("path", path_) )); } - event_box_.show(); - getState(percentage_used); // Call parent update ALabel::update(); } diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index a332d58c..09ce8e83 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -28,13 +28,24 @@ auto waybar::modules::Memory::update() -> void { auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2); auto available_ram_gigabytes = memfree / std::pow(1024, 2); - getState(used_ram_percentage); - label_.set_markup(fmt::format(format_, - used_ram_percentage, - fmt::arg("total", total_ram_gigabytes), - fmt::arg("percentage", used_ram_percentage), - fmt::arg("used", used_ram_gigabytes), - fmt::arg("avail", available_ram_gigabytes))); + auto format = format_; + auto state = getState(used_ram_percentage); + if (!state.empty() && config_["format-" + state].isString()) { + format = config_["format-" + state].asString(); + } + + if (format.empty()) { + event_box_.hide(); + } else { + event_box_.show(); + label_.set_markup(fmt::format(format, + used_ram_percentage, + fmt::arg("total", total_ram_gigabytes), + fmt::arg("percentage", used_ram_percentage), + fmt::arg("used", used_ram_gigabytes), + fmt::arg("avail", available_ram_gigabytes))); + } + if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { auto tooltip_format = config_["tooltip-format"].asString(); @@ -48,7 +59,6 @@ auto waybar::modules::Memory::update() -> void { label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); } } - event_box_.show(); } else { event_box_.hide(); } diff --git a/src/modules/river/tags.cpp b/src/modules/river/tags.cpp index 804ea090..e96b2016 100644 --- a/src/modules/river/tags.cpp +++ b/src/modules/river/tags.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "client.hpp" #include "modules/river/tags.hpp" #include "river-status-unstable-v1-client-protocol.h" @@ -64,8 +66,20 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con // Default to 9 tags const uint32_t num_tags = config["num-tags"].isUInt() ? config_["num-tags"].asUInt() : 9; - for (uint32_t tag = 1; tag <= num_tags; ++tag) { - Gtk::Button &button = buttons_.emplace_back(std::to_string(tag)); + + std::vector tag_labels(num_tags); + for (uint32_t tag = 0; tag < num_tags; ++tag) { + tag_labels[tag] = std::to_string(tag+1); + } + const Json::Value custom_labels = config["tag-labels"]; + if (custom_labels.isArray() && !custom_labels.empty()) { + for (uint32_t tag = 0; tag < std::min(num_tags, custom_labels.size()); ++tag) { + tag_labels[tag] = custom_labels[tag].asString(); + } + } + + for (const auto &tag_label : tag_labels) { + Gtk::Button &button = buttons_.emplace_back(tag_label); button.set_relief(Gtk::RELIEF_NONE); box_.pack_start(button, false, false, 0); button.show(); diff --git a/src/modules/simpleclock.cpp b/src/modules/simpleclock.cpp new file mode 100644 index 00000000..3004fc2c --- /dev/null +++ b/src/modules/simpleclock.cpp @@ -0,0 +1,33 @@ +#include "modules/simpleclock.hpp" +#include + +waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) + : ALabel(config, "clock", id, "{:%H:%M}", 60) { + thread_ = [this] { + dp.emit(); + auto now = std::chrono::system_clock::now(); + auto timeout = std::chrono::floor(now + interval_); + auto diff = std::chrono::seconds(timeout.time_since_epoch().count() % interval_.count()); + thread_.sleep_until(timeout - diff); + }; +} + +auto waybar::modules::Clock::update() -> void { + tzset(); // Update timezone information + auto now = std::chrono::system_clock::now(); + auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); + auto text = fmt::format(format_, localtime); + label_.set_markup(text); + + if (tooltipEnabled()) { + if (config_["tooltip-format"].isString()) { + auto tooltip_format = config_["tooltip-format"].asString(); + auto tooltip_text = fmt::format(tooltip_format, localtime); + label_.set_tooltip_text(tooltip_text); + } else { + label_.set_tooltip_text(text); + } + } + // Call parent update + ALabel::update(); +} diff --git a/src/modules/temperature.cpp b/src/modules/temperature.cpp index dc6b2d78..84560e8d 100644 --- a/src/modules/temperature.cpp +++ b/src/modules/temperature.cpp @@ -40,6 +40,16 @@ auto waybar::modules::Temperature::update() -> void { fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k), fmt::arg("icon", getIcon(temperature_c, "", max_temp)))); + if (tooltipEnabled()) { + std::string tooltip_format = "{temperatureC}°C"; + if (config_["tooltip-format"].isString()) { + tooltip_format = config_["tooltip-format"].asString(); + } + label_.set_tooltip_text(fmt::format(tooltip_format, + fmt::arg("temperatureC", temperature_c), + fmt::arg("temperatureF", temperature_f), + fmt::arg("temperatureK", temperature_k))); + } // Call parent update ALabel::update(); } diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index e70ad9c3..4cbb8ce6 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -367,16 +367,16 @@ void Task::handle_output_leave(struct wl_output *output) void Task::handle_state(struct wl_array *state) { state_ = 0; - for (auto* entry = static_cast(state->data); - entry < static_cast(state->data) + state->size; - entry++) { - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) + size_t size = state->size / sizeof(uint32_t); + for (size_t i = 0; i < size; ++i) { + auto entry = static_cast(state->data)[i]; + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) state_ |= MAXIMIZED; - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) state_ |= MINIMIZED; - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) state_ |= ACTIVE; - if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) + if (entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) state_ |= FULLSCREEN; } } @@ -637,8 +637,20 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu Taskbar::~Taskbar() { if (manager_) { - zwlr_foreign_toplevel_manager_v1_destroy(manager_); - manager_ = nullptr; + struct wl_display *display = Client::inst()->wl_display; + /* + * Send `stop` request and wait for one roundtrip. + * This is not quite correct as the protocol encourages us to wait for the .finished event, + * but it should work with wlroots foreign toplevel manager implementation. + */ + zwlr_foreign_toplevel_manager_v1_stop(manager_); + wl_display_roundtrip(display); + + if (manager_) { + spdlog::warn("Foreign toplevel manager destroyed before .finished event"); + zwlr_foreign_toplevel_manager_v1_destroy(manager_); + manager_ = nullptr; + } } }