Waybar/src/modules/upower/upower.cpp

265 lines
7.7 KiB
C++

#include "modules/upower/upower.hpp"
#include "gtkmm/icontheme.h"
#include "gtkmm/label.h"
#include "gtkmm/tooltip.h"
#include "modules/upower/upower_tooltip.hpp"
namespace waybar::modules::upower {
UPower::UPower(const std::string& id, const Json::Value& config)
: AModule(config, "upower", id),
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
icon_(),
label_(),
devices(),
m_Mutex(),
client() {
box_.pack_start(icon_);
box_.pack_start(label_);
event_box_.add(box_);
// Icon Size
if (config_["icon-size"].isUInt()) {
iconSize = config_["icon-size"].asUInt();
}
icon_.set_pixel_size(iconSize);
// Hide If Empty
if (config_["hide-if-empty"].isBool()) {
hideIfEmpty = config_["hide-if-empty"].asBool();
}
// Tooltip Spacing
if (config_["tooltip-spacing"].isUInt()) {
tooltip_spacing = config_["tooltip-spacing"].asUInt();
}
// Tooltip
if (config_["tooltip"].isBool()) {
tooltip_enabled = config_["tooltip"].asBool();
}
box_.set_has_tooltip(tooltip_enabled);
if (tooltip_enabled) {
// Sets the window to use when showing the tooltip
upower_tooltip = new UPowerTooltip(iconSize, tooltip_spacing);
box_.set_tooltip_window(*upower_tooltip);
box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::show_tooltip_callback));
}
GError* error = NULL;
client = up_client_new_full(NULL, &error);
if (client == NULL) {
throw std::runtime_error("Unable to create UPower client!");
}
// Connect to Login1 PrepareForSleep signal
login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (!login1_connection) {
throw std::runtime_error("Unable to connect to the SYSTEM Bus!...");
} else {
login1_id = g_dbus_connection_signal_subscribe(login1_connection,
"org.freedesktop.login1",
"org.freedesktop.login1.Manager",
"PrepareForSleep",
"/org/freedesktop/login1",
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
prepareForSleep_cb,
this,
NULL);
}
g_signal_connect(client, "device-added", G_CALLBACK(deviceAdded_cb), this);
g_signal_connect(client, "device-removed", G_CALLBACK(deviceRemoved_cb), this);
resetDevices();
setDisplayDevice();
}
UPower::~UPower() {
if (client != NULL) g_object_unref(client);
if (login1_id > 0) {
g_dbus_connection_signal_unsubscribe(login1_connection, login1_id);
login1_id = 0;
}
removeDevices();
}
void UPower::deviceAdded_cb(UpClient* client, UpDevice* device, gpointer data) {
UPower* up = static_cast<UPower*>(data);
up->addDevice(device);
up->setDisplayDevice();
// Update the widget
up->dp.emit();
}
void UPower::deviceRemoved_cb(UpClient* client, const gchar* objectPath, gpointer data) {
UPower* up = static_cast<UPower*>(data);
up->removeDevice(objectPath);
up->setDisplayDevice();
// Update the widget
up->dp.emit();
}
void UPower::deviceNotify_cb(UpDevice* device, GParamSpec* pspec, gpointer data) {
UPower* up = static_cast<UPower*>(data);
// Update the widget
up->dp.emit();
}
void UPower::prepareForSleep_cb(GDBusConnection* system_bus, const gchar* sender_name,
const gchar* object_path, const gchar* interface_name,
const gchar* signal_name, GVariant* parameters, gpointer data) {
if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)"))) {
gboolean sleeping;
g_variant_get(parameters, "(b)", &sleeping);
if (!sleeping) {
UPower* up = static_cast<UPower*>(data);
up->resetDevices();
up->setDisplayDevice();
}
}
}
void UPower::removeDevice(const gchar* objectPath) {
std::lock_guard<std::mutex> guard(m_Mutex);
if (devices.find(objectPath) != devices.end()) {
UpDevice* device = devices[objectPath];
if (G_IS_OBJECT(device)) {
g_object_unref(device);
}
devices.erase(objectPath);
}
}
void UPower::addDevice(UpDevice* device) {
if (G_IS_OBJECT(device)) {
const gchar* objectPath = up_device_get_object_path(device);
// Due to the device getting cleared after this event is fired, we
// create a new object pointing to its objectPath
gboolean ret;
device = up_device_new();
ret = up_device_set_object_path_sync(device, objectPath, NULL, NULL);
if (!ret) {
g_object_unref(G_OBJECT(device));
return;
}
std::lock_guard<std::mutex> guard(m_Mutex);
if (devices.find(objectPath) != devices.end()) {
UpDevice* device = devices[objectPath];
if (G_IS_OBJECT(device)) {
g_object_unref(device);
}
devices.erase(objectPath);
}
g_signal_connect(device, "notify", G_CALLBACK(deviceNotify_cb), this);
devices.emplace(Devices::value_type(objectPath, device));
}
}
void UPower::setDisplayDevice() {
std::lock_guard<std::mutex> guard(m_Mutex);
displayDevice = up_client_get_display_device(client);
g_signal_connect(displayDevice, "notify", G_CALLBACK(deviceNotify_cb), this);
}
void UPower::removeDevices() {
std::lock_guard<std::mutex> guard(m_Mutex);
if (!devices.empty()) {
auto it = devices.cbegin();
while (it != devices.cend()) {
if (G_IS_OBJECT(it->second)) {
g_object_unref(it->second);
}
devices.erase(it++);
}
}
}
/** Removes all devices and adds the current devices */
void UPower::resetDevices() {
// Removes all devices
removeDevices();
// Adds all devices
GPtrArray* newDevices = up_client_get_devices2(client);
for (guint i = 0; i < newDevices->len; i++) {
UpDevice* device = (UpDevice*)g_ptr_array_index(newDevices, i);
if (device && G_IS_OBJECT(device)) addDevice(device);
}
// Update the widget
dp.emit();
}
bool UPower::show_tooltip_callback(int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& tooltip) {
return true;
}
auto UPower::update() -> void {
std::lock_guard<std::mutex> guard(m_Mutex);
UpDeviceKind kind;
UpDeviceState state;
double percentage;
gboolean is_power_supply;
gboolean is_present;
gchar* icon_name;
g_object_get(displayDevice,
"kind",
&kind,
"state",
&state,
"is-present",
&is_present,
"power-supply",
&is_power_supply,
"percentage",
&percentage,
"icon-name",
&icon_name,
NULL);
bool displayDeviceValid =
kind == UpDeviceKind::UP_DEVICE_KIND_BATTERY || kind == UpDeviceKind::UP_DEVICE_KIND_UPS;
std::string percentString = "";
uint tooltipCount = 0;
if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) {
event_box_.set_visible(false);
goto update;
}
event_box_.set_visible(true);
// Tooltip
if (tooltip_enabled) {
tooltipCount = upower_tooltip->updateTooltip(devices);
// Disable the tooltip if there aren't any devices in the tooltip
box_.set_has_tooltip(!devices.empty() && tooltipCount > 0);
}
// Set percentage
if (displayDeviceValid) {
percentString = std::to_string(int(percentage + 0.5)) + "%";
}
label_.set_text(percentString);
// Set icon
if (!Gtk::IconTheme::get_default()->has_icon(icon_name)) {
icon_name = (char*)"battery-missing-symbolic";
}
icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
update:
// Call parent update
AModule::update();
}
} // namespace waybar::modules::upower