mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
Compare commits
144 Commits
Author | SHA1 | Date | |
---|---|---|---|
a194755b4f | |||
165814a57f | |||
![]() |
b665843085 | ||
![]() |
ee4fbc58f7 | ||
![]() |
3081b0c576 | ||
![]() |
b7a527b122 | ||
![]() |
54a6668846 | ||
![]() |
ed0f7453c9 | ||
![]() |
19fe929d1f | ||
![]() |
0a28b50a8c | ||
![]() |
70bc318a01 | ||
![]() |
22817089db | ||
![]() |
aecd80702e | ||
![]() |
abd7a0cf25 | ||
![]() |
e30fba0b8f | ||
![]() |
b5ea14c896 | ||
![]() |
5b33a5917c | ||
![]() |
66b71cc857 | ||
![]() |
c8237437d2 | ||
![]() |
57544fe694 | ||
![]() |
e90c66a102 | ||
![]() |
db1d859881 | ||
![]() |
cc961d40b3 | ||
![]() |
29cebaa0a7 | ||
![]() |
2c7bbe9852 | ||
![]() |
eb74623b7d | ||
![]() |
b9260575e5 | ||
![]() |
8cf676176d | ||
![]() |
3db5673e70 | ||
![]() |
46f5034030 | ||
![]() |
6f7e7c5199 | ||
![]() |
2ba11e8401 | ||
![]() |
b084bf721e | ||
![]() |
e40860c3e9 | ||
![]() |
2bcd0eb09f | ||
![]() |
94c34a29c4 | ||
![]() |
7fb37c0069 | ||
![]() |
ce2ede70f2 | ||
![]() |
b91adc9f29 | ||
![]() |
86b3e456e1 | ||
![]() |
1eb4684b60 | ||
![]() |
a3904ff039 | ||
![]() |
88828265c0 | ||
![]() |
600653538b | ||
![]() |
28635c1f6d | ||
![]() |
b09f6cc731 | ||
![]() |
c8910901e5 | ||
![]() |
6b7fd36863 | ||
![]() |
20b091dcd8 | ||
![]() |
106535e3eb | ||
![]() |
d3520536c4 | ||
![]() |
05e5a7e5fd | ||
![]() |
2b07dea3a6 | ||
![]() |
629686f760 | ||
![]() |
c71c0fca6e | ||
![]() |
9207fff627 | ||
![]() |
dae7794bdf | ||
![]() |
c087d8c318 | ||
![]() |
6dfa74bd5d | ||
![]() |
e9b29613b2 | ||
![]() |
d01ce7d812 | ||
![]() |
8ce1d15885 | ||
![]() |
4d9e0ea802 | ||
![]() |
2f04a49129 | ||
![]() |
6dc33fe88f | ||
![]() |
effad1a5c3 | ||
![]() |
05efdb74f0 | ||
![]() |
d3bcff31e5 | ||
![]() |
430f0e5d65 | ||
![]() |
4dff1d4b2b | ||
![]() |
56df72f61c | ||
![]() |
8fdd456fa9 | ||
![]() |
3e1176e896 | ||
![]() |
52a8b1d911 | ||
![]() |
3ecd4030e3 | ||
![]() |
841a004acd | ||
![]() |
839975c348 | ||
![]() |
185aa104b0 | ||
![]() |
24d56023fd | ||
![]() |
d3e7a8c797 | ||
![]() |
b2279c9565 | ||
![]() |
52906407af | ||
![]() |
2721e19ee6 | ||
![]() |
388912d4a7 | ||
![]() |
f62b3d0e9d | ||
![]() |
2d0fdaeec6 | ||
![]() |
b9348180f5 | ||
![]() |
1e2b9cb5ed | ||
![]() |
62702a4878 | ||
![]() |
c85738574c | ||
![]() |
96a8e5df83 | ||
![]() |
85b4ff4f81 | ||
![]() |
0f6eff1f20 | ||
![]() |
7a5e702334 | ||
![]() |
8687ed2068 | ||
![]() |
2be0e966e1 | ||
![]() |
facb53e81f | ||
![]() |
2bfc0e1da6 | ||
![]() |
6d24b22b21 | ||
![]() |
5f0fa71f32 | ||
![]() |
4f81e55e41 | ||
![]() |
ca0122c3cb | ||
![]() |
7200b16520 | ||
![]() |
e488daae16 | ||
![]() |
f3df15650a | ||
![]() |
f8a9a970b2 | ||
![]() |
75e21c4853 | ||
![]() |
495b63d7dc | ||
![]() |
52983c7188 | ||
![]() |
2211a79840 | ||
![]() |
daca57129f | ||
![]() |
19c7c0763f | ||
![]() |
14c6550593 | ||
![]() |
7aae93e7ed | ||
![]() |
dffba78401 | ||
![]() |
a8a1a4985f | ||
![]() |
31683d9e2a | ||
![]() |
00e143d47e | ||
![]() |
6e9ba3fc01 | ||
![]() |
a373f6b654 | ||
![]() |
91bd28d410 | ||
![]() |
acde076913 | ||
![]() |
f5655526d0 | ||
![]() |
56f956ff90 | ||
![]() |
f97c1c7136 | ||
![]() |
9ee883ee1b | ||
![]() |
1887512ba1 | ||
![]() |
2ae13c4092 | ||
![]() |
c5f1771375 | ||
![]() |
c4bace504c | ||
![]() |
3bfeed31bc | ||
![]() |
d774de6c46 | ||
![]() |
b20041d85d | ||
![]() |
e4900db9a2 | ||
![]() |
e2bfa5e019 | ||
![]() |
423d8495e4 | ||
![]() |
1fb2b8efd5 | ||
![]() |
3299d4a25c | ||
![]() |
e125bbeb4d | ||
![]() |
55c59253d6 | ||
![]() |
e7deab92c7 | ||
![]() |
d21f29cb14 | ||
![]() |
cdece498c1 | ||
![]() |
5bfdbf116d |
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
- fedora
|
||||
- opensuse
|
||||
- gentoo
|
||||
cpp_std: [c++17]
|
||||
cpp_std: [c++20]
|
||||
include:
|
||||
- distro: fedora
|
||||
cpp_std: c++20
|
||||
|
@ -3,5 +3,5 @@
|
||||
FROM archlinux:base-devel
|
||||
|
||||
RUN pacman -Syu --noconfirm && \
|
||||
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 && \
|
||||
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 iniparser fftw && \
|
||||
sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Waybar [](LICENSE) [](https://paypal.me/ARouillard)<br>
|
||||
|
||||
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
|
||||
> Available in Arch [community](https://www.archlinux.org/packages/extra/x86_64/waybar/) or
|
||||
> Available in Arch [extra](https://www.archlinux.org/packages/extra/x86_64/waybar/) or
|
||||
[AUR](https://aur.archlinux.org/packages/waybar-git/), [Gentoo](https://packages.gentoo.org/packages/gui-apps/waybar), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar).<br>
|
||||
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
|
||||
|
||||
|
76
flake.lock
generated
76
flake.lock
generated
@ -2,15 +2,15 @@
|
||||
"nodes": {
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1676293499,
|
||||
"narHash": "sha256-uIOTlTxvrXxpKeTvwBI1JGDGtCxMXE3BI0LFwoQMhiQ=",
|
||||
"lastModified": 1692523566,
|
||||
"narHash": "sha256-VDJDihK6jNebVw9y3qKCVD6+6QaC/x8kxZzL4MaIPPY=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "71e3022e3ab20bbf1342640547ef5bc14fb43bf4",
|
||||
"rev": "d208c58e2f7afef838add5f18a9936b12a71d695",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -36,27 +36,15 @@
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1642700792,
|
||||
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
||||
"type": "github"
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1676283394,
|
||||
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
|
||||
"lastModified": 1689068808,
|
||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
|
||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -67,11 +55,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1643381941,
|
||||
"narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=",
|
||||
"lastModified": 1677383253,
|
||||
"narHash": "sha256-UfpzWfSxkfXHnb4boXZNaKsAcUrZT9Hw+tao1oZxd08=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5",
|
||||
"rev": "9952d6bc395f5841262b006fbace8dd7e143b634",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -83,11 +71,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1676300157,
|
||||
"narHash": "sha256-1HjRzfp6LOLfcj/HJHdVKWAkX9QRAouoh6AjzJiIerU=",
|
||||
"lastModified": 1692638711,
|
||||
"narHash": "sha256-J0LgSFgJVGCC1+j5R2QndadWI1oumusg6hCtYAzLID4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "545c7a31e5dedea4a6d372712a18e00ce097d462",
|
||||
"rev": "91a22f76cd1716f9d0149e8a5c68424bb691de15",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -101,9 +89,39 @@
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
16
flake.nix
16
flake.nix
@ -30,14 +30,14 @@
|
||||
]);
|
||||
in
|
||||
{
|
||||
overlays.default = _: prev: {
|
||||
waybar = prev.callPackage ./nix/default.nix {
|
||||
overlays.default = final: prev: {
|
||||
waybar = final.callPackage ./nix/default.nix {
|
||||
version = prev.waybar.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
};
|
||||
packages = genSystems
|
||||
(system:
|
||||
(self.overlays.default null pkgsFor.${system})
|
||||
(self.overlays.default pkgsFor.${system} pkgsFor.${system})
|
||||
// {
|
||||
default = self.packages.${system}.waybar;
|
||||
});
|
||||
@ -47,17 +47,12 @@
|
||||
let pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
overlays = [ devshell.overlay ];
|
||||
overlays = [ devshell.overlays.default ];
|
||||
};
|
||||
in
|
||||
pkgs.devshell.mkShell {
|
||||
imports = [ "${pkgs.devshell.extraModulesDir}/language/c.nix" ];
|
||||
commands = [
|
||||
{
|
||||
package = pkgs.devshell.cli;
|
||||
help = "Per project developer environments";
|
||||
}
|
||||
];
|
||||
|
||||
devshell.packages = with pkgs; [
|
||||
clang-tools
|
||||
gdb
|
||||
@ -79,6 +74,7 @@
|
||||
at-spi2-atk atkmm cairo cairomm catch2 fmt_8 fontconfig
|
||||
gdk-pixbuf glibmm gtk3 harfbuzz pango pangomm wayland-protocols
|
||||
]);
|
||||
|
||||
env = with pkgs; [
|
||||
{ name = "CPLUS_INCLUDE_PATH"; prefix = "$DEVSHELL_DIR/include"; }
|
||||
{ name = "PKG_CONFIG_PATH"; prefix = "$DEVSHELL_DIR/lib/pkgconfig"; }
|
||||
|
27
include/AAppIconLabel.hpp
Normal file
27
include/AAppIconLabel.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/image.h>
|
||||
|
||||
#include "AIconLabel.hpp"
|
||||
|
||||
namespace waybar {
|
||||
|
||||
class AAppIconLabel : public AIconLabel {
|
||||
public:
|
||||
AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
|
||||
const std::string &format, uint16_t interval = 0, bool ellipsize = false,
|
||||
bool enable_click = false, bool enable_scroll = false);
|
||||
virtual ~AAppIconLabel() = default;
|
||||
auto update() -> void override;
|
||||
|
||||
protected:
|
||||
void updateAppIconName(const std::string &app_identifier,
|
||||
const std::string &alternative_app_identifier);
|
||||
void updateAppIcon();
|
||||
unsigned app_icon_size_{24};
|
||||
bool update_app_icon_{true};
|
||||
std::string app_icon_name_;
|
||||
};
|
||||
|
||||
} // namespace waybar
|
@ -59,7 +59,8 @@ class Bluetooth : public ALabel {
|
||||
auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool;
|
||||
auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool;
|
||||
|
||||
auto findCurController(ControllerInfo&) -> bool;
|
||||
// Returns std::nullopt if no controller could be found
|
||||
auto findCurController() -> std::optional<ControllerInfo>;
|
||||
auto findConnectedDevices(const std::string&, std::vector<DeviceInfo>&) -> void;
|
||||
|
||||
#ifdef WANT_RFKILL
|
||||
@ -68,7 +69,7 @@ class Bluetooth : public ALabel {
|
||||
const std::unique_ptr<GDBusObjectManager, void (*)(GDBusObjectManager*)> manager_;
|
||||
|
||||
std::string state_;
|
||||
ControllerInfo cur_controller_;
|
||||
std::optional<ControllerInfo> cur_controller_;
|
||||
std::vector<DeviceInfo> connected_devices_;
|
||||
DeviceInfo cur_focussed_device_;
|
||||
std::string device_enumerate_;
|
||||
|
@ -34,11 +34,18 @@ class Clock final : public ALabel {
|
||||
|
||||
auto first_day_of_week() -> date::weekday;
|
||||
const date::time_zone* current_timezone();
|
||||
bool is_timezone_fixed();
|
||||
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
|
||||
auto timezones_text(std::chrono::system_clock::time_point now) -> std::string;
|
||||
|
||||
/*Calendar properties*/
|
||||
WeeksSide cldWPos_{WeeksSide::HIDDEN};
|
||||
/*
|
||||
0 - calendar.format.months
|
||||
1 - calendar.format.weekdays
|
||||
2 - calendar.format.days
|
||||
3 - calendar.format.today
|
||||
4 - calendar.format.weeks
|
||||
5 - tooltip-format
|
||||
*/
|
||||
std::map<int, std::string const> fmtMap_;
|
||||
CldMode cldMode_{CldMode::MONTH};
|
||||
uint cldMonCols_{3}; // Count of the month in the row
|
||||
@ -52,8 +59,8 @@ class Clock final : public ALabel {
|
||||
std::string cldMonCached_{};
|
||||
date::day cldBaseDay_{0};
|
||||
/*Calendar functions*/
|
||||
auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime)
|
||||
-> std::string;
|
||||
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
|
||||
const date::time_zone* tz) -> const std::string;
|
||||
/*Clock actions*/
|
||||
void cldModeSwitch();
|
||||
void cldShift_up();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/args.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
@ -34,6 +35,7 @@ class Custom : public ALabel {
|
||||
std::string alt_;
|
||||
std::string tooltip_;
|
||||
std::vector<std::string> class_;
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> fields_;
|
||||
int percentage_;
|
||||
FILE* fp_;
|
||||
int pid_;
|
||||
|
@ -1,13 +1,13 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "ALabel.hpp"
|
||||
#include "AAppIconLabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "util/json.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
class Window : public waybar::ALabel, public EventHandler {
|
||||
class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
public:
|
||||
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
virtual ~Window();
|
||||
@ -24,6 +24,19 @@ class Window : public waybar::ALabel, public EventHandler {
|
||||
static auto parse(const Json::Value&) -> Workspace;
|
||||
};
|
||||
|
||||
struct WindowData {
|
||||
bool floating;
|
||||
int monitor = -1;
|
||||
std::string class_name;
|
||||
std::string initial_class_name;
|
||||
std::string title;
|
||||
std::string initial_title;
|
||||
bool fullscreen;
|
||||
bool grouped;
|
||||
|
||||
static auto parse(const Json::Value&) -> WindowData;
|
||||
};
|
||||
|
||||
auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||
auto getActiveWorkspace() -> Workspace;
|
||||
void onEvent(const std::string&) override;
|
||||
@ -34,12 +47,13 @@ class Window : public waybar::ALabel, public EventHandler {
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
std::string last_title_;
|
||||
WindowData window_data_;
|
||||
Workspace workspace_;
|
||||
std::string solo_class_;
|
||||
std::string last_solo_class_;
|
||||
bool solo_;
|
||||
bool all_floating_;
|
||||
bool swallowing_;
|
||||
bool fullscreen_;
|
||||
};
|
||||
|
||||
|
@ -9,27 +9,38 @@
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
struct WorkspaceDto {
|
||||
int id;
|
||||
|
||||
static WorkspaceDto parse(const Json::Value& value);
|
||||
};
|
||||
|
||||
class Workspace {
|
||||
public:
|
||||
Workspace(int id);
|
||||
Workspace(WorkspaceDto dto);
|
||||
int id() const { return id_; };
|
||||
int active() const { return active_; };
|
||||
Workspace(const Json::Value& workspace_data);
|
||||
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
||||
void set_active(bool value = true) { active_ = value; };
|
||||
Gtk::Button& button() { return button_; };
|
||||
|
||||
int id() const { return id_; };
|
||||
std::string name() const { return name_; };
|
||||
std::string output() const { return output_; };
|
||||
bool active() const { return active_; };
|
||||
bool is_special() const { return is_special_; };
|
||||
bool is_persistent() const { return is_persistent_; };
|
||||
bool is_empty() const { return windows_ == 0; };
|
||||
bool is_urgent() const { return is_urgent_; };
|
||||
|
||||
auto handle_clicked(GdkEventButton* bt) -> bool;
|
||||
void set_active(bool value = true) { active_ = value; };
|
||||
void set_persistent(bool value = true) { is_persistent_ = value; };
|
||||
void set_urgent(bool value = true) { is_urgent_ = value; };
|
||||
void set_windows(uint value) { windows_ = value; };
|
||||
|
||||
void update(const std::string& format, const std::string& icon);
|
||||
|
||||
private:
|
||||
int id_;
|
||||
bool active_;
|
||||
std::string name_;
|
||||
std::string output_;
|
||||
uint windows_;
|
||||
bool active_ = false;
|
||||
bool is_special_ = false;
|
||||
bool is_persistent_ = false;
|
||||
bool is_urgent_ = false;
|
||||
|
||||
Gtk::Button button_;
|
||||
Gtk::Box content_;
|
||||
@ -39,23 +50,39 @@ class Workspace {
|
||||
class Workspaces : public AModule, public EventHandler {
|
||||
public:
|
||||
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
virtual ~Workspaces();
|
||||
~Workspaces() override;
|
||||
void update() override;
|
||||
void init();
|
||||
|
||||
auto all_outputs() const -> bool { return all_outputs_; }
|
||||
auto show_special() const -> bool { return show_special_; }
|
||||
|
||||
auto get_bar_output() const -> std::string { return bar_.output->name; }
|
||||
|
||||
private:
|
||||
void onEvent(const std::string&) override;
|
||||
void update_window_count();
|
||||
void sort_workspaces();
|
||||
void create_workspace(int id);
|
||||
void remove_workspace(int id);
|
||||
void create_workspace(Json::Value& value);
|
||||
void remove_workspace(std::string name);
|
||||
void set_urgent_workspace(std::string windowaddress);
|
||||
|
||||
bool all_outputs_ = false;
|
||||
bool show_special_ = false;
|
||||
|
||||
void fill_persistent_workspaces();
|
||||
void create_persistent_workspaces();
|
||||
std::vector<std::string> persistent_workspaces_to_create_;
|
||||
bool persistent_created_ = false;
|
||||
|
||||
std::string format_;
|
||||
std::map<std::string, std::string> icons_map_;
|
||||
bool with_icon_;
|
||||
int active_workspace_id;
|
||||
uint64_t monitor_id_;
|
||||
std::string active_workspace_name_;
|
||||
std::vector<std::unique_ptr<Workspace>> workspaces_;
|
||||
std::vector<int> workspaces_to_create_;
|
||||
std::vector<int> workspaces_to_remove_;
|
||||
std::vector<Json::Value> workspaces_to_create_;
|
||||
std::vector<std::string> workspaces_to_remove_;
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
Gtk::Box box_;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <fmt/chrono.h>
|
||||
#include <gtkmm/label.h>
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AModule.hpp"
|
||||
@ -40,6 +41,7 @@ class KeyboardState : public AModule {
|
||||
|
||||
struct libinput* libinput_;
|
||||
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
||||
std::set<int> binding_keys;
|
||||
|
||||
util::SleeperThread libinput_thread_, hotplug_thread_;
|
||||
};
|
||||
|
@ -66,9 +66,9 @@ class Mpris : public ALabel {
|
||||
int album_len_;
|
||||
int title_len_;
|
||||
int dynamic_len_;
|
||||
std::string dynamic_separator_;
|
||||
std::vector<std::string> dynamic_order_;
|
||||
std::vector<std::string> dynamic_prio_;
|
||||
std::vector<std::string> dynamic_order_;
|
||||
std::string dynamic_separator_;
|
||||
bool truncate_hours_;
|
||||
bool tooltip_len_limits_;
|
||||
std::string ellipsis_;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "AIconLabel.hpp"
|
||||
#include "AAppIconLabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "client.hpp"
|
||||
#include "modules/sway/ipc/client.hpp"
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace waybar::modules::sway {
|
||||
|
||||
class Window : public AIconLabel, public sigc::trackable {
|
||||
class Window : public AAppIconLabel, public sigc::trackable {
|
||||
public:
|
||||
Window(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||
virtual ~Window() = default;
|
||||
@ -25,8 +25,6 @@ class Window : public AIconLabel, public sigc::trackable {
|
||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||
getFocusedNode(const Json::Value& nodes, std::string& output);
|
||||
void getTree();
|
||||
void updateAppIconName();
|
||||
void updateAppIcon();
|
||||
|
||||
const Bar& bar_;
|
||||
std::string window_;
|
||||
@ -37,9 +35,6 @@ class Window : public AIconLabel, public sigc::trackable {
|
||||
std::string old_app_id_;
|
||||
std::size_t app_nb_;
|
||||
std::string shell_;
|
||||
unsigned app_icon_size_{24};
|
||||
bool update_app_icon_{true};
|
||||
std::string app_icon_name_;
|
||||
int floating_count_;
|
||||
util::JsonParser parser_;
|
||||
std::mutex mutex_;
|
||||
|
@ -41,6 +41,7 @@ class Workspaces : public AModule, public sigc::trackable {
|
||||
|
||||
const Bar& bar_;
|
||||
std::vector<Json::Value> workspaces_;
|
||||
std::vector<std::string> high_priority_named_;
|
||||
std::vector<std::string> workspaces_order_;
|
||||
Gtk::Box box_;
|
||||
util::JsonParser parser_;
|
||||
|
@ -93,7 +93,7 @@ template <>
|
||||
struct formatter<Glib::ustring> : formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const Glib::ustring& value, FormatContext& ctx) {
|
||||
return formatter<std::string>::format(value, ctx);
|
||||
return formatter<std::string>::format(static_cast<std::string>(value), ctx);
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
@ -95,6 +95,7 @@ class SleeperThread {
|
||||
}
|
||||
|
||||
~SleeperThread() {
|
||||
connection_.disconnect();
|
||||
stop();
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
|
@ -25,12 +25,12 @@ The *backlight* module displays the current backlight level.
|
||||
The maximum length in characters the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
@ -81,9 +81,9 @@ The *backlight* module displays the current backlight level.
|
||||
|
||||
```
|
||||
"backlight": {
|
||||
"device": "intel_backlight",
|
||||
"format": "{percent}% {icon}",
|
||||
"format-icons": ["", ""]
|
||||
"device": "intel_backlight",
|
||||
"format": "{percent}% {icon}",
|
||||
"format-icons": ["", ""]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -23,9 +23,9 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
||||
Define the max percentage of the battery, for when you've set the battery to stop charging at a lower level to save it. For example, if you've set the battery to stop at 80% that will become the new 100%.
|
||||
|
||||
*design-capacity*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Option to use the battery design capacity instead of it's current maximal capacity.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Option to use the battery design capacity instead of it's current maximal capacity.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
@ -56,12 +56,12 @@ The *battery* module displays the current capacity and state (eg. charging) of y
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer++
|
||||
@ -132,8 +132,8 @@ The *battery* module allows one to define custom formats based on up to two fact
|
||||
# STATES
|
||||
|
||||
- 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>*.
|
||||
- Also each state can have its own *format*. Those con be configured via *format-<name>*. Or if you want to differentiate a bit more even as *format-<status>-<state>*. For more information see *custom-formats*.
|
||||
- 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>*.
|
||||
- Also each state can have its own *format*. Those con be configured via *format-<name>*. Or if you want to differentiate a bit more even as *format-<status>-<state>*. For more information see *custom-formats*.
|
||||
|
||||
|
||||
|
||||
@ -141,15 +141,15 @@ The *battery* module allows one to define custom formats based on up to two fact
|
||||
|
||||
```
|
||||
"battery": {
|
||||
"bat": "BAT2",
|
||||
"interval": 60,
|
||||
"states": {
|
||||
"warning": 30,
|
||||
"critical": 15
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
"max-length": 25
|
||||
"bat": "BAT2",
|
||||
"interval": 60,
|
||||
"states": {
|
||||
"warning": 30,
|
||||
"critical": 15
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
"max-length": 25
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -42,6 +42,10 @@ Addressed by *bluetooth*
|
||||
typeof: string ++
|
||||
This format is used when the displayed controller is connected to at least 1 device.
|
||||
|
||||
*format-no-controller*: ++
|
||||
typeof: string ++
|
||||
This format is used when no bluetooth controller could be found
|
||||
|
||||
*format-icons*: ++
|
||||
typeof: array/object ++
|
||||
Based on the current battery percentage (see section *EXPERIMENTAL BATTERY PERCENTAGE FEATURE*), the corresponding icon gets selected. ++
|
||||
@ -57,12 +61,12 @@ Addressed by *bluetooth*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
@ -113,6 +117,10 @@ Addressed by *bluetooth*
|
||||
typeof: string ++
|
||||
This format is used when the displayed controller is connected to at least 1 device.
|
||||
|
||||
*tooltip-format-no-controller*: ++
|
||||
typeof: string ++
|
||||
This format is used when no bluetooth controller could be found
|
||||
|
||||
*tooltip-format-enumerate-connected*: ++
|
||||
typeof: string ++
|
||||
This format is used to define how each connected device should be displayed within the *device_enumerate* format replacement in the tooltip menu.
|
||||
|
182
man/waybar-cava.5.scd
Normal file
182
man/waybar-cava.5.scd
Normal file
@ -0,0 +1,182 @@
|
||||
waybar-cava(5) "waybar-cava" "User Manual"
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - cava module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*cava* module for karlstav/cava project. See it on github: https://github.com/karlstav/cava.
|
||||
|
||||
|
||||
# FILES
|
||||
|
||||
$XDG_CONFIG_HOME/waybar/config ++
|
||||
Per user configuration file
|
||||
|
||||
# ADDITIONAL FILES
|
||||
|
||||
libcava lives in:
|
||||
|
||||
. /usr/lib/libcava.so or /usr/lib64/libcava.so
|
||||
. /usr/lib/pkgconfig/cava.pc or /usr/lib64/pkgconfig/cava.pc
|
||||
. /usr/include/cava
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
[- *Option*
|
||||
:- *Typeof*
|
||||
:- *Default*
|
||||
:- *Description*
|
||||
|[ *cava_config*
|
||||
:[ string
|
||||
:[
|
||||
:< Path where cava configuration file is placed to
|
||||
|[ *framerate*
|
||||
:[ integer
|
||||
:[ 30
|
||||
:[ rames per second. Is used as a replacement for *interval*
|
||||
|[ *autosens*
|
||||
:[ integer
|
||||
:[ 1
|
||||
:[ Will attempt to decrease sensitivity if the bars peak
|
||||
|[ *sensitivity*
|
||||
:[ integer
|
||||
:[ 100
|
||||
:[ Manual sensitivity in %. It's recommended to be omitted when *autosens* = 1
|
||||
|[ *bars*
|
||||
:[ integer
|
||||
:[ 12
|
||||
:[ The number of bars
|
||||
|[ *lower_cutoff_freq*
|
||||
:[ long integer
|
||||
:[ 50
|
||||
:[ Lower cutoff frequencies for lowest bars the bandwidth of the visualizer
|
||||
|[ *higher_cutoff_freq*
|
||||
:[ long integer
|
||||
:[ 10000
|
||||
:[ Higher cutoff frequencies for highest bars the bandwidth of the visualizer
|
||||
|[ *sleep_timer*
|
||||
:[ integer
|
||||
:[ 5
|
||||
:[ Seconds with no input before cava main thread goes to sleep mode
|
||||
|[ *method*
|
||||
:[ string
|
||||
:[ pulse
|
||||
:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem
|
||||
|[ *source*
|
||||
:[ string
|
||||
:[ auto
|
||||
:[ See cava configuration
|
||||
|[ *sample_rate*
|
||||
:[ long integer
|
||||
:[ 44100
|
||||
:[ See cava configuration
|
||||
|[ *sample_bits*
|
||||
:[ integer
|
||||
:[ 16
|
||||
:[ See cava configuration
|
||||
|[ *stereo*
|
||||
:[ bool
|
||||
:[ true
|
||||
:[ Visual channels
|
||||
|[ *reverse*
|
||||
:[ bool
|
||||
:[ false
|
||||
:[ Displays frequencies the other way around
|
||||
|[ *bar_delimiter*
|
||||
:[ integer
|
||||
:[ 0
|
||||
:[ Each bar is separated by a delimiter. Use decimal value in ascii table(i.e. 59 = ";"). 0 means no delimiter
|
||||
|[ *monstercat*
|
||||
:[ bool
|
||||
:[ false
|
||||
:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves"
|
||||
|[ *waves*
|
||||
:[ bool
|
||||
:[ false
|
||||
:[ Disables or enables the so-called "Monstercat smoothing" with of without "waves"
|
||||
|[ *noise_reduction*
|
||||
:[ double
|
||||
:[ 0.77
|
||||
:[ Range between 0 - 1. The raw visualization is very noisy, this factor adjust the integral and gravity filters to keep the signal smooth. 1 - will be very slow and smooth, 0 - will be fast but noisy
|
||||
|[ *input_delay*
|
||||
:[ integer
|
||||
:[ 2
|
||||
:[ Sets the delay before fetching audio source thread start working. On author machine Waybar starts much faster then pipewire audio server, and without a little delay cava module fails due to pipewire is not ready
|
||||
|[ *ascii_max_range*
|
||||
:[ integer
|
||||
:[ 7
|
||||
:[ It's impossible to set it directly. The value is dictated by the number of icons in the array *format-icons*
|
||||
|[ *data_format*
|
||||
:[ string
|
||||
:[ asci
|
||||
:[ It's impossible to set it. Waybar sets it to = asci for internal needs
|
||||
|[ *raw_target*
|
||||
:[ string
|
||||
:[ /dev/stdout
|
||||
:[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs
|
||||
|
||||
Configuration can be provided as:
|
||||
- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped
|
||||
- Without cava configuration file. In such case cava should be configured through provided list of the configuration option
|
||||
- Mix. When provided both And cava configuration file And configuration options. In such case waybar applies configuration file first then overrides particular options by the provided list of configuration options
|
||||
|
||||
# ACTIONS
|
||||
|
||||
[- *String*
|
||||
:- *Action*
|
||||
|[ *mode*
|
||||
:< Switch main cava thread and fetching audio source thread from/to pause/resume
|
||||
|
||||
# DEPENDENCIES
|
||||
|
||||
- iniparser
|
||||
- fftw3
|
||||
|
||||
# SOLVING ISSUES
|
||||
|
||||
. On start Waybar throws an exception "error while loading shared libraries: libcava.so: cannot open shared object file: No such file or directory".
|
||||
It might happen when libcava for some reason hasn't been registered in the system. sudo ldconfig should help
|
||||
. Waybar is starting but cava module doesn't react on the music
|
||||
1. In such case for at first need to make sure usual cava application is working as well
|
||||
2. If so, need to comment all configuration options. Uncomment cava_config and provide the path to the working cava config
|
||||
3. You might set too huge or too small input_delay. Try to setup to 4 seconds, restart waybar and check again 4 seconds past. Usual even on weak machines it should be enough
|
||||
4. You might accidentally switched action mode to pause mode
|
||||
|
||||
# RISING ISSUES
|
||||
|
||||
For clear understanding: this module is a cava API's consumer. So for any bugs related to cava engine you should contact to Cava upstream(https://github.com/karlstav/cava) ++
|
||||
with the one Exception. Cava upstream doesn't provide cava as a shared library. For that this module author made a fork libcava(https://github.com/LukashonakV/cava). ++
|
||||
So the order is:
|
||||
. cava upstream
|
||||
. libcava upstream.
|
||||
In case when cava releases new version and you're wanna get it, it should be raised an issue to libcava(https://github.com/LukashonakV/cava) with title ++
|
||||
\[Bump\]x.x.x where x.x.x is cava release version.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"cava": {
|
||||
//"cava_config": "$XDG_CONFIG_HOME/cava/cava.conf",
|
||||
"framerate": 30,
|
||||
"autosens": 1,
|
||||
//"sensitivity": 100,
|
||||
"bars": 14,
|
||||
"lower_cutoff_freq": 50,
|
||||
"higher_cutoff_freq": 10000,
|
||||
"method": "pulse",
|
||||
"source": "auto",
|
||||
"stereo": true,
|
||||
"reverse": false,
|
||||
"bar_delimiter": 0,
|
||||
"monstercat": false,
|
||||
"waves": false,
|
||||
"noise_reduction": 0.77,
|
||||
"input_delay": 2,
|
||||
"format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ],
|
||||
"actions": {
|
||||
"on-click-right": "mode"
|
||||
}
|
||||
},
|
||||
```
|
@ -2,7 +2,7 @@ waybar-clock(5) "waybar-clock" "User Manual"
|
||||
|
||||
# NAME
|
||||
|
||||
clock
|
||||
waybar - clock module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
@ -11,7 +11,7 @@ clock
|
||||
# FILES
|
||||
|
||||
$XDG_CONFIG_HOME/waybar/config ++
|
||||
Per user configuration file
|
||||
Per user configuration file
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
@ -37,13 +37,13 @@ $XDG_CONFIG_HOME/waybar/config ++
|
||||
:[ list of strings
|
||||
:[
|
||||
:[ A list of timezones (as in *timezone*) to use for time display, changed using
|
||||
the scroll wheel. Do not specify *timezone* option when *timezones* is specified.
|
||||
the scroll wheel. Do not specify *timezone* option when *timezones* is specified.
|
||||
"" represents the system's local timezone
|
||||
|[ *locale*
|
||||
:[ string
|
||||
:[
|
||||
:[ A locale to be used to display the time. Intended to render times in custom
|
||||
timezones with the proper language and format
|
||||
timezones with the proper language and format
|
||||
|[ *max-length*
|
||||
:[ integer
|
||||
:[
|
||||
@ -104,14 +104,14 @@ View all valid format options in *strftime(3)* or have a look <https://fmt.dev/l
|
||||
:[ integer
|
||||
:[
|
||||
:[ The position where week numbers should be displayed. Disabled when is empty.
|
||||
Possible values: left|right
|
||||
Possible values: left|right
|
||||
|[ *on-scroll*
|
||||
:[ integer
|
||||
:[ 1
|
||||
:[ Value to scroll months/years forward/backward. Can be negative. Is
|
||||
configured under *on-scroll* option
|
||||
configured under *on-scroll* option
|
||||
|
||||
3. Adressed by *clock: calendar: format*
|
||||
3. Addressed by *clock: calendar: format*
|
||||
[- *Option*
|
||||
:- *Typeof*
|
||||
:- *Default*
|
||||
@ -164,9 +164,9 @@ View all valid format options in *strftime(3)* or have a look <https://fmt.dev/l
|
||||
|
||||
```
|
||||
"clock": {
|
||||
"interval": 60,
|
||||
"format": "{:%H:%M}",
|
||||
"max-length": 25
|
||||
"interval": 60,
|
||||
"format": "{:%H:%M}",
|
||||
"max-length": 25
|
||||
}
|
||||
```
|
||||
|
||||
@ -174,30 +174,30 @@ View all valid format options in *strftime(3)* or have a look <https://fmt.dev/l
|
||||
|
||||
```
|
||||
"clock": {
|
||||
"format": "{:%H:%M} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
"mode-mon-col" : 3,
|
||||
"weeks-pos" : "right",
|
||||
"on-scroll" : 1,
|
||||
"on-click-right": "mode",
|
||||
"format": {
|
||||
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||
"days": "<span color='#ecc6d9'><b>{}</b></span>",
|
||||
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||
"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"on-click-right": "mode",
|
||||
"on-click-forward": "tz_up",
|
||||
"on-click-backward": "tz_down",
|
||||
"on-scroll-up": "shift_up",
|
||||
"on-scroll-down": "shift_down"
|
||||
}
|
||||
"format": "{:%H:%M} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
"mode-mon-col" : 3,
|
||||
"weeks-pos" : "right",
|
||||
"on-scroll" : 1,
|
||||
"on-click-right": "mode",
|
||||
"format": {
|
||||
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||
"days": "<span color='#ecc6d9'><b>{}</b></span>",
|
||||
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||
"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"on-click-right": "mode",
|
||||
"on-click-forward": "tz_up",
|
||||
"on-click-backward": "tz_down",
|
||||
"on-scroll-up": "shift_up",
|
||||
"on-scroll-down": "shift_down"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
@ -205,10 +205,10 @@ View all valid format options in *strftime(3)* or have a look <https://fmt.dev/l
|
||||
|
||||
```
|
||||
"clock": {
|
||||
"interval": 60,
|
||||
"tooltip": true,
|
||||
"format": "{:%H.%M}",
|
||||
"tooltip-format": "{:%Y-%m-%d}",
|
||||
"interval": 60,
|
||||
"tooltip": true,
|
||||
"format": "{:%H.%M}",
|
||||
"tooltip-format": "{:%Y-%m-%d}",
|
||||
}
|
||||
```
|
||||
|
||||
@ -238,31 +238,31 @@ Example of working config
|
||||
|
||||
```
|
||||
"clock": {
|
||||
"format": "{:%H:%M} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
"mode-mon-col" : 3,
|
||||
"weeks-pos" : "right",
|
||||
"on-scroll" : 1,
|
||||
"on-click-right": "mode",
|
||||
"format": {
|
||||
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||
"days": "<span color='#ecc6d9'><b>{}</b></span>",
|
||||
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||
"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"on-click-right": "mode",
|
||||
"on-click-forward": "tz_up",
|
||||
"on-click-backward": "tz_down",
|
||||
"on-scroll-up": "shift_up",
|
||||
"on-scroll-down": "shift_down"
|
||||
}
|
||||
},
|
||||
"format": "{:%H:%M} ",
|
||||
"format-alt": "{:%A, %B %d, %Y (%R)} ",
|
||||
"tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
"mode-mon-col" : 3,
|
||||
"weeks-pos" : "right",
|
||||
"on-scroll" : 1,
|
||||
"on-click-right": "mode",
|
||||
"format": {
|
||||
"months": "<span color='#ffead3'><b>{}</b></span>",
|
||||
"days": "<span color='#ecc6d9'><b>{}</b></span>",
|
||||
"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
|
||||
"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
|
||||
"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"on-click-right": "mode",
|
||||
"on-click-forward": "tz_up",
|
||||
"on-click-backward": "tz_down",
|
||||
"on-scroll-up": "shift_up",
|
||||
"on-scroll-down": "shift_down"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
# AUTHOR
|
||||
|
@ -30,12 +30,12 @@ The *cpu* module displays the current cpu utilization.
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
|
@ -1,5 +1,4 @@
|
||||
waybar-custom(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - custom module
|
||||
@ -19,14 +18,13 @@ Addressed by *custom/<name>*
|
||||
|
||||
*exec-if*: ++
|
||||
typeof: string ++
|
||||
The path to a script, which determines if the script in *exec* should be executed.
|
||||
The path to a script, which determines if the script in *exec* should be executed. ++
|
||||
*exec* will be executed if the exit code of *exec-if* equals 0.
|
||||
|
||||
*exec-on-event*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after
|
||||
executing the event command.
|
||||
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after executing the event command.
|
||||
|
||||
*return-type*: ++
|
||||
typeof: string ++
|
||||
@ -34,20 +32,19 @@ Addressed by *custom/<name>*
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
The interval (in seconds) in which the information gets polled.
|
||||
Use *once* if you want to execute the module only on startup.
|
||||
You can update it manually with a signal. If no *interval* is defined,
|
||||
it is assumed that the out script loops it self.
|
||||
The interval (in seconds) in which the information gets polled. ++
|
||||
Use *once* if you want to execute the module only on startup. ++
|
||||
You can update it manually with a signal. If no *interval* is defined, it is assumed that the out script loops it self.
|
||||
|
||||
*restart-interval*: ++
|
||||
typeof: integer ++
|
||||
The restart interval (in seconds).
|
||||
Can't be used with the *interval* option, so only with continuous scripts.
|
||||
The restart interval (in seconds). ++
|
||||
Can't be used with the *interval* option, so only with continuous scripts. ++
|
||||
Once the script exit, it'll be re-executed after the *restart-interval*.
|
||||
|
||||
*signal*: ++
|
||||
typeof: integer ++
|
||||
The signal number used to update the module.
|
||||
The signal number used to update the module. ++
|
||||
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
|
||||
|
||||
*format*: ++
|
||||
@ -68,12 +65,12 @@ Addressed by *custom/<name>*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
|
@ -40,12 +40,12 @@ Addressed by *disk*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
|
@ -13,24 +13,24 @@ The *tags* module displays the current state of tags in dwl.
|
||||
Addressed by *dwl/tags*
|
||||
|
||||
*num-tags*: ++
|
||||
typeof: uint ++
|
||||
default: 9 ++
|
||||
The number of tags that should be displayed. Max 32.
|
||||
typeof: uint ++
|
||||
default: 9 ++
|
||||
The number of tags that should be displayed. Max 32.
|
||||
|
||||
*tag-labels*: ++
|
||||
typeof: array ++
|
||||
The label to display for each tag.
|
||||
typeof: array ++
|
||||
The label to display for each tag.
|
||||
|
||||
*disable-click*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled.
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
```
|
||||
"dwl/tags": {
|
||||
"num-tags": 5
|
||||
"num-tags": 5
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -18,12 +18,12 @@ Addressed by *hyprland/language*
|
||||
The format, how information should be displayed.
|
||||
|
||||
*format-<lang>* ++
|
||||
typeof: string++
|
||||
Provide an alternative name to display per language where <lang> is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below.
|
||||
typeof: string++
|
||||
Provide an alternative name to display per language where <lang> is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below.
|
||||
|
||||
*keyboard-name*: ++
|
||||
typeof: string ++
|
||||
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended.
|
||||
typeof: string ++
|
||||
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended.
|
||||
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
@ -41,10 +41,10 @@ Addressed by *hyprland/language*
|
||||
|
||||
```
|
||||
"hyprland/language": {
|
||||
"format": "Lang: {long}"
|
||||
"format-en": "AMERICA, HELL YEAH!"
|
||||
"format-tr": "As bayrakları"
|
||||
"keyboard-name": "at-translated-set-2-keyboard"
|
||||
"format": "Lang: {long}"
|
||||
"format-en": "AMERICA, HELL YEAH!"
|
||||
"format-tr": "As bayrakları"
|
||||
"keyboard-name": "at-translated-set-2-keyboard"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -26,12 +26,12 @@ Addressed by *hyprland/submap*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
@ -71,9 +71,9 @@ Addressed by *hyprland/submap*
|
||||
|
||||
```
|
||||
"hyprland/submap": {
|
||||
"format": "✌️ {}",
|
||||
"max-length": 8,
|
||||
"tooltip": false
|
||||
"format": "✌️ {}",
|
||||
"max-length": 8,
|
||||
"tooltip": false
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -14,13 +14,38 @@ Addressed by *hyprland/window*
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {} ++
|
||||
default: {title} ++
|
||||
The format, how information should be displayed. On {} the current window title is displayed.
|
||||
|
||||
*rewrite*: ++
|
||||
typeof: object ++
|
||||
Rules to rewrite window title. See *rewrite rules*.
|
||||
|
||||
*separate-outputs*: ++
|
||||
typeof: bool ++
|
||||
Show the active window of the monitor the bar belongs to, instead of the focused window.
|
||||
|
||||
*icon*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Option to hide the application icon.
|
||||
|
||||
*icon-size*: ++
|
||||
typeof: integer ++
|
||||
default: 24 ++
|
||||
Option to change the size of the application icon.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
See the output of "hyprctl clients" for examples
|
||||
|
||||
*{title}*: The current title of the focused window.
|
||||
|
||||
*{initialTitle}*: The initial title of the focused window.
|
||||
|
||||
*{class}*: The current class of the focused window.
|
||||
|
||||
*{initialClass}*: The initial class of the focused window.
|
||||
|
||||
# REWRITE RULES
|
||||
|
||||
*rewrite* is an object where keys are regular expressions and values are
|
||||
@ -37,14 +62,28 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
|
||||
|
||||
```
|
||||
"hyprland/window": {
|
||||
"format": "{}",
|
||||
"rewrite": {
|
||||
"(.*) - Mozilla Firefox": "🌎 $1",
|
||||
"(.*) - zsh": "> [$1]"
|
||||
}
|
||||
"format": "{}",
|
||||
"rewrite": {
|
||||
"(.*) - Mozilla Firefox": "🌎 $1",
|
||||
"(.*) - zsh": "> [$1]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#window*
|
||||
- *window#waybar.empty #window* When no windows are in the workspace
|
||||
|
||||
The following classes are applied to the entire Waybar rather than just the
|
||||
window widget:
|
||||
|
||||
- *window#waybar.empty* When no windows are in the workspace
|
||||
- *window#waybar.solo* When one tiled window is visible in the workspace
|
||||
(floating windows may be present)
|
||||
- *window#waybar.<app_id>* Where *<app_id>* is the *class* (e.g. *chromium*) of
|
||||
the solo tiled window in the workspace (use *hyprctl clients* to see classes)
|
||||
- *window#waybar.floating* When there are only floating windows in the workspace
|
||||
- *window#waybar.fullscreen* When there is a fullscreen window in the workspace;
|
||||
useful with Hyprland's *fullscreen, 1* mode
|
||||
- *window#waybar.swallowing* When there is a swallowed window in the workspace
|
||||
|
@ -1,4 +1,4 @@
|
||||
waybar-wlr-workspaces(5)
|
||||
waybar-hyprland-workspaces(5)
|
||||
|
||||
# NAME
|
||||
|
||||
@ -21,10 +21,22 @@ Addressed by *hyprland/workspaces*
|
||||
typeof: array ++
|
||||
Based on the workspace id and state, the corresponding icon gets selected. See *icons*.
|
||||
|
||||
*show-special*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to true special workspaces will be shown.
|
||||
|
||||
*all-outputs*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{id}*: id of workspace assigned by compositor
|
||||
|
||||
*{name}*: workspace name assigned by compositor
|
||||
|
||||
*{icon}*: Icon, as defined in *format-icons*.
|
||||
|
||||
# ICONS
|
||||
@ -33,11 +45,13 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||
|
||||
- *default*: Will be shown, when no string match is found.
|
||||
- *active*: Will be shown, when workspace is active
|
||||
- *special*: Will be shown on non-active special workspaces
|
||||
- *persistent*: Will be shown on non-active persistent workspaces
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"wlr/workspaces": {
|
||||
"hyprland/workspaces": {
|
||||
"format": "{name}: {icon}",
|
||||
"format-icons": {
|
||||
"1": "",
|
||||
@ -48,7 +62,10 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||
"active": "",
|
||||
"default": ""
|
||||
},
|
||||
"sort-by-number": true
|
||||
"persistent_workspaces": {
|
||||
"*": 5, // 5 workspaces by default on every monitor
|
||||
"HDMI-A-1": 3 // but only three on HDMI-A-1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -57,3 +74,6 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||
- *#workspaces*
|
||||
- *#workspaces button*
|
||||
- *#workspaces button.active*
|
||||
- *#workspaces button.persistent*
|
||||
- *#workspaces button.special*
|
||||
- *#workspaces button.urgent*
|
||||
|
@ -28,12 +28,12 @@ screensaving, also known as "presentation mode".
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
@ -64,9 +64,9 @@ screensaving, also known as "presentation mode".
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
*start-activated*: ++
|
||||
typeof: bool ++
|
||||
default: *false* ++
|
||||
Whether the inhibit should be activated when starting waybar.
|
||||
typeof: bool ++
|
||||
default: *false* ++
|
||||
Whether the inhibit should be activated when starting waybar.
|
||||
|
||||
*timeout*: ++
|
||||
typeof: double ++
|
||||
@ -97,8 +97,8 @@ screensaving, also known as "presentation mode".
|
||||
"idle_inhibitor": {
|
||||
"format": "{icon}",
|
||||
"format-icons": {
|
||||
"activated": "",
|
||||
"deactivated": ""
|
||||
"activated": "",
|
||||
"deactivated": ""
|
||||
},
|
||||
"timeout": 30.5
|
||||
}
|
||||
|
@ -13,24 +13,26 @@ The *image* module displays an image from a path.
|
||||
*path*: ++
|
||||
typeof: string ++
|
||||
The path to the image.
|
||||
|
||||
*exec*: ++
|
||||
typeof: string ++
|
||||
The path to the script, which should return image path file
|
||||
it will only execute if the path is not set
|
||||
The path to the script, which should return image path file. ++
|
||||
It will only execute if the path is not set
|
||||
|
||||
*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.
|
||||
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 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*: ++
|
||||
|
@ -85,8 +85,8 @@ See *systemd-inhibit*(1) for more information.
|
||||
"what": "handle-lid-switch",
|
||||
"format": "{icon}",
|
||||
"format-icons": {
|
||||
"activated": "",
|
||||
"deactivated": ""
|
||||
"activated": "",
|
||||
"deactivated": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -13,73 +13,73 @@ The *jack* module displays the current state of the JACK server.
|
||||
Addressed by *jack*
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: *{load}%* ++
|
||||
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||
typeof: string ++
|
||||
default: *{load}%* ++
|
||||
The format, how information should be displayed. This format is used when other formats aren't specified.
|
||||
|
||||
*format-connected*: ++
|
||||
typeof: string ++
|
||||
This format is used when the module is connected to the JACK server.
|
||||
typeof: string ++
|
||||
This format is used when the module is connected to the JACK server.
|
||||
|
||||
*format-disconnected*: ++
|
||||
typeof: string ++
|
||||
This format is used when the module is not connected to the JACK server.
|
||||
typeof: string ++
|
||||
This format is used when the module is not connected to the JACK server.
|
||||
|
||||
*format-xrun*: ++
|
||||
typeof: string ++
|
||||
This format is used for one polling interval, when the JACK server reports an xrun.
|
||||
typeof: string ++
|
||||
This format is used for one polling interval, when the JACK server reports an xrun.
|
||||
|
||||
*realtime*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to drop real-time privileges for the JACK client opened by Waybar.
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to drop real-time privileges for the JACK client opened by Waybar.
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to disable tooltip on hover.
|
||||
typeof: bool ++
|
||||
default: *true* ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*tooltip-format*: ++
|
||||
typeof: string ++
|
||||
default: *{bufsize}/{samplerate} {latency}ms* ++
|
||||
The format of information displayed in the tooltip.
|
||||
typeof: string ++
|
||||
default: *{bufsize}/{samplerate} {latency}ms* ++
|
||||
The format of information displayed in the tooltip.
|
||||
|
||||
*interval*: ++
|
||||
typeof: integer ++
|
||||
default: 1 ++
|
||||
The interval in which the information gets polled.
|
||||
typeof: integer ++
|
||||
default: 1 ++
|
||||
The interval in which the information gets polled.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*max-length*: ++
|
||||
typeof: integer ++
|
||||
The maximum length in character the module should display.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
typeof: string ++
|
||||
Command to execute when you right clicked on the module.
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
@ -97,10 +97,10 @@ Addressed by *jack*
|
||||
|
||||
```
|
||||
"jack": {
|
||||
"format": "DSP {}%",
|
||||
"format-xrun": "{xruns} xruns",
|
||||
"format-disconnected": "DSP off",
|
||||
"realtime": true
|
||||
"format": "DSP {}%",
|
||||
"format-xrun": "{xruns} xruns",
|
||||
"format-disconnected": "DSP off",
|
||||
"realtime": true
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -48,6 +48,11 @@ You must be a member of the input group to use this module.
|
||||
default: chooses first valid input device ++
|
||||
Which libevdev input device to show the state of. Libevdev devices can be found in /dev/input. The device should support number lock, caps lock, and scroll lock events.
|
||||
|
||||
*binding-keys*: ++
|
||||
typeof: array ++
|
||||
default: [58, 69, 70] ++
|
||||
Customize the key to trigger this module, the key number can be find in /usr/include/linux/input-event-codes.h or running sudo libinput debug-events --show-keycodes.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
*{name}*: Caps, Num, or Scroll.
|
||||
@ -65,13 +70,13 @@ The following *format-icons* can be set.
|
||||
|
||||
```
|
||||
"keyboard-state": {
|
||||
"numlock": true,
|
||||
"capslock": true,
|
||||
"format": "{name} {icon}",
|
||||
"format-icons": {
|
||||
"locked": "",
|
||||
"unlocked": ""
|
||||
}
|
||||
"numlock": true,
|
||||
"capslock": true,
|
||||
"format": "{name} {icon}",
|
||||
"format-icons": {
|
||||
"locked": "",
|
||||
"unlocked": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -40,12 +40,12 @@ Addressed by *memory*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
|
@ -13,8 +13,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||
*player*: ++
|
||||
typeof: string ++
|
||||
default: playerctld ++
|
||||
Name of the MPRIS player to attach to. Using the default value always
|
||||
follows the currenly active player.
|
||||
Name of the MPRIS player to attach to. Using the default value always follows the currenly active player.
|
||||
|
||||
*ignored-players*: ++
|
||||
typeof: []string ++
|
||||
@ -49,18 +48,15 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||
|
||||
*artist-len*: ++
|
||||
typeof: integer ++
|
||||
Maximum length of the Artist tag (Wide/Fullwidth Unicode characters
|
||||
count as two). Set to zero to hide the artist in `{dynamic}` tag.
|
||||
Maximum length of the Artist tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the artist in `{dynamic}` tag.
|
||||
|
||||
*album-len*: ++
|
||||
typeof: integer ++
|
||||
Maximum length of the Album tag (Wide/Fullwidth Unicode characters count
|
||||
as two). Set to zero to hide the album in `{dynamic}` tag.
|
||||
Maximum length of the Album tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the album in `{dynamic}` tag.
|
||||
|
||||
*title-len*: ++
|
||||
typeof: integer ++
|
||||
Maximum length of the Title tag (Wide/Fullwidth Unicode characters count
|
||||
as two). Set to zero to hide the title in `{dynamic}` tag.
|
||||
Maximum length of the Title tag (Wide/Fullwidth Unicode characters count as two). Set to zero to hide the title in `{dynamic}` tag.
|
||||
|
||||
*dynamic-len*: ++
|
||||
typeof: integer ++
|
||||
@ -101,14 +97,12 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||
*enable-tooltip-len-limits*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Option to enable the length limits for the tooltip as well. By default
|
||||
the tooltip ignores all length limits.
|
||||
Option to enable the length limits for the tooltip as well. By default the tooltip ignores all length limits.
|
||||
|
||||
*ellipsis*: ++
|
||||
typeof: string ++
|
||||
default: "…" ++
|
||||
This character will be used when any of the tags exceed their maximum
|
||||
length. If you don't want to use an ellipsis, set this to empty string.
|
||||
This character will be used when any of the tags exceed their maximum length. If you don't want to use an ellipsis, set this to empty string.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
@ -124,7 +118,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||
|
||||
*align*: ++
|
||||
typeof: float ++
|
||||
The alignment of the text, where 0 is left-aligned and 1 is right-aligned.
|
||||
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*: ++
|
||||
@ -148,8 +142,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||
|
||||
*status-icons*: ++
|
||||
typeof: map[string]string ++
|
||||
Allows setting _{status-icon}_ based on player status (playing, paused,
|
||||
stopped).
|
||||
Allows setting _{status-icon}_ based on player status (playing, paused, stopped).
|
||||
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
@ -167,7 +160,7 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||
*{length}*: Length of the track, formatted as HH:MM:SS
|
||||
|
||||
*{dynamic}*: Use _{artist}_, _{album}_, _{title}_ and _{length}_, automatically omit++
|
||||
empty values
|
||||
empty values
|
||||
|
||||
*{player_icon}*: Chooses an icon from _player-icons_ based on _{player}_
|
||||
|
||||
|
@ -65,12 +65,12 @@ Addressed by *network*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
|
@ -15,45 +15,45 @@ It may not be set until a layout is first applied.
|
||||
Addressed by *river/layout*
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {} ++
|
||||
The format, how information should be displayed. On {} data gets inserted.
|
||||
typeof: string ++
|
||||
default: {} ++
|
||||
The format, how information should be displayed. On {} data gets inserted.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*max-length*: ++
|
||||
typeof: integer ++
|
||||
The maximum length in character the module should display.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
typeof: string ++
|
||||
Command to execute when you right clicked on the module.
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
```
|
||||
"river/layout": {
|
||||
"format": "{}",
|
||||
"min-length": 4,
|
||||
"align": "right"
|
||||
"format": "{}",
|
||||
"min-length": 4,
|
||||
"align": "right"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -13,59 +13,59 @@ The *mode* module displays the current mapping mode of river.
|
||||
Addressed by *river/mode*
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {} ++
|
||||
The format, how information should be displayed. On {} data gets inserted.
|
||||
typeof: string ++
|
||||
default: {} ++
|
||||
The format, how information should be displayed. On {} data gets inserted.
|
||||
|
||||
*rotate*: ++
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
typeof: integer ++
|
||||
Positive value to rotate the text label.
|
||||
|
||||
*max-length*: ++
|
||||
typeof: integer ++
|
||||
The maximum length in character the module should display.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
typeof: string ++
|
||||
Command to execute when you right clicked on the module.
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
|
||||
*on-scroll-up*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling up on the module.
|
||||
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.
|
||||
typeof: string ++
|
||||
Command to execute when scrolling down on the module.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```
|
||||
"river/mode": {
|
||||
"format": " {}"
|
||||
"format": " {}"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -13,24 +13,24 @@ The *tags* module displays the current state of tags in river.
|
||||
Addressed by *river/tags*
|
||||
|
||||
*num-tags*: ++
|
||||
typeof: uint ++
|
||||
default: 9 ++
|
||||
The number of tags that should be displayed. Max 32.
|
||||
typeof: uint ++
|
||||
default: 9 ++
|
||||
The number of tags that should be displayed. Max 32.
|
||||
|
||||
*tag-labels*: ++
|
||||
typeof: array ++
|
||||
The label to display for each tag.
|
||||
typeof: array ++
|
||||
The label to display for each tag.
|
||||
|
||||
*disable-click*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can left click to set focused tag. Right click to toggle tag focus. If set to true this behaviour is disabled.
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
```
|
||||
"river/tags": {
|
||||
"num-tags": 5
|
||||
"num-tags": 5
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -26,12 +26,12 @@ Addressed by *river/window*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
@ -49,11 +49,11 @@ Addressed by *river/window*
|
||||
|
||||
```
|
||||
"river/window": {
|
||||
"format": "{}"
|
||||
"format": "{}"
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#window*
|
||||
- *#window.focused* Applied when the output this module's bar belongs to is focused.
|
||||
- *#window.focused* Applied when the output this module's bar belongs to is focused.
|
||||
|
@ -27,12 +27,12 @@ cursor is over the module, and clicking on the module toggles mute.
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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.
|
||||
|
||||
*scroll-step*: ++
|
||||
typeof: int ++
|
||||
@ -58,12 +58,12 @@ cursor is over the module, and clicking on the module toggles mute.
|
||||
|
||||
*on-scroll-up*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling up on the module.
|
||||
Command to execute when scrolling up on the module. ++
|
||||
This replaces the default behaviour of volume control.
|
||||
|
||||
*on-scroll-down*: ++
|
||||
typeof: string ++
|
||||
Command to execute when scrolling down on the module.
|
||||
Command to execute when scrolling down on the module. ++
|
||||
This replaces the default behaviour of volume control.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
@ -80,8 +80,8 @@ cursor is over the module, and clicking on the module toggles mute.
|
||||
|
||||
```
|
||||
"sndio": {
|
||||
"format": "{raw_value} 🎜",
|
||||
"scroll-step": 3
|
||||
"format": "{raw_value} 🎜",
|
||||
"scroll-step": 3
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
waybar-states(5)
|
||||
|
||||
# NAME
|
||||
|
||||
waybar - states property
|
||||
|
||||
# OVERVIEW
|
||||
|
||||
Some modules support 'states' which allows percentage values to be used as styling triggers to
|
||||
|
@ -43,11 +43,11 @@ Addressed by *sway/language*
|
||||
|
||||
```
|
||||
"sway/language": {
|
||||
"format": "{}",
|
||||
"format": "{}",
|
||||
},
|
||||
|
||||
"sway/language": {
|
||||
"format": "{short} {variant}",
|
||||
"format": "{short} {variant}",
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -26,12 +26,12 @@ Addressed by *sway/mode*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
@ -70,8 +70,8 @@ Addressed by *sway/mode*
|
||||
|
||||
```
|
||||
"sway/mode": {
|
||||
"format": " {}",
|
||||
"max-length": 50
|
||||
"format": " {}",
|
||||
"max-length": 50
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -50,11 +50,11 @@ Addressed by *sway/scratchpad*
|
||||
|
||||
```
|
||||
"sway/scratchpad": {
|
||||
"format": "{icon} {count}",
|
||||
"show-empty": false,
|
||||
"format-icons": ["", ""],
|
||||
"tooltip": true,
|
||||
"tooltip-format": "{app}: {title}"
|
||||
"format": "{icon} {count}",
|
||||
"show-empty": false,
|
||||
"format-icons": ["", ""],
|
||||
"tooltip": true,
|
||||
"tooltip-format": "{app}: {title}"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -26,12 +26,12 @@ Addressed by *sway/window*
|
||||
The maximum length in character the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
@ -84,7 +84,7 @@ Addressed by *sway/window*
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If the workspace itself is focused and the workspace contains nodes or floating_nodes, show the workspace name. If not set, text remains empty but styles according to nodes in the workspace are still applied.
|
||||
|
||||
|
||||
*rewrite*: ++
|
||||
typeof: object ++
|
||||
Rules to rewrite the module format output. See *rewrite rules*.
|
||||
@ -124,18 +124,22 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
|
||||
|
||||
```
|
||||
"sway/window": {
|
||||
"format": "{}",
|
||||
"max-length": 50,
|
||||
"rewrite": {
|
||||
"(.*) - Mozilla Firefox": "🌎 $1",
|
||||
"(.*) - zsh": "> [$1]"
|
||||
}
|
||||
"format": "{}",
|
||||
"max-length": 50,
|
||||
"rewrite": {
|
||||
"(.*) - Mozilla Firefox": "🌎 $1",
|
||||
"(.*) - zsh": "> [$1]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#window*
|
||||
|
||||
The following classes are applied to the entire Waybar rather than just the
|
||||
window widget:
|
||||
|
||||
- *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-css option is not set
|
||||
- *window#waybar.solo* When one tiled window is in the workspace
|
||||
- *window#waybar.floating* When there are only floating windows in the workspace
|
||||
|
@ -13,74 +13,74 @@ The *workspaces* module displays the currently used workspaces in Sway.
|
||||
Addressed by *sway/workspaces*
|
||||
|
||||
*all-outputs*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output.
|
||||
|
||||
*format*: ++
|
||||
typeof: string ++
|
||||
default: {value} ++
|
||||
The format, how information should be displayed.
|
||||
typeof: string ++
|
||||
default: {value} ++
|
||||
The format, how information should be displayed.
|
||||
|
||||
*format-icons*: ++
|
||||
typeof: array ++
|
||||
Based on the workspace name and state, the corresponding icon gets selected. See *icons*.
|
||||
typeof: array ++
|
||||
Based on the workspace name and state, the corresponding icon gets selected. See *icons*.
|
||||
|
||||
*disable-scroll*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
|
||||
|
||||
*disable-click*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can click to change workspace. If set to true this behaviour is disabled.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can click to change workspace. If set to true this behaviour is disabled.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
*disable-scroll-wraparound*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, scrolling on the workspace indicator will wrap around to the first workspace when reading the end, and vice versa. If set to true this behavior is disabled.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, scrolling on the workspace indicator will wrap around to the first workspace when reading the end, and vice versa. If set to true this behavior is disabled.
|
||||
|
||||
*enable-bar-scroll*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
|
||||
|
||||
*disable-markup*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to true, button label will escape pango markup.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to true, button label will escape pango markup.
|
||||
|
||||
*current-only*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to true. Only focused workspaces will be shown.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If set to true. Only focused workspaces will be shown.
|
||||
|
||||
*persistent_workspaces*: ++
|
||||
typeof: json (see below) ++
|
||||
default: empty ++
|
||||
Lists workspaces that should always be shown, even when non existent
|
||||
typeof: json (see below) ++
|
||||
default: empty ++
|
||||
Lists workspaces that should always be shown, even when non existent
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
typeof: string ++
|
||||
Command to execute when the module is updated.
|
||||
|
||||
*disable-auto-back-and-forth*: ++
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
warp-on-scroll: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
If set to false, you can scroll to cycle through workspaces without mouse warping being enabled. If set to true this behaviour is disabled.
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
If set to false, you can scroll to cycle through workspaces without mouse warping being enabled. If set to true this behaviour is disabled.
|
||||
|
||||
# FORMAT REPLACEMENTS
|
||||
|
||||
@ -102,6 +102,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||
- *urgent*: Will be shown, when workspace is flagged as urgent
|
||||
- *focused*: Will be shown, when workspace is focused
|
||||
- *persistent*: Will be shown, when workspace is persistent one.
|
||||
- *high-priority-named*: Icons by names will be shown always for that workspaces, independent by state.
|
||||
|
||||
# PERSISTENT WORKSPACES
|
||||
|
||||
@ -111,11 +112,11 @@ an empty list denoting all outputs.
|
||||
|
||||
```
|
||||
"sway/workspaces": {
|
||||
"persistent_workspaces": {
|
||||
"3": [], // Always show a workspace with name '3', on all outputs if it does not exists
|
||||
"4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists
|
||||
"5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists
|
||||
}
|
||||
"persistent_workspaces": {
|
||||
"3": [], // Always show a workspace with name '3', on all outputs if it does not exists
|
||||
"4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists
|
||||
"5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -125,19 +126,20 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
|
||||
|
||||
```
|
||||
"sway/workspaces": {
|
||||
"disable-scroll": true,
|
||||
"all-outputs": true,
|
||||
"format": "{name}: {icon}",
|
||||
"format-icons": {
|
||||
"1": "",
|
||||
"2": "",
|
||||
"3": "",
|
||||
"4": "",
|
||||
"5": "",
|
||||
"urgent": "",
|
||||
"focused": "",
|
||||
"default": ""
|
||||
}
|
||||
"disable-scroll": true,
|
||||
"all-outputs": true,
|
||||
"format": "{name}: {icon}",
|
||||
"format-icons": {
|
||||
"1": "",
|
||||
"2": "",
|
||||
"3": "",
|
||||
"4": "",
|
||||
"5": "",
|
||||
"high-priority-named": [ "1", "2" ],
|
||||
"urgent": "",
|
||||
"focused": "",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -66,12 +66,12 @@ Addressed by *temperature*
|
||||
The maximum length in characters the module should display.
|
||||
|
||||
*min-length*: ++
|
||||
typeof: integer ++
|
||||
The minimum length in characters the module should take up.
|
||||
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.
|
||||
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 ++
|
||||
|
@ -6,32 +6,32 @@ waybar - tray module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
_WARNING_ *tray* is still in beta. There may me bugs. Breaking changes may occur.
|
||||
_WARNING_ *tray* is still in beta. There may be bugs. Breaking changes may occur.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
Addressed by *tray*
|
||||
|
||||
*icon-size*: ++
|
||||
typeof: integer ++
|
||||
Defines the size of the tray icons.
|
||||
typeof: integer ++
|
||||
Defines the size of the tray icons.
|
||||
|
||||
*show-passive-items*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Defines visibility of the tray icons with *Passive* status.
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Defines visibility of the tray icons with *Passive* status.
|
||||
|
||||
*smooth-scrolling-threshold*: ++
|
||||
typeof: double ++
|
||||
Threshold to be used when scrolling.
|
||||
|
||||
*spacing*: ++
|
||||
typeof: integer ++
|
||||
Defines the spacing between the tray icons.
|
||||
typeof: integer ++
|
||||
Defines the spacing between the tray icons.
|
||||
|
||||
*reverse-direction*: ++
|
||||
typeof: bool ++
|
||||
Defines if new app icons should be added in a reverse order
|
||||
typeof: bool ++
|
||||
Defines if new app icons should be added in a reverse order
|
||||
|
||||
*on-update*: ++
|
||||
typeof: string ++
|
||||
@ -41,8 +41,8 @@ Addressed by *tray*
|
||||
|
||||
```
|
||||
"tray": {
|
||||
"icon-size": 21,
|
||||
"spacing": 10
|
||||
"icon-size": 21,
|
||||
"spacing": 10
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -12,10 +12,10 @@ compatible devices in the tooltip.
|
||||
# CONFIGURATION
|
||||
|
||||
*native-path*: ++
|
||||
typeof: string ++
|
||||
default: ++
|
||||
The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++
|
||||
Can be obtained using `upower --dump`
|
||||
typeof: string ++
|
||||
default: ++
|
||||
The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++
|
||||
Can be obtained using `upower --dump`
|
||||
|
||||
*icon-size*: ++
|
||||
typeof: integer ++
|
||||
|
@ -93,9 +93,9 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
|
||||
|
||||
```
|
||||
"wireplumber": {
|
||||
"format": "{volume}%",
|
||||
"format-muted": "",
|
||||
"on-click": "helvum"
|
||||
"format": "{volume}%",
|
||||
"format-muted": "",
|
||||
"on-click": "helvum"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -2,7 +2,7 @@ waybar-wlr-taskbar(5)
|
||||
|
||||
# NAME
|
||||
|
||||
wlroots - Taskbar module
|
||||
waybar - wlr taskbar module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
@ -89,7 +89,7 @@ Addressed by *wlr/taskbar*
|
||||
|
||||
*{icon}*: The icon of the application.
|
||||
|
||||
*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
|
||||
*{name}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
|
||||
|
||||
*{title}*: The title of the application.
|
||||
|
||||
|
@ -29,9 +29,8 @@ Addressed by *wlr/workspaces*
|
||||
*sort-by-coordinates*: ++
|
||||
typeof: bool ++
|
||||
default: true ++
|
||||
Should workspaces be sorted by coordinates.
|
||||
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first.
|
||||
If both are false - sort by id will be performed.
|
||||
Should workspaces be sorted by coordinates. ++
|
||||
Note that if both *sort-by-name* and *sort-by-coordinates* are true sort by name will be first. If both are false - sort by id will be performed.
|
||||
|
||||
*sort-by-number*: ++
|
||||
typeof: bool ++
|
||||
|
@ -268,6 +268,7 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert
|
||||
- *waybar-backlight(5)*
|
||||
- *waybar-battery(5)*
|
||||
- *waybar-bluetooth(5)*
|
||||
- *waybar-cava(5)*
|
||||
- *waybar-clock(5)*
|
||||
- *waybar-cpu(5)*
|
||||
- *waybar-custom(5)*
|
||||
|
@ -1,10 +1,10 @@
|
||||
project(
|
||||
'waybar', 'cpp', 'c',
|
||||
version: '0.9.19',
|
||||
version: '0.9.22',
|
||||
license: 'MIT',
|
||||
meson_version: '>= 0.50.0',
|
||||
default_options : [
|
||||
'cpp_std=c++17',
|
||||
'cpp_std=c++20',
|
||||
'buildtype=release',
|
||||
'default_library=static'
|
||||
],
|
||||
@ -159,6 +159,7 @@ src_files = files(
|
||||
'src/AModule.cpp',
|
||||
'src/ALabel.cpp',
|
||||
'src/AIconLabel.cpp',
|
||||
'src/AAppIconLabel.cpp',
|
||||
'src/modules/custom.cpp',
|
||||
'src/modules/disk.cpp',
|
||||
'src/modules/idle_inhibitor.cpp',
|
||||
@ -345,7 +346,7 @@ if get_option('experimental')
|
||||
endif
|
||||
|
||||
cava = dependency('cava',
|
||||
version : '>=0.8.4',
|
||||
version : '>=0.8.5',
|
||||
required: get_option('cava'),
|
||||
fallback : ['cava', 'cava_dep'],
|
||||
not_found_message: 'cava is not found. Building waybar without cava')
|
||||
@ -422,6 +423,7 @@ if scdoc.found()
|
||||
main_manpage_path,
|
||||
'waybar-backlight.5.scd',
|
||||
'waybar-battery.5.scd',
|
||||
'waybar-cava.5.scd',
|
||||
'waybar-clock.5.scd',
|
||||
'waybar-cpu.5.scd',
|
||||
'waybar-custom.5.scd',
|
||||
|
@ -1,89 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
import gi
|
||||
gi.require_version("Playerctl", "2.0")
|
||||
from gi.repository import Playerctl, GLib
|
||||
from gi.repository.Playerctl import Player
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
import signal
|
||||
import gi
|
||||
import json
|
||||
gi.require_version('Playerctl', '2.0')
|
||||
from gi.repository import Playerctl, GLib
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def write_output(text, player):
|
||||
logger.info('Writing output')
|
||||
|
||||
output = {'text': text,
|
||||
'class': 'custom-' + player.props.player_name,
|
||||
'alt': player.props.player_name}
|
||||
|
||||
sys.stdout.write(json.dumps(output) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def on_play(player, status, manager):
|
||||
logger.info('Received new playback status')
|
||||
on_metadata(player, player.props.metadata, manager)
|
||||
|
||||
|
||||
def on_metadata(player, metadata, manager):
|
||||
logger.info('Received new metadata')
|
||||
track_info = ''
|
||||
|
||||
if player.props.player_name == 'spotify' and \
|
||||
'mpris:trackid' in metadata.keys() and \
|
||||
':ad:' in player.props.metadata['mpris:trackid']:
|
||||
track_info = 'AD PLAYING'
|
||||
elif player.get_artist() != '' and player.get_title() != '':
|
||||
track_info = '{artist} - {title}'.format(artist=player.get_artist(),
|
||||
title=player.get_title())
|
||||
else:
|
||||
track_info = player.get_title()
|
||||
|
||||
if player.props.status != 'Playing' and track_info:
|
||||
track_info = ' ' + track_info
|
||||
write_output(track_info, player)
|
||||
|
||||
|
||||
def on_player_appeared(manager, player, selected_player=None):
|
||||
if player is not None and (selected_player is None or player.name == selected_player):
|
||||
init_player(manager, player)
|
||||
else:
|
||||
logger.debug("New player appeared, but it's not the selected player, skipping")
|
||||
|
||||
|
||||
def on_player_vanished(manager, player):
|
||||
logger.info('Player has vanished')
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def init_player(manager, name):
|
||||
logger.debug('Initialize player: {player}'.format(player=name.name))
|
||||
player = Playerctl.Player.new_from_name(name)
|
||||
player.connect('playback-status', on_play, manager)
|
||||
player.connect('metadata', on_metadata, manager)
|
||||
manager.manage_player(player)
|
||||
on_metadata(player, player.props.metadata, manager)
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
logger.debug('Received signal to stop, exiting')
|
||||
sys.stdout.write('\n')
|
||||
logger.info("Received signal to stop, exiting")
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
# loop.quit()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class PlayerManager:
|
||||
def __init__(self, selected_player=None):
|
||||
self.manager = Playerctl.PlayerManager()
|
||||
self.loop = GLib.MainLoop()
|
||||
self.manager.connect(
|
||||
"name-appeared", lambda *args: self.on_player_appeared(*args))
|
||||
self.manager.connect(
|
||||
"player-vanished", lambda *args: self.on_player_vanished(*args))
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
self.selected_player = selected_player
|
||||
|
||||
self.init_players()
|
||||
|
||||
def init_players(self):
|
||||
for player in self.manager.props.player_names:
|
||||
if self.selected_player is not None and self.selected_player != player.name:
|
||||
logger.debug(f"{player.name} is not the filtered player, skipping it")
|
||||
continue
|
||||
self.init_player(player)
|
||||
|
||||
def run(self):
|
||||
logger.info("Starting main loop")
|
||||
self.loop.run()
|
||||
|
||||
def init_player(self, player):
|
||||
logger.info(f"Initialize new player: {player.name}")
|
||||
player = Playerctl.Player.new_from_name(player)
|
||||
player.connect("playback-status",
|
||||
self.on_playback_status_changed, None)
|
||||
player.connect("metadata", self.on_metadata_changed, None)
|
||||
self.manager.manage_player(player)
|
||||
self.on_metadata_changed(player, player.props.metadata)
|
||||
|
||||
def get_players(self) -> List[Player]:
|
||||
return self.manager.props.players
|
||||
|
||||
def write_output(self, text, player):
|
||||
logger.debug(f"Writing output: {text}")
|
||||
|
||||
output = {"text": text,
|
||||
"class": "custom-" + player.props.player_name,
|
||||
"alt": player.props.player_name}
|
||||
|
||||
sys.stdout.write(json.dumps(output) + "\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
def clear_output(self):
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
def on_playback_status_changed(self, player, status, _=None):
|
||||
logger.debug(f"Playback status changed for player {player.props.player_name}: {status}")
|
||||
self.on_metadata_changed(player, player.props.metadata)
|
||||
|
||||
def get_first_playing_player(self):
|
||||
players = self.get_players()
|
||||
logger.debug(f"Getting first playing player from {len(players)} players")
|
||||
if len(players) > 0:
|
||||
# if any are playing, show the first one that is playing
|
||||
# reverse order, so that the most recently added ones are preferred
|
||||
for player in players[::-1]:
|
||||
if player.props.status == "Playing":
|
||||
return player
|
||||
# if none are playing, show the first one
|
||||
return players[0]
|
||||
else:
|
||||
logger.debug("No players found")
|
||||
return None
|
||||
|
||||
def show_most_important_player(self):
|
||||
logger.debug("Showing most important player")
|
||||
# show the currently playing player
|
||||
# or else show the first paused player
|
||||
# or else show nothing
|
||||
current_player = self.get_first_playing_player()
|
||||
if current_player is not None:
|
||||
self.on_metadata_changed(current_player, current_player.props.metadata)
|
||||
else:
|
||||
self.clear_output()
|
||||
|
||||
def on_metadata_changed(self, player, metadata, _=None):
|
||||
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||
player_name = player.props.player_name
|
||||
artist = player.get_artist()
|
||||
title = player.get_title()
|
||||
|
||||
track_info = ""
|
||||
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
|
||||
track_info = "Advertisement"
|
||||
elif artist is not None and title is not None:
|
||||
track_info = f"{artist} - {title}"
|
||||
else:
|
||||
track_info = title
|
||||
|
||||
if track_info:
|
||||
if player.props.status == "Playing":
|
||||
track_info = " " + track_info
|
||||
else:
|
||||
track_info = " " + track_info
|
||||
# only print output if no other player is playing
|
||||
current_playing = self.get_first_playing_player()
|
||||
if current_playing is None or current_playing.props.player_name == player.props.player_name:
|
||||
self.write_output(track_info, player)
|
||||
else:
|
||||
logger.debug(f"Other player {current_playing.props.player_name} is playing, skipping")
|
||||
|
||||
def on_player_appeared(self, _, player):
|
||||
logger.info(f"Player has appeared: {player.name}")
|
||||
if player is not None and (self.selected_player is None or player.name == self.selected_player):
|
||||
self.init_player(player)
|
||||
else:
|
||||
logger.debug(
|
||||
"New player appeared, but it's not the selected player, skipping")
|
||||
|
||||
def on_player_vanished(self, _, player):
|
||||
logger.info(f"Player {player.props.player_name} has vanished")
|
||||
self.show_most_important_player()
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
# Increase verbosity with every occurrence of -v
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||
parser.add_argument("-v", "--verbose", action="count", default=0)
|
||||
|
||||
# Define for which player we're listening
|
||||
parser.add_argument('--player')
|
||||
# Define for which player we"re listening
|
||||
parser.add_argument("--player")
|
||||
|
||||
parser.add_argument("--enable-logging", action="store_true")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
@ -92,37 +161,22 @@ def main():
|
||||
arguments = parse_arguments()
|
||||
|
||||
# Initialize logging
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG,
|
||||
format='%(name)s %(levelname)s %(message)s')
|
||||
if arguments.enable_logging:
|
||||
logfile = os.path.join(os.path.dirname(
|
||||
os.path.realpath(__file__)), "media-player.log")
|
||||
logging.basicConfig(filename=logfile, level=logging.DEBUG,
|
||||
format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s")
|
||||
|
||||
# Logging is set by default to WARN and higher.
|
||||
# With every occurrence of -v it's lowered by one
|
||||
logger.setLevel(max((3 - arguments.verbose) * 10, 0))
|
||||
|
||||
# Log the sent command line arguments
|
||||
logger.debug('Arguments received {}'.format(vars(arguments)))
|
||||
|
||||
manager = Playerctl.PlayerManager()
|
||||
loop = GLib.MainLoop()
|
||||
|
||||
manager.connect('name-appeared', lambda *args: on_player_appeared(*args, arguments.player))
|
||||
manager.connect('player-vanished', on_player_vanished)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
|
||||
for player in manager.props.player_names:
|
||||
if arguments.player is not None and arguments.player != player.name:
|
||||
logger.debug('{player} is not the filtered player, skipping it'
|
||||
.format(player=player.name)
|
||||
)
|
||||
continue
|
||||
|
||||
init_player(manager, player)
|
||||
|
||||
loop.run()
|
||||
logger.info("Creating player manager")
|
||||
if arguments.player:
|
||||
logger.info(f"Filtering for player: {arguments.player}")
|
||||
player = PlayerManager(arguments.player)
|
||||
player.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
133
src/AAppIconLabel.cpp
Normal file
133
src/AAppIconLabel.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "AAppIconLabel.hpp"
|
||||
|
||||
#include <gdkmm/pixbuf.h>
|
||||
#include <glibmm/fileutils.h>
|
||||
#include <glibmm/keyfile.h>
|
||||
#include <glibmm/miscutils.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "util/gtk_icon.hpp"
|
||||
|
||||
namespace waybar {
|
||||
|
||||
AAppIconLabel::AAppIconLabel(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)
|
||||
: AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) {
|
||||
// Icon size
|
||||
if (config["icon-size"].isUInt()) {
|
||||
app_icon_size_ = config["icon-size"].asUInt();
|
||||
}
|
||||
image_.set_pixel_size(app_icon_size_);
|
||||
}
|
||||
|
||||
std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
|
||||
const std::string& alternative_app_identifier) {
|
||||
const auto data_dirs = Glib::get_system_data_dirs();
|
||||
for (const auto& data_dir : data_dirs) {
|
||||
const auto data_app_dir = data_dir + "applications/";
|
||||
auto desktop_file_path = data_app_dir + app_identifier + ".desktop";
|
||||
if (std::filesystem::exists(desktop_file_path)) {
|
||||
return desktop_file_path;
|
||||
}
|
||||
if (!alternative_app_identifier.empty()) {
|
||||
desktop_file_path = data_app_dir + alternative_app_identifier + ".desktop";
|
||||
if (std::filesystem::exists(desktop_file_path)) {
|
||||
return desktop_file_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Glib::ustring> getIconName(const std::string& app_identifier,
|
||||
const std::string& alternative_app_identifier) {
|
||||
const auto desktop_file_path = getDesktopFilePath(app_identifier, alternative_app_identifier);
|
||||
if (!desktop_file_path.has_value()) {
|
||||
// Try some heuristics to find a matching icon
|
||||
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) {
|
||||
return app_identifier;
|
||||
}
|
||||
|
||||
const auto app_identifier_desktop = app_identifier + "-desktop";
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) {
|
||||
return app_identifier_desktop;
|
||||
}
|
||||
|
||||
const auto to_lower = [](const std::string& str) {
|
||||
auto str_cpy = str;
|
||||
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return str;
|
||||
};
|
||||
|
||||
const auto first_space = app_identifier.find_first_of(' ');
|
||||
if (first_space != std::string::npos) {
|
||||
const auto first_word = to_lower(app_identifier.substr(0, first_space));
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
|
||||
return first_word;
|
||||
}
|
||||
}
|
||||
|
||||
const auto first_dash = app_identifier.find_first_of('-');
|
||||
if (first_dash != std::string::npos) {
|
||||
const auto first_word = to_lower(app_identifier.substr(0, first_dash));
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
|
||||
return first_word;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
Glib::KeyFile desktop_file;
|
||||
desktop_file.load_from_file(desktop_file_path.value());
|
||||
return desktop_file.get_string("Desktop Entry", "Icon");
|
||||
} catch (Glib::FileError& error) {
|
||||
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
||||
error.what().c_str());
|
||||
} catch (Glib::KeyFileError& error) {
|
||||
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
||||
error.what().c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
|
||||
const std::string& alternative_app_identifier) {
|
||||
if (!iconEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto icon_name = getIconName(app_identifier, alternative_app_identifier);
|
||||
if (icon_name.has_value()) {
|
||||
app_icon_name_ = icon_name.value();
|
||||
} else {
|
||||
app_icon_name_ = "";
|
||||
}
|
||||
update_app_icon_ = true;
|
||||
}
|
||||
|
||||
void AAppIconLabel::updateAppIcon() {
|
||||
if (update_app_icon_) {
|
||||
update_app_icon_ = false;
|
||||
if (app_icon_name_.empty()) {
|
||||
image_.set_visible(false);
|
||||
} else {
|
||||
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
||||
image_.set_visible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto AAppIconLabel::update() -> void {
|
||||
updateAppIcon();
|
||||
AIconLabel::update();
|
||||
}
|
||||
|
||||
} // namespace waybar
|
@ -190,9 +190,8 @@ auto waybar::modules::Backlight::update() -> void {
|
||||
event_box_.show();
|
||||
const uint8_t percent =
|
||||
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
|
||||
std::string desc =
|
||||
fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)),
|
||||
fmt::arg("icon", getIcon(percent)));
|
||||
std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent),
|
||||
fmt::arg("icon", getIcon(percent)));
|
||||
label_.set_markup(desc);
|
||||
getState(percent);
|
||||
if (tooltipEnabled()) {
|
||||
@ -202,7 +201,7 @@ auto waybar::modules::Backlight::update() -> void {
|
||||
}
|
||||
if (!tooltip_format.empty()) {
|
||||
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
|
||||
fmt::arg("percent", std::to_string(percent)),
|
||||
fmt::arg("percent", percent),
|
||||
fmt::arg("icon", getIcon(percent))));
|
||||
} else {
|
||||
label_.set_tooltip_text(desc);
|
||||
|
@ -92,28 +92,30 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
|
||||
std::back_inserter(device_preference_), [](auto x) { return x.asString(); });
|
||||
}
|
||||
|
||||
// NOTE: assumption made that the controller that is selcected stays unchanged
|
||||
// NOTE: assumption made that the controller that is selected stays unchanged
|
||||
// for duration of the module
|
||||
if (!findCurController(cur_controller_)) {
|
||||
if (cur_controller_ = findCurController(); !cur_controller_) {
|
||||
if (config_["controller-alias"].isString()) {
|
||||
spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'",
|
||||
config_["controller-alias"].asString());
|
||||
} else {
|
||||
spdlog::error("findCurController() failed: no bluetooth controller found");
|
||||
}
|
||||
event_box_.hide();
|
||||
return;
|
||||
}
|
||||
findConnectedDevices(cur_controller_.path, connected_devices_);
|
||||
update();
|
||||
} else {
|
||||
// These calls only make sense if a controller could be found
|
||||
findConnectedDevices(cur_controller_->path, connected_devices_);
|
||||
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
||||
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
|
||||
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
|
||||
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
|
||||
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
|
||||
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
|
||||
this);
|
||||
#ifdef WANT_RFKILL
|
||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
|
||||
#endif
|
||||
}
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
@ -144,12 +146,16 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
|
||||
std::string state;
|
||||
std::string tooltip_format;
|
||||
if (!cur_controller_.powered)
|
||||
state = "off";
|
||||
else if (!connected_devices_.empty())
|
||||
state = "connected";
|
||||
else
|
||||
state = "on";
|
||||
if (cur_controller_) {
|
||||
if (!cur_controller_->powered)
|
||||
state = "off";
|
||||
else if (!connected_devices_.empty())
|
||||
state = "connected";
|
||||
else
|
||||
state = "on";
|
||||
} else {
|
||||
state = "no-controller";
|
||||
}
|
||||
#ifdef WANT_RFKILL
|
||||
if (rfkill_.getState()) state = "disabled";
|
||||
#endif
|
||||
@ -187,8 +193,6 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
tooltip_format = config_["tooltip-format"].asString();
|
||||
}
|
||||
|
||||
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 && !label_.get_style_context()->has_class(style_class)) {
|
||||
label_.get_style_context()->add_class(style_class);
|
||||
@ -196,25 +200,32 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
label_.get_style_context()->remove_class(style_class);
|
||||
}
|
||||
};
|
||||
update_style_context("discoverable", cur_controller_.discoverable);
|
||||
update_style_context("discovering", cur_controller_.discovering);
|
||||
update_style_context("pairable", cur_controller_.pairable);
|
||||
update_style_context("discoverable", cur_controller_ ? cur_controller_->discoverable : false);
|
||||
update_style_context("discovering", cur_controller_ ? cur_controller_->discovering : false);
|
||||
update_style_context("pairable", cur_controller_ ? cur_controller_->pairable : false);
|
||||
if (!state_.empty()) {
|
||||
update_style_context(state_, false);
|
||||
}
|
||||
update_style_context(state, true);
|
||||
state_ = state;
|
||||
|
||||
label_.set_markup(fmt::format(
|
||||
fmt::runtime(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),
|
||||
fmt::arg("controller_alias", cur_controller_.alias),
|
||||
fmt::arg("device_address", cur_focussed_device_.address),
|
||||
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||
fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_label),
|
||||
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0))));
|
||||
if (format_.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
event_box_.show();
|
||||
label_.set_markup(fmt::format(
|
||||
fmt::runtime(format_), fmt::arg("status", state_),
|
||||
fmt::arg("num_connections", connected_devices_.size()),
|
||||
fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"),
|
||||
fmt::arg("controller_address_type",
|
||||
cur_controller_ ? cur_controller_->address_type : "null"),
|
||||
fmt::arg("controller_alias", cur_controller_ ? cur_controller_->alias : "null"),
|
||||
fmt::arg("device_address", cur_focussed_device_.address),
|
||||
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||
fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_label),
|
||||
fmt::arg("device_battery_percentage",
|
||||
cur_focussed_device_.battery_percentage.value_or(0))));
|
||||
}
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
bool tooltip_enumerate_connections_ = config_["tooltip-format-enumerate-connected"].isString();
|
||||
@ -250,9 +261,10 @@ auto waybar::modules::Bluetooth::update() -> void {
|
||||
label_.set_tooltip_text(fmt::format(
|
||||
fmt::runtime(tooltip_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),
|
||||
fmt::arg("controller_alias", cur_controller_.alias),
|
||||
fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"),
|
||||
fmt::arg("controller_address_type",
|
||||
cur_controller_ ? cur_controller_->address_type : "null"),
|
||||
fmt::arg("controller_alias", cur_controller_ ? cur_controller_->alias : "null"),
|
||||
fmt::arg("device_address", cur_focussed_device_.address),
|
||||
fmt::arg("device_address_type", cur_focussed_device_.address_type),
|
||||
fmt::arg("device_alias", cur_focussed_device_.alias), fmt::arg("icon", icon_tooltip),
|
||||
@ -292,8 +304,8 @@ auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged(
|
||||
|
||||
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
|
||||
if (interface_name == "org.bluez.Adapter1") {
|
||||
if (object_path == bt->cur_controller_.path) {
|
||||
bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), bt->cur_controller_);
|
||||
if (object_path == bt->cur_controller_->path) {
|
||||
bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), *bt->cur_controller_);
|
||||
bt->dp.emit();
|
||||
}
|
||||
} else if (interface_name == "org.bluez.Device1" || interface_name == "org.bluez.Battery1") {
|
||||
@ -378,22 +390,23 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
|
||||
return false;
|
||||
}
|
||||
|
||||
auto waybar::modules::Bluetooth::findCurController(ControllerInfo& controller_info) -> bool {
|
||||
bool found_controller = false;
|
||||
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
|
||||
std::optional<ControllerInfo> controller_info;
|
||||
|
||||
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
|
||||
for (GList* l = objects; l != NULL; l = l->next) {
|
||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||
if (getControllerProperties(object, controller_info) &&
|
||||
ControllerInfo info;
|
||||
if (getControllerProperties(object, info) &&
|
||||
(!config_["controller-alias"].isString() ||
|
||||
config_["controller-alias"].asString() == controller_info.alias)) {
|
||||
found_controller = true;
|
||||
config_["controller-alias"].asString() == info.alias)) {
|
||||
controller_info = std::move(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_list_free_full(objects, g_object_unref);
|
||||
|
||||
return found_controller;
|
||||
return controller_info;
|
||||
}
|
||||
|
||||
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
|
||||
@ -404,7 +417,7 @@ auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_con
|
||||
GDBusObject* object = G_DBUS_OBJECT(l->data);
|
||||
DeviceInfo device;
|
||||
if (getDeviceProperties(object, device) && device.connected &&
|
||||
device.paired_controller == cur_controller_.path) {
|
||||
device.paired_controller == cur_controller_->path) {
|
||||
connected_devices.push_back(device);
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,14 @@
|
||||
|
||||
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
: 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) {
|
||||
current_time_zone_idx_{0},
|
||||
is_calendar_in_tooltip_{false},
|
||||
is_timezoned_list_in_tooltip_{false} {
|
||||
if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
|
||||
for (const auto& zone_name : config_["timezones"]) {
|
||||
if (!zone_name.isString()) continue;
|
||||
if (zone_name.asString().empty())
|
||||
// local time should be shown
|
||||
time_zones_.push_back(date::current_zone());
|
||||
else
|
||||
try {
|
||||
@ -43,8 +44,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
}
|
||||
}
|
||||
|
||||
// If all timezones are parsed and no one is good, add current time zone. nullptr in timezones
|
||||
// vector means that local time should be shown
|
||||
// If all timezones are parsed and no one is good
|
||||
if (!time_zones_.size()) {
|
||||
time_zones_.push_back(date::current_zone());
|
||||
}
|
||||
@ -52,10 +52,12 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
// Check if a particular placeholder is present in the tooltip format, to know what to calculate
|
||||
// on update.
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
std::string trimmed_format = config_["tooltip-format"].asString();
|
||||
std::string trimmed_format{config_["tooltip-format"].asString()};
|
||||
fmtMap_.insert({5, trimmed_format});
|
||||
trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(),
|
||||
[](unsigned char x) { return std::isspace(x); }),
|
||||
trimmed_format.end());
|
||||
|
||||
if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) {
|
||||
is_calendar_in_tooltip_ = true;
|
||||
}
|
||||
@ -156,53 +158,35 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
|
||||
}
|
||||
|
||||
const date::time_zone* waybar::modules::Clock::current_timezone() {
|
||||
return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_]
|
||||
: date::current_zone();
|
||||
}
|
||||
|
||||
bool waybar::modules::Clock::is_timezone_fixed() {
|
||||
return time_zones_[current_time_zone_idx_] != nullptr;
|
||||
return time_zones_[current_time_zone_idx_];
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::update() -> void {
|
||||
const auto* time_zone = current_timezone();
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now)};
|
||||
const auto* tz{current_timezone()};
|
||||
const date::zoned_time now{
|
||||
tz,
|
||||
date::floor<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now())}; // Define local time is based on provided time zone
|
||||
const date::year_month_day today{
|
||||
date::floor<date::days>(now.get_local_time())}; // Convert now to year_month_day
|
||||
const date::year_month_day shiftedDay{today + cldCurrShift_}; // Shift today
|
||||
// Define shift local time
|
||||
const auto shiftedNow{date::make_zoned(
|
||||
tz, date::local_days(shiftedDay) +
|
||||
(now.get_local_time() - date::floor<date::days>(now.get_local_time())))};
|
||||
|
||||
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + cldCurrShift_;
|
||||
if (cldCurrShift_.count()) {
|
||||
shifted_date = date::year_month_day(shifted_date.year(), shifted_date.month(), date::day(1));
|
||||
}
|
||||
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
|
||||
auto shifted_ztime = date::zoned_time{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
|
||||
tzset();
|
||||
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
|
||||
text = fmt::format(locale_, fmt::runtime(format_), localtime);
|
||||
} else {
|
||||
text = fmt::format(locale_, fmt::runtime(format_), ztime);
|
||||
}
|
||||
label_.set_markup(text);
|
||||
label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now));
|
||||
|
||||
if (tooltipEnabled()) {
|
||||
if (config_["tooltip-format"].isString()) {
|
||||
std::string calendar_lines{""};
|
||||
std::string timezoned_time_lines{""};
|
||||
if (is_calendar_in_tooltip_) {
|
||||
calendar_lines = get_calendar(ztime, shifted_ztime);
|
||||
}
|
||||
if (is_timezoned_list_in_tooltip_) {
|
||||
timezoned_time_lines = timezones_text(&now);
|
||||
}
|
||||
auto tooltip_format = config_["tooltip-format"].asString();
|
||||
text = fmt::format(locale_, fmt::runtime(tooltip_format), shifted_ztime,
|
||||
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
|
||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
|
||||
label_.set_tooltip_markup(text);
|
||||
}
|
||||
const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time())
|
||||
: ""};
|
||||
const std::string cld_text{(is_calendar_in_tooltip_) ? get_calendar(today, shiftedDay, tz)
|
||||
: ""};
|
||||
|
||||
const std::string text{fmt::format(locale_, fmt::runtime(fmtMap_[5]), shiftedNow,
|
||||
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), tz_text),
|
||||
fmt::arg(kCalendarPlaceholder.c_str(), cld_text))};
|
||||
label_.set_tooltip_markup(text);
|
||||
}
|
||||
|
||||
// Call parent update
|
||||
@ -218,15 +202,15 @@ auto waybar::modules::Clock::doAction(const std::string& name) -> void {
|
||||
}
|
||||
|
||||
// The number of weeks in calendar month layout plus 1 more for calendar titles
|
||||
unsigned cldRowsInMonth(date::year_month const ym, date::weekday const firstdow) {
|
||||
const unsigned cldRowsInMonth(const date::year_month& ym, const date::weekday& firstdow) {
|
||||
using namespace date;
|
||||
return static_cast<unsigned>(
|
||||
ceil<weeks>((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) +
|
||||
2;
|
||||
}
|
||||
|
||||
auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, unsigned const line)
|
||||
-> const date::year_month_weekday {
|
||||
auto cldGetWeekForLine(const date::year_month& ym, const date::weekday& firstdow,
|
||||
unsigned const line) -> const date::year_month_weekday {
|
||||
unsigned index = line - 2;
|
||||
auto sd = date::sys_days{ym / 1};
|
||||
if (date::weekday{sd} == firstdow) ++index;
|
||||
@ -234,8 +218,8 @@ auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow,
|
||||
return ymdw;
|
||||
}
|
||||
|
||||
auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym,
|
||||
unsigned const line, date::weekday const firstdow,
|
||||
auto getCalendarLine(const date::year_month_day& currDate, const date::year_month ym,
|
||||
const unsigned line, const date::weekday& firstdow,
|
||||
const std::locale* const locale_) -> std::string {
|
||||
using namespace date::literals;
|
||||
std::ostringstream res;
|
||||
@ -317,10 +301,9 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const
|
||||
return res.str();
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
const date::zoned_seconds& wtime) -> std::string {
|
||||
auto daypoint = date::floor<date::days>(wtime.get_local_time());
|
||||
const auto ymd{date::year_month_day{daypoint}};
|
||||
auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
|
||||
const date::year_month_day& ymd,
|
||||
const date::time_zone* tz) -> const std::string {
|
||||
const auto ym{ymd.year() / ymd.month()};
|
||||
const auto y{ymd.year()};
|
||||
const auto d{ymd.day()};
|
||||
@ -328,9 +311,6 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
const auto maxRows{12 / cldMonCols_};
|
||||
std::ostringstream os;
|
||||
std::ostringstream tmp;
|
||||
// get currdate
|
||||
daypoint = date::floor<date::days>(now.get_local_time());
|
||||
const auto currDate{date::year_month_day{daypoint}};
|
||||
|
||||
if (cldMode_ == CldMode::YEAR) {
|
||||
if (y / date::month{1} / 1 == cldYearShift_)
|
||||
@ -360,6 +340,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
else
|
||||
m = 0u;
|
||||
}
|
||||
|
||||
for (auto row{0u}; row < maxRows; ++row) {
|
||||
const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_),
|
||||
std::begin(ml) + ((row + 1) * cldMonCols_));
|
||||
@ -376,8 +357,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
if (line < ml[static_cast<unsigned>(ymTmp.month()) - 1u])
|
||||
os << fmt::format(fmt::runtime(fmtMap_[4]),
|
||||
(line == 2)
|
||||
? date::sys_days{ymTmp / 1}
|
||||
: date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)})
|
||||
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
|
||||
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
|
||||
ymTmp, firstdow, line)}})
|
||||
<< ' ';
|
||||
else
|
||||
os << std::string(cldWnLen_, ' ');
|
||||
@ -386,7 +368,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
|
||||
os << fmt::format(
|
||||
fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"),
|
||||
getCalendarLine(currDate, ymTmp, line, firstdow, &locale_),
|
||||
getCalendarLine(today, ymTmp, line, firstdow, &locale_),
|
||||
(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)));
|
||||
|
||||
// Week numbers on the right
|
||||
@ -396,8 +378,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
os << ' '
|
||||
<< fmt::format(fmt::runtime(fmtMap_[4]),
|
||||
(line == 2)
|
||||
? date::sys_days{ymTmp / 1}
|
||||
: date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)});
|
||||
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
|
||||
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
|
||||
ymTmp, firstdow, line)}});
|
||||
else
|
||||
os << std::string(cldWnLen_, ' ');
|
||||
}
|
||||
@ -420,7 +403,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
|
||||
os << fmt::format( // Apply days format
|
||||
fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())),
|
||||
// Apply today format
|
||||
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", ymd.day()))));
|
||||
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d))));
|
||||
|
||||
if (cldMode_ == CldMode::YEAR)
|
||||
cldYearCached_ = os.str();
|
||||
@ -456,7 +439,7 @@ void waybar::modules::Clock::tz_down() {
|
||||
current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
|
||||
}
|
||||
|
||||
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point* now)
|
||||
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now)
|
||||
-> std::string {
|
||||
if (time_zones_.size() == 1) {
|
||||
return "";
|
||||
@ -470,7 +453,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
|
||||
if (!timezone) {
|
||||
timezone = date::current_zone();
|
||||
}
|
||||
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(*now)};
|
||||
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(now)};
|
||||
os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n';
|
||||
}
|
||||
return os.str();
|
||||
|
@ -122,14 +122,14 @@ auto waybar::modules::Custom::update() -> void {
|
||||
(output_.out.empty() || output_.exit_code != 0)) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
fields_.clear();
|
||||
if (config_["return-type"].asString() == "json") {
|
||||
parseOutputJson();
|
||||
} else {
|
||||
parseOutputRaw();
|
||||
}
|
||||
auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_),
|
||||
fmt::arg("icon", getIcon(percentage_, alt_)),
|
||||
fmt::arg("percentage", percentage_));
|
||||
auto str = fmt::vformat(format_, fields_);
|
||||
|
||||
if (str.empty()) {
|
||||
event_box_.hide();
|
||||
} else {
|
||||
@ -173,6 +173,7 @@ void waybar::modules::Custom::parseOutputRaw() {
|
||||
} else {
|
||||
text_ = line;
|
||||
}
|
||||
fields_.push_back(text_);
|
||||
tooltip_ = line;
|
||||
class_.clear();
|
||||
} else if (i == 1) {
|
||||
@ -189,32 +190,45 @@ void waybar::modules::Custom::parseOutputRaw() {
|
||||
void waybar::modules::Custom::parseOutputJson() {
|
||||
std::istringstream output(output_.out);
|
||||
std::string line;
|
||||
getline(output, line);
|
||||
class_.clear();
|
||||
while (getline(output, line)) {
|
||||
auto parsed = parser_.parse(line);
|
||||
auto parsed = parser_.parse(line);
|
||||
|
||||
// Preserve order so that first "{}" is resolved to "text" for backwards compatability
|
||||
if (parsed["text"].isString()) {
|
||||
auto str = parsed["text"].asString();
|
||||
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||
text_ = Glib::Markup::escape_text(parsed["text"].asString());
|
||||
} else {
|
||||
text_ = parsed["text"].asString();
|
||||
str = Glib::Markup::escape_text(str);
|
||||
}
|
||||
fields_.push_back(str);
|
||||
}
|
||||
|
||||
for (auto const& key : parsed.getMemberNames()) {
|
||||
if (!parsed[key].isString()) continue;
|
||||
auto str = parsed[key].asString();
|
||||
if (config_["escape"].isBool() && config_["escape"].asBool()) {
|
||||
alt_ = Glib::Markup::escape_text(parsed["alt"].asString());
|
||||
} else {
|
||||
alt_ = parsed["alt"].asString();
|
||||
str = Glib::Markup::escape_text(str);
|
||||
}
|
||||
tooltip_ = parsed["tooltip"].asString();
|
||||
if (parsed["class"].isString()) {
|
||||
class_.push_back(parsed["class"].asString());
|
||||
} else if (parsed["class"].isArray()) {
|
||||
for (auto const& c : parsed["class"]) {
|
||||
class_.push_back(c.asString());
|
||||
}
|
||||
fields_.push_back(fmt::arg(key.c_str(), str));
|
||||
}
|
||||
|
||||
tooltip_ = parsed["tooltip"].asString();
|
||||
if (parsed["class"].isString()) {
|
||||
class_.push_back(parsed["class"].asString());
|
||||
} else if (parsed["class"].isArray()) {
|
||||
for (auto const& c : parsed["class"]) {
|
||||
class_.push_back(c.asString());
|
||||
}
|
||||
if (!parsed["percentage"].asString().empty() && parsed["percentage"].isNumeric()) {
|
||||
percentage_ = (int)lround(parsed["percentage"].asFloat());
|
||||
} else {
|
||||
percentage_ = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!parsed["percentage"].asString().empty() && parsed["percentage"].isNumeric()) {
|
||||
percentage_ = (int)lround(parsed["percentage"].asFloat());
|
||||
} else {
|
||||
percentage_ = 0;
|
||||
}
|
||||
|
||||
// Allow overriding icon from json, otherwise use percentage based icon
|
||||
if (parsed["icon"].isNull()) {
|
||||
fields_.push_back(fmt::arg("icon", getIcon(percentage_, alt_)));
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
return "";
|
||||
}
|
||||
response.append(buffer, sizeWritten);
|
||||
} while (sizeWritten == 8192);
|
||||
} while (sizeWritten > 0);
|
||||
|
||||
close(SERVERSOCKET);
|
||||
return response;
|
||||
|
@ -38,7 +38,10 @@ auto Language::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
std::string layoutName = std::string{};
|
||||
if (config_.isMember("format-" + layout_.short_description)) {
|
||||
if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) {
|
||||
const auto propName = "format-" + layout_.short_description + "-" + layout_.variant;
|
||||
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
|
||||
} else if (config_.isMember("format-" + layout_.short_description)) {
|
||||
const auto propName = "format-" + layout_.short_description;
|
||||
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
|
||||
} else {
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include "modules/hyprland/window.hpp"
|
||||
|
||||
#include <glibmm/fileutils.h>
|
||||
#include <glibmm/keyfile.h>
|
||||
#include <glibmm/miscutils.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -8,13 +11,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "util/json.hpp"
|
||||
#include "util/rewrite_string.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: ALabel(config, "window", id, "{}", 0, true), bar_(bar) {
|
||||
: AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) {
|
||||
modulesReady = true;
|
||||
separate_outputs = config["separate-outputs"].asBool();
|
||||
|
||||
@ -24,6 +26,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
|
||||
queryActiveWorkspace();
|
||||
update();
|
||||
dp.emit();
|
||||
|
||||
// register for hyprland ipc
|
||||
gIPC->registerForIPC("activewindow", this);
|
||||
@ -44,28 +47,27 @@ auto Window::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title);
|
||||
std::string window_address = workspace_.last_window;
|
||||
|
||||
if (window_name != last_title_) {
|
||||
if (window_name.empty()) {
|
||||
label_.get_style_context()->add_class("empty");
|
||||
} else {
|
||||
label_.get_style_context()->remove_class("empty");
|
||||
}
|
||||
last_title_ = window_name;
|
||||
}
|
||||
window_data_.title = window_name;
|
||||
|
||||
if (!format_.empty()) {
|
||||
label_.show();
|
||||
label_.set_markup(fmt::format(fmt::runtime(format_),
|
||||
waybar::util::rewriteString(window_name, config_["rewrite"])));
|
||||
label_.set_markup(waybar::util::rewriteString(
|
||||
fmt::format(fmt::runtime(format_), fmt::arg("title", window_name),
|
||||
fmt::arg("initialTitle", window_data_.initial_title),
|
||||
fmt::arg("class", window_data_.class_name),
|
||||
fmt::arg("initialClass", window_data_.initial_class_name)),
|
||||
config_["rewrite"]));
|
||||
} else {
|
||||
label_.hide();
|
||||
}
|
||||
|
||||
setClass("empty", workspace_.windows == 0);
|
||||
setClass("solo", solo_);
|
||||
setClass("fullscreen", fullscreen_);
|
||||
setClass("floating", all_floating_);
|
||||
setClass("swallowing", swallowing_);
|
||||
setClass("fullscreen", fullscreen_);
|
||||
|
||||
if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) {
|
||||
if (bar_.window.get_style_context()->has_class(last_solo_class_)) {
|
||||
@ -80,7 +82,7 @@ auto Window::update() -> void {
|
||||
}
|
||||
last_solo_class_ = solo_class_;
|
||||
|
||||
ALabel::update();
|
||||
AAppIconLabel::update();
|
||||
}
|
||||
|
||||
auto Window::getActiveWorkspace() -> Workspace {
|
||||
@ -102,9 +104,9 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
||||
|
||||
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
|
||||
assert(workspaces.isArray());
|
||||
auto workspace = std::find_if(monitors.begin(), monitors.end(),
|
||||
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
|
||||
[&](Json::Value workspace) { return workspace["id"] == id; });
|
||||
if (workspace == std::end(monitors)) {
|
||||
if (workspace == std::end(workspaces)) {
|
||||
spdlog::warn("No workspace with id {}", id);
|
||||
return Workspace{-1, 0, "", ""};
|
||||
}
|
||||
@ -116,6 +118,13 @@ auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
|
||||
value["lastwindowtitle"].asString()};
|
||||
}
|
||||
|
||||
auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
|
||||
return WindowData{value["floating"].asBool(), value["monitor"].asInt(),
|
||||
value["class"].asString(), value["initialClass"].asString(),
|
||||
value["title"].asString(), value["initialTitle"].asString(),
|
||||
value["fullscreen"].asBool(), !value["grouped"].empty()};
|
||||
}
|
||||
|
||||
void Window::queryActiveWorkspace() {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
@ -126,36 +135,57 @@ void Window::queryActiveWorkspace() {
|
||||
}
|
||||
|
||||
if (workspace_.windows > 0) {
|
||||
const auto clients = gIPC->getSocket1Reply("j/clients");
|
||||
Json::Value json = parser_.parse(clients);
|
||||
assert(json.isArray());
|
||||
auto active_window = std::find_if(json.begin(), json.end(), [&](Json::Value window) {
|
||||
const auto clients = gIPC->getSocket1JsonReply("clients");
|
||||
assert(clients.isArray());
|
||||
auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
|
||||
return window["address"] == workspace_.last_window;
|
||||
});
|
||||
if (active_window == std::end(json)) {
|
||||
if (active_window == std::end(clients)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (workspace_.windows == 1 && !(*active_window)["floating"].asBool()) {
|
||||
solo_class_ = (*active_window)["class"].asString();
|
||||
} else {
|
||||
solo_class_ = "";
|
||||
}
|
||||
window_data_ = WindowData::parse(*active_window);
|
||||
updateAppIconName(window_data_.class_name, window_data_.initial_class_name);
|
||||
std::vector<Json::Value> workspace_windows;
|
||||
std::copy_if(json.begin(), json.end(), std::back_inserter(workspace_windows),
|
||||
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows),
|
||||
[&](Json::Value window) {
|
||||
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
|
||||
});
|
||||
solo_ = 1 == std::count_if(workspace_windows.begin(), workspace_windows.end(),
|
||||
swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(),
|
||||
[&](Json::Value window) { return !window["swallowing"].isNull(); });
|
||||
std::vector<Json::Value> visible_windows;
|
||||
std::copy_if(workspace_windows.begin(), workspace_windows.end(),
|
||||
std::back_inserter(visible_windows),
|
||||
[&](Json::Value window) { return !window["hidden"].asBool(); });
|
||||
solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.end(),
|
||||
[&](Json::Value window) { return !window["floating"].asBool(); });
|
||||
all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(),
|
||||
all_floating_ = std::all_of(visible_windows.begin(), visible_windows.end(),
|
||||
[&](Json::Value window) { return window["floating"].asBool(); });
|
||||
fullscreen_ = (*active_window)["fullscreen"].asBool();
|
||||
fullscreen_ = window_data_.fullscreen;
|
||||
|
||||
// Fullscreen windows look like they are solo
|
||||
if (fullscreen_) {
|
||||
solo_ = true;
|
||||
}
|
||||
|
||||
// Grouped windows have a tab bar and therefore don't look fullscreen or solo
|
||||
if (window_data_.grouped) {
|
||||
fullscreen_ = false;
|
||||
solo_ = false;
|
||||
}
|
||||
|
||||
if (solo_) {
|
||||
solo_class_ = window_data_.class_name;
|
||||
} else {
|
||||
solo_class_ = "";
|
||||
}
|
||||
} else {
|
||||
solo_class_ = "";
|
||||
solo_ = false;
|
||||
window_data_ = WindowData{};
|
||||
all_floating_ = false;
|
||||
swallowing_ = false;
|
||||
fullscreen_ = false;
|
||||
solo_ = false;
|
||||
solo_class_ = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
Json::Value config_format = config["format"];
|
||||
|
||||
format_ = config_format.isString() ? config_format.asString() : "{id}";
|
||||
format_ = config_format.isString() ? config_format.asString() : "{name}";
|
||||
with_icon_ = format_.find("{icon}") != std::string::npos;
|
||||
|
||||
if (with_icon_ && icons_map_.empty()) {
|
||||
@ -28,13 +28,23 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
icons_map_.emplace("", "");
|
||||
}
|
||||
|
||||
auto config_all_outputs = config_["all-outputs"];
|
||||
if (config_all_outputs.isBool()) {
|
||||
all_outputs_ = config_all_outputs.asBool();
|
||||
}
|
||||
|
||||
auto config_show_special = config_["show-special"];
|
||||
if (config_show_special.isBool()) {
|
||||
show_special_ = config_show_special.asBool();
|
||||
}
|
||||
|
||||
box_.set_name("workspaces");
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
event_box_.add(box_);
|
||||
modulesReady = true;
|
||||
if (!gIPC.get()) {
|
||||
if (!gIPC) {
|
||||
gIPC = std::make_unique<IPC>();
|
||||
}
|
||||
|
||||
@ -43,32 +53,38 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
gIPC->registerForIPC("workspace", this);
|
||||
gIPC->registerForIPC("createworkspace", this);
|
||||
gIPC->registerForIPC("destroyworkspace", this);
|
||||
gIPC->registerForIPC("focusedmon", this);
|
||||
gIPC->registerForIPC("moveworkspace", this);
|
||||
gIPC->registerForIPC("openwindow", this);
|
||||
gIPC->registerForIPC("closewindow", this);
|
||||
gIPC->registerForIPC("movewindow", this);
|
||||
gIPC->registerForIPC("urgent", this);
|
||||
}
|
||||
|
||||
auto Workspaces::update() -> void {
|
||||
for (int &workspace_to_remove : workspaces_to_remove_) {
|
||||
for (std::string workspace_to_remove : workspaces_to_remove_) {
|
||||
remove_workspace(workspace_to_remove);
|
||||
}
|
||||
|
||||
workspaces_to_remove_.clear();
|
||||
|
||||
for (int &workspace_to_create : workspaces_to_create_) {
|
||||
for (Json::Value &workspace_to_create : workspaces_to_create_) {
|
||||
create_workspace(workspace_to_create);
|
||||
}
|
||||
|
||||
workspaces_to_create_.clear();
|
||||
|
||||
for (std::unique_ptr<Workspace> &workspace : workspaces_) {
|
||||
workspace->set_active(workspace->id() == active_workspace_id);
|
||||
|
||||
for (auto &workspace : workspaces_) {
|
||||
workspace->set_active(workspace->name() == active_workspace_name_);
|
||||
if (workspace->name() == active_workspace_name_ && workspace.get()->is_urgent()) {
|
||||
workspace->set_urgent(false);
|
||||
}
|
||||
std::string &workspace_icon = icons_map_[""];
|
||||
if (with_icon_) {
|
||||
workspace_icon = workspace->select_icon(icons_map_);
|
||||
}
|
||||
|
||||
workspace->update(format_, workspace_icon);
|
||||
}
|
||||
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
@ -76,35 +92,102 @@ void Workspaces::onEvent(const std::string &ev) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
||||
std::string payload = ev.substr(eventName.size() + 2);
|
||||
|
||||
if (eventName == "workspace") {
|
||||
std::from_chars(payload.data(), payload.data() + payload.size(), active_workspace_id);
|
||||
active_workspace_name_ = payload;
|
||||
|
||||
} else if (eventName == "destroyworkspace") {
|
||||
int deleted_workspace_id;
|
||||
std::from_chars(payload.data(), payload.data() + payload.size(), deleted_workspace_id);
|
||||
workspaces_to_remove_.push_back(deleted_workspace_id);
|
||||
workspaces_to_remove_.push_back(payload);
|
||||
|
||||
} else if (eventName == "createworkspace") {
|
||||
int new_workspace_id;
|
||||
std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id);
|
||||
workspaces_to_create_.push_back(new_workspace_id);
|
||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||
for (Json::Value workspace_json : workspaces_json) {
|
||||
if (workspace_json["name"].asString() == payload &&
|
||||
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||
(show_special() || !workspace_json["name"].asString().starts_with("special"))) {
|
||||
workspaces_to_create_.push_back(workspace_json);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (eventName == "focusedmon") {
|
||||
active_workspace_name_ = payload.substr(payload.find(',') + 1);
|
||||
|
||||
} else if (eventName == "moveworkspace" && !all_outputs()) {
|
||||
std::string workspace = payload.substr(0, payload.find(','));
|
||||
std::string new_output = payload.substr(payload.find(',') + 1);
|
||||
if (bar_.output->name == new_output) { // TODO: implement this better
|
||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||
for (Json::Value workspace_json : workspaces_json) {
|
||||
if (workspace_json["name"].asString() == workspace &&
|
||||
bar_.output->name == workspace_json["monitor"].asString()) {
|
||||
workspaces_to_create_.push_back(workspace_json);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
workspaces_to_remove_.push_back(workspace);
|
||||
}
|
||||
} else if (eventName == "openwindow" || eventName == "closewindow" || eventName == "movewindow") {
|
||||
update_window_count();
|
||||
} else if (eventName == "urgent") {
|
||||
set_urgent_workspace(payload);
|
||||
}
|
||||
|
||||
dp.emit();
|
||||
}
|
||||
|
||||
void Workspaces::create_workspace(int id) {
|
||||
workspaces_.push_back(std::make_unique<Workspace>(id));
|
||||
void Workspaces::update_window_count() {
|
||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||
for (auto &workspace : workspaces_) {
|
||||
auto workspace_json = std::find_if(
|
||||
workspaces_json.begin(), workspaces_json.end(),
|
||||
[&](Json::Value const &x) { return x["name"].asString() == workspace->name(); });
|
||||
if (workspace_json != workspaces_json.end()) {
|
||||
try {
|
||||
workspace->set_windows((*workspace_json)["windows"].asUInt());
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Failed to update window count: {}", e.what());
|
||||
}
|
||||
} else {
|
||||
workspace->set_windows(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Workspaces::create_workspace(Json::Value &value) {
|
||||
// replace the existing persistent workspace if it exists
|
||||
auto workspace = std::find_if(
|
||||
workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr<Workspace> const &x) {
|
||||
auto name = value["name"].asString();
|
||||
return x->is_persistent() &&
|
||||
((name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name());
|
||||
});
|
||||
if (workspace != workspaces_.end()) {
|
||||
// replace workspace, but keep persistent flag
|
||||
workspaces_.erase(workspace);
|
||||
value["persistent"] = true;
|
||||
}
|
||||
|
||||
// create new workspace
|
||||
workspaces_.emplace_back(std::make_unique<Workspace>(value));
|
||||
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
||||
box_.pack_start(new_workspace_button, false, false);
|
||||
sort_workspaces();
|
||||
new_workspace_button.show_all();
|
||||
}
|
||||
|
||||
void Workspaces::remove_workspace(int id) {
|
||||
void Workspaces::remove_workspace(std::string name) {
|
||||
auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||
[&](std::unique_ptr<Workspace> &x) { return x->id() == id; });
|
||||
[&](std::unique_ptr<Workspace> &x) { return x->name() == name; });
|
||||
|
||||
if (workspace == workspaces_.end()) {
|
||||
spdlog::warn("Can't find workspace with id {}", id);
|
||||
// happens when a workspace on another monitor is destroyed
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*workspace)->is_persistent()) {
|
||||
// don't remove persistent workspaces, create_workspace will take care of replacement
|
||||
return;
|
||||
}
|
||||
|
||||
@ -112,19 +195,89 @@ void Workspaces::remove_workspace(int id) {
|
||||
workspaces_.erase(workspace);
|
||||
}
|
||||
|
||||
void Workspaces::fill_persistent_workspaces() {
|
||||
if (config_["persistent_workspaces"].isObject() && !all_outputs()) {
|
||||
const Json::Value persistent_workspaces = config_["persistent_workspaces"];
|
||||
const std::vector<std::string> keys = persistent_workspaces.getMemberNames();
|
||||
|
||||
for (const std::string &key : keys) {
|
||||
const Json::Value &value = persistent_workspaces[key];
|
||||
if (value.isInt()) {
|
||||
// value is a number => create that many workspaces for this monitor
|
||||
// only add if either:
|
||||
// 1. key is "*" and this monitor is not already defined in the config
|
||||
// 2. key is the current monitor name
|
||||
if ((key == "*" && std::find(keys.begin(), keys.end(), bar_.output->name) == keys.end()) ||
|
||||
key == bar_.output->name) {
|
||||
int amount = value.asInt();
|
||||
spdlog::debug("Creating {} persistent workspaces for monitor {}", amount,
|
||||
bar_.output->name);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
persistent_workspaces_to_create_.emplace_back(
|
||||
std::to_string(monitor_id_ * amount + i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (value.isArray() && !value.empty()) {
|
||||
// value is an array => key is a workspace name
|
||||
// values are monitor names this workspace should be shown on
|
||||
for (const Json::Value &monitor : value) {
|
||||
if (monitor.isString() && monitor.asString() == bar_.output->name) {
|
||||
persistent_workspaces_to_create_.emplace_back(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Workspaces::create_persistent_workspaces() {
|
||||
for (const std::string &workspace_name : persistent_workspaces_to_create_) {
|
||||
Json::Value new_workspace;
|
||||
try {
|
||||
// numbered persistent workspaces get the name as ID
|
||||
new_workspace["id"] = workspace_name == "special" ? -99 : std::stoi(workspace_name);
|
||||
} catch (const std::exception &e) {
|
||||
// named persistent workspaces start with ID=0
|
||||
new_workspace["id"] = 0;
|
||||
}
|
||||
new_workspace["name"] = workspace_name;
|
||||
new_workspace["monitor"] = bar_.output->name;
|
||||
new_workspace["windows"] = 0;
|
||||
new_workspace["persistent"] = true;
|
||||
|
||||
create_workspace(new_workspace);
|
||||
}
|
||||
}
|
||||
|
||||
void Workspaces::init() {
|
||||
const auto activeWorkspace = WorkspaceDto::parse(gIPC->getSocket1JsonReply("activeworkspace"));
|
||||
active_workspace_id = activeWorkspace.id;
|
||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||
for (const Json::Value &workspace_json : workspaces_json) {
|
||||
workspaces_.push_back(
|
||||
std::make_unique<Workspace>(Workspace(WorkspaceDto::parse(workspace_json))));
|
||||
active_workspace_name_ = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
||||
|
||||
// get monitor ID from name (used by persistent workspaces)
|
||||
monitor_id_ = 0;
|
||||
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||
auto current_monitor = std::find_if(
|
||||
monitors.begin(), monitors.end(),
|
||||
[this](const Json::Value &m) { return m["name"].asString() == bar_.output->name; });
|
||||
if (current_monitor == monitors.end()) {
|
||||
spdlog::error("Monitor '{}' does not have an ID? Using 0", bar_.output->name);
|
||||
} else {
|
||||
monitor_id_ = (*current_monitor)["id"].asInt();
|
||||
}
|
||||
|
||||
for (auto &workspace : workspaces_) {
|
||||
box_.pack_start(workspace->button(), false, false);
|
||||
fill_persistent_workspaces();
|
||||
create_persistent_workspaces();
|
||||
|
||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||
for (Json::Value workspace_json : workspaces_json) {
|
||||
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||
(!workspace_json["name"].asString().starts_with("special") || show_special()))
|
||||
create_workspace(workspace_json);
|
||||
}
|
||||
|
||||
update_window_count();
|
||||
|
||||
sort_workspaces();
|
||||
|
||||
dp.emit();
|
||||
@ -136,19 +289,33 @@ Workspaces::~Workspaces() {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
}
|
||||
|
||||
WorkspaceDto WorkspaceDto::parse(const Json::Value &value) {
|
||||
return WorkspaceDto{value["id"].asInt()};
|
||||
}
|
||||
Workspace::Workspace(const Json::Value &workspace_data)
|
||||
: id_(workspace_data["id"].asInt()),
|
||||
name_(workspace_data["name"].asString()),
|
||||
output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
||||
windows_(workspace_data["windows"].asInt()),
|
||||
active_(true) {
|
||||
if (name_.starts_with("name:")) {
|
||||
name_ = name_.substr(5);
|
||||
} else if (name_.starts_with("special")) {
|
||||
name_ = id_ == -99 ? name_ : name_.substr(8);
|
||||
is_special_ = true;
|
||||
}
|
||||
|
||||
Workspace::Workspace(WorkspaceDto dto) : Workspace(dto.id){};
|
||||
if (workspace_data.isMember("persistent")) {
|
||||
is_persistent_ = workspace_data["persistent"].asBool();
|
||||
}
|
||||
|
||||
button_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||
button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
|
||||
false);
|
||||
|
||||
Workspace::Workspace(int id) : id_(id) {
|
||||
button_.set_relief(Gtk::RELIEF_NONE);
|
||||
content_.set_center_widget(label_);
|
||||
button_.add(content_);
|
||||
};
|
||||
|
||||
void add_or_remove_class(Glib::RefPtr<Gtk::StyleContext> context, bool condition,
|
||||
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
||||
const std::string &class_name) {
|
||||
if (condition) {
|
||||
context->add_class(class_name);
|
||||
@ -158,17 +325,48 @@ void add_or_remove_class(Glib::RefPtr<Gtk::StyleContext> context, bool condition
|
||||
}
|
||||
|
||||
void Workspace::update(const std::string &format, const std::string &icon) {
|
||||
Glib::RefPtr<Gtk::StyleContext> style_context = button_.get_style_context();
|
||||
auto style_context = button_.get_style_context();
|
||||
add_or_remove_class(style_context, active(), "active");
|
||||
add_or_remove_class(style_context, is_special(), "special");
|
||||
add_or_remove_class(style_context, is_empty(), "persistent");
|
||||
add_or_remove_class(style_context, is_urgent(), "urgent");
|
||||
|
||||
label_.set_markup(
|
||||
fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("icon", icon)));
|
||||
label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
||||
fmt::arg("name", name()), fmt::arg("icon", icon)));
|
||||
}
|
||||
|
||||
void Workspaces::sort_workspaces() {
|
||||
std::sort(workspaces_.begin(), workspaces_.end(),
|
||||
[](std::unique_ptr<Workspace> &lhs, std::unique_ptr<Workspace> &rhs) {
|
||||
return lhs->id() < rhs->id();
|
||||
[](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
||||
// normal -> named persistent -> named -> special -> named special
|
||||
|
||||
// both normal (includes numbered persistent) => sort by ID
|
||||
if (a->id() > 0 && b->id() > 0) {
|
||||
return a->id() < b->id();
|
||||
}
|
||||
|
||||
// one normal, one special => normal first
|
||||
if ((a->is_special()) ^ (b->is_special())) {
|
||||
return b->is_special();
|
||||
}
|
||||
|
||||
// only one normal, one named
|
||||
if ((a->id() > 0) ^ (b->id() > 0)) {
|
||||
return a->id() > 0;
|
||||
}
|
||||
|
||||
// both special
|
||||
if (a->is_special() && b->is_special()) {
|
||||
// if one is -99 => put it last
|
||||
if (a->id() == -99 || b->id() == -99) {
|
||||
return b->id() == -99;
|
||||
}
|
||||
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1)
|
||||
return a->name() < b->name();
|
||||
}
|
||||
|
||||
// sort non-special named workspaces by name (ID <= -1377)
|
||||
return a->name() < b->name();
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < workspaces_.size(); ++i) {
|
||||
@ -184,16 +382,67 @@ std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_ma
|
||||
}
|
||||
}
|
||||
|
||||
auto named_icon_it = icons_map.find(std::to_string(id()));
|
||||
if (is_special()) {
|
||||
auto special_icon_it = icons_map.find("special");
|
||||
if (special_icon_it != icons_map.end()) {
|
||||
return special_icon_it->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto named_icon_it = icons_map.find(name());
|
||||
if (named_icon_it != icons_map.end()) {
|
||||
return named_icon_it->second;
|
||||
}
|
||||
|
||||
if (is_persistent()) {
|
||||
auto persistent_icon_it = icons_map.find("persistent");
|
||||
if (persistent_icon_it != icons_map.end()) {
|
||||
return persistent_icon_it->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto default_icon_it = icons_map.find("default");
|
||||
if (default_icon_it != icons_map.end()) {
|
||||
return default_icon_it->second;
|
||||
}
|
||||
|
||||
return icons_map[""];
|
||||
}
|
||||
|
||||
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
|
||||
try {
|
||||
if (id() > 0) { // normal or numbered persistent
|
||||
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
||||
} else if (!is_special()) { // named
|
||||
gIPC->getSocket1Reply("dispatch workspace name:" + name());
|
||||
} else if (id() != -99) { // named special
|
||||
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
|
||||
} else { // special
|
||||
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
||||
}
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Workspaces::set_urgent_workspace(std::string windowaddress) {
|
||||
const Json::Value clients_json = gIPC->getSocket1JsonReply("clients");
|
||||
int workspace_id;
|
||||
|
||||
for (Json::Value client_json : clients_json) {
|
||||
if (client_json["address"].asString().ends_with(windowaddress)) {
|
||||
workspace_id = client_json["workspace"]["id"].asInt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto workspace =
|
||||
std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||
[&](std::unique_ptr<Workspace> &x) { return x->id() == workspace_id; });
|
||||
if (workspace->get() != nullptr) {
|
||||
workspace->get()->set_urgent();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
@ -142,6 +142,21 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||
}
|
||||
}
|
||||
|
||||
auto keys = config_["binding-keys"];
|
||||
if (keys.isArray()) {
|
||||
for (const auto& key : keys) {
|
||||
if (key.isInt()) {
|
||||
binding_keys.insert(key.asInt());
|
||||
} else {
|
||||
spdlog::warn("Cannot read key binding {} as int.", key.asString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding_keys.insert(KEY_CAPSLOCK);
|
||||
binding_keys.insert(KEY_NUMLOCK);
|
||||
binding_keys.insert(KEY_SCROLLLOCK);
|
||||
}
|
||||
|
||||
DIR* dev_dir = opendir(devices_path_.c_str());
|
||||
if (dev_dir == nullptr) {
|
||||
throw errno_error(errno, "Failed to open " + devices_path_);
|
||||
@ -171,14 +186,8 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
||||
auto state = libinput_event_keyboard_get_key_state(keyboard_event);
|
||||
if (state == LIBINPUT_KEY_STATE_RELEASED) {
|
||||
uint32_t key = libinput_event_keyboard_get_key(keyboard_event);
|
||||
switch (key) {
|
||||
case KEY_CAPSLOCK:
|
||||
case KEY_NUMLOCK:
|
||||
case KEY_SCROLLLOCK:
|
||||
dp.emit();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (binding_keys.contains(key)) {
|
||||
dp.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,13 @@ auto waybar::modules::Clock::update() -> void {
|
||||
tzset(); // Update timezone information
|
||||
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);
|
||||
auto text = fmt::format(fmt::runtime(format_), localtime);
|
||||
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);
|
||||
auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime);
|
||||
label_.set_tooltip_text(tooltip_text);
|
||||
} else {
|
||||
label_.set_tooltip_text(text);
|
||||
|
@ -22,7 +22,7 @@ struct fmt::formatter<Glib::VariantBase> : formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const Glib::VariantBase& value, FormatContext& ctx) {
|
||||
if (is_printable(value)) {
|
||||
return formatter<std::string>::format(value.print(), ctx);
|
||||
return formatter<std::string>::format(static_cast<std::string>(value.print()), ctx);
|
||||
} else {
|
||||
return formatter<std::string>::format(value.get_type_string(), ctx);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "modules/sway/ipc/client.hpp"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@ -17,12 +18,16 @@ Ipc::~Ipc() {
|
||||
|
||||
if (fd_ > 0) {
|
||||
// To fail the IPC header
|
||||
write(fd_, "close-sway-ipc", 14);
|
||||
if (write(fd_, "close-sway-ipc", 14) == -1) {
|
||||
spdlog::error("Failed to close sway IPC");
|
||||
}
|
||||
close(fd_);
|
||||
fd_ = -1;
|
||||
}
|
||||
if (fd_event_ > 0) {
|
||||
write(fd_event_, "close-sway-ipc", 14);
|
||||
if (write(fd_event_, "close-sway-ipc", 14) == -1) {
|
||||
spdlog::error("Failed to close sway IPC event handler");
|
||||
}
|
||||
close(fd_event_);
|
||||
fd_event_ = -1;
|
||||
}
|
||||
|
@ -17,13 +17,7 @@
|
||||
namespace waybar::modules::sway {
|
||||
|
||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
||||
// Icon size
|
||||
if (config_["icon-size"].isUInt()) {
|
||||
app_icon_size_ = config["icon-size"].asUInt();
|
||||
}
|
||||
image_.set_pixel_size(app_icon_size_);
|
||||
|
||||
: AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
||||
ipc_.subscribe(R"(["window","workspace"])");
|
||||
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
|
||||
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
|
||||
@ -49,7 +43,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
|
||||
auto output = payload["output"].isString() ? payload["output"].asString() : "";
|
||||
std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) =
|
||||
getFocusedNode(payload["nodes"], output);
|
||||
updateAppIconName();
|
||||
updateAppIconName(app_id_, app_class_);
|
||||
dp.emit();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Window: {}", e.what());
|
||||
@ -57,105 +51,6 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> getDesktopFilePath(const std::string& app_id,
|
||||
const std::string& app_class) {
|
||||
const auto data_dirs = Glib::get_system_data_dirs();
|
||||
for (const auto& data_dir : data_dirs) {
|
||||
const auto data_app_dir = data_dir + "applications/";
|
||||
auto desktop_file_path = data_app_dir + app_id + ".desktop";
|
||||
if (std::filesystem::exists(desktop_file_path)) {
|
||||
return desktop_file_path;
|
||||
}
|
||||
if (!app_class.empty()) {
|
||||
desktop_file_path = data_app_dir + app_class + ".desktop";
|
||||
if (std::filesystem::exists(desktop_file_path)) {
|
||||
return desktop_file_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Glib::ustring> getIconName(const std::string& app_id, const std::string& app_class) {
|
||||
const auto desktop_file_path = getDesktopFilePath(app_id, app_class);
|
||||
if (!desktop_file_path.has_value()) {
|
||||
// Try some heuristics to find a matching icon
|
||||
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(app_id)) {
|
||||
return app_id;
|
||||
}
|
||||
|
||||
const auto app_id_desktop = app_id + "-desktop";
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(app_id_desktop)) {
|
||||
return app_id_desktop;
|
||||
}
|
||||
|
||||
const auto to_lower = [](const std::string& str) {
|
||||
auto str_cpy = str;
|
||||
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return str;
|
||||
};
|
||||
|
||||
const auto first_space = app_id.find_first_of(' ');
|
||||
if (first_space != std::string::npos) {
|
||||
const auto first_word = to_lower(app_id.substr(0, first_space));
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
|
||||
return first_word;
|
||||
}
|
||||
}
|
||||
|
||||
const auto first_dash = app_id.find_first_of('-');
|
||||
if (first_dash != std::string::npos) {
|
||||
const auto first_word = to_lower(app_id.substr(0, first_dash));
|
||||
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
|
||||
return first_word;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
Glib::KeyFile desktop_file;
|
||||
desktop_file.load_from_file(desktop_file_path.value());
|
||||
return desktop_file.get_string("Desktop Entry", "Icon");
|
||||
} catch (Glib::FileError& error) {
|
||||
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
||||
error.what().c_str());
|
||||
} catch (Glib::KeyFileError& error) {
|
||||
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
|
||||
error.what().c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Window::updateAppIconName() {
|
||||
if (!iconEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto icon_name = getIconName(app_id_, app_class_);
|
||||
if (icon_name.has_value()) {
|
||||
app_icon_name_ = icon_name.value();
|
||||
} else {
|
||||
app_icon_name_ = "";
|
||||
}
|
||||
update_app_icon_ = true;
|
||||
}
|
||||
|
||||
void Window::updateAppIcon() {
|
||||
if (update_app_icon_) {
|
||||
update_app_icon_ = false;
|
||||
if (app_icon_name_.empty()) {
|
||||
image_.set_visible(false);
|
||||
} else {
|
||||
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
|
||||
image_.set_visible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Window::update() -> void {
|
||||
spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_,
|
||||
floating_count_);
|
||||
@ -210,7 +105,7 @@ auto Window::update() -> void {
|
||||
updateAppIcon();
|
||||
|
||||
// Call parent update
|
||||
AIconLabel::update();
|
||||
AAppIconLabel::update();
|
||||
}
|
||||
|
||||
void Window::setClass(std::string classname, bool enable) {
|
||||
@ -250,6 +145,40 @@ std::pair<int, int> leafNodesInWorkspace(const Json::Value& node) {
|
||||
return {sum, floating_sum};
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const Json::Value>> getSingleChildNode(
|
||||
const Json::Value& node) {
|
||||
auto const& nodes = node["nodes"];
|
||||
if (nodes.empty()) {
|
||||
if (node["type"].asString() == "workspace")
|
||||
return {};
|
||||
else if (node["type"].asString() == "floating_con") {
|
||||
return {};
|
||||
} else {
|
||||
return {std::cref(node)};
|
||||
}
|
||||
}
|
||||
auto it = std::cbegin(nodes);
|
||||
if (it == std::cend(nodes)) {
|
||||
return {};
|
||||
}
|
||||
auto const& child = *it;
|
||||
++it;
|
||||
if (it != std::cend(nodes)) {
|
||||
return {};
|
||||
}
|
||||
return {getSingleChildNode(child)};
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string, std::string> getWindowInfo(const Json::Value& node) {
|
||||
const auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
||||
: node["window_properties"]["instance"].asString();
|
||||
const auto app_class = node["window_properties"]["class"].isString()
|
||||
? node["window_properties"]["class"].asString()
|
||||
: "";
|
||||
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
|
||||
return {app_id, app_class, shell};
|
||||
}
|
||||
|
||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||
gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_,
|
||||
const Bar& bar_, Json::Value& parentWorkspace,
|
||||
@ -286,12 +215,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu
|
||||
// found node
|
||||
spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name,
|
||||
output, node["name"].asString());
|
||||
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
||||
: node["window_properties"]["instance"].asString();
|
||||
const auto app_class = node["window_properties"]["class"].isString()
|
||||
? node["window_properties"]["class"].asString()
|
||||
: "";
|
||||
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
|
||||
const auto [app_id, app_class, shell] = getWindowInfo(node);
|
||||
int nb = node.size();
|
||||
int floating_count = 0;
|
||||
std::string workspace_layout = "";
|
||||
@ -331,15 +255,24 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu
|
||||
std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent);
|
||||
// using an empty string as default ensures that no window depending styles are set due to the
|
||||
// checks above for !name.empty()
|
||||
std::string app_id = "";
|
||||
std::string app_class = "";
|
||||
std::string workspace_layout = "";
|
||||
if (all_leaf_nodes.first == 1) {
|
||||
const auto single_child = getSingleChildNode(immediateParent);
|
||||
if (single_child.has_value()) {
|
||||
std::tie(app_id, app_class, workspace_layout) = getWindowInfo(single_child.value());
|
||||
}
|
||||
}
|
||||
return {all_leaf_nodes.first,
|
||||
all_leaf_nodes.second,
|
||||
0,
|
||||
(all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0)
|
||||
? config_["offscreen-css-text"].asString()
|
||||
: "",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
app_id,
|
||||
app_class,
|
||||
workspace_layout,
|
||||
immediateParent["layout"].asString()};
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||
: AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
|
||||
bar_(bar),
|
||||
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
if (config["format-icons"]["high-priority-named"].isArray()) {
|
||||
for (auto &it : config["format-icons"]["high-priority-named"]) {
|
||||
high_priority_named_.push_back(it.asString());
|
||||
}
|
||||
}
|
||||
box_.set_name("workspaces");
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
@ -279,9 +284,24 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
|
||||
}
|
||||
|
||||
std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) {
|
||||
std::vector<std::string> keys = {"urgent", "focused", name, "visible", "default"};
|
||||
std::vector<std::string> keys = {"high-priority-named", "urgent", "focused", name, "default"};
|
||||
for (auto const &key : keys) {
|
||||
if (key == "focused" || key == "visible" || key == "urgent") {
|
||||
if (key == "high-priority-named") {
|
||||
auto it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(),
|
||||
[&](const std::string &member) { return member == name; });
|
||||
if (it != high_priority_named_.end()) {
|
||||
return config_["format-icons"][name].asString();
|
||||
}
|
||||
|
||||
it = std::find_if(high_priority_named_.begin(), high_priority_named_.end(),
|
||||
[&](const std::string &member) {
|
||||
return trimWorkspaceName(member) == trimWorkspaceName(name);
|
||||
});
|
||||
if (it != high_priority_named_.end()) {
|
||||
return config_["format-icons"][trimWorkspaceName(name)].asString();
|
||||
}
|
||||
}
|
||||
if (key == "focused" || key == "urgent") {
|
||||
if (config_["format-icons"][key].isString() && node[key].asBool()) {
|
||||
return config_["format-icons"][key].asString();
|
||||
}
|
||||
@ -327,7 +347,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!config_["warp-on-scroll"].asBool()) {
|
||||
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
|
||||
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none"));
|
||||
}
|
||||
try {
|
||||
@ -335,7 +355,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Workspaces: {}", e.what());
|
||||
}
|
||||
if (!config_["warp-on-scroll"].asBool()) {
|
||||
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
|
||||
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container"));
|
||||
}
|
||||
return true;
|
||||
|
@ -517,7 +517,7 @@ void Task::handle_closed() {
|
||||
bool Task::handle_clicked(GdkEventButton *bt) {
|
||||
/* filter out additional events for double/triple clicks */
|
||||
if (bt->type == GDK_BUTTON_PRESS) {
|
||||
/* save where the button press ocurred in case it becomes a drag */
|
||||
/* save where the button press occurred in case it becomes a drag */
|
||||
drag_start_button = bt->button;
|
||||
drag_start_x = bt->x;
|
||||
drag_start_y = bt->y;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "util/prepare_for_sleep.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace {
|
||||
class PrepareForSleep {
|
||||
@ -9,7 +10,7 @@ class PrepareForSleep {
|
||||
GError *error = NULL;
|
||||
login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
|
||||
if (!login1_connection) {
|
||||
throw std::runtime_error("Unable to connect to the SYSTEM Bus!...");
|
||||
spdlog::warn("Unable to connect to the SYSTEM Bus!...");
|
||||
} else {
|
||||
login1_id = g_dbus_connection_signal_subscribe(
|
||||
login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager",
|
||||
|
@ -1,10 +1,10 @@
|
||||
[wrap-file]
|
||||
directory = Catch2-3.3.2
|
||||
source_url = https://github.com/catchorg/Catch2/archive/v3.3.2.tar.gz
|
||||
source_filename = Catch2-3.3.2.tar.gz
|
||||
source_hash = 8361907f4d9bff3ae7c1edb027f813659f793053c99b67837a0c0375f065bae2
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.3.2-1/Catch2-3.3.2.tar.gz
|
||||
wrapdb_version = 3.3.2-1
|
||||
directory = Catch2-3.4.0
|
||||
source_url = https://github.com/catchorg/Catch2/archive/v3.4.0.tar.gz
|
||||
source_filename = Catch2-3.4.0.tar.gz
|
||||
source_hash = 122928b814b75717316c71af69bd2b43387643ba076a6ec16e7882bfb2dfacbb
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.4.0-1/Catch2-3.4.0.tar.gz
|
||||
wrapdb_version = 3.4.0-1
|
||||
|
||||
[provide]
|
||||
catch2 = catch2_dep
|
||||
|
@ -1,7 +1,7 @@
|
||||
[wrap-file]
|
||||
directory = cava-0.8.4
|
||||
source_url = https://github.com/LukashonakV/cava/archive/0.8.4.tar.gz
|
||||
source_filename = cava-0.8.4.tar.gz
|
||||
source_hash = 523353f446570277d40b8e1efb84468d70fdec53e1356a555c14bf466557a3ed
|
||||
directory = cava-0.8.5
|
||||
source_url = https://github.com/LukashonakV/cava/archive/0.8.5.tar.gz
|
||||
source_filename = cava-0.8.5.tar.gz
|
||||
source_hash = 9ce3df7d374dc83ed0704fe3caef5e00600ce061d85608aad4142d2c59aa4647
|
||||
[provide]
|
||||
cava = cava_dep
|
||||
|
@ -1,12 +1,13 @@
|
||||
[wrap-file]
|
||||
directory = fmt-8.1.1
|
||||
source_url = https://github.com/fmtlib/fmt/archive/8.1.1.tar.gz
|
||||
source_filename = fmt-8.1.1.tar.gz
|
||||
source_hash = 3d794d3cf67633b34b2771eb9f073bde87e846e0d395d254df7b211ef1ec7346
|
||||
patch_filename = fmt_8.1.1-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_8.1.1-1/get_patch
|
||||
patch_hash = 6035a67c7a8c90bed74c293c7265c769f47a69816125f7566bccb8e2543cee5e
|
||||
directory = fmt-9.1.0
|
||||
source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz
|
||||
source_filename = fmt-9.1.0.tar.gz
|
||||
source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2
|
||||
patch_filename = fmt_9.1.0-2_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-2/get_patch
|
||||
patch_hash = 23e8c4829f3e63f509b5643fe6bb87cbed39eae9594c451b338475d14d051967
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_9.1.0-2/fmt-9.1.0.tar.gz
|
||||
wrapdb_version = 9.1.0-2
|
||||
|
||||
[provide]
|
||||
fmt = fmt_dep
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
[wrap-file]
|
||||
directory = spdlog-1.10.0
|
||||
source_url = https://github.com/gabime/spdlog/archive/v1.10.0.tar.gz
|
||||
source_filename = v1.10.0.tar.gz
|
||||
source_hash = 697f91700237dbae2326b90469be32b876b2b44888302afbc7aceb68bcfe8224
|
||||
patch_filename = spdlog_1.10.0-3_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.10.0-3/get_patch
|
||||
patch_hash = 5bb07b4af1e971817d4b886efbe077aaf6c36d72d3d7e461bbcf6631f3725704
|
||||
wrapdb_version = 1.10.0-3
|
||||
directory = spdlog-1.11.0
|
||||
source_url = https://github.com/gabime/spdlog/archive/v1.11.0.tar.gz
|
||||
source_filename = v1.11.0.tar.gz
|
||||
source_hash = ca5cae8d6cac15dae0ec63b21d6ad3530070650f68076f3a4a862ca293a858bb
|
||||
patch_filename = spdlog_1.11.0-2_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.11.0-2/get_patch
|
||||
patch_hash = db1364fe89502ac67f245a6c8c51290a52afd74a51eed26fa9ecb5b3443df57a
|
||||
wrapdb_version = 1.11.0-2
|
||||
|
||||
[provide]
|
||||
spdlog = spdlog_dep
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user