commit
d4a07483b2
|
@ -42,6 +42,9 @@
|
||||||
#ifdef HAVE_LIBEVDEV
|
#ifdef HAVE_LIBEVDEV
|
||||||
#include "modules/keyboard_state.hpp"
|
#include "modules/keyboard_state.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_UPOWER
|
||||||
|
#include "modules/upower/upower.hpp"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBPULSE
|
#ifdef HAVE_LIBPULSE
|
||||||
#include "modules/pulseaudio.hpp"
|
#include "modules/pulseaudio.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libupower-glib/upower.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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 &);
|
||||||
|
~UPower();
|
||||||
|
auto update() -> void;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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 &);
|
||||||
|
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;
|
||||||
|
guint login1_id;
|
||||||
|
GDBusConnection *login1_connection;
|
||||||
|
UPowerTooltip *upower_tooltip;
|
||||||
|
std::string lastStatus;
|
||||||
|
bool showAltText;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::upower
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libupower-glib/upower.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
Gtk::Box* contentBox;
|
||||||
|
|
||||||
|
uint iconSize;
|
||||||
|
uint tooltipSpacing;
|
||||||
|
uint tooltipPadding;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding);
|
||||||
|
~UPowerTooltip();
|
||||||
|
|
||||||
|
uint updateTooltip(Devices& devices);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace waybar::modules::upower
|
|
@ -0,0 +1,74 @@
|
||||||
|
waybar-upower(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
waybar - upower module
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The *upower* module displays the main battery capacity with all other upower
|
||||||
|
compatible devices in the tooltip.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
*icon-size*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 20 ++
|
||||||
|
Defines the size of the icons.
|
||||||
|
|
||||||
|
*format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {percentage} ++
|
||||||
|
The text format.
|
||||||
|
|
||||||
|
*format-alt*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {percentage} {time} ++
|
||||||
|
The text format when toggled.
|
||||||
|
|
||||||
|
*hide-if-empty*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: true ++
|
||||||
|
Defines visibility of the module if no devices can be found.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
defualt: true ++
|
||||||
|
Option to disable tooltip on hover.
|
||||||
|
|
||||||
|
*tooltip-spacing*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 4 ++
|
||||||
|
Defines the spacing between the tooltip device name and device battery ++
|
||||||
|
status.
|
||||||
|
|
||||||
|
*tooltip-padding*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
default: 4 ++
|
||||||
|
Defines the spacing between the tooltip window edge and the tooltip content.
|
||||||
|
|
||||||
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
|
*{percentage}*: The battery capacity in percentage
|
||||||
|
|
||||||
|
*{time}*: An estimated time either until empty or until fully charged ++
|
||||||
|
depending on the charging state.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
"upower": {
|
||||||
|
"icon-size": 20,
|
||||||
|
"hide-if-empty": true,
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-spacing": 20
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# STYLE
|
||||||
|
|
||||||
|
- *#upower*
|
||||||
|
- *#upower.charging*
|
||||||
|
- *#upower.discharging*
|
||||||
|
- *#upower.unknown-status*
|
12
meson.build
12
meson.build
|
@ -86,12 +86,13 @@ wayland_cursor = dependency('wayland-cursor')
|
||||||
wayland_protos = dependency('wayland-protocols')
|
wayland_protos = dependency('wayland-protocols')
|
||||||
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
||||||
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
||||||
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled()))
|
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled() or get_option('upower_glib').enabled()))
|
||||||
jsoncpp = dependency('jsoncpp')
|
jsoncpp = dependency('jsoncpp')
|
||||||
sigcpp = dependency('sigc++-2.0')
|
sigcpp = dependency('sigc++-2.0')
|
||||||
libepoll = dependency('epoll-shim', required: false)
|
libepoll = dependency('epoll-shim', required: false)
|
||||||
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
||||||
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
||||||
|
upower_glib = dependency('upower-glib', required: get_option('upower_glib'))
|
||||||
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
||||||
libudev = dependency('libudev', required: get_option('libudev'))
|
libudev = dependency('libudev', required: get_option('libudev'))
|
||||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||||
|
@ -203,6 +204,12 @@ if libnl.found() and libnlgen.found()
|
||||||
src_files += 'src/modules/network.cpp'
|
src_files += 'src/modules/network.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if (upower_glib.found() and giounix.found() and not get_option('logind').disabled())
|
||||||
|
add_project_arguments('-DHAVE_UPOWER', language: 'cpp')
|
||||||
|
src_files += 'src/modules/upower/upower.cpp'
|
||||||
|
src_files += 'src/modules/upower/upower_tooltip.cpp'
|
||||||
|
endif
|
||||||
|
|
||||||
if libpulse.found()
|
if libpulse.found()
|
||||||
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
||||||
src_files += 'src/modules/pulseaudio.cpp'
|
src_files += 'src/modules/pulseaudio.cpp'
|
||||||
|
@ -288,6 +295,7 @@ executable(
|
||||||
giounix,
|
giounix,
|
||||||
libnl,
|
libnl,
|
||||||
libnlgen,
|
libnlgen,
|
||||||
|
upower_glib,
|
||||||
libpulse,
|
libpulse,
|
||||||
libudev,
|
libudev,
|
||||||
libepoll,
|
libepoll,
|
||||||
|
@ -296,7 +304,7 @@ executable(
|
||||||
gtk_layer_shell,
|
gtk_layer_shell,
|
||||||
libsndio,
|
libsndio,
|
||||||
tz_dep,
|
tz_dep,
|
||||||
xkbregistry
|
xkbregistry
|
||||||
],
|
],
|
||||||
include_directories: [include_directories('include')],
|
include_directories: [include_directories('include')],
|
||||||
install: true,
|
install: true,
|
||||||
|
|
|
@ -3,6 +3,7 @@ option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl suppo
|
||||||
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
|
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
|
||||||
option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features')
|
option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features')
|
||||||
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
||||||
|
option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower')
|
||||||
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
||||||
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
||||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
||||||
|
|
|
@ -12,6 +12,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||||
return new waybar::modules::Battery(id, config_[name]);
|
return new waybar::modules::Battery(id, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_UPOWER
|
||||||
|
if (ref == "upower") {
|
||||||
|
return new waybar::modules::upower::UPower(id, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
if (ref == "sway/mode") {
|
if (ref == "sway/mode") {
|
||||||
return new waybar::modules::sway::Mode(id, config_[name]);
|
return new waybar::modules::sway::Mode(id, config_[name]);
|
||||||
|
|
|
@ -0,0 +1,356 @@
|
||||||
|
#include "modules/upower/upower.hpp"
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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(),
|
||||||
|
showAltText(false) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = new 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_EMPTY:
|
||||||
|
case UP_DEVICE_STATE_FULLY_CHARGED:
|
||||||
|
case UP_DEVICE_STATE_DISCHARGING:
|
||||||
|
case UP_DEVICE_STATE_PENDING_DISCHARGE:
|
||||||
|
return "discharging";
|
||||||
|
default:
|
||||||
|
return "unknown-status";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPower::handleToggle(GdkEventButton* const& event) {
|
||||||
|
std::lock_guard<std::mutex> guard(m_Mutex);
|
||||||
|
showAltText = !showAltText;
|
||||||
|
dp.emit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
UpDeviceKind kind;
|
||||||
|
UpDeviceState state;
|
||||||
|
double percentage;
|
||||||
|
gint64 time_empty;
|
||||||
|
gint64 time_full;
|
||||||
|
gchar* icon_name;
|
||||||
|
|
||||||
|
g_object_get(displayDevice,
|
||||||
|
"kind",
|
||||||
|
&kind,
|
||||||
|
"state",
|
||||||
|
&state,
|
||||||
|
"percentage",
|
||||||
|
&percentage,
|
||||||
|
"icon-name",
|
||||||
|
&icon_name,
|
||||||
|
"time-to-empty",
|
||||||
|
&time_empty,
|
||||||
|
"time-to-full",
|
||||||
|
&time_full,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
bool displayDeviceValid =
|
||||||
|
kind == UpDeviceKind::UP_DEVICE_KIND_BATTERY || kind == UpDeviceKind::UP_DEVICE_KIND_UPS;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (devices.size() == 0 && !displayDeviceValid && hideIfEmpty) {
|
||||||
|
event_box_.set_visible(false);
|
||||||
|
// Call parent update
|
||||||
|
AModule::update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_box_.set_visible(true);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
std::string percentString = "";
|
||||||
|
if (displayDeviceValid) {
|
||||||
|
percentString = std::to_string(int(percentage + 0.5)) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label format
|
||||||
|
std::string time_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(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 (!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);
|
||||||
|
|
||||||
|
// Call parent update
|
||||||
|
AModule::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace waybar::modules::upower
|
|
@ -0,0 +1,168 @@
|
||||||
|
#include "modules/upower/upower_tooltip.hpp"
|
||||||
|
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
#include "gtkmm/enums.h"
|
||||||
|
#include "gtkmm/icontheme.h"
|
||||||
|
#include "gtkmm/image.h"
|
||||||
|
#include "gtkmm/label.h"
|
||||||
|
|
||||||
|
namespace waybar::modules::upower {
|
||||||
|
UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_)
|
||||||
|
: Gtk::Window(),
|
||||||
|
iconSize(iconSize_),
|
||||||
|
tooltipSpacing(tooltipSpacing_),
|
||||||
|
tooltipPadding(tooltipPadding_) {
|
||||||
|
contentBox = new Gtk::Box(Gtk::ORIENTATION_VERTICAL);
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
child->~Widget();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 || 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 (!Gtk::IconTheme::get_default()->has_icon(deviceIconName)) {
|
||||||
|
deviceIconName = "battery-missing-symbolic";
|
||||||
|
}
|
||||||
|
deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID);
|
||||||
|
modelBox->add(*deviceIcon);
|
||||||
|
|
||||||
|
// Set model
|
||||||
|
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 (!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);
|
||||||
|
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