commit
3d023a0421
|
@ -55,16 +55,12 @@
|
||||||
#include "modules/sndio.hpp"
|
#include "modules/sndio.hpp"
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_GIO_UNIX
|
#ifdef HAVE_GIO_UNIX
|
||||||
|
#include "modules/bluetooth.hpp"
|
||||||
#include "modules/inhibitor.hpp"
|
#include "modules/inhibitor.hpp"
|
||||||
#endif
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
#if defined(__linux__)
|
|
||||||
#ifdef WANT_RFKILL
|
|
||||||
#include "modules/bluetooth.hpp"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace waybar {
|
namespace waybar {
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,79 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ALabel.hpp"
|
#include "ALabel.hpp"
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
#include "util/rfkill.hpp"
|
#include "util/rfkill.hpp"
|
||||||
|
#endif
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
class Bluetooth : public ALabel {
|
class Bluetooth : public ALabel {
|
||||||
|
struct ControllerInfo {
|
||||||
|
std::string path;
|
||||||
|
std::string address;
|
||||||
|
std::string address_type;
|
||||||
|
// std::string name; // just use alias instead
|
||||||
|
std::string alias;
|
||||||
|
bool powered;
|
||||||
|
bool discoverable;
|
||||||
|
bool pairable;
|
||||||
|
bool discovering;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: there are some properties that not all devices provide
|
||||||
|
struct DeviceInfo {
|
||||||
|
std::string path;
|
||||||
|
std::string paired_controller;
|
||||||
|
std::string address;
|
||||||
|
std::string address_type;
|
||||||
|
// std::optional<std::string> name; // just use alias instead
|
||||||
|
std::string alias;
|
||||||
|
std::optional<std::string> icon;
|
||||||
|
bool paired;
|
||||||
|
bool trusted;
|
||||||
|
bool blocked;
|
||||||
|
bool connected;
|
||||||
|
bool services_resolved;
|
||||||
|
// NOTE: experimental feature in bluez
|
||||||
|
std::optional<unsigned char> battery_percentage;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Bluetooth(const std::string&, const Json::Value&);
|
Bluetooth(const std::string&, const Json::Value&);
|
||||||
~Bluetooth() = default;
|
~Bluetooth() = default;
|
||||||
auto update() -> void;
|
auto update() -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*,
|
||||||
|
gpointer) -> void;
|
||||||
|
static auto onInterfaceProxyPropertiesChanged(GDBusObjectManagerClient*, GDBusObjectProxy*,
|
||||||
|
GDBusProxy*, GVariant*, const gchar* const*,
|
||||||
|
gpointer) -> void;
|
||||||
|
|
||||||
|
auto getDeviceBatteryPercentage(GDBusObject*) -> std::optional<unsigned char>;
|
||||||
|
auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool;
|
||||||
|
auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool;
|
||||||
|
|
||||||
|
auto findCurController(ControllerInfo&) -> bool;
|
||||||
|
auto findConnectedDevices(const std::string&, std::vector<DeviceInfo>&) -> void;
|
||||||
|
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
util::Rfkill rfkill_;
|
util::Rfkill rfkill_;
|
||||||
|
#endif
|
||||||
|
const std::unique_ptr<GDBusObjectManager, void (*)(GDBusObjectManager*)> manager_;
|
||||||
|
|
||||||
|
std::string state_;
|
||||||
|
ControllerInfo cur_controller_;
|
||||||
|
std::vector<DeviceInfo> connected_devices_;
|
||||||
|
DeviceInfo cur_focussed_device_;
|
||||||
|
std::string device_enumerate_;
|
||||||
|
|
||||||
|
std::vector<std::string> device_preference_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|
|
@ -6,21 +6,41 @@ waybar - bluetooth module
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
The *bluetooth* module displays information about the status of the device's bluetooth device.
|
The *bluetooth* module displays information about a bluetooth controller and its connections.
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
Addressed by *bluetooth*
|
Addressed by *bluetooth*
|
||||||
|
|
||||||
|
*controller*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Use the controller with the defined alias. Otherwise a random controller is used. Recommended to define when there is more than 1 controller available to the system.
|
||||||
|
|
||||||
|
*format-device-preference*: ++
|
||||||
|
typeof: array ++
|
||||||
|
A ranking of bluetooth devices, addressed by their alias. The order is from *first displayed* to *last displayed*. ++
|
||||||
|
If this config option is not defined or none of the devices in the list are connected, it will fall back to showing the last connected device.
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: *{icon}* ++
|
default: * {status}* ++
|
||||||
The format, how information should be displayed. This format is used when other formats aren't specified.
|
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||||
|
|
||||||
*format-icons*: ++
|
*format-disabled*: ++
|
||||||
typeof: array/object ++
|
typeof: string ++
|
||||||
Based on the device status, the corresponding icon gets selected. ++
|
This format is used when the displayed controller is disabled.
|
||||||
The order is *low* to *high*. Or by the state if it is an object.
|
|
||||||
|
*format-off*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is turned off.
|
||||||
|
|
||||||
|
*format-on*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is turned on with no devices connected.
|
||||||
|
|
||||||
|
*format-connected*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is connected to at least 1 device.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
|
@ -71,23 +91,94 @@ Addressed by *bluetooth*
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified.
|
The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified.
|
||||||
|
|
||||||
|
*tooltip-format-disabled*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is disabled.
|
||||||
|
|
||||||
|
*tooltip-format-off*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is turned off.
|
||||||
|
|
||||||
|
*tooltip-format-on*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is turned on with no devices connected.
|
||||||
|
|
||||||
|
*tooltip-format-connected*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed controller is connected to at least 1 device.
|
||||||
|
|
||||||
|
*tooltip-format-enumerate-connected*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used to define how each connected device should be displayed within the *device_enumerate* format replacement in the tooltip menu.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{status}*: Status of the bluetooth device.
|
*{status}*: Status of the bluetooth device.
|
||||||
|
|
||||||
*{icon}*: Icon, as defined in *format-icons*.
|
*{num_connections}*: Number of connections the displayed controller has.
|
||||||
|
|
||||||
|
*{controller_address}*: Address of the displayed controller.
|
||||||
|
|
||||||
|
*{controller_address_type}*: Address type of the displayed controller.
|
||||||
|
|
||||||
|
*{controller_alias}*: Alias of the displayed controller.
|
||||||
|
|
||||||
|
*{device_address}*: Address of the displayed device.
|
||||||
|
|
||||||
|
*{device_address_type}*: Address type of the displayed device.
|
||||||
|
|
||||||
|
*{device_alias}*: Alias of the displayed device.
|
||||||
|
|
||||||
|
*{device_enumerate}*: Show a list of all connected devices, each on a seperate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++
|
||||||
|
and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip related format options.
|
||||||
|
|
||||||
|
# EXPERIMENTAL BATTERY PERCENTAGE FEATURE
|
||||||
|
|
||||||
|
At the time of writing, the experimental features of BlueZ need to be turned on, for the battery percentage options listed below to work.
|
||||||
|
|
||||||
|
## FORMAT REPLACEMENT
|
||||||
|
|
||||||
|
*{device_battery_percentage}*: Battery percentage of the displayed device if available. Use only in the config options defined below.
|
||||||
|
|
||||||
|
## CONFIGURATION
|
||||||
|
|
||||||
|
*format-connected-battery*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed device provides its battery percentage.
|
||||||
|
|
||||||
|
*tooltip-format-connected-battery*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used when the displayed device provides its battery percentage.
|
||||||
|
|
||||||
|
*tooltip-format-enumerate-connected-battery*: ++
|
||||||
|
typeof: string ++
|
||||||
|
This format is used to define how each connected device with a battery should be displayed within the *device_enumerate* format replacement option. ++
|
||||||
|
When this config option is not defined, it will fall back on the *tooltip-format-enumerate-connected* config option.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
"format": "{icon}",
|
// "controller": "controller1", // specify the alias of the controller if there are more than 1 on the system
|
||||||
"format-alt": "bluetooth: {status}",
|
"format": " {status}",
|
||||||
"format-icons": {
|
"format-disabled": "", // an empty format will hide the module
|
||||||
"enabled": "",
|
"format-connected": " {num_connections} connected",
|
||||||
"disabled": ""
|
"tooltip-format": "{controller_alias}\\t{controller_address}",
|
||||||
},
|
"tooltip-format-connected": "{controller_alias}\\t{controller_address}\\n\\n{device_enumerate}",
|
||||||
"tooltip-format": "{}"
|
"tooltip-format-enumerate-connected": "{device_alias}\\t{device_address}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
"bluetooth": {
|
||||||
|
"format": " {status}",
|
||||||
|
"format-connected": " {device_alias}",
|
||||||
|
"format-connected-battery": " {device_alias} {device_battery_percentage}%",
|
||||||
|
// "format-device-preference": [ "device1", "device2" ], // preference list deciding the displayed device
|
||||||
|
"tooltip-format": "{controller_alias}\\t{controller_address}\\n\\n{num_connections} connected",
|
||||||
|
"tooltip-format-connected": "{controller_alias}\\t{controller_address}\\n\\n{num_connections} connected\\n\\n{device_enumerate}",
|
||||||
|
"tooltip-format-enumerate-connected": "{device_alias}\\t{device_address}",
|
||||||
|
"tooltip-format-enumerate-connected-battery": "{device_alias}\\t{device_address}\\t{device_battery_percentage}%"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -95,3 +186,9 @@ Addressed by *bluetooth*
|
||||||
|
|
||||||
- *#bluetooth*
|
- *#bluetooth*
|
||||||
- *#bluetooth.disabled*
|
- *#bluetooth.disabled*
|
||||||
|
- *#bluetooth.off*
|
||||||
|
- *#bluetooth.on*
|
||||||
|
- *#bluetooth.connected*
|
||||||
|
- *#bluetooth.discoverable*
|
||||||
|
- *#bluetooth.discovering*
|
||||||
|
- *#bluetooth.pairable*
|
||||||
|
|
|
@ -253,17 +253,15 @@ endif
|
||||||
if (giounix.found() and not get_option('logind').disabled())
|
if (giounix.found() and not get_option('logind').disabled())
|
||||||
add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp')
|
add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp')
|
||||||
src_files += 'src/modules/inhibitor.cpp'
|
src_files += 'src/modules/inhibitor.cpp'
|
||||||
|
src_files += 'src/modules/bluetooth.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get_option('rfkill').enabled()
|
if get_option('rfkill').enabled() and is_linux
|
||||||
if is_linux
|
|
||||||
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
add_project_arguments('-DWANT_RFKILL', language: 'cpp')
|
||||||
src_files += files(
|
src_files += files(
|
||||||
'src/modules/bluetooth.cpp',
|
|
||||||
'src/util/rfkill.cpp'
|
'src/util/rfkill.cpp'
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
if tz_dep.found()
|
if tz_dep.found()
|
||||||
add_project_arguments('-DHAVE_LIBDATE', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBDATE', language: 'cpp')
|
||||||
|
|
|
@ -101,6 +101,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_GIO_UNIX
|
#ifdef HAVE_GIO_UNIX
|
||||||
|
if (ref == "bluetooth") {
|
||||||
|
return new waybar::modules::Bluetooth(id, config_[name]);
|
||||||
|
}
|
||||||
if (ref == "inhibitor") {
|
if (ref == "inhibitor") {
|
||||||
return new waybar::modules::Inhibitor(id, bar_, config_[name]);
|
return new waybar::modules::Inhibitor(id, bar_, config_[name]);
|
||||||
}
|
}
|
||||||
|
@ -108,13 +111,6 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
if (ref == "temperature") {
|
if (ref == "temperature") {
|
||||||
return new waybar::modules::Temperature(id, config_[name]);
|
return new waybar::modules::Temperature(id, config_[name]);
|
||||||
}
|
}
|
||||||
#if defined(__linux__)
|
|
||||||
#ifdef WANT_RFKILL
|
|
||||||
if (ref == "bluetooth") {
|
|
||||||
return new waybar::modules::Bluetooth(id, config_[name]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
||||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,391 @@
|
||||||
#include "modules/bluetooth.hpp"
|
#include "modules/bluetooth.hpp"
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using GDBusManager = std::unique_ptr<GDBusObjectManager, void (*)(GDBusObjectManager*)>;
|
||||||
|
|
||||||
|
auto generateManager() -> GDBusManager {
|
||||||
|
GError* error = nullptr;
|
||||||
|
GDBusObjectManager* manager = g_dbus_object_manager_client_new_for_bus_sync(
|
||||||
|
G_BUS_TYPE_SYSTEM,
|
||||||
|
GDBusObjectManagerClientFlags::G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
|
||||||
|
"org.bluez", "/", NULL, NULL, NULL, NULL, &error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
spdlog::error("g_dbus_object_manager_client_new_for_bus_sync() failed: {}", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto destructor = [](GDBusObjectManager* manager) {
|
||||||
|
if (manager) {
|
||||||
|
g_object_unref(manager);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return GDBusManager{manager, destructor};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool {
|
||||||
|
auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name);
|
||||||
|
if (gvar) {
|
||||||
|
bool property_value = g_variant_get_boolean(gvar);
|
||||||
|
g_variant_unref(gvar);
|
||||||
|
return property_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::error("getBoolProperty() failed: doesn't have property {}", property_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name)
|
||||||
|
-> std::optional<std::string> {
|
||||||
|
auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name);
|
||||||
|
if (gvar) {
|
||||||
|
std::string property_value = g_variant_get_string(gvar, NULL);
|
||||||
|
g_variant_unref(gvar);
|
||||||
|
return property_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getStringProperty(GDBusProxy* proxy, const char* property_name) -> std::string {
|
||||||
|
auto property_value = getOptionalStringProperty(proxy, property_name);
|
||||||
|
if (!property_value.has_value()) {
|
||||||
|
spdlog::error("getStringProperty() failed: doesn't have property {}", property_name);
|
||||||
|
}
|
||||||
|
return property_value.value_or("");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getUcharProperty(GDBusProxy* proxy, const char* property_name) -> unsigned char {
|
||||||
|
auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name);
|
||||||
|
if (gvar) {
|
||||||
|
unsigned char property_value;
|
||||||
|
g_variant_get(gvar, "y", &property_value);
|
||||||
|
g_variant_unref(gvar);
|
||||||
|
|
||||||
|
return property_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::error("getUcharProperty() failed: doesn't have property {}", property_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
|
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "bluetooth", id, "{icon}", 10), rfkill_{RFKILL_TYPE_BLUETOOTH} {
|
: ALabel(config, "bluetooth", id, " {status}", 10),
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
rfkill_{RFKILL_TYPE_BLUETOOTH},
|
||||||
|
#endif
|
||||||
|
manager_(generateManager()) {
|
||||||
|
|
||||||
|
if (config_["format-device-preference"].isArray()) {
|
||||||
|
std::transform(config_["format-device-preference"].begin(),
|
||||||
|
config_["format-device-preference"].end(),
|
||||||
|
std::back_inserter(device_preference_), [](auto x) { return x.asString(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: assumption made that the controller that is selcected stays unchanged
|
||||||
|
// for duration of the module
|
||||||
|
if (!findCurController(cur_controller_)) {
|
||||||
|
if (config_["controller-alias"].isString()) {
|
||||||
|
spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'",
|
||||||
|
config_["controller-alias"].asString());
|
||||||
|
} else {
|
||||||
|
spdlog::error("findCurController() failed: no bluetooth controller found");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
findConnectedDevices(cur_controller_.path, connected_devices_);
|
||||||
|
|
||||||
|
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||||
|
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
||||||
|
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
|
||||||
|
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||||
|
this);
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Bluetooth::update() -> void {
|
auto waybar::modules::Bluetooth::update() -> void {
|
||||||
std::string status = rfkill_.getState() ? "disabled" : "enabled";
|
// focussed device is either:
|
||||||
|
// - the first device in the device_preference_ list that is connected to the
|
||||||
label_.set_markup(
|
// current controller (if none fallback to last connected device)
|
||||||
fmt::format(format_, fmt::arg("status", status), fmt::arg("icon", getIcon(0, status))));
|
// - it is the last device that connected to the current controller
|
||||||
if (status == "disabled") {
|
if (!connected_devices_.empty()) {
|
||||||
label_.get_style_context()->add_class("disabled");
|
bool preferred_device_connected = false;
|
||||||
} else {
|
if (!device_preference_.empty()) {
|
||||||
label_.get_style_context()->remove_class("disabled");
|
for (const std::string& device_alias : device_preference_) {
|
||||||
|
auto it =
|
||||||
|
std::find_if(connected_devices_.begin(), connected_devices_.end(),
|
||||||
|
[device_alias](auto device) { return device_alias == device.alias; });
|
||||||
|
if (it != connected_devices_.end()) {
|
||||||
|
preferred_device_connected = true;
|
||||||
|
cur_focussed_device_ = *it;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!preferred_device_connected) {
|
||||||
|
cur_focussed_device_ = connected_devices_.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string state;
|
||||||
|
std::string tooltip_format;
|
||||||
|
if (!cur_controller_.powered)
|
||||||
|
state = "off";
|
||||||
|
else if (!connected_devices_.empty())
|
||||||
|
state = "connected";
|
||||||
|
else
|
||||||
|
state = "on";
|
||||||
|
#ifdef WANT_RFKILL
|
||||||
|
if (rfkill_.getState()) state = "disabled";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!alt_) {
|
||||||
|
if (state == "connected" && cur_focussed_device_.battery_percentage.has_value() &&
|
||||||
|
config_["format-connected-battery"].isString()) {
|
||||||
|
format_ = config_["format-connected-battery"].asString();
|
||||||
|
} else if (config_["format-" + state].isString()) {
|
||||||
|
format_ = config_["format-" + state].asString();
|
||||||
|
} else if (config_["format"].isString()) {
|
||||||
|
format_ = config_["format"].asString();
|
||||||
|
} else {
|
||||||
|
format_ = default_format_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config_["tooltip-format-" + state].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||||
|
} else if (config_["tooltip-format"].isString()) {
|
||||||
|
tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
format_.empty() ? event_box_.hide() : event_box_.show();
|
||||||
|
|
||||||
|
auto update_style_context = [this](const std::string& style_class, bool in_next_state) {
|
||||||
|
if (in_next_state && !label_.get_style_context()->has_class(style_class)) {
|
||||||
|
label_.get_style_context()->add_class(style_class);
|
||||||
|
} else if (!in_next_state && label_.get_style_context()->has_class(style_class)) {
|
||||||
|
label_.get_style_context()->remove_class(style_class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
update_style_context("discoverable", cur_controller_.discoverable);
|
||||||
|
update_style_context("discovering", cur_controller_.discovering);
|
||||||
|
update_style_context("pairable", cur_controller_.pairable);
|
||||||
|
if (!state_.empty()) {
|
||||||
|
update_style_context(state_, false);
|
||||||
|
}
|
||||||
|
update_style_context(state, true);
|
||||||
|
state_ = state;
|
||||||
|
|
||||||
|
label_.set_markup(fmt::format(
|
||||||
|
format_, fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()),
|
||||||
|
fmt::arg("controller_address", cur_controller_.address),
|
||||||
|
fmt::arg("controller_address_type", cur_controller_.address_type),
|
||||||
|
fmt::arg("controller_alias", cur_controller_.alias),
|
||||||
|
fmt::arg("device_address", cur_focussed_device_.address),
|
||||||
|
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||||
|
fmt::arg("device_alias", cur_focussed_device_.alias),
|
||||||
|
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0))));
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
if (config_["tooltip-format"].isString()) {
|
bool tooltip_enumerate_connections_ = config_["tooltip-format-enumerate-connected"].isString();
|
||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
bool tooltip_enumerate_connections_battery_ =
|
||||||
auto tooltip_text = fmt::format(tooltip_format, status, fmt::arg("status", status));
|
config_["tooltip-format-enumerate-connected-battery"].isString();
|
||||||
label_.set_tooltip_text(tooltip_text);
|
if (tooltip_enumerate_connections_ || tooltip_enumerate_connections_battery_) {
|
||||||
|
std::stringstream ss;
|
||||||
|
for (DeviceInfo dev : connected_devices_) {
|
||||||
|
if ((tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value()) ||
|
||||||
|
tooltip_enumerate_connections_) {
|
||||||
|
ss << "\n";
|
||||||
|
std::string enumerate_format =
|
||||||
|
(tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value())
|
||||||
|
? config_["tooltip-format-enumerate-connected-battery"].asString()
|
||||||
|
: config_["tooltip-format-enumerate-connected"].asString();
|
||||||
|
ss << fmt::format(
|
||||||
|
enumerate_format, fmt::arg("device_address", dev.address),
|
||||||
|
fmt::arg("device_address_type", dev.address_type),
|
||||||
|
fmt::arg("device_alias", dev.alias),
|
||||||
|
fmt::arg("device_battery_percentage", dev.battery_percentage.value_or(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device_enumerate_ = ss.str();
|
||||||
|
// don't start the connected devices text with a new line
|
||||||
|
if (!device_enumerate_.empty()) {
|
||||||
|
device_enumerate_.erase(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label_.set_tooltip_text(fmt::format(
|
||||||
|
tooltip_format, fmt::arg("status", state_),
|
||||||
|
fmt::arg("num_connections", connected_devices_.size()),
|
||||||
|
fmt::arg("controller_address", cur_controller_.address),
|
||||||
|
fmt::arg("controller_address_type", cur_controller_.address_type),
|
||||||
|
fmt::arg("controller_alias", cur_controller_.alias),
|
||||||
|
fmt::arg("device_address", cur_focussed_device_.address),
|
||||||
|
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||||
|
fmt::arg("device_alias", cur_focussed_device_.alias),
|
||||||
|
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0)),
|
||||||
|
fmt::arg("device_enumerate", device_enumerate_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call parent update
|
||||||
|
ALabel::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: only for when the org.bluez.Battery1 interface is added/removed after/before a device is
|
||||||
|
// connected/disconnected
|
||||||
|
auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* manager,
|
||||||
|
GDBusObject* object,
|
||||||
|
GDBusInterface* interface,
|
||||||
|
gpointer user_data) -> void {
|
||||||
|
std::string interface_name = g_dbus_proxy_get_interface_name(G_DBUS_PROXY(interface));
|
||||||
|
std::string object_path = g_dbus_proxy_get_object_path(G_DBUS_PROXY(interface));
|
||||||
|
if (interface_name == "org.bluez.Battery1") {
|
||||||
|
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
|
||||||
|
auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(),
|
||||||
|
[object_path](auto d) { return d.path == object_path; });
|
||||||
|
if (device != bt->connected_devices_.end()) {
|
||||||
|
device->battery_percentage = bt->getDeviceBatteryPercentage(object);
|
||||||
|
bt->dp.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged(
|
||||||
|
GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy,
|
||||||
|
GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data)
|
||||||
|
-> void {
|
||||||
|
std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy);
|
||||||
|
std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy));
|
||||||
|
|
||||||
|
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
|
||||||
|
if (interface_name == "org.bluez.Adapter1") {
|
||||||
|
if (object_path == bt->cur_controller_.path) {
|
||||||
|
bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), bt->cur_controller_);
|
||||||
|
bt->dp.emit();
|
||||||
|
}
|
||||||
|
} else if (interface_name == "org.bluez.Device1" || interface_name == "org.bluez.Battery1") {
|
||||||
|
DeviceInfo device;
|
||||||
|
bt->getDeviceProperties(G_DBUS_OBJECT(object_proxy), device);
|
||||||
|
auto cur_device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(),
|
||||||
|
[device](auto d) { return d.path == device.path; });
|
||||||
|
if (cur_device == bt->connected_devices_.end()) {
|
||||||
|
if (device.connected) {
|
||||||
|
bt->connected_devices_.push_back(device);
|
||||||
|
bt->dp.emit();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(status);
|
if (!device.connected) {
|
||||||
|
bt->connected_devices_.erase(cur_device);
|
||||||
|
} else {
|
||||||
|
*cur_device = device;
|
||||||
|
}
|
||||||
|
bt->dp.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object)
|
||||||
|
-> std::optional<unsigned char> {
|
||||||
|
GDBusProxy* proxy_device_bat =
|
||||||
|
G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Battery1"));
|
||||||
|
if (proxy_device_bat != NULL) {
|
||||||
|
unsigned char battery_percentage = getUcharProperty(proxy_device_bat, "Percentage");
|
||||||
|
g_object_unref(proxy_device_bat);
|
||||||
|
|
||||||
|
return battery_percentage;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info)
|
||||||
|
-> bool {
|
||||||
|
GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1"));
|
||||||
|
|
||||||
|
if (proxy_device != NULL) {
|
||||||
|
device_info.path = g_dbus_object_get_object_path(object);
|
||||||
|
device_info.paired_controller = getStringProperty(proxy_device, "Adapter");
|
||||||
|
device_info.address = getStringProperty(proxy_device, "Address");
|
||||||
|
device_info.address_type = getStringProperty(proxy_device, "AddressType");
|
||||||
|
device_info.alias = getStringProperty(proxy_device, "Alias");
|
||||||
|
device_info.icon = getOptionalStringProperty(proxy_device, "Icon");
|
||||||
|
device_info.paired = getBoolProperty(proxy_device, "Paired");
|
||||||
|
device_info.trusted = getBoolProperty(proxy_device, "Trusted");
|
||||||
|
device_info.blocked = getBoolProperty(proxy_device, "Blocked");
|
||||||
|
device_info.connected = getBoolProperty(proxy_device, "Connected");
|
||||||
|
device_info.services_resolved = getBoolProperty(proxy_device, "ServicesResolved");
|
||||||
|
|
||||||
|
g_object_unref(proxy_device);
|
||||||
|
|
||||||
|
device_info.battery_percentage = getDeviceBatteryPercentage(object);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
|
||||||
|
ControllerInfo& controller_info) -> bool {
|
||||||
|
GDBusProxy* proxy_controller =
|
||||||
|
G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1"));
|
||||||
|
|
||||||
|
if (proxy_controller != NULL) {
|
||||||
|
controller_info.path = g_dbus_object_get_object_path(object);
|
||||||
|
controller_info.address = getStringProperty(proxy_controller, "Address");
|
||||||
|
controller_info.address_type = getStringProperty(proxy_controller, "AddressType");
|
||||||
|
controller_info.alias = getStringProperty(proxy_controller, "Alias");
|
||||||
|
controller_info.powered = getBoolProperty(proxy_controller, "Powered");
|
||||||
|
controller_info.discoverable = getBoolProperty(proxy_controller, "Discoverable");
|
||||||
|
controller_info.pairable = getBoolProperty(proxy_controller, "Pairable");
|
||||||
|
controller_info.discovering = getBoolProperty(proxy_controller, "Discovering");
|
||||||
|
|
||||||
|
g_object_unref(proxy_controller);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::findCurController(ControllerInfo& controller_info) -> bool {
|
||||||
|
bool found_controller = false;
|
||||||
|
|
||||||
|
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||||
|
for (GList* l = objects; l != NULL; l = l->next) {
|
||||||
|
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||||
|
if (getControllerProperties(object, controller_info) &&
|
||||||
|
(!config_["controller-alias"].isString() ||
|
||||||
|
config_["controller-alias"].asString() == controller_info.alias)) {
|
||||||
|
found_controller = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_list_free_full(objects, g_object_unref);
|
||||||
|
|
||||||
|
return found_controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
|
||||||
|
std::vector<DeviceInfo>& connected_devices)
|
||||||
|
-> void {
|
||||||
|
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||||
|
for (GList* l = objects; l != NULL; l = l->next) {
|
||||||
|
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||||
|
DeviceInfo device;
|
||||||
|
if (getDeviceProperties(object, device) && device.connected &&
|
||||||
|
device.paired_controller == cur_controller_.path) {
|
||||||
|
connected_devices.push_back(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_list_free_full(objects, g_object_unref);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue