Upower refactoring (#3220)
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
This commit is contained in:
parent
29917fb073
commit
2ead1bbf84
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include <giomm/dbusconnection.h>
|
||||
#include <gtkmm/icontheme.h>
|
||||
#include <libupower-glib/upower.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AIconLabel.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class UPower final : public AIconLabel {
|
||||
public:
|
||||
UPower(const std::string &, const Json::Value &);
|
||||
virtual ~UPower();
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
const std::string NO_BATTERY{"battery-missing-symbolic"};
|
||||
|
||||
// Config
|
||||
bool showIcon_{true};
|
||||
bool hideIfEmpty_{true};
|
||||
int iconSize_{20};
|
||||
int tooltip_spacing_{4};
|
||||
int tooltip_padding_{4};
|
||||
Gtk::Box contentBox_; // tooltip box
|
||||
std::string tooltipFormat_;
|
||||
|
||||
// UPower device info
|
||||
struct upDevice_output {
|
||||
UpDevice *upDevice{NULL};
|
||||
double percentage{0.0};
|
||||
double temperature{0.0};
|
||||
guint64 time_full{0u};
|
||||
guint64 time_empty{0u};
|
||||
gchar *icon_name{(char *)'\0'};
|
||||
bool upDeviceValid{false};
|
||||
UpDeviceState state;
|
||||
UpDeviceKind kind;
|
||||
char *nativePath{(char *)'\0'};
|
||||
char *model{(char *)'\0'};
|
||||
};
|
||||
|
||||
// Technical variables
|
||||
std::string nativePath_;
|
||||
std::string lastStatus_;
|
||||
Glib::ustring label_markup_;
|
||||
std::mutex mutex_;
|
||||
Glib::RefPtr<Gtk::IconTheme> gtkTheme_;
|
||||
|
||||
// Technical functions
|
||||
void addDevice(UpDevice *);
|
||||
void removeDevice(const gchar *);
|
||||
void removeDevices();
|
||||
void resetDevices();
|
||||
void setDisplayDevice();
|
||||
const Glib::ustring getText(const upDevice_output &upDevice_, const std::string &format);
|
||||
bool queryTooltipCb(int, int, bool, const Glib::RefPtr<Gtk::Tooltip> &);
|
||||
|
||||
// DBUS variables
|
||||
guint watcherID_;
|
||||
Glib::RefPtr<Gio::DBus::Connection> conn_;
|
||||
guint subscrID_{0u};
|
||||
|
||||
// UPower variables
|
||||
UpClient *upClient_;
|
||||
upDevice_output upDevice_; // Device to display
|
||||
typedef std::unordered_map<std::string, upDevice_output> Devices;
|
||||
Devices devices_;
|
||||
bool upRunning_{true};
|
||||
|
||||
// DBus callbacks
|
||||
void getConn_cb(Glib::RefPtr<Gio::AsyncResult> &result);
|
||||
void onAppear(const Glib::RefPtr<Gio::DBus::Connection> &, const Glib::ustring &,
|
||||
const Glib::ustring &);
|
||||
void onVanished(const Glib::RefPtr<Gio::DBus::Connection> &, const Glib::ustring &);
|
||||
void prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
||||
const Glib::ustring &sender_name, const Glib::ustring &object_path,
|
||||
const Glib::ustring &interface_name, const Glib::ustring &signal_name,
|
||||
const Glib::VariantContainerBase ¶meters);
|
||||
|
||||
// UPower callbacks
|
||||
static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data);
|
||||
static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data);
|
||||
static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data);
|
||||
// UPower secondary functions
|
||||
void getUpDeviceInfo(upDevice_output &upDevice_);
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
|
@ -1,82 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <libupower-glib/upower.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "glibconfig.h"
|
||||
#include "gtkmm/box.h"
|
||||
#include "gtkmm/image.h"
|
||||
#include "gtkmm/label.h"
|
||||
#include "modules/upower/upower_tooltip.hpp"
|
||||
|
||||
namespace waybar::modules::upower {
|
||||
|
||||
class UPower : public AModule {
|
||||
public:
|
||||
UPower(const std::string &, const Json::Value &);
|
||||
virtual ~UPower();
|
||||
auto update() -> void override;
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<std::string, UpDevice *> Devices;
|
||||
|
||||
const std::string DEFAULT_FORMAT = "{percentage}";
|
||||
const std::string DEFAULT_FORMAT_ALT = "{percentage} {time}";
|
||||
|
||||
static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data);
|
||||
static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data);
|
||||
static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data);
|
||||
static void prepareForSleep_cb(GDBusConnection *system_bus, const gchar *sender_name,
|
||||
const gchar *object_path, const gchar *interface_name,
|
||||
const gchar *signal_name, GVariant *parameters,
|
||||
gpointer user_data);
|
||||
static void upowerAppear(GDBusConnection *conn, const gchar *name, const gchar *name_owner,
|
||||
gpointer data);
|
||||
static void upowerDisappear(GDBusConnection *connection, const gchar *name, gpointer user_data);
|
||||
|
||||
void removeDevice(const gchar *objectPath);
|
||||
void addDevice(UpDevice *device);
|
||||
void setDisplayDevice();
|
||||
void resetDevices();
|
||||
void removeDevices();
|
||||
bool show_tooltip_callback(int, int, bool, const Glib::RefPtr<Gtk::Tooltip> &tooltip);
|
||||
bool handleToggle(GdkEventButton *const &) override;
|
||||
std::string timeToString(gint64 time);
|
||||
|
||||
const std::string getDeviceStatus(UpDeviceState &state);
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Image icon_;
|
||||
Gtk::Label label_;
|
||||
|
||||
// Config
|
||||
bool hideIfEmpty = true;
|
||||
bool tooltip_enabled = true;
|
||||
uint tooltip_spacing = 4;
|
||||
uint tooltip_padding = 4;
|
||||
uint iconSize = 20;
|
||||
std::string format = DEFAULT_FORMAT;
|
||||
std::string format_alt = DEFAULT_FORMAT_ALT;
|
||||
|
||||
Devices devices;
|
||||
std::mutex m_Mutex;
|
||||
UpClient *client;
|
||||
UpDevice *displayDevice = nullptr;
|
||||
guint login1_id;
|
||||
GDBusConnection *login1_connection;
|
||||
std::unique_ptr<UPowerTooltip> upower_tooltip;
|
||||
std::string lastStatus;
|
||||
const char *lastWarningLevel = nullptr;
|
||||
bool showAltText;
|
||||
bool showIcon = true;
|
||||
bool upowerRunning;
|
||||
guint upowerWatcher_id;
|
||||
std::string nativePath_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::upower
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <libupower-glib/upower.h>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "gtkmm/box.h"
|
||||
#include "gtkmm/label.h"
|
||||
#include "gtkmm/window.h"
|
||||
|
||||
namespace waybar::modules::upower {
|
||||
|
||||
class UPowerTooltip : public Gtk::Window {
|
||||
private:
|
||||
typedef std::unordered_map<std::string, UpDevice*> Devices;
|
||||
|
||||
const std::string getDeviceIcon(UpDeviceKind& kind);
|
||||
|
||||
std::unique_ptr<Gtk::Box> contentBox;
|
||||
|
||||
uint iconSize;
|
||||
uint tooltipSpacing;
|
||||
uint tooltipPadding;
|
||||
|
||||
public:
|
||||
UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding);
|
||||
virtual ~UPowerTooltip();
|
||||
|
||||
uint updateTooltip(Devices& devices);
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::upower
|
|
@ -335,10 +335,7 @@ endif
|
|||
|
||||
if (upower_glib.found() and not get_option('logind').disabled())
|
||||
add_project_arguments('-DHAVE_UPOWER', language: 'cpp')
|
||||
src_files += files(
|
||||
'src/modules/upower/upower.cpp',
|
||||
'src/modules/upower/upower_tooltip.cpp',
|
||||
)
|
||||
src_files += files('src/modules/upower.cpp')
|
||||
man_files += files('man/waybar-upower.5.scd')
|
||||
endif
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
#include "modules/gamemode.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_UPOWER
|
||||
#include "modules/upower/upower.hpp"
|
||||
#include "modules/upower.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_PIPEWIRE
|
||||
#include "modules/privacy/privacy.hpp"
|
||||
|
@ -130,7 +130,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
|
|||
#endif
|
||||
#ifdef HAVE_UPOWER
|
||||
if (ref == "upower") {
|
||||
return new waybar::modules::upower::UPower(id, config_[name]);
|
||||
return new waybar::modules::UPower(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_PIPEWIRE
|
||||
|
|
|
@ -0,0 +1,479 @@
|
|||
#include "modules/upower.hpp"
|
||||
|
||||
#include <giomm/dbuswatchname.h>
|
||||
#include <gtkmm/tooltip.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
UPower::UPower(const std::string &id, const Json::Value &config)
|
||||
: AIconLabel(config, "upower", id, "{percentage}", 0, true, true, true) {
|
||||
box_.set_name(name_);
|
||||
box_.set_spacing(0);
|
||||
box_.set_has_tooltip(AModule::tooltipEnabled());
|
||||
// Tooltip box
|
||||
contentBox_.set_orientation((box_.get_orientation() == Gtk::ORIENTATION_HORIZONTAL)
|
||||
? Gtk::ORIENTATION_VERTICAL
|
||||
: Gtk::ORIENTATION_HORIZONTAL);
|
||||
// Get current theme
|
||||
gtkTheme_ = Gtk::IconTheme::get_default();
|
||||
|
||||
// Icon Size
|
||||
if (config_["icon-size"].isInt()) {
|
||||
iconSize_ = config_["icon-size"].asInt();
|
||||
}
|
||||
image_.set_pixel_size(iconSize_);
|
||||
|
||||
// Show icon only when "show-icon" isn't set to false
|
||||
if (config_["show-icon"].isBool()) showIcon_ = config_["show-icon"].asBool();
|
||||
if (!showIcon_) box_.remove(image_);
|
||||
// Device user wants
|
||||
if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString();
|
||||
|
||||
// Hide If Empty
|
||||
if (config_["hide-if-empty"].isBool()) hideIfEmpty_ = config_["hide-if-empty"].asBool();
|
||||
|
||||
// Tooltip Spacing
|
||||
if (config_["tooltip-spacing"].isInt()) tooltip_spacing_ = config_["tooltip-spacing"].asInt();
|
||||
|
||||
// Tooltip Padding
|
||||
if (config_["tooltip-padding"].isInt()) {
|
||||
tooltip_padding_ = config_["tooltip-padding"].asInt();
|
||||
contentBox_.set_margin_top(tooltip_padding_);
|
||||
contentBox_.set_margin_bottom(tooltip_padding_);
|
||||
contentBox_.set_margin_left(tooltip_padding_);
|
||||
contentBox_.set_margin_right(tooltip_padding_);
|
||||
}
|
||||
|
||||
// Tooltip Format
|
||||
if (config_["tooltip-format"].isString()) tooltipFormat_ = config_["tooltip-format"].asString();
|
||||
|
||||
// Start watching DBUS
|
||||
watcherID_ = Gio::DBus::watch_name(
|
||||
Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.UPower",
|
||||
sigc::mem_fun(*this, &UPower::onAppear), sigc::mem_fun(*this, &UPower::onVanished),
|
||||
Gio::DBus::BusNameWatcherFlags::BUS_NAME_WATCHER_FLAGS_AUTO_START);
|
||||
// Get DBus async connect
|
||||
Gio::DBus::Connection::get(Gio::DBus::BusType::BUS_TYPE_SYSTEM,
|
||||
sigc::mem_fun(*this, &UPower::getConn_cb));
|
||||
|
||||
// Make UPower client
|
||||
GError **gErr = NULL;
|
||||
upClient_ = up_client_new_full(NULL, gErr);
|
||||
if (upClient_ == NULL)
|
||||
spdlog::error("Upower. UPower client connection error. {}", (*gErr)->message);
|
||||
|
||||
// Subscribe UPower events
|
||||
g_signal_connect(upClient_, "device-added", G_CALLBACK(deviceAdded_cb), this);
|
||||
g_signal_connect(upClient_, "device-removed", G_CALLBACK(deviceRemoved_cb), this);
|
||||
|
||||
// Subscribe tooltip query events
|
||||
box_.set_has_tooltip();
|
||||
box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::queryTooltipCb), false);
|
||||
|
||||
resetDevices();
|
||||
setDisplayDevice();
|
||||
// Update the widget
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
UPower::~UPower() {
|
||||
if (upDevice_.upDevice != NULL) g_object_unref(upDevice_.upDevice);
|
||||
if (upClient_ != NULL) g_object_unref(upClient_);
|
||||
if (subscrID_ > 0u) {
|
||||
conn_->signal_unsubscribe(subscrID_);
|
||||
subscrID_ = 0u;
|
||||
}
|
||||
Gio::DBus::unwatch_name(watcherID_);
|
||||
watcherID_ = 0u;
|
||||
removeDevices();
|
||||
}
|
||||
|
||||
static const std::string getDeviceStatus(UpDeviceState &state) {
|
||||
switch (state) {
|
||||
case UP_DEVICE_STATE_CHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_CHARGE:
|
||||
return "charging";
|
||||
case UP_DEVICE_STATE_DISCHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_DISCHARGE:
|
||||
return "discharging";
|
||||
case UP_DEVICE_STATE_FULLY_CHARGED:
|
||||
return "full";
|
||||
case UP_DEVICE_STATE_EMPTY:
|
||||
return "empty";
|
||||
default:
|
||||
return "unknown-status";
|
||||
}
|
||||
}
|
||||
|
||||
static const std::string getDeviceIcon(UpDeviceKind &kind) {
|
||||
switch (kind) {
|
||||
case UP_DEVICE_KIND_LINE_POWER:
|
||||
return "ac-adapter-symbolic";
|
||||
case UP_DEVICE_KIND_BATTERY:
|
||||
return "battery-symbolic";
|
||||
case UP_DEVICE_KIND_UPS:
|
||||
return "uninterruptible-power-supply-symbolic";
|
||||
case UP_DEVICE_KIND_MONITOR:
|
||||
return "video-display-symbolic";
|
||||
case UP_DEVICE_KIND_MOUSE:
|
||||
return "input-mouse-symbolic";
|
||||
case UP_DEVICE_KIND_KEYBOARD:
|
||||
return "input-keyboard-symbolic";
|
||||
case UP_DEVICE_KIND_PDA:
|
||||
return "pda-symbolic";
|
||||
case UP_DEVICE_KIND_PHONE:
|
||||
return "phone-symbolic";
|
||||
case UP_DEVICE_KIND_MEDIA_PLAYER:
|
||||
return "multimedia-player-symbolic";
|
||||
case UP_DEVICE_KIND_TABLET:
|
||||
return "computer-apple-ipad-symbolic";
|
||||
case UP_DEVICE_KIND_COMPUTER:
|
||||
return "computer-symbolic";
|
||||
case UP_DEVICE_KIND_GAMING_INPUT:
|
||||
return "input-gaming-symbolic";
|
||||
case UP_DEVICE_KIND_PEN:
|
||||
return "input-tablet-symbolic";
|
||||
case UP_DEVICE_KIND_TOUCHPAD:
|
||||
return "input-touchpad-symbolic";
|
||||
case UP_DEVICE_KIND_MODEM:
|
||||
return "modem-symbolic";
|
||||
case UP_DEVICE_KIND_NETWORK:
|
||||
return "network-wired-symbolic";
|
||||
case UP_DEVICE_KIND_HEADSET:
|
||||
return "audio-headset-symbolic";
|
||||
case UP_DEVICE_KIND_HEADPHONES:
|
||||
return "audio-headphones-symbolic";
|
||||
case UP_DEVICE_KIND_OTHER_AUDIO:
|
||||
case UP_DEVICE_KIND_SPEAKERS:
|
||||
return "audio-speakers-symbolic";
|
||||
case UP_DEVICE_KIND_VIDEO:
|
||||
return "camera-web-symbolic";
|
||||
case UP_DEVICE_KIND_PRINTER:
|
||||
return "printer-symbolic";
|
||||
case UP_DEVICE_KIND_SCANNER:
|
||||
return "scanner-symbolic";
|
||||
case UP_DEVICE_KIND_CAMERA:
|
||||
return "camera-photo-symbolic";
|
||||
case UP_DEVICE_KIND_BLUETOOTH_GENERIC:
|
||||
return "bluetooth-active-symbolic";
|
||||
case UP_DEVICE_KIND_TOY:
|
||||
case UP_DEVICE_KIND_REMOTE_CONTROL:
|
||||
case UP_DEVICE_KIND_WEARABLE:
|
||||
case UP_DEVICE_KIND_LAST:
|
||||
default:
|
||||
return "battery-symbolic";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string secondsToString(const std::chrono::seconds sec) {
|
||||
const auto ds{std::chrono::duration_cast<std::chrono::days>(sec)};
|
||||
const auto hrs{std::chrono::duration_cast<std::chrono::hours>(sec - ds)};
|
||||
const auto min{std::chrono::duration_cast<std::chrono::minutes>(sec - ds - hrs)};
|
||||
std::string_view strRet{(ds.count() > 0) ? "{D}d {H}h {M}min"
|
||||
: (hrs.count() > 0) ? "{H}h {M}min"
|
||||
: (min.count() > 0) ? "{M}min"
|
||||
: ""};
|
||||
spdlog::debug(
|
||||
"UPower::secondsToString(). seconds: \"{0}\", minutes: \"{1}\", hours: \"{2}\", \
|
||||
days: \"{3}\", strRet: \"{4}\"",
|
||||
sec.count(), min.count(), hrs.count(), ds.count(), strRet);
|
||||
return fmt::format(fmt::runtime(strRet), fmt::arg("D", ds.count()), fmt::arg("H", hrs.count()),
|
||||
fmt::arg("M", min.count()));
|
||||
}
|
||||
|
||||
auto UPower::update() -> void {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
// Don't update widget if the UPower service isn't running
|
||||
if (!upRunning_) {
|
||||
if (hideIfEmpty_) box_.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
getUpDeviceInfo(upDevice_);
|
||||
|
||||
if (upDevice_.upDevice == NULL && hideIfEmpty_) {
|
||||
box_.hide();
|
||||
return;
|
||||
}
|
||||
/* Every Device which is handled by Upower and which is not
|
||||
* UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery
|
||||
*/
|
||||
const bool upDeviceValid{upDevice_.kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN &&
|
||||
upDevice_.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER};
|
||||
// Get CSS status
|
||||
const auto status{getDeviceStatus(upDevice_.state)};
|
||||
// Remove last status if it exists
|
||||
if (!lastStatus_.empty() && box_.get_style_context()->has_class(lastStatus_))
|
||||
box_.get_style_context()->remove_class(lastStatus_);
|
||||
if (!box_.get_style_context()->has_class(status)) box_.get_style_context()->add_class(status);
|
||||
lastStatus_ = status;
|
||||
|
||||
if (devices_.size() == 0 && !upDeviceValid && hideIfEmpty_) {
|
||||
box_.hide();
|
||||
// Call parent update
|
||||
AModule::update();
|
||||
return;
|
||||
}
|
||||
|
||||
label_.set_markup(getText(upDevice_, format_));
|
||||
// Set icon
|
||||
if (upDevice_.icon_name == NULL || !gtkTheme_->has_icon(upDevice_.icon_name))
|
||||
upDevice_.icon_name = (char *)NO_BATTERY.c_str();
|
||||
image_.set_from_icon_name(upDevice_.icon_name, Gtk::ICON_SIZE_INVALID);
|
||||
|
||||
box_.show();
|
||||
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
void UPower::getConn_cb(Glib::RefPtr<Gio::AsyncResult> &result) {
|
||||
try {
|
||||
conn_ = Gio::DBus::Connection::get_finish(result);
|
||||
// Subscribe DBUs events
|
||||
subscrID_ = conn_->signal_subscribe(sigc::mem_fun(*this, &UPower::prepareForSleep_cb),
|
||||
"org.freedesktop.login1", "org.freedesktop.login1.Manager",
|
||||
"PrepareForSleep", "/org/freedesktop/login1");
|
||||
|
||||
} catch (const Glib::Error &e) {
|
||||
spdlog::error("Upower. DBus connection error. {}", e.what().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void UPower::onAppear(const Glib::RefPtr<Gio::DBus::Connection> &conn, const Glib::ustring &name,
|
||||
const Glib::ustring &name_owner) {
|
||||
upRunning_ = true;
|
||||
}
|
||||
|
||||
void UPower::onVanished(const Glib::RefPtr<Gio::DBus::Connection> &conn,
|
||||
const Glib::ustring &name) {
|
||||
upRunning_ = false;
|
||||
}
|
||||
|
||||
void UPower::prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
||||
const Glib::ustring &sender_name, const Glib::ustring &object_path,
|
||||
const Glib::ustring &interface_name,
|
||||
const Glib::ustring &signal_name,
|
||||
const Glib::VariantContainerBase ¶meters) {
|
||||
if (parameters.is_of_type(Glib::VariantType("(b)"))) {
|
||||
Glib::Variant<bool> sleeping;
|
||||
parameters.get_child(sleeping, 0);
|
||||
if (!sleeping.get()) {
|
||||
resetDevices();
|
||||
setDisplayDevice();
|
||||
// Update the widget
|
||||
dp.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::addDevice(UpDevice *device) {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
|
||||
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
|
||||
device = up_device_new();
|
||||
upDevice_output upDevice{.upDevice = device};
|
||||
gboolean ret{up_device_set_object_path_sync(device, objectPath, NULL, NULL)};
|
||||
if (!ret) {
|
||||
g_object_unref(G_OBJECT(device));
|
||||
return;
|
||||
}
|
||||
|
||||
if (devices_.find(objectPath) != devices_.cend()) {
|
||||
auto upDevice{devices_[objectPath]};
|
||||
if (G_IS_OBJECT(upDevice.upDevice)) g_object_unref(upDevice.upDevice);
|
||||
devices_.erase(objectPath);
|
||||
}
|
||||
|
||||
g_signal_connect(device, "notify", G_CALLBACK(deviceNotify_cb), this);
|
||||
devices_.emplace(Devices::value_type(objectPath, upDevice));
|
||||
}
|
||||
}
|
||||
|
||||
void UPower::removeDevice(const gchar *objectPath) {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
if (devices_.find(objectPath) != devices_.cend()) {
|
||||
auto upDevice{devices_[objectPath]};
|
||||
if (G_IS_OBJECT(upDevice.upDevice)) g_object_unref(upDevice.upDevice);
|
||||
devices_.erase(objectPath);
|
||||
}
|
||||
}
|
||||
|
||||
void UPower::removeDevices() {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
if (!devices_.empty()) {
|
||||
auto it{devices_.cbegin()};
|
||||
while (it != devices_.cend()) {
|
||||
if (G_IS_OBJECT(it->second.upDevice)) g_object_unref(it->second.upDevice);
|
||||
devices_.erase(it++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes all devices and adds the current devices
|
||||
void UPower::resetDevices() {
|
||||
// Remove all devices
|
||||
removeDevices();
|
||||
|
||||
// Adds all devices
|
||||
GPtrArray *newDevices = up_client_get_devices2(upClient_);
|
||||
if (newDevices != NULL)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void UPower::setDisplayDevice() {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
|
||||
if (nativePath_.empty()) {
|
||||
upDevice_.upDevice = up_client_get_display_device(upClient_);
|
||||
getUpDeviceInfo(upDevice_);
|
||||
} else {
|
||||
g_ptr_array_foreach(
|
||||
up_client_get_devices2(upClient_),
|
||||
[](gpointer data, gpointer user_data) {
|
||||
upDevice_output upDevice;
|
||||
auto thisPtr{static_cast<UPower *>(user_data)};
|
||||
upDevice.upDevice = static_cast<UpDevice *>(data);
|
||||
thisPtr->getUpDeviceInfo(upDevice);
|
||||
if (0 == std::strcmp(upDevice.nativePath, thisPtr->nativePath_.c_str())) {
|
||||
// Unref current upDevice
|
||||
if (thisPtr->upDevice_.upDevice) g_object_unref(thisPtr->upDevice_.upDevice);
|
||||
// Reassign new upDevice
|
||||
thisPtr->upDevice_ = upDevice;
|
||||
}
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
if (upDevice_.upDevice)
|
||||
g_signal_connect(upDevice_.upDevice, "notify", G_CALLBACK(deviceNotify_cb), this);
|
||||
}
|
||||
|
||||
void UPower::getUpDeviceInfo(upDevice_output &upDevice_) {
|
||||
if (upDevice_.upDevice && G_IS_OBJECT(upDevice_.upDevice)) {
|
||||
g_object_get(upDevice_.upDevice, "kind", &upDevice_.kind, "state", &upDevice_.state,
|
||||
"percentage", &upDevice_.percentage, "icon-name", &upDevice_.icon_name,
|
||||
"time-to-empty", &upDevice_.time_empty, "time-to-full", &upDevice_.time_full,
|
||||
"temperature", &upDevice_.temperature, "native-path", &upDevice_.nativePath,
|
||||
"model", &upDevice_.model, NULL);
|
||||
spdlog::debug(
|
||||
"UPower. getUpDeviceInfo. kind: \"{0}\". state: \"{1}\". percentage: \"{2}\". \
|
||||
icon_name: \"{3}\". time-to-empty: \"{4}\". time-to-full: \"{5}\". temperature: \"{6}\". \
|
||||
native_path: \"{7}\". model: \"{8}\"",
|
||||
fmt::format_int(upDevice_.kind).str(), fmt::format_int(upDevice_.state).str(),
|
||||
upDevice_.percentage, upDevice_.icon_name, upDevice_.time_empty, upDevice_.time_full,
|
||||
upDevice_.temperature, upDevice_.nativePath, upDevice_.model);
|
||||
}
|
||||
}
|
||||
|
||||
const Glib::ustring UPower::getText(const upDevice_output &upDevice_, const std::string &format) {
|
||||
Glib::ustring ret{""};
|
||||
if (upDevice_.upDevice) {
|
||||
std::string timeStr{""};
|
||||
switch (upDevice_.state) {
|
||||
case UP_DEVICE_STATE_CHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_CHARGE:
|
||||
timeStr = secondsToString(std::chrono::seconds(upDevice_.time_full));
|
||||
break;
|
||||
case UP_DEVICE_STATE_DISCHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_DISCHARGE:
|
||||
timeStr = secondsToString(std::chrono::seconds(upDevice_.time_empty));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = fmt::format(
|
||||
fmt::runtime(format),
|
||||
fmt::arg("percentage", std::to_string((int)std::round(upDevice_.percentage)) + '%'),
|
||||
fmt::arg("time", timeStr),
|
||||
fmt::arg("temperature", fmt::format("{:-.2g}C", upDevice_.temperature)),
|
||||
fmt::arg("model", upDevice_.model), fmt::arg("native-path", upDevice_.nativePath));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UPower::queryTooltipCb(int x, int y, bool keyboard_tooltip,
|
||||
const Glib::RefPtr<Gtk::Tooltip> &tooltip) {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
|
||||
// Clear content box
|
||||
contentBox_.forall([this](Gtk::Widget &wg) { contentBox_.remove(wg); });
|
||||
|
||||
// Fill content box with the content
|
||||
for (auto pairDev : devices_) {
|
||||
// Get device info
|
||||
getUpDeviceInfo(pairDev.second);
|
||||
|
||||
if (pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN &&
|
||||
pairDev.second.kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER) {
|
||||
// Make box record
|
||||
Gtk::Box *boxRec{new Gtk::Box{box_.get_orientation(), tooltip_spacing_}};
|
||||
contentBox_.add(*boxRec);
|
||||
Gtk::Box *boxDev{new Gtk::Box{box_.get_orientation()}};
|
||||
Gtk::Box *boxUsr{new Gtk::Box{box_.get_orientation()}};
|
||||
boxRec->add(*boxDev);
|
||||
boxRec->add(*boxUsr);
|
||||
// Construct device box
|
||||
// Set icon from kind
|
||||
std::string iconNameDev{getDeviceIcon(pairDev.second.kind)};
|
||||
if (!gtkTheme_->has_icon(iconNameDev)) iconNameDev = (char *)NO_BATTERY.c_str();
|
||||
Gtk::Image *iconDev{new Gtk::Image{}};
|
||||
iconDev->set_from_icon_name(iconNameDev, Gtk::ICON_SIZE_INVALID);
|
||||
iconDev->set_pixel_size(iconSize_);
|
||||
boxDev->add(*iconDev);
|
||||
// Set label from model
|
||||
Gtk::Label *labelDev{new Gtk::Label{pairDev.second.model}};
|
||||
boxDev->add(*labelDev);
|
||||
// Construct user box
|
||||
// Set icon from icon state
|
||||
if (pairDev.second.icon_name == NULL || !gtkTheme_->has_icon(pairDev.second.icon_name))
|
||||
pairDev.second.icon_name = (char *)NO_BATTERY.c_str();
|
||||
Gtk::Image *iconTooltip{new Gtk::Image{}};
|
||||
iconTooltip->set_from_icon_name(pairDev.second.icon_name, Gtk::ICON_SIZE_INVALID);
|
||||
iconTooltip->set_pixel_size(iconSize_);
|
||||
boxUsr->add(*iconTooltip);
|
||||
// Set markup text
|
||||
Gtk::Label *labelTooltip{new Gtk::Label{}};
|
||||
labelTooltip->set_markup(getText(pairDev.second, tooltipFormat_));
|
||||
boxUsr->add(*labelTooltip);
|
||||
}
|
||||
}
|
||||
tooltip->set_custom(contentBox_);
|
||||
contentBox_.show_all();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace waybar::modules
|
|
@ -1,418 +0,0 @@
|
|||
#include "modules/upower/upower.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "gtkmm/tooltip.h"
|
||||
#include "util/gtk_icon.hpp"
|
||||
|
||||
static const char* getDeviceWarningLevel(UpDeviceLevel level) {
|
||||
switch (level) {
|
||||
case UP_DEVICE_LEVEL_CRITICAL:
|
||||
return "critical";
|
||||
case UP_DEVICE_LEVEL_LOW:
|
||||
return "low";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
showAltText(false) {
|
||||
// Show icon only when "show-icon" isn't set to false
|
||||
if (config_["show-icon"].isBool()) {
|
||||
showIcon = config_["show-icon"].asBool();
|
||||
}
|
||||
|
||||
if (showIcon) {
|
||||
box_.pack_start(icon_);
|
||||
}
|
||||
|
||||
box_.pack_start(label_);
|
||||
box_.set_name(name_);
|
||||
event_box_.add(box_);
|
||||
|
||||
// Device user wants
|
||||
if (config_["native-path"].isString()) nativePath_ = config_["native-path"].asString();
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Format
|
||||
if (config_["format"].isString()) {
|
||||
format = config_["format"].asString();
|
||||
}
|
||||
|
||||
// Format Alt
|
||||
if (config_["format-alt"].isString()) {
|
||||
format_alt = config_["format-alt"].asString();
|
||||
}
|
||||
|
||||
// Tooltip Spacing
|
||||
if (config_["tooltip-spacing"].isUInt()) {
|
||||
tooltip_spacing = config_["tooltip-spacing"].asUInt();
|
||||
}
|
||||
|
||||
// Tooltip Padding
|
||||
if (config_["tooltip-padding"].isUInt()) {
|
||||
tooltip_padding = config_["tooltip-padding"].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 = std::make_unique<UPowerTooltip>(iconSize, tooltip_spacing, tooltip_padding);
|
||||
box_.set_tooltip_window(*upower_tooltip);
|
||||
box_.signal_query_tooltip().connect(sigc::mem_fun(*this, &UPower::show_tooltip_callback));
|
||||
}
|
||||
|
||||
upowerWatcher_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, "org.freedesktop.UPower",
|
||||
G_BUS_NAME_WATCHER_FLAGS_AUTO_START, upowerAppear,
|
||||
upowerDisappear, this, NULL);
|
||||
|
||||
client = up_client_new_full(NULL, NULL);
|
||||
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, NULL);
|
||||
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);
|
||||
}
|
||||
|
||||
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &UPower::handleToggle));
|
||||
|
||||
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 (displayDevice != NULL) g_object_unref(displayDevice);
|
||||
if (client != NULL) g_object_unref(client);
|
||||
if (login1_id > 0) {
|
||||
g_dbus_connection_signal_unsubscribe(login1_connection, login1_id);
|
||||
login1_id = 0;
|
||||
}
|
||||
g_bus_unwatch_name(upowerWatcher_id);
|
||||
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::upowerAppear(GDBusConnection* conn, const gchar* name, const gchar* name_owner,
|
||||
gpointer data) {
|
||||
UPower* up = static_cast<UPower*>(data);
|
||||
up->upowerRunning = true;
|
||||
up->event_box_.set_visible(true);
|
||||
}
|
||||
void UPower::upowerDisappear(GDBusConnection* conn, const gchar* name, gpointer data) {
|
||||
UPower* up = static_cast<UPower*>(data);
|
||||
up->upowerRunning = false;
|
||||
up->event_box_.set_visible(false);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (nativePath_.empty())
|
||||
displayDevice = up_client_get_display_device(client);
|
||||
else {
|
||||
g_ptr_array_foreach(
|
||||
up_client_get_devices2(client),
|
||||
[](gpointer data, gpointer user_data) {
|
||||
UpDevice* device{static_cast<UpDevice*>(data)};
|
||||
UPower* thisPtr{static_cast<UPower*>(user_data)};
|
||||
gchar* nativePath;
|
||||
if (!thisPtr->displayDevice) {
|
||||
g_object_get(device, "native-path", &nativePath, NULL);
|
||||
if (!std::strcmp(nativePath, thisPtr->nativePath_.c_str()))
|
||||
thisPtr->displayDevice = device;
|
||||
}
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
if (displayDevice) 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;
|
||||
}
|
||||
|
||||
const std::string UPower::getDeviceStatus(UpDeviceState& state) {
|
||||
switch (state) {
|
||||
case UP_DEVICE_STATE_CHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_CHARGE:
|
||||
return "charging";
|
||||
case UP_DEVICE_STATE_DISCHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_DISCHARGE:
|
||||
return "discharging";
|
||||
case UP_DEVICE_STATE_FULLY_CHARGED:
|
||||
return "full";
|
||||
case UP_DEVICE_STATE_EMPTY:
|
||||
return "empty";
|
||||
default:
|
||||
return "unknown-status";
|
||||
}
|
||||
}
|
||||
|
||||
bool UPower::handleToggle(GdkEventButton* const& event) {
|
||||
std::lock_guard<std::mutex> guard(m_Mutex);
|
||||
showAltText = !showAltText;
|
||||
return AModule::handleToggle(event);
|
||||
}
|
||||
|
||||
std::string UPower::timeToString(gint64 time) {
|
||||
if (time == 0) return "";
|
||||
float hours = (float)time / 3600;
|
||||
float hours_fixed = static_cast<float>(static_cast<int>(hours * 10)) / 10;
|
||||
float minutes = static_cast<float>(static_cast<int>(hours * 60 * 10)) / 10;
|
||||
if (hours_fixed >= 1) {
|
||||
return fmt::format("{H} h", fmt::arg("H", hours_fixed));
|
||||
} else {
|
||||
return fmt::format("{M} min", fmt::arg("M", minutes));
|
||||
}
|
||||
}
|
||||
|
||||
auto UPower::update() -> void {
|
||||
std::lock_guard<std::mutex> guard(m_Mutex);
|
||||
|
||||
// Don't update widget if the UPower service isn't running
|
||||
if (!upowerRunning) {
|
||||
if (hideIfEmpty) {
|
||||
event_box_.set_visible(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UpDeviceKind kind = UP_DEVICE_KIND_UNKNOWN;
|
||||
UpDeviceState state = UP_DEVICE_STATE_UNKNOWN;
|
||||
UpDeviceLevel level = UP_DEVICE_LEVEL_UNKNOWN;
|
||||
double percentage = 0.0;
|
||||
gint64 time_empty = 0;
|
||||
gint64 time_full = 0;
|
||||
gchar* icon_name{(char*)'\0'};
|
||||
std::string percentString{""};
|
||||
std::string time_format{""};
|
||||
|
||||
bool displayDeviceValid{false};
|
||||
|
||||
if (displayDevice) {
|
||||
g_object_get(displayDevice, "kind", &kind, "state", &state, "percentage", &percentage,
|
||||
"icon-name", &icon_name, "time-to-empty", &time_empty, "time-to-full", &time_full,
|
||||
"warning-level", &level, NULL);
|
||||
/* Every Device which is handled by Upower and which is not
|
||||
* UP_DEVICE_KIND_UNKNOWN (0) or UP_DEVICE_KIND_LINE_POWER (1) is a Battery
|
||||
*/
|
||||
displayDeviceValid = (kind != UpDeviceKind::UP_DEVICE_KIND_UNKNOWN &&
|
||||
kind != UpDeviceKind::UP_DEVICE_KIND_LINE_POWER);
|
||||
}
|
||||
|
||||
// CSS status class
|
||||
const std::string status = getDeviceStatus(state);
|
||||
// Remove last status if it exists
|
||||
if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) {
|
||||
box_.get_style_context()->remove_class(lastStatus);
|
||||
}
|
||||
// Add the new status class to the Box
|
||||
if (!box_.get_style_context()->has_class(status)) {
|
||||
box_.get_style_context()->add_class(status);
|
||||
}
|
||||
lastStatus = status;
|
||||
|
||||
const char* warning_level = getDeviceWarningLevel(level);
|
||||
if (lastWarningLevel && box_.get_style_context()->has_class(lastWarningLevel)) {
|
||||
box_.get_style_context()->remove_class(lastWarningLevel);
|
||||
}
|
||||
if (warning_level && !box_.get_style_context()->has_class(warning_level)) {
|
||||
box_.get_style_context()->add_class(warning_level);
|
||||
}
|
||||
lastWarningLevel = warning_level;
|
||||
|
||||
if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) {
|
||||
event_box_.set_visible(false);
|
||||
// Call parent update
|
||||
AModule::update();
|
||||
return;
|
||||
}
|
||||
|
||||
event_box_.set_visible(true);
|
||||
|
||||
if (displayDeviceValid) {
|
||||
// Tooltip
|
||||
if (tooltip_enabled) {
|
||||
uint 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
|
||||
percentString = std::to_string(int(percentage + 0.5)) + "%";
|
||||
|
||||
// Label format
|
||||
switch (state) {
|
||||
case UP_DEVICE_STATE_CHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_CHARGE:
|
||||
time_format = timeToString(time_full);
|
||||
break;
|
||||
case UP_DEVICE_STATE_DISCHARGING:
|
||||
case UP_DEVICE_STATE_PENDING_DISCHARGE:
|
||||
time_format = timeToString(time_empty);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string label_format =
|
||||
fmt::format(fmt::runtime(showAltText ? format_alt : format),
|
||||
fmt::arg("percentage", percentString), fmt::arg("time", time_format));
|
||||
// Only set the label text if it doesn't only contain spaces
|
||||
bool onlySpaces = true;
|
||||
for (auto& character : label_format) {
|
||||
if (character == ' ') continue;
|
||||
onlySpaces = false;
|
||||
break;
|
||||
}
|
||||
label_.set_markup(onlySpaces ? "" : label_format);
|
||||
|
||||
// Set icon
|
||||
if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) {
|
||||
icon_name = (char*)"battery-missing-symbolic";
|
||||
}
|
||||
icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
|
||||
|
||||
// Call parent update
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::upower
|
|
@ -1,160 +0,0 @@
|
|||
#include "modules/upower/upower_tooltip.hpp"
|
||||
|
||||
#include "gtkmm/box.h"
|
||||
#include "gtkmm/enums.h"
|
||||
#include "gtkmm/image.h"
|
||||
#include "gtkmm/label.h"
|
||||
#include "util/gtk_icon.hpp"
|
||||
|
||||
namespace waybar::modules::upower {
|
||||
UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_)
|
||||
: Gtk::Window(),
|
||||
contentBox(std::make_unique<Gtk::Box>(Gtk::ORIENTATION_VERTICAL)),
|
||||
iconSize(iconSize_),
|
||||
tooltipSpacing(tooltipSpacing_),
|
||||
tooltipPadding(tooltipPadding_) {
|
||||
// Sets the Tooltip Padding
|
||||
contentBox->set_margin_top(tooltipPadding);
|
||||
contentBox->set_margin_bottom(tooltipPadding);
|
||||
contentBox->set_margin_left(tooltipPadding);
|
||||
contentBox->set_margin_right(tooltipPadding);
|
||||
|
||||
add(*contentBox);
|
||||
contentBox->show();
|
||||
}
|
||||
|
||||
UPowerTooltip::~UPowerTooltip() {}
|
||||
|
||||
uint UPowerTooltip::updateTooltip(Devices& devices) {
|
||||
// Removes all old devices
|
||||
for (auto child : contentBox->get_children()) {
|
||||
delete child;
|
||||
}
|
||||
|
||||
uint deviceCount = 0;
|
||||
// Adds all valid devices
|
||||
for (auto pair : devices) {
|
||||
UpDevice* device = pair.second;
|
||||
std::string objectPath = pair.first;
|
||||
|
||||
if (!G_IS_OBJECT(device)) continue;
|
||||
|
||||
Gtk::Box* box = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, tooltipSpacing);
|
||||
|
||||
UpDeviceKind kind;
|
||||
double percentage;
|
||||
gchar* native_path;
|
||||
gchar* model;
|
||||
gchar* icon_name;
|
||||
|
||||
g_object_get(device, "kind", &kind, "percentage", &percentage, "native-path", &native_path,
|
||||
"model", &model, "icon-name", &icon_name, NULL);
|
||||
|
||||
// Skip Line_Power and BAT0 devices
|
||||
if (kind == UP_DEVICE_KIND_LINE_POWER || native_path == NULL || strlen(native_path) == 0 ||
|
||||
strcmp(native_path, "BAT0") == 0)
|
||||
continue;
|
||||
|
||||
Gtk::Box* modelBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL);
|
||||
box->add(*modelBox);
|
||||
// Set device icon
|
||||
std::string deviceIconName = getDeviceIcon(kind);
|
||||
Gtk::Image* deviceIcon = new Gtk::Image();
|
||||
deviceIcon->set_pixel_size(iconSize);
|
||||
if (!DefaultGtkIconThemeWrapper::has_icon(deviceIconName)) {
|
||||
deviceIconName = "battery-missing-symbolic";
|
||||
}
|
||||
deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID);
|
||||
modelBox->add(*deviceIcon);
|
||||
|
||||
// Set model
|
||||
if (model == NULL) model = (gchar*)"";
|
||||
Gtk::Label* modelLabel = new Gtk::Label(model);
|
||||
modelBox->add(*modelLabel);
|
||||
|
||||
Gtk::Box* chargeBox = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL);
|
||||
box->add(*chargeBox);
|
||||
|
||||
// Set icon
|
||||
Gtk::Image* icon = new Gtk::Image();
|
||||
icon->set_pixel_size(iconSize);
|
||||
if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) {
|
||||
icon_name = (char*)"battery-missing-symbolic";
|
||||
}
|
||||
icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID);
|
||||
chargeBox->add(*icon);
|
||||
|
||||
// Set percentage
|
||||
std::string percentString = std::to_string(int(percentage + 0.5)) + "%";
|
||||
Gtk::Label* percentLabel = new Gtk::Label(percentString);
|
||||
chargeBox->add(*percentLabel);
|
||||
|
||||
contentBox->add(*box);
|
||||
|
||||
deviceCount++;
|
||||
}
|
||||
|
||||
contentBox->show_all();
|
||||
return deviceCount;
|
||||
}
|
||||
|
||||
const std::string UPowerTooltip::getDeviceIcon(UpDeviceKind& kind) {
|
||||
switch (kind) {
|
||||
case UP_DEVICE_KIND_LINE_POWER:
|
||||
return "ac-adapter-symbolic";
|
||||
case UP_DEVICE_KIND_BATTERY:
|
||||
return "battery";
|
||||
case UP_DEVICE_KIND_UPS:
|
||||
return "uninterruptible-power-supply-symbolic";
|
||||
case UP_DEVICE_KIND_MONITOR:
|
||||
return "video-display-symbolic";
|
||||
case UP_DEVICE_KIND_MOUSE:
|
||||
return "input-mouse-symbolic";
|
||||
case UP_DEVICE_KIND_KEYBOARD:
|
||||
return "input-keyboard-symbolic";
|
||||
case UP_DEVICE_KIND_PDA:
|
||||
return "pda-symbolic";
|
||||
case UP_DEVICE_KIND_PHONE:
|
||||
return "phone-symbolic";
|
||||
case UP_DEVICE_KIND_MEDIA_PLAYER:
|
||||
return "multimedia-player-symbolic";
|
||||
case UP_DEVICE_KIND_TABLET:
|
||||
return "computer-apple-ipad-symbolic";
|
||||
case UP_DEVICE_KIND_COMPUTER:
|
||||
return "computer-symbolic";
|
||||
case UP_DEVICE_KIND_GAMING_INPUT:
|
||||
return "input-gaming-symbolic";
|
||||
case UP_DEVICE_KIND_PEN:
|
||||
return "input-tablet-symbolic";
|
||||
case UP_DEVICE_KIND_TOUCHPAD:
|
||||
return "input-touchpad-symbolic";
|
||||
case UP_DEVICE_KIND_MODEM:
|
||||
return "modem-symbolic";
|
||||
case UP_DEVICE_KIND_NETWORK:
|
||||
return "network-wired-symbolic";
|
||||
case UP_DEVICE_KIND_HEADSET:
|
||||
return "audio-headset-symbolic";
|
||||
case UP_DEVICE_KIND_HEADPHONES:
|
||||
return "audio-headphones-symbolic";
|
||||
case UP_DEVICE_KIND_OTHER_AUDIO:
|
||||
case UP_DEVICE_KIND_SPEAKERS:
|
||||
return "audio-speakers-symbolic";
|
||||
case UP_DEVICE_KIND_VIDEO:
|
||||
return "camera-web-symbolic";
|
||||
case UP_DEVICE_KIND_PRINTER:
|
||||
return "printer-symbolic";
|
||||
case UP_DEVICE_KIND_SCANNER:
|
||||
return "scanner-symbolic";
|
||||
case UP_DEVICE_KIND_CAMERA:
|
||||
return "camera-photo-symbolic";
|
||||
case UP_DEVICE_KIND_BLUETOOTH_GENERIC:
|
||||
return "bluetooth-active-symbolic";
|
||||
case UP_DEVICE_KIND_TOY:
|
||||
case UP_DEVICE_KIND_REMOTE_CONTROL:
|
||||
case UP_DEVICE_KIND_WEARABLE:
|
||||
case UP_DEVICE_KIND_LAST:
|
||||
default:
|
||||
return "battery-symbolic";
|
||||
}
|
||||
}
|
||||
} // namespace waybar::modules::upower
|
Loading…
Reference in New Issue