diff --git a/include/factory.hpp b/include/factory.hpp index 3855ce22..3954dac4 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -42,6 +42,9 @@ #ifdef HAVE_LIBEVDEV #include "modules/keyboard_state.hpp" #endif +#ifdef HAVE_UPOWER +#include "modules/upower/upower.hpp" +#endif #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif diff --git a/include/modules/upower/upower.hpp b/include/modules/upower/upower.hpp new file mode 100644 index 00000000..5b4d2f5f --- /dev/null +++ b/include/modules/upower/upower.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include +#include +#include + +#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 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 &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 diff --git a/include/modules/upower/upower_tooltip.hpp b/include/modules/upower/upower_tooltip.hpp new file mode 100644 index 00000000..9d36a70b --- /dev/null +++ b/include/modules/upower/upower_tooltip.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#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 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 diff --git a/man/waybar-upower.5.scd b/man/waybar-upower.5.scd new file mode 100644 index 00000000..e6f63076 --- /dev/null +++ b/man/waybar-upower.5.scd @@ -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* diff --git a/meson.build b/meson.build index dcc61870..e9daeca6 100644 --- a/meson.build +++ b/meson.build @@ -86,12 +86,13 @@ wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0']) 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') sigcpp = dependency('sigc++-2.0') libepoll = dependency('epoll-shim', required: false) libnl = dependency('libnl-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')) libudev = dependency('libudev', required: get_option('libudev')) libevdev = dependency('libevdev', required: get_option('libevdev')) @@ -203,6 +204,12 @@ if libnl.found() and libnlgen.found() src_files += 'src/modules/network.cpp' 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() add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp') src_files += 'src/modules/pulseaudio.cpp' @@ -288,6 +295,7 @@ executable( giounix, libnl, libnlgen, + upower_glib, libpulse, libudev, libepoll, @@ -296,7 +304,7 @@ executable( gtk_layer_shell, libsndio, tz_dep, - xkbregistry + xkbregistry ], include_directories: [include_directories('include')], install: true, diff --git a/meson_options.txt b/meson_options.txt index 230a53d6..d2e98476 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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('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('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower') 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('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') diff --git a/src/factory.cpp b/src/factory.cpp index 900653b5..ab0dc435 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -12,6 +12,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Battery(id, config_[name]); } #endif +#ifdef HAVE_UPOWER + if (ref == "upower") { + return new waybar::modules::upower::UPower(id, config_[name]); + } +#endif #ifdef HAVE_SWAY if (ref == "sway/mode") { return new waybar::modules::sway::Mode(id, config_[name]); diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp new file mode 100644 index 00000000..f6285146 --- /dev/null +++ b/src/modules/upower/upower.cpp @@ -0,0 +1,356 @@ +#include "modules/upower/upower.hpp" + +#include + +#include +#include + +#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(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(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(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(data); + up->resetDevices(); + up->setDisplayDevice(); + } + } +} + +void UPower::removeDevice(const gchar* objectPath) { + std::lock_guard 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 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 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 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& 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 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(static_cast(hours * 10)) / 10; + float minutes = static_cast(static_cast(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 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 diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp new file mode 100644 index 00000000..644b8e08 --- /dev/null +++ b/src/modules/upower/upower_tooltip.cpp @@ -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