From 61783aafaa351d39074593ec5d3eb2603024b3d2 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 17 Aug 2021 04:31:17 +0300 Subject: [PATCH 01/66] save --- include/modules/wlr/taskbar.hpp | 4 +++ src/modules/wlr/taskbar.cpp | 61 ++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 891ad55..6ba30ec 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -3,6 +3,7 @@ #include "AModule.hpp" #include "bar.hpp" #include "client.hpp" +#include "giomm/desktopappinfo.h" #include "util/json.hpp" #include @@ -61,6 +62,7 @@ class Task Gtk::Image icon_; Gtk::Label text_before_; Gtk::Label text_after_; + Glib::RefPtr app_info_; bool button_visible_; bool ignored_; @@ -70,6 +72,7 @@ class Task std::string format_tooltip_; + std::string name_; std::string title_; std::string app_id_; uint32_t state_ = 0; @@ -77,6 +80,7 @@ class Task private: std::string repr() const; std::string state_string(bool = false) const; + void set_desktop_app_info(const std::string &app_id); public: /* Getter functions */ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 932a95e..12b7c84 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -86,8 +86,7 @@ static Glib::RefPtr load_icon_from_file(std::string icon_path, int } } -/* Method 1 - get the correct icon name from the desktop file */ -static std::string get_from_desktop_app_info(const std::string &app_id) +static Glib::RefPtr get_app_info_by_name(const std::string& app_id) { static std::vector prefixes = search_prefix(); @@ -103,33 +102,30 @@ static std::string get_from_desktop_app_info(const std::string &app_id) ".desktop" }; - Glib::RefPtr app_info; + for (auto& prefix : prefixes) { + for (auto& folder : app_folders) { + for (auto& suffix : suffixes) { + auto app_info_ = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix); + if (!app_info_) { + continue; + } - for (auto& prefix : prefixes) - for (auto& folder : app_folders) - for (auto& suffix : suffixes) - if (!app_info) - app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix); + return app_info_; + } + } + } - if (app_info && app_info->get_icon()) - return app_info->get_icon()->to_string(); - - return ""; + return {}; } -/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ -static std::string get_from_icon_theme(const Glib::RefPtr& icon_theme, - const std::string &app_id) +void Task::set_desktop_app_info(const std::string &app_id) { - if (icon_theme->lookup_icon(app_id, 24)) - return app_id; + auto app_info = get_app_info_by_name(app_id); + if (app_info) { + app_info_ = app_info; + return; + } - return ""; -} - -/* Method 3 - as last resort perform a search for most appropriate desktop info file */ -static std::string get_from_desktop_app_info_search(const std::string &app_id) -{ std::string desktop_file = ""; gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str()); @@ -137,6 +133,7 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id) for (size_t i=0; desktop_list[0][i]; i++) { if (desktop_file == "") { desktop_file = desktop_list[0][i]; + // TODO: debug. Possible error } else { auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); auto startup_class = tmp_info->get_startup_wm_class(); @@ -151,7 +148,18 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id) } g_free(desktop_list); - return get_from_desktop_app_info(desktop_file); + app_info = get_app_info_by_name(desktop_file); + app_info_ = app_info; +} + +/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ +static std::string get_from_icon_theme(const Glib::RefPtr& icon_theme, + const std::string &app_id) +{ + if (icon_theme->lookup_icon(app_id, 24)) + return app_id; + + return ""; } static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, @@ -560,14 +568,17 @@ void Task::update() { bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false; std::string title = title_; + std::string name = name_; std::string app_id = app_id_; if (markup) { title = Glib::Markup::escape_text(title); + name = Glib::Markup::escape_text(name); app_id = Glib::Markup::escape_text(app_id); } if (!format_before_.empty()) { auto txt = fmt::format(format_before_, fmt::arg("title", title), + fmt::arg("name", name), fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) @@ -581,6 +592,7 @@ void Task::update() if (!format_after_.empty()) { auto txt = fmt::format(format_after_, fmt::arg("title", title), + fmt::arg("name", name), fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) @@ -595,6 +607,7 @@ void Task::update() if (!format_tooltip_.empty()) { auto txt = fmt::format(format_tooltip_, fmt::arg("title", title), + fmt::arg("name", name), fmt::arg("app_id", app_id), fmt::arg("state", state_string()), fmt::arg("short_state", state_string(true)) From 38afa345dd1012f2ff9e66cbb06d16e3190635d3 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 22 Aug 2021 20:18:03 +0300 Subject: [PATCH 02/66] Finish --- include/modules/wlr/taskbar.hpp | 11 ++- man/waybar-wlr-taskbar.5.scd | 11 ++- src/modules/wlr/taskbar.cpp | 170 +++++++++++++++++++------------- 3 files changed, 120 insertions(+), 72 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 6ba30ec..5d9d93c 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -66,7 +67,8 @@ class Task bool button_visible_; bool ignored_; - bool with_icon_; + bool with_icon_ = false; + bool with_name_ = false; std::string format_before_; std::string format_after_; @@ -80,7 +82,8 @@ class Task private: std::string repr() const; std::string state_string(bool = false) const; - void set_desktop_app_info(const std::string &app_id); + void set_app_info_from_app_id_list(const std::string& app_id_list); + bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, Glib::RefPtr app_info, int size); public: /* Getter functions */ @@ -139,6 +142,7 @@ class Taskbar : public waybar::AModule std::vector> icon_themes_; std::unordered_set ignore_list_; + std::map app_ids_replace_map_; struct zwlr_foreign_toplevel_manager_v1 *manager_; struct wl_seat *seat_; @@ -161,8 +165,9 @@ class Taskbar : public waybar::AModule bool show_output(struct wl_output *) const; bool all_outputs() const; - std::vector> icon_themes() const; + const std::vector>& icon_themes() const; const std::unordered_set& ignore_list() const; + const std::map& app_ids_replace_map() const; }; } /* namespace waybar::modules::wlr */ diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 0e86238..ebd41fb 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -72,10 +72,16 @@ Addressed by *wlr/taskbar* typeof: array ++ List of app_id to be invisible. +*app_ids-mapping*: ++ + typeof: object ++ + Directory of app_id to be replaced with + # FORMAT REPLACEMENTS *{icon}*: The icon of the application. +*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id} + *{title}*: The title of the application. *{app_id}*: The app_id (== application name) of the application. @@ -105,7 +111,10 @@ Addressed by *wlr/taskbar* "on-click-middle": "close", "ignore-list": [ "Alacritty" - ] + ], + "app_ids-mapping": { + "firefoxdeveloperedition": "firefox-developer-edition" + } } ``` diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 12b7c84..06ac367 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -11,7 +11,9 @@ #include #include #include +#include +#include #include #include @@ -86,7 +88,7 @@ static Glib::RefPtr load_icon_from_file(std::string icon_path, int } } -static Glib::RefPtr get_app_info_by_name(const std::string& app_id) +static Glib::RefPtr get_app_info_by_name(const std::string& app_id) { static std::vector prefixes = search_prefix(); @@ -118,12 +120,11 @@ static Glib::RefPtr get_app_info_by_name(const std::string& app return {}; } -void Task::set_desktop_app_info(const std::string &app_id) +Glib::RefPtr get_desktop_app_info(const std::string &app_id) { auto app_info = get_app_info_by_name(app_id); if (app_info) { - app_info_ = app_info; - return; + return app_info; } std::string desktop_file = ""; @@ -133,7 +134,6 @@ void Task::set_desktop_app_info(const std::string &app_id) for (size_t i=0; desktop_list[0][i]; i++) { if (desktop_file == "") { desktop_file = desktop_list[0][i]; - // TODO: debug. Possible error } else { auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); auto startup_class = tmp_info->get_startup_wm_class(); @@ -148,12 +148,45 @@ void Task::set_desktop_app_info(const std::string &app_id) } g_free(desktop_list); - app_info = get_app_info_by_name(desktop_file); - app_info_ = app_info; + return get_app_info_by_name(desktop_file); } -/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ -static std::string get_from_icon_theme(const Glib::RefPtr& icon_theme, +void Task::set_app_info_from_app_id_list(const std::string& app_id_list) { + std::string app_id; + std::istringstream stream(app_id_list); + + /* Wayfire sends a list of app-id's in space separated format, other compositors + * send a single app-id, but in any case this works fine */ + while (stream >> app_id) + { + app_info_ = get_desktop_app_info(app_id); + if (app_info_) { + return; + } + + auto lower_app_id = app_id; + std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), + [](char c){ return std::tolower(c); }); + app_info_ = get_desktop_app_info(lower_app_id); + if (app_info_) { + return; + } + + size_t start = 0, end = app_id.size(); + start = app_id.rfind(".", end); + std::string app_name = app_id.substr(start+1, app_id.size()); + app_info_ = get_desktop_app_info(app_name); + if (app_info_) { + return; + } + + start = app_id.find("-"); + app_name = app_id.substr(0, start); + app_info_ = get_desktop_app_info(app_name); + } +} + +static std::string get_icon_name_from_icon_theme(const Glib::RefPtr& icon_theme, const std::string &app_id) { if (icon_theme->lookup_icon(app_id, 24)) @@ -162,62 +195,37 @@ static std::string get_from_icon_theme(const Glib::RefPtr& icon_ return ""; } -static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, - const std::string &app_id_list, int size) +bool Task::image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, Glib::RefPtr app_info, int size) { - std::string app_id; - std::istringstream stream(app_id_list); - bool found = false; + std::string ret_icon_name = "unknown"; + if (app_info) { + std::string icon_name = get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class()); + if (!icon_name.empty()) { + ret_icon_name = icon_name; + } else { + if (app_info->get_icon()) { + ret_icon_name = app_info->get_icon()->to_string(); + } + } + } - /* Wayfire sends a list of app-id's in space separated format, other compositors - * send a single app-id, but in any case this works fine */ - while (stream >> app_id) - { - size_t start = 0, end = app_id.size(); - start = app_id.rfind(".", end); - std::string app_name = app_id.substr(start+1, app_id.size()); + Glib::RefPtr pixbuf; - auto lower_app_id = app_id; - std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), - [](char c){ return std::tolower(c); }); + try { + pixbuf = icon_theme->load_icon(ret_icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); + } catch(...) { + if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS)) + pixbuf = load_icon_from_file(ret_icon_name, size); + else + pixbuf = {}; + } - std::string icon_name = get_from_icon_theme(icon_theme, app_id); + if (pixbuf) { + image.set(pixbuf); + return true; + } - if (icon_name.empty()) - icon_name = get_from_icon_theme(icon_theme, lower_app_id); - if (icon_name.empty()) - icon_name = get_from_icon_theme(icon_theme, app_name); - if (icon_name.empty()) - icon_name = get_from_desktop_app_info(app_id); - if (icon_name.empty()) - icon_name = get_from_desktop_app_info(lower_app_id); - if (icon_name.empty()) - icon_name = get_from_desktop_app_info(app_name); - if (icon_name.empty()) - icon_name = get_from_desktop_app_info_search(app_id); - - if (icon_name.empty()) - icon_name = "unknown"; - - Glib::RefPtr pixbuf; - - try { - pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); - } catch(...) { - if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS)) - pixbuf = load_icon_from_file(icon_name, size); - else - pixbuf = {}; - } - - if (pixbuf) { - image.set(pixbuf); - found = true; - break; - } - } - - return found; + return false; } /* Task class implementation */ @@ -298,13 +306,15 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, content_.show(); button_.add(content_); - with_icon_ = false; format_before_.clear(); format_after_.clear(); if (config_["format"].isString()) { /* The user defined a format string, use it */ auto format = config_["format"].asString(); + if (format.find("{name}") != std::string::npos) { + with_name_ = true; + } auto icon_pos = format.find("{icon}"); if (icon_pos == 0) { @@ -406,13 +416,28 @@ void Task::handle_app_id(const char *app_id) } } - if (!with_icon_) + auto ids_replace_map = tbar_->app_ids_replace_map(); + if (ids_replace_map.count(app_id_)) { + auto replaced_id = ids_replace_map[app_id_]; + spdlog::debug(fmt::format("Task ({}) [{}] app_id was replaced with {}", id_, app_id_, replaced_id)); + app_id_ = replaced_id; + } + + if (!with_icon_ && !with_name_) { return; + } + + set_app_info_from_app_id_list(app_id_); + name_ = app_info_ ? app_info_->get_display_name() : app_id; + + if (!with_icon_) { + return; + } int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16; bool found = false; for (auto& icon_theme : tbar_->icon_themes()) { - if (image_load_icon(icon_, icon_theme, app_id_, icon_size)) { + if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) { found = true; break; } @@ -735,6 +760,15 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } } + // Load app_id remappings + if (config_["app_ids-mapping"].isObject()) { + const Json::Value& mapping = config_["app_ids-mapping"]; + const std::vector app_ids = config_["app_ids-mapping"].getMemberNames(); + for (auto& app_id : app_ids) { + app_ids_replace_map_.emplace(app_id, mapping[app_id].asString()); + } + } + icon_themes_.push_back(Gtk::IconTheme::get_default()); } @@ -866,10 +900,10 @@ bool Taskbar::all_outputs() const return config_["all-outputs"].isBool() && config_["all-outputs"].asBool(); } -std::vector> Taskbar::icon_themes() const -{ - return icon_themes_; -} -const std::unordered_set &Taskbar::ignore_list() const { return ignore_list_; } +const std::vector>& Taskbar::icon_themes() const { return icon_themes_; } + +const std::unordered_set& Taskbar::ignore_list() const { return ignore_list_; } + +const std::map& Taskbar::app_ids_replace_map() const { return app_ids_replace_map_; } } /* namespace waybar::modules::wlr */ From ddfe036f003d18684a4c6f96fbdc4e94e386b387 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 22 Aug 2021 20:42:16 +0300 Subject: [PATCH 03/66] Format --- include/modules/wlr/taskbar.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 5d9d93c..6680bbf 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -82,8 +82,8 @@ class Task private: std::string repr() const; std::string state_string(bool = false) const; - void set_app_info_from_app_id_list(const std::string& app_id_list); - bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, Glib::RefPtr app_info, int size); + void set_app_info_from_app_id_list(const std::string& app_id_list); + bool image_load_icon(Gtk::Image& image, const Glib::RefPtr& icon_theme, Glib::RefPtr app_info, int size); public: /* Getter functions */ @@ -142,7 +142,7 @@ class Taskbar : public waybar::AModule std::vector> icon_themes_; std::unordered_set ignore_list_; - std::map app_ids_replace_map_; + std::map app_ids_replace_map_; struct zwlr_foreign_toplevel_manager_v1 *manager_; struct wl_seat *seat_; From d60bb90b772a63933d5a9813bef926853d76054f Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 22 Aug 2021 20:43:37 +0300 Subject: [PATCH 04/66] Fix typo --- man/waybar-wlr-taskbar.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index ebd41fb..4ce43da 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -74,7 +74,7 @@ Addressed by *wlr/taskbar* *app_ids-mapping*: ++ typeof: object ++ - Directory of app_id to be replaced with + Dictionary of app_id to be replaced with # FORMAT REPLACEMENTS From 94e53c37776f0859feaaefbb197b4842cbe6900f Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 29 Aug 2021 16:34:29 +0300 Subject: [PATCH 05/66] Add swap flags --- src/modules/memory/common.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/modules/memory/common.cpp b/src/modules/memory/common.cpp index 31219ed..07a1caf 100644 --- a/src/modules/memory/common.cpp +++ b/src/modules/memory/common.cpp @@ -12,7 +12,15 @@ auto waybar::modules::Memory::update() -> void { parseMeminfo(); unsigned long memtotal = meminfo_["MemTotal"]; + unsigned long swaptotal = 0; + if (meminfo_.count("SwapTotal")) { + swaptotal = meminfo_["SwapTotal"]; + } unsigned long memfree; + unsigned long swapfree = 0; + if (meminfo_.count("SwapFree")) { + swapfree = meminfo_["SwapFree"]; + } if (meminfo_.count("MemAvailable")) { // New kernels (3.4+) have an accurate available memory field. memfree = meminfo_["MemAvailable"]; @@ -24,9 +32,16 @@ auto waybar::modules::Memory::update() -> void { if (memtotal > 0 && memfree >= 0) { auto total_ram_gigabytes = memtotal / std::pow(1024, 2); + auto total_swap_gigabytes = swaptotal / std::pow(1024, 2); int used_ram_percentage = 100 * (memtotal - memfree) / memtotal; + int used_swap_percentage = 0; + if (swaptotal && swapfree) { + used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal; + } auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2); + auto used_swap_gigabytes = (swaptotal - swapfree) / std::pow(1024, 2); auto available_ram_gigabytes = memfree / std::pow(1024, 2); + auto available_swap_gigabytes = swapfree / std::pow(1024, 2); auto format = format_; auto state = getState(used_ram_percentage); @@ -43,9 +58,13 @@ auto waybar::modules::Memory::update() -> void { used_ram_percentage, fmt::arg("icon", getIcon(used_ram_percentage, icons)), fmt::arg("total", total_ram_gigabytes), + fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("percentage", used_ram_percentage), + fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), - fmt::arg("avail", available_ram_gigabytes))); + fmt::arg("swapUsed", used_swap_gigabytes), + fmt::arg("avail", available_ram_gigabytes), + fmt::arg("swapAvail", available_swap_gigabytes))); } if (tooltipEnabled()) { @@ -54,9 +73,13 @@ auto waybar::modules::Memory::update() -> void { label_.set_tooltip_text(fmt::format(tooltip_format, used_ram_percentage, fmt::arg("total", total_ram_gigabytes), + fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("percentage", used_ram_percentage), + fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), - fmt::arg("avail", available_ram_gigabytes))); + fmt::arg("swapUsed", used_swap_gigabytes), + fmt::arg("avail", available_ram_gigabytes), + fmt::arg("swapAvail", available_swap_gigabytes))); } else { label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); } From 8d9e322507790785598ced6ea5346d069f1c5d52 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sun, 29 Aug 2021 16:37:02 +0300 Subject: [PATCH 06/66] Add man --- man/waybar-memory.5.scd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/man/waybar-memory.5.scd b/man/waybar-memory.5.scd index 0639c07..d960ffd 100644 --- a/man/waybar-memory.5.scd +++ b/man/waybar-memory.5.scd @@ -84,12 +84,20 @@ Addressed by *memory* *{percentage}*: Percentage of memory in use. +*{swapPercentage}*: Percentage of swap in use. + *{total}*: Amount of total memory available in GiB. +*{swapTotal}*: Amount of total swap available in GiB. + *{used}*: Amount of used memory in GiB. +*{swapUsed}*: Amount of used swap in GiB. + *{avail}*: Amount of available memory in GiB. +*{swapAvail}*: Amount of available swap in GiB. + # EXAMPLES ``` From 08b4a8333193097a3b119e38baa745bca5eaf771 Mon Sep 17 00:00:00 2001 From: Ashutosh Malviya Date: Fri, 15 Oct 2021 19:07:25 +0530 Subject: [PATCH 07/66] Add reverse scrolling config option for pulseaudio When natural scrolling is enabled, the behaviour of scrolling on pulseaudio module is reversed, this commit reverses the direction of scroll variable if "reverse-scrolling" is set to 1 in config file. --- src/modules/pulseaudio.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index cf42780..e5b855b 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -79,6 +79,13 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { if (dir == SCROLL_DIR::NONE) { return true; } + if (config_["reverse-scrolling"].asInt() == 1){ + if (dir == SCROLL_DIR::UP) { + dir = SCROLL_DIR::DOWN; + } else if (dir == SCROLL_DIR::DOWN) { + dir = SCROLL_DIR::UP; + } + } double volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_volume_t change = volume_tick; pa_cvolume pa_volume = pa_volume_; From 17bb5643aec1ec67d7a0c45bfc91ad806b842a75 Mon Sep 17 00:00:00 2001 From: mazunki Date: Tue, 9 Nov 2021 18:38:07 +0100 Subject: [PATCH 08/66] explicitly checking for errors to silence unused variable warnings when writing to fd --- include/util/command.hpp | 4 +++- src/modules/sway/ipc/client.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index 3a38da3..d3a1e43 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -68,7 +68,9 @@ inline int close(FILE* fp, pid_t pid) { inline FILE* open(const std::string& cmd, int& pid) { if (cmd == "") return nullptr; int fd[2]; - pipe(fd); + if (pipe(fd) != 0){ + throw std::runtime_error("Couldn't open a file descriptor"); + } pid_t child_pid = fork(); diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 58aed60..1111746 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -14,12 +14,16 @@ Ipc::~Ipc() { if (fd_ > 0) { // To fail the IPC header - write(fd_, "close-sway-ipc", 14); + if (write(fd_, "close-sway-ipc", 14) == -1) { + std::runtime_error("Couldn't close Sway IPC through fd"); + } close(fd_); fd_ = -1; } if (fd_event_ > 0) { - write(fd_event_, "close-sway-ipc", 14); + if (write(fd_event_, "close-sway-ipc", 14) == -1) { + std::runtime_error("Couldn't close Sway IPC through fd_event"); + } close(fd_event_); fd_event_ = -1; } From b0eab5d793e48af1fcfc4b281aab6bfbd10b769b Mon Sep 17 00:00:00 2001 From: mazunki Date: Tue, 9 Nov 2021 19:03:15 +0100 Subject: [PATCH 09/66] maybe we shouldn't actually runtime error, but still doing a check --- include/util/command.hpp | 2 +- src/modules/sway/ipc/client.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index d3a1e43..4920333 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -69,7 +69,7 @@ inline FILE* open(const std::string& cmd, int& pid) { if (cmd == "") return nullptr; int fd[2]; if (pipe(fd) != 0){ - throw std::runtime_error("Couldn't open a file descriptor"); + // std::runtime_error("Couldn't open a file descriptor"); } pid_t child_pid = fork(); diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 1111746..15bdf1e 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -15,14 +15,14 @@ Ipc::~Ipc() { if (fd_ > 0) { // To fail the IPC header if (write(fd_, "close-sway-ipc", 14) == -1) { - std::runtime_error("Couldn't close Sway IPC through fd"); + //std::runtime_error("Couldn't close Sway IPC through fd"); } close(fd_); fd_ = -1; } if (fd_event_ > 0) { if (write(fd_event_, "close-sway-ipc", 14) == -1) { - std::runtime_error("Couldn't close Sway IPC through fd_event"); + //std::runtime_error("Couldn't close Sway IPC through fd_event"); } close(fd_event_); fd_event_ = -1; From e10c9dd011e394b734be7bab6fc2ca6885f9de1f Mon Sep 17 00:00:00 2001 From: mazunki Date: Tue, 9 Nov 2021 19:04:05 +0100 Subject: [PATCH 10/66] changing want_route_dump to default to true, since we say we have gwaddr support --- src/modules/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 99ccd8e..d5f8012 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -88,7 +88,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET), efd_(-1), ev_fd_(-1), - want_route_dump_(false), + want_route_dump_(true), want_link_dump_(false), want_addr_dump_(false), dump_in_progress_(false), @@ -121,7 +121,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf } if (!config_["interface"].isString()) { - // "interface" isn't configure, then try to guess the external + // "interface" isn't configured, then try to guess the external // interface currently used for internet. want_route_dump_ = true; } else { From bb7b376fa6edc121890021ed191cdf497a5df66f Mon Sep 17 00:00:00 2001 From: mazunki Date: Wed, 24 Nov 2021 01:37:21 +0100 Subject: [PATCH 11/66] removed commented useless runtime errors --- include/util/command.hpp | 1 - src/modules/sway/ipc/client.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/util/command.hpp b/include/util/command.hpp index 4920333..9c4305a 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -69,7 +69,6 @@ inline FILE* open(const std::string& cmd, int& pid) { if (cmd == "") return nullptr; int fd[2]; if (pipe(fd) != 0){ - // std::runtime_error("Couldn't open a file descriptor"); } pid_t child_pid = fork(); diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index 15bdf1e..ea1a3c5 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -22,7 +22,6 @@ Ipc::~Ipc() { } if (fd_event_ > 0) { if (write(fd_event_, "close-sway-ipc", 14) == -1) { - //std::runtime_error("Couldn't close Sway IPC through fd_event"); } close(fd_event_); fd_event_ = -1; From 2240c79b1a79546b5e6126d4218ddbfaa8613ac5 Mon Sep 17 00:00:00 2001 From: Brent George Date: Wed, 1 Dec 2021 18:24:35 -0700 Subject: [PATCH 12/66] Adjust max wifi strength that is possible --- src/modules/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e7b20ab..b86989f 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -789,8 +789,8 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { // signalstrength in dBm from mBm signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100; - // WiFi-hardware usually operates in the range -90 to -20dBm. - const int hardwareMax = -20; + // WiFi-hardware usually operates in the range -90 to -30dBm. + const int hardwareMax = -30; const int hardwareMin = -90; const int strength = ((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100; From 5cbbd65ac4d64c60f80c994bad54e18d32da22e1 Mon Sep 17 00:00:00 2001 From: 187Qrly <56300140+187Qrly@users.noreply.github.com> Date: Fri, 3 Dec 2021 20:11:25 +0100 Subject: [PATCH 13/66] Add Gentoo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98b99a2..587a554 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Highly customizable Wayland bar for Sway and Wlroots based compositors.
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or -[AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)
+[AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)* #### Current features From f573e32d0b788867a783e9b013b94a4f3029d83a Mon Sep 17 00:00:00 2001 From: John Fredriksson <94405030+jfred9@users.noreply.github.com> Date: Fri, 3 Dec 2021 23:56:51 +0100 Subject: [PATCH 14/66] bar: Fix crash when unplugging HDMI There is a double delete situation which causes a SIGSEGV to happen during destruction of bar. This was introduced by the group feature patch. The same object pointer is stored in two different vectors of unique_ptr element. Replace with shared_ptr to handle reference counting correctly and avoid double delete. --- include/bar.hpp | 8 ++++---- src/bar.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 4aa17c1..01a9d03 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -112,14 +112,14 @@ class Bar { Gtk::Box center_; Gtk::Box right_; Gtk::Box box_; - std::vector> modules_left_; - std::vector> modules_center_; - std::vector> modules_right_; + std::vector> modules_left_; + std::vector> modules_center_; + std::vector> modules_right_; #ifdef HAVE_SWAY using BarIpcClient = modules::sway::BarIpcClient; std::unique_ptr _ipc_client; #endif - std::vector> modules_all_; + std::vector> modules_all_; }; } // namespace waybar diff --git a/src/bar.cpp b/src/bar.cpp index fbd4623..133c29a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -735,18 +735,19 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk module = factory.makeModule(ref); } - modules_all_.emplace_back(module); + std::shared_ptr module_sp(module); + modules_all_.emplace_back(module_sp); if (group) { group->pack_start(*module, false, false); } else { if (pos == "modules-left") { - modules_left_.emplace_back(module); + modules_left_.emplace_back(module_sp); } if (pos == "modules-center") { - modules_center_.emplace_back(module); + modules_center_.emplace_back(module_sp); } if (pos == "modules-right") { - modules_right_.emplace_back(module); + modules_right_.emplace_back(module_sp); } } module->dp.connect([module, &name] { From eae65099d0717a3dcf9d3e37dba52cf646d293fe Mon Sep 17 00:00:00 2001 From: Alexis Cellier Date: Fri, 3 Dec 2021 23:32:53 +0100 Subject: [PATCH 15/66] Add logind feature, with its 'inhibitor' module The logind feature adds a new inhibitor module which allows to acquire the inhibitor locks that logind presents. Signed-off-by: Alexis Cellier --- include/factory.hpp | 3 + include/modules/inhibitor.hpp | 27 ++++++ meson.build | 7 +- meson_options.txt | 1 + src/factory.cpp | 5 + src/modules/inhibitor.cpp | 175 ++++++++++++++++++++++++++++++++++ 6 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 include/modules/inhibitor.hpp create mode 100644 src/modules/inhibitor.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 43dd2cf..3855ce2 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -51,6 +51,9 @@ #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" #endif +#ifdef HAVE_GIO_UNIX +#include "modules/inhibitor.hpp" +#endif #include "bar.hpp" #include "modules/custom.hpp" #include "modules/temperature.hpp" diff --git a/include/modules/inhibitor.hpp b/include/modules/inhibitor.hpp new file mode 100644 index 0000000..aa2f97d --- /dev/null +++ b/include/modules/inhibitor.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +#include "ALabel.hpp" +#include "bar.hpp" + +namespace waybar::modules { + +class Inhibitor : public ALabel { + public: + Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&); + ~Inhibitor() override; + auto update() -> void; + auto activated() -> bool; + + private: + auto handleToggle(::GdkEventButton* const& e) -> bool; + + const std::unique_ptr<::GDBusConnection, void(*)(::GDBusConnection*)> dbus_; + const std::string inhibitors_; + int handle_ = -1; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index 7f2d956..5e62eec 100644 --- a/meson.build +++ b/meson.build @@ -86,7 +86,7 @@ wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) -giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk')) +giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled())) jsoncpp = dependency('jsoncpp') sigcpp = dependency('sigc++-2.0') libepoll = dependency('epoll-shim', required: false) @@ -242,6 +242,11 @@ if libsndio.found() src_files += 'src/modules/sndio.cpp' endif +if (giounix.found() and not get_option('logind').disabled()) + add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp') + src_files += 'src/modules/inhibitor.cpp' +endif + if get_option('rfkill').enabled() if is_linux add_project_arguments('-DWANT_RFKILL', language: 'cpp') diff --git a/meson_options.txt b/meson_options.txt index f4f60d0..230a53d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,5 +10,6 @@ option('mpd', type: 'feature', value: 'auto', description: 'Enable support for t option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') +option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind') option('tests', type: 'feature', value: 'auto', description: 'Enable tests') option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') diff --git a/src/factory.cpp b/src/factory.cpp index 351aa33..900653b 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -94,6 +94,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "sndio") { return new waybar::modules::Sndio(id, config_[name]); } +#endif +#ifdef HAVE_GIO_UNIX + if (ref == "inhibitor") { + return new waybar::modules::Inhibitor(id, bar_, config_[name]); + } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); diff --git a/src/modules/inhibitor.cpp b/src/modules/inhibitor.cpp new file mode 100644 index 0000000..1e3f2d3 --- /dev/null +++ b/src/modules/inhibitor.cpp @@ -0,0 +1,175 @@ +#include "modules/inhibitor.hpp" + +#include +#include +#include + +namespace { + +using DBus = std::unique_ptr; + +auto dbus() -> DBus { + GError *error = nullptr; + GDBusConnection* connection = + g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + + if (error) { + spdlog::error("g_bus_get_sync() failed: {}", error->message); + g_error_free(error); + connection = nullptr; + } + + auto destructor = [](GDBusConnection* connection) { + GError *error = nullptr; + g_dbus_connection_close_sync(connection, nullptr, &error); + if (error) { + spdlog::error( + "g_bus_connection_close_sync failed(): {}", + error->message); + g_error_free(error); + } + }; + + return DBus{connection, destructor}; +} + +auto getLocks(const DBus& bus, const std::string& inhibitors) -> int { + GError *error = nullptr; + GUnixFDList* fd_list; + int handle; + + auto reply = g_dbus_connection_call_with_unix_fd_list_sync(bus.get(), + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit", + g_variant_new( + "(ssss)", + inhibitors.c_str(), + "waybar", + "Asked by user", + "block"), + G_VARIANT_TYPE("(h)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &fd_list, + nullptr, + &error); + if (error) { + spdlog::error( + "g_dbus_connection_call_with_unix_fd_list_sync() failed: {}", + error->message); + g_error_free(error); + handle = -1; + } else { + gint index; + g_variant_get(reply, "(h)", &index); + g_variant_unref(reply); + handle = g_unix_fd_list_get(fd_list, index, nullptr); + g_object_unref(fd_list); + } + + return handle; +} + +auto checkInhibitor(const std::string& inhibitor) -> const std::string& { + static const auto inhibitors = std::array{ + "idle", + "shutdown", + "sleep", + "handle-power-key", + "handle-suspend-key", + "handle-hibernate-key", + "handle-lid-switch" + }; + + if (std::find(inhibitors.begin(), inhibitors.end(), inhibitor) + == inhibitors.end()) { + throw std::runtime_error("invalid logind inhibitor " + inhibitor); + } + + return inhibitor; +} + +auto getInhibitors(const Json::Value& config) -> std::string { + std::string inhibitors = "idle"; + + if (config["what"].empty()) { + return inhibitors; + } + + if (config["what"].isString()) { + return checkInhibitor(config["what"].asString()); + } + + if (config["what"].isArray()) { + inhibitors = checkInhibitor(config["what"][0].asString()); + for (decltype(config["what"].size()) i = 1; i < config["what"].size(); ++i) { + inhibitors += ":" + checkInhibitor(config["what"][i].asString()); + } + return inhibitors; + } + + return inhibitors; +} + +} + +namespace waybar::modules { + +Inhibitor::Inhibitor(const std::string& id, const Bar& bar, + const Json::Value& config) + : ALabel(config, "inhibitor", id, "{status}", true), + dbus_(::dbus()), + inhibitors_(::getInhibitors(config)) { + event_box_.add_events(Gdk::BUTTON_PRESS_MASK); + event_box_.signal_button_press_event().connect( + sigc::mem_fun(*this, &Inhibitor::handleToggle)); + dp.emit(); +} + +Inhibitor::~Inhibitor() { + if (handle_ != -1) { + ::close(handle_); + } +} + +auto Inhibitor::activated() -> bool { + return handle_ != -1; +} + +auto Inhibitor::update() -> void { + std::string status_text = activated() ? "activated" : "deactivated"; + + label_.get_style_context()->remove_class( + activated() ? "deactivated" : "activated"); + label_.set_markup( + fmt::format(format_, fmt::arg("status", status_text), + fmt::arg("icon", getIcon(0, status_text)))); + label_.get_style_context()->add_class(status_text); + + if (tooltipEnabled()) { + label_.set_tooltip_text(status_text); + } + + return ALabel::update(); +} + +auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool { + if (e->button == 1) { + if (activated()) { + ::close(handle_); + handle_ = -1; + } else { + handle_ = ::getLocks(dbus_, inhibitors_); + if (handle_ == -1) { + spdlog::error("cannot get inhibitor locks"); + } + } + } + + return ALabel::handleToggle(e); +} + +} // waybar::modules From 795246263f80b61b28925383eb36b00b8b8d79f0 Mon Sep 17 00:00:00 2001 From: Alexis Cellier Date: Tue, 14 Dec 2021 08:15:44 +0100 Subject: [PATCH 16/66] man: Add waybar-inhibitor man page --- man/waybar-inhibitor.5.scd | 92 ++++++++++++++++++++++++++++++++++++++ meson.build | 4 ++ 2 files changed, 96 insertions(+) create mode 100644 man/waybar-inhibitor.5.scd diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd new file mode 100644 index 0000000..0838f4d --- /dev/null +++ b/man/waybar-inhibitor.5.scd @@ -0,0 +1,92 @@ +waybar-inhibitor(5) + +# NAME + +waybar - inhibitor module + +# DESCRIPTION + +The *inhibitor* module allows to take an inhibitor lock that logind provides. +See *systemd-inhibit*(1) for more information. + +# CONFIGURATION + +*what*: ++ + typeof: string or array ++ + The inhibitor lock or locks that should be taken when active. The available inhibitor locks are *idle*, *shutdown*, *sleep*, *handle-power-key*, *handle-suspend-key*, *handle-hibernate-key* and *handle-lid-switch*. + +*format*: ++ + typeof: string ++ + The format, how the state should be displayed. + +*format-icons*: ++ + typeof: array ++ + Based on the current state, the corresponding icon gets selected. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. A click also toggles the state + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +# FORMAT REPLACEMENTS + +*{status}*: status (*activated* or *deactivated*) + +*{icon}*: Icon, as defined in *format-icons* + +# EXAMPLES + +``` +"inhibitor": { + "what": "handle-lid-switch", + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + } +} +``` diff --git a/meson.build b/meson.build index 5e62eec..cfe2d51 100644 --- a/meson.build +++ b/meson.build @@ -352,6 +352,10 @@ if scdoc.found() 'waybar-sndio.5.scd', ] + if (giounix.found() and not get_option('logind').disabled()) + man_files += 'waybar-inhibitor.5.scd' + endif + foreach file : man_files path = '@0@'.format(file) basename = path.split('/')[-1] From d1f8b42d229aa4a505ece5ef898c6f60749856e2 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 14 Dec 2021 11:28:13 -0700 Subject: [PATCH 17/66] add Debian build dependency to README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 587a554..c5806e3 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] libsndio [sndio module] libevdev [KeyboardState module] +xkbregistry ``` **Build dependencies** @@ -101,7 +102,8 @@ sudo apt install \ libsigc++-2.0-dev \ libspdlog-dev \ libwayland-dev \ - scdoc + scdoc \ + libxkbregistry-dev ``` From 9bc86347be53023987a9c331ec113cdec1f189b6 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 14 Dec 2021 11:34:15 -0700 Subject: [PATCH 18/66] change signal strength to penalize overly strong signals --- src/modules/network.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/modules/network.cpp b/src/modules/network.cpp index b86989f..56cea04 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -788,13 +788,16 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { if (bss[NL80211_BSS_SIGNAL_MBM] != nullptr) { // signalstrength in dBm from mBm signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100; - // WiFi-hardware usually operates in the range -90 to -30dBm. - const int hardwareMax = -30; + + // If a signal is too strong, it can overwhelm receiving circuity that is designed + // to pick up and process a certain signal level. The following percentage is scaled to + // punish signals that are too strong (>= -45dBm) or too weak (<= -45 dBm). + const int hardwareOptimum = -45; const int hardwareMin = -90; const int strength = - ((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100; - signal_strength_ = std::clamp(strength, 0, 100); + 100 - ((abs(signal_strength_dbm_ - hardwareOptimum) / double{hardwareOptimum - hardwareMin}) * 100); + signal_strength_ = std::clamp(strength, 0, 100); } if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) { signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); From 3218612d3bc7ab12a81fbe97e85d7259e8027312 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 14 Dec 2021 11:36:46 -0700 Subject: [PATCH 19/66] change frequency to GHz --- include/modules/network.hpp | 2 +- src/modules/network.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index c91b598..f7f43fb 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -73,7 +73,7 @@ class Network : public ALabel { int cidr_; int32_t signal_strength_dbm_; uint8_t signal_strength_; - uint32_t frequency_; + float frequency_; uint32_t route_priority; util::SleeperThread thread_; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 56cea04..88e5338 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -88,7 +88,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf #ifdef WANT_RFKILL rfkill_{RFKILL_TYPE_WLAN}, #endif - frequency_(0) { + frequency_(0.0) { // Start with some "text" in the module's label_, update() will then // update it. Since the text should be different, update() will be able @@ -336,7 +336,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), - fmt::arg("frequency", frequency_), + 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")), @@ -365,7 +365,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("ipaddr", ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), - fmt::arg("frequency", frequency_), + 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")), @@ -403,7 +403,7 @@ void waybar::modules::Network::clearIface() { cidr_ = 0; signal_strength_dbm_ = 0; signal_strength_ = 0; - frequency_ = 0; + frequency_ = 0.0; } int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { @@ -470,7 +470,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->essid_.clear(); net->signal_strength_dbm_ = 0; net->signal_strength_ = 0; - net->frequency_ = 0; + net->frequency_ = 0.0; } } net->carrier_ = carrier.value(); @@ -806,8 +806,8 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { void waybar::modules::Network::parseFreq(struct nlattr **bss) { if (bss[NL80211_BSS_FREQUENCY] != nullptr) { - // in MHz - frequency_ = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + // in GHz + frequency_ = (double) nla_get_u32(bss[NL80211_BSS_FREQUENCY]) / 1000; } } From 13d25d403e3823fed06ec76f629c1dc47e4d6b51 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 14 Dec 2021 11:37:39 -0700 Subject: [PATCH 20/66] add to network module - signalStrengthApp shows what applications can run at current signal strength --- include/modules/network.hpp | 1 + src/modules/network.cpp | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index f7f43fb..7b8281b 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -73,6 +73,7 @@ class Network : public ALabel { int cidr_; int32_t signal_strength_dbm_; uint8_t signal_strength_; + std::string signal_strength_app_; float frequency_; uint32_t route_priority; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 88e5338..4825a09 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -331,6 +331,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), + fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), @@ -360,6 +361,7 @@ auto waybar::modules::Network::update() -> void { fmt::arg("essid", essid_), fmt::arg("signaldBm", signal_strength_dbm_), fmt::arg("signalStrength", signal_strength_), + fmt::arg("signalStrengthApp", signal_strength_app_), fmt::arg("ifname", ifname_), fmt::arg("netmask", netmask_), fmt::arg("ipaddr", ipaddr_), @@ -403,6 +405,7 @@ void waybar::modules::Network::clearIface() { cidr_ = 0; signal_strength_dbm_ = 0; signal_strength_ = 0; + signal_strength_app_.clear(); frequency_ = 0.0; } @@ -470,6 +473,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { net->essid_.clear(); net->signal_strength_dbm_ = 0; net->signal_strength_ = 0; + net->signal_strength_app_.clear(); net->frequency_ = 0.0; } } @@ -798,6 +802,20 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { const int strength = 100 - ((abs(signal_strength_dbm_ - hardwareOptimum) / double{hardwareOptimum - hardwareMin}) * 100); signal_strength_ = std::clamp(strength, 0, 100); + + if (signal_strength_dbm_ >= -50) { + signal_strength_app_ = "Great Connectivity"; + } else if (signal_strength_dbm_ >= -60) { + signal_strength_app_ = "Good Connectivity"; + } else if (signal_strength_dbm_ >= -67) { + signal_strength_app_ = "Streaming"; + } else if (signal_strength_dbm_ >= -70) { + signal_strength_app_ = "Web Surfing"; + } else if (signal_strength_dbm_ >= -80) { + signal_strength_app_ = "Basic Connectivity"; + } else { + signal_strength_app_ = "Poor Connectivity"; + } } if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) { signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); From 548bd2ab1a834466e9d95560fd6ce1d6a2a398ba Mon Sep 17 00:00:00 2001 From: ilkecan Date: Tue, 28 Dec 2021 15:57:10 +0300 Subject: [PATCH 21/66] Add `fixed-center` option Resolves #957 --- man/waybar.5.scd.in | 6 ++++++ src/bar.cpp | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index c42f6eb..249ca7e 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -82,6 +82,12 @@ Also a minimal example configuration can be found on the at the bottom of this m default: *true* ++ Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar. +*fixed-center* ++ + typeof: bool ++ + default: *true* + Prefer fixed center position for the `modules-center` block. The center block will stay in the middle of the bar whenever possible. It can still be pushed around if other blocks need more space. + When false, the center block is centered in the space between the left and right block. + *passthrough* ++ typeof: bool ++ default: *false* ++ diff --git a/src/bar.cpp b/src/bar.cpp index 133c29a..9685c7a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -767,7 +767,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk auto waybar::Bar::setupWidgets() -> void { window.add(box_); box_.pack_start(left_, false, false); - box_.set_center_widget(center_); + if (config["fixed-center"].isBool() ? config["fixed-center"].asBool() : true) { + box_.set_center_widget(center_); + } else { + box_.pack_start(center_, true, false); + } box_.pack_end(right_, false, false); // Convert to button code for every module that is used. From 5a4f7a70ef55914cbd1ff8332ef624e8df14d412 Mon Sep 17 00:00:00 2001 From: Yura Shatunov <46374280+volucris1@users.noreply.github.com> Date: Tue, 4 Jan 2022 14:53:59 +0700 Subject: [PATCH 22/66] fix indent --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cfe2d51..2c43426 100644 --- a/meson.build +++ b/meson.build @@ -295,7 +295,7 @@ executable( gtk_layer_shell, libsndio, tz_dep, - xkbregistry + xkbregistry ], include_directories: [include_directories('include')], install: true, From 1e560cf0c9ac11d6509fb9b7635788c3387e1e00 Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Jan 2022 14:47:44 +0300 Subject: [PATCH 23/66] merge --- .github/workflows/freebsd.yml | 1 + README.md | 2 +- include/bar.hpp | 50 ++++- include/client.hpp | 1 + include/factory.hpp | 3 + include/group.hpp | 21 ++ include/modules/clock.hpp | 11 +- include/modules/inhibitor.hpp | 27 +++ include/modules/sway/bar.hpp | 49 +++++ include/modules/sway/language.hpp | 3 +- include/util/SafeSignal.hpp | 75 ++++++++ man/waybar-inhibitor.5.scd | 92 +++++++++ man/waybar-sway-language.5.scd | 2 + man/waybar.5.scd.in | 41 +++- meson.build | 18 +- meson_options.txt | 2 + resources/custom_modules/mediaplayer.py | 1 + src/bar.cpp | 242 ++++++++++++++++++------ src/client.cpp | 1 - src/factory.cpp | 7 + src/group.cpp | 19 ++ src/modules/clock.cpp | 110 +++++++---- src/modules/inhibitor.cpp | 175 +++++++++++++++++ src/modules/network.cpp | 4 +- src/modules/pulseaudio.cpp | 9 +- src/modules/sway/bar.cpp | 107 +++++++++++ src/modules/sway/language.cpp | 17 +- test/GlibTestsFixture.hpp | 24 +++ test/SafeSignal.cpp | 145 ++++++++++++++ test/meson.build | 14 ++ 30 files changed, 1149 insertions(+), 124 deletions(-) create mode 100644 include/group.hpp create mode 100644 include/modules/inhibitor.hpp create mode 100644 include/modules/sway/bar.hpp create mode 100644 include/util/SafeSignal.hpp create mode 100644 man/waybar-inhibitor.5.scd create mode 100644 src/group.cpp create mode 100644 src/modules/inhibitor.cpp create mode 100644 src/modules/sway/bar.cpp create mode 100644 test/GlibTestsFixture.hpp create mode 100644 test/SafeSignal.cpp diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 03e5d70..d5064fe 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -13,6 +13,7 @@ jobs: - name: Test in FreeBSD VM uses: vmactions/freebsd-vm@v0.1.5 # aka FreeBSD 13.0 with: + mem: 2048 usesh: true prepare: | export CPPFLAGS=-isystem/usr/local/include LDFLAGS=-L/usr/local/lib # sndio diff --git a/README.md b/README.md index 98b99a2..587a554 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Highly customizable Wayland bar for Sway and Wlroots based compositors.
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or -[AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)
+[AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)* #### Current features diff --git a/include/bar.hpp b/include/bar.hpp index 6f3dfcf..01a9d03 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -8,6 +8,9 @@ #include #include +#include +#include + #include "AModule.hpp" #include "xdg-output-unstable-v1-client-protocol.h" @@ -36,6 +39,19 @@ struct bar_margins { int left = 0; }; +struct bar_mode { + bar_layer layer; + bool exclusive; + bool passthrough; + bool visible; +}; + +#ifdef HAVE_SWAY +namespace modules::sway { +class BarIpcClient; +} +#endif // HAVE_SWAY + class BarSurface { protected: BarSurface() = default; @@ -54,38 +70,56 @@ class BarSurface { class Bar { public: + using bar_mode_map = std::map; + static const bar_mode_map PRESET_MODES; + static const std::string_view MODE_DEFAULT; + static const std::string_view MODE_INVISIBLE; + Bar(struct waybar_output *w_output, const Json::Value &); Bar(const Bar &) = delete; - ~Bar() = default; + ~Bar(); + void setMode(const std::string_view &); void setVisible(bool visible); void toggle(); void handleSignal(int); struct waybar_output *output; Json::Value config; - struct wl_surface * surface; - bool exclusive = true; + struct wl_surface *surface; bool visible = true; bool vertical = false; Gtk::Window window; +#ifdef HAVE_SWAY + std::string bar_id; +#endif + private: void onMap(GdkEventAny *); auto setupWidgets() -> void; - void getModules(const Factory &, const std::string &); + void getModules(const Factory &, const std::string &, Gtk::Box*); void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); + void setMode(const bar_mode &); + + /* Copy initial set of modes to allow customization */ + bar_mode_map configured_modes = PRESET_MODES; + std::string last_mode_{MODE_DEFAULT}; std::unique_ptr surface_impl_; - bar_layer layer_; Gtk::Box left_; Gtk::Box center_; Gtk::Box right_; Gtk::Box box_; - std::vector> modules_left_; - std::vector> modules_center_; - std::vector> modules_right_; + std::vector> modules_left_; + std::vector> modules_center_; + std::vector> modules_right_; +#ifdef HAVE_SWAY + using BarIpcClient = modules::sway::BarIpcClient; + std::unique_ptr _ipc_client; +#endif + std::vector> modules_all_; }; } // namespace waybar diff --git a/include/client.hpp b/include/client.hpp index bd80d0b..7fc3dce 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -29,6 +29,7 @@ class Client { struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = nullptr; std::vector> bars; Config config; + std::string bar_id; private: Client() = default; diff --git a/include/factory.hpp b/include/factory.hpp index 43dd2cf..3855ce2 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -51,6 +51,9 @@ #ifdef HAVE_LIBSNDIO #include "modules/sndio.hpp" #endif +#ifdef HAVE_GIO_UNIX +#include "modules/inhibitor.hpp" +#endif #include "bar.hpp" #include "modules/custom.hpp" #include "modules/temperature.hpp" diff --git a/include/group.hpp b/include/group.hpp new file mode 100644 index 0000000..f282f9c --- /dev/null +++ b/include/group.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include "AModule.hpp" +#include "bar.hpp" +#include "factory.hpp" + +namespace waybar { + +class Group : public AModule { + public: + Group(const std::string&, const Bar&, const Json::Value&); + ~Group() = default; + auto update() -> void; + operator Gtk::Widget &(); + Gtk::Box box; +}; + +} // namespace waybar diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 17752e4..9f95019 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -17,6 +17,8 @@ struct waybar_time { date::zoned_seconds ztime; }; +const std::string kCalendarPlaceholder = "calendar"; + class Clock : public ALabel { public: Clock(const std::string&, const Json::Value&); @@ -26,18 +28,19 @@ class Clock : public ALabel { private: util::SleeperThread thread_; std::locale locale_; - const date::time_zone* time_zone_; - bool fixed_time_zone_; - int time_zone_idx_; + std::vector time_zones_; + int current_time_zone_idx_; date::year_month_day cached_calendar_ymd_ = date::January/1/0; std::string cached_calendar_text_; + bool is_calendar_in_tooltip_; bool handleScroll(GdkEventScroll* e); auto calendar_text(const waybar_time& wtime) -> std::string; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto first_day_of_week() -> date::weekday; - bool setTimeZone(Json::Value zone_name); + const date::time_zone* current_timezone(); + bool is_timezone_fixed(); }; } // namespace waybar::modules diff --git a/include/modules/inhibitor.hpp b/include/modules/inhibitor.hpp new file mode 100644 index 0000000..aa2f97d --- /dev/null +++ b/include/modules/inhibitor.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +#include "ALabel.hpp" +#include "bar.hpp" + +namespace waybar::modules { + +class Inhibitor : public ALabel { + public: + Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&); + ~Inhibitor() override; + auto update() -> void; + auto activated() -> bool; + + private: + auto handleToggle(::GdkEventButton* const& e) -> bool; + + const std::unique_ptr<::GDBusConnection, void(*)(::GDBusConnection*)> dbus_; + const std::string inhibitors_; + int handle_ = -1; +}; + +} // namespace waybar::modules diff --git a/include/modules/sway/bar.hpp b/include/modules/sway/bar.hpp new file mode 100644 index 0000000..c4381a4 --- /dev/null +++ b/include/modules/sway/bar.hpp @@ -0,0 +1,49 @@ +#pragma once +#include + +#include "modules/sway/ipc/client.hpp" +#include "util/SafeSignal.hpp" +#include "util/json.hpp" + +namespace waybar { + +class Bar; + +namespace modules::sway { + +/* + * Supported subset of i3/sway IPC barconfig object + */ +struct swaybar_config { + std::string id; + std::string mode; + std::string hidden_state; +}; + +/** + * swaybar IPC client + */ +class BarIpcClient { + public: + BarIpcClient(waybar::Bar& bar); + + private: + void onInitialConfig(const struct Ipc::ipc_response& res); + void onIpcEvent(const struct Ipc::ipc_response&); + void onConfigUpdate(const swaybar_config& config); + void onVisibilityUpdate(bool visible_by_modifier); + void update(); + + Bar& bar_; + util::JsonParser parser_; + Ipc ipc_; + + swaybar_config bar_config_; + bool visible_by_modifier_ = false; + + SafeSignal signal_visible_; + SafeSignal signal_config_; +}; + +} // namespace modules::sway +} // namespace waybar diff --git a/include/modules/sway/language.hpp b/include/modules/sway/language.hpp index 1faf52b..92e2bba 100644 --- a/include/modules/sway/language.hpp +++ b/include/modules/sway/language.hpp @@ -32,6 +32,7 @@ class Language : public ALabel, public sigc::trackable { std::string short_name; std::string variant; std::string short_description; + std::string country_flag() const; }; class XKBContext { @@ -54,7 +55,7 @@ class Language : public ALabel, public sigc::trackable { const static std::string XKB_LAYOUT_NAMES_KEY; const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY; - + Layout layout_; std::string tooltip_format_ = ""; std::map layouts_map_; diff --git a/include/util/SafeSignal.hpp b/include/util/SafeSignal.hpp new file mode 100644 index 0000000..3b68653 --- /dev/null +++ b/include/util/SafeSignal.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace waybar { + +/** + * Thread-safe signal wrapper. + * Uses Glib::Dispatcher to pass events to another thread and locked queue to pass the arguments. + */ +template +struct SafeSignal : sigc::signal...)> { + public: + SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); } + + template + void emit(EmitArgs&&... args) { + if (main_tid_ == std::this_thread::get_id()) { + /* + * Bypass the queue if the method is called the main thread. + * Ensures that events emitted from the main thread are processed synchronously and saves a + * few CPU cycles on locking/queuing. + * As a downside, this makes main thread events prioritized over the other threads and + * disrupts chronological order. + */ + signal_t::emit(std::forward(args)...); + } else { + { + std::unique_lock lock(mutex_); + queue_.emplace(std::forward(args)...); + } + dp_.emit(); + } + } + + template + inline void operator()(EmitArgs&&... args) { + emit(std::forward(args)...); + } + + protected: + using signal_t = sigc::signal...)>; + using slot_t = decltype(std::declval().make_slot()); + using arg_tuple_t = std::tuple...>; + // ensure that unwrapped methods are not accessible + using signal_t::emit_reverse; + using signal_t::make_slot; + + void handle_event() { + for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { + auto args = queue_.front(); + queue_.pop(); + lock.unlock(); + std::apply(cached_fn_, args); + } + } + + Glib::Dispatcher dp_; + std::mutex mutex_; + std::queue queue_; + const std::thread::id main_tid_ = std::this_thread::get_id(); + // cache functor for signal emission to avoid recreating it on each event + const slot_t cached_fn_ = make_slot(); +}; + +} // namespace waybar diff --git a/man/waybar-inhibitor.5.scd b/man/waybar-inhibitor.5.scd new file mode 100644 index 0000000..0838f4d --- /dev/null +++ b/man/waybar-inhibitor.5.scd @@ -0,0 +1,92 @@ +waybar-inhibitor(5) + +# NAME + +waybar - inhibitor module + +# DESCRIPTION + +The *inhibitor* module allows to take an inhibitor lock that logind provides. +See *systemd-inhibit*(1) for more information. + +# CONFIGURATION + +*what*: ++ + typeof: string or array ++ + The inhibitor lock or locks that should be taken when active. The available inhibitor locks are *idle*, *shutdown*, *sleep*, *handle-power-key*, *handle-suspend-key*, *handle-hibernate-key* and *handle-lid-switch*. + +*format*: ++ + typeof: string ++ + The format, how the state should be displayed. + +*format-icons*: ++ + typeof: array ++ + Based on the current state, the corresponding icon gets selected. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. A click also toggles the state + +*on-click-middle*: ++ + typeof: string ++ + Command to execute when middle-clicked on the module using mousewheel. + +*on-click-right*: ++ + typeof: string ++ + Command to execute when you right clicked on the module. + +*on-update*: ++ + typeof: string ++ + Command to execute when the module is updated. + +*on-scroll-up*: ++ + typeof: string ++ + Command to execute when scrolling up on the module. + +*on-scroll-down*: ++ + typeof: string ++ + Command to execute when scrolling down on the module. + +*smooth-scrolling-threshold*: ++ + typeof: double ++ + Threshold to be used when scrolling. + +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +# FORMAT REPLACEMENTS + +*{status}*: status (*activated* or *deactivated*) + +*{icon}*: Icon, as defined in *format-icons* + +# EXAMPLES + +``` +"inhibitor": { + "what": "handle-lid-switch", + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + } +} +``` diff --git a/man/waybar-sway-language.5.scd b/man/waybar-sway-language.5.scd index 92a647e..1c88314 100644 --- a/man/waybar-sway-language.5.scd +++ b/man/waybar-sway-language.5.scd @@ -37,6 +37,8 @@ Addressed by *sway/language* *{variant}*: Variant of layout (e.g. "dvorak"). +*{flag}*: Country flag of layout. + # EXAMPLES ``` diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 66d5b2e..c42f6eb 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -72,14 +72,19 @@ Also a minimal example configuration can be found on the at the bottom of this m typeof: string ++ Optional name added as a CSS class, for styling multiple waybars. +*mode* ++ + typeof: string ++ + Selects one of the preconfigured display modes. This is an equivalent of the sway-bar(5) *mode* command and supports the same values: *dock*, *hide*, *invisible*, *overlay*. ++ + Note: *hide* and *invisible* modes may be not as useful without Sway IPC. + *exclusive* ++ typeof: bool ++ - default: *true* unless the layer is set to *overlay* ++ + default: *true* ++ Option to request an exclusive zone from the compositor. Disable this to allow drawing application windows underneath or on top of the bar. *passthrough* ++ typeof: bool ++ - default: *false* unless the layer is set to *overlay* ++ + default: *false* ++ Option to pass any pointer events to the window under the bar. Intended to be used with either *top* or *overlay* layers and without exclusive zone. @@ -89,6 +94,16 @@ Also a minimal example configuration can be found on the at the bottom of this m Option to disable the use of gtk-layer-shell for popups. Only functional if compiled with gtk-layer-shell support. +*ipc* ++ + typeof: bool ++ + default: false ++ + Option to subscribe to the Sway IPC bar configuration and visibility events and control waybar with *swaymsg bar* commands. ++ + Requires *bar_id* value from sway configuration to be either passed with the *-b* commandline argument or specified with the *id* option. + +*id* ++ + typeof: string ++ + *bar_id* for the Sway IPC. Use this if you need to override the value passed with the *-b bar_id* commandline argument for the specific bar instance. + *include* ++ typeof: string|array ++ Paths to additional configuration files. @@ -203,6 +218,28 @@ When positioning Waybar on the left or right side of the screen, sometimes it's Valid options for the "rotate" property are: 0, 90, 180 and 270. +## Grouping modules + +Module groups allow stacking modules in the direction orthogonal to the bar direction. When the bar is positioned on the top or bottom of the screen, modules in a group are stacked vertically. Likewise, when positioned on the left or right, modules in a group are stacked horizontally. + +A module group is defined by specifying a module named "group/some-group-name". The group must also be configured with a list of contained modules. Example: + +``` +{ + "modules-right": ["group/hardware", "clock"], + + "group/hardware": { + "modules": [ + "cpu", + "memory", + "battery" + ] + }, + + ... +} +``` + # SUPPORTED MODULES - *waybar-backlight(5)* diff --git a/meson.build b/meson.build index 62ac8e3..cfe2d51 100644 --- a/meson.build +++ b/meson.build @@ -86,7 +86,7 @@ wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk')) -giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk')) +giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled())) jsoncpp = dependency('jsoncpp') sigcpp = dependency('sigc++-2.0') libepoll = dependency('epoll-shim', required: false) @@ -150,6 +150,7 @@ src_files = files( 'src/bar.cpp', 'src/client.cpp', 'src/config.cpp', + 'src/group.cpp', 'src/util/ustring_clen.cpp' ) @@ -177,6 +178,7 @@ endif add_project_arguments('-DHAVE_SWAY', language: 'cpp') src_files += [ 'src/modules/sway/ipc/client.cpp', + 'src/modules/sway/bar.cpp', 'src/modules/sway/mode.cpp', 'src/modules/sway/language.cpp', 'src/modules/sway/window.cpp', @@ -240,6 +242,11 @@ if libsndio.found() src_files += 'src/modules/sndio.cpp' endif +if (giounix.found() and not get_option('logind').disabled()) + add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp') + src_files += 'src/modules/inhibitor.cpp' +endif + if get_option('rfkill').enabled() if is_linux add_project_arguments('-DWANT_RFKILL', language: 'cpp') @@ -257,6 +264,10 @@ else src_files += 'src/modules/simpleclock.cpp' endif +if get_option('experimental') + add_project_arguments('-DUSE_EXPERIMENTAL', language: 'cpp') +endif + subdir('protocol') executable( @@ -341,6 +352,10 @@ if scdoc.found() 'waybar-sndio.5.scd', ] + if (giounix.found() and not get_option('logind').disabled()) + man_files += 'waybar-inhibitor.5.scd' + endif + foreach file : man_files path = '@0@'.format(file) basename = path.split('/')[-1] @@ -383,3 +398,4 @@ if clangtidy.found() '-p', meson.build_root() ] + src_files) endif + diff --git a/meson_options.txt b/meson_options.txt index 81e4468..230a53d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,4 +10,6 @@ option('mpd', type: 'feature', value: 'auto', description: 'Enable support for t option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support') option('rfkill', type: 'feature', value: 'auto', description: 'Enable support for RFKILL') option('sndio', type: 'feature', value: 'auto', description: 'Enable support for sndio') +option('logind', type: 'feature', value: 'auto', description: 'Enable support for logind') option('tests', type: 'feature', value: 'auto', description: 'Enable tests') +option('experimental', type : 'boolean', value : false, description: 'Enable experimental features') diff --git a/resources/custom_modules/mediaplayer.py b/resources/custom_modules/mediaplayer.py index fa9aa58..1630d97 100755 --- a/resources/custom_modules/mediaplayer.py +++ b/resources/custom_modules/mediaplayer.py @@ -110,6 +110,7 @@ def main(): signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) for player in manager.props.player_names: if arguments.player is not None and arguments.player != player.name: diff --git a/src/bar.cpp b/src/bar.cpp index a8b230e..133c29a 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -9,8 +9,13 @@ #include "bar.hpp" #include "client.hpp" #include "factory.hpp" +#include "group.hpp" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#ifdef HAVE_SWAY +#include "modules/sway/bar.hpp" +#endif + namespace waybar { static constexpr const char* MIN_HEIGHT_MSG = "Requested height: {} is less than the minimum height: {} required by the modules"; @@ -23,6 +28,84 @@ static constexpr const char* BAR_SIZE_MSG = "Bar configured (width: {}, height: static constexpr const char* SIZE_DEFINED = "{} size is defined in the config file so it will stay like that"; +const Bar::bar_mode_map Bar::PRESET_MODES = { // + {"default", + {// Special mode to hold the global bar configuration + .layer = bar_layer::BOTTOM, + .exclusive = true, + .passthrough = false, + .visible = true}}, + {"dock", + {// Modes supported by the sway config; see man sway-bar(5) + .layer = bar_layer::BOTTOM, + .exclusive = true, + .passthrough = false, + .visible = true}}, + {"hide", + {// + .layer = bar_layer::TOP, + .exclusive = false, + .passthrough = false, + .visible = true}}, + {"invisible", + {// + .layer = bar_layer::BOTTOM, + .exclusive = false, + .passthrough = true, + .visible = false}}, + {"overlay", + {// + .layer = bar_layer::TOP, + .exclusive = false, + .passthrough = true, + .visible = true}}}; + +const std::string_view Bar::MODE_DEFAULT = "default"; +const std::string_view Bar::MODE_INVISIBLE = "invisible"; +const std::string_view DEFAULT_BAR_ID = "bar-0"; + +/* Deserializer for enum bar_layer */ +void from_json(const Json::Value& j, bar_layer& l) { + if (j == "bottom") { + l = bar_layer::BOTTOM; + } else if (j == "top") { + l = bar_layer::TOP; + } else if (j == "overlay") { + l = bar_layer::OVERLAY; + } +} + +/* Deserializer for struct bar_mode */ +void from_json(const Json::Value& j, bar_mode& m) { + if (j.isObject()) { + if (auto v = j["layer"]; v.isString()) { + from_json(v, m.layer); + } + if (auto v = j["exclusive"]; v.isBool()) { + m.exclusive = v.asBool(); + } + if (auto v = j["passthrough"]; v.isBool()) { + m.passthrough = v.asBool(); + } + if (auto v = j["visible"]; v.isBool()) { + m.visible = v.asBool(); + } + } +} + +/* Deserializer for JSON Object -> map + * Assumes that all the values in the object are deserializable to the same type. + */ +template ::value>> +void from_json(const Json::Value& j, std::map& m) { + if (j.isObject()) { + for (auto it = j.begin(); it != j.end(); ++it) { + from_json(*it, m[it.key().asString()]); + } + } +} + #ifdef HAVE_GTK_LAYER_SHELL struct GLSSurfaceImpl : public BarSurface, public sigc::trackable { GLSSurfaceImpl(Gtk::Window& window, struct waybar_output& output) : window_{window} { @@ -391,7 +474,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), window{Gtk::WindowType::WINDOW_TOPLEVEL}, - layer_{bar_layer::BOTTOM}, left_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0), right_(Gtk::ORIENTATION_HORIZONTAL, 0), @@ -403,27 +485,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) window.get_style_context()->add_class(config["name"].asString()); window.get_style_context()->add_class(config["position"].asString()); - if (config["layer"] == "top") { - layer_ = bar_layer::TOP; - } else if (config["layer"] == "overlay") { - layer_ = bar_layer::OVERLAY; - } - - if (config["exclusive"].isBool()) { - exclusive = config["exclusive"].asBool(); - } else if (layer_ == bar_layer::OVERLAY) { - // swaybar defaults: overlay mode does not reserve an exclusive zone - exclusive = false; - } - - bool passthrough = false; - if (config["passthrough"].isBool()) { - passthrough = config["passthrough"].asBool(); - } else if (layer_ == bar_layer::OVERLAY) { - // swaybar defaults: overlay mode does not accept pointer events. - passthrough = true; - } - auto position = config["position"].asString(); if (position == "right" || position == "left") { @@ -505,15 +566,43 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) surface_impl_ = std::make_unique(window, *output); } - surface_impl_->setLayer(layer_); - surface_impl_->setExclusiveZone(exclusive); surface_impl_->setMargins(margins_); - surface_impl_->setPassThrough(passthrough); surface_impl_->setPosition(position); surface_impl_->setSize(width, height); + /* Read custom modes if available */ + if (auto modes = config.get("modes", {}); modes.isObject()) { + from_json(modes, configured_modes); + } + + /* Update "default" mode with the global bar options */ + from_json(config, configured_modes[MODE_DEFAULT]); + + if (auto mode = config.get("mode", {}); mode.isString()) { + setMode(config["mode"].asString()); + } else { + setMode(MODE_DEFAULT); + } + window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap)); +#if HAVE_SWAY + if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) { + bar_id = Client::inst()->bar_id; + if (auto id = config["id"]; id.isString()) { + bar_id = id.asString(); + } + if (bar_id.empty()) { + bar_id = DEFAULT_BAR_ID; + } + try { + _ipc_client = std::make_unique(*this); + } catch (const std::exception& exc) { + spdlog::warn("Failed to open bar ipc connection: {}", exc.what()); + } + } +#endif + setupWidgets(); window.show_all(); @@ -528,6 +617,44 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) } } +/* Need to define it here because of forward declared members */ +waybar::Bar::~Bar() = default; + +void waybar::Bar::setMode(const std::string_view& mode) { + using namespace std::literals::string_literals; + + auto style = window.get_style_context(); + /* remove styles added by previous setMode calls */ + style->remove_class("mode-"s + last_mode_); + + auto it = configured_modes.find(mode); + if (it != configured_modes.end()) { + last_mode_ = mode; + style->add_class("mode-"s + last_mode_); + setMode(it->second); + } else { + spdlog::warn("Unknown mode \"{}\" requested", mode); + last_mode_ = MODE_DEFAULT; + style->add_class("mode-"s + last_mode_); + setMode(configured_modes.at(MODE_DEFAULT)); + } +} + +void waybar::Bar::setMode(const struct bar_mode& mode) { + surface_impl_->setLayer(mode.layer); + surface_impl_->setExclusiveZone(mode.exclusive); + surface_impl_->setPassThrough(mode.passthrough); + + if (mode.visible) { + window.get_style_context()->remove_class("hidden"); + window.set_opacity(1); + } else { + window.get_style_context()->add_class("hidden"); + window.set_opacity(0); + } + surface_impl_->commit(); +} + void waybar::Bar::onMap(GdkEventAny*) { /* * Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor). @@ -538,17 +665,7 @@ void waybar::Bar::onMap(GdkEventAny*) { void waybar::Bar::setVisible(bool value) { visible = value; - if (!visible) { - window.get_style_context()->add_class("hidden"); - window.set_opacity(0); - surface_impl_->setLayer(bar_layer::BOTTOM); - } else { - window.get_style_context()->remove_class("hidden"); - window.set_opacity(1); - surface_impl_->setLayer(layer_); - } - surface_impl_->setExclusiveZone(exclusive && visible); - surface_impl_->commit(); + setMode(visible ? MODE_DEFAULT : MODE_INVISIBLE); } void waybar::Bar::toggle() { setVisible(!visible); } @@ -594,19 +711,7 @@ void waybar::Bar::setupAltFormatKeyForModuleList(const char* module_list_name) { } void waybar::Bar::handleSignal(int signal) { - for (auto& module : modules_left_) { - auto* custom = dynamic_cast(module.get()); - if (custom != nullptr) { - custom->refresh(signal); - } - } - for (auto& module : modules_center_) { - auto* custom = dynamic_cast(module.get()); - if (custom != nullptr) { - custom->refresh(signal); - } - } - for (auto& module : modules_right_) { + for (auto& module : modules_all_) { auto* custom = dynamic_cast(module.get()); if (custom != nullptr) { custom->refresh(signal); @@ -614,19 +719,36 @@ void waybar::Bar::handleSignal(int signal) { } } -void waybar::Bar::getModules(const Factory& factory, const std::string& pos) { - if (config[pos].isArray()) { - for (const auto& name : config[pos]) { +void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk::Box* group = nullptr) { + auto module_list = group ? config[pos]["modules"] : config[pos]; + if (module_list.isArray()) { + for (const auto& name : module_list) { try { - auto module = factory.makeModule(name.asString()); - if (pos == "modules-left") { - modules_left_.emplace_back(module); + auto ref = name.asString(); + AModule* module; + + if (ref.compare(0, 6, "group/") == 0 && ref.size() > 6) { + auto group_module = new waybar::Group(ref, *this, config[ref]); + getModules(factory, ref, &group_module->box); + module = group_module; + } else { + module = factory.makeModule(ref); } - if (pos == "modules-center") { - modules_center_.emplace_back(module); - } - if (pos == "modules-right") { - modules_right_.emplace_back(module); + + std::shared_ptr module_sp(module); + modules_all_.emplace_back(module_sp); + if (group) { + group->pack_start(*module, false, false); + } else { + if (pos == "modules-left") { + modules_left_.emplace_back(module_sp); + } + if (pos == "modules-center") { + modules_center_.emplace_back(module_sp); + } + if (pos == "modules-right") { + modules_right_.emplace_back(module_sp); + } } module->dp.connect([module, &name] { try { diff --git a/src/client.cpp b/src/client.cpp index 95f5a29..8adbeac 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -199,7 +199,6 @@ int waybar::Client::main(int argc, char *argv[]) { bool show_version = false; std::string config_opt; std::string style_opt; - std::string bar_id; std::string log_level; auto cli = clara::detail::Help(show_help) | clara::detail::Opt(show_version)["-v"]["--version"]("Show version") | diff --git a/src/factory.cpp b/src/factory.cpp index a577751..900653b 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -30,10 +30,12 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "wlr/taskbar") { return new waybar::modules::wlr::Taskbar(id, bar_, config_[name]); } +#ifdef USE_EXPERIMENTAL if (ref == "wlr/workspaces") { return new waybar::modules::wlr::WorkspaceManager(id, bar_, config_[name]); } #endif +#endif #ifdef HAVE_RIVER if (ref == "river/tags") { return new waybar::modules::river::Tags(id, bar_, config_[name]); @@ -92,6 +94,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "sndio") { return new waybar::modules::Sndio(id, config_[name]); } +#endif +#ifdef HAVE_GIO_UNIX + if (ref == "inhibitor") { + return new waybar::modules::Inhibitor(id, bar_, config_[name]); + } #endif if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); diff --git a/src/group.cpp b/src/group.cpp new file mode 100644 index 0000000..9d2188c --- /dev/null +++ b/src/group.cpp @@ -0,0 +1,19 @@ +#include "group.hpp" +#include +#include + +namespace waybar { + +Group::Group(const std::string& name, const Bar& bar, const Json::Value& config) + : AModule(config, name, "", false, false), + box{bar.vertical ? Gtk::ORIENTATION_HORIZONTAL : Gtk::ORIENTATION_VERTICAL, 0} + { +} + +auto Group::update() -> void { + // noop +} + +Group::operator Gtk::Widget&() { return box; } + +} // namespace waybar diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7c94c45..7e7d742 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -14,17 +14,51 @@ using waybar::modules::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) - : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), fixed_time_zone_(false) { + : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), + current_time_zone_idx_(0), + is_calendar_in_tooltip_(false) +{ if (config_["timezones"].isArray() && !config_["timezones"].empty()) { - time_zone_idx_ = 0; - setTimeZone(config_["timezones"][time_zone_idx_]); - } else { - setTimeZone(config_["timezone"]); + for (const auto& zone_name: config_["timezones"]) { + if (!zone_name.isString() || zone_name.asString().empty()) { + time_zones_.push_back(nullptr); + continue; + } + time_zones_.push_back( + date::locate_zone( + zone_name.asString() + ) + ); + } + } else if (config_["timezone"].isString() && !config_["timezone"].asString().empty()) { + time_zones_.push_back( + date::locate_zone( + config_["timezone"].asString() + ) + ); } - if (fixed_time_zone_) { + + // If all timezones are parsed and no one is good, add nullptr to the timezones vector, to mark that local time should be shown. + if (!time_zones_.size()) { + time_zones_.push_back(nullptr); + } + + if (!is_timezone_fixed()) { spdlog::warn("As using a timezone, some format args may be missing as the date library haven't got a release since 2018."); } + // Check if a particular placeholder is present in the tooltip format, to know what to calculate on update. + if (config_["tooltip-format"].isString()) { + std::string trimmed_format = config_["tooltip-format"].asString(); + trimmed_format.erase(std::remove_if(trimmed_format.begin(), + trimmed_format.end(), + [](unsigned char x){return std::isspace(x);}), + trimmed_format.end()); + if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { + is_calendar_in_tooltip_ = true; + } + } + if (config_["locale"].isString()) { locale_ = std::locale(config_["locale"].asString()); } else { @@ -40,53 +74,46 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) }; } +const date::time_zone* waybar::modules::Clock::current_timezone() { + return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_] : date::current_zone(); +} + +bool waybar::modules::Clock::is_timezone_fixed() { + return time_zones_[current_time_zone_idx_] != nullptr; +} + auto waybar::modules::Clock::update() -> void { - if (!fixed_time_zone_) { - // Time zone can change. Be sure to pick that. - time_zone_ = date::current_zone(); - } - - auto now = std::chrono::system_clock::now(); + auto time_zone = current_timezone(); + auto now = std::chrono::system_clock::now(); waybar_time wtime = {locale_, - date::make_zoned(time_zone_, date::floor(now))}; - - std::string text; - if (!fixed_time_zone_) { + date::make_zoned(time_zone, date::floor(now))}; + std::string text = ""; + if (!is_timezone_fixed()) { // As date dep is not fully compatible, prefer fmt tzset(); auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); text = fmt::format(format_, localtime); - label_.set_markup(text); } else { text = fmt::format(format_, wtime); - label_.set_markup(text); } + label_.set_markup(text); if (tooltipEnabled()) { if (config_["tooltip-format"].isString()) { - const auto calendar = calendar_text(wtime); - auto tooltip_format = config_["tooltip-format"].asString(); - auto tooltip_text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar)); - label_.set_tooltip_markup(tooltip_text); - } else { - label_.set_tooltip_markup(text); + std::string calendar_lines = ""; + if (is_calendar_in_tooltip_) { + calendar_lines = calendar_text(wtime); + } + auto tooltip_format = config_["tooltip-format"].asString(); + text = fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines)); } } + + label_.set_tooltip_markup(text); // Call parent update ALabel::update(); } -bool waybar::modules::Clock::setTimeZone(Json::Value zone_name) { - if (!zone_name.isString() || zone_name.asString().empty()) { - fixed_time_zone_ = false; - return false; - } - - time_zone_ = date::locate_zone(zone_name.asString()); - fixed_time_zone_ = true; - return true; -} - bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { // defer to user commands if set if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) { @@ -97,17 +124,18 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll *e) { if (dir != SCROLL_DIR::UP && dir != SCROLL_DIR::DOWN) { return true; } - if (!config_["timezones"].isArray() || config_["timezones"].empty()) { + if (time_zones_.size() == 1) { return true; } - auto nr_zones = config_["timezones"].size(); + + auto nr_zones = time_zones_.size(); if (dir == SCROLL_DIR::UP) { - size_t new_idx = time_zone_idx_ + 1; - time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; + size_t new_idx = current_time_zone_idx_ + 1; + current_time_zone_idx_ = new_idx == nr_zones ? 0 : new_idx; } else { - time_zone_idx_ = time_zone_idx_ == 0 ? nr_zones - 1 : time_zone_idx_ - 1; + current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1; } - setTimeZone(config_["timezones"][time_zone_idx_]); + update(); return true; } diff --git a/src/modules/inhibitor.cpp b/src/modules/inhibitor.cpp new file mode 100644 index 0000000..1e3f2d3 --- /dev/null +++ b/src/modules/inhibitor.cpp @@ -0,0 +1,175 @@ +#include "modules/inhibitor.hpp" + +#include +#include +#include + +namespace { + +using DBus = std::unique_ptr; + +auto dbus() -> DBus { + GError *error = nullptr; + GDBusConnection* connection = + g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + + if (error) { + spdlog::error("g_bus_get_sync() failed: {}", error->message); + g_error_free(error); + connection = nullptr; + } + + auto destructor = [](GDBusConnection* connection) { + GError *error = nullptr; + g_dbus_connection_close_sync(connection, nullptr, &error); + if (error) { + spdlog::error( + "g_bus_connection_close_sync failed(): {}", + error->message); + g_error_free(error); + } + }; + + return DBus{connection, destructor}; +} + +auto getLocks(const DBus& bus, const std::string& inhibitors) -> int { + GError *error = nullptr; + GUnixFDList* fd_list; + int handle; + + auto reply = g_dbus_connection_call_with_unix_fd_list_sync(bus.get(), + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit", + g_variant_new( + "(ssss)", + inhibitors.c_str(), + "waybar", + "Asked by user", + "block"), + G_VARIANT_TYPE("(h)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &fd_list, + nullptr, + &error); + if (error) { + spdlog::error( + "g_dbus_connection_call_with_unix_fd_list_sync() failed: {}", + error->message); + g_error_free(error); + handle = -1; + } else { + gint index; + g_variant_get(reply, "(h)", &index); + g_variant_unref(reply); + handle = g_unix_fd_list_get(fd_list, index, nullptr); + g_object_unref(fd_list); + } + + return handle; +} + +auto checkInhibitor(const std::string& inhibitor) -> const std::string& { + static const auto inhibitors = std::array{ + "idle", + "shutdown", + "sleep", + "handle-power-key", + "handle-suspend-key", + "handle-hibernate-key", + "handle-lid-switch" + }; + + if (std::find(inhibitors.begin(), inhibitors.end(), inhibitor) + == inhibitors.end()) { + throw std::runtime_error("invalid logind inhibitor " + inhibitor); + } + + return inhibitor; +} + +auto getInhibitors(const Json::Value& config) -> std::string { + std::string inhibitors = "idle"; + + if (config["what"].empty()) { + return inhibitors; + } + + if (config["what"].isString()) { + return checkInhibitor(config["what"].asString()); + } + + if (config["what"].isArray()) { + inhibitors = checkInhibitor(config["what"][0].asString()); + for (decltype(config["what"].size()) i = 1; i < config["what"].size(); ++i) { + inhibitors += ":" + checkInhibitor(config["what"][i].asString()); + } + return inhibitors; + } + + return inhibitors; +} + +} + +namespace waybar::modules { + +Inhibitor::Inhibitor(const std::string& id, const Bar& bar, + const Json::Value& config) + : ALabel(config, "inhibitor", id, "{status}", true), + dbus_(::dbus()), + inhibitors_(::getInhibitors(config)) { + event_box_.add_events(Gdk::BUTTON_PRESS_MASK); + event_box_.signal_button_press_event().connect( + sigc::mem_fun(*this, &Inhibitor::handleToggle)); + dp.emit(); +} + +Inhibitor::~Inhibitor() { + if (handle_ != -1) { + ::close(handle_); + } +} + +auto Inhibitor::activated() -> bool { + return handle_ != -1; +} + +auto Inhibitor::update() -> void { + std::string status_text = activated() ? "activated" : "deactivated"; + + label_.get_style_context()->remove_class( + activated() ? "deactivated" : "activated"); + label_.set_markup( + fmt::format(format_, fmt::arg("status", status_text), + fmt::arg("icon", getIcon(0, status_text)))); + label_.get_style_context()->add_class(status_text); + + if (tooltipEnabled()) { + label_.set_tooltip_text(status_text); + } + + return ALabel::update(); +} + +auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool { + if (e->button == 1) { + if (activated()) { + ::close(handle_); + handle_ = -1; + } else { + handle_ = ::getLocks(dbus_, inhibitors_); + if (handle_ == -1) { + spdlog::error("cannot get inhibitor locks"); + } + } + } + + return ALabel::handleToggle(e); +} + +} // waybar::modules diff --git a/src/modules/network.cpp b/src/modules/network.cpp index e7b20ab..b86989f 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -789,8 +789,8 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) { // signalstrength in dBm from mBm signal_strength_dbm_ = nla_get_s32(bss[NL80211_BSS_SIGNAL_MBM]) / 100; - // WiFi-hardware usually operates in the range -90 to -20dBm. - const int hardwareMax = -20; + // WiFi-hardware usually operates in the range -90 to -30dBm. + const int hardwareMax = -30; const int hardwareMin = -90; const int strength = ((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index cf42780..3c32060 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -79,6 +79,13 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) { if (dir == SCROLL_DIR::NONE) { return true; } + if (config_["reverse-scrolling"].asInt() == 1){ + if (dir == SCROLL_DIR::UP) { + dir = SCROLL_DIR::DOWN; + } else if (dir == SCROLL_DIR::DOWN) { + dir = SCROLL_DIR::UP; + } + } double volume_tick = static_cast(PA_VOLUME_NORM) / 100; pa_volume_t change = volume_tick; pa_cvolume pa_volume = pa_volume_; @@ -211,7 +218,7 @@ static const std::array ports = { }; const std::vector waybar::modules::Pulseaudio::getPulseIcon() const { - std::vector res = {default_source_name_}; + std::vector res = {current_sink_name_, default_source_name_}; std::string nameLC = port_name_ + form_factor_; std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower); for (auto const &port : ports) { diff --git a/src/modules/sway/bar.cpp b/src/modules/sway/bar.cpp new file mode 100644 index 0000000..6ad74af --- /dev/null +++ b/src/modules/sway/bar.cpp @@ -0,0 +1,107 @@ +#include "modules/sway/bar.hpp" + +#include +#include + +#include + +#include "bar.hpp" +#include "modules/sway/ipc/ipc.hpp" + +namespace waybar::modules::sway { + +BarIpcClient::BarIpcClient(waybar::Bar& bar) : bar_{bar} { + { + sigc::connection handle = + ipc_.signal_cmd.connect(sigc::mem_fun(*this, &BarIpcClient::onInitialConfig)); + ipc_.sendCmd(IPC_GET_BAR_CONFIG, bar_.bar_id); + + handle.disconnect(); + } + + signal_config_.connect(sigc::mem_fun(*this, &BarIpcClient::onConfigUpdate)); + signal_visible_.connect(sigc::mem_fun(*this, &BarIpcClient::onVisibilityUpdate)); + + ipc_.subscribe(R"(["bar_state_update", "barconfig_update"])"); + ipc_.signal_event.connect(sigc::mem_fun(*this, &BarIpcClient::onIpcEvent)); + // Launch worker + ipc_.setWorker([this] { + try { + ipc_.handleEvent(); + } catch (const std::exception& e) { + spdlog::error("BarIpcClient::handleEvent {}", e.what()); + } + }); +} + +struct swaybar_config parseConfig(const Json::Value& payload) { + swaybar_config conf; + if (auto id = payload["id"]; id.isString()) { + conf.id = id.asString(); + } + if (auto mode = payload["mode"]; mode.isString()) { + conf.mode = mode.asString(); + } + if (auto hs = payload["hidden_state"]; hs.isString()) { + conf.hidden_state = hs.asString(); + } + return conf; +} + +void BarIpcClient::onInitialConfig(const struct Ipc::ipc_response& res) { + auto payload = parser_.parse(res.payload); + if (auto success = payload.get("success", true); !success.asBool()) { + auto err = payload.get("error", "Unknown error"); + throw std::runtime_error(err.asString()); + } + auto config = parseConfig(payload); + onConfigUpdate(config); +} + +void BarIpcClient::onIpcEvent(const struct Ipc::ipc_response& res) { + try { + auto payload = parser_.parse(res.payload); + if (auto id = payload["id"]; id.isString() && id.asString() != bar_.bar_id) { + spdlog::trace("swaybar ipc: ignore event for {}", id.asString()); + return; + } + if (payload.isMember("visible_by_modifier")) { + // visibility change for hidden bar + signal_visible_(payload["visible_by_modifier"].asBool()); + } else { + // configuration update + auto config = parseConfig(payload); + signal_config_(std::move(config)); + } + } catch (const std::exception& e) { + spdlog::error("BarIpcClient::onEvent {}", e.what()); + } +} + +void BarIpcClient::onConfigUpdate(const swaybar_config& config) { + spdlog::info("config update for {}: id {}, mode {}, hidden_state {}", + bar_.bar_id, + config.id, + config.mode, + config.hidden_state); + bar_config_ = config; + update(); +} + +void BarIpcClient::onVisibilityUpdate(bool visible_by_modifier) { + spdlog::debug("visiblity update for {}: {}", bar_.bar_id, visible_by_modifier); + visible_by_modifier_ = visible_by_modifier; + update(); +} + +void BarIpcClient::update() { + bool visible = visible_by_modifier_; + if (bar_config_.mode == "invisible") { + visible = false; + } else if (bar_config_.mode != "hide" || bar_config_.hidden_state != "hide") { + visible = true; + } + bar_.setMode(visible ? bar_config_.mode : Bar::MODE_INVISIBLE); +} + +} // namespace waybar::modules::sway diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 186fa4b..73a64c3 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -99,7 +99,8 @@ auto Language::update() -> void { fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant))); + fmt::arg("variant", layout_.variant), + fmt::arg("flag", layout_.country_flag()))); label_.set_markup(display_layout); if (tooltipEnabled()) { if (tooltip_format_ != "") { @@ -107,7 +108,8 @@ auto Language::update() -> void { fmt::arg("short", layout_.short_name), fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name), - fmt::arg("variant", layout_.variant))); + fmt::arg("variant", layout_.variant), + fmt::arg("flag", layout_.country_flag()))); label_.set_tooltip_markup(tooltip_display_layout); } else { label_.set_tooltip_markup(display_layout); @@ -212,4 +214,15 @@ Language::XKBContext::~XKBContext() { rxkb_context_unref(context_); delete layout_; } + +std::string Language::Layout::country_flag() const { + if (short_name.size() != 2) return ""; + unsigned char result[] = "\xf0\x9f\x87\x00\xf0\x9f\x87\x00"; + result[3] = short_name[0] + 0x45; + result[7] = short_name[1] + 0x45; + // Check if both emojis are in A-Z symbol bounds + if (result[3] < 0xa6 || result[3] > 0xbf) return ""; + if (result[7] < 0xa6 || result[7] > 0xbf) return ""; + return std::string{reinterpret_cast(result)}; +} } // namespace waybar::modules::sway diff --git a/test/GlibTestsFixture.hpp b/test/GlibTestsFixture.hpp new file mode 100644 index 0000000..a21c8e0 --- /dev/null +++ b/test/GlibTestsFixture.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +/** + * Minimal Glib application to be used for tests that require Glib main loop + */ +class GlibTestsFixture : public sigc::trackable { + public: + GlibTestsFixture() : main_loop_{Glib::MainLoop::create()} {} + + void setTimeout(int timeout) { + Glib::signal_timeout().connect_once([]() { throw std::runtime_error("Test timed out"); }, + timeout); + } + + void run(std::function fn) { + Glib::signal_idle().connect_once(fn); + main_loop_->run(); + } + + void quit() { main_loop_->quit(); } + + protected: + Glib::RefPtr main_loop_; +}; diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp new file mode 100644 index 0000000..2c67317 --- /dev/null +++ b/test/SafeSignal.cpp @@ -0,0 +1,145 @@ +#define CATCH_CONFIG_RUNNER +#include "util/SafeSignal.hpp" + +#include + +#include +#include +#include + +#include "GlibTestsFixture.hpp" + +using namespace waybar; + +template +using remove_cvref_t = typename std::remove_cv::type>::type; + +/** + * Basic sanity test for SafeSignal: + * check that type deduction works, events are delivered and the order is right + * Running this with -fsanitize=thread should not fail + */ +TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal basic functionality", "[signal][thread][util]") { + const int NUM_EVENTS = 100; + int count = 0; + int last_value = 0; + + SafeSignal test_signal; + + const auto main_tid = std::this_thread::get_id(); + std::thread producer; + + // timeout the test in 500ms + setTimeout(500); + + test_signal.connect([&](auto val, auto str) { + static_assert(std::is_same::value); + static_assert(std::is_same::value); + // check that we're in the same thread as the main loop + REQUIRE(std::this_thread::get_id() == main_tid); + // check event order + REQUIRE(val == last_value + 1); + + last_value = val; + if (++count >= NUM_EVENTS) { + this->quit(); + }; + }); + + run([&]() { + // check that events from the same thread are delivered and processed synchronously + test_signal.emit(1, "test"); + REQUIRE(count == 1); + + // start another thread and generate events + producer = std::thread([&]() { + for (auto i = 2; i <= NUM_EVENTS; ++i) { + test_signal.emit(i, "test"); + } + }); + }); + producer.join(); + REQUIRE(count == NUM_EVENTS); +} + +template +struct TestObject { + T value; + unsigned copied = 0; + unsigned moved = 0; + + TestObject(const T& v) : value(v){}; + ~TestObject() = default; + + TestObject(const TestObject& other) + : value(other.value), copied(other.copied + 1), moved(other.moved) {} + + TestObject(TestObject&& other) noexcept + : value(std::move(other.value)), + copied(std::exchange(other.copied, 0)), + moved(std::exchange(other.moved, 0) + 1) {} + + TestObject& operator=(const TestObject& other) { + value = other.value; + copied = other.copied + 1; + moved = other.moved; + return *this; + } + + TestObject& operator=(TestObject&& other) noexcept { + value = std::move(other.value); + copied = std::exchange(other.copied, 0); + moved = std::exchange(other.moved, 0) + 1; + return *this; + } + + bool operator==(T other) const { return value == other; } + operator T() const { return value; } +}; + +/* + * Check the number of copies/moves performed on the object passed through SafeSignal + */ +TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thread][util]") { + const int NUM_EVENTS = 3; + int count = 0; + + SafeSignal> test_signal; + + std::thread producer; + + // timeout the test in 500ms + setTimeout(500); + + test_signal.connect([&](auto& val) { + static_assert(std::is_same, remove_cvref_t>::value); + + /* explicit move in the producer thread */ + REQUIRE(val.moved <= 1); + /* copy within the SafeSignal queuing code */ + REQUIRE(val.copied <= 1); + + if (++count >= NUM_EVENTS) { + this->quit(); + }; + }); + + run([&]() { + test_signal.emit(1); + REQUIRE(count == 1); + producer = std::thread([&]() { + for (auto i = 2; i <= NUM_EVENTS; ++i) { + TestObject t{i}; + // check that signal.emit accepts moved objects + test_signal.emit(std::move(t)); + } + }); + }); + producer.join(); + REQUIRE(count == NUM_EVENTS); +} + +int main(int argc, char* argv[]) { + Glib::init(); + return Catch::Session().run(argc, argv); +} diff --git a/test/meson.build b/test/meson.build index 85b9771..bbef21e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -2,6 +2,7 @@ test_inc = include_directories('../include') test_dep = [ catch2, fmt, + gtkmm, jsoncpp, spdlog, ] @@ -14,8 +15,21 @@ config_test = executable( include_directories: test_inc, ) +safesignal_test = executable( + 'safesignal_test', + 'SafeSignal.cpp', + dependencies: test_dep, + include_directories: test_inc, +) + test( 'Configuration test', config_test, workdir: meson.source_root(), ) + +test( + 'SafeSignal test', + safesignal_test, + workdir: meson.source_root(), +) From 812832d38adeb62e675a85e004f05851ed60f26c Mon Sep 17 00:00:00 2001 From: dmitry Date: Wed, 5 Jan 2022 15:38:52 +0300 Subject: [PATCH 24/66] fix: Missing tasks --- include/modules/wlr/taskbar.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 71c9fd2..973d15d 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -64,8 +64,8 @@ class Task Gtk::Label text_before_; Gtk::Label text_after_; Glib::RefPtr app_info_; - bool button_visible_; - bool ignored_; + bool button_visible_ = false; + bool ignored_ = false; bool with_icon_ = false; bool with_name_ = false; From 4a85ec0f59137b041d99801292a9b708b99a15a5 Mon Sep 17 00:00:00 2001 From: Johannes Wienke Date: Thu, 6 Jan 2022 17:49:06 +0100 Subject: [PATCH 25/66] fix: match sway workspace order with sway one Ensure that sway workspaces are always displayed in the same order as used internally by sway. The previous sorting code always sorted unnumbered workspaces lexicographically. This isn't the order used by sway internally. Therefore, commands such as "workspace next" might have jumped arbitrarily in waybar. This commit reworks the sorting code such that the internal order is always obeyed. Additionally, numbered persistent workspaces are inserted at their natural position at the front of the workspace list while unnumbered ones are appended. This should match the expectations of workspace ordering known from sway's behavior. The changes make the configuration property "numeric-first" unnecessary as this will always be the case now. There's also no reasonable way around this behavior now. Otherwise, persistent workspaces would jump around in the visual representation as soon as they become known to sway. Fixes #802 --- man/waybar-sway-workspaces.5.scd | 5 --- src/modules/sway/workspaces.cpp | 74 +++++++++++++++++--------------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index f2808b9..b575e09 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -69,10 +69,6 @@ Addressed by *sway/workspaces* typeof: string ++ Command to execute when the module is updated. -*numeric-first*: ++ - typeof: bool ++ - Whether to put workspaces starting with numbers before workspaces that do not start with a number. - *disable-auto-back-and-forth*: ++ typeof: bool ++ Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration. @@ -120,7 +116,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge "sway/workspaces": { "disable-scroll": true, "all-outputs": true, - "numeric-first": false, "format": "{name}: {icon}", "format-icons": { "1": "", diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 43dcf33..074d145 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -98,6 +99,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { Json::Value v; v["name"] = p_w_name; v["target_output"] = bar_.output->name; + v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); break; } @@ -107,57 +109,59 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { Json::Value v; v["name"] = p_w_name; v["target_output"] = ""; + v["num"] = convertWorkspaceNameToNum(p_w_name); workspaces_.emplace_back(std::move(v)); } } } - // config option to sort numeric workspace names before others - bool config_numeric_first = config_["numeric-first"].asBool(); - + // sway has a defined ordering of workspaces that should be preserved in + // the representation displayed by waybar to ensure that commands such + // as "workspace prev" or "workspace next" make sense when looking at + // the workspace representation in the bar. + // Due to waybar's own feature of persistent workspaces unknown to sway, + // custom sorting logic is necessary to make these workspaces appear + // naturally in the list of workspaces without messing up sway's + // sorting. For this purpose, a custom numbering property is created + // that preserves the order provided by sway while inserting numbered + // persistent workspaces at their natural positions. + // + // All of this code assumes that sway provides numbered workspaces first + // and other workspaces are sorted by their creation time. + // + // In a first pass, the maximum "num" value is computed to enqueue + // unnumbered workspaces behind numbered ones when computing the sort + // attribute. + int max_num = -1; + for (auto & workspace : workspaces_) { + max_num = std::max(workspace["num"].asInt(), max_num); + } + for (auto & workspace : workspaces_) { + auto workspace_num = workspace["num"].asInt(); + if (workspace_num > -1) { + workspace["sort"] = workspace_num; + } else { + workspace["sort"] = ++max_num; + } + } std::sort(workspaces_.begin(), workspaces_.end(), - [config_numeric_first](const Json::Value &lhs, const Json::Value &rhs) { - // the "num" property (integer type): - // The workspace number or -1 for workspaces that do - // not start with a number. - // We could rely on sway providing this property: - // - // auto l = lhs["num"].asInt(); - // auto r = rhs["num"].asInt(); - // - // We cannot rely on the "num" property as provided by sway - // via IPC, because persistent workspace might not exist in - // sway's view. However, we need this property also for - // not-yet created persistent workspace. As such, we simply - // duplicate sway's logic of assigning the "num" property - // into waybar (see convertWorkspaceNameToNum). This way the - // sorting should work out even when we include workspaces - // that do not currently exist. + [](const Json::Value &lhs, const Json::Value &rhs) { auto lname = lhs["name"].asString(); auto rname = rhs["name"].asString(); - int l = convertWorkspaceNameToNum(lname); - int r = convertWorkspaceNameToNum(rname); + int l = lhs["sort"].asInt(); + int r = rhs["sort"].asInt(); if (l == r) { - // in case both integers are the same, lexicographical - // sort. This also covers the case when both don't have a - // number (i.e., l == r == -1). + // In case both integers are the same, lexicographical + // sort. The code above already ensure that this will only + // happend in case of explicitly numbered workspaces. return lname < rname; } - // one of the workspaces doesn't begin with a number, so - // num is -1. - if (l < 0 || r < 0) { - if (config_numeric_first) { - return r < 0; - } - return l < 0; - } - - // both workspaces have a "num" so let's just compare those return l < r; }); + } dp.emit(); } catch (const std::exception &e) { From 2301788a810b64be9361b15fbd8f510002c74cc4 Mon Sep 17 00:00:00 2001 From: mazunki Date: Sat, 8 Jan 2022 01:44:46 +0100 Subject: [PATCH 26/66] created a hotfix for libfmt-8.1.0 and above --- src/modules/clock.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7e7d742..96fb34a 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -233,8 +233,9 @@ struct fmt::formatter : fmt::formatter { template auto format(const waybar_time& t, FormatContext& ctx) { #if FMT_VERSION >= 80000 - auto& tm_format = specs; +//auto& tm_format = specs; #endif - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); +// return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); + return fmt::formatter::format((const std::tm&) t, ctx); } }; From c0d84853ea2f3f8b016abf92458ba15378de6c91 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 7 Jan 2022 18:09:44 -0800 Subject: [PATCH 27/66] refactor(clock): extract waybar_time to util/waybar_time.hpp --- include/modules/clock.hpp | 18 ++++++------------ include/util/waybar_time.hpp | 29 +++++++++++++++++++++++++++++ src/modules/clock.cpp | 14 ++------------ 3 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 include/util/waybar_time.hpp diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index 9f95019..ddcdf60 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -1,21 +1,14 @@ #pragma once -#include -#if FMT_VERSION < 60000 -#include -#else -#include -#endif #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" -namespace waybar::modules { +namespace waybar { -struct waybar_time { - std::locale locale; - date::zoned_seconds ztime; -}; +struct waybar_time; + +namespace modules { const std::string kCalendarPlaceholder = "calendar"; @@ -43,4 +36,5 @@ class Clock : public ALabel { bool is_timezone_fixed(); }; -} // namespace waybar::modules +} // namespace modules +} // namespace waybar diff --git a/include/util/waybar_time.hpp b/include/util/waybar_time.hpp new file mode 100644 index 0000000..3eaf114 --- /dev/null +++ b/include/util/waybar_time.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include + +namespace waybar { + +struct waybar_time { + std::locale locale; + date::zoned_seconds ztime; +}; + +} // namespace waybar + +template <> +struct fmt::formatter : fmt::formatter { + template + auto format(const waybar::waybar_time& t, FormatContext& ctx) { +#if FMT_VERSION >= 80000 + auto& tm_format = specs; +#endif + return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); + } +}; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 7e7d742..739b79d 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -6,12 +6,13 @@ #include #include #include "util/ustring_clen.hpp" +#include "util/waybar_time.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include #endif -using waybar::modules::waybar_time; +using waybar::waybar_time; waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), @@ -227,14 +228,3 @@ auto waybar::modules::Clock::first_day_of_week() -> date::weekday { #endif return date::Sunday; } - -template <> -struct fmt::formatter : fmt::formatter { - template - auto format(const waybar_time& t, FormatContext& ctx) { -#if FMT_VERSION >= 80000 - auto& tm_format = specs; -#endif - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); - } -}; From 8c41aaae046eb014b9192679f9c707fc429dbb9b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 7 Jan 2022 18:59:08 -0800 Subject: [PATCH 28/66] refactor(test): use single executable --- test/SafeSignal.cpp | 6 ------ test/config.cpp | 1 - test/main.cpp | 26 ++++++++++++++++++++++++++ test/meson.build | 23 ++++++----------------- 4 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 test/main.cpp diff --git a/test/SafeSignal.cpp b/test/SafeSignal.cpp index 2c67317..244487f 100644 --- a/test/SafeSignal.cpp +++ b/test/SafeSignal.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_RUNNER #include "util/SafeSignal.hpp" #include @@ -138,8 +137,3 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thr producer.join(); REQUIRE(count == NUM_EVENTS); } - -int main(int argc, char* argv[]) { - Glib::init(); - return Catch::Session().run(argc, argv); -} diff --git a/test/config.cpp b/test/config.cpp index edd6d6b..29b0502 100644 --- a/test/config.cpp +++ b/test/config.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_MAIN #include "config.hpp" #include diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..bbfcc9f --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,26 @@ +#define CATCH_CONFIG_RUNNER +#include +#include +#include + +#include +#include +#include + +int main(int argc, char* argv[]) { + Catch::Session session; + Glib::init(); + + session.applyCommandLine(argc, argv); + const auto logger = spdlog::default_logger(); + const auto& reporter_name = session.config().getReporterName(); + if (reporter_name == "tap") { + spdlog::set_pattern("# [%l] %v"); + } else if (reporter_name == "compact") { + logger->sinks().clear(); + } else { + logger->sinks().assign({std::make_shared()}); + } + + return session.run(); +} diff --git a/test/meson.build b/test/meson.build index bbef21e..473a626 100644 --- a/test/meson.build +++ b/test/meson.build @@ -7,29 +7,18 @@ test_dep = [ spdlog, ] -config_test = executable( - 'config_test', +waybar_test = executable( + 'waybar_test', + 'main.cpp', + 'SafeSignal.cpp', 'config.cpp', '../src/config.cpp', dependencies: test_dep, include_directories: test_inc, ) -safesignal_test = executable( - 'safesignal_test', - 'SafeSignal.cpp', - dependencies: test_dep, - include_directories: test_inc, -) - test( - 'Configuration test', - config_test, - workdir: meson.source_root(), -) - -test( - 'SafeSignal test', - safesignal_test, + 'waybar', + waybar_test, workdir: meson.source_root(), ) From 766c8a103533828346577eab4be21aca49a83094 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 7 Jan 2022 19:32:18 -0800 Subject: [PATCH 29/66] test: add tests for waybar_time formatter --- test/meson.build | 14 +++++-- test/waybar_time.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 test/waybar_time.cpp diff --git a/test/meson.build b/test/meson.build index 473a626..b1e1123 100644 --- a/test/meson.build +++ b/test/meson.build @@ -6,13 +6,21 @@ test_dep = [ jsoncpp, spdlog, ] - -waybar_test = executable( - 'waybar_test', +test_src = files( 'main.cpp', 'SafeSignal.cpp', 'config.cpp', '../src/config.cpp', +) + +if tz_dep.found() + test_dep += tz_dep + test_src += files('waybar_time.cpp') +endif + +waybar_test = executable( + 'waybar_test', + test_src, dependencies: test_dep, include_directories: test_inc, ) diff --git a/test/waybar_time.cpp b/test/waybar_time.cpp new file mode 100644 index 0000000..5fc3312 --- /dev/null +++ b/test/waybar_time.cpp @@ -0,0 +1,90 @@ +#include "util/waybar_time.hpp" + +#include +#include + +#include +#include +#include + +using namespace std::literals::chrono_literals; + +/* + * Check that the date/time formatter with locale and timezone support is working as expected. + */ + +const date::zoned_time TEST_TIME = date::make_zoned( + "UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s); + +TEST_CASE("Format UTC time", "[clock][util]") { + waybar::waybar_time tm{std::locale("C"), TEST_TIME}; + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + try { + tm.locale = std::locale("en_US"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM")); + REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } + SECTION("GB locale") { + try { + tm.locale = std::locale("en_GB"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05")); + REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } +} + +TEST_CASE("Format zoned time", "[clock][util]") { + waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)}; + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + + /* Test a few locales that are most likely to be present */ + SECTION("US locale") { + try { + tm.locale = std::locale("en_US"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM")); + REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } + + SECTION("GB locale") { + try { + tm.locale = std::locale("en_GB"); + + REQUIRE(fmt::format("{}", tm).empty()); // no format specified + REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704 + Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05")); + REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05"); + REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405"); + } catch (const std::runtime_error&) { + // locale not found; ignore + } + } +} From 1489a539f85a177d5efe78a292fabfe9758a2f48 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 7 Jan 2022 22:09:46 -0800 Subject: [PATCH 30/66] chore: bump supported fmt ver to 7.0.0 Certain features we use were added only in 7.0 and the code no longer compiles with any earlier versions. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 2c43426..1ccf634 100644 --- a/meson.build +++ b/meson.build @@ -79,7 +79,7 @@ is_netbsd = host_machine.system() == 'netbsd' is_openbsd = host_machine.system() == 'openbsd' thread_dep = dependency('threads') -fmt = dependency('fmt', version : ['>=5.3.0'], fallback : ['fmt', 'fmt_dep']) +fmt = dependency('fmt', version : ['>=7.0.0'], fallback : ['fmt', 'fmt_dep']) spdlog = dependency('spdlog', version : ['>=1.8.5'], fallback : ['spdlog', 'spdlog_dep'], default_options : ['external_fmt=true']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') From 7f6bef20496ca23e83692801f6ff2957d28dde76 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 7 Jan 2022 22:25:15 -0800 Subject: [PATCH 31/66] fix(util): make waybar_time formatter compatible with fmt 8.1.0 Stop using private implementation details of the `formatter`. We never needed anything from the class besides the format specifier, which is easily obtainable with public API. --- include/util/waybar_time.hpp | 32 +++++++++++++++++++++----------- src/modules/clock.cpp | 8 +++++++- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/include/util/waybar_time.hpp b/include/util/waybar_time.hpp index 3eaf114..c74e58c 100644 --- a/include/util/waybar_time.hpp +++ b/include/util/waybar_time.hpp @@ -1,12 +1,7 @@ #pragma once -#include -#if FMT_VERSION < 60000 -#include -#else -#include -#endif #include +#include namespace waybar { @@ -18,12 +13,27 @@ struct waybar_time { } // namespace waybar template <> -struct fmt::formatter : fmt::formatter { +struct fmt::formatter { + std::string_view specs; + + template + constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') { + ++it; + } + auto end = it; + while (end != ctx.end() && *end != '}') { + ++end; + } + if (end != it) { + specs = {it, std::string_view::size_type(end - it)}; + } + return end; + } + template auto format(const waybar::waybar_time& t, FormatContext& ctx) { -#if FMT_VERSION >= 80000 - auto& tm_format = specs; -#endif - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); + return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime)); } }; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 739b79d..b0a6776 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,10 +1,16 @@ #include "modules/clock.hpp" -#include #include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include #include #include + #include "util/ustring_clen.hpp" #include "util/waybar_time.hpp" #ifdef HAVE_LANGINFO_1STDAY From ce404199de0fb6cea8ff59cb8546c6669949a311 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 7 Jan 2022 22:50:23 -0800 Subject: [PATCH 32/66] chore: add `tzdata` to the alpine builder Fixes date formatting test execution on alpine. --- Dockerfiles/alpine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/alpine b/Dockerfiles/alpine index c0e032f..03836aa 100644 --- a/Dockerfiles/alpine +++ b/Dockerfiles/alpine @@ -2,4 +2,4 @@ FROM alpine:latest -RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon +RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata From 5c574975b733aa7e9c0ca347c77eaf3bc6b1155b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sun, 9 Jan 2022 10:30:15 +0000 Subject: [PATCH 33/66] resources: don't break tray icon checkboxes via CSS These three lines break checkboxes and other forms of UI in status indicator dropdowns. For instance, they break checkboxes on NetworkManager's "nm-applet --indicator" via libappindicator-gtk3. First, disabling borders completely hides those UI elements, as they seem to render entirely via borders. Second, min-height makes checkboxes just flat lines. When removed entirely, the border settings seem to have had an effect on the workspaces widget, which now renders with round underline borders. To undo that, re-add those two lines inside its section. The min-height setting doesn't seem to affect anything that I can see. Remove it entirely, for now. Fixes #1148. --- resources/style.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/style.css b/resources/style.css index 0235942..3bc7052 100644 --- a/resources/style.css +++ b/resources/style.css @@ -1,10 +1,7 @@ * { - border: none; - border-radius: 0; /* `otf-font-awesome` is required to be installed for icons */ font-family: Roboto, Helvetica, Arial, sans-serif; font-size: 13px; - min-height: 0; } window#waybar { @@ -43,6 +40,9 @@ window#waybar.chromium { color: #ffffff; /* Use box-shadow instead of border so the text isn't offset */ box-shadow: inset 0 -3px transparent; + /* Avoid rounded borders under each workspace name */ + border: none; + border-radius: 0; } /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ From 1540797cd93bd91517b7fb28b2a129cb8938381d Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 10 Jan 2022 09:58:29 +0100 Subject: [PATCH 34/66] chore: v0.9.9 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1ccf634..2c644cc 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.8', + version: '0.9.9', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From 5abdecc402ed5de5c7224d8d3a878d25f97a0e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Nogueira?= Date: Tue, 11 Jan 2022 17:28:17 -0300 Subject: [PATCH 35/66] fix(man): add missing newlines to wlr docs. --- man/waybar-wlr-taskbar.5.scd | 5 +++++ man/waybar-wlr-workspaces.5.scd | 1 + 2 files changed, 6 insertions(+) diff --git a/man/waybar-wlr-taskbar.5.scd b/man/waybar-wlr-taskbar.5.scd index 40be7be..b2946ac 100644 --- a/man/waybar-wlr-taskbar.5.scd +++ b/man/waybar-wlr-taskbar.5.scd @@ -93,10 +93,15 @@ Addressed by *wlr/taskbar* # CLICK ACTIONS *activate*: Bring the application into foreground. + *minimize*: Toggle application's minimized state. + *minimize-raise*: Bring the application into foreground or toggle its minimized state. + *maximize*: Toggle application's maximized state. + *fullscreen*: Toggle application's fullscreen state. + *close*: Close the application. # EXAMPLES diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index f0df5e9..5d7b2ac 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -52,6 +52,7 @@ Addressed by *wlr/workspaces* # CLICK ACTIONS *activate*: Switch to workspace. + *close*: Close the workspace. # ICONS From 3a6e5be59d2839f9187a88c6449ff64f74e11f8e Mon Sep 17 00:00:00 2001 From: nullobsi Date: Fri, 14 Jan 2022 10:36:24 -0800 Subject: [PATCH 36/66] Use locale when formatting clock --- src/modules/clock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index b0a6776..6aa5afb 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -99,7 +99,7 @@ auto waybar::modules::Clock::update() -> void { // As date dep is not fully compatible, prefer fmt tzset(); auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); - text = fmt::format(format_, localtime); + text = fmt::format(locale_, format_, localtime); } else { text = fmt::format(format_, wtime); } From 27df7a9aa7c76de901ad3ad8f9c5fcb1b3af3d8d Mon Sep 17 00:00:00 2001 From: Kevin Locke Date: Sun, 23 Jan 2022 18:36:59 -0700 Subject: [PATCH 37/66] battery: read status with spaces According to the [sysfs class power ABI], /sys/class/power_supply//status may contain "Not charging". This is already handled by status_gt() and update() (where ' ' is converted to '-' for use in config keys) but was not being read due to skipws. Read with std::getline() to handle this case. [sysfs class power ABI]: https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power Fixes: #1139 Signed-off-by: Kevin Locke --- src/modules/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 2656769..c0075a4 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -161,7 +161,7 @@ const std::tuple waybar::modules::Battery::g uint32_t energy_now; uint32_t energy_full_design; std::string _status; - std::ifstream(bat / "status") >> _status; + std::getline(std::ifstream(bat / "status"), _status); // Some battery will report current and charge in μA/μAh. // Scale these by the voltage to get μW/μWh. From 667d0a45f46d0b2fc34b300e639b035d7c091345 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 Jan 2022 09:20:43 +0100 Subject: [PATCH 38/66] Update command.hpp --- include/util/command.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/util/command.hpp b/include/util/command.hpp index 9c4305a..5f398e1 100644 --- a/include/util/command.hpp +++ b/include/util/command.hpp @@ -69,6 +69,8 @@ inline FILE* open(const std::string& cmd, int& pid) { if (cmd == "") return nullptr; int fd[2]; if (pipe(fd) != 0){ + spdlog::error("Unable to pipe fd"); + return nullptr; } pid_t child_pid = fork(); From 65dd245362fb203942b4a5a968e755a33ed880a6 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 Jan 2022 09:21:56 +0100 Subject: [PATCH 39/66] Update clock.cpp --- src/modules/clock.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 96fb34a..8a0254d 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -233,9 +233,8 @@ struct fmt::formatter : fmt::formatter { template auto format(const waybar_time& t, FormatContext& ctx) { #if FMT_VERSION >= 80000 -//auto& tm_format = specs; + auto& tm_format = specs; #endif -// return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); - return fmt::formatter::format((const std::tm&) t, ctx); + return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime)); } }; From c4cc7ae396b8f040522ec3fcf76b9f9fbd1c65d4 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 Jan 2022 09:22:47 +0100 Subject: [PATCH 40/66] Update client.cpp --- src/modules/sway/ipc/client.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index ea1a3c5..58aed60 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -14,15 +14,12 @@ Ipc::~Ipc() { if (fd_ > 0) { // To fail the IPC header - if (write(fd_, "close-sway-ipc", 14) == -1) { - //std::runtime_error("Couldn't close Sway IPC through fd"); - } + write(fd_, "close-sway-ipc", 14); close(fd_); fd_ = -1; } if (fd_event_ > 0) { - if (write(fd_event_, "close-sway-ipc", 14) == -1) { - } + write(fd_event_, "close-sway-ipc", 14); close(fd_event_); fd_event_ = -1; } From bcadf64031ee0520212aa8f092f5ac14122cd924 Mon Sep 17 00:00:00 2001 From: Felix Weilbach Date: Sun, 17 Oct 2021 20:08:16 +0200 Subject: [PATCH 41/66] Show application icon when using sway window module Signed-off-by: Felix Weilbach --- include/AIconLabel.hpp | 25 ++++++++++++++ include/modules/sway/window.hpp | 6 ++-- meson.build | 1 + src/AIconLabel.cpp | 28 +++++++++++++++ src/modules/sway/window.cpp | 61 +++++++++++++++++++++++++++++++-- 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 include/AIconLabel.hpp create mode 100644 src/AIconLabel.cpp diff --git a/include/AIconLabel.hpp b/include/AIconLabel.hpp new file mode 100644 index 0000000..3eac7dd --- /dev/null +++ b/include/AIconLabel.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "ALabel.hpp" + +namespace waybar { + +class AIconLabel : public ALabel { + public: + AIconLabel(const Json::Value &config, const std::string &name, const std::string &id, + const std::string &format, uint16_t interval = 0, bool ellipsize = false, + bool enable_click = false, bool enable_scroll = false); + virtual ~AIconLabel() = default; + virtual auto update() -> void; + + protected: + Gtk::Image image_; + Gtk::Box box_; + + bool iconEnabled() const; +}; + +} // namespace waybar diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index 0f7ae31..0ab129e67 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -2,7 +2,8 @@ #include #include -#include "ALabel.hpp" + +#include "AIconLabel.hpp" #include "bar.hpp" #include "client.hpp" #include "modules/sway/ipc/client.hpp" @@ -10,7 +11,7 @@ namespace waybar::modules::sway { -class Window : public ALabel, public sigc::trackable { +class Window : public AIconLabel, public sigc::trackable { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); ~Window() = default; @@ -23,6 +24,7 @@ class Window : public ALabel, public sigc::trackable { std::string& output); void getTree(); std::string rewriteTitle(const std::string& title); + void updateAppIcon(); const Bar& bar_; std::string window_; diff --git a/meson.build b/meson.build index 2c644cc..ba5a69d 100644 --- a/meson.build +++ b/meson.build @@ -142,6 +142,7 @@ src_files = files( 'src/factory.cpp', 'src/AModule.cpp', 'src/ALabel.cpp', + 'src/AIconLabel.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', diff --git a/src/AIconLabel.cpp b/src/AIconLabel.cpp new file mode 100644 index 0000000..5bc36e4 --- /dev/null +++ b/src/AIconLabel.cpp @@ -0,0 +1,28 @@ +#include "AIconLabel.hpp" + +#include + +namespace waybar { + +AIconLabel::AIconLabel(const Json::Value &config, const std::string &name, const std::string &id, + const std::string &format, uint16_t interval, bool ellipsize, + bool enable_click, bool enable_scroll) + : ALabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { + event_box_.remove(); + box_.set_orientation(Gtk::Orientation::ORIENTATION_HORIZONTAL); + box_.set_spacing(8); + box_.add(image_); + box_.add(label_); + event_box_.add(box_); +} + +auto AIconLabel::update() -> void { + image_.set_visible(image_.get_visible() && iconEnabled()); + ALabel::update(); +} + +bool AIconLabel::iconEnabled() const { + return config_["icon"].isBool() ? config_["icon"].asBool() : true; +} + +} // namespace waybar diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index fc81b2c..00418a1 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -1,11 +1,20 @@ #include "modules/sway/window.hpp" + +#include +#include +#include +#include +#include #include + +#include #include +#include namespace waybar::modules::sway { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { + : AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { ipc_.subscribe(R"(["window","workspace"])"); ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent)); ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd)); @@ -29,12 +38,60 @@ void Window::onCmd(const struct Ipc::ipc_response& res) { auto payload = parser_.parse(res.payload); auto output = payload["output"].isString() ? payload["output"].asString() : ""; std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output); + updateAppIcon(); dp.emit(); } catch (const std::exception& e) { spdlog::error("Window: {}", e.what()); } } +std::optional getDesktopFilePath(const std::string& app_id) { + const auto data_dirs = Glib::get_system_data_dirs(); + for (const auto& data_dir : data_dirs) { + const auto desktop_file_path = data_dir + "applications/" + app_id + ".desktop"; + if (std::filesystem::exists(desktop_file_path)) { + return desktop_file_path; + } + } + return {}; +} + +std::optional getIconName(const std::string& app_id) { + const auto desktop_file_path = getDesktopFilePath(app_id); + if (!desktop_file_path.has_value()) { + return {}; + } + try { + Glib::KeyFile desktop_file; + desktop_file.load_from_file(desktop_file_path.value()); + const auto icon_name = desktop_file.get_string("Desktop Entry", "Icon"); + if (icon_name.empty()) { + return {}; + } + return icon_name; + } catch (Glib::FileError& error) { + spdlog::warn( + "Error while loading desktop file {}: {}", desktop_file_path.value(), error.what().c_str()); + } catch (Glib::KeyFileError& error) { + spdlog::warn( + "Error while loading desktop file {}: {}", desktop_file_path.value(), error.what().c_str()); + } + return {}; +} + +void Window::updateAppIcon() { + if (!iconEnabled()) { + return; + } + const auto icon_name = getIconName(app_id_); + if (icon_name.has_value()) { + image_.set_from_icon_name(icon_name.value(), Gtk::ICON_SIZE_LARGE_TOOLBAR); + image_.set_visible(true); + return; + } + image_.set_visible(false); +} + auto Window::update() -> void { if (!old_app_id_.empty()) { bar_.window.get_style_context()->remove_class(old_app_id_); @@ -63,7 +120,7 @@ auto Window::update() -> void { label_.set_tooltip_text(window_); } // Call parent update - ALabel::update(); + AIconLabel::update(); } int leafNodesInWorkspace(const Json::Value& node) { From f4f1267a717fec82bdd97d83101d3e5f9b8b9bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Tue, 25 Jan 2022 20:03:19 +0100 Subject: [PATCH 42/66] sway/language: fix segmentation fault for layouts not found in XKBContext --- src/modules/sway/language.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/sway/language.cpp b/src/modules/sway/language.cpp index 73a64c3..f64c7d0 100644 --- a/src/modules/sway/language.cpp +++ b/src/modules/sway/language.cpp @@ -154,7 +154,10 @@ auto Language::init_layouts_map(const std::vector& used_layouts) -> std::map short_name_to_number_map; for (const auto& used_layout_name : used_layouts) { - auto used_layout = &layouts_map_.find(used_layout_name)->second; + auto found = layouts_map_.find(used_layout_name); + if (found == layouts_map_.end()) + continue; + auto used_layout = &found->second; auto layouts_with_same_name_list = found_by_short_names[used_layout->short_name]; if (layouts_with_same_name_list.size() < 2) { continue; From 32d42749f9b4c44fb0b8b46b2236884c4198b594 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 28 Jan 2022 18:42:52 +0100 Subject: [PATCH 43/66] use pack_end instead of pack_start --- src/modules/sni/tray.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index c32c0d6..4606857 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -25,7 +25,7 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) } void Tray::onAdd(std::unique_ptr& item) { - box_.pack_start(item->event_box); + box_.pack_end(item->event_box); dp.emit(); } From 941cf47693ad9ea48054634f30684691c352247f Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 28 Jan 2022 19:14:46 +0100 Subject: [PATCH 44/66] Add config option "reverse-direction" --- man/waybar-tray.5.scd | 4 ++++ src/modules/sni/tray.cpp | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/man/waybar-tray.5.scd b/man/waybar-tray.5.scd index c664594..bfd1f29 100644 --- a/man/waybar-tray.5.scd +++ b/man/waybar-tray.5.scd @@ -29,6 +29,10 @@ Addressed by *tray* typeof: integer ++ Defines the spacing between the tray icons. +*reverse-direction*: ++ + typeof: bool ++ + Defines if new app icons should be added in a reverse order + *on-update*: ++ typeof: string ++ Command to execute when the module is updated. diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 4606857..94120be 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -25,7 +25,11 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config) } void Tray::onAdd(std::unique_ptr& item) { - box_.pack_end(item->event_box); + if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) { + box_.pack_end(item->event_box); + } else { + box_.pack_start(item->event_box); + } dp.emit(); } From a0ee9e7fc1ec8a381926f824c072ad7cbb29552c Mon Sep 17 00:00:00 2001 From: snehrbass <62710600+snehrbass@users.noreply.github.com> Date: Sat, 29 Jan 2022 14:19:53 -0500 Subject: [PATCH 45/66] Fix Typo in Example for sway/mode --- man/waybar-sway-mode.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-sway-mode.5.scd b/man/waybar-sway-mode.5.scd index b8b59cd..29eed4f 100644 --- a/man/waybar-sway-mode.5.scd +++ b/man/waybar-sway-mode.5.scd @@ -69,7 +69,7 @@ Addressed by *sway/mode* # EXAMPLES ``` -"sway/window": { +"sway/mode": { "format": " {}", "max-length": 50 } From 895bc878f8773cb974bdc73f2ef531f4755e416b Mon Sep 17 00:00:00 2001 From: Evyatar Stalinsky Date: Tue, 1 Feb 2022 22:42:03 +0200 Subject: [PATCH 46/66] Properly initialize distance_scrolled members. When they were uninitialized it caused getScrollDir() to incorrectly return SCROLL_DIR::NONE in some circumstances --- src/AModule.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AModule.cpp b/src/AModule.cpp index 7da942e..0e0f00e 100644 --- a/src/AModule.cpp +++ b/src/AModule.cpp @@ -6,7 +6,9 @@ namespace waybar { AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id, bool enable_click, bool enable_scroll) - : name_(std::move(name)), config_(std::move(config)) { + : name_(std::move(name)), config_(std::move(config)) + , distance_scrolled_y_(0.0) + , distance_scrolled_x_(0.0) { // configure events' user commands if (config_["on-click"].isString() || config_["on-click-middle"].isString() || config_["on-click-backward"].isString() || config_["on-click-forward"].isString() || From c80cc873afc0251937980d89c4f7fb2268a43bb0 Mon Sep 17 00:00:00 2001 From: snehrbass <62710600+snehrbass@users.noreply.github.com> Date: Wed, 2 Feb 2022 00:01:18 -0500 Subject: [PATCH 47/66] Update spotify example to work on copy paste --- man/waybar-custom.5.scd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/man/waybar-custom.5.scd b/man/waybar-custom.5.scd index 8f9dcfa..3d30859 100644 --- a/man/waybar-custom.5.scd +++ b/man/waybar-custom.5.scd @@ -151,7 +151,8 @@ $text\\n$tooltip\\n$class* "max-length": 40, "interval": 30, // Remove this if your script is endless and write in loop "exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder - "exec-if": "pgrep spotify" + "exec-if": "pgrep spotify", + "return-type": "json" } ``` From 6be741afc91c737b973533be6abbb366a7d3adf5 Mon Sep 17 00:00:00 2001 From: gemmelos <73130646+gemmelos@users.noreply.github.com> Date: Wed, 2 Feb 2022 12:41:06 +0100 Subject: [PATCH 48/66] Fix disabled tooltip on clock module Seems like the tooltip text should only be set if both `if (tooltipEnabled())` and `if (config_["tooltip-format"].isString())` passes. --- src/modules/clock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 6aa5afb..acb90b7 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -113,10 +113,10 @@ auto waybar::modules::Clock::update() -> void { } auto tooltip_format = config_["tooltip-format"].asString(); text = fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines)); + label_.set_tooltip_markup(text); } } - label_.set_tooltip_markup(text); // Call parent update ALabel::update(); } From 6c188455a4ca75fbb11cd07de0feb19cc5d1341a Mon Sep 17 00:00:00 2001 From: gemmelos <73130646+gemmelos@users.noreply.github.com> Date: Sat, 5 Feb 2022 19:05:50 +0100 Subject: [PATCH 49/66] Update man page clock module - tooltip Add tooltip documentation to the clock man page now that this is works. --- man/waybar-clock.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar-clock.5.scd b/man/waybar-clock.5.scd index 2c901d2..265e739 100644 --- a/man/waybar-clock.5.scd +++ b/man/waybar-clock.5.scd @@ -86,6 +86,11 @@ The *clock* module displays the current date and time. typeof: double ++ Threshold to be used when scrolling. +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + View all valid format options in *strftime(3)*. # FORMAT REPLACEMENTS From 061cb76fc48f493b187928f841083981e9733a28 Mon Sep 17 00:00:00 2001 From: Alex Maystrenko Date: Sat, 5 Feb 2022 20:47:29 +0100 Subject: [PATCH 50/66] make functions in header file inline added header guard now string utils can be used in any part of the project --- include/util/string.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/util/string.hpp b/include/util/string.hpp index d644b4c..c8c0209 100644 --- a/include/util/string.hpp +++ b/include/util/string.hpp @@ -1,15 +1,17 @@ +#pragma once + #include const std::string WHITESPACE = " \n\r\t\f\v"; -std::string ltrim(const std::string s) { +inline std::string ltrim(const std::string s) { size_t begin = s.find_first_not_of(WHITESPACE); return (begin == std::string::npos) ? "" : s.substr(begin); } -std::string rtrim(const std::string s) { +inline std::string rtrim(const std::string s) { size_t end = s.find_last_not_of(WHITESPACE); return (end == std::string::npos) ? "" : s.substr(0, end + 1); } -std::string trim(const std::string& s) { return rtrim(ltrim(s)); } +inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); } From 2697d432a4c271f1cd6b701799842688c0e4f85e Mon Sep 17 00:00:00 2001 From: Alex Maystrenko Date: Sat, 5 Feb 2022 20:54:06 +0100 Subject: [PATCH 51/66] ltrim and rtrim take argument by const-ref --- include/util/string.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/util/string.hpp b/include/util/string.hpp index c8c0209..24a9b2b 100644 --- a/include/util/string.hpp +++ b/include/util/string.hpp @@ -4,12 +4,12 @@ const std::string WHITESPACE = " \n\r\t\f\v"; -inline std::string ltrim(const std::string s) { +inline std::string ltrim(const std::string& s) { size_t begin = s.find_first_not_of(WHITESPACE); return (begin == std::string::npos) ? "" : s.substr(begin); } -inline std::string rtrim(const std::string s) { +inline std::string rtrim(const std::string& s) { size_t end = s.find_last_not_of(WHITESPACE); return (end == std::string::npos) ? "" : s.substr(0, end + 1); } From f3819ee9549dd54b60168cf3b9ff84ac195d3542 Mon Sep 17 00:00:00 2001 From: Alex Maystrenko Date: Sat, 5 Feb 2022 21:01:07 +0100 Subject: [PATCH 52/66] remove duplicated string manipulation methods --- src/modules/wlr/taskbar.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index ddc360b..55c9431 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -4,6 +4,7 @@ #include "glibmm/fileutils.h" #include "glibmm/refptr.h" #include "util/format.hpp" +#include "util/string.hpp" #include #include @@ -26,27 +27,6 @@ namespace waybar::modules::wlr { -/* String manipulation methods */ -const std::string WHITESPACE = " \n\r\t\f\v"; - -static std::string ltrim(const std::string& s) -{ - size_t start = s.find_first_not_of(WHITESPACE); - return (start == std::string::npos) ? "" : s.substr(start); -} - -static std::string rtrim(const std::string& s) -{ - size_t end = s.find_last_not_of(WHITESPACE); - return (end == std::string::npos) ? "" : s.substr(0, end + 1); -} - -static std::string trim(const std::string& s) -{ - return rtrim(ltrim(s)); -} - - /* Icon loading functions */ static std::vector search_prefix() { From d4da04a7507177f702aee7c5e58daa6788d00fd5 Mon Sep 17 00:00:00 2001 From: Alex Maystrenko Date: Sat, 5 Feb 2022 21:19:55 +0100 Subject: [PATCH 53/66] move Glib ustring format helper to utils this formatter is useful for other modules which want to print Glib exceptions messages --- include/util/format.hpp | 11 +++++++++++ src/modules/sni/item.cpp | 8 +------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index 543a100..48f0f51 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include class pow_format { public: @@ -84,5 +85,15 @@ namespace fmt { ); } }; + + + // Glib ustirng support + template <> + struct formatter : formatter { + template + auto format(const Glib::ustring& value, FormatContext& ctx) { + return formatter::format(value, ctx); + } + }; } diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index b504c8d..74987ca 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -8,13 +8,7 @@ #include #include -template <> -struct fmt::formatter : formatter { - template - auto format(const Glib::ustring& value, FormatContext& ctx) { - return formatter::format(value, ctx); - } -}; +#include "util/format.hpp" template <> struct fmt::formatter : formatter { From 1a93a6cfa593093868ac684831e0072b3dacc46a Mon Sep 17 00:00:00 2001 From: Dorian Rudolph Date: Wed, 9 Feb 2022 14:20:09 +0100 Subject: [PATCH 54/66] fix #1431: audio output does not update on unplug --- src/modules/pulseaudio.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 3c32060..b1f7830 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -54,7 +54,9 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) { c, static_cast(static_cast(PA_SUBSCRIPTION_MASK_SERVER) | static_cast(PA_SUBSCRIPTION_MASK_SINK) | - static_cast(PA_SUBSCRIPTION_MASK_SOURCE)), + static_cast(PA_SUBSCRIPTION_MASK_SINK_INPUT) | + static_cast(PA_SUBSCRIPTION_MASK_SOURCE) | + static_cast(PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT)), nullptr, nullptr); break; @@ -121,8 +123,12 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte pa_context_get_server_info(context, serverInfoCb, data); } else if (facility == PA_SUBSCRIPTION_EVENT_SINK) { pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data); + } else if (facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { + pa_context_get_sink_info_list(context, sinkInfoCb, data); } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) { pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data); + } else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) { + pa_context_get_source_info_list(context, sourceInfoCb, data); } } @@ -279,7 +285,7 @@ auto waybar::modules::Pulseaudio::update() -> void { fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPulseIcon())))); getState(volume_); - + if (tooltipEnabled()) { if (tooltip_format.empty() && config_["tooltip-format"].isString()) { tooltip_format = config_["tooltip-format"].asString(); From fc818dd7941ac6f4fe57784a1d2f073caf801347 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Sun, 13 Feb 2022 11:12:52 +0000 Subject: [PATCH 55/66] ci: update freebsd-vm to 0.1.6 --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index d5064fe..1e420fd 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Test in FreeBSD VM - uses: vmactions/freebsd-vm@v0.1.5 # aka FreeBSD 13.0 + uses: vmactions/freebsd-vm@v0.1.6 # aka FreeBSD 13.0 with: mem: 2048 usesh: true From 851508df5e5d54607ef85f47021fa13b9ae8c142 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Sun, 13 Feb 2022 11:14:01 +0000 Subject: [PATCH 56/66] ci: explicitly install wayland-protocols on FreeBSD meson.build:86:0: ERROR: Dependency "wayland-protocols" not found, tried pkgconfig --- .github/workflows/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 1e420fd..f3c107a 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -21,7 +21,7 @@ jobs: pkg install -y git # subprojects/date pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \ libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \ - pkgconf pulseaudio scdoc sndio spdlog + pkgconf pulseaudio scdoc sndio spdlog wayland-protocols run: | meson build -Dman-pages=enabled ninja -C build From ac20428fdf265aa56565f720190f7cd525d638e2 Mon Sep 17 00:00:00 2001 From: TheDaemoness Date: Fri, 18 Feb 2022 17:41:33 -0800 Subject: [PATCH 57/66] Add 1 to songPosition in the MPD module --- src/modules/mpd/mpd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 6d27286..e5cafc0 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -129,7 +129,7 @@ void waybar::modules::MPD::setLabel() { album = getTag(MPD_TAG_ALBUM); title = getTag(MPD_TAG_TITLE); date = getTag(MPD_TAG_DATE); - song_pos = mpd_status_get_song_pos(status_.get()); + song_pos = mpd_status_get_song_pos(status_.get()) + 1; volume = mpd_status_get_volume(status_.get()); if (volume < 0) { volume = 0; From 3c18c43b9a5318df8e18e4d25d25250722fdf688 Mon Sep 17 00:00:00 2001 From: Brent George Date: Tue, 22 Feb 2022 16:33:33 -0700 Subject: [PATCH 58/66] limit cpu load figure to 2 points --- src/modules/cpu/common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/cpu/common.cpp b/src/modules/cpu/common.cpp index 4cf67eb..903b3bd 100644 --- a/src/modules/cpu/common.cpp +++ b/src/modules/cpu/common.cpp @@ -62,7 +62,7 @@ auto waybar::modules::Cpu::update() -> void { double waybar::modules::Cpu::getCpuLoad() { double load[1]; if (getloadavg(load, 1) != -1) { - return load[0]; + return std::ceil(load[0] * 100.0) / 100.0; } throw std::runtime_error("Can't get Cpu load"); } From a595b61e0fc1069b8d577c36e9c6f5c65911c3f1 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Fri, 25 Feb 2022 12:22:55 -0500 Subject: [PATCH 59/66] Improve keyboard_state error messages --- include/modules/keyboard_state.hpp | 2 - man/waybar-keyboard-state.5.scd | 2 + src/modules/keyboard_state.cpp | 119 ++++++++++++++++++++--------- 3 files changed, 85 insertions(+), 38 deletions(-) diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index 1793bfe..ed137c1 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -24,8 +24,6 @@ class KeyboardState : public AModule { auto update() -> void; private: - static auto openDevice(const std::string&) -> std::pair; - Gtk::Box box_; Gtk::Label numlock_label_; Gtk::Label capslock_label_; diff --git a/man/waybar-keyboard-state.5.scd b/man/waybar-keyboard-state.5.scd index 1d7c3a8..94db07d 100644 --- a/man/waybar-keyboard-state.5.scd +++ b/man/waybar-keyboard-state.5.scd @@ -8,6 +8,8 @@ waybar - keyboard-state module The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock. +You must be a member of the input group to use this module. + # CONFIGURATION *interval*: ++ diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 2b6eb2d..7813c3b 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -1,6 +1,8 @@ #include "modules/keyboard_state.hpp" +#include #include #include +#include extern "C" { #include @@ -8,6 +10,63 @@ extern "C" { #include } +class errno_error : public std::runtime_error { + public: + int code; + errno_error(int code, const std::string& msg) + : std::runtime_error(getErrorMsg(code, msg.c_str())), + code(code) {} + errno_error(int code, const char* msg) + : std::runtime_error(getErrorMsg(code, msg)), + code(code) {} + private: + static auto getErrorMsg(int err, const char* msg) -> std::string { + const auto errno_name = strerrorname_np(err); + const auto errno_str = strerror(err); + std::string error_msg{msg}; + error_msg += ": "; + error_msg += errno_name; + error_msg += " "; + error_msg += errno_str; + return error_msg; + } +}; + +auto openFile(const std::string& path, int flags) -> int { + int fd = open(path.c_str(), flags); + if (fd < 0) { + if (errno == EACCES) { + throw errno_error(errno, "Can't open " + path + " (are you in the input group?)"); + } else { + throw errno_error(errno, "Can't open " + path); + } + } + return fd; +} + +auto closeFile(int fd) -> void { + int res = close(fd); + if (res < 0) { + throw errno_error(errno, "Can't close file"); + } +} + +auto openDevice(int fd) -> libevdev* { + libevdev* dev; + int err = libevdev_new_from_fd(fd, &dev); + if (err < 0) { + throw errno_error(-err, "Can't create libevdev device"); + } + return dev; +} + +auto supportsLockStates(const libevdev* dev) -> bool { + return libevdev_has_event_type(dev, EV_LED) + && libevdev_has_event_code(dev, EV_LED, LED_NUML) + && libevdev_has_event_code(dev, EV_LED, LED_CAPSL) + && libevdev_has_event_code(dev, EV_LED, LED_SCROLLL); +} + waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) : AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), @@ -48,26 +107,36 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& if (config_["device-path"].isString()) { std::string dev_path = config_["device-path"].asString(); - std::tie(fd_, dev_) = openDevice(dev_path); + fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); + dev_ = openDevice(fd_); } else { DIR* dev_dir = opendir("/dev/input"); if (dev_dir == nullptr) { - throw std::runtime_error("Failed to open /dev/input"); + throw errno_error(errno, "Failed to open /dev/input"); } dirent *ep; while ((ep = readdir(dev_dir))) { if (ep->d_type != DT_CHR) continue; std::string dev_path = std::string("/dev/input/") + ep->d_name; + int fd = openFile(dev_path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); try { - std::tie(fd_, dev_) = openDevice(dev_path); - spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path); - break; - } catch (const std::runtime_error& e) { - continue; + auto dev = openDevice(fd); + if (supportsLockStates(dev)) { + spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path); + fd_ = fd; + dev_ = dev; + break; + } + } catch (const errno_error& e) { + // ENOTTY just means the device isn't an evdev device, skip it + if (e.code != ENOTTY) { + spdlog::warn(e.what()); + } } + closeFile(fd); } if (dev_ == nullptr) { - throw std::runtime_error("Failed to find keyboard device"); + throw errno_error(errno, "Failed to find keyboard device"); } } @@ -79,35 +148,13 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& waybar::modules::KeyboardState::~KeyboardState() { libevdev_free(dev_); - int err = close(fd_); - if (err < 0) { - // Not much we can do, so ignore it. + try { + closeFile(fd_); + } catch (const std::runtime_error& e) { + spdlog::warn(e.what()); } } -auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair { - int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); - if (fd < 0) { - throw std::runtime_error("Can't open " + path); - } - - libevdev* dev; - int err = libevdev_new_from_fd(fd, &dev); - if (err < 0) { - throw std::runtime_error("Can't create libevdev device"); - } - if (!libevdev_has_event_type(dev, EV_LED)) { - throw std::runtime_error("Device doesn't support LED events"); - } - if (!libevdev_has_event_code(dev, EV_LED, LED_NUML) - || !libevdev_has_event_code(dev, EV_LED, LED_CAPSL) - || !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) { - throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events"); - } - - return std::make_pair(fd, dev); -} - auto waybar::modules::KeyboardState::update() -> void { int err = LIBEVDEV_READ_STATUS_SUCCESS; while (err == LIBEVDEV_READ_STATUS_SUCCESS) { @@ -117,8 +164,8 @@ auto waybar::modules::KeyboardState::update() -> void { err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev); } } - if (err != -EAGAIN) { - throw std::runtime_error("Failed to sync evdev device"); + if (-err != EAGAIN) { + throw errno_error(-err, "Failed to sync evdev device"); } int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); From 8f3fbebede0a0fcce632a4390aaf54d7572795c8 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Fri, 25 Feb 2022 12:56:22 -0500 Subject: [PATCH 60/66] Make error messages portable --- src/modules/keyboard_state.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 7813c3b..63871fd 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -21,13 +21,21 @@ class errno_error : public std::runtime_error { code(code) {} private: static auto getErrorMsg(int err, const char* msg) -> std::string { - const auto errno_name = strerrorname_np(err); - const auto errno_str = strerror(err); std::string error_msg{msg}; error_msg += ": "; + +#ifdef _GNU_SOURCE + // strerrorname_np gets the error code's name; it's nice to have, but it's a GNU extension + const auto errno_name = strerrorname_np(err); error_msg += errno_name; error_msg += " "; +#endif + + // strerror(3) man page says 1024 should be a big enough buffer to avoid ERANGE + char errno_str[1024] = {}; + strerror_r(err, errno_str, sizeof errno_str); error_msg += errno_str; + return error_msg; } }; From e3f56b8110fb6ebe4f895c65faaba57e1ad6a19c Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Fri, 25 Feb 2022 13:13:43 -0500 Subject: [PATCH 61/66] Don't use gnu extensions which are too new for debian and ubuntu --- src/modules/keyboard_state.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 63871fd..57defa7 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -24,13 +24,6 @@ class errno_error : public std::runtime_error { std::string error_msg{msg}; error_msg += ": "; -#ifdef _GNU_SOURCE - // strerrorname_np gets the error code's name; it's nice to have, but it's a GNU extension - const auto errno_name = strerrorname_np(err); - error_msg += errno_name; - error_msg += " "; -#endif - // strerror(3) man page says 1024 should be a big enough buffer to avoid ERANGE char errno_str[1024] = {}; strerror_r(err, errno_str, sizeof errno_str); From ebe442479545fbfe652656a13a7589a7d9e78c22 Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Fri, 25 Feb 2022 13:28:47 -0500 Subject: [PATCH 62/66] Fix remaining posix compatability issues in keyboard_state --- src/modules/keyboard_state.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 57defa7..1ac075d 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -24,9 +24,14 @@ class errno_error : public std::runtime_error { std::string error_msg{msg}; error_msg += ": "; - // strerror(3) man page says 1024 should be a big enough buffer to avoid ERANGE - char errno_str[1024] = {}; - strerror_r(err, errno_str, sizeof errno_str); +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 32) + // strerrorname_np gets the error code's name; it's nice to have, but it's a recent GNU extension + const auto errno_name = strerrorname_np(err); + error_msg += errno_name; + error_msg += " "; +#endif + + const auto errno_str = strerror(err); error_msg += errno_str; return error_msg; From adc67b6f75ec956a258962e24c2d64bc2601e5eb Mon Sep 17 00:00:00 2001 From: Xiretza Date: Thu, 3 Mar 2022 18:10:49 +0100 Subject: [PATCH 63/66] style.css: add fontawesome to beginning of font list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there is some other font installed that 1) matches the four existing font families and 2) provides its own glyph in the private use area used by Awesome, then that font's glyph will be used instead of the intended icon. For example, the following character (U+F001, "music"):  ...looks like a pair of musical notes in fontawesome, but DejaVu Sans also provides a glyph, which looks like a couple of squares. DejaVu Sans matches first when "sans-serif" is requested, so its (unrelated) glyph is used. --- resources/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/style.css b/resources/style.css index 3bc7052..563ee0d 100644 --- a/resources/style.css +++ b/resources/style.css @@ -1,6 +1,6 @@ * { /* `otf-font-awesome` is required to be installed for icons */ - font-family: Roboto, Helvetica, Arial, sans-serif; + font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif; font-size: 13px; } From 54085dbde0689b1956ef38d6478c85d7bc888531 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Thu, 3 Mar 2022 19:40:43 -0500 Subject: [PATCH 64/66] avoid use-after-free in lambda --- src/bar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bar.cpp b/src/bar.cpp index 9685c7a..eb00dd1 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -750,7 +750,7 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos, Gtk modules_right_.emplace_back(module_sp); } } - module->dp.connect([module, &name] { + module->dp.connect([module, name] { try { module->update(); } catch (const std::exception& e) { From 383d999fec4ccff37b57cecc8c99aa609df2e12b Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 6 Mar 2022 14:54:48 +0100 Subject: [PATCH 65/66] chore: v0.9.10 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ba5a69d..17a6bd5 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'waybar', 'cpp', 'c', - version: '0.9.9', + version: '0.9.10', license: 'MIT', meson_version: '>= 0.49.0', default_options : [ From 50fc63b749288d03e6d31fc6ea6e730a8357da08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Gro=C3=9F?= Date: Mon, 7 Mar 2022 14:53:53 +0100 Subject: [PATCH 66/66] man: document icon in sway/window module Default changed in bcadf64031ee0520212aa8f092f5ac14122cd924 and it wasn't documented. --- man/waybar-sway-window.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index ea06069..df13807 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -70,6 +70,11 @@ Addressed by *sway/window* typeof: object ++ Rules to rewrite window title. See *rewrite rules*. +*icon*: ++ + typeof: bool ++ + default: true ++ + Option to hide the application icon. + # REWRITE RULES *rewrite* is an object where keys are regular expressions and values are