mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
71 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 | |||
3acd31c3e9 | |||
456e06c4b5 | |||
a2751cfcd6 | |||
d9cc995405 | |||
00a2ebf00d | |||
41dea6e46c | |||
a650c7d90c |
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,6 +2,8 @@
|
|||||||
*~
|
*~
|
||||||
vgcore.*
|
vgcore.*
|
||||||
/.vscode
|
/.vscode
|
||||||
|
/.idea
|
||||||
|
/.cache
|
||||||
*.swp
|
*.swp
|
||||||
packagecache
|
packagecache
|
||||||
/subprojects/**/
|
/subprojects/**/
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
FROM alpine:latest
|
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
|
FROM archlinux:base-devel
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm && \
|
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
|
FROM debian:sid
|
||||||
|
|
||||||
RUN apt-get update && \
|
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
|
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(jsoncpp)' 'pkgconfig(libinput)' 'pkgconfig(libmpdclient)' \
|
||||||
'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \
|
'pkgconfig(libnl-3.0)' 'pkgconfig(libnl-genl-3.0)' 'pkgconfig(libpulse)' \
|
||||||
'pkgconfig(libudev)' 'pkgconfig(pugixml)' 'pkgconfig(sigc++-2.0)' 'pkgconfig(spdlog)' \
|
'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
|
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 && \
|
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 \
|
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 \
|
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 addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
|
||||||
zypper -n refresh && \
|
zypper -n refresh && \
|
||||||
zypper -n install -t pattern devel_C_C++ && \
|
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
|
- Network
|
||||||
- Bluetooth
|
- Bluetooth
|
||||||
- Pulseaudio
|
- Pulseaudio
|
||||||
|
- Wireplumber
|
||||||
- Disk
|
- Disk
|
||||||
- Memory
|
- Memory
|
||||||
- Cpu load average
|
- Cpu load average
|
||||||
- Temperature
|
- Temperature
|
||||||
- MPD
|
- MPD
|
||||||
- Custom scripts
|
- Custom scripts
|
||||||
|
- Custom image
|
||||||
- Multiple output configuration
|
- Multiple output configuration
|
||||||
- And many more customizations
|
- And many more customizations
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#ifdef HAVE_HYPRLAND
|
#ifdef HAVE_HYPRLAND
|
||||||
#include "modules/hyprland/backend.hpp"
|
#include "modules/hyprland/backend.hpp"
|
||||||
#include "modules/hyprland/language.hpp"
|
#include "modules/hyprland/language.hpp"
|
||||||
|
#include "modules/hyprland/submap.hpp"
|
||||||
#include "modules/hyprland/window.hpp"
|
#include "modules/hyprland/window.hpp"
|
||||||
#endif
|
#endif
|
||||||
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
#if defined(__FreeBSD__) || (defined(__linux__) && !defined(NO_FILESYSTEM))
|
||||||
@ -41,6 +42,9 @@
|
|||||||
#ifdef HAVE_DBUSMENU
|
#ifdef HAVE_DBUSMENU
|
||||||
#include "modules/sni/tray.hpp"
|
#include "modules/sni/tray.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_MPRIS
|
||||||
|
#include "modules/mpris/mpris.hpp"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBNL
|
#ifdef HAVE_LIBNL
|
||||||
#include "modules/network.hpp"
|
#include "modules/network.hpp"
|
||||||
#endif
|
#endif
|
||||||
@ -77,6 +81,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "modules/custom.hpp"
|
#include "modules/custom.hpp"
|
||||||
|
#include "modules/image.hpp"
|
||||||
#include "modules/temperature.hpp"
|
#include "modules/temperature.hpp"
|
||||||
#include "modules/user.hpp"
|
#include "modules/user.hpp"
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ class Clock : public ALabel {
|
|||||||
|
|
||||||
bool handleScroll(GdkEventScroll* e);
|
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 calendar_text(const waybar_time& wtime) -> std::string;
|
||||||
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
|
||||||
auto first_day_of_week() -> date::weekday;
|
auto first_day_of_week() -> date::weekday;
|
||||||
|
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
|
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
|
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
|
@ -23,7 +23,7 @@ Addressed by *hyprland/language*
|
|||||||
|
|
||||||
*keyboard-name*: ++
|
*keyboard-name*: ++
|
||||||
typeof: string ++
|
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": {
|
"hyprland/language": {
|
||||||
"format": "Lang: {}"
|
"format": "Lang: {}"
|
||||||
"format-us": "AMERICA, HELL YEAH!" // For American English
|
"format-en": "AMERICA, HELL YEAH!"
|
||||||
"format-tr": "As bayrakları" // For Turkish
|
"format-tr": "As bayrakları"
|
||||||
"keyboard-name": "AT Translated Set 2 keyboard"
|
"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"
|
||||||
|
}
|
||||||
|
```
|
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}*
|
@ -73,6 +73,10 @@ Addressed by *sway/workspaces*
|
|||||||
typeof: bool ++
|
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.
|
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
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{value}*: Name of the workspace, as defined by sway.
|
*{value}*: Name of the workspace, as defined by sway.
|
||||||
|
@ -47,6 +47,10 @@ compatible devices in the tooltip.
|
|||||||
default: 4 ++
|
default: 4 ++
|
||||||
Defines the spacing between the tooltip window edge and the tooltip content.
|
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
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{percentage}*: The battery capacity in percentage
|
*{percentage}*: The battery capacity in percentage
|
||||||
|
@ -266,6 +266,7 @@ A module group is defined by specifying a module named "group/some-group-name".
|
|||||||
- *waybar-keyboard-state(5)*
|
- *waybar-keyboard-state(5)*
|
||||||
- *waybar-memory(5)*
|
- *waybar-memory(5)*
|
||||||
- *waybar-mpd(5)*
|
- *waybar-mpd(5)*
|
||||||
|
- *waybar-mpris(5)*
|
||||||
- *waybar-network(5)*
|
- *waybar-network(5)*
|
||||||
- *waybar-pulseaudio(5)*
|
- *waybar-pulseaudio(5)*
|
||||||
- *waybar-river-mode(5)*
|
- *waybar-river-mode(5)*
|
||||||
|
17
meson.build
17
meson.build
@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'waybar', 'cpp', 'c',
|
'waybar', 'cpp', 'c',
|
||||||
version: '0.9.16',
|
version: '0.9.17',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>= 0.49.0',
|
meson_version: '>= 0.49.0',
|
||||||
default_options : [
|
default_options : [
|
||||||
@ -86,7 +86,10 @@ wayland_cursor = dependency('wayland-cursor')
|
|||||||
wayland_protos = dependency('wayland-protocols')
|
wayland_protos = dependency('wayland-protocols')
|
||||||
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
|
||||||
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
|
||||||
giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabled() or get_option('logind').enabled() or get_option('upower_glib').enabled()))
|
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'])
|
jsoncpp = dependency('jsoncpp', version : ['>=1.9.2'], fallback : ['jsoncpp', 'jsoncpp_dep'])
|
||||||
sigcpp = dependency('sigc++-2.0')
|
sigcpp = dependency('sigc++-2.0')
|
||||||
libinotify = dependency('libinotify', required: false)
|
libinotify = dependency('libinotify', required: false)
|
||||||
@ -95,6 +98,7 @@ libinput = dependency('libinput', required: get_option('libinput'))
|
|||||||
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
||||||
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
||||||
upower_glib = dependency('upower-glib', required: get_option('upower_glib'))
|
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'))
|
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
|
||||||
libudev = dependency('libudev', required: get_option('libudev'))
|
libudev = dependency('libudev', required: get_option('libudev'))
|
||||||
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
libevdev = dependency('libevdev', required: get_option('libevdev'))
|
||||||
@ -151,6 +155,7 @@ src_files = files(
|
|||||||
'src/modules/custom.cpp',
|
'src/modules/custom.cpp',
|
||||||
'src/modules/disk.cpp',
|
'src/modules/disk.cpp',
|
||||||
'src/modules/idle_inhibitor.cpp',
|
'src/modules/idle_inhibitor.cpp',
|
||||||
|
'src/modules/image.cpp',
|
||||||
'src/modules/temperature.cpp',
|
'src/modules/temperature.cpp',
|
||||||
'src/modules/user.cpp',
|
'src/modules/user.cpp',
|
||||||
'src/main.cpp',
|
'src/main.cpp',
|
||||||
@ -219,6 +224,7 @@ if true
|
|||||||
src_files += 'src/modules/hyprland/backend.cpp'
|
src_files += 'src/modules/hyprland/backend.cpp'
|
||||||
src_files += 'src/modules/hyprland/window.cpp'
|
src_files += 'src/modules/hyprland/window.cpp'
|
||||||
src_files += 'src/modules/hyprland/language.cpp'
|
src_files += 'src/modules/hyprland/language.cpp'
|
||||||
|
src_files += 'src/modules/hyprland/submap.cpp'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if libnl.found() and libnlgen.found()
|
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'
|
src_files += 'src/modules/upower/upower_tooltip.cpp'
|
||||||
endif
|
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()
|
if libpulse.found()
|
||||||
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
add_project_arguments('-DHAVE_LIBPULSE', language: 'cpp')
|
||||||
src_files += 'src/modules/pulseaudio.cpp'
|
src_files += 'src/modules/pulseaudio.cpp'
|
||||||
@ -333,6 +344,7 @@ executable(
|
|||||||
libnl,
|
libnl,
|
||||||
libnlgen,
|
libnlgen,
|
||||||
upower_glib,
|
upower_glib,
|
||||||
|
playerctl,
|
||||||
libpulse,
|
libpulse,
|
||||||
libjack,
|
libjack,
|
||||||
libwireplumber,
|
libwireplumber,
|
||||||
@ -386,6 +398,7 @@ if scdoc.found()
|
|||||||
'waybar-keyboard-state.5.scd',
|
'waybar-keyboard-state.5.scd',
|
||||||
'waybar-memory.5.scd',
|
'waybar-memory.5.scd',
|
||||||
'waybar-mpd.5.scd',
|
'waybar-mpd.5.scd',
|
||||||
|
'waybar-mpris.5.scd',
|
||||||
'waybar-network.5.scd',
|
'waybar-network.5.scd',
|
||||||
'waybar-pulseaudio.5.scd',
|
'waybar-pulseaudio.5.scd',
|
||||||
'waybar-river-mode.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('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features')
|
||||||
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
|
||||||
option('upower_glib', type: 'feature', value: 'auto', description: 'Enable support for upower')
|
option('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('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
|
||||||
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
|
||||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
||||||
|
@ -22,6 +22,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
return new waybar::modules::upower::UPower(id, config_[name]);
|
return new waybar::modules::upower::UPower(id, config_[name]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_MPRIS
|
||||||
|
if (ref == "mpris") {
|
||||||
|
return new waybar::modules::mpris::Mpris(id, config_[name]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SWAY
|
#ifdef HAVE_SWAY
|
||||||
if (ref == "sway/mode") {
|
if (ref == "sway/mode") {
|
||||||
return new waybar::modules::sway::Mode(id, config_[name]);
|
return new waybar::modules::sway::Mode(id, config_[name]);
|
||||||
@ -67,6 +72,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
if (ref == "hyprland/language") {
|
if (ref == "hyprland/language") {
|
||||||
return new waybar::modules::hyprland::Language(id, bar_, config_[name]);
|
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
|
#endif
|
||||||
if (ref == "idle_inhibitor") {
|
if (ref == "idle_inhibitor") {
|
||||||
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
return new waybar::modules::IdleInhibitor(id, bar_, config_[name]);
|
||||||
@ -148,6 +156,8 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
|
|||||||
}
|
}
|
||||||
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
|
||||||
return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
|
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) {
|
} catch (const std::exception& e) {
|
||||||
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
auto err = fmt::format("Disabling module \"{}\", {}", name, e.what());
|
||||||
|
@ -107,6 +107,15 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
std::ifstream(node.path() / "type") >> type;
|
std::ifstream(node.path() / "type") >> type;
|
||||||
|
|
||||||
if (!type.compare("Battery")) {
|
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;
|
check_map[node.path()] = true;
|
||||||
auto search = batteries_.find(node.path());
|
auto search = batteries_.find(node.path());
|
||||||
if (search == batteries_.end()) {
|
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;
|
bool total_energy_full_design_exists = false;
|
||||||
uint32_t total_capacity = 0;
|
uint32_t total_capacity = 0;
|
||||||
bool total_capacity_exists = false;
|
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";
|
std::string status = "Unknown";
|
||||||
for (auto const& item : batteries_) {
|
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;
|
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;
|
uint32_t voltage_now = 0;
|
||||||
bool voltage_now_exists = false;
|
bool voltage_now_exists = false;
|
||||||
if (fs::exists(bat / "voltage_now")) {
|
if (fs::exists(bat / "voltage_now")) {
|
||||||
@ -481,8 +504,16 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
|
|||||||
}
|
}
|
||||||
|
|
||||||
float time_remaining{0.0f};
|
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;
|
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 &&
|
} else if (status == "Charging" && total_energy_exists && total_energy_full_exists &&
|
||||||
total_power_exists) {
|
total_power_exists) {
|
||||||
if (total_power != 0)
|
if (total_power != 0)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@ -65,6 +66,11 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
|||||||
if (config_["on-scroll"][kCalendarPlaceholder].isInt()) {
|
if (config_["on-scroll"][kCalendarPlaceholder].isInt()) {
|
||||||
calendar_shift_init_ =
|
calendar_shift_init_ =
|
||||||
date::months{config_["on-scroll"].get(kCalendarPlaceholder, 0).asInt()};
|
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("");
|
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] {
|
thread_ = [this] {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
auto now = std::chrono::system_clock::now();
|
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 waybar::modules::Clock::update() -> void {
|
||||||
auto time_zone = current_timezone();
|
auto time_zone = current_timezone();
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
waybar_time wtime = {locale_, date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now) +
|
waybar_time wtime = {locale_,
|
||||||
calendar_shift_)};
|
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 = "";
|
std::string text = "";
|
||||||
if (!is_timezone_fixed()) {
|
if (!is_timezone_fixed()) {
|
||||||
// As date dep is not fully compatible, prefer fmt
|
// As date dep is not fully compatible, prefer fmt
|
||||||
@ -113,12 +141,16 @@ auto waybar::modules::Clock::update() -> void {
|
|||||||
if (config_["tooltip-format"].isString()) {
|
if (config_["tooltip-format"].isString()) {
|
||||||
std::string calendar_lines{""};
|
std::string calendar_lines{""};
|
||||||
std::string timezoned_time_lines{""};
|
std::string timezoned_time_lines{""};
|
||||||
if (is_calendar_in_tooltip_) calendar_lines = calendar_text(wtime);
|
if (is_calendar_in_tooltip_) {
|
||||||
if (is_timezoned_list_in_tooltip_) timezoned_time_lines = timezones_text(&now);
|
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();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
text =
|
text = fmt::format(tooltip_format, shifted_wtime,
|
||||||
fmt::format(tooltip_format, wtime, fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
||||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||||
label_.set_tooltip_markup(text);
|
label_.set_tooltip_markup(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,7 +168,7 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
|
|||||||
auto dir = AModule::getScrollDir(e);
|
auto dir = AModule::getScrollDir(e);
|
||||||
|
|
||||||
// Shift calendar date
|
// Shift calendar date
|
||||||
if (calendar_shift_init_.count() > 0) {
|
if (calendar_shift_init_.count() != 0) {
|
||||||
if (dir == SCROLL_DIR::UP)
|
if (dir == SCROLL_DIR::UP)
|
||||||
calendar_shift_ += calendar_shift_init_;
|
calendar_shift_ += calendar_shift_init_;
|
||||||
else
|
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 daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
|
||||||
const auto ymd{date::year_month_day{daypoint}};
|
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}
|
? date::day{0}
|
||||||
: ymd.day()};
|
: ymd.day()};
|
||||||
const date::year_month ym{ymd.year(), ymd.month()};
|
const date::year_month ym{ymd.year(), ymd.month()};
|
||||||
const auto week_format{config_["format-calendar-weekdays"].isString()
|
const auto first_dow = first_day_of_week();
|
||||||
? config_["format-calendar-weekdays"].asString()
|
|
||||||
: ""};
|
|
||||||
const auto wn_format{config_["format-calendar-weeks"].isString()
|
|
||||||
? config_["format-calendar-weeks"].asString()
|
|
||||||
: ""};
|
|
||||||
|
|
||||||
std::stringstream os;
|
std::stringstream os;
|
||||||
|
|
||||||
const auto first_dow = first_day_of_week();
|
enum class WeeksSide {
|
||||||
int ws{0}; // weeks-pos: side(1 - left, 2 - right)
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
HIDDEN,
|
||||||
|
};
|
||||||
|
WeeksSide weeks_pos = WeeksSide::HIDDEN;
|
||||||
|
|
||||||
if (config_["calendar-weeks-pos"].isString()) {
|
if (config_["calendar-weeks-pos"].isString()) {
|
||||||
if (config_["calendar-weeks-pos"].asString() == "left") {
|
if (config_["calendar-weeks-pos"].asString() == "left") {
|
||||||
ws = 1;
|
weeks_pos = WeeksSide::LEFT;
|
||||||
// Add paddings before the header
|
// 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") {
|
} else if (config_["calendar-weeks-pos"].asString() == "right") {
|
||||||
ws = 2;
|
weeks_pos = WeeksSide::RIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
weekdays_header(first_dow, os);
|
weekdays_header(first_dow, os);
|
||||||
|
|
||||||
// First week prefixed with spaces if needed.
|
// First week day prefixed with spaces if needed.
|
||||||
auto wd = date::weekday(ym / 1);
|
date::sys_days print_wd{ym / 1};
|
||||||
|
auto wd{date::weekday{print_wd}};
|
||||||
auto empty_days = (wd - first_dow).count();
|
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*/
|
/* Print weeknumber on the left for the first row*/
|
||||||
if (ws == 1) {
|
if (weeks_pos == WeeksSide::LEFT) {
|
||||||
os << fmt::format(wn_format, lwd);
|
os << fmt::format(fmt_str_weeks_, print_wd) << ' ';
|
||||||
os << ' ';
|
|
||||||
lwd += date::weeks{1};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty_days > 0) {
|
if (empty_days > 0) {
|
||||||
os << std::string(empty_days * 3 - 1, ' ');
|
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) {
|
if (wd != first_dow) {
|
||||||
os << ' ';
|
os << ' ';
|
||||||
} else if (unsigned(d) != 1) {
|
} else if (unsigned(d) != 1) {
|
||||||
if (ws == 2) {
|
if (weeks_pos == WeeksSide::RIGHT) {
|
||||||
os << ' ';
|
os << ' ' << fmt::format(fmt_str_weeks_, print_wd);
|
||||||
os << fmt::format(wn_format, lwd);
|
|
||||||
lwd += date::weeks{1};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os << '\n';
|
os << '\n';
|
||||||
|
|
||||||
if (ws == 1) {
|
print_wd = (ym / d);
|
||||||
os << fmt::format(wn_format, lwd);
|
|
||||||
os << ' ';
|
if (weeks_pos == WeeksSide::LEFT) {
|
||||||
lwd += date::weeks{1};
|
os << fmt::format(fmt_str_weeks_, print_wd) << ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d == curr_day) {
|
if (d == curr_day) {
|
||||||
if (config_["today-format"].isString()) {
|
if (config_["today-format"].isString()) {
|
||||||
auto today_format = config_["today-format"].asString();
|
auto today_format = config_["today-format"].asString();
|
||||||
@ -242,17 +271,17 @@ auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::str
|
|||||||
} else {
|
} else {
|
||||||
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
os << "<b><u>" << date::format("%e", d) << "</u></b>";
|
||||||
}
|
}
|
||||||
} else if (config_["format-calendar"].isString()) {
|
} else {
|
||||||
os << fmt::format(config_["format-calendar"].asString(), date::format("%e", d));
|
os << fmt::format(fmt_str_calendar_, date::format("%e", d));
|
||||||
} else
|
}
|
||||||
os << date::format("%e", d);
|
|
||||||
/*Print weeks on the right when the endings with spaces*/
|
/*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());
|
empty_days = 6 - (wd.c_encoding() - first_dow.c_encoding());
|
||||||
if (empty_days > 0) {
|
if (empty_days > 0 && empty_days < 7) {
|
||||||
os << std::string(empty_days * 3 + 1, ' ');
|
os << std::string(empty_days * 3, ' ');
|
||||||
os << fmt::format(wn_format, lwd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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 {
|
-> void {
|
||||||
std::stringstream res;
|
std::stringstream res;
|
||||||
auto wd = first_dow;
|
auto wd = first_week_day;
|
||||||
do {
|
do {
|
||||||
if (wd != first_dow) res << ' ';
|
if (wd != first_week_day) {
|
||||||
|
res << ' ';
|
||||||
|
}
|
||||||
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
|
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
|
||||||
auto clen = ustring_clen(wd_ustring);
|
auto clen = ustring_clen(wd_ustring);
|
||||||
auto wd_len = wd_ustring.length();
|
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, ' ');
|
const std::string pad(2 - clen, ' ');
|
||||||
res << pad << wd_ustring;
|
res << pad << wd_ustring;
|
||||||
} while (++wd != first_dow);
|
} while (++wd != first_week_day);
|
||||||
res << "\n";
|
res << '\n';
|
||||||
|
|
||||||
if (config_["format-calendar-weekdays"].isString()) {
|
if (config_["format-calendar-weekdays"].isString()) {
|
||||||
os << fmt::format(config_["format-calendar-weekdays"].asString(), res.str());
|
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();
|
timezone = date::current_zone();
|
||||||
}
|
}
|
||||||
wtime = {locale_, date::make_zoned(timezone, date::floor<std::chrono::seconds>(*now))};
|
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();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@ auto waybar::modules::Cpu::update() -> void {
|
|||||||
auto icons = std::vector<std::string>{state};
|
auto icons = std::vector<std::string>{state};
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
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("load", cpu_load));
|
|
||||||
store.push_back(fmt::arg("usage", total_usage));
|
store.push_back(fmt::arg("usage", total_usage));
|
||||||
store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
|
store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
|
||||||
store.push_back(fmt::arg("max_frequency", max_frequency));
|
store.push_back(fmt::arg("max_frequency", max_frequency));
|
||||||
|
@ -49,18 +49,17 @@ auto Language::update() -> void {
|
|||||||
|
|
||||||
void Language::onEvent(const std::string& ev) {
|
void Language::onEvent(const std::string& ev) {
|
||||||
std::lock_guard<std::mutex> lg(mutex_);
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
auto layoutName = ev.substr(ev.find_last_of(',') + 1);
|
auto kbName = ev.substr(ev.find_last_of('>') + 1, ev.find_first_of(','));
|
||||||
auto keebName = ev.substr(0, ev.find_last_of(','));
|
auto layoutName = ev.substr(ev.find_first_of(',') + 1);
|
||||||
keebName = keebName.substr(keebName.find_first_of('>') + 2);
|
|
||||||
|
|
||||||
if (config_.isMember("keyboard-name") && keebName != config_["keyboard-name"].asString())
|
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
|
||||||
return; // ignore
|
return; // ignore
|
||||||
|
|
||||||
const auto BRIEFNAME = getShortFrom(layoutName);
|
const auto briefName = getShortFrom(layoutName);
|
||||||
|
|
||||||
if (config_.isMember("format-" + BRIEFNAME)) {
|
if (config_.isMember("format-" + briefName)) {
|
||||||
const auto PROPNAME = "format-" + BRIEFNAME;
|
const auto propName = "format-" + briefName;
|
||||||
layoutName = fmt::format(format_, config_[PROPNAME].asString());
|
layoutName = fmt::format(format_, config_[propName].asString());
|
||||||
} else {
|
} else {
|
||||||
layoutName = fmt::format(format_, layoutName);
|
layoutName = fmt::format(format_, layoutName);
|
||||||
}
|
}
|
||||||
@ -77,19 +76,30 @@ void Language::onEvent(const std::string& ev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Language::initLanguage() {
|
void Language::initLanguage() {
|
||||||
const auto INPUTDEVICES = gIPC->getSocket1Reply("devices");
|
const auto inputDevices = gIPC->getSocket1Reply("devices");
|
||||||
|
|
||||||
if (!config_.isMember("keyboard-name")) return;
|
const auto kbName = config_["keyboard-name"].asString();
|
||||||
|
|
||||||
const auto KEEBNAME = config_["keyboard-name"].asString();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto searcher = INPUTDEVICES.substr(INPUTDEVICES.find(KEEBNAME) + KEEBNAME.length());
|
auto searcher = kbName.empty()
|
||||||
searcher = searcher.substr(searcher.find("Keyboard at"));
|
? inputDevices
|
||||||
searcher = searcher.substr(searcher.find("keymap:") + 7);
|
: inputDevices.substr(inputDevices.find(kbName) + kbName.length());
|
||||||
|
searcher = searcher.substr(searcher.find("keymap:") + 8);
|
||||||
searcher = searcher.substr(0, searcher.find_first_of("\n\t"));
|
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_);
|
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
|
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();
|
||||||
|
}
|
@ -96,7 +96,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
auto format = config_["format-disconnected"].isString()
|
auto format = config_["format-disconnected"].isString()
|
||||||
? config_["format-disconnected"].asString()
|
? config_["format-disconnected"].asString()
|
||||||
: "disconnected";
|
: "disconnected";
|
||||||
label_.set_markup(format);
|
if (format.empty()) {
|
||||||
|
label_.set_markup(format);
|
||||||
|
label_.show();
|
||||||
|
} else {
|
||||||
|
label_.hide();
|
||||||
|
}
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
std::string tooltip_format;
|
std::string tooltip_format;
|
||||||
@ -107,9 +112,8 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
label_.set_tooltip_text(tooltip_format);
|
label_.set_tooltip_text(tooltip_format);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
label_.get_style_context()->remove_class("disconnected");
|
|
||||||
}
|
}
|
||||||
|
label_.get_style_context()->remove_class("disconnected");
|
||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
Glib::ustring artist, album_artist, album, title;
|
Glib::ustring artist, album_artist, album, title;
|
||||||
@ -169,7 +173,7 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt());
|
if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
label_.set_markup(fmt::format(
|
auto text = fmt::format(
|
||||||
format, fmt::arg("artist", artist.raw()), fmt::arg("albumArtist", album_artist.raw()),
|
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("album", album.raw()), fmt::arg("title", title.raw()), fmt::arg("date", date),
|
||||||
fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime),
|
fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime),
|
||||||
@ -177,7 +181,13 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon),
|
||||||
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon),
|
||||||
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon),
|
fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon),
|
||||||
fmt::arg("filename", filename)));
|
fmt::arg("filename", filename));
|
||||||
|
if (text.empty()) {
|
||||||
|
label_.hide();
|
||||||
|
} else {
|
||||||
|
label_.show();
|
||||||
|
label_.set_markup(text);
|
||||||
|
}
|
||||||
} catch (fmt::format_error const& e) {
|
} catch (fmt::format_error const& e) {
|
||||||
spdlog::warn("mpd: format error: {}", e.what());
|
spdlog::warn("mpd: format error: {}", 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
|
@ -295,10 +295,16 @@ auto waybar::modules::Pulseaudio::update() -> void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
|
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_),
|
format, fmt::arg("desc", desc_), fmt::arg("volume", volume_),
|
||||||
fmt::arg("format_source", format_source), fmt::arg("source_volume", source_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_);
|
getState(volume_);
|
||||||
|
|
||||||
if (tooltipEnabled()) {
|
if (tooltipEnabled()) {
|
||||||
|
@ -110,7 +110,13 @@ auto Sndio::update() -> void {
|
|||||||
label_.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();
|
||||||
|
}
|
||||||
|
|
||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,6 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||||||
watcher_(SNI::Watcher::getInstance()),
|
watcher_(SNI::Watcher::getInstance()),
|
||||||
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
|
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
|
||||||
std::bind(&Tray::onRemove, 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");
|
box_.set_name("tray");
|
||||||
event_box_.add(box_);
|
event_box_.add(box_);
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
|
@ -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
|
// In a first pass, the maximum "num" value is computed to enqueue
|
||||||
// unnumbered workspaces behind numbered ones when computing the sort
|
// unnumbered workspaces behind numbered ones when computing the sort
|
||||||
// attribute.
|
// 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;
|
int max_num = -1;
|
||||||
for (auto &workspace : workspaces_) {
|
for (auto &workspace : workspaces_) {
|
||||||
max_num = std::max(workspace["num"].asInt(), max_num);
|
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(),
|
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 lname = lhs["name"].asString();
|
||||||
auto rname = rhs["name"].asString();
|
auto rname = rhs["name"].asString();
|
||||||
int l = lhs["sort"].asInt();
|
int l = lhs["sort"].asInt();
|
||||||
int r = rhs["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
|
// In case both integers are the same, lexicographical
|
||||||
// sort. The code above already ensure that this will only
|
// sort. The code above already ensure that this will only
|
||||||
// happend in case of explicitly numbered workspaces.
|
// happend in case of explicitly numbered workspaces.
|
||||||
|
//
|
||||||
|
// Additionally, if the config specifies to sort workspaces
|
||||||
|
// alphabetically do this here.
|
||||||
return lname < rname;
|
return lname < rname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,8 +252,7 @@ const std::string UPower::getDeviceStatus(UpDeviceState& state) {
|
|||||||
bool UPower::handleToggle(GdkEventButton* const& event) {
|
bool UPower::handleToggle(GdkEventButton* const& event) {
|
||||||
std::lock_guard<std::mutex> guard(m_Mutex);
|
std::lock_guard<std::mutex> guard(m_Mutex);
|
||||||
showAltText = !showAltText;
|
showAltText = !showAltText;
|
||||||
dp.emit();
|
return AModule::handleToggle(event);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UPower::timeToString(gint64 time) {
|
std::string UPower::timeToString(gint64 time) {
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "gdkmm/cursor.h"
|
#include "gdkmm/cursor.h"
|
||||||
#include "gdkmm/event.h"
|
#include "gdkmm/event.h"
|
||||||
#include "gdkmm/types.h"
|
#include "gdkmm/types.h"
|
||||||
|
#include "glibmm/fileutils.h"
|
||||||
#include "sigc++/functors/mem_fun.h"
|
#include "sigc++/functors/mem_fun.h"
|
||||||
#include "sigc++/functors/ptr_fun.h"
|
#include "sigc++/functors/ptr_fun.h"
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ const static int LEFT_MOUSE_BUTTON_CODE = 1;
|
|||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
User::User(const std::string& id, const Json::Value& config)
|
User::User(const std::string& id, const Json::Value& config)
|
||||||
: AIconLabel(config, "user", id, "{user} {work_H}:{work_M}", 60, false, true, true) {
|
: AIconLabel(config, "user", id, "{user} {work_H}:{work_M}", 60, false, true, true) {
|
||||||
|
AIconLabel::box_.set_spacing(0);
|
||||||
if (AIconLabel::iconEnabled()) {
|
if (AIconLabel::iconEnabled()) {
|
||||||
this->init_avatar(AIconLabel::config_);
|
this->init_avatar(AIconLabel::config_);
|
||||||
}
|
}
|
||||||
@ -106,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) {
|
void User::init_user_avatar(const std::string& path, int width, int height) {
|
||||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf_ = Gdk::Pixbuf::create_from_file(path, width, height);
|
if (Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
|
||||||
AIconLabel::image_.set(pixbuf_);
|
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 {
|
auto User::update() -> void {
|
||||||
@ -132,6 +138,6 @@ auto User::update() -> void {
|
|||||||
fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)),
|
fmt::arg("work_S", fmt::format("{:%S}", workSystemTimeSeconds)),
|
||||||
fmt::arg("user", systemUser));
|
fmt::arg("user", systemUser));
|
||||||
ALabel::label_.set_markup(label);
|
ALabel::label_.set_markup(label);
|
||||||
ALabel::update();
|
AIconLabel::update();
|
||||||
}
|
}
|
||||||
}; // namespace waybar::modules
|
}; // namespace waybar::modules
|
||||||
|
@ -54,17 +54,25 @@ uint32_t waybar::modules::Wireplumber::getDefaultNodeId(waybar::modules::Wireplu
|
|||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self) {
|
void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber* self) {
|
||||||
auto proxy = static_cast<WpPipewireObject*>(wp_object_manager_lookup(
|
auto proxy = static_cast<WpProxy*>(
|
||||||
self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_PW_GLOBAL_PROPERTY, "object.id", "=u",
|
wp_object_manager_lookup(self->om_, WP_TYPE_GLOBAL_PROXY, WP_CONSTRAINT_TYPE_G_PROPERTY,
|
||||||
self->node_id_, NULL));
|
"bound-id", "=u", self->node_id_, NULL));
|
||||||
|
|
||||||
if (!proxy) {
|
if (!proxy) {
|
||||||
throw std::runtime_error(fmt::format("Object '{}' not found\n", self->node_id_));
|
throw std::runtime_error(fmt::format("Object '{}' not found\n", self->node_id_));
|
||||||
}
|
}
|
||||||
|
|
||||||
g_autoptr(WpProperties) properties = wp_pipewire_object_get_properties(proxy);
|
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);
|
properties = wp_properties_ensure_unique_owner(properties);
|
||||||
self->node_name_ = wp_properties_get(properties, "node.nick");
|
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) {
|
void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* self) {
|
||||||
@ -120,6 +128,7 @@ void waybar::modules::Wireplumber::activatePlugins() {
|
|||||||
|
|
||||||
void waybar::modules::Wireplumber::prepare() {
|
void waybar::modules::Wireplumber::prepare() {
|
||||||
wp_object_manager_add_interest(om_, WP_TYPE_NODE, NULL);
|
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_object_manager_request_object_features(om_, WP_TYPE_GLOBAL_PROXY,
|
||||||
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL);
|
WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL);
|
||||||
}
|
}
|
||||||
@ -156,8 +165,8 @@ auto waybar::modules::Wireplumber::update() -> void {
|
|||||||
label_.get_style_context()->remove_class("muted");
|
label_.get_style_context()->remove_class("muted");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string markup =
|
std::string markup = fmt::format(format, fmt::arg("node_name", node_name_),
|
||||||
fmt::format(format, fmt::arg("node_name", node_name_), fmt::arg("volume", volume_));
|
fmt::arg("volume", volume_), fmt::arg("icon", getIcon(volume_)));
|
||||||
label_.set_markup(markup);
|
label_.set_markup(markup);
|
||||||
|
|
||||||
getState(volume_);
|
getState(volume_);
|
||||||
@ -169,7 +178,8 @@ auto waybar::modules::Wireplumber::update() -> void {
|
|||||||
|
|
||||||
if (!tooltip_format.empty()) {
|
if (!tooltip_format.empty()) {
|
||||||
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("node_name", node_name_),
|
label_.set_tooltip_text(fmt::format(tooltip_format, fmt::arg("node_name", node_name_),
|
||||||
fmt::arg("volume", volume_)));
|
fmt::arg("volume", volume_),
|
||||||
|
fmt::arg("icon", getIcon(volume_))));
|
||||||
} else {
|
} else {
|
||||||
label_.set_tooltip_text(node_name_);
|
label_.set_tooltip_text(node_name_);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "client.hpp"
|
||||||
#include "gtkmm/widget.h"
|
#include "gtkmm/widget.h"
|
||||||
#include "modules/wlr/workspace_manager_binding.hpp"
|
#include "modules/wlr/workspace_manager_binding.hpp"
|
||||||
|
|
||||||
@ -166,8 +167,20 @@ WorkspaceManager::~WorkspaceManager() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
zext_workspace_manager_v1_destroy(workspace_manager_);
|
wl_display *display = Client::inst()->wl_display;
|
||||||
workspace_manager_ = nullptr;
|
|
||||||
|
// 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 {
|
auto WorkspaceManager::remove_workspace_group(uint32_t id) -> void {
|
||||||
|
Reference in New Issue
Block a user