diff --git a/include/factory.hpp b/include/factory.hpp index 55031b8..ca707a3 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -27,7 +27,7 @@ #include "modules/hyprland/language.hpp" #include "modules/hyprland/window.hpp" #endif -#if defined(__linux__) && !defined(NO_FILESYSTEM) +#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) #include "modules/battery.hpp" #endif #if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 5f25fd5..614b36a 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -6,7 +6,9 @@ #include #endif #include +#if defined(__linux__) #include +#endif #include #include diff --git a/include/modules/wlr/workspace_manager.hpp b/include/modules/wlr/workspace_manager.hpp index e65594a..963d610 100644 --- a/include/modules/wlr/workspace_manager.hpp +++ b/include/modules/wlr/workspace_manager.hpp @@ -152,6 +152,7 @@ class WorkspaceManager : public AModule { bool sort_by_name_ = true; bool sort_by_coordinates_ = true; + bool sort_by_number_ = false; bool all_outputs_ = false; bool active_only_ = false; bool creation_delayed_ = false; diff --git a/man/waybar-wlr-workspaces.5.scd b/man/waybar-wlr-workspaces.5.scd index 5d7b2ac..169112f 100644 --- a/man/waybar-wlr-workspaces.5.scd +++ b/man/waybar-wlr-workspaces.5.scd @@ -33,6 +33,11 @@ Addressed by *wlr/workspaces* Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. If both are false - sort by id will be performed. +*sort-by-number*: ++ + typeof: bool ++ + default: false ++ + If set to true, workspace names will be sorted numerically. Takes presedence over any other sort-by option. + *all-outputs*: ++ typeof: bool ++ default: false ++ @@ -75,7 +80,8 @@ Additional to workspace name matching, the following *format-icons* can be set. "5": "", "focused": "", "default": "" - } + }, + "sort-by-number": true } ``` diff --git a/meson.build b/meson.build index aab5a49..e537bcc 100644 --- a/meson.build +++ b/meson.build @@ -179,6 +179,11 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd 'src/modules/memory/bsd.cpp', 'src/modules/memory/common.cpp', ) + if is_freebsd + src_files += files( + 'src/modules/battery.cpp', + ) + endif endif add_project_arguments('-DHAVE_SWAY', language: 'cpp') diff --git a/src/factory.cpp b/src/factory.cpp index 9b9dde8..d00a7d4 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -7,7 +7,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { auto hash_pos = name.find('#'); auto ref = name.substr(0, hash_pos); auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; -#if defined(__linux__) && !defined(NO_FILESYSTEM) +#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM)) if (ref == "battery") { return new waybar::modules::Battery(id, config_[name]); } diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index e25ad9e..8877494 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -1,9 +1,13 @@ #include "modules/battery.hpp" - +#if defined(__FreeBSD__) +#include +#endif #include +#include waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) : AButton(config, "battery", id, "{capacity}%", 60) { +#if defined(__Linux__) battery_watch_fd_ = inotify_init1(IN_CLOEXEC); if (battery_watch_fd_ == -1) { throw std::runtime_error("Unable to listen batteries."); @@ -19,11 +23,12 @@ waybar::modules::Battery::Battery(const std::string& id, const Json::Value& conf if (global_watch < 0) { throw std::runtime_error("Could not watch for battery plug/unplug"); } - +#endif worker(); } waybar::modules::Battery::~Battery() { +#if defined(__linux__) std::lock_guard guard(battery_list_mutex_); if (global_watch >= 0) { @@ -39,9 +44,16 @@ waybar::modules::Battery::~Battery() { batteries_.erase(it); } close(battery_watch_fd_); +#endif } void waybar::modules::Battery::worker() { +#if defined(__FreeBSD__) + thread_timer_ = [this] { + dp.emit(); + thread_timer_.sleep_for(interval_); + }; +#else thread_timer_ = [this] { // Make sure we eventually update the list of batteries even if we miss an // inotify event for some reason @@ -68,9 +80,11 @@ void waybar::modules::Battery::worker() { refreshBatteries(); dp.emit(); }; +#endif } void waybar::modules::Battery::refreshBatteries() { +#if defined(__linux__) std::lock_guard guard(battery_list_mutex_); // Mark existing list of batteries as not necessarily found std::map check_map; @@ -135,6 +149,7 @@ void waybar::modules::Battery::refreshBatteries() { batteries_.erase(check.first); } } +#endif } // Unknown > Full > Not charging > Discharging > Charging @@ -156,6 +171,58 @@ const std::tuple waybar::modules::Battery::g std::lock_guard guard(battery_list_mutex_); try { +#if defined(__FreeBSD__) + /* Allocate state of battery units reported via ACPI. */ + int battery_units = 0; + size_t battery_units_size = sizeof battery_units; + if (sysctlbyname("hw.acpi.battery.units", &battery_units, &battery_units_size, NULL, 0) != 0) { + throw std::runtime_error("sysctl hw.acpi.battery.units failed"); + } + + if (battery_units < 0) { + throw std::runtime_error("No battery units"); + } + + int capacity; + size_t size_capacity = sizeof capacity; + if (sysctlbyname("hw.acpi.battery.life", &capacity, &size_capacity, NULL, 0) != 0) { + throw std::runtime_error("sysctl hw.acpi.battery.life failed"); + } + int time; + size_t size_time = sizeof time; + if (sysctlbyname("hw.acpi.battery.time", &time, &size_time, NULL, 0) != 0) { + throw std::runtime_error("sysctl hw.acpi.battery.time failed"); + } + int rate; + size_t size_rate = sizeof rate; + if (sysctlbyname("hw.acpi.battery.rate", &rate, &size_rate, NULL, 0) != 0) { + throw std::runtime_error("sysctl hw.acpi.battery.rate failed"); + } + + auto status = getAdapterStatus(capacity); + // Handle full-at + if (config_["full-at"].isUInt()) { + auto full_at = config_["full-at"].asUInt(); + if (full_at < 100) { + capacity = 100.f * capacity / full_at; + } + } + if (capacity > 100.f) { + // This can happen when the battery is calibrating and goes above 100% + // Handle it gracefully by clamping at 100% + capacity = 100.f; + } + uint8_t cap = round(capacity); + if (cap == 100 && status == "Plugged") { + // If we've reached 100% just mark as full as some batteries can stay + // stuck reporting they're still charging but not yet done + status = "Full"; + } + + // spdlog::info("{} {} {} {}", capacity,time,status,rate); + return {capacity, time / 60.0, status, rate}; + +#elif defined(__linux__) uint32_t total_power = 0; // μW bool total_power_exists = false; uint32_t total_energy = 0; // μWh @@ -456,6 +523,7 @@ const std::tuple waybar::modules::Battery::g if (cap == 100 && status == "Charging") status = "Full"; return {cap, time_remaining, status, total_power / 1e6}; +#endif } catch (const std::exception& e) { spdlog::error("Battery: {}", e.what()); return {0, 0, "Unknown", 0}; @@ -463,11 +531,22 @@ const std::tuple waybar::modules::Battery::g } const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) const { +#if defined(__FreeBSD__) + int state; + size_t size_state = sizeof state; + if (sysctlbyname("hw.acpi.battery.state", &state, &size_state, NULL, 0) != 0) { + throw std::runtime_error("sysctl hw.acpi.battery.state failed"); + } + bool online = state == 2; + std::string status{"Unknown"}; // TODO: add status in FreeBSD + { +#else if (!adapter_.empty()) { bool online; std::string status; std::ifstream(adapter_ / "online") >> online; std::getline(std::ifstream(adapter_ / "status"), status); +#endif if (capacity == 100) { return "Full"; } @@ -497,10 +576,12 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai } auto waybar::modules::Battery::update() -> void { +#if defined(__linux__) if (batteries_.empty()) { event_box_.hide(); return; } +#endif auto [capacity, time_remaining, status, power] = getInfos(); if (status == "Unknown") { status = getAdapterStatus(capacity); diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 20e438d..0d22fe0 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -60,7 +60,10 @@ std::string Window::getLastWindowTitle(uint workspaceID) { auto workspace = std::find_if(json.begin(), json.end(), [&](Json::Value workspace) { return workspace["id"].as() == workspaceID; }); - assert(workspace != std::end(json)); + + if (workspace != std::end(json)) { + return ""; + } return (*workspace)["lastwindowtitle"].as(); } diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index cc3c815..da83cb7 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -32,6 +32,11 @@ WorkspaceManager::WorkspaceManager(const std::string &id, const waybar::Bar &bar sort_by_coordinates_ = config_sort_by_coordinates.asBool(); } + auto config_sort_by_number = config_["sort-by-number"]; + if (config_sort_by_number.isBool()) { + sort_by_number_ = config_sort_by_number.asBool(); + } + auto config_all_outputs = config_["all-outputs"]; if (config_all_outputs.isBool()) { all_outputs_ = config_all_outputs.asBool(); @@ -61,6 +66,12 @@ auto WorkspaceManager::workspace_comparator() const auto is_name_less = lhs->get_name() < rhs->get_name(); auto is_name_eq = lhs->get_name() == rhs->get_name(); auto is_coords_less = lhs->get_coords() < rhs->get_coords(); + auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name()); + + if (sort_by_number_) { + return is_number_less; + } + if (sort_by_name_) { if (sort_by_coordinates_) { return is_name_eq ? is_coords_less : is_name_less;