Compare commits

...

96 Commits

Author SHA1 Message Date
a194755b4f
fixup! modules/custom: Allow json fields as format specifiers 2023-09-07 02:49:43 +02:00
165814a57f
modules/custom: Allow json fields as format specifiers 2023-08-30 07:57:11 +02:00
Alexis Rouillard
b665843085
Merge pull request #2429 from khaneliman/hyprland-urgent
Hyprland urgent class support
2023-08-24 17:00:40 +02:00
Austin Horstman
ee4fbc58f7
hyprland add urgent ipc support 2023-08-23 12:52:29 -05:00
Austin Horstman
3081b0c576
flake lock update 2023-08-23 12:14:35 -05:00
Alexis Rouillard
b7a527b122
Merge pull request #2424 from asas1asas200/feat-keyboard-bindingkey 2023-08-20 09:40:36 +02:00
asas1asas200
54a6668846 feat(keyboard-state): add binding-keys options 2023-08-20 08:33:34 +08:00
Alexis Rouillard
ed0f7453c9
Merge pull request #2420 from Alexays/chore-subprojects
chore: update subprojects
2023-08-18 10:39:15 +02:00
Alex
19fe929d1f chore: update subprojects 2023-08-18 09:48:03 +02:00
Alexis Rouillard
0a28b50a8c
Merge pull request #2413 from PucklaJ/bluetooth-no-controller
Add format-no-controller to bluetooth module and fix it still being visible if the format string is empty
2023-08-16 17:08:48 +02:00
PucklaJ
70bc318a01 Fix bluetooth module still being visible if format is empty 2023-08-16 16:30:59 +02:00
PucklaJ
22817089db Add no-controller format to bluetooth module 2023-08-16 15:34:06 +02:00
Alexis Rouillard
aecd80702e
Merge pull request #2412 from jpalus/simpleclock-fmt-compile-fix
Fix simpleclock module compilation with recent fmt
2023-08-16 14:35:39 +02:00
Jan Palus
abd7a0cf25
Fix simpleclock module compilation with recent fmt
both `format_` and `tooltip_format` are dynamically provided formats so
wrap them in `fmt::runtime()`
2023-08-16 14:32:48 +02:00
Alex
e30fba0b8f chore: 0.9.22 2023-08-16 13:34:05 +02:00
Alex
b5ea14c896 revert: release event 2023-08-16 13:31:24 +02:00
Alexis Rouillard
5b33a5917c
Merge pull request #2404 from eltociear/master-1
fix: typo in taskbar.cpp
2023-08-16 08:55:33 +02:00
Alexis Rouillard
66b71cc857
Merge pull request #2409 from jpalus/fmt-cast-ustring-to-string 2023-08-15 22:35:46 +02:00
Jan Palus
c8237437d2
Explicitly cast ustring to string when passing to fmt
don't rely on implicit conversion which is no longer present in fmt
10.1.0

Fixes #2403
2023-08-15 20:57:07 +02:00
Ikko Eltociear Ashimine
57544fe694
fix: typo in taskbar.cpp
ocurred -> occurred
2023-08-15 00:45:09 +09:00
Alex
e90c66a102 chore: 0.9.21 2023-08-14 15:33:52 +02:00
Alex
db1d859881 fix: lint 2023-08-14 14:20:08 +02:00
Alexis Rouillard
cc961d40b3
Merge pull request #2402 from frankebel/master
docs: fix Arch repository name
2023-08-14 13:28:54 +02:00
Frank Ebel
29cebaa0a7
docs: fix Arch repository name 2023-08-14 12:42:33 +02:00
Alexis Rouillard
2c7bbe9852
Merge pull request #2393 from zjeffer/hyprland/fixes
Hyprland/workspaces: use named icons instead of searching by id
2023-08-14 11:46:55 +02:00
Alexis Rouillard
eb74623b7d
Merge pull request #2399 from KosmX/click_release_action
Button release action
2023-08-14 11:46:27 +02:00
Alexis Rouillard
b9260575e5
Merge pull request #2401 from zjeffer/master
Updated hyprland-workspaces manpage
2023-08-14 11:23:35 +02:00
zjeffer
8cf676176d Updated hyprland-workspaces manpage 2023-08-14 11:17:49 +02:00
KosmX
3db5673e70
register key hander 2023-08-13 18:48:23 +02:00
KosmX
46f5034030
Add button release events to eventMap 2023-08-13 18:20:43 +02:00
zjeffer
6f7e7c5199 formatting 2023-08-10 20:37:42 +02:00
zjeffer
2ba11e8401 Hyprland/workspaces: use named icons instead of searching by id 2023-08-10 20:09:30 +02:00
Alexis Rouillard
b084bf721e
Merge pull request #2377 from marshallmoats/master
Change default workspace format
2023-08-09 08:16:52 +02:00
Marshall Moats
e40860c3e9 keep id 2023-08-08 17:17:58 -05:00
Marshall Moats
2bcd0eb09f
Change default workspace format 2023-08-02 16:02:50 -05:00
Alexis Rouillard
94c34a29c4
Merge pull request #2370 from spikespaz/nix-fix-overlay
flake: fix infrec in package overlay
2023-08-02 08:41:39 +02:00
Alexis Rouillard
7fb37c0069
Merge pull request #2373 from marshallmoats/master 2023-08-02 08:35:10 +02:00
Marshall Moats
ce2ede70f2 Fix logical errors, formatting error 2023-08-01 15:55:37 -05:00
Jacob Birkett
b91adc9f29 flake: fix infrec in package overlay 2023-07-31 09:37:33 -07:00
Alexis Rouillard
86b3e456e1
Merge pull request #2341 from zjeffer/hyprland/workspaces
Persistent workspaces in hyprland/workspaces
2023-07-31 08:34:30 +02:00
Alexis Rouillard
1eb4684b60
Merge pull request #2367 from diederikdehaas/more-manpage-fixes
man: Fix several whitespace formatting issues
2023-07-31 08:33:09 +02:00
Diederik de Haas
a3904ff039
man: Fix several whitespace formatting issues
Fix the following whitespace formatting issues:
- Indentation in scdoc source files should be done with tabs.
- Lines where there (clearly) should be a line break, need to have '++'
  at the end, but several were missing them.
- The scdoc manual (clearly) states that lines should be hard wrapped
  at 80 columns, but when line(s) are indented, that causes rendering
  issues. So lines where a line break was not clearly intended or
  clearly not intended, have been put onto 1 line to circumvent the
  rendering issue.

Link: https://lists.sr.ht/~sircmpwn/public-inbox/%3C8251560.T7Z3S40VBb%40bagend%3E
2023-07-30 17:37:30 +02:00
Alexis Rouillard
88828265c0
Merge pull request #2366 from zjeffer/fix/build-warnings
Fixed sway IPC compile warnings
2023-07-30 11:52:11 +02:00
zjeffer
600653538b Persistent workspaces in hyprland/workspaces 2023-07-30 11:47:37 +02:00
zjeffer
28635c1f6d Fixed sway IPC compile warnings 2023-07-30 11:43:34 +02:00
Alexis Rouillard
b09f6cc731
Merge pull request #2365 from diederikdehaas/manpage-fixes
Manpage fixes
2023-07-30 10:07:03 +02:00
Diederik de Haas
c8910901e5
man: Make NAME-ing consistent
Tools like `apropos` and `whatis` use the NAME section to generate their
database, so make sure every manpage has it.
Also make sure they all have a brief description and make it consistent
across the manpages.
2023-07-30 10:00:02 +02:00
Diederik de Haas
6b7fd36863
man/waybar-clock: Fix typo and formatting
typo: Adressed -> Addressed
formatting: Add missing spaces
2023-07-30 09:56:19 +02:00
Alexis Rouillard
20b091dcd8
Merge pull request #2360 from LukashonakV/ISSUE_2356 2023-07-30 01:12:40 +02:00
Viktar Lukashonak
106535e3eb
tooltip-format spaces breaks pango format
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-29 23:46:35 +03:00
Alexis Rouillard
d3520536c4
Merge pull request #2352 from Mr-Pine/hyprland-window-fix 2023-07-29 11:02:39 +02:00
MisterPine
05e5a7e5fd
Document icon for hyprland/window 2023-07-29 00:35:01 +02:00
MisterPine
2b07dea3a6
Fix broken start behavior for hyprland/window 2023-07-29 00:30:33 +02:00
Alexis Rouillard
629686f760
Merge pull request #2347 from MightyPlaza/typo
hyprland/workspaces fix typo
2023-07-28 09:58:09 +02:00
MightyPlaza
c71c0fca6e
fix typo 2023-07-25 14:48:20 +00:00
Alexis Rouillard
9207fff627
Merge pull request #2345 from LukashonakV/ISSUE_2344
Clock. Narrow seconds precision
2023-07-24 23:19:19 +02:00
Viktar Lukashonak
dae7794bdf
Clock. Narrow seconds precision
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-25 00:08:13 +03:00
Alexis Rouillard
c087d8c318
Merge pull request #2316 from MightyPlaza/workspaces
hyprland/workspaces improvements
2023-07-24 13:40:23 +02:00
Alexis Rouillard
6dfa74bd5d
Merge pull request #2322 from zjeffer/master
Use C++20 by default
2023-07-24 09:27:18 +02:00
Alexis Rouillard
e9b29613b2
Merge pull request #2340 from LukashonakV/ISSUE_2331
time conversion between time zones
2023-07-24 09:26:02 +02:00
Viktar Lukashonak
d01ce7d812
Rewview changes.
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-24 10:16:38 +03:00
Alexis Rouillard
8ce1d15885
Merge pull request #2323 from zjeffer/mediaplayer-improvements
Mediaplayer improvements
2023-07-24 08:56:46 +02:00
Viktar Lukashonak
4d9e0ea802
time conversion between time zones
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-24 01:21:33 +03:00
Tuur Vanhoutte
2f04a49129
Merge branch 'Alexays:master' into master 2023-07-23 14:30:14 +02:00
zjeffer
6dc33fe88f Mediaplayer improvements 2023-07-23 13:55:25 +02:00
Alexis Rouillard
effad1a5c3
Merge pull request #2336 from Anakael/pr/anakael/add-high-priority-workspaces
[sway/workspaces] feat: add high-priority-named optiion
2023-07-21 17:33:10 +02:00
dmitry
05efdb74f0 format 2023-07-20 22:57:33 +03:00
dmitry
d3bcff31e5 add high-priority-named optiion 2023-07-20 22:56:15 +03:00
Alexis Rouillard
430f0e5d65
Merge pull request #2333 from LukashonakV/cava_man
cava man page
2023-07-19 21:47:33 +02:00
Viktar Lukashonak
4dff1d4b2b
cava man page
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-19 20:44:52 +03:00
Alexis Rouillard
56df72f61c
Merge pull request #2328 from LukashonakV/ISSUE_2281
cava bump
2023-07-18 16:59:38 +02:00
Viktar Lukashonak
8fdd456fa9
cava bump
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-18 17:51:23 +03:00
Alex
3e1176e896 fix: lint 2023-07-18 08:35:46 +02:00
Alexis Rouillard
52a8b1d911
Merge pull request #2234 from kris7t/offscreen-app-id
sway/window: app_id on unfocused workspaces
2023-07-18 08:32:32 +02:00
MightyPlaza
24d56023fd
last fixes 2023-07-17 22:38:58 +00:00
MightyPlaza
d3e7a8c797
Merge branch 'Alexays:master' into workspaces 2023-07-17 22:38:13 +00:00
MightyPlaza
52906407af
Merge pull request #3 from zjeffer/pr-fixes
Some more small improvements
2023-07-17 14:27:38 +00:00
zjeffer
2721e19ee6 small improvements 2023-07-17 16:22:59 +02:00
MightyPlaza
2d0fdaeec6
special fix 2023-07-16 18:22:14 +00:00
MightyPlaza
b9348180f5
Merge branch 'Alexays:master' into workspaces 2023-07-16 18:21:36 +00:00
zjeffer
62702a4878 fixed lint 2023-07-16 16:00:26 +02:00
zjeffer
c85738574c Use C++20 by default 2023-07-16 16:00:26 +02:00
MightyPlaza
96a8e5df83
Merge branch 'Alexays:master' into workspaces 2023-07-16 13:19:24 +00:00
MightyPlaza
2bfc0e1da6
moves createWorkspace to update() 2023-07-16 01:49:46 +00:00
MightyPlaza
6d24b22b21
moves createWorkspace to update() 2023-07-16 01:43:54 +00:00
MightyPlaza
5f0fa71f32
moves createWorkspace to update() 2023-07-16 01:43:25 +00:00
MightyPlaza
4f81e55e41
workspaces.hpp 2023-07-16 01:20:30 +00:00
MightyPlaza
ca0122c3cb
workspaces.cpp 2023-07-16 01:18:41 +00:00
MightyPlaza
7200b16520
documentation 2023-07-16 01:02:39 +00:00
MightyPlaza
e488daae16
Merge pull request #2 from zjeffer/pr-fixes
use IPC for click events, clang-tidy fixes
2023-07-15 22:18:24 +00:00
zjeffer
f3df15650a use IPC for click events, clang-tidy fixes 2023-07-15 23:48:12 +02:00
MightyPlaza
f8a9a970b2
removes "sort-by-number" 2023-07-15 18:43:46 +00:00
MightyPlaza
75e21c4853
Update waybar-hyprland-workspaces.5.scd 2023-07-15 18:36:55 +00:00
MightyPlaza
495b63d7dc
workspaces.hpp 2023-07-15 17:44:57 +00:00
MightyPlaza
52983c7188
workspaces.cpp 2023-07-15 17:43:22 +00:00
Kristóf Marussy
5bfdbf116d
sway/window: app_id on unfocused workspaces
If the unfocused workspace has a single window, use window-specific
styling when offscreen-css is enabled.
2023-06-13 01:52:13 +02:00
68 changed files with 1469 additions and 806 deletions

View File

@ -13,7 +13,7 @@ jobs:
- fedora
- opensuse
- gentoo
cpp_std: [c++17]
cpp_std: [c++20]
include:
- distro: fedora
cpp_std: c++20

View File

@ -1,7 +1,7 @@
# Waybar [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)<br>![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png)
> 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
View File

@ -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",

View File

@ -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"; }

View File

@ -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_;

View File

@ -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();

View File

@ -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_;

View File

@ -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_;

View File

@ -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_;
};

View File

@ -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_;

View File

@ -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

View File

@ -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": ["", ""]
}
```

View File

@ -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
}
```

View File

@ -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
View 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"
}
},
```

View File

@ -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

View File

@ -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 ++

View File

@ -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 ++

View File

@ -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 ++

View File

@ -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
}
```

View File

@ -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"
}
```

View File

@ -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
}
```

View File

@ -25,6 +25,16 @@ Addressed by *hyprland/window*
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
@ -52,11 +62,11 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
```
"hyprland/window": {
"format": "{}",
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
"format": "{}",
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
}
```

View File

@ -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*

View File

@ -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
}

View File

@ -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*: ++

View File

@ -85,8 +85,8 @@ See *systemd-inhibit*(1) for more information.
"what": "handle-lid-switch",
"format": "{icon}",
"format-icons": {
"activated": "",
"deactivated": ""
"activated": "",
"deactivated": ""
}
}
```

View File

@ -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
}
```

View File

@ -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": ""
}
}
```

View File

@ -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 ++

View File

@ -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}_

View File

@ -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 ++

View File

@ -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"
}
```

View File

@ -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": " {}"
}
```

View File

@ -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
}
```

View File

@ -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.

View File

@ -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
}
```

View File

@ -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

View File

@ -43,11 +43,11 @@ Addressed by *sway/language*
```
"sway/language": {
"format": "{}",
"format": "{}",
},
"sway/language": {
"format": "{short} {variant}",
"format": "{short} {variant}",
}
```

View File

@ -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
}
```

View File

@ -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}"
}
```

View File

@ -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 ++
@ -124,12 +124,12 @@ 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]"
}
}
```

View File

@ -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": ""
}
}
```

View File

@ -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 ++

View File

@ -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
}
```

View File

@ -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 ++

View File

@ -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"
}
```

View File

@ -2,7 +2,7 @@ waybar-wlr-taskbar(5)
# NAME
wlroots - Taskbar module
waybar - wlr taskbar module
# DESCRIPTION

View File

@ -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 ++

View File

@ -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)*

View File

@ -1,10 +1,10 @@
project(
'waybar', 'cpp', 'c',
version: '0.9.20',
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'
],
@ -346,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')
@ -423,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',

View File

@ -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()

View File

@ -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);
}
}

View File

@ -17,15 +17,15 @@
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())
// nullptr means that local time should be shown
time_zones_.push_back(nullptr);
// local time should be shown
time_zones_.push_back(date::current_zone());
else
try {
time_zones_.push_back(date::locate_zone(zone_name.asString()));
@ -35,8 +35,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
} else if (config_["timezone"].isString()) {
if (config_["timezone"].asString().empty())
// nullptr means that local time should be shown
time_zones_.push_back(nullptr);
time_zones_.push_back(date::current_zone());
else
try {
time_zones_.push_back(date::locate_zone(config_["timezone"].asString()));
@ -47,17 +46,18 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
// If all timezones are parsed and no one is good
if (!time_zones_.size()) {
// nullptr means that local time should be shown
time_zones_.push_back(nullptr);
time_zones_.push_back(date::current_zone());
}
// 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;
}
@ -158,52 +158,35 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
const date::time_zone* waybar::modules::Clock::current_timezone() {
return is_timezone_fixed() ? 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
@ -219,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;
@ -235,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;
@ -318,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()};
@ -329,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_)
@ -361,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_));
@ -377,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_, ' ');
@ -387,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
@ -397,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_, ' ');
}
@ -421,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();
@ -457,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 "";
@ -471,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();

View File

@ -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_)));
}
}

View File

@ -26,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);

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -145,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,
@ -181,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 = "";
@ -226,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()};
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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