diff --git a/include/factory.hpp b/include/factory.hpp index 7d4d14e..b14b998 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -32,6 +32,7 @@ #include "bar.hpp" #include "modules/custom.hpp" #include "modules/temperature.hpp" +#include "modules/bluetooth.hpp" namespace waybar { diff --git a/include/modules/bluetooth.hpp b/include/modules/bluetooth.hpp new file mode 100644 index 0000000..04c213d --- /dev/null +++ b/include/modules/bluetooth.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "ALabel.hpp" + +#include +#include "util/sleeper_thread.hpp" +#include "util/rfkill.hpp" + +namespace waybar::modules { + +class Bluetooth : public ALabel { + public: + Bluetooth(const std::string&, const Json::Value&); + ~Bluetooth() = default; + auto update() -> void; + + private: + std::string status_; + util::SleeperThread thread_; + util::SleeperThread intervall_thread_; + + util::Rfkill rfkill_; +}; + +} // namespace waybar::modules diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 91e4ddb..edb5aa6 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -11,6 +11,7 @@ #include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" +#include "util/rfkill.hpp" namespace waybar::modules { @@ -71,6 +72,9 @@ class Network : public ALabel { util::SleeperThread thread_; util::SleeperThread thread_timer_; + util::SleeperThread thread_rfkill_; + + util::Rfkill rfkill_; }; } // namespace waybar::modules diff --git a/include/util/rfkill.hpp b/include/util/rfkill.hpp new file mode 100644 index 0000000..5dbf3ce --- /dev/null +++ b/include/util/rfkill.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace waybar::util { + +class Rfkill { + public:; + Rfkill(enum rfkill_type rfkill_type); + ~Rfkill() = default; + void waitForEvent(); + bool getState() const; + + private: + enum rfkill_type rfkill_type_; + int state_ = 0; +}; + +} // namespace waybar::util diff --git a/man/waybar-bluetooth.5.scd b/man/waybar-bluetooth.5.scd new file mode 100644 index 0000000..a07e544 --- /dev/null +++ b/man/waybar-bluetooth.5.scd @@ -0,0 +1,94 @@ +waybar-bluetooth(5) + +# NAME + +waybar - bluetooth module + +# DESCRIPTION + +The *bluetooth* module displays information about the status of the device's bluetooth device. + +# CONFIGURATION + +Addressed by *bluetooth* + +*interval*: ++ + typeof: integer ++ + default: 60 ++ + The interval in which the bluetooth state gets updated. + +*format*: ++ + typeof: string ++ + default: *{icon}* ++ + The format, how information should be displayed. This format is used when other formats aren't specified. + +*format-icons*: ++ + typeof: array/object ++ + Based on the device status, the corresponding icon gets selected. ++ + The order is *low* to *high*. Or by the state if it is an object. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*on-click*: ++ + typeof: string ++ + Command to execute when clicked on the module. + +*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-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. + +*tooltip-format*: ++ + typeof: string ++ + The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified. + +# FORMAT REPLACEMENTS + +*{status}*: Status of the bluetooth device. + +*{icon}*: Icon, as defined in *format-icons*. + +# EXAMPLES + +``` +"bluetooth": { + "format": "{icon}", + "format-alt": "bluetooth: {status}", + "interval": 30, + "format-icons": { + "enabled": "", + "disabled": "" + } + "tooltip-format": "{status}" +} +``` + +# STYLE + +- *#bluetooth* diff --git a/man/waybar-network.5.scd b/man/waybar-network.5.scd index 6bf2c94..aa6eb3f 100644 --- a/man/waybar-network.5.scd +++ b/man/waybar-network.5.scd @@ -47,6 +47,10 @@ Addressed by *network* typeof: string ++ This format is used when the displayed interface is disconnected. +*format-disabled*: ++ + typeof: string ++ + This format is used when the displayed interface is disabled. + *format-icons*: ++ typeof: array/object ++ Based on the current signal strength, the corresponding icon gets selected. ++ @@ -109,6 +113,10 @@ Addressed by *network* typeof: string ++ This format is used when the displayed interface is disconnected. +*tooltip-format-disabled*: ++ + typeof: string ++ + This format is used when the displayed interface is disabled. + # FORMAT REPLACEMENTS *{ifname}*: Name of the network interface. @@ -146,6 +154,7 @@ Addressed by *network* "format-wifi": "{essid} ({signalStrength}%) ", "format-ethernet": "{ifname} ", "format-disconnected": "", //An empty format will hide the module. + "format-disconnected": "", "tooltip-format": "{ifname}", "tooltip-format-wifi": "{essid} ({signalStrength}%) ", "tooltip-format-ethernet": "{ifname} ", @@ -158,6 +167,7 @@ Addressed by *network* - *#network* - *#network.disconnected* +- *#network.disabled* - *#network.linked* - *#network.ethernet* - *#network.wifi* diff --git a/meson.build b/meson.build index 1dab13d..0730ccd 100644 --- a/meson.build +++ b/meson.build @@ -108,6 +108,7 @@ src_files = files( 'src/ALabel.cpp', 'src/modules/memory.cpp', 'src/modules/battery.cpp', + 'src/modules/bluetooth.cpp', 'src/modules/clock.cpp', 'src/modules/custom.cpp', 'src/modules/cpu.cpp', @@ -116,7 +117,8 @@ src_files = files( 'src/modules/temperature.cpp', 'src/main.cpp', 'src/bar.cpp', - 'src/client.cpp' + 'src/client.cpp', + 'src/util/rfkill.cpp' ) if true # find_program('sway', required : false).found() diff --git a/src/factory.cpp b/src/factory.cpp index 8f7cea7..16a6903 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -66,6 +66,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "temperature") { return new waybar::modules::Temperature(id, config_[name]); } + if (ref == "bluetooth") { + return new waybar::modules::Bluetooth(id, config_[name]); + } if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), id, config_[name]); } diff --git a/src/modules/bluetooth.cpp b/src/modules/bluetooth.cpp new file mode 100644 index 0000000..b390976 --- /dev/null +++ b/src/modules/bluetooth.cpp @@ -0,0 +1,45 @@ +#include "modules/bluetooth.hpp" +#include "util/rfkill.hpp" +#include +#include + +waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config) + : ALabel(config, "bluetooth", id, "{icon}", 10), + status_("disabled"), + rfkill_{RFKILL_TYPE_BLUETOOTH} { + thread_ = [this] { + dp.emit(); + rfkill_.waitForEvent(); + }; + intervall_thread_ = [this] { + auto now = std::chrono::system_clock::now(); + auto timeout = std::chrono::floor(now + interval_); + auto diff = std::chrono::seconds(timeout.time_since_epoch().count() % interval_.count()); + thread_.sleep_until(timeout - diff); + dp.emit(); + }; +} + +auto waybar::modules::Bluetooth::update() -> void { + if (rfkill_.getState()) { + status_ = "disabled"; + } else { + status_ = "enabled"; + } + + label_.set_markup( + fmt::format( + format_, + fmt::arg("status", status_), + fmt::arg("icon", getIcon(0, status_)))); + + if (tooltipEnabled()) { + if (config_["tooltip-format"].isString()) { + auto tooltip_format = config_["tooltip-format"].asString(); + auto tooltip_text = fmt::format(tooltip_format, status_); + label_.set_tooltip_text(tooltip_text); + } else { + label_.set_tooltip_text(status_); + } + } +} diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a98e18d..0639d3e 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -4,7 +4,7 @@ #include #include #include "util/format.hpp" - +#include "util/rfkill.hpp" namespace { @@ -86,7 +86,8 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf cidr_(-1), signal_strength_dbm_(0), signal_strength_(0), - frequency_(0) { + frequency_(0), + rfkill_{RFKILL_TYPE_WLAN} { auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY); auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY); if (down_octets) { @@ -196,6 +197,7 @@ void waybar::modules::Network::createInfoSocket() { } void waybar::modules::Network::worker() { + // update via here not working thread_timer_ = [this] { { std::lock_guard lock(mutex_); @@ -206,6 +208,16 @@ void waybar::modules::Network::worker() { } thread_timer_.sleep_for(interval_); }; + thread_rfkill_ = [this] { + rfkill_.waitForEvent(); + { + std::lock_guard lock(mutex_); + if (ifid_ > 0) { + getInfo(); + dp.emit(); + } + } + }; thread_ = [this] { std::array events{}; @@ -222,7 +234,11 @@ void waybar::modules::Network::worker() { } const std::string waybar::modules::Network::getNetworkState() const { - if (ifid_ == -1) return "disconnected"; + if (ifid_ == -1) { + if (rfkill_.getState()) + return "disabled"; + return "disconnected"; + } if (ipaddr_.empty()) return "linked"; if (essid_.empty()) return "ethernet"; return "wifi"; diff --git a/src/util/rfkill.cpp b/src/util/rfkill.cpp new file mode 100644 index 0000000..df77598 --- /dev/null +++ b/src/util/rfkill.cpp @@ -0,0 +1,83 @@ +/* https://git.kernel.org/pub/scm/linux/kernel/git/jberg/rfkill.git/ + * + * Copyright 2009 Johannes Berg + * Copyright 2009 Marcel Holtmann + * Copyright 2009 Tim Gardner + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "util/rfkill.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +waybar::util::Rfkill::Rfkill(const enum rfkill_type rfkill_type) + : rfkill_type_(rfkill_type) { +} + +void waybar::util::Rfkill::waitForEvent() { + struct rfkill_event event; + struct pollfd p; + ssize_t len; + int fd, n; + + fd = open("/dev/rfkill", O_RDONLY); + if (fd < 0) { + throw std::runtime_error("Can't open RFKILL control device"); + return; + } + + memset(&p, 0, sizeof(p)); + p.fd = fd; + p.events = POLLIN | POLLHUP; + + while (1) { + n = poll(&p, 1, -1); + if (n < 0) { + throw std::runtime_error("Failed to poll RFKILL control device"); + break; + } + + if (n == 0) + continue; + + len = read(fd, &event, sizeof(event)); + if (len < 0) { + throw std::runtime_error("Reading of RFKILL events failed"); + break; + } + + if (len != RFKILL_EVENT_SIZE_V1) { + throw std::runtime_error("Wrong size of RFKILL event"); + continue; + } + + if(event.type == rfkill_type_ && event.op == RFKILL_OP_CHANGE) { + state_ = event.soft || event.hard; + break; + } + } + + close(fd); + return; +} + + +bool waybar::util::Rfkill::getState() const { + return state_; +}