mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
f0bead34d4 | |||
d6bd440027 | |||
c692d7bf64 | |||
2045aac5b0 | |||
a92223c316 | |||
24d03d13ce | |||
b3b5d8f9ab | |||
86850f5c7a | |||
0bc5314e08 | |||
1d6cfe7ce6 | |||
2a3ebc12de | |||
1938bb5d28 | |||
a35861b3b9 | |||
8b512e7b22 | |||
fb083f93dc | |||
f795e7a308 | |||
21abd4f9f9 | |||
f724cc3f9d | |||
bfbb2f9a40 | |||
91357f210d | |||
3e48551f25 | |||
c05f41d732 | |||
4d59de42af | |||
6e296838e4 | |||
e00e36981e | |||
4136ffaecb | |||
bd199e414b | |||
531bdfb8bb | |||
c1ea7626b9 | |||
995802e8ae | |||
0079092699 | |||
b5c686c0dd | |||
4c4d09992e | |||
9218968d2f | |||
a08967e008 | |||
272c638f7e | |||
57ad7f9536 | |||
2a76d8e5b9 | |||
c5babb4c44 | |||
328575a721 | |||
ea9078d887 | |||
b1833b1f36 | |||
53e89dace7 | |||
3cbcef61cf | |||
22084691ff | |||
f4afa59861 | |||
ce8c13788a | |||
b74f3c7aaa | |||
2111865efe | |||
94d6ae9741 | |||
e6760bf9dd | |||
7671ccfbc6 | |||
459541ed89 | |||
da3d9533d1 | |||
8db1996ccc | |||
cfef78a5bc | |||
60fa5e9f67 | |||
cea59ddc6c | |||
3730793197 | |||
d2b4076ac8 | |||
e63e3a0ca9 | |||
99d370d9ed | |||
80b2b29a77 | |||
27ad9ec267 | |||
9eb6c4e296 | |||
748c6125d0 | |||
235861fd3d | |||
5e9bbe5c76 | |||
74fa131ebe | |||
2c7cb0e9d4 | |||
ce8ae5bf17 | |||
062e7bb9b4 | |||
3acd31c3e9 | |||
456e06c4b5 | |||
a2751cfcd6 | |||
d9cc995405 | |||
00a2ebf00d | |||
c2f98d07ef | |||
833dcc1bb8 | |||
8c24e26f0e | |||
56b4a11a9c | |||
1111763251 | |||
769858fbb4 | |||
2695815bcc | |||
49afb87e34 | |||
5250123dcb | |||
c0b3e9ee35 | |||
454ba610f4 | |||
3718902b9d | |||
9f0a14c22b | |||
781da93f3d | |||
8f4f67f69f | |||
8be5bab8ad | |||
d02e23c759 | |||
d2b22c6ec5 | |||
ed898cd211 | |||
1a1c617520 | |||
253222d31c | |||
51e6fc6250 | |||
af1668dfd0 | |||
cf5877073a | |||
bd567800c9 | |||
6477e539d0 | |||
242e19a07d | |||
0e53c37d6b | |||
3030850b22 | |||
92cc01f401 | |||
d48eebd4d3 | |||
eb705533b5 | |||
3cf027fc56 | |||
09120caf17 | |||
73495df377 | |||
31137c30fb | |||
41dea6e46c | |||
a650c7d90c |
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,6 +2,8 @@
|
||||
*~
|
||||
vgcore.*
|
||||
/.vscode
|
||||
/.idea
|
||||
/.cache
|
||||
*.swp
|
||||
packagecache
|
||||
/subprojects/**/
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata
|
||||
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml-dev libnl3-dev pulseaudio-dev libmpdclient-dev sndio-dev scdoc libxkbcommon tzdata playerctl-dev
|
||||
|
@ -3,4 +3,4 @@
|
||||
FROM archlinux:base-devel
|
||||
|
||||
RUN pacman -Syu --noconfirm && \
|
||||
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection --noconfirm libxkbcommon
|
||||
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl
|
||||
|
@ -3,5 +3,5 @@
|
||||
FROM debian:sid
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 && \
|
||||
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc libdbusmenu-gtk3-dev libnl-3-dev libnl-genl-3-dev libpulse-dev libmpdclient-dev gobject-introspection libgirepository1.0-dev libxkbcommon-dev libxkbregistry-dev libxkbregistry0 libplayerctl-dev && \
|
||||
apt-get clean
|
||||
|
@ -8,5 +8,6 @@ RUN dnf install -y @c-development git-core meson scdoc 'pkgconfig(date)' \
|
||||
'pkgconfig(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \
|
||||
'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \
|
||||
'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \
|
||||
'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' && \
|
||||
'pkgconfig(wayland-client)' 'pkgconfig(wayland-cursor)' 'pkgconfig(wayland-protocols)' 'pkgconfig(xkbregistry)' \
|
||||
'pkgconfig(playerctl)' && \
|
||||
dnf clean all -y
|
||||
|
@ -8,4 +8,4 @@ RUN export FEATURES="-ipc-sandbox -network-sandbox -pid-sandbox -sandbox -usersa
|
||||
emerge --verbose --update --deep --with-bdeps=y --backtrack=30 --newuse @world && \
|
||||
USE="wayland gtk3 gtk -doc X" emerge dev-vcs/git dev-libs/wayland dev-libs/wayland-protocols =dev-cpp/gtkmm-3.24.6 x11-libs/libxkbcommon \
|
||||
x11-libs/gtk+:3 dev-libs/libdbusmenu dev-libs/libnl sys-power/upower media-libs/libpulse dev-libs/libevdev media-libs/libmpdclient \
|
||||
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc
|
||||
media-sound/sndio gui-libs/gtk-layer-shell app-text/scdoc media-sound/playerctl
|
||||
|
@ -6,4 +6,4 @@ RUN zypper -n up && \
|
||||
zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
|
||||
zypper -n refresh && \
|
||||
zypper -n install -t pattern devel_C_C++ && \
|
||||
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc
|
||||
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel
|
||||
|
@ -16,12 +16,14 @@
|
||||
- Network
|
||||
- Bluetooth
|
||||
- Pulseaudio
|
||||
- Wireplumber
|
||||
- Disk
|
||||
- Memory
|
||||
- Cpu load average
|
||||
- Temperature
|
||||
- MPD
|
||||
- Custom scripts
|
||||
- Custom image
|
||||
- Multiple output configuration
|
||||
- And many more customizations
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <glibmm/markup.h>
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/cssprovider.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include "AModule.hpp"
|
||||
|
||||
namespace waybar {
|
||||
|
||||
class AButton : public AModule {
|
||||
public:
|
||||
AButton(const Json::Value &, const std::string &, const std::string &, const std::string &format,
|
||||
uint16_t interval = 0, bool ellipsize = false, bool enable_click = false,
|
||||
bool enable_scroll = false);
|
||||
virtual ~AButton() = default;
|
||||
virtual auto update() -> void;
|
||||
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
|
||||
virtual std::string getIcon(uint16_t, const std::vector<std::string> &alts, uint16_t max = 0);
|
||||
|
||||
protected:
|
||||
Gtk::Button button_ = Gtk::Button(name_);
|
||||
Gtk::Label *label_ = (Gtk::Label *)button_.get_child();
|
||||
std::string format_;
|
||||
const std::chrono::seconds interval_;
|
||||
bool alt_ = false;
|
||||
std::string default_format_;
|
||||
|
||||
virtual bool handleToggle(GdkEventButton *const &e);
|
||||
virtual std::string getState(uint8_t value, bool lesser = false);
|
||||
};
|
||||
|
||||
} // namespace waybar
|
@ -25,6 +25,7 @@
|
||||
#ifdef HAVE_HYPRLAND
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "modules/hyprland/language.hpp"
|
||||
#include "modules/hyprland/submap.hpp"
|
||||
#include "modules/hyprland/window.hpp"
|
||||
#endif
|
||||
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
||||
@ -41,6 +42,9 @@
|
||||
#ifdef HAVE_DBUSMENU
|
||||
#include "modules/sni/tray.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_MPRIS
|
||||
#include "modules/mpris/mpris.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBNL
|
||||
#include "modules/network.hpp"
|
||||
#endif
|
||||
@ -72,8 +76,12 @@
|
||||
#ifdef HAVE_LIBJACK
|
||||
#include "modules/jack.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_LIBWIREPLUMBER
|
||||
#include "modules/wireplumber.hpp"
|
||||
#endif
|
||||
#include "bar.hpp"
|
||||
#include "modules/custom.hpp"
|
||||
#include "modules/image.hpp"
|
||||
#include "modules/temperature.hpp"
|
||||
#include "modules/user.hpp"
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
@ -14,16 +14,18 @@ struct udev_device;
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Backlight : public AButton {
|
||||
class Backlight : public ALabel {
|
||||
class BacklightDev {
|
||||
public:
|
||||
BacklightDev() = default;
|
||||
BacklightDev(std::string name, int actual, int max);
|
||||
BacklightDev(std::string name, int actual, int max, bool powered);
|
||||
std::string_view name() const;
|
||||
int get_actual() const;
|
||||
void set_actual(int actual);
|
||||
int get_max() const;
|
||||
void set_max(int max);
|
||||
bool get_powered() const;
|
||||
void set_powered(bool powered);
|
||||
friend inline bool operator==(const BacklightDev &lhs, const BacklightDev &rhs) {
|
||||
return lhs.name_ == rhs.name_ && lhs.actual_ == rhs.actual_ && lhs.max_ == rhs.max_;
|
||||
}
|
||||
@ -32,6 +34,7 @@ class Backlight : public AButton {
|
||||
std::string name_;
|
||||
int actual_ = 1;
|
||||
int max_ = 1;
|
||||
bool powered_ = true;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
@ -26,7 +26,7 @@ namespace fs = std::experimental::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
#endif
|
||||
|
||||
class Battery : public AButton {
|
||||
class Battery : public ALabel {
|
||||
public:
|
||||
Battery(const std::string&, const Json::Value&);
|
||||
~Battery();
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#ifdef WANT_RFKILL
|
||||
#include "util/rfkill.hpp"
|
||||
#endif
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Bluetooth : public AButton {
|
||||
class Bluetooth : public ALabel {
|
||||
struct ControllerInfo {
|
||||
std::string path;
|
||||
std::string address;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <date/tz.h>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar {
|
||||
@ -14,7 +14,7 @@ namespace modules {
|
||||
const std::string kCalendarPlaceholder = "calendar";
|
||||
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
|
||||
|
||||
class Clock : public AButton {
|
||||
class Clock : public ALabel {
|
||||
public:
|
||||
Clock(const std::string&, const Json::Value&);
|
||||
~Clock() = default;
|
||||
@ -33,6 +33,9 @@ class Clock : public AButton {
|
||||
|
||||
bool handleScroll(GdkEventScroll* e);
|
||||
|
||||
std::string fmt_str_weeks_;
|
||||
std::string fmt_str_calendar_;
|
||||
int fmt_weeks_left_pad_{0};
|
||||
auto calendar_text(const waybar_time& wtime) -> std::string;
|
||||
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
||||
auto first_day_of_week() -> date::weekday;
|
||||
|
@ -9,12 +9,12 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Cpu : public AButton {
|
||||
class Cpu : public ALabel {
|
||||
public:
|
||||
Cpu(const std::string&, const Json::Value&);
|
||||
~Cpu() = default;
|
||||
|
@ -5,14 +5,14 @@
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/command.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Custom : public AButton {
|
||||
class Custom : public ALabel {
|
||||
public:
|
||||
Custom(const std::string&, const std::string&, const Json::Value&);
|
||||
~Custom();
|
||||
|
@ -5,13 +5,13 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/format.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Disk : public AButton {
|
||||
class Disk : public ALabel {
|
||||
public:
|
||||
Disk(const std::string&, const Json::Value&);
|
||||
~Disk() = default;
|
||||
|
@ -1,17 +1,25 @@
|
||||
#pragma once
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
class EventHandler {
|
||||
public:
|
||||
virtual void onEvent(const std::string& ev) = 0;
|
||||
virtual ~EventHandler() = default;
|
||||
};
|
||||
|
||||
class IPC {
|
||||
public:
|
||||
IPC() { startIPC(); }
|
||||
|
||||
void registerForIPC(const std::string&, std::function<void(const std::string&)>);
|
||||
void registerForIPC(const std::string&, EventHandler*);
|
||||
void unregisterForIPC(EventHandler*);
|
||||
|
||||
std::string getSocket1Reply(const std::string& rq);
|
||||
|
||||
@ -20,7 +28,7 @@ class IPC {
|
||||
void parseIPC(const std::string&);
|
||||
|
||||
std::mutex callbackMutex;
|
||||
std::deque<std::pair<std::string, std::function<void(const std::string&)>>> callbacks;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<IPC> gIPC;
|
||||
|
@ -1,16 +1,16 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "util/json.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
class Language : public waybar::AButton {
|
||||
class Language : public waybar::ALabel, public EventHandler {
|
||||
public:
|
||||
Language(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
~Language() = default;
|
||||
~Language();
|
||||
|
||||
auto update() -> void;
|
||||
|
||||
|
26
include/modules/hyprland/submap.hpp
Normal file
26
include/modules/hyprland/submap.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "util/json.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
class Submap : public waybar::ALabel, public EventHandler {
|
||||
public:
|
||||
Submap(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
~Submap();
|
||||
|
||||
auto update() -> void;
|
||||
|
||||
private:
|
||||
void onEvent(const std::string&);
|
||||
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
std::string submap_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
@ -9,16 +9,16 @@
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
class Window : public waybar::ALabel {
|
||||
class Window : public waybar::ALabel, public EventHandler {
|
||||
public:
|
||||
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
~Window() = default;
|
||||
~Window();
|
||||
|
||||
auto update() -> void;
|
||||
|
||||
private:
|
||||
uint getActiveWorkspaceID(std::string);
|
||||
std::string getLastWindowTitle(uint);
|
||||
int getActiveWorkspaceID(std::string);
|
||||
std::string getLastWindowTitle(int);
|
||||
void onEvent(const std::string&);
|
||||
|
||||
bool separate_outputs;
|
||||
@ -28,4 +28,4 @@ class Window : public waybar::ALabel {
|
||||
std::string lastView;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class IdleInhibitor : public AButton {
|
||||
class IdleInhibitor : public ALabel {
|
||||
sigc::connection timeout_;
|
||||
|
||||
public:
|
||||
|
34
include/modules/image.hpp
Normal file
34
include/modules/image.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <gtkmm/image.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "util/command.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Image : public AModule {
|
||||
public:
|
||||
Image(const std::string&, const std::string&, const Json::Value&);
|
||||
auto update() -> void;
|
||||
void refresh(int /*signal*/);
|
||||
|
||||
private:
|
||||
void delayWorker();
|
||||
void handleEvent();
|
||||
|
||||
Gtk::Image image_;
|
||||
std::string path_;
|
||||
int size_;
|
||||
int interval_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
@ -4,12 +4,12 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Inhibitor : public AButton {
|
||||
class Inhibitor : public ALabel {
|
||||
public:
|
||||
Inhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
~Inhibitor() override;
|
||||
|
@ -5,12 +5,12 @@
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Memory : public AButton {
|
||||
class Memory : public ALabel {
|
||||
public:
|
||||
Memory(const std::string&, const Json::Value&);
|
||||
~Memory() = default;
|
||||
|
@ -7,12 +7,12 @@
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "modules/mpd/state.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class MPD : public AButton {
|
||||
class MPD : public ALabel {
|
||||
friend class detail::Context;
|
||||
|
||||
// State machine
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
class MPD;
|
||||
|
68
include/modules/mpris/mpris.hpp
Normal file
68
include/modules/mpris/mpris.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "gtkmm/box.h"
|
||||
#include "gtkmm/label.h"
|
||||
|
||||
extern "C" {
|
||||
#include <playerctl/playerctl.h>
|
||||
}
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules::mpris {
|
||||
|
||||
class Mpris : public AModule {
|
||||
public:
|
||||
Mpris(const std::string&, const Json::Value&);
|
||||
~Mpris();
|
||||
auto update() -> void;
|
||||
bool handleToggle(GdkEventButton* const&);
|
||||
|
||||
private:
|
||||
static auto onPlayerNameAppeared(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void;
|
||||
static auto onPlayerNameVanished(PlayerctlPlayerManager*, PlayerctlPlayerName*, gpointer) -> void;
|
||||
static auto onPlayerPlay(PlayerctlPlayer*, gpointer) -> void;
|
||||
static auto onPlayerPause(PlayerctlPlayer*, gpointer) -> void;
|
||||
static auto onPlayerStop(PlayerctlPlayer*, gpointer) -> void;
|
||||
static auto onPlayerMetadata(PlayerctlPlayer*, GVariant*, gpointer) -> void;
|
||||
|
||||
struct PlayerInfo {
|
||||
std::string name;
|
||||
PlayerctlPlaybackStatus status;
|
||||
std::string status_string;
|
||||
|
||||
std::optional<std::string> artist;
|
||||
std::optional<std::string> album;
|
||||
std::optional<std::string> title;
|
||||
std::optional<std::string> length; // as HH:MM:SS
|
||||
};
|
||||
|
||||
auto getPlayerInfo() -> std::optional<PlayerInfo>;
|
||||
auto getIcon(const Json::Value&, const std::string&) -> std::string;
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Label label_;
|
||||
|
||||
// config
|
||||
std::string format_;
|
||||
std::string format_playing_;
|
||||
std::string format_paused_;
|
||||
std::string format_stopped_;
|
||||
std::chrono::seconds interval_;
|
||||
std::string player_;
|
||||
std::vector<std::string> ignored_players_;
|
||||
|
||||
PlayerctlPlayerManager* manager;
|
||||
PlayerctlPlayer* player;
|
||||
std::string lastStatus;
|
||||
std::string lastPlayer;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::mpris
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
#ifdef WANT_RFKILL
|
||||
#include "util/rfkill.hpp"
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Network : public AButton {
|
||||
class Network : public ALabel {
|
||||
public:
|
||||
Network(const std::string&, const Json::Value&);
|
||||
~Network();
|
||||
|
@ -7,11 +7,11 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Pulseaudio : public AButton {
|
||||
class Pulseaudio : public ALabel {
|
||||
public:
|
||||
Pulseaudio(const std::string&, const Json::Value&);
|
||||
~Pulseaudio();
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Clock : public AButton {
|
||||
class Clock : public ALabel {
|
||||
public:
|
||||
Clock(const std::string&, const Json::Value&);
|
||||
~Clock() = default;
|
||||
|
@ -4,12 +4,12 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Sndio : public AButton {
|
||||
class Sndio : public ALabel {
|
||||
public:
|
||||
Sndio(const std::string &, const Json::Value &);
|
||||
~Sndio();
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
#include "modules/sway/ipc/client.hpp"
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
namespace waybar::modules::sway {
|
||||
|
||||
class Language : public AButton, public sigc::trackable {
|
||||
class Language : public ALabel, public sigc::trackable {
|
||||
public:
|
||||
Language(const std::string& id, const Json::Value& config);
|
||||
~Language() = default;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
#include "modules/sway/ipc/client.hpp"
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
namespace waybar::modules::sway {
|
||||
|
||||
class Mode : public AButton, public sigc::trackable {
|
||||
class Mode : public ALabel, public sigc::trackable {
|
||||
public:
|
||||
Mode(const std::string&, const Json::Value&);
|
||||
~Mode() = default;
|
||||
|
@ -4,12 +4,12 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "AButton.hpp"
|
||||
#include "ALabel.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Temperature : public AButton {
|
||||
class Temperature : public ALabel {
|
||||
public:
|
||||
Temperature(const std::string&, const Json::Value&);
|
||||
~Temperature() = default;
|
||||
|
@ -14,18 +14,18 @@ class User : public AIconLabel {
|
||||
~User() = default;
|
||||
auto update() -> void;
|
||||
|
||||
bool handleToggle(GdkEventButton* const& e) override;
|
||||
|
||||
private:
|
||||
util::SleeperThread thread_;
|
||||
|
||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf_;
|
||||
|
||||
static constexpr inline int defaultUserImageWidth_ = 20;
|
||||
static constexpr inline int defaultUserImageHeight_ = 20;
|
||||
|
||||
long uptime_as_seconds();
|
||||
std::string get_user_login();
|
||||
std::string get_user_home_dir();
|
||||
std::string get_default_user_avatar_path();
|
||||
std::string get_user_login() const;
|
||||
std::string get_user_home_dir() const;
|
||||
std::string get_default_user_avatar_path() const;
|
||||
void init_default_user_avatar(int width, int height);
|
||||
void init_user_avatar(const std::string& path, int width, int height);
|
||||
void init_avatar(const Json::Value& config);
|
||||
|
39
include/modules/wireplumber.hpp
Normal file
39
include/modules/wireplumber.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wp/wp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
class Wireplumber : public ALabel {
|
||||
public:
|
||||
Wireplumber(const std::string&, const Json::Value&);
|
||||
~Wireplumber();
|
||||
auto update() -> void;
|
||||
|
||||
private:
|
||||
void loadRequiredApiModules();
|
||||
void prepare();
|
||||
void activatePlugins();
|
||||
static void updateVolume(waybar::modules::Wireplumber* self);
|
||||
static void updateNodeName(waybar::modules::Wireplumber* self);
|
||||
static uint32_t getDefaultNodeId(waybar::modules::Wireplumber* self);
|
||||
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
|
||||
static void onObjectManagerInstalled(waybar::modules::Wireplumber* self);
|
||||
|
||||
WpCore* wp_core_;
|
||||
GPtrArray* apis_;
|
||||
WpObjectManager* om_;
|
||||
uint32_t pending_plugins_;
|
||||
bool muted_;
|
||||
double volume_;
|
||||
uint32_t node_id_{0};
|
||||
std::string node_name_;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules
|
@ -37,8 +37,8 @@ The *backlight* module displays the current backlight level.
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*states*: ++
|
||||
typeof: array ++
|
||||
A number of backlight states which get activated on certain brightness levels.
|
||||
typeof: object ++
|
||||
A number of backlight states which get activated on certain brightness levels. See *waybar-states(5)*.
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
|
@ -33,7 +33,7 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
||||
The interval in which the information gets polled.
|
||||
|
||||
*states*: ++
|
||||
typeof: array ++
|
||||
typeof: object ++
|
||||
A number of battery states which get activated on certain capacity levels. See *waybar-states(5)*.
|
||||
|
||||
*format*: ++
|
||||
|
@ -137,7 +137,7 @@ Addressed by *bluetooth*
|
||||
|
||||
*{device_alias}*: Alias of the displayed device.
|
||||
|
||||
*{device_enumerate}*: Show a list of all connected devices, each on a seperate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++
|
||||
*{device_enumerate}*: Show a list of all connected devices, each on a separate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++
|
||||
and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip related format options.
|
||||
|
||||
# EXPERIMENTAL BATTERY PERCENTAGE FEATURE
|
||||
|
@ -42,7 +42,7 @@ The *cpu* module displays the current cpu utilization.
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*states*: ++
|
||||
typeof: array ++
|
||||
typeof: object ++
|
||||
A number of cpu usage states which get activated on certain usage levels. See *waybar-states(5)*.
|
||||
|
||||
*on-click*: ++
|
||||
|
@ -32,7 +32,7 @@ Addressed by *disk*
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*states*: ++
|
||||
typeof: array ++
|
||||
typeof: object ++
|
||||
A number of disk utilization states which get activated on certain percentage thresholds (percentage_used). See *waybar-states(5)*.
|
||||
|
||||
*max-length*: ++
|
||||
|
@ -23,7 +23,7 @@ Feral Gamemode optimizations.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
defualt: true ++
|
||||
default: true ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*tooltip-format*: ++
|
||||
|
@ -23,7 +23,7 @@ Addressed by *hyprland/language*
|
||||
|
||||
*keyboard-name*: ++
|
||||
typeof: string ++
|
||||
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "AT Translated set..." is recommended.
|
||||
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended.
|
||||
|
||||
|
||||
|
||||
@ -32,9 +32,9 @@ Addressed by *hyprland/language*
|
||||
```
|
||||
"hyprland/language": {
|
||||
"format": "Lang: {}"
|
||||
"format-us": "AMERICA, HELL YEAH!" // For American English
|
||||
"format-tr": "As bayrakları" // For Turkish
|
||||
"keyboard-name": "AT Translated Set 2 keyboard"
|
||||
"format-en": "AMERICA, HELL YEAH!"
|
||||
"format-tr": "As bayrakları"
|
||||
"keyboard-name": "at-translated-set-2-keyboard"
|
||||
}
|
||||
```
|
||||
|
||||
|
82
man/waybar-hyprland-submap.5.scd
Normal file
82
man/waybar-hyprland-submap.5.scd
Normal file
@ -0,0 +1,82 @@
|
||||
waybar-hyprland-submap(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - hyprland submap module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *submap* module displays the currently active submap similar to *sway/mode*.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
Addressed by *hyprland/submap*
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {} ++
|
||||
The format, how information should be displayed. On {} the currently active submap is displayed.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*max-length*: ++
|
||||
typeof: integer ++
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
|
||||
*align*: ++
|
||||
typeof: float ++
|
||||
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
Command to execute when clicked on the module.
|
||||
|
||||
*on-click-middle*: ++
|
||||
typeof: string ++
|
||||
Command to execute when middle-clicked on the module using mousewheel.
|
||||
|
||||
*on-click-right*: ++
|
||||
typeof: string ++
|
||||
Command to execute when you right clicked on the module.
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
|
||||
*on-scroll-up*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling up on the module.
|
||||
|
||||
*on-scroll-down*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling down on the module.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"hyprland/submap": {
|
||||
"format": "✌️ {}",
|
||||
"max-length": 8,
|
||||
"tooltip": false
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#submap*
|
72
man/waybar-image.5.scd
Normal file
72
man/waybar-image.5.scd
Normal file
@ -0,0 +1,72 @@
|
||||
waybar-custom(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - image module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *image* module displays an image from a path.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
Addressed by *custom/<name>*
|
||||
|
||||
*path*: ++
|
||||
typeof: string ++
|
||||
The path to the image.
|
||||
|
||||
*size*: ++
|
||||
typeof: integer ++
|
||||
The width/height to render the image.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
The interval (in seconds) to re-render the image.
|
||||
This is useful if the contents of *path* changes.
|
||||
If no *interval* is defined, the image will only be rendered once.
|
||||
|
||||
*signal*: ++
|
||||
typeof: integer ++
|
||||
The signal number used to update the module.
|
||||
This can be used instead of *interval* if the file changes irregularly.
|
||||
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
Command to execute when clicked on the module.
|
||||
|
||||
*on-click-middle*: ++
|
||||
typeof: string ++
|
||||
Command to execute when middle-clicked on the module using mousewheel.
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
|
||||
*on-scroll-up*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling up on the module.
|
||||
|
||||
*on-scroll-down*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling down on the module.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
## Spotify:
|
||||
|
||||
## mpd:
|
||||
|
||||
```
|
||||
"image/album-art": {
|
||||
"path": "/tmp/mpd_art",
|
||||
"size": 32,
|
||||
"interval": 5,
|
||||
"on-click": "mpc toggle"
|
||||
}
|
||||
```
|
@ -6,7 +6,7 @@ waybar - inhibitor module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *inhibitor* module allows to take an inhibitor lock that logind provides.
|
||||
The *inhibitor* module allows one to take an inhibitor lock that logind provides.
|
||||
See *systemd-inhibit*(1) for more information.
|
||||
|
||||
# CONFIGURATION
|
||||
|
@ -32,7 +32,7 @@ Addressed by *memory*
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*states*: ++
|
||||
typeof: array ++
|
||||
typeof: object ++
|
||||
A number of memory utilization states which get activated on certain percentage thresholds. See *waybar-states(5)*.
|
||||
|
||||
*max-length*: ++
|
||||
|
103
man/waybar-mpris.5.scd
Normal file
103
man/waybar-mpris.5.scd
Normal file
@ -0,0 +1,103 @@
|
||||
waybar-mpris(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - MPRIS module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *mpris* module displays currently playing media via libplayerctl.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
*player*: ++
|
||||
typeof: string ++
|
||||
default: playerctld ++
|
||||
Name of the MPRIS player to attach to. Using the default value always
|
||||
follows the currenly active player.
|
||||
|
||||
*ignored-players*: ++
|
||||
typeof: []string ++
|
||||
Ignore updates of the listed players, when using playerctld.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
Refresh MPRIS information on a timer.
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {player} ({status}) {dynamic} ++
|
||||
The text format.
|
||||
|
||||
*format-[status]*: ++
|
||||
typeof: string ++
|
||||
The status-specific text format.
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
default: play-pause ++
|
||||
Overwrite default action toggles.
|
||||
|
||||
*on-middle-click*: ++
|
||||
typeof: string ++
|
||||
default: previous track ++
|
||||
Overwrite default action toggles.
|
||||
|
||||
*on-right-click*: ++
|
||||
typeof: string ++
|
||||
default: next track ++
|
||||
Overwrite default action toggles.
|
||||
|
||||
*player-icons*: ++
|
||||
typeof: map[string]string
|
||||
Allows setting _{player-icon}_ based on player-name property.
|
||||
|
||||
*status-icons*: ++
|
||||
typeof: map[string]string
|
||||
Allows setting _{status-icon}_ based on player status (playing, paused,
|
||||
stopped).
|
||||
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{player}*: The name of the current media player
|
||||
|
||||
*{status}*: The current status (playing, paused, stopped)
|
||||
|
||||
*{artist}*: The artist of the current track
|
||||
|
||||
*{album}*: The album title of the current track
|
||||
|
||||
*{title}*: The title of the current track
|
||||
|
||||
*{length}*: Length of the track, formatted as HH:MM:SS
|
||||
|
||||
*{dynamic}*: Use _{artist}_, _{album}_, _{title}_ and _{length}_, automatically omit++
|
||||
empty values
|
||||
|
||||
*{player-icon}*: Chooses an icon from _player-icons_ based on _{player}_
|
||||
|
||||
*{status-icon}*: Chooses an icon from _status-icons_ based on _{status}_
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"mpris": {
|
||||
"format": "DEFAULT: {player_icon} {dynamic}",
|
||||
"format-paused": "DEFAULT: {status_icon} <i>{dynamic}</i>",
|
||||
"player-icons": {
|
||||
"default": "▶",
|
||||
"mpv": "🎵"
|
||||
},
|
||||
"status-icons": {
|
||||
"paused": "⏸"
|
||||
},
|
||||
// "ignored-players": ["firefox"]
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#mpris*
|
||||
- *#mpris.${status}*
|
||||
- *#mpris.${player}*
|
@ -43,8 +43,8 @@ Additionally you can control the volume by scrolling *up* or *down* while the cu
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*states*: ++
|
||||
typeof: array ++
|
||||
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*
|
||||
typeof: object ++
|
||||
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*.
|
||||
|
||||
*max-length*: ++
|
||||
typeof: integer ++
|
||||
|
@ -7,14 +7,13 @@ apply a class when the value matches the declared state value.
|
||||
|
||||
# STATES
|
||||
|
||||
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
|
||||
Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
|
||||
|
||||
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state.
|
||||
Each class gets activated when the current capacity is equal or below the configured *<value>*.
|
||||
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state.
|
||||
Each class gets activated when the current value is equal to or less than the configured *<value>* for the *battery* module, or equal to or greater than the configured *<value>* for all other modules.
|
||||
|
||||
- Also each state can have its own *format*.
|
||||
Those can be configured via *format-<name>*.
|
||||
Or if you want to differentiate a bit more even as *format-<status>-<state>*.
|
||||
- Also, each state can have its own *format*.
|
||||
Those can be configured via *format-<name>*, or if you want to differentiate a bit more, as *format-<status>-<state>*.
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
|
@ -73,6 +73,10 @@ Addressed by *sway/workspaces*
|
||||
typeof: bool ++
|
||||
Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration.
|
||||
|
||||
*alphabetical_sort*: ++
|
||||
typeof: bool ++
|
||||
Whether to sort workspaces alphabetically. Please note this can make "swaymsg workspace prev/next" move to workspaces inconsistent with the ordering shown in Waybar.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{value}*: Name of the workspace, as defined by sway.
|
||||
|
@ -33,7 +33,7 @@ compatible devices in the tooltip.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
defualt: true ++
|
||||
default: true ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*tooltip-spacing*: ++
|
||||
@ -47,6 +47,10 @@ compatible devices in the tooltip.
|
||||
default: 4 ++
|
||||
Defines the spacing between the tooltip window edge and the tooltip content.
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
Command to execute when clicked on the module.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{percentage}*: The battery capacity in percentage
|
||||
|
87
man/waybar-wireplumber.5.scd
Normal file
87
man/waybar-wireplumber.5.scd
Normal file
@ -0,0 +1,87 @@
|
||||
waybar-wireplumber(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - WirePlumber module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *wireplumber* module displays the current volume reported by WirePlumber.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: *{volume}%* ++
|
||||
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||
|
||||
*format-muted*: ++
|
||||
typeof: string ++
|
||||
This format is used when the sound is muted.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*tooltip-format*: ++
|
||||
typeof: string ++
|
||||
default: *{node_name}* ++
|
||||
The format of information displayed in the tooltip.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*states*: ++
|
||||
typeof: object ++
|
||||
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*.
|
||||
|
||||
*max-length*: ++
|
||||
typeof: integer ++
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
|
||||
*align*: ++
|
||||
typeof: float ++
|
||||
The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text.
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
Command to execute when clicked on the module.
|
||||
|
||||
*on-click-middle*: ++
|
||||
typeof: string ++
|
||||
Command to execute when middle-clicked on the module using mousewheel.
|
||||
|
||||
*on-click-right*: ++
|
||||
typeof: string ++
|
||||
Command to execute when you right clicked on the module.
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{volume}*: Volume in percentage.
|
||||
|
||||
*{node_name}*: The node's nickname as reported by WirePlumber (*node.nick* property)
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"wireplumber": {
|
||||
"format": "{volume}%",
|
||||
"format-muted": "",
|
||||
"on-click": "helvum"
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#wireplumber*
|
||||
- *#wireplumber.muted*
|
@ -266,6 +266,7 @@ A module group is defined by specifying a module named "group/some-group-name".
|
||||
- *waybar-keyboard-state(5)*
|
||||
- *waybar-memory(5)*
|
||||
- *waybar-mpd(5)*
|
||||
- *waybar-mpris(5)*
|
||||
- *waybar-network(5)*
|
||||
- *waybar-pulseaudio(5)*
|
||||
- *waybar-river-mode(5)*
|
||||
|
27
meson.build
27
meson.build
@ -1,6 +1,6 @@
|
||||
project(
|
||||
'waybar', 'cpp', 'c',
|
||||
version: '0.9.15',
|
||||
version: '0.9.17',
|
||||
license: 'MIT',
|
||||
meson_version: '>= 0.49.0',
|
||||
default_options : [
|
||||
@ -86,8 +86,11 @@ 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() or get_option('upower_glib').enabled()))
|
||||
jsoncpp = dependency('jsoncpp')
|
||||
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or
|
||||
get_option('logind').enabled() or
|
||||
get_option('upower_glib').enabled() or
|
||||
get_option('mpris').enabled()))
|
||||
jsoncpp = dependency('jsoncpp', version : ['>=1.9.2'], fallback : ['jsoncpp', 'jsoncpp_dep'])
|
||||
sigcpp = dependency('sigc++-2.0')
|
||||
libinotify = dependency('libinotify', required: false)
|
||||
libepoll = dependency('epoll-shim', required: false)
|
||||
@ -95,12 +98,14 @@ libinput = dependency('libinput', required: get_option('libinput'))
|
||||
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'))
|
||||
playerctl = dependency('playerctl', version : ['>=2.0.0'], required: get_option('mpris'))
|
||||
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
||||
libudev = dependency('libudev', required: get_option('libudev'))
|
||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
|
||||
xkbregistry = dependency('xkbregistry')
|
||||
libjack = dependency('jack', required: get_option('jack'))
|
||||
libwireplumber = dependency('wireplumber-0.4', required: get_option('wireplumber'))
|
||||
|
||||
libsndio = compiler.find_library('sndio', required: get_option('sndio'))
|
||||
if libsndio.found()
|
||||
@ -145,12 +150,12 @@ endif
|
||||
src_files = files(
|
||||
'src/factory.cpp',
|
||||
'src/AModule.cpp',
|
||||
'src/AButton.cpp',
|
||||
'src/ALabel.cpp',
|
||||
'src/AIconLabel.cpp',
|
||||
'src/modules/custom.cpp',
|
||||
'src/modules/disk.cpp',
|
||||
'src/modules/idle_inhibitor.cpp',
|
||||
'src/modules/image.cpp',
|
||||
'src/modules/temperature.cpp',
|
||||
'src/modules/user.cpp',
|
||||
'src/main.cpp',
|
||||
@ -219,6 +224,7 @@ if true
|
||||
src_files += 'src/modules/hyprland/backend.cpp'
|
||||
src_files += 'src/modules/hyprland/window.cpp'
|
||||
src_files += 'src/modules/hyprland/language.cpp'
|
||||
src_files += 'src/modules/hyprland/submap.cpp'
|
||||
endif
|
||||
|
||||
if libnl.found() and libnlgen.found()
|
||||
@ -237,6 +243,11 @@ if (upower_glib.found() and giounix.found() and not get_option('logind').disable
|
||||
src_files += 'src/modules/upower/upower_tooltip.cpp'
|
||||
endif
|
||||
|
||||
if (playerctl.found() and giounix.found() and not get_option('logind').disabled())
|
||||
add_project_arguments('-DHAVE_MPRIS', language: 'cpp')
|
||||
src_files += 'src/modules/mpris/mpris.cpp'
|
||||
endif
|
||||
|
||||
if libpulse.found()
|
||||
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
||||
src_files += 'src/modules/pulseaudio.cpp'
|
||||
@ -247,6 +258,11 @@ if libjack.found()
|
||||
src_files += 'src/modules/jack.cpp'
|
||||
endif
|
||||
|
||||
if libwireplumber.found()
|
||||
add_project_arguments('-DHAVE_LIBWIREPLUMBER', language: 'cpp')
|
||||
src_files += 'src/modules/wireplumber.cpp'
|
||||
endif
|
||||
|
||||
if dbusmenu_gtk.found()
|
||||
add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp')
|
||||
src_files += files(
|
||||
@ -328,8 +344,10 @@ executable(
|
||||
libnl,
|
||||
libnlgen,
|
||||
upower_glib,
|
||||
playerctl,
|
||||
libpulse,
|
||||
libjack,
|
||||
libwireplumber,
|
||||
libudev,
|
||||
libinotify,
|
||||
libepoll,
|
||||
@ -380,6 +398,7 @@ if scdoc.found()
|
||||
'waybar-keyboard-state.5.scd',
|
||||
'waybar-memory.5.scd',
|
||||
'waybar-mpd.5.scd',
|
||||
'waybar-mpris.5.scd',
|
||||
'waybar-network.5.scd',
|
||||
'waybar-pulseaudio.5.scd',
|
||||
'waybar-river-mode.5.scd',
|
||||
|
@ -5,6 +5,7 @@ option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev s
|
||||
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('mpris', type: 'feature', value: 'auto', description: 'Enable support for mpris')
|
||||
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')
|
||||
@ -16,3 +17,4 @@ option('logind', type: 'feature', value: 'auto', description: 'Enable support fo
|
||||
option('tests', type: 'feature', value: 'auto', description: 'Enable tests')
|
||||
option('experimental', type : 'boolean', value : false, description: 'Enable experimental features')
|
||||
option('jack', type: 'feature', value: 'auto', description: 'Enable support for JACK')
|
||||
option('wireplumber', type: 'feature', value: 'auto', description: 'Enable support for WirePlumber')
|
@ -81,6 +81,7 @@ button:hover {
|
||||
#backlight,
|
||||
#network,
|
||||
#pulseaudio,
|
||||
#wireplumber,
|
||||
#custom-media,
|
||||
#tray,
|
||||
#mode,
|
||||
@ -176,6 +177,15 @@ label:focus {
|
||||
color: #2a5c45;
|
||||
}
|
||||
|
||||
#wireplumber {
|
||||
background-color: #fff0f5;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#wireplumber.muted {
|
||||
background-color: #f53c3c;
|
||||
}
|
||||
|
||||
#custom-media {
|
||||
background-color: #66cc99;
|
||||
color: #2a5c45;
|
||||
|
160
src/AButton.cpp
160
src/AButton.cpp
@ -1,160 +0,0 @@
|
||||
#include "AButton.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <util/command.hpp>
|
||||
|
||||
namespace waybar {
|
||||
|
||||
AButton::AButton(const Json::Value& config, const std::string& name, const std::string& id,
|
||||
const std::string& format, uint16_t interval, bool ellipsize, bool enable_click,
|
||||
bool enable_scroll)
|
||||
: AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll),
|
||||
format_(config_["format"].isString() ? config_["format"].asString() : format),
|
||||
interval_(config_["interval"] == "once"
|
||||
? std::chrono::seconds(100000000)
|
||||
: std::chrono::seconds(
|
||||
config_["interval"].isUInt() ? config_["interval"].asUInt() : interval)),
|
||||
default_format_(format_) {
|
||||
button_.set_name(name);
|
||||
button_.set_relief(Gtk::RELIEF_NONE);
|
||||
|
||||
/* https://github.com/Alexays/Waybar/issues/1731 */
|
||||
auto css = Gtk::CssProvider::create();
|
||||
css->load_from_data("button { min-width: 0; }");
|
||||
button_.get_style_context()->add_provider(css, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
|
||||
if (!id.empty()) {
|
||||
button_.get_style_context()->add_class(id);
|
||||
}
|
||||
event_box_.add(button_);
|
||||
if (config_["max-length"].isUInt()) {
|
||||
label_->set_max_width_chars(config_["max-length"].asInt());
|
||||
label_->set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
||||
label_->set_single_line_mode(true);
|
||||
} else if (ellipsize && label_->get_max_width_chars() == -1) {
|
||||
label_->set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
|
||||
label_->set_single_line_mode(true);
|
||||
}
|
||||
|
||||
if (config_["min-length"].isUInt()) {
|
||||
label_->set_width_chars(config_["min-length"].asUInt());
|
||||
}
|
||||
|
||||
uint rotate = 0;
|
||||
|
||||
if (config_["rotate"].isUInt()) {
|
||||
rotate = config["rotate"].asUInt();
|
||||
label_->set_angle(rotate);
|
||||
}
|
||||
|
||||
if (config_["align"].isDouble()) {
|
||||
auto align = config_["align"].asFloat();
|
||||
if (rotate == 90 || rotate == 270) {
|
||||
label_->set_yalign(align);
|
||||
} else {
|
||||
label_->set_xalign(align);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(config_["on-click"].isString() || config_["on-click-middle"].isString() ||
|
||||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
|
||||
config_["on-click-right"].isString() || config_["format-alt"].isString() || enable_click)) {
|
||||
button_.set_sensitive(false);
|
||||
} else {
|
||||
button_.signal_pressed().connect([this] {
|
||||
GdkEventButton* e = (GdkEventButton*)gdk_event_new(GDK_BUTTON_PRESS);
|
||||
e->button = 1;
|
||||
handleToggle(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto AButton::update() -> void { AModule::update(); }
|
||||
|
||||
std::string AButton::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
|
||||
auto format_icons = config_["format-icons"];
|
||||
if (format_icons.isObject()) {
|
||||
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
|
||||
format_icons = format_icons[alt];
|
||||
} else {
|
||||
format_icons = format_icons["default"];
|
||||
}
|
||||
}
|
||||
if (format_icons.isArray()) {
|
||||
auto size = format_icons.size();
|
||||
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||
format_icons = format_icons[idx];
|
||||
}
|
||||
if (format_icons.isString()) {
|
||||
return format_icons.asString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string AButton::getIcon(uint16_t percentage, const std::vector<std::string>& alts,
|
||||
uint16_t max) {
|
||||
auto format_icons = config_["format-icons"];
|
||||
if (format_icons.isObject()) {
|
||||
std::string _alt = "default";
|
||||
for (const auto& alt : alts) {
|
||||
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
|
||||
_alt = alt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
format_icons = format_icons[_alt];
|
||||
}
|
||||
if (format_icons.isArray()) {
|
||||
auto size = format_icons.size();
|
||||
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
|
||||
format_icons = format_icons[idx];
|
||||
}
|
||||
if (format_icons.isString()) {
|
||||
return format_icons.asString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool waybar::AButton::handleToggle(GdkEventButton* const& e) {
|
||||
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
|
||||
alt_ = !alt_;
|
||||
if (alt_ && config_["format-alt"].isString()) {
|
||||
format_ = config_["format-alt"].asString();
|
||||
} else {
|
||||
format_ = default_format_;
|
||||
}
|
||||
}
|
||||
return AModule::handleToggle(e);
|
||||
}
|
||||
|
||||
std::string AButton::getState(uint8_t value, bool lesser) {
|
||||
if (!config_["states"].isObject()) {
|
||||
return "";
|
||||
}
|
||||
// Get current state
|
||||
std::vector<std::pair<std::string, uint8_t>> states;
|
||||
if (config_["states"].isObject()) {
|
||||
for (auto it = config_["states"].begin(); it != config_["states"].end(); ++it) {
|
||||
if (it->isUInt() && it.key().isString()) {
|
||||
states.emplace_back(it.key().asString(), it->asUInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort states
|
||||
std::sort(states.begin(), states.end(), [&lesser](auto& a, auto& b) {
|
||||
return lesser ? a.second < b.second : a.second > b.second;
|
||||
});
|
||||
std::string valid_state;
|
||||
for (auto const& state : states) {
|
||||
if ((lesser ? value <= state.second : value >= state.second) && valid_state.empty()) {
|
||||
button_.get_style_context()->add_class(state.first);
|
||||
valid_state = state.first;
|
||||
} else {
|
||||
button_.get_style_context()->remove_class(state.first);
|
||||
}
|
||||
}
|
||||
return valid_state;
|
||||
}
|
||||
|
||||
} // namespace waybar
|
@ -22,6 +22,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
return new waybar::modules::upower::UPower(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_MPRIS
|
||||
if (ref == "mpris") {
|
||||
return new waybar::modules::mpris::Mpris(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SWAY
|
||||
if (ref == "sway/mode") {
|
||||
return new waybar::modules::sway::Mode(id, config_[name]);
|
||||
@ -67,6 +72,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
if (ref == "hyprland/language") {
|
||||
return new waybar::modules::hyprland::Language(id, bar_, config_[name]);
|
||||
}
|
||||
if (ref == "hyprland/submap") {
|
||||
return new waybar::modules::hyprland::Submap(id, bar_, config_[name]);
|
||||
}
|
||||
#endif
|
||||
if (ref == "idle_inhibitor") {
|
||||
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
||||
@ -137,12 +145,19 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
||||
if (ref == "jack") {
|
||||
return new waybar::modules::JACK(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LIBWIREPLUMBER
|
||||
if (ref == "wireplumber") {
|
||||
return new waybar::modules::Wireplumber(id, config_[name]);
|
||||
}
|
||||
#endif
|
||||
if (ref == "temperature") {
|
||||
return new waybar::modules::Temperature(id, config_[name]);
|
||||
}
|
||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
||||
} else if (ref.compare(0, 6, "image/") == 0 && ref.size() > 6) {
|
||||
return new waybar::modules::Image(ref.substr(6), id, config_[name]);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
||||
|
@ -73,8 +73,9 @@ void check_nn(const void *ptr, const char *message = "ptr was null") {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max)
|
||||
: name_(std::move(name)), actual_(actual), max_(max) {}
|
||||
waybar::modules::Backlight::BacklightDev::BacklightDev(std::string name, int actual, int max,
|
||||
bool powered)
|
||||
: name_(std::move(name)), actual_(actual), max_(max), powered_(powered) {}
|
||||
|
||||
std::string_view waybar::modules::Backlight::BacklightDev::name() const { return name_; }
|
||||
|
||||
@ -86,8 +87,12 @@ int waybar::modules::Backlight::BacklightDev::get_max() const { return max_; }
|
||||
|
||||
void waybar::modules::Backlight::BacklightDev::set_max(int max) { max_ = max; }
|
||||
|
||||
bool waybar::modules::Backlight::BacklightDev::get_powered() const { return powered_; }
|
||||
|
||||
void waybar::modules::Backlight::BacklightDev::set_powered(bool powered) { powered_ = powered; }
|
||||
|
||||
waybar::modules::Backlight::Backlight(const std::string &id, const Json::Value &config)
|
||||
: AButton(config, "backlight", id, "{percent}%", 2),
|
||||
: ALabel(config, "backlight", id, "{percent}%", 2),
|
||||
preferred_device_(config["device"].isString() ? config["device"].asString() : "") {
|
||||
// Get initial state
|
||||
{
|
||||
@ -172,21 +177,26 @@ auto waybar::modules::Backlight::update() -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t percent =
|
||||
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
||||
label_->set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)),
|
||||
fmt::arg("icon", getIcon(percent))));
|
||||
getState(percent);
|
||||
if (best->get_powered()) {
|
||||
event_box_.show();
|
||||
const uint8_t percent =
|
||||
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
||||
label_.set_markup(fmt::format(format_, fmt::arg("percent", std::to_string(percent)),
|
||||
fmt::arg("icon", getIcon(percent))));
|
||||
getState(percent);
|
||||
} else {
|
||||
event_box_.hide();
|
||||
}
|
||||
} else {
|
||||
if (!previous_best_.has_value()) {
|
||||
return;
|
||||
}
|
||||
label_->set_markup("");
|
||||
label_.set_markup("");
|
||||
}
|
||||
previous_best_ = best == nullptr ? std::nullopt : std::optional{*best};
|
||||
previous_format_ = format_;
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
template <class ForwardIt>
|
||||
@ -215,6 +225,7 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
|
||||
|
||||
const char *actual = udev_device_get_sysattr_value(dev, actual_brightness_attr);
|
||||
const char *max = udev_device_get_sysattr_value(dev, "max_brightness");
|
||||
const char *power = udev_device_get_sysattr_value(dev, "bl_power");
|
||||
|
||||
auto found =
|
||||
std::find_if(first, last, [name](const auto &device) { return device.name() == name; });
|
||||
@ -225,10 +236,14 @@ void waybar::modules::Backlight::upsert_device(ForwardIt first, ForwardIt last,
|
||||
if (max != nullptr) {
|
||||
found->set_max(std::stoi(max));
|
||||
}
|
||||
if (power != nullptr) {
|
||||
found->set_powered(std::stoi(power) == 0);
|
||||
}
|
||||
} else {
|
||||
const int actual_int = actual == nullptr ? 0 : std::stoi(actual);
|
||||
const int max_int = max == nullptr ? 0 : std::stoi(max);
|
||||
*inserter = BacklightDev{name, actual_int, max_int};
|
||||
const bool power_bool = power == nullptr ? true : std::stoi(power) == 0;
|
||||
*inserter = BacklightDev{name, actual_int, max_int, power_bool};
|
||||
++inserter;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include <iostream>
|
||||
waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "battery", id, "{capacity}%", 60) {
|
||||
: ALabel(config, "battery", id, "{capacity}%", 60) {
|
||||
#if defined(__linux__)
|
||||
battery_watch_fd_ = inotify_init1(IN_CLOEXEC);
|
||||
if (battery_watch_fd_ == -1) {
|
||||
@ -107,6 +107,15 @@ void waybar::modules::Battery::refreshBatteries() {
|
||||
std::ifstream(node.path() / "type") >> type;
|
||||
|
||||
if (!type.compare("Battery")) {
|
||||
// Ignore non-system power supplies unless explicitly requested
|
||||
if (!bat_defined && fs::exists(node.path() / "scope")) {
|
||||
std::string scope;
|
||||
std::ifstream(node.path() / "scope") >> scope;
|
||||
if (g_ascii_strcasecmp(scope.data(), "device") == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
check_map[node.path()] = true;
|
||||
auto search = batteries_.find(node.path());
|
||||
if (search == batteries_.end()) {
|
||||
@ -233,6 +242,10 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||
bool total_energy_full_design_exists = false;
|
||||
uint32_t total_capacity = 0;
|
||||
bool total_capacity_exists = false;
|
||||
uint32_t time_to_empty_now = 0;
|
||||
bool time_to_empty_now_exists = false;
|
||||
uint32_t time_to_full_now = 0;
|
||||
bool time_to_full_now_exists = false;
|
||||
|
||||
std::string status = "Unknown";
|
||||
for (auto const& item : batteries_) {
|
||||
@ -260,6 +273,16 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||
std::ifstream(bat / "current_avg") >> current_now;
|
||||
}
|
||||
|
||||
if (fs::exists(bat / "time_to_empty_now")) {
|
||||
time_to_empty_now_exists = true;
|
||||
std::ifstream(bat / "time_to_empty_now") >> time_to_empty_now;
|
||||
}
|
||||
|
||||
if (fs::exists(bat / "time_to_full_now")) {
|
||||
time_to_full_now_exists = true;
|
||||
std::ifstream(bat / "time_to_full_now") >> time_to_full_now;
|
||||
}
|
||||
|
||||
uint32_t voltage_now = 0;
|
||||
bool voltage_now_exists = false;
|
||||
if (fs::exists(bat / "voltage_now")) {
|
||||
@ -470,7 +493,9 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||
}
|
||||
}
|
||||
|
||||
if (!adapter_.empty() && status == "Discharging") {
|
||||
// Give `Plugged` higher priority over `Not charging`.
|
||||
// So in a setting where TLP is used, `Plugged` is shown when the threshold is reached
|
||||
if (!adapter_.empty() && (status == "Discharging" || status == "Not charging")) {
|
||||
bool online;
|
||||
std::string current_status;
|
||||
std::ifstream(adapter_ / "online") >> online;
|
||||
@ -479,8 +504,16 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||
}
|
||||
|
||||
float time_remaining{0.0f};
|
||||
if (status == "Discharging" && total_power_exists && total_energy_exists) {
|
||||
if (status == "Discharging" && time_to_empty_now_exists) {
|
||||
if (time_to_empty_now != 0) time_remaining = (float)time_to_empty_now / 1000.0f;
|
||||
} else if (status == "Discharging" && total_power_exists && total_energy_exists) {
|
||||
if (total_power != 0) time_remaining = (float)total_energy / total_power;
|
||||
} else if (status == "Charging" && time_to_full_now_exists) {
|
||||
if (time_to_full_now_exists && (time_to_full_now != 0))
|
||||
time_remaining = -(float)time_to_full_now / 1000.0f;
|
||||
// If we've turned positive it means the battery is past 100% and so just report that as no
|
||||
// time remaining
|
||||
if (time_remaining > 0.0f) time_remaining = 0.0f;
|
||||
} else if (status == "Charging" && total_energy_exists && total_energy_full_exists &&
|
||||
total_power_exists) {
|
||||
if (total_power != 0)
|
||||
@ -493,7 +526,7 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
||||
float calculated_capacity{0.0f};
|
||||
if (total_capacity_exists) {
|
||||
if (total_capacity > 0.0f)
|
||||
calculated_capacity = (float)total_capacity;
|
||||
calculated_capacity = (float)total_capacity / batteries_.size();
|
||||
else if (total_energy_full_exists && total_energy_exists) {
|
||||
if (total_energy_full > 0.0f)
|
||||
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||
@ -611,14 +644,14 @@ auto waybar::modules::Battery::update() -> void {
|
||||
} else if (config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
button_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("timeTo", tooltip_text_default),
|
||||
fmt::arg("power", power), fmt::arg("capacity", capacity),
|
||||
fmt::arg("time", time_remaining_formatted)));
|
||||
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("timeTo", tooltip_text_default),
|
||||
fmt::arg("power", power), fmt::arg("capacity", capacity),
|
||||
fmt::arg("time", time_remaining_formatted)));
|
||||
}
|
||||
if (!old_status_.empty()) {
|
||||
button_.get_style_context()->remove_class(old_status_);
|
||||
label_.get_style_context()->remove_class(old_status_);
|
||||
}
|
||||
button_.get_style_context()->add_class(status);
|
||||
label_.get_style_context()->add_class(status);
|
||||
old_status_ = status;
|
||||
if (!state.empty() && config_["format-" + status + "-" + state].isString()) {
|
||||
format = config_["format-" + status + "-" + state].asString();
|
||||
@ -632,10 +665,10 @@ auto waybar::modules::Battery::update() -> void {
|
||||
} else {
|
||||
event_box_.show();
|
||||
auto icons = std::vector<std::string>{status + "-" + state, status, state};
|
||||
label_->set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("power", power),
|
||||
fmt::arg("icon", getIcon(capacity, icons)),
|
||||
fmt::arg("time", time_remaining_formatted)));
|
||||
label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("power", power),
|
||||
fmt::arg("icon", getIcon(capacity, icons)),
|
||||
fmt::arg("time", time_remaining_formatted)));
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ auto getUcharProperty(GDBusProxy* proxy, const char* property_name) -> unsigned
|
||||
} // namespace
|
||||
|
||||
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "bluetooth", id, " {status}", 10),
|
||||
: ALabel(config, "bluetooth", id, " {status}", 10),
|
||||
#ifdef WANT_RFKILL
|
||||
rfkill_{RFKILL_TYPE_BLUETOOTH},
|
||||
#endif
|
||||
@ -190,10 +190,10 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
format_.empty() ? event_box_.hide() : event_box_.show();
|
||||
|
||||
auto update_style_context = [this](const std::string& style_class, bool in_next_state) {
|
||||
if (in_next_state && !button_.get_style_context()->has_class(style_class)) {
|
||||
button_.get_style_context()->add_class(style_class);
|
||||
} else if (!in_next_state && button_.get_style_context()->has_class(style_class)) {
|
||||
button_.get_style_context()->remove_class(style_class);
|
||||
if (in_next_state && !label_.get_style_context()->has_class(style_class)) {
|
||||
label_.get_style_context()->add_class(style_class);
|
||||
} else if (!in_next_state && label_.get_style_context()->has_class(style_class)) {
|
||||
label_.get_style_context()->remove_class(style_class);
|
||||
}
|
||||
};
|
||||
update_style_context("discoverable", cur_controller_.discoverable);
|
||||
@ -205,7 +205,7 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
update_style_context(state, true);
|
||||
state_ = state;
|
||||
|
||||
label_->set_markup(fmt::format(
|
||||
label_.set_markup(fmt::format(
|
||||
format_, fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()),
|
||||
fmt::arg("controller_address", cur_controller_.address),
|
||||
fmt::arg("controller_address_type", cur_controller_.address_type),
|
||||
@ -246,7 +246,7 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
device_enumerate_.erase(0, 1);
|
||||
}
|
||||
}
|
||||
button_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
tooltip_format, fmt::arg("status", state_),
|
||||
fmt::arg("num_connections", connected_devices_.size()),
|
||||
fmt::arg("controller_address", cur_controller_.address),
|
||||
@ -260,7 +260,7 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
// NOTE: only for when the org.bluez.Battery1 interface is added/removed after/before a device is
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
@ -18,7 +19,7 @@
|
||||
using waybar::waybar_time;
|
||||
|
||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
||||
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
|
||||
current_time_zone_idx_(0),
|
||||
is_calendar_in_tooltip_(false),
|
||||
is_timezoned_list_in_tooltip_(false) {
|
||||
@ -65,6 +66,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
if (config_["on-scroll"][kCalendarPlaceholder].isInt()) {
|
||||
calendar_shift_init_ =
|
||||
date::months{config_["on-scroll"].get(kCalendarPlaceholder, 0).asInt()};
|
||||
event_box_.add_events(Gdk::LEAVE_NOTIFY_MASK);
|
||||
event_box_.signal_leave_notify_event().connect([this](GdkEventCrossing*) {
|
||||
calendar_shift_ = date::months{0};
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +80,22 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
locale_ = std::locale("");
|
||||
}
|
||||
|
||||
if (config_["format-calendar-weeks"].isString()) {
|
||||
fmt_str_weeks_ =
|
||||
std::regex_replace(config_["format-calendar-weeks"].asString(), std::regex("\\{\\}"),
|
||||
(first_day_of_week() == date::Monday) ? "{:%V}" : "{:%U}");
|
||||
fmt_weeks_left_pad_ =
|
||||
std::regex_replace(fmt_str_weeks_, std::regex("</?[^>]+>|\\{.*\\}"), "").length();
|
||||
} else {
|
||||
fmt_str_weeks_ = "";
|
||||
}
|
||||
|
||||
if (config_["format-calendar"].isString()) {
|
||||
fmt_str_calendar_ = config_["format-calendar"].asString();
|
||||
} else {
|
||||
fmt_str_calendar_ = "{}";
|
||||
}
|
||||
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
auto now = std::chrono::system_clock::now();
|
||||
@ -96,8 +118,14 @@ bool waybar::modules::Clock::is_timezone_fixed() {
|
||||
auto waybar::modules::Clock::update() -> void {
|
||||
auto time_zone = current_timezone();
|
||||
auto now = std::chrono::system_clock::now();
|
||||
waybar_time wtime = {locale_, date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now) +
|
||||
calendar_shift_)};
|
||||
waybar_time wtime = {locale_,
|
||||
date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now))};
|
||||
|
||||
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + calendar_shift_;
|
||||
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
|
||||
waybar_time shifted_wtime = {
|
||||
locale_, date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now_shifted))};
|
||||
|
||||
std::string text = "";
|
||||
if (!is_timezone_fixed()) {
|
||||
// As date dep is not fully compatible, prefer fmt
|
||||
@ -107,24 +135,28 @@ auto waybar::modules::Clock::update() -> void {
|
||||
} else {
|
||||
text = fmt::format(format_, wtime);
|
||||
}
|
||||
label_->set_markup(text);
|
||||
label_.set_markup(text);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
std::string calendar_lines{""};
|
||||
std::string timezoned_time_lines{""};
|
||||
if (is_calendar_in_tooltip_) calendar_lines = calendar_text(wtime);
|
||||
if (is_timezoned_list_in_tooltip_) timezoned_time_lines = timezones_text(&now);
|
||||
if (is_calendar_in_tooltip_) {
|
||||
calendar_lines = calendar_text(shifted_wtime);
|
||||
}
|
||||
if (is_timezoned_list_in_tooltip_) {
|
||||
timezoned_time_lines = timezones_text(&now);
|
||||
}
|
||||
auto tooltip_format = config_["tooltip-format"].asString();
|
||||
text =
|
||||
fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||
button_.set_tooltip_markup(text);
|
||||
text = fmt::format(tooltip_format, shifted_wtime,
|
||||
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||
label_.set_tooltip_markup(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
|
||||
@ -136,7 +168,7 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
|
||||
auto dir = AModule::getScrollDir(e);
|
||||
|
||||
// Shift calendar date
|
||||
if (calendar_shift_init_.count() > 0) {
|
||||
if (calendar_shift_init_.count() != 0) {
|
||||
if (dir == SCROLL_DIR::UP)
|
||||
calendar_shift_ += calendar_shift_init_;
|
||||
else
|
||||
@ -168,73 +200,70 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
||||
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
||||
const auto ymd{date::year_month_day{daypoint}};
|
||||
|
||||
if (calendar_cached_ymd_ == ymd) return calendar_cached_text_;
|
||||
if (calendar_cached_ymd_ == ymd) {
|
||||
return calendar_cached_text_;
|
||||
}
|
||||
|
||||
const auto curr_day{(calendar_shift_init_.count() > 0 && calendar_shift_.count() != 0)
|
||||
const auto curr_day{(calendar_shift_init_.count() != 0 && calendar_shift_.count() != 0)
|
||||
? date::day{0}
|
||||
: ymd.day()};
|
||||
const date::year_month ym{ymd.year(), ymd.month()};
|
||||
const auto week_format{config_["format-calendar-weekdays"].isString()
|
||||
? config_["format-calendar-weekdays"].asString()
|
||||
: ""};
|
||||
const auto wn_format{config_["format-calendar-weeks"].isString()
|
||||
? config_["format-calendar-weeks"].asString()
|
||||
: ""};
|
||||
const auto first_dow = first_day_of_week();
|
||||
|
||||
std::stringstream os;
|
||||
|
||||
const auto first_dow = first_day_of_week();
|
||||
int ws{0}; // weeks-pos: side(1 - left, 2 - right)
|
||||
enum class WeeksSide {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
HIDDEN,
|
||||
};
|
||||
WeeksSide weeks_pos = WeeksSide::HIDDEN;
|
||||
|
||||
if (config_["calendar-weeks-pos"].isString()) {
|
||||
if (config_["calendar-weeks-pos"].asString() == "left") {
|
||||
ws = 1;
|
||||
weeks_pos = WeeksSide::LEFT;
|
||||
// Add paddings before the header
|
||||
os << std::string(4, ' ');
|
||||
os << std::string(3 + fmt_weeks_left_pad_, ' ');
|
||||
} else if (config_["calendar-weeks-pos"].asString() == "right") {
|
||||
ws = 2;
|
||||
weeks_pos = WeeksSide::RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
weekdays_header(first_dow, os);
|
||||
|
||||
// First week prefixed with spaces if needed.
|
||||
auto wd = date::weekday(ym / 1);
|
||||
// First week day prefixed with spaces if needed.
|
||||
date::sys_days print_wd{ym / 1};
|
||||
auto wd{date::weekday{print_wd}};
|
||||
auto empty_days = (wd - first_dow).count();
|
||||
date::sys_days lwd{static_cast<date::sys_days>(ym / 1) + date::days{7 - empty_days}};
|
||||
|
||||
if (first_dow == date::Monday) {
|
||||
lwd -= date::days{1};
|
||||
}
|
||||
/* Print weeknumber on the left for the first row*/
|
||||
if (ws == 1) {
|
||||
os << fmt::format(wn_format, lwd);
|
||||
os << ' ';
|
||||
lwd += date::weeks{1};
|
||||
if (weeks_pos == WeeksSide::LEFT) {
|
||||
os << fmt::format(fmt_str_weeks_, print_wd) << ' ';
|
||||
}
|
||||
|
||||
if (empty_days > 0) {
|
||||
os << std::string(empty_days * 3 - 1, ' ');
|
||||
}
|
||||
auto last_day = (ym / date::literals::last).day();
|
||||
for (auto d = date::day(1); d <= last_day; ++d, ++wd) {
|
||||
|
||||
const auto last_day = (ym / date::literals::last).day();
|
||||
|
||||
for (auto d{date::day{1}}; d <= last_day; ++d, ++wd) {
|
||||
if (wd != first_dow) {
|
||||
os << ' ';
|
||||
} else if (unsigned(d) != 1) {
|
||||
if (ws == 2) {
|
||||
os << ' ';
|
||||
os << fmt::format(wn_format, lwd);
|
||||
lwd += date::weeks{1};
|
||||
if (weeks_pos == WeeksSide::RIGHT) {
|
||||
os << ' ' << fmt::format(fmt_str_weeks_, print_wd);
|
||||
}
|
||||
|
||||
os << '\n';
|
||||
|
||||
if (ws == 1) {
|
||||
os << fmt::format(wn_format, lwd);
|
||||
os << ' ';
|
||||
lwd += date::weeks{1};
|
||||
print_wd = (ym / d);
|
||||
|
||||
if (weeks_pos == WeeksSide::LEFT) {
|
||||
os << fmt::format(fmt_str_weeks_, print_wd) << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (d == curr_day) {
|
||||
if (config_["today-format"].isString()) {
|
||||
auto today_format = config_["today-format"].asString();
|
||||
@ -242,17 +271,17 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
||||
} else {
|
||||
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
||||
}
|
||||
} else if (config_["format-calendar"].isString()) {
|
||||
os << fmt::format(config_["format-calendar"].asString(), date::format("%e", d));
|
||||
} else
|
||||
os << date::format("%e", d);
|
||||
} else {
|
||||
os << fmt::format(fmt_str_calendar_, date::format("%e", d));
|
||||
}
|
||||
/*Print weeks on the right when the endings with spaces*/
|
||||
if (ws == 2 && d == last_day) {
|
||||
if (weeks_pos == WeeksSide::RIGHT && d == last_day) {
|
||||
empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding());
|
||||
if (empty_days > 0) {
|
||||
os << std::string(empty_days * 3 + 1, ' ');
|
||||
os << fmt::format(wn_format, lwd);
|
||||
if (empty_days > 0 && empty_days < 7) {
|
||||
os << std::string(empty_days * 3, ' ');
|
||||
}
|
||||
|
||||
os << ' ' << fmt::format(fmt_str_weeks_, print_wd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,12 +291,14 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
||||
return result;
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os)
|
||||
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_week_day, std::ostream& os)
|
||||
-> void {
|
||||
std::stringstream res;
|
||||
auto wd = first_dow;
|
||||
auto wd = first_week_day;
|
||||
do {
|
||||
if (wd != first_dow) res << ' ';
|
||||
if (wd != first_week_day) {
|
||||
res << ' ';
|
||||
}
|
||||
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
|
||||
auto clen = ustring_clen(wd_ustring);
|
||||
auto wd_len = wd_ustring.length();
|
||||
@ -278,8 +309,8 @@ auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std
|
||||
}
|
||||
const std::string pad(2 - clen, ' ');
|
||||
res << pad << wd_ustring;
|
||||
} while (++wd != first_dow);
|
||||
res << "\n";
|
||||
} while (++wd != first_week_day);
|
||||
res << '\n';
|
||||
|
||||
if (config_["format-calendar-weekdays"].isString()) {
|
||||
os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str());
|
||||
@ -303,7 +334,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
|
||||
timezone = date::current_zone();
|
||||
}
|
||||
wtime = {locale_, date::make_zoned(timezone, date::floor<std::chrono::seconds>(*now))};
|
||||
os << fmt::format(format_, wtime) << "\n";
|
||||
os << fmt::format(format_, wtime) << '\n';
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#endif
|
||||
|
||||
waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "cpu", id, "{usage}%", 10) {
|
||||
: ALabel(config, "cpu", id, "{usage}%", 10) {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
@ -23,7 +23,7 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
auto [cpu_usage, tooltip] = getCpuUsage();
|
||||
auto [max_frequency, min_frequency, avg_frequency] = getCpuFrequency();
|
||||
if (tooltipEnabled()) {
|
||||
button_.set_tooltip_text(tooltip);
|
||||
label_.set_tooltip_text(tooltip);
|
||||
}
|
||||
auto format = format_;
|
||||
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
|
||||
@ -39,7 +39,6 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
auto icons = std::vector<std::string>{state};
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(fmt::arg("load", cpu_load));
|
||||
store.push_back(fmt::arg("load", cpu_load));
|
||||
store.push_back(fmt::arg("usage", total_usage));
|
||||
store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
|
||||
store.push_back(fmt::arg("max_frequency", max_frequency));
|
||||
@ -52,11 +51,11 @@ auto waybar::modules::Cpu::update() -> void {
|
||||
auto icon_format = fmt::format("icon{}", core_i);
|
||||
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
|
||||
}
|
||||
label_->set_markup(fmt::vformat(format, store));
|
||||
label_.set_markup(fmt::vformat(format, store));
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
double waybar::modules::Cpu::getCpuLoad() {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
||||
const Json::Value& config)
|
||||
: AButton(config, "custom-" + name, id, "{}"),
|
||||
: ALabel(config, "custom-" + name, id, "{}"),
|
||||
name_(name),
|
||||
id_(id),
|
||||
percentage_(0),
|
||||
@ -21,6 +21,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
|
||||
waybar::modules::Custom::~Custom() {
|
||||
if (pid_ != -1) {
|
||||
killpg(pid_, SIGTERM);
|
||||
waitpid(pid_, NULL, 0);
|
||||
pid_ = -1;
|
||||
}
|
||||
}
|
||||
@ -103,13 +104,13 @@ void waybar::modules::Custom::handleEvent() {
|
||||
}
|
||||
|
||||
bool waybar::modules::Custom::handleScroll(GdkEventScroll* e) {
|
||||
auto ret = AButton::handleScroll(e);
|
||||
auto ret = ALabel::handleScroll(e);
|
||||
handleEvent();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool waybar::modules::Custom::handleToggle(GdkEventButton* const& e) {
|
||||
auto ret = AButton::handleToggle(e);
|
||||
auto ret = ALabel::handleToggle(e);
|
||||
handleEvent();
|
||||
return ret;
|
||||
}
|
||||
@ -131,33 +132,33 @@ auto waybar::modules::Custom::update() -> void {
|
||||
if (str.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
label_->set_markup(str);
|
||||
label_.set_markup(str);
|
||||
if (tooltipEnabled()) {
|
||||
if (text_ == tooltip_) {
|
||||
if (button_.get_tooltip_markup() != str) {
|
||||
button_.set_tooltip_markup(str);
|
||||
if (label_.get_tooltip_markup() != str) {
|
||||
label_.set_tooltip_markup(str);
|
||||
}
|
||||
} else {
|
||||
if (button_.get_tooltip_markup() != tooltip_) {
|
||||
button_.set_tooltip_markup(tooltip_);
|
||||
if (label_.get_tooltip_markup() != tooltip_) {
|
||||
label_.set_tooltip_markup(tooltip_);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto classes = button_.get_style_context()->list_classes();
|
||||
auto classes = label_.get_style_context()->list_classes();
|
||||
for (auto const& c : classes) {
|
||||
if (c == id_) continue;
|
||||
button_.get_style_context()->remove_class(c);
|
||||
label_.get_style_context()->remove_class(c);
|
||||
}
|
||||
for (auto const& c : class_) {
|
||||
button_.get_style_context()->add_class(c);
|
||||
label_.get_style_context()->add_class(c);
|
||||
}
|
||||
button_.get_style_context()->add_class("flat");
|
||||
button_.get_style_context()->add_class("text-button");
|
||||
label_.get_style_context()->add_class("flat");
|
||||
label_.get_style_context()->add_class("text-button");
|
||||
event_box_.show();
|
||||
}
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
void waybar::modules::Custom::parseOutputRaw() {
|
||||
|
@ -3,7 +3,7 @@
|
||||
using namespace waybar::util;
|
||||
|
||||
waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "disk", id, "{}%", 30), path_("/") {
|
||||
: ALabel(config, "disk", id, "{}%", 30), path_("/") {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
@ -58,7 +58,7 @@ auto waybar::modules::Disk::update() -> void {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
event_box_.show();
|
||||
label_->set_markup(
|
||||
label_.set_markup(
|
||||
fmt::format(format, stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
|
||||
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks),
|
||||
fmt::arg("used", used), fmt::arg("percentage_used", percentage_used),
|
||||
@ -70,12 +70,12 @@ auto waybar::modules::Disk::update() -> void {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
button_.set_tooltip_text(
|
||||
label_.set_tooltip_text(
|
||||
fmt::format(tooltip_format, stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
|
||||
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks),
|
||||
fmt::arg("used", used), fmt::arg("percentage_used", percentage_used),
|
||||
fmt::arg("total", total), fmt::arg("path", path_)));
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
@ -95,15 +95,37 @@ void IPC::parseIPC(const std::string& ev) {
|
||||
|
||||
for (auto& [eventname, handler] : callbacks) {
|
||||
if (eventname == request) {
|
||||
handler(ev);
|
||||
handler->onEvent(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPC::registerForIPC(const std::string& ev, std::function<void(const std::string&)> fn) {
|
||||
void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) {
|
||||
if (!ev_handler) {
|
||||
return;
|
||||
}
|
||||
callbackMutex.lock();
|
||||
|
||||
callbacks.emplace_back(std::make_pair(ev, fn));
|
||||
callbacks.emplace_back(std::make_pair(ev, ev_handler));
|
||||
|
||||
callbackMutex.unlock();
|
||||
}
|
||||
|
||||
void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||
if (!ev_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
callbackMutex.lock();
|
||||
|
||||
for (auto it = callbacks.begin(); it != callbacks.end();) {
|
||||
auto it_current = it;
|
||||
it++;
|
||||
auto& [eventname, handler] = *it_current;
|
||||
if (handler == ev_handler) {
|
||||
callbacks.erase(it_current);
|
||||
}
|
||||
}
|
||||
|
||||
callbackMutex.unlock();
|
||||
}
|
||||
@ -168,4 +190,4 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
@ -11,7 +11,7 @@
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
Language::Language(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: AButton(config, "language", id, "{}", 0, true), bar_(bar) {
|
||||
: ALabel(config, "language", id, "{}", 0, true), bar_(bar) {
|
||||
modulesReady = true;
|
||||
|
||||
if (!gIPC.get()) {
|
||||
@ -21,40 +21,45 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con
|
||||
// get the active layout when open
|
||||
initLanguage();
|
||||
|
||||
button_.hide();
|
||||
AButton::update();
|
||||
label_.hide();
|
||||
ALabel::update();
|
||||
|
||||
// register for hyprland ipc
|
||||
gIPC->registerForIPC("activelayout", [&](const std::string& ev) { this->onEvent(ev); });
|
||||
gIPC->registerForIPC("activelayout", this);
|
||||
}
|
||||
|
||||
Language::~Language() {
|
||||
gIPC->unregisterForIPC(this);
|
||||
// wait for possible event handler to finish
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
}
|
||||
|
||||
auto Language::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
if (!format_.empty()) {
|
||||
button_.show();
|
||||
label_->set_markup(layoutName_);
|
||||
label_.show();
|
||||
label_.set_markup(layoutName_);
|
||||
} else {
|
||||
button_.hide();
|
||||
label_.hide();
|
||||
}
|
||||
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
void Language::onEvent(const std::string& ev) {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
auto layoutName = ev.substr(ev.find_last_of(',') + 1);
|
||||
auto keebName = ev.substr(0, ev.find_last_of(','));
|
||||
keebName = keebName.substr(keebName.find_first_of('>') + 2);
|
||||
auto kbName = ev.substr(ev.find_last_of('>') + 1, ev.find_first_of(','));
|
||||
auto layoutName = ev.substr(ev.find_first_of(',') + 1);
|
||||
|
||||
if (config_.isMember("keyboard-name") && keebName != config_["keyboard-name"].asString())
|
||||
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
|
||||
return; // ignore
|
||||
|
||||
const auto BRIEFNAME = getShortFrom(layoutName);
|
||||
const auto briefName = getShortFrom(layoutName);
|
||||
|
||||
if (config_.isMember("format-" + BRIEFNAME)) {
|
||||
const auto PROPNAME = "format-" + BRIEFNAME;
|
||||
layoutName = fmt::format(format_, config_[PROPNAME].asString());
|
||||
if (config_.isMember("format-" + briefName)) {
|
||||
const auto propName = "format-" + briefName;
|
||||
layoutName = fmt::format(format_, config_[propName].asString());
|
||||
} else {
|
||||
layoutName = fmt::format(format_, layoutName);
|
||||
}
|
||||
@ -71,19 +76,30 @@ void Language::onEvent(const std::string& ev) {
|
||||
}
|
||||
|
||||
void Language::initLanguage() {
|
||||
const auto INPUTDEVICES = gIPC->getSocket1Reply("devices");
|
||||
const auto inputDevices = gIPC->getSocket1Reply("devices");
|
||||
|
||||
if (!config_.isMember("keyboard-name")) return;
|
||||
|
||||
const auto KEEBNAME = config_["keyboard-name"].asString();
|
||||
const auto kbName = config_["keyboard-name"].asString();
|
||||
|
||||
try {
|
||||
auto searcher = INPUTDEVICES.substr(INPUTDEVICES.find(KEEBNAME) + KEEBNAME.length());
|
||||
searcher = searcher.substr(searcher.find("Keyboard at"));
|
||||
searcher = searcher.substr(searcher.find("keymap:") + 7);
|
||||
auto searcher = kbName.empty()
|
||||
? inputDevices
|
||||
: inputDevices.substr(inputDevices.find(kbName) + kbName.length());
|
||||
searcher = searcher.substr(searcher.find("keymap:") + 8);
|
||||
searcher = searcher.substr(0, searcher.find_first_of("\n\t"));
|
||||
|
||||
layoutName_ = searcher;
|
||||
auto layoutName = std::string{};
|
||||
const auto briefName = getShortFrom(searcher);
|
||||
|
||||
if (config_.isMember("format-" + briefName)) {
|
||||
const auto propName = "format-" + briefName;
|
||||
layoutName = fmt::format(format_, config_[propName].asString());
|
||||
} else {
|
||||
layoutName = fmt::format(format_, searcher);
|
||||
}
|
||||
|
||||
layoutName = waybar::util::sanitize_string(layoutName);
|
||||
|
||||
layoutName_ = layoutName;
|
||||
|
||||
spdlog::debug("hyprland language initLanguage found {}", layoutName_);
|
||||
|
||||
|
62
src/modules/hyprland/submap.cpp
Normal file
62
src/modules/hyprland/submap.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "modules/hyprland/submap.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <util/sanitize_str.hpp>
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: ALabel(config, "submap", id, "{}", 0, true), bar_(bar) {
|
||||
modulesReady = true;
|
||||
|
||||
if (!gIPC.get()) {
|
||||
gIPC = std::make_unique<IPC>();
|
||||
}
|
||||
|
||||
label_.hide();
|
||||
ALabel::update();
|
||||
|
||||
// register for hyprland ipc
|
||||
gIPC->registerForIPC("submap", this);
|
||||
}
|
||||
|
||||
Submap::~Submap() {
|
||||
gIPC->unregisterForIPC(this);
|
||||
// wait for possible event handler to finish
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
}
|
||||
|
||||
auto Submap::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
if (submap_.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
label_.set_markup(fmt::format(format_, submap_));
|
||||
if (tooltipEnabled()) {
|
||||
label_.set_tooltip_text(submap_);
|
||||
}
|
||||
event_box_.show();
|
||||
}
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
void Submap::onEvent(const std::string& ev) {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
if (ev.find("submap") == std::string::npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto submapName = ev.substr(ev.find_last_of('>') + 1);
|
||||
submapName = waybar::util::sanitize_string(submapName);
|
||||
|
||||
submap_ = submapName;
|
||||
|
||||
spdlog::debug("hyprland submap onevent with {}", submap_);
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
} // namespace waybar::modules::hyprland
|
@ -25,7 +25,13 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
ALabel::update();
|
||||
|
||||
// register for hyprland ipc
|
||||
gIPC->registerForIPC("activewindow", [&](const std::string& ev) { this->onEvent(ev); });
|
||||
gIPC->registerForIPC("activewindow", this);
|
||||
}
|
||||
|
||||
Window::~Window() {
|
||||
gIPC->unregisterForIPC(this);
|
||||
// wait for possible event handler to finish
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
}
|
||||
|
||||
auto Window::update() -> void {
|
||||
@ -43,24 +49,26 @@ auto Window::update() -> void {
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
uint Window::getActiveWorkspaceID(std::string monitorName) {
|
||||
int Window::getActiveWorkspaceID(std::string monitorName) {
|
||||
auto cmd = waybar::util::command::exec("hyprctl monitors -j");
|
||||
assert(cmd.exit_code == 0);
|
||||
Json::Value json = parser_.parse(cmd.out);
|
||||
assert(json.isArray());
|
||||
auto monitor = std::find_if(json.begin(), json.end(),
|
||||
[&](Json::Value monitor) { return monitor["name"] == monitorName; });
|
||||
assert(monitor != std::end(json));
|
||||
return (*monitor)["activeWorkspace"]["id"].as<uint>();
|
||||
if (monitor == std::end(json)) {
|
||||
return 0;
|
||||
}
|
||||
return (*monitor)["activeWorkspace"]["id"].as<int>();
|
||||
}
|
||||
|
||||
std::string Window::getLastWindowTitle(uint workspaceID) {
|
||||
std::string Window::getLastWindowTitle(int workspaceID) {
|
||||
auto cmd = waybar::util::command::exec("hyprctl workspaces -j");
|
||||
assert(cmd.exit_code == 0);
|
||||
Json::Value json = parser_.parse(cmd.out);
|
||||
assert(json.isArray());
|
||||
auto workspace = std::find_if(json.begin(), json.end(), [&](Json::Value workspace) {
|
||||
return workspace["id"].as<uint>() == workspaceID;
|
||||
return workspace["id"].as<int>() == workspaceID;
|
||||
});
|
||||
|
||||
if (workspace == std::end(json)) {
|
||||
@ -89,4 +97,4 @@ void Window::onEvent(const std::string& ev) {
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
} // namespace waybar::modules::hyprland
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
@ -8,7 +8,7 @@ bool waybar::modules::IdleInhibitor::status = false;
|
||||
|
||||
waybar::modules::IdleInhibitor::IdleInhibitor(const std::string& id, const Bar& bar,
|
||||
const Json::Value& config)
|
||||
: AButton(config, "idle_inhibitor", id, "{status}", 0, false, true),
|
||||
: ALabel(config, "idle_inhibitor", id, "{status}", 0, false, true),
|
||||
bar_(bar),
|
||||
idle_inhibitor_(nullptr),
|
||||
pid_(-1) {
|
||||
@ -49,13 +49,13 @@ waybar::modules::IdleInhibitor::~IdleInhibitor() {
|
||||
auto waybar::modules::IdleInhibitor::update() -> void {
|
||||
// Check status
|
||||
if (status) {
|
||||
button_.get_style_context()->remove_class("deactivated");
|
||||
label_.get_style_context()->remove_class("deactivated");
|
||||
if (idle_inhibitor_ == nullptr) {
|
||||
idle_inhibitor_ = zwp_idle_inhibit_manager_v1_create_inhibitor(
|
||||
waybar::Client::inst()->idle_inhibit_manager, bar_.surface);
|
||||
}
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("activated");
|
||||
label_.get_style_context()->remove_class("activated");
|
||||
if (idle_inhibitor_ != nullptr) {
|
||||
zwp_idle_inhibitor_v1_destroy(idle_inhibitor_);
|
||||
idle_inhibitor_ = nullptr;
|
||||
@ -63,11 +63,11 @@ auto waybar::modules::IdleInhibitor::update() -> void {
|
||||
}
|
||||
|
||||
std::string status_text = status ? "activated" : "deactivated";
|
||||
label_->set_markup(fmt::format(format_, fmt::arg("status", status_text),
|
||||
fmt::arg("icon", getIcon(0, status_text))));
|
||||
button_.get_style_context()->add_class(status_text);
|
||||
label_.set_markup(fmt::format(format_, fmt::arg("status", status_text),
|
||||
fmt::arg("icon", getIcon(0, status_text))));
|
||||
label_.get_style_context()->add_class(status_text);
|
||||
if (tooltipEnabled()) {
|
||||
button_.set_tooltip_markup(
|
||||
label_.set_tooltip_markup(
|
||||
status ? fmt::format(config_["tooltip-format-activated"].isString()
|
||||
? config_["tooltip-format-activated"].asString()
|
||||
: "{status}",
|
||||
@ -80,7 +80,7 @@ auto waybar::modules::IdleInhibitor::update() -> void {
|
||||
fmt::arg("icon", getIcon(0, status_text))));
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
void waybar::modules::IdleInhibitor::toggleStatus() {
|
||||
@ -124,6 +124,6 @@ bool waybar::modules::IdleInhibitor::handleToggle(GdkEventButton* const& e) {
|
||||
}
|
||||
}
|
||||
|
||||
AButton::handleToggle(e);
|
||||
ALabel::handleToggle(e);
|
||||
return true;
|
||||
}
|
||||
|
59
src/modules/image.cpp
Normal file
59
src/modules/image.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "modules/image.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
waybar::modules::Image::Image(const std::string& name, const std::string& id,
|
||||
const Json::Value& config)
|
||||
: AModule(config, "image-" + name, id, "{}") {
|
||||
event_box_.add(image_);
|
||||
|
||||
dp.emit();
|
||||
|
||||
path_ = config["path"].asString();
|
||||
size_ = config["size"].asInt();
|
||||
|
||||
interval_ = config_["interval"].asInt();
|
||||
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
|
||||
if (interval_ == 0) {
|
||||
interval_ = INT_MAX;
|
||||
}
|
||||
|
||||
delayWorker();
|
||||
}
|
||||
|
||||
void waybar::modules::Image::delayWorker() {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
auto interval = std::chrono::seconds(interval_);
|
||||
thread_.sleep_for(interval);
|
||||
};
|
||||
}
|
||||
|
||||
void waybar::modules::Image::refresh(int sig) {
|
||||
if (sig == SIGRTMIN + config_["signal"].asInt()) {
|
||||
thread_.wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::Image::update() -> void {
|
||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||
|
||||
if (Glib::file_test(path_, Glib::FILE_TEST_EXISTS))
|
||||
pixbuf = Gdk::Pixbuf::create_from_file(path_, size_, size_);
|
||||
else
|
||||
pixbuf = {};
|
||||
|
||||
if (pixbuf) {
|
||||
image_.set(pixbuf);
|
||||
image_.show();
|
||||
} else {
|
||||
image_.clear();
|
||||
image_.hide();
|
||||
}
|
||||
|
||||
AModule::update();
|
||||
}
|
@ -98,7 +98,7 @@ auto getInhibitors(const Json::Value& config) -> std::string {
|
||||
namespace waybar::modules {
|
||||
|
||||
Inhibitor::Inhibitor(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: AButton(config, "inhibitor", id, "{status}", true),
|
||||
: ALabel(config, "inhibitor", id, "{status}", true),
|
||||
dbus_(::dbus()),
|
||||
inhibitors_(::getInhibitors(config)) {
|
||||
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||
@ -117,16 +117,16 @@ auto Inhibitor::activated() -> bool { return handle_ != -1; }
|
||||
auto Inhibitor::update() -> void {
|
||||
std::string status_text = activated() ? "activated" : "deactivated";
|
||||
|
||||
button_.get_style_context()->remove_class(activated() ? "deactivated" : "activated");
|
||||
label_->set_markup(fmt::format(format_, fmt::arg("status", status_text),
|
||||
fmt::arg("icon", getIcon(0, status_text))));
|
||||
button_.get_style_context()->add_class(status_text);
|
||||
label_.get_style_context()->remove_class(activated() ? "deactivated" : "activated");
|
||||
label_.set_markup(fmt::format(format_, fmt::arg("status", status_text),
|
||||
fmt::arg("icon", getIcon(0, status_text))));
|
||||
label_.get_style_context()->add_class(status_text);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
button_.set_tooltip_text(status_text);
|
||||
label_.set_tooltip_text(status_text);
|
||||
}
|
||||
|
||||
return AButton::update();
|
||||
return ALabel::update();
|
||||
}
|
||||
|
||||
auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool {
|
||||
@ -142,7 +142,7 @@ auto Inhibitor::handleToggle(GdkEventButton* const& e) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
return AButton::handleToggle(e);
|
||||
return ALabel::handleToggle(e);
|
||||
}
|
||||
|
||||
} // namespace waybar::modules
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "modules/memory.hpp"
|
||||
|
||||
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "memory", id, "{}%", 30) {
|
||||
: ALabel(config, "memory", id, "{}%", 30) {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
@ -55,7 +55,7 @@ auto waybar::modules::Memory::update() -> void {
|
||||
} else {
|
||||
event_box_.show();
|
||||
auto icons = std::vector<std::string>{state};
|
||||
label_->set_markup(fmt::format(
|
||||
label_.set_markup(fmt::format(
|
||||
format, used_ram_percentage, fmt::arg("icon", getIcon(used_ram_percentage, icons)),
|
||||
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
|
||||
fmt::arg("percentage", used_ram_percentage),
|
||||
@ -67,7 +67,7 @@ auto waybar::modules::Memory::update() -> void {
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
auto tooltip_format = config_["tooltip-format"].asString();
|
||||
button_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
tooltip_format, used_ram_percentage, fmt::arg("total", total_ram_gigabytes),
|
||||
fmt::arg("swapTotal", total_swap_gigabytes),
|
||||
fmt::arg("percentage", used_ram_percentage),
|
||||
@ -75,12 +75,12 @@ auto waybar::modules::Memory::update() -> void {
|
||||
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
|
||||
fmt::arg("swapAvail", available_swap_gigabytes)));
|
||||
} else {
|
||||
button_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
event_box_.hide();
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace waybar::modules {
|
||||
#endif
|
||||
|
||||
waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "mpd", id, "{album} - {artist} - {title}", 5, false, true),
|
||||
: ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5, false, true),
|
||||
module_name_(id.empty() ? "mpd" : "mpd#" + id),
|
||||
server_(nullptr),
|
||||
port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
|
||||
@ -47,7 +47,7 @@ auto waybar::modules::MPD::update() -> void {
|
||||
context_.update();
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
void waybar::modules::MPD::queryMPD() {
|
||||
@ -88,15 +88,20 @@ std::string waybar::modules::MPD::getFilename() const {
|
||||
|
||||
void waybar::modules::MPD::setLabel() {
|
||||
if (connection_ == nullptr) {
|
||||
button_.get_style_context()->add_class("disconnected");
|
||||
button_.get_style_context()->remove_class("stopped");
|
||||
button_.get_style_context()->remove_class("playing");
|
||||
button_.get_style_context()->remove_class("paused");
|
||||
label_.get_style_context()->add_class("disconnected");
|
||||
label_.get_style_context()->remove_class("stopped");
|
||||
label_.get_style_context()->remove_class("playing");
|
||||
label_.get_style_context()->remove_class("paused");
|
||||
|
||||
auto format = config_["format-disconnected"].isString()
|
||||
? config_["format-disconnected"].asString()
|
||||
: "disconnected";
|
||||
label_->set_markup(format);
|
||||
if (format.empty()) {
|
||||
label_.set_markup(format);
|
||||
label_.show();
|
||||
} else {
|
||||
label_.hide();
|
||||
}
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
std::string tooltip_format;
|
||||
@ -104,12 +109,11 @@ void waybar::modules::MPD::setLabel() {
|
||||
? config_["tooltip-format-disconnected"].asString()
|
||||
: "MPD (disconnected)";
|
||||
// Nothing to format
|
||||
button_.set_tooltip_text(tooltip_format);
|
||||
label_.set_tooltip_text(tooltip_format);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("disconnected");
|
||||
}
|
||||
label_.get_style_context()->remove_class("disconnected");
|
||||
|
||||
auto format = format_;
|
||||
Glib::ustring artist, album_artist, album, title;
|
||||
@ -121,19 +125,19 @@ void waybar::modules::MPD::setLabel() {
|
||||
if (stopped()) {
|
||||
format =
|
||||
config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped";
|
||||
button_.get_style_context()->add_class("stopped");
|
||||
button_.get_style_context()->remove_class("playing");
|
||||
button_.get_style_context()->remove_class("paused");
|
||||
label_.get_style_context()->add_class("stopped");
|
||||
label_.get_style_context()->remove_class("playing");
|
||||
label_.get_style_context()->remove_class("paused");
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("stopped");
|
||||
label_.get_style_context()->remove_class("stopped");
|
||||
if (playing()) {
|
||||
button_.get_style_context()->add_class("playing");
|
||||
button_.get_style_context()->remove_class("paused");
|
||||
label_.get_style_context()->add_class("playing");
|
||||
label_.get_style_context()->remove_class("paused");
|
||||
} else if (paused()) {
|
||||
format = config_["format-paused"].isString() ? config_["format-paused"].asString()
|
||||
: config_["format"].asString();
|
||||
button_.get_style_context()->add_class("paused");
|
||||
button_.get_style_context()->remove_class("playing");
|
||||
label_.get_style_context()->add_class("paused");
|
||||
label_.get_style_context()->remove_class("playing");
|
||||
}
|
||||
|
||||
stateIcon = getStateIcon();
|
||||
@ -169,17 +173,21 @@ void waybar::modules::MPD::setLabel() {
|
||||
if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt());
|
||||
|
||||
try {
|
||||
label_->set_markup(fmt::format(
|
||||
format, fmt::arg("artist", Glib::Markup::escape_text(artist).raw()),
|
||||
fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist).raw()),
|
||||
fmt::arg("album", Glib::Markup::escape_text(album).raw()),
|
||||
fmt::arg("title", Glib::Markup::escape_text(title).raw()),
|
||||
fmt::arg("date", Glib::Markup::escape_text(date).raw()), fmt::arg("volume", volume),
|
||||
fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime),
|
||||
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
||||
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
||||
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
|
||||
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename)));
|
||||
auto text = fmt::format(
|
||||
format, fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()),
|
||||
fmt::arg("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date),
|
||||
fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime),
|
||||
fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos),
|
||||
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
||||
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
||||
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon),
|
||||
fmt::arg("filename", filename));
|
||||
if (text.empty()) {
|
||||
label_.hide();
|
||||
} else {
|
||||
label_.show();
|
||||
label_.set_markup(text);
|
||||
}
|
||||
} catch (fmt::format_error const& e) {
|
||||
spdlog::warn("mpd: format error: {}", e.what());
|
||||
}
|
||||
@ -198,7 +206,7 @@ void waybar::modules::MPD::setLabel() {
|
||||
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
||||
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
||||
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon));
|
||||
button_.set_tooltip_text(tooltip_text);
|
||||
label_.set_tooltip_text(tooltip_text);
|
||||
} catch (fmt::format_error const& e) {
|
||||
spdlog::warn("mpd: format error (tooltip): {}", e.what());
|
||||
}
|
||||
|
394
src/modules/mpris/mpris.cpp
Normal file
394
src/modules/mpris/mpris.cpp
Normal file
@ -0,0 +1,394 @@
|
||||
#include "modules/mpris/mpris.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include <playerctl/playerctl.h>
|
||||
}
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace waybar::modules::mpris {
|
||||
|
||||
const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}";
|
||||
|
||||
Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||
: AModule(config, "mpris", id),
|
||||
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||
label_(),
|
||||
format_(DEFAULT_FORMAT),
|
||||
interval_(0),
|
||||
player_("playerctld"),
|
||||
manager(),
|
||||
player() {
|
||||
box_.pack_start(label_);
|
||||
box_.set_name(name_);
|
||||
event_box_.add(box_);
|
||||
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Mpris::handleToggle));
|
||||
|
||||
if (config_["format"].isString()) {
|
||||
format_ = config_["format"].asString();
|
||||
}
|
||||
if (config_["format-playing"].isString()) {
|
||||
format_playing_ = config_["format-playing"].asString();
|
||||
}
|
||||
if (config_["format-paused"].isString()) {
|
||||
format_paused_ = config_["format-paused"].asString();
|
||||
}
|
||||
if (config_["format-stopped"].isString()) {
|
||||
format_stopped_ = config_["format-stopped"].asString();
|
||||
}
|
||||
if (config_["interval"].isUInt()) {
|
||||
interval_ = std::chrono::seconds(config_["interval"].asUInt());
|
||||
}
|
||||
if (config_["player"].isString()) {
|
||||
player_ = config_["player"].asString();
|
||||
}
|
||||
if (config_["ignored-players"].isArray()) {
|
||||
for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end();
|
||||
++it) {
|
||||
ignored_players_.push_back(it->asString());
|
||||
}
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
manager = playerctl_player_manager_new(&error);
|
||||
if (error) {
|
||||
throw std::runtime_error(fmt::format("unable to create MPRIS client: {}", error->message));
|
||||
}
|
||||
|
||||
g_object_connect(manager, "signal::name-appeared", G_CALLBACK(onPlayerNameAppeared), this, NULL);
|
||||
g_object_connect(manager, "signal::name-vanished", G_CALLBACK(onPlayerNameVanished), this, NULL);
|
||||
|
||||
if (player_ == "playerctld") {
|
||||
// use playerctld proxy
|
||||
PlayerctlPlayerName name = {
|
||||
.instance = (gchar*)player_.c_str(),
|
||||
.source = PLAYERCTL_SOURCE_DBUS_SESSION,
|
||||
};
|
||||
player = playerctl_player_new_from_name(&name, &error);
|
||||
|
||||
} else {
|
||||
GList* players = playerctl_list_players(&error);
|
||||
if (error) {
|
||||
auto e = fmt::format("unable to list players: {}", error->message);
|
||||
g_error_free(error);
|
||||
throw std::runtime_error(e);
|
||||
}
|
||||
|
||||
for (auto p = players; p != NULL; p = p->next) {
|
||||
auto pn = static_cast<PlayerctlPlayerName*>(p->data);
|
||||
if (strcmp(pn->name, player_.c_str()) == 0) {
|
||||
player = playerctl_player_new_from_name(pn, &error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("unable to connect to player {}: {}", player_, error->message));
|
||||
}
|
||||
|
||||
if (player) {
|
||||
g_object_connect(player, "signal::play", G_CALLBACK(onPlayerPlay), this, "signal::pause",
|
||||
G_CALLBACK(onPlayerPause), this, "signal::stop", G_CALLBACK(onPlayerStop),
|
||||
this, "signal::stop", G_CALLBACK(onPlayerStop), this, "signal::metadata",
|
||||
G_CALLBACK(onPlayerMetadata), this, NULL);
|
||||
}
|
||||
|
||||
// allow setting an interval count that triggers periodic refreshes
|
||||
if (interval_.count() > 0) {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
};
|
||||
}
|
||||
|
||||
// trigger initial update
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
Mpris::~Mpris() {
|
||||
if (manager != NULL) g_object_unref(manager);
|
||||
if (player != NULL) g_object_unref(player);
|
||||
}
|
||||
|
||||
auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::string {
|
||||
if (icons.isObject()) {
|
||||
if (icons[key].isString()) {
|
||||
return icons[key].asString();
|
||||
} else if (icons["default"].isString()) {
|
||||
return icons["default"].asString();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name,
|
||||
gpointer data) -> void {
|
||||
Mpris* mpris = static_cast<Mpris*>(data);
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: name-appeared callback: {}", player_name->name);
|
||||
|
||||
if (std::string(player_name->name) != mpris->player_) {
|
||||
return;
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
mpris->player = playerctl_player_new_from_name(player_name, &error);
|
||||
g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause",
|
||||
G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop),
|
||||
mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::metadata",
|
||||
G_CALLBACK(onPlayerMetadata), mpris, NULL);
|
||||
|
||||
mpris->dp.emit();
|
||||
}
|
||||
|
||||
auto Mpris::onPlayerNameVanished(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name,
|
||||
gpointer data) -> void {
|
||||
Mpris* mpris = static_cast<Mpris*>(data);
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: player-vanished callback: {}", player_name->name);
|
||||
|
||||
if (std::string(player_name->name) == mpris->player_) {
|
||||
mpris->player = nullptr;
|
||||
mpris->dp.emit();
|
||||
}
|
||||
}
|
||||
|
||||
auto Mpris::onPlayerPlay(PlayerctlPlayer* player, gpointer data) -> void {
|
||||
Mpris* mpris = static_cast<Mpris*>(data);
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: player-play callback");
|
||||
// update widget
|
||||
mpris->dp.emit();
|
||||
}
|
||||
|
||||
auto Mpris::onPlayerPause(PlayerctlPlayer* player, gpointer data) -> void {
|
||||
Mpris* mpris = static_cast<Mpris*>(data);
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: player-pause callback");
|
||||
// update widget
|
||||
mpris->dp.emit();
|
||||
}
|
||||
|
||||
auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void {
|
||||
Mpris* mpris = static_cast<Mpris*>(data);
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: player-stop callback");
|
||||
|
||||
// hide widget
|
||||
mpris->event_box_.set_visible(false);
|
||||
// update widget
|
||||
mpris->dp.emit();
|
||||
}
|
||||
|
||||
auto Mpris::onPlayerMetadata(PlayerctlPlayer* player, GVariant* metadata, gpointer data) -> void {
|
||||
Mpris* mpris = static_cast<Mpris*>(data);
|
||||
if (!mpris) return;
|
||||
|
||||
spdlog::debug("mpris: player-metadata callback");
|
||||
// update widget
|
||||
mpris->dp.emit();
|
||||
}
|
||||
|
||||
auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
|
||||
if (!player) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
|
||||
char* player_status = nullptr;
|
||||
auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED;
|
||||
g_object_get(player, "status", &player_status, "playback-status", &player_playback_status, NULL);
|
||||
|
||||
std::string player_name = player_;
|
||||
if (player_name == "playerctld") {
|
||||
GList* players = playerctl_list_players(&error);
|
||||
if (error) {
|
||||
auto e = fmt::format("unable to list players: {}", error->message);
|
||||
g_error_free(error);
|
||||
throw std::runtime_error(e);
|
||||
}
|
||||
// > get the list of players [..] in order of activity
|
||||
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
|
||||
players = g_list_first(players);
|
||||
if (players) player_name = static_cast<PlayerctlPlayerName*>(players->data)->name;
|
||||
}
|
||||
|
||||
if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
|
||||
[&](const std::string& pn) { return player_name == pn; })) {
|
||||
spdlog::warn("mpris[{}]: ignoring player update", player_name);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// make status lowercase
|
||||
player_status[0] = std::tolower(player_status[0]);
|
||||
|
||||
PlayerInfo info = {
|
||||
.name = player_name,
|
||||
.status = player_playback_status,
|
||||
.status_string = player_status,
|
||||
};
|
||||
|
||||
if (auto artist_ = playerctl_player_get_artist(player, &error)) {
|
||||
spdlog::debug("mpris[{}]: artist = {}", info.name, artist_);
|
||||
info.artist = Glib::Markup::escape_text(artist_);
|
||||
g_free(artist_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
if (auto album_ = playerctl_player_get_album(player, &error)) {
|
||||
spdlog::debug("mpris[{}]: album = {}", info.name, album_);
|
||||
info.album = Glib::Markup::escape_text(album_);
|
||||
g_free(album_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
if (auto title_ = playerctl_player_get_title(player, &error)) {
|
||||
spdlog::debug("mpris[{}]: title = {}", info.name, title_);
|
||||
info.title = Glib::Markup::escape_text(title_);
|
||||
g_free(title_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
if (auto length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) {
|
||||
spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_);
|
||||
std::chrono::microseconds len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
|
||||
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
|
||||
auto len_m = std::chrono::duration_cast<std::chrono::minutes>(len - len_h);
|
||||
auto len_s = std::chrono::duration_cast<std::chrono::seconds>(len - len_m);
|
||||
info.length = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count());
|
||||
g_free(length_);
|
||||
}
|
||||
if (error) goto errorexit;
|
||||
|
||||
return info;
|
||||
|
||||
errorexit:
|
||||
spdlog::error("mpris[{}]: {}", info.name, error->message);
|
||||
g_error_free(error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool Mpris::handleToggle(GdkEventButton* const& e) {
|
||||
GError* error = nullptr;
|
||||
|
||||
auto info = getPlayerInfo();
|
||||
if (!info) return false;
|
||||
|
||||
if (e->type == GdkEventType::GDK_BUTTON_PRESS) {
|
||||
switch (e->button) {
|
||||
case 1: // left-click
|
||||
if (config_["on-click"].isString()) {
|
||||
return AModule::handleToggle(e);
|
||||
}
|
||||
playerctl_player_play_pause(player, &error);
|
||||
break;
|
||||
case 2: // middle-click
|
||||
if (config_["on-middle-click"].isString()) {
|
||||
return AModule::handleToggle(e);
|
||||
}
|
||||
playerctl_player_previous(player, &error);
|
||||
break;
|
||||
case 3: // right-click
|
||||
if (config_["on-right-click"].isString()) {
|
||||
return AModule::handleToggle(e);
|
||||
}
|
||||
playerctl_player_next(player, &error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
spdlog::error("mpris[{}]: error running builtin on-click action: {}", (*info).name,
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mpris::update() -> void {
|
||||
auto opt = getPlayerInfo();
|
||||
if (!opt) {
|
||||
event_box_.set_visible(false);
|
||||
AModule::update();
|
||||
return;
|
||||
}
|
||||
auto info = *opt;
|
||||
|
||||
if (info.status == PLAYERCTL_PLAYBACK_STATUS_STOPPED) {
|
||||
spdlog::debug("mpris[{}]: player stopped, skipping update", info.name);
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::debug("mpris[{}]: running update", info.name);
|
||||
|
||||
// dynamic is the auto-formatted string containing a nice out-of-the-box
|
||||
// format text
|
||||
std::stringstream dynamic;
|
||||
if (info.artist) dynamic << *info.artist << " - ";
|
||||
if (info.album) dynamic << *info.album << " - ";
|
||||
if (info.title) dynamic << *info.title;
|
||||
if (info.length)
|
||||
dynamic << " "
|
||||
<< "<small>"
|
||||
<< "[" << *info.length << "]"
|
||||
<< "</small>";
|
||||
|
||||
// set css class for player status
|
||||
if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) {
|
||||
box_.get_style_context()->remove_class(lastStatus);
|
||||
}
|
||||
if (!box_.get_style_context()->has_class(info.status_string)) {
|
||||
box_.get_style_context()->add_class(info.status_string);
|
||||
}
|
||||
lastStatus = info.status_string;
|
||||
|
||||
// set css class for player name
|
||||
if (!lastPlayer.empty() && box_.get_style_context()->has_class(lastPlayer)) {
|
||||
box_.get_style_context()->remove_class(lastPlayer);
|
||||
}
|
||||
if (!box_.get_style_context()->has_class(info.name)) {
|
||||
box_.get_style_context()->add_class(info.name);
|
||||
}
|
||||
lastPlayer = info.name;
|
||||
|
||||
auto formatstr = format_;
|
||||
switch (info.status) {
|
||||
case PLAYERCTL_PLAYBACK_STATUS_PLAYING:
|
||||
if (!format_playing_.empty()) formatstr = format_playing_;
|
||||
break;
|
||||
case PLAYERCTL_PLAYBACK_STATUS_PAUSED:
|
||||
if (!format_paused_.empty()) formatstr = format_paused_;
|
||||
break;
|
||||
case PLAYERCTL_PLAYBACK_STATUS_STOPPED:
|
||||
if (!format_stopped_.empty()) formatstr = format_stopped_;
|
||||
break;
|
||||
}
|
||||
auto label_format =
|
||||
fmt::format(formatstr, fmt::arg("player", info.name), fmt::arg("status", info.status_string),
|
||||
fmt::arg("artist", *info.artist), fmt::arg("title", *info.title),
|
||||
fmt::arg("album", *info.album), fmt::arg("length", *info.length),
|
||||
fmt::arg("dynamic", dynamic.str()),
|
||||
fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)),
|
||||
fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string)));
|
||||
label_.set_markup(label_format);
|
||||
|
||||
event_box_.set_visible(true);
|
||||
// call parent update
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::mpris
|
@ -78,7 +78,7 @@ waybar::modules::Network::readBandwidthUsage() {
|
||||
}
|
||||
|
||||
waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
|
||||
: AButton(config, "network", id, DEFAULT_FORMAT, 60),
|
||||
: ALabel(config, "network", id, DEFAULT_FORMAT, 60),
|
||||
ifid_(-1),
|
||||
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
|
||||
efd_(-1),
|
||||
@ -95,11 +95,11 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
|
||||
#endif
|
||||
frequency_(0.0) {
|
||||
|
||||
// Start with some "text" in the module's label_-> update() will then
|
||||
// Start with some "text" in the module's label_. update() will then
|
||||
// update it. Since the text should be different, update() will be able
|
||||
// to show or hide the event_box_. This is to work around the case where
|
||||
// the module start with no text, but the the event_box_ is shown.
|
||||
label_->set_markup("<s></s>");
|
||||
label_.set_markup("<s></s>");
|
||||
|
||||
auto bandwidth = readBandwidthUsage();
|
||||
if (bandwidth.has_value()) {
|
||||
@ -309,8 +309,8 @@ auto waybar::modules::Network::update() -> void {
|
||||
|
||||
if (!alt_) {
|
||||
auto state = getNetworkState();
|
||||
if (!state_.empty() && button_.get_style_context()->has_class(state_)) {
|
||||
button_.get_style_context()->remove_class(state_);
|
||||
if (!state_.empty() && label_.get_style_context()->has_class(state_)) {
|
||||
label_.get_style_context()->remove_class(state_);
|
||||
}
|
||||
if (config_["format-" + state].isString()) {
|
||||
default_format_ = config_["format-" + state].asString();
|
||||
@ -322,8 +322,8 @@ auto waybar::modules::Network::update() -> void {
|
||||
if (config_["tooltip-format-" + state].isString()) {
|
||||
tooltip_format = config_["tooltip-format-" + state].asString();
|
||||
}
|
||||
if (!button_.get_style_context()->has_class(state)) {
|
||||
button_.get_style_context()->add_class(state);
|
||||
if (!label_.get_style_context()->has_class(state)) {
|
||||
label_.get_style_context()->add_class(state);
|
||||
}
|
||||
format_ = default_format_;
|
||||
state_ = state;
|
||||
@ -349,8 +349,8 @@ auto waybar::modules::Network::update() -> void {
|
||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
||||
fmt::arg("bandwidthTotalBytes",
|
||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
|
||||
if (text.compare(label_->get_label()) != 0) {
|
||||
label_->set_markup(text);
|
||||
if (text.compare(label_.get_label()) != 0) {
|
||||
label_.set_markup(text);
|
||||
if (text.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
@ -382,16 +382,16 @@ auto waybar::modules::Network::update() -> void {
|
||||
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
|
||||
fmt::arg("bandwidthTotalBytes",
|
||||
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
|
||||
if (button_.get_tooltip_text() != tooltip_text) {
|
||||
button_.set_tooltip_markup(tooltip_text);
|
||||
if (label_.get_tooltip_text() != tooltip_text) {
|
||||
label_.set_tooltip_markup(tooltip_text);
|
||||
}
|
||||
} else if (button_.get_tooltip_text() != text) {
|
||||
button_.set_tooltip_markup(text);
|
||||
} else if (label_.get_tooltip_text() != text) {
|
||||
label_.set_tooltip_markup(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
bool waybar::modules::Network::checkInterface(std::string name) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "modules/pulseaudio.hpp"
|
||||
|
||||
waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value &config)
|
||||
: AButton(config, "pulseaudio", id, "{volume}%"),
|
||||
: ALabel(config, "pulseaudio", id, "{volume}%"),
|
||||
mainloop_(nullptr),
|
||||
mainloop_api_(nullptr),
|
||||
context_(nullptr),
|
||||
@ -260,11 +260,12 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
if (!alt_) {
|
||||
std::string format_name = "format";
|
||||
if (monitor_.find("a2dp_sink") != std::string::npos || // PulseAudio
|
||||
monitor_.find("a2dp-sink") != std::string::npos) { // PipeWire
|
||||
monitor_.find("a2dp-sink") != std::string::npos || // PipeWire
|
||||
monitor_.find("bluez") != std::string::npos) {
|
||||
format_name = format_name + "-bluetooth";
|
||||
button_.get_style_context()->add_class("bluetooth");
|
||||
label_.get_style_context()->add_class("bluetooth");
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("bluetooth");
|
||||
label_.get_style_context()->remove_class("bluetooth");
|
||||
}
|
||||
if (muted_) {
|
||||
// Check muted bluetooth format exist, otherwise fallback to default muted format
|
||||
@ -272,32 +273,38 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
format_name = "format";
|
||||
}
|
||||
format_name = format_name + "-muted";
|
||||
button_.get_style_context()->add_class("muted");
|
||||
button_.get_style_context()->add_class("sink-muted");
|
||||
label_.get_style_context()->add_class("muted");
|
||||
label_.get_style_context()->add_class("sink-muted");
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("muted");
|
||||
button_.get_style_context()->remove_class("sink-muted");
|
||||
label_.get_style_context()->remove_class("muted");
|
||||
label_.get_style_context()->remove_class("sink-muted");
|
||||
}
|
||||
format = config_[format_name].isString() ? config_[format_name].asString() : format;
|
||||
}
|
||||
// TODO: find a better way to split source/sink
|
||||
std::string format_source = "{volume}%";
|
||||
if (source_muted_) {
|
||||
button_.get_style_context()->add_class("source-muted");
|
||||
label_.get_style_context()->add_class("source-muted");
|
||||
if (config_["format-source-muted"].isString()) {
|
||||
format_source = config_["format-source-muted"].asString();
|
||||
}
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("source-muted");
|
||||
label_.get_style_context()->remove_class("source-muted");
|
||||
if (config_["format-source-muted"].isString()) {
|
||||
format_source = config_["format-source"].asString();
|
||||
}
|
||||
}
|
||||
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
|
||||
label_->set_markup(fmt::format(
|
||||
auto text = fmt::format(
|
||||
format, fmt::arg("desc", desc_), fmt::arg("volume", volume_),
|
||||
fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_),
|
||||
fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
||||
fmt::arg("source_desc", source_desc_), fmt::arg("icon", getIcon(volume_, getPulseIcon())));
|
||||
if (text.empty()) {
|
||||
label_.hide();
|
||||
} else {
|
||||
label_.set_markup(text);
|
||||
label_.show();
|
||||
}
|
||||
getState(volume_);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
@ -305,16 +312,16 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
button_.set_tooltip_text(fmt::format(
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
tooltip_format, fmt::arg("desc", desc_), fmt::arg("volume", volume_),
|
||||
fmt::arg("format_source", format_source), fmt::arg("source_volume", source_volume_),
|
||||
fmt::arg("source_desc", source_desc_),
|
||||
fmt::arg("icon", getIcon(volume_, getPulseIcon()))));
|
||||
} else {
|
||||
button_.set_tooltip_text(desc_);
|
||||
label_.set_tooltip_text(desc_);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <time.h>
|
||||
|
||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "clock", id, "{:%H:%M}", 60) {
|
||||
: ALabel(config, "clock", id, "{:%H:%M}", 60) {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
auto now = std::chrono::system_clock::now();
|
||||
@ -19,17 +19,17 @@ auto waybar::modules::Clock::update() -> void {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
||||
auto text = fmt::format(format_, localtime);
|
||||
label_->set_markup(text);
|
||||
label_.set_markup(text);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
auto tooltip_format = config_["tooltip-format"].asString();
|
||||
auto tooltip_text = fmt::format(tooltip_format, localtime);
|
||||
button_.set_tooltip_text(tooltip_text);
|
||||
label_.set_tooltip_text(tooltip_text);
|
||||
} else {
|
||||
button_.set_tooltip_text(text);
|
||||
label_.set_tooltip_text(text);
|
||||
}
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ auto Sndio::connect_to_sndio() -> void {
|
||||
}
|
||||
|
||||
Sndio::Sndio(const std::string &id, const Json::Value &config)
|
||||
: AButton(config, "sndio", id, "{volume}%", 1, false, true),
|
||||
: ALabel(config, "sndio", id, "{volume}%", 1, false, true),
|
||||
hdl_(nullptr),
|
||||
pfds_(0),
|
||||
addr_(0),
|
||||
@ -105,14 +105,20 @@ auto Sndio::update() -> void {
|
||||
unsigned int vol = 100. * static_cast<double>(volume_) / static_cast<double>(maxval_);
|
||||
|
||||
if (volume_ == 0) {
|
||||
button_.get_style_context()->add_class("muted");
|
||||
label_.get_style_context()->add_class("muted");
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("muted");
|
||||
label_.get_style_context()->remove_class("muted");
|
||||
}
|
||||
|
||||
label_->set_markup(fmt::format(format, fmt::arg("volume", vol), fmt::arg("raw_value", volume_)));
|
||||
auto text = fmt::format(format, fmt::arg("volume", vol), fmt::arg("raw_value", volume_));
|
||||
if (text.empty()) {
|
||||
label_.hide();
|
||||
} else {
|
||||
label_.set_markup(text);
|
||||
label_.show();
|
||||
}
|
||||
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
auto Sndio::set_desc(struct sioctl_desc *d, unsigned int val) -> void {
|
||||
|
@ -10,9 +10,6 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
watcher_(SNI::Watcher::getInstance()),
|
||||
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
|
||||
std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
|
||||
spdlog::warn(
|
||||
"For a functional tray you must have libappindicator-* installed and export "
|
||||
"XDG_CURRENT_DESKTOP=Unity");
|
||||
box_.set_name("tray");
|
||||
event_box_.add(box_);
|
||||
if (!id.empty()) {
|
||||
|
@ -18,7 +18,7 @@ const std::string Language::XKB_LAYOUT_NAMES_KEY = "xkb_layout_names";
|
||||
const std::string Language::XKB_ACTIVE_LAYOUT_NAME_KEY = "xkb_active_layout_name";
|
||||
|
||||
Language::Language(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "language", id, "{}", 0, true) {
|
||||
: ALabel(config, "language", id, "{}", 0, true) {
|
||||
is_variant_displayed = format_.find("{variant}") != std::string::npos;
|
||||
if (format_.find("{}") != std::string::npos || format_.find("{short}") != std::string::npos) {
|
||||
displayed_short_flag |= static_cast<std::byte>(DispayedShortFlag::ShortName);
|
||||
@ -99,7 +99,7 @@ auto Language::update() -> void {
|
||||
format_, fmt::arg("short", layout_.short_name),
|
||||
fmt::arg("shortDescription", layout_.short_description), fmt::arg("long", layout_.full_name),
|
||||
fmt::arg("variant", layout_.variant), fmt::arg("flag", layout_.country_flag())));
|
||||
label_->set_markup(display_layout);
|
||||
label_.set_markup(display_layout);
|
||||
if (tooltipEnabled()) {
|
||||
if (tooltip_format_ != "") {
|
||||
auto tooltip_display_layout = trim(
|
||||
@ -107,22 +107,22 @@ auto Language::update() -> void {
|
||||
fmt::arg("shortDescription", layout_.short_description),
|
||||
fmt::arg("long", layout_.full_name), fmt::arg("variant", layout_.variant),
|
||||
fmt::arg("flag", layout_.country_flag())));
|
||||
button_.set_tooltip_markup(tooltip_display_layout);
|
||||
label_.set_tooltip_markup(tooltip_display_layout);
|
||||
} else {
|
||||
button_.set_tooltip_markup(display_layout);
|
||||
label_.set_tooltip_markup(display_layout);
|
||||
}
|
||||
}
|
||||
|
||||
event_box_.show();
|
||||
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
auto Language::set_current_layout(std::string current_layout) -> void {
|
||||
button_.get_style_context()->remove_class(layout_.short_name);
|
||||
label_.get_style_context()->remove_class(layout_.short_name);
|
||||
layout_ = layouts_map_[current_layout];
|
||||
button_.get_style_context()->add_class(layout_.short_name);
|
||||
label_.get_style_context()->add_class(layout_.short_name);
|
||||
}
|
||||
|
||||
auto Language::init_layouts_map(const std::vector<std::string>& used_layouts) -> void {
|
||||
|
@ -5,7 +5,7 @@
|
||||
namespace waybar::modules::sway {
|
||||
|
||||
Mode::Mode(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "mode", id, "{}", 0, true) {
|
||||
: ALabel(config, "mode", id, "{}", 0, true) {
|
||||
ipc_.subscribe(R"(["mode"])");
|
||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent));
|
||||
// Launch worker
|
||||
@ -42,14 +42,14 @@ auto Mode::update() -> void {
|
||||
if (mode_.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
label_->set_markup(fmt::format(format_, mode_));
|
||||
label_.set_markup(fmt::format(format_, mode_));
|
||||
if (tooltipEnabled()) {
|
||||
button_.set_tooltip_text(mode_);
|
||||
label_.set_tooltip_text(mode_);
|
||||
}
|
||||
event_box_.show();
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::sway
|
||||
|
@ -130,6 +130,10 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
|
||||
// In a first pass, the maximum "num" value is computed to enqueue
|
||||
// unnumbered workspaces behind numbered ones when computing the sort
|
||||
// attribute.
|
||||
//
|
||||
// Note: if the 'alphabetical_sort' option is true, the user is in
|
||||
// agreement that the "workspace prev/next" commands may not follow
|
||||
// the order displayed in Waybar.
|
||||
int max_num = -1;
|
||||
for (auto &workspace : workspaces_) {
|
||||
max_num = std::max(workspace["num"].asInt(), max_num);
|
||||
@ -143,16 +147,19 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
|
||||
}
|
||||
}
|
||||
std::sort(workspaces_.begin(), workspaces_.end(),
|
||||
[](const Json::Value &lhs, const Json::Value &rhs) {
|
||||
[this](const Json::Value &lhs, const Json::Value &rhs) {
|
||||
auto lname = lhs["name"].asString();
|
||||
auto rname = rhs["name"].asString();
|
||||
int l = lhs["sort"].asInt();
|
||||
int r = rhs["sort"].asInt();
|
||||
|
||||
if (l == r) {
|
||||
if (l == r || config_["alphabetical_sort"].asBool()) {
|
||||
// In case both integers are the same, lexicographical
|
||||
// sort. The code above already ensure that this will only
|
||||
// happend in case of explicitly numbered workspaces.
|
||||
//
|
||||
// Additionally, if the config specifies to sort workspaces
|
||||
// alphabetically do this here.
|
||||
return lname < rname;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#endif
|
||||
|
||||
waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config)
|
||||
: AButton(config, "temperature", id, "{temperatureC}°C", 10) {
|
||||
: ALabel(config, "temperature", id, "{temperatureC}°C", 10) {
|
||||
#if defined(__FreeBSD__)
|
||||
// try to read sysctl?
|
||||
#else
|
||||
@ -42,9 +42,9 @@ auto waybar::modules::Temperature::update() -> void {
|
||||
auto format = format_;
|
||||
if (critical) {
|
||||
format = config_["format-critical"].isString() ? config_["format-critical"].asString() : format;
|
||||
button_.get_style_context()->add_class("critical");
|
||||
label_.get_style_context()->add_class("critical");
|
||||
} else {
|
||||
button_.get_style_context()->remove_class("critical");
|
||||
label_.get_style_context()->remove_class("critical");
|
||||
}
|
||||
|
||||
if (format.empty()) {
|
||||
@ -55,21 +55,21 @@ auto waybar::modules::Temperature::update() -> void {
|
||||
}
|
||||
|
||||
auto max_temp = config_["critical-threshold"].isInt() ? config_["critical-threshold"].asInt() : 0;
|
||||
label_->set_markup(fmt::format(format, fmt::arg("temperatureC", temperature_c),
|
||||
fmt::arg("temperatureF", temperature_f),
|
||||
fmt::arg("temperatureK", temperature_k),
|
||||
fmt::arg("icon", getIcon(temperature_c, "", max_temp))));
|
||||
label_.set_markup(fmt::format(format, fmt::arg("temperatureC", temperature_c),
|
||||
fmt::arg("temperatureF", temperature_f),
|
||||
fmt::arg("temperatureK", temperature_k),
|
||||
fmt::arg("icon", getIcon(temperature_c, "", max_temp))));
|
||||
if (tooltipEnabled()) {
|
||||
std::string tooltip_format = "{temperatureC}°C";
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
button_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("temperatureC", temperature_c),
|
||||
fmt::arg("temperatureF", temperature_f),
|
||||
fmt::arg("temperatureK", temperature_k)));
|
||||
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("temperatureC", temperature_c),
|
||||
fmt::arg("temperatureF", temperature_f),
|
||||
fmt::arg("temperatureK", temperature_k)));
|
||||
}
|
||||
// Call parent update
|
||||
AButton::update();
|
||||
ALabel::update();
|
||||
}
|
||||
|
||||
float waybar::modules::Temperature::getTemperature() {
|
||||
|
@ -252,8 +252,7 @@ const std::string UPower::getDeviceStatus(UpDeviceState& state) {
|
||||
bool UPower::handleToggle(GdkEventButton* const& event) {
|
||||
std::lock_guard<std::mutex> guard(m_Mutex);
|
||||
showAltText = !showAltText;
|
||||
dp.emit();
|
||||
return true;
|
||||
return AModule::handleToggle(event);
|
||||
}
|
||||
|
||||
std::string UPower::timeToString(gint64 time) {
|
||||
|
@ -6,7 +6,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
#include "gdkmm/cursor.h"
|
||||
#include "gdkmm/event.h"
|
||||
#include "gdkmm/types.h"
|
||||
#include "glibmm/fileutils.h"
|
||||
#include "sigc++/functors/mem_fun.h"
|
||||
#include "sigc++/functors/ptr_fun.h"
|
||||
|
||||
#if HAVE_CPU_LINUX
|
||||
#include <sys/sysinfo.h>
|
||||
@ -16,15 +22,34 @@
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
const static int LEFT_MOUSE_BUTTON_CODE = 1;
|
||||
|
||||
namespace waybar::modules {
|
||||
User::User(const std::string& id, const Json::Value& config)
|
||||
: AIconLabel(config, "user", id, "{user} {work_H}:{work_M}", 60, false, false, true) {
|
||||
: AIconLabel(config, "user", id, "{user} {work_H}:{work_M}", 60, false, true, true) {
|
||||
AIconLabel::box_.set_spacing(0);
|
||||
if (AIconLabel::iconEnabled()) {
|
||||
this->init_avatar(AIconLabel::config_);
|
||||
}
|
||||
this->init_update_worker();
|
||||
}
|
||||
|
||||
bool User::handleToggle(GdkEventButton* const& e) {
|
||||
if (AIconLabel::config_["open-on-click"].isBool() &&
|
||||
AIconLabel::config_["open-on-click"].asBool() && e->button == LEFT_MOUSE_BUTTON_CODE) {
|
||||
std::string openPath = this->get_user_home_dir();
|
||||
if (AIconLabel::config_["open-path"].isString()) {
|
||||
std::string customPath = AIconLabel::config_["open-path"].asString();
|
||||
if (!customPath.empty()) {
|
||||
openPath = std::move(customPath);
|
||||
}
|
||||
}
|
||||
|
||||
Gio::AppInfo::launch_default_for_uri("file:///" + openPath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
long User::uptime_as_seconds() {
|
||||
long uptime = 0;
|
||||
|
||||
@ -45,9 +70,9 @@ long User::uptime_as_seconds() {
|
||||
return uptime;
|
||||
}
|
||||
|
||||
std::string User::get_user_login() { return Glib::get_user_name(); }
|
||||
std::string User::get_user_login() const { return Glib::get_user_name(); }
|
||||
|
||||
std::string User::get_user_home_dir() { return Glib::get_home_dir(); }
|
||||
std::string User::get_user_home_dir() const { return Glib::get_home_dir(); }
|
||||
|
||||
void User::init_update_worker() {
|
||||
this->thread_ = [this] {
|
||||
@ -74,7 +99,7 @@ void User::init_avatar(const Json::Value& config) {
|
||||
this->init_default_user_avatar(width, width);
|
||||
}
|
||||
|
||||
std::string User::get_default_user_avatar_path() {
|
||||
std::string User::get_default_user_avatar_path() const {
|
||||
return this->get_user_home_dir() + "/" + ".face";
|
||||
}
|
||||
|
||||
@ -83,8 +108,12 @@ void User::init_default_user_avatar(int width, int height) {
|
||||
}
|
||||
|
||||
void User::init_user_avatar(const std::string& path, int width, int height) {
|
||||
this->pixbuf_ = Gdk::Pixbuf::create_from_file(path, width, height);
|
||||
AIconLabel::image_.set(this->pixbuf_);
|
||||
if (Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
|
||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf_ = Gdk::Pixbuf::create_from_file(path, width, height);
|
||||
AIconLabel::image_.set(pixbuf_);
|
||||
} else {
|
||||
AIconLabel::box_.remove(AIconLabel::image_);
|
||||
}
|
||||
}
|
||||
|
||||
auto User::update() -> void {
|
||||
@ -109,6 +138,6 @@ auto User::update() -> void {
|
||||
fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)),
|
||||
fmt::arg("user", systemUser));
|
||||
ALabel::label_.set_markup(label);
|
||||
ALabel::update();
|
||||
AIconLabel::update();
|
||||
}
|
||||
}; // namespace waybar::modules
|
||||
|
190
src/modules/wireplumber.cpp
Normal file
190
src/modules/wireplumber.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
#include "modules/wireplumber.hpp"
|
||||
|
||||
waybar::modules::Wireplumber::Wireplumber(const std::string& id, const Json::Value& config)
|
||||
: ALabel(config, "wireplumber", id, "{volume}%"),
|
||||
wp_core_(nullptr),
|
||||
apis_(nullptr),
|
||||
om_(nullptr),
|
||||
pending_plugins_(0),
|
||||
muted_(false),
|
||||
volume_(0.0),
|
||||
node_id_(0) {
|
||||
wp_init(WP_INIT_ALL);
|
||||
wp_core_ = wp_core_new(NULL, NULL);
|
||||
apis_ = g_ptr_array_new_with_free_func(g_object_unref);
|
||||
om_ = wp_object_manager_new();
|
||||
|
||||
prepare();
|
||||
|
||||
loadRequiredApiModules();
|
||||
|
||||
if (!wp_core_connect(wp_core_)) {
|
||||
throw std::runtime_error("Could not connect to PipeWire\n");
|
||||
}
|
||||
|
||||
g_signal_connect_swapped(om_, "installed", (GCallback)onObjectManagerInstalled, this);
|
||||
|
||||
activatePlugins();
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
waybar::modules::Wireplumber::~Wireplumber() {
|
||||
g_clear_pointer(&apis_, g_ptr_array_unref);
|
||||
g_clear_object(&om_);
|
||||
g_clear_object(&wp_core_);
|
||||
}
|
||||
|
||||
uint32_t waybar::modules::Wireplumber::getDefaultNodeId(waybar::modules::Wireplumber* self) {
|
||||
uint32_t id;
|
||||
g_autoptr(WpPlugin) def_nodes_api = wp_plugin_find(self->wp_core_, "default-nodes-api");
|
||||
|
||||
if (!def_nodes_api) {
|
||||
throw std::runtime_error("Default nodes API is not loaded\n");
|
||||
}
|
||||
|
||||
g_signal_emit_by_name(def_nodes_api, "get-default-node", "Audio/Sink", &id);
|
||||
|
||||
if (id <= 0 || id >= G_MAXUINT32) {
|
||||
auto err = fmt::format("'{}' is not a valid ID (returned by default-nodes-api)\n", id);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self) {
|
||||
auto proxy = static_cast<WpProxy*>(
|
||||
wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY,
|
||||
"bound-id", "=u", self->node_id_, NULL));
|
||||
|
||||
if (!proxy) {
|
||||
throw std::runtime_error(fmt::format("Object '{}' not found\n", self->node_id_));
|
||||
}
|
||||
|
||||
g_autoptr(WpProperties) properties =
|
||||
WP_IS_PIPEWIRE_OBJECT(proxy) ? wp_pipewire_object_get_properties(WP_PIPEWIRE_OBJECT(proxy))
|
||||
: wp_properties_new_empty();
|
||||
g_autoptr(WpProperties) global_p = wp_global_proxy_get_global_properties(WP_GLOBAL_PROXY(proxy));
|
||||
properties = wp_properties_ensure_unique_owner(properties);
|
||||
wp_properties_add(properties, global_p);
|
||||
wp_properties_set(properties, "object.id", NULL);
|
||||
auto nick = wp_properties_get(properties, "node.nick");
|
||||
auto description = wp_properties_get(properties, "node.description");
|
||||
|
||||
self->node_name_ = nick ? nick : description;
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self) {
|
||||
double vol;
|
||||
GVariant* variant = NULL;
|
||||
g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api");
|
||||
g_signal_emit_by_name(mixer_api, "get-volume", self->node_id_, &variant);
|
||||
if (!variant) {
|
||||
auto err = fmt::format("Node {} does not support volume\n", self->node_id_);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
|
||||
g_variant_lookup(variant, "volume", "d", &vol);
|
||||
g_variant_lookup(variant, "mute", "b", &self->muted_);
|
||||
g_clear_pointer(&variant, g_variant_unref);
|
||||
|
||||
self->volume_ = std::round(vol * 100.0F);
|
||||
self->dp.emit();
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wireplumber* self) {
|
||||
self->node_id_ =
|
||||
self->config_["node-id"].isInt() ? self->config_["node-id"].asInt() : getDefaultNodeId(self);
|
||||
|
||||
g_autoptr(WpPlugin) mixer_api = wp_plugin_find(self->wp_core_, "mixer-api");
|
||||
|
||||
updateVolume(self);
|
||||
updateNodeName(self);
|
||||
g_signal_connect_swapped(mixer_api, "changed", (GCallback)updateVolume, self);
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult* res,
|
||||
waybar::modules::Wireplumber* self) {
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!wp_object_activate_finish(p, res, &error)) {
|
||||
throw std::runtime_error(error->message);
|
||||
}
|
||||
|
||||
if (--self->pending_plugins_ == 0) {
|
||||
wp_core_install_object_manager(self->wp_core_, self->om_);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::activatePlugins() {
|
||||
for (uint16_t i = 0; i < apis_->len; i++) {
|
||||
WpPlugin* plugin = static_cast<WpPlugin*>(g_ptr_array_index(apis_, i));
|
||||
pending_plugins_++;
|
||||
wp_object_activate(WP_OBJECT(plugin), WP_PLUGIN_FEATURE_ENABLED, NULL,
|
||||
(GAsyncReadyCallback)onPluginActivated, this);
|
||||
}
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::prepare() {
|
||||
wp_object_manager_add_interest(om_, WP_TYPE_NODE, NULL);
|
||||
wp_object_manager_add_interest(om_, WP_TYPE_GLOBAL_PROXY, NULL);
|
||||
wp_object_manager_request_object_features(om_, WP_TYPE_GLOBAL_PROXY,
|
||||
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL);
|
||||
}
|
||||
|
||||
void waybar::modules::Wireplumber::loadRequiredApiModules() {
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!wp_core_load_component(wp_core_, "libwireplumber-module-default-nodes-api", "module", NULL,
|
||||
&error)) {
|
||||
throw std::runtime_error(error->message);
|
||||
}
|
||||
|
||||
if (!wp_core_load_component(wp_core_, "libwireplumber-module-mixer-api", "module", NULL,
|
||||
&error)) {
|
||||
throw std::runtime_error(error->message);
|
||||
}
|
||||
|
||||
g_ptr_array_add(apis_, wp_plugin_find(wp_core_, "default-nodes-api"));
|
||||
g_ptr_array_add(apis_, ({
|
||||
WpPlugin* p = wp_plugin_find(wp_core_, "mixer-api");
|
||||
g_object_set(G_OBJECT(p), "scale", 1 /* cubic */, NULL);
|
||||
p;
|
||||
}));
|
||||
}
|
||||
|
||||
auto waybar::modules::Wireplumber::update() -> void {
|
||||
auto format = format_;
|
||||
std::string tooltip_format;
|
||||
|
||||
if (muted_) {
|
||||
format = config_["format-muted"].isString() ? config_["format-muted"].asString() : format;
|
||||
label_.get_style_context()->add_class("muted");
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("muted");
|
||||
}
|
||||
|
||||
std::string markup = fmt::format(format, fmt::arg("node_name", node_name_),
|
||||
fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_)));
|
||||
label_.set_markup(markup);
|
||||
|
||||
getState(volume_);
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
|
||||
if (!tooltip_format.empty()) {
|
||||
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("node_name", node_name_),
|
||||
fmt::arg("volume", volume_),
|
||||
fmt::arg("icon", getIcon(volume_))));
|
||||
} else {
|
||||
label_.set_tooltip_text(node_name_);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
ALabel::update();
|
||||
}
|
@ -6,8 +6,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "client.hpp"
|
||||
#include "gtkmm/widget.h"
|
||||
#include "modules/wlr/workspace_manager_binding.hpp"
|
||||
|
||||
@ -66,10 +68,13 @@ auto WorkspaceManager::workspace_comparator() const
|
||||
auto is_name_less = lhs->get_name() < rhs->get_name();
|
||||
auto is_name_eq = lhs->get_name() == rhs->get_name();
|
||||
auto is_coords_less = lhs->get_coords() < rhs->get_coords();
|
||||
auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name());
|
||||
|
||||
if (sort_by_number_) {
|
||||
return is_number_less;
|
||||
try {
|
||||
auto is_number_less = std::stoi(lhs->get_name()) < std::stoi(rhs->get_name());
|
||||
return is_number_less;
|
||||
} catch (std::invalid_argument) {
|
||||
}
|
||||
}
|
||||
|
||||
if (sort_by_name_) {
|
||||
@ -162,8 +167,20 @@ WorkspaceManager::~WorkspaceManager() {
|
||||
return;
|
||||
}
|
||||
|
||||
zext_workspace_manager_v1_destroy(workspace_manager_);
|
||||
workspace_manager_ = nullptr;
|
||||
wl_display *display = Client::inst()->wl_display;
|
||||
|
||||
// Send `stop` request and wait for one roundtrip. This is not quite correct as
|
||||
// the protocol encourages us to wait for the .finished event, but it should work
|
||||
// with wlroots workspace manager implementation.
|
||||
zext_workspace_manager_v1_stop(workspace_manager_);
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
// If the .finished handler is still not executed, destroy the workspace manager here.
|
||||
if (workspace_manager_) {
|
||||
spdlog::warn("Foreign toplevel manager destroyed before .finished event");
|
||||
zext_workspace_manager_v1_destroy(workspace_manager_);
|
||||
workspace_manager_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void {
|
||||
|
9
subprojects/jsoncpp.wrap
Normal file
9
subprojects/jsoncpp.wrap
Normal file
@ -0,0 +1,9 @@
|
||||
[wrap-file]
|
||||
directory = jsoncpp-1.9.5
|
||||
source_url = https://github.com/open-source-parsers/jsoncpp/archive/1.9.5.tar.gz
|
||||
source_filename = jsoncpp-1.9.5.tar.gz
|
||||
source_hash = f409856e5920c18d0c2fb85276e24ee607d2a09b5e7d5f0a371368903c275da2
|
||||
|
||||
[provide]
|
||||
jsoncpp = jsoncpp_dep
|
||||
|
Reference in New Issue
Block a user