Compare commits

...

130 Commits

Author SHA1 Message Date
e30fba0b8f chore: 0.9.22 2023-08-16 13:34:05 +02:00
b5ea14c896 revert: release event 2023-08-16 13:31:24 +02:00
5b33a5917c Merge pull request #2404 from eltociear/master-1
fix: typo in taskbar.cpp
2023-08-16 08:55:33 +02:00
66b71cc857 Merge pull request #2409 from jpalus/fmt-cast-ustring-to-string 2023-08-15 22:35:46 +02:00
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
57544fe694 fix: typo in taskbar.cpp
ocurred -> occurred
2023-08-15 00:45:09 +09:00
e90c66a102 chore: 0.9.21 2023-08-14 15:33:52 +02:00
db1d859881 fix: lint 2023-08-14 14:20:08 +02:00
cc961d40b3 Merge pull request #2402 from frankebel/master
docs: fix Arch repository name
2023-08-14 13:28:54 +02:00
29cebaa0a7 docs: fix Arch repository name 2023-08-14 12:42:33 +02:00
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
eb74623b7d Merge pull request #2399 from KosmX/click_release_action
Button release action
2023-08-14 11:46:27 +02:00
b9260575e5 Merge pull request #2401 from zjeffer/master
Updated hyprland-workspaces manpage
2023-08-14 11:23:35 +02:00
8cf676176d Updated hyprland-workspaces manpage 2023-08-14 11:17:49 +02:00
3db5673e70 register key hander 2023-08-13 18:48:23 +02:00
46f5034030 Add button release events to eventMap 2023-08-13 18:20:43 +02:00
6f7e7c5199 formatting 2023-08-10 20:37:42 +02:00
2ba11e8401 Hyprland/workspaces: use named icons instead of searching by id 2023-08-10 20:09:30 +02:00
b084bf721e Merge pull request #2377 from marshallmoats/master
Change default workspace format
2023-08-09 08:16:52 +02:00
e40860c3e9 keep id 2023-08-08 17:17:58 -05:00
2bcd0eb09f Change default workspace format 2023-08-02 16:02:50 -05:00
94c34a29c4 Merge pull request #2370 from spikespaz/nix-fix-overlay
flake: fix infrec in package overlay
2023-08-02 08:41:39 +02:00
7fb37c0069 Merge pull request #2373 from marshallmoats/master 2023-08-02 08:35:10 +02:00
ce2ede70f2 Fix logical errors, formatting error 2023-08-01 15:55:37 -05:00
b91adc9f29 flake: fix infrec in package overlay 2023-07-31 09:37:33 -07:00
86b3e456e1 Merge pull request #2341 from zjeffer/hyprland/workspaces
Persistent workspaces in hyprland/workspaces
2023-07-31 08:34:30 +02:00
1eb4684b60 Merge pull request #2367 from diederikdehaas/more-manpage-fixes
man: Fix several whitespace formatting issues
2023-07-31 08:33:09 +02:00
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
88828265c0 Merge pull request #2366 from zjeffer/fix/build-warnings
Fixed sway IPC compile warnings
2023-07-30 11:52:11 +02:00
600653538b Persistent workspaces in hyprland/workspaces 2023-07-30 11:47:37 +02:00
28635c1f6d Fixed sway IPC compile warnings 2023-07-30 11:43:34 +02:00
b09f6cc731 Merge pull request #2365 from diederikdehaas/manpage-fixes
Manpage fixes
2023-07-30 10:07:03 +02:00
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
6b7fd36863 man/waybar-clock: Fix typo and formatting
typo: Adressed -> Addressed
formatting: Add missing spaces
2023-07-30 09:56:19 +02:00
20b091dcd8 Merge pull request #2360 from LukashonakV/ISSUE_2356 2023-07-30 01:12:40 +02:00
106535e3eb tooltip-format spaces breaks pango format
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-29 23:46:35 +03:00
d3520536c4 Merge pull request #2352 from Mr-Pine/hyprland-window-fix 2023-07-29 11:02:39 +02:00
05e5a7e5fd Document icon for hyprland/window 2023-07-29 00:35:01 +02:00
2b07dea3a6 Fix broken start behavior for hyprland/window 2023-07-29 00:30:33 +02:00
629686f760 Merge pull request #2347 from MightyPlaza/typo
hyprland/workspaces fix typo
2023-07-28 09:58:09 +02:00
c71c0fca6e fix typo 2023-07-25 14:48:20 +00:00
9207fff627 Merge pull request #2345 from LukashonakV/ISSUE_2344
Clock. Narrow seconds precision
2023-07-24 23:19:19 +02:00
dae7794bdf Clock. Narrow seconds precision
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-25 00:08:13 +03:00
c087d8c318 Merge pull request #2316 from MightyPlaza/workspaces
hyprland/workspaces improvements
2023-07-24 13:40:23 +02:00
6dfa74bd5d Merge pull request #2322 from zjeffer/master
Use C++20 by default
2023-07-24 09:27:18 +02:00
e9b29613b2 Merge pull request #2340 from LukashonakV/ISSUE_2331
time conversion between time zones
2023-07-24 09:26:02 +02:00
d01ce7d812 Rewview changes.
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-24 10:16:38 +03:00
8ce1d15885 Merge pull request #2323 from zjeffer/mediaplayer-improvements
Mediaplayer improvements
2023-07-24 08:56:46 +02:00
4d9e0ea802 time conversion between time zones
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-24 01:21:33 +03:00
2f04a49129 Merge branch 'Alexays:master' into master 2023-07-23 14:30:14 +02:00
6dc33fe88f Mediaplayer improvements 2023-07-23 13:55:25 +02:00
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
05efdb74f0 format 2023-07-20 22:57:33 +03:00
d3bcff31e5 add high-priority-named optiion 2023-07-20 22:56:15 +03:00
430f0e5d65 Merge pull request #2333 from LukashonakV/cava_man
cava man page
2023-07-19 21:47:33 +02:00
4dff1d4b2b cava man page
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-19 20:44:52 +03:00
56df72f61c Merge pull request #2328 from LukashonakV/ISSUE_2281
cava bump
2023-07-18 16:59:38 +02:00
8fdd456fa9 cava bump
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2023-07-18 17:51:23 +03:00
3e1176e896 fix: lint 2023-07-18 08:35:46 +02:00
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
3ecd4030e3 chore: v0.9.20 2023-07-18 08:29:32 +02:00
841a004acd fix: lint 2023-07-18 08:28:19 +02:00
839975c348 Merge pull request #2318 from calvinchd/hyprland-window-remove-empty-css
hyprland/window remove duplicate empty css
2023-07-18 08:22:27 +02:00
185aa104b0 Merge pull request #2326 from czM1K3/master
hyprland/language: Differentiating keyboard layout variants
2023-07-18 08:21:52 +02:00
24d56023fd last fixes 2023-07-17 22:38:58 +00:00
d3e7a8c797 Merge branch 'Alexays:master' into workspaces 2023-07-17 22:38:13 +00:00
b2279c9565 Differencing keyboard layout variant for hyprland/language 2023-07-17 22:20:50 +02:00
52906407af Merge pull request #3 from zjeffer/pr-fixes
Some more small improvements
2023-07-17 14:27:38 +00:00
2721e19ee6 small improvements 2023-07-17 16:22:59 +02:00
388912d4a7 Merge pull request #2324 from xytovl/fix-crash-on-resume 2023-07-17 08:26:53 +02:00
f62b3d0e9d Ensure signal is disconnected in destructor 2023-07-16 23:29:28 +02:00
2d0fdaeec6 special fix 2023-07-16 18:22:14 +00:00
b9348180f5 Merge branch 'Alexays:master' into workspaces 2023-07-16 18:21:36 +00:00
1e2b9cb5ed Merge pull request #2306 from stefonarch/patch-1
Fix wrong name for {name}
2023-07-16 18:21:00 +02:00
62702a4878 fixed lint 2023-07-16 16:00:26 +02:00
c85738574c Use C++20 by default 2023-07-16 16:00:26 +02:00
96a8e5df83 Merge branch 'Alexays:master' into workspaces 2023-07-16 13:19:24 +00:00
85b4ff4f81 Merge pull request #2321 from calvinchd/hyprland-runtime-err 2023-07-16 15:10:03 +02:00
0f6eff1f20 hyprland: fix json parser runtime err from socket read ending early 2023-07-16 22:18:27 +10:00
7a5e702334 Merge pull request #2317 from m4rch3n1ng/backlight-percent 2023-07-16 09:34:47 +02:00
8687ed2068 Update man for hyprland/window to replace #window.empty with window#waybar.empty #window 2023-07-16 16:41:50 +10:00
2be0e966e1 hyprland/window: remove .empty css class for #window 2023-07-16 16:40:54 +10:00
may
facb53e81f backlight: do not convert percent to string in fmt 2023-07-16 04:14:43 +02:00
2bfc0e1da6 moves createWorkspace to update() 2023-07-16 01:49:46 +00:00
6d24b22b21 moves createWorkspace to update() 2023-07-16 01:43:54 +00:00
5f0fa71f32 moves createWorkspace to update() 2023-07-16 01:43:25 +00:00
4f81e55e41 workspaces.hpp 2023-07-16 01:20:30 +00:00
ca0122c3cb workspaces.cpp 2023-07-16 01:18:41 +00:00
7200b16520 documentation 2023-07-16 01:02:39 +00:00
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
f3df15650a use IPC for click events, clang-tidy fixes 2023-07-15 23:48:12 +02:00
f8a9a970b2 removes "sort-by-number" 2023-07-15 18:43:46 +00:00
75e21c4853 Update waybar-hyprland-workspaces.5.scd 2023-07-15 18:36:55 +00:00
495b63d7dc workspaces.hpp 2023-07-15 17:44:57 +00:00
52983c7188 workspaces.cpp 2023-07-15 17:43:22 +00:00
2211a79840 Merge pull request #2308 from gardenappl/hidden-fix
hyprland/window: rename .hidden to .swallowing (and fix grouped windows)
2023-07-12 18:13:50 +02:00
daca57129f hyprland/window: rename .hidden to .swallowing (and fix grouped windows) 2023-07-12 19:01:45 +03:00
19c7c0763f Merge pull request #2307 from gardenappl/hidden-fix
hyprland/window: Fix overlap with .hidden class from default style
2023-07-12 17:27:37 +02:00
14c6550593 hyprland/window: Fix overlap with .hidden class from default style 2023-07-12 17:56:12 +03:00
7aae93e7ed Fix wrong name for {name} 2023-07-12 16:31:58 +02:00
dffba78401 Merge pull request #2303 from Mr-Pine/hyprland-window-icon 2023-07-11 08:08:20 +02:00
a8a1a4985f Add removed secondary identifier
(class for xwayland under sway)
2023-07-10 23:48:18 +02:00
31683d9e2a Implemented AAppIconLabel for sway/window 2023-07-10 22:55:46 +02:00
00e143d47e Introducce AAppIconLabel class
Implemented for hyprland
2023-07-10 22:50:58 +02:00
6e9ba3fc01 Fix spacing if icon is false 2023-07-10 22:26:02 +02:00
a373f6b654 Icon working 2023-07-10 22:02:03 +02:00
91bd28d410 Merge pull request #2294 from Mr-Pine/hyprland-window-data
`hyprland/window` expose more data
2023-07-09 10:18:26 +02:00
acde076913 Merge pull request #2288 from LukashonakV/arch_cava
Arch CI cava
2023-07-09 10:17:55 +02:00
f5655526d0 Merge pull request #2296 from m-bdf/clock-tz-changes 2023-07-09 04:22:15 +02:00
56f956ff90 clock: handle timezone changes (again) 2023-07-09 01:44:39 +02:00
f97c1c7136 remove getWindowData 2023-07-08 23:22:29 +02:00
9ee883ee1b No dashes is format arg name 2023-07-08 23:11:11 +02:00
1887512ba1 Update scd 2023-07-08 22:40:16 +02:00
2ae13c4092 consitent naming 2023-07-08 22:40:16 +02:00
c5f1771375 Use already existing queryActiveWorkspace() 2023-07-08 22:40:16 +02:00
c4bace504c Separate query and struct 2023-07-08 22:40:16 +02:00
3bfeed31bc Merge pull request #2293 from sigboe/master 2023-07-08 21:47:04 +02:00
d774de6c46 fix, default to true, sway/workspaces: warp-on-scroll 2023-07-08 21:32:19 +02:00
b20041d85d cava dependencies 2023-07-08 08:41:37 +00:00
e4900db9a2 Merge pull request #2286 from calvinchd/hyprland-window-noinfo 2023-07-06 08:24:11 +02:00
e2bfa5e019 hyprland/window: fix no info with separate-outputs=true 2023-07-06 11:01:24 +10:00
423d8495e4 Merge pull request #2284 from jbeich/dbus 2023-07-05 20:17:51 +02:00
1fb2b8efd5 fix(util): don't abort modules from SleeperThread after 3c9cbc99d7
[warning] module sway/workspaces: Disabling module "sway/workspaces", Unable to connect to the SYSTEM Bus!...
[warning] module sway/mode: Disabling module "sway/mode", Unable to connect to the SYSTEM Bus!...
[warning] module sway/scratchpad: Disabling module "sway/scratchpad", Unable to connect to the SYSTEM Bus!...
[warning] module custom/media: Disabling module "custom/media", Unable to connect to the SYSTEM Bus!...
[warning] module sway/window: Disabling module "sway/window", Unable to connect to the SYSTEM Bus!...
[warning] module cpu: Disabling module "cpu", Unable to connect to the SYSTEM Bus!...
[warning] module memory: Disabling module "memory", Unable to connect to the SYSTEM Bus!...
[warning] module temperature: Disabling module "temperature", Unable to connect to the SYSTEM Bus!...
[warning] module sway/language: Disabling module "sway/language", Unable to connect to the SYSTEM Bus!...
[warning] module battery: Disabling module "battery", Unable to connect to the SYSTEM Bus!...
[warning] module battery#bat2: Disabling module "battery#bat2", Unable to connect to the SYSTEM Bus!...
2023-07-05 17:47:24 +00:00
3299d4a25c Merge pull request #2270 from gardenappl/hypr-backend
hyprland/window: Add .hidden CSS class, account for hidden & fullscreen windows
2023-07-05 08:14:49 +02:00
e125bbeb4d hyprland/window: properly check visibility for .floating class 2023-07-05 03:43:03 +03:00
55c59253d6 Update man pages 2023-07-05 03:15:59 +03:00
e7deab92c7 Merge pull request #2282 from zjeffer/fix/build-warnings
Fixed build warnings
2023-07-04 23:09:13 +02:00
d21f29cb14 Fixed build warnings 2023-07-04 23:05:26 +02:00
cdece498c1 hyprland/window: .solo class accounts for hidden & fullscreen windows 2023-07-02 20:58:42 +03:00
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 1524 additions and 828 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

@ -3,5 +3,5 @@
FROM archlinux:base-devel
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl && \
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \
sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen

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

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

27
include/AAppIconLabel.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <gtkmm/box.h>
#include <gtkmm/image.h>
#include "AIconLabel.hpp"
namespace waybar {
class AAppIconLabel : public AIconLabel {
public:
AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id,
const std::string &format, uint16_t interval = 0, bool ellipsize = false,
bool enable_click = false, bool enable_scroll = false);
virtual ~AAppIconLabel() = default;
auto update() -> void override;
protected:
void updateAppIconName(const std::string &app_identifier,
const std::string &alternative_app_identifier);
void updateAppIcon();
unsigned app_icon_size_{24};
bool update_app_icon_{true};
std::string app_icon_name_;
};
} // namespace waybar

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,13 +1,13 @@
#include <fmt/format.h>
#include "ALabel.hpp"
#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "util/json.hpp"
namespace waybar::modules::hyprland {
class Window : public waybar::ALabel, public EventHandler {
class Window : public waybar::AAppIconLabel, public EventHandler {
public:
Window(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~Window();
@ -24,6 +24,19 @@ class Window : public waybar::ALabel, public EventHandler {
static auto parse(const Json::Value&) -> Workspace;
};
struct WindowData {
bool floating;
int monitor = -1;
std::string class_name;
std::string initial_class_name;
std::string title;
std::string initial_title;
bool fullscreen;
bool grouped;
static auto parse(const Json::Value&) -> WindowData;
};
auto getActiveWorkspace(const std::string&) -> Workspace;
auto getActiveWorkspace() -> Workspace;
void onEvent(const std::string&) override;
@ -34,12 +47,13 @@ class Window : public waybar::ALabel, public EventHandler {
std::mutex mutex_;
const Bar& bar_;
util::JsonParser parser_;
std::string last_title_;
WindowData window_data_;
Workspace workspace_;
std::string solo_class_;
std::string last_solo_class_;
bool solo_;
bool all_floating_;
bool swallowing_;
bool fullscreen_;
};

View File

@ -9,27 +9,35 @@
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; };
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_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;
Gtk::Button button_;
Gtk::Box content_;
@ -39,23 +47,38 @@ 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);
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

@ -66,9 +66,9 @@ class Mpris : public ALabel {
int album_len_;
int title_len_;
int dynamic_len_;
std::string dynamic_separator_;
std::vector<std::string> dynamic_order_;
std::vector<std::string> dynamic_prio_;
std::vector<std::string> dynamic_order_;
std::string dynamic_separator_;
bool truncate_hours_;
bool tooltip_len_limits_;
std::string ellipsis_;

View File

@ -4,7 +4,7 @@
#include <tuple>
#include "AIconLabel.hpp"
#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
@ -12,7 +12,7 @@
namespace waybar::modules::sway {
class Window : public AIconLabel, public sigc::trackable {
class Window : public AAppIconLabel, public sigc::trackable {
public:
Window(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~Window() = default;
@ -25,8 +25,6 @@ class Window : public AIconLabel, public sigc::trackable {
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
getFocusedNode(const Json::Value& nodes, std::string& output);
void getTree();
void updateAppIconName();
void updateAppIcon();
const Bar& bar_;
std::string window_;
@ -37,9 +35,6 @@ class Window : public AIconLabel, public sigc::trackable {
std::string old_app_id_;
std::size_t app_nb_;
std::string shell_;
unsigned app_icon_size_{24};
bool update_app_icon_{true};
std::string app_icon_name_;
int floating_count_;
util::JsonParser parser_;
std::mutex mutex_;

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

@ -95,6 +95,7 @@ class SleeperThread {
}
~SleeperThread() {
connection_.disconnect();
stop();
if (thread_.joinable()) {
thread_.join();

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

@ -57,12 +57,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 ++

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

@ -14,13 +14,38 @@ Addressed by *hyprland/window*
*format*: ++
typeof: string ++
default: {} ++
default: {title} ++
The format, how information should be displayed. On {} the current window title is displayed.
*rewrite*: ++
typeof: object ++
Rules to rewrite window title. See *rewrite rules*.
*separate-outputs*: ++
typeof: bool ++
Show the active window of the monitor the bar belongs to, instead of the focused window.
*icon*: ++
typeof: bool ++
default: false ++
Option to hide the application icon.
*icon-size*: ++
typeof: integer ++
default: 24 ++
Option to change the size of the application icon.
# FORMAT REPLACEMENTS
See the output of "hyprctl clients" for examples
*{title}*: The current title of the focused window.
*{initialTitle}*: The initial title of the focused window.
*{class}*: The current class of the focused window.
*{initialClass}*: The initial class of the focused window.
# REWRITE RULES
*rewrite* is an object where keys are regular expressions and values are
@ -37,14 +62,28 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
```
"hyprland/window": {
"format": "{}",
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
"format": "{}",
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
}
```
# STYLE
- *#window*
- *window#waybar.empty #window* When no windows are in the workspace
The following classes are applied to the entire Waybar rather than just the
window widget:
- *window#waybar.empty* When no windows are in the workspace
- *window#waybar.solo* When one tiled window is visible in the workspace
(floating windows may be present)
- *window#waybar.<app_id>* Where *<app_id>* is the *class* (e.g. *chromium*) of
the solo tiled window in the workspace (use *hyprctl clients* to see classes)
- *window#waybar.floating* When there are only floating windows in the workspace
- *window#waybar.fullscreen* When there is a fullscreen window in the workspace;
useful with Hyprland's *fullscreen, 1* mode
- *window#waybar.swallowing* When there is a swallowed window in the workspace

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,5 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *#workspaces*
- *#workspaces button*
- *#workspaces button.active*
- *#workspaces button.persistent*
- *#workspaces button.special*

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

@ -65,13 +65,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 ++
@ -84,7 +84,7 @@ Addressed by *sway/window*
typeof: bool ++
default: false ++
If the workspace itself is focused and the workspace contains nodes or floating_nodes, show the workspace name. If not set, text remains empty but styles according to nodes in the workspace are still applied.
*rewrite*: ++
typeof: object ++
Rules to rewrite the module format output. See *rewrite rules*.
@ -124,18 +124,22 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
```
"sway/window": {
"format": "{}",
"max-length": 50,
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
"format": "{}",
"max-length": 50,
"rewrite": {
"(.*) - Mozilla Firefox": "🌎 $1",
"(.*) - zsh": "> [$1]"
}
}
```
# STYLE
- *#window*
The following classes are applied to the entire Waybar rather than just the
window widget:
- *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-css option is not set
- *window#waybar.solo* When one tiled window is in the workspace
- *window#waybar.floating* When there are only floating windows in the workspace

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
@ -89,7 +89,7 @@ Addressed by *wlr/taskbar*
*{icon}*: The icon of the application.
*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
*{name}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
*{title}*: The title of the application.

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.19',
version: '0.9.22',
license: 'MIT',
meson_version: '>= 0.50.0',
default_options : [
'cpp_std=c++17',
'cpp_std=c++20',
'buildtype=release',
'default_library=static'
],
@ -159,6 +159,7 @@ src_files = files(
'src/AModule.cpp',
'src/ALabel.cpp',
'src/AIconLabel.cpp',
'src/AAppIconLabel.cpp',
'src/modules/custom.cpp',
'src/modules/disk.cpp',
'src/modules/idle_inhibitor.cpp',
@ -345,7 +346,7 @@ if get_option('experimental')
endif
cava = dependency('cava',
version : '>=0.8.4',
version : '>=0.8.5',
required: get_option('cava'),
fallback : ['cava', 'cava_dep'],
not_found_message: 'cava is not found. Building waybar without cava')
@ -422,6 +423,7 @@ if scdoc.found()
main_manpage_path,
'waybar-backlight.5.scd',
'waybar-battery.5.scd',
'waybar-cava.5.scd',
'waybar-clock.5.scd',
'waybar-cpu.5.scd',
'waybar-custom.5.scd',

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

133
src/AAppIconLabel.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "AAppIconLabel.hpp"
#include <gdkmm/pixbuf.h>
#include <glibmm/fileutils.h>
#include <glibmm/keyfile.h>
#include <glibmm/miscutils.h>
#include <spdlog/spdlog.h>
#include <filesystem>
#include <optional>
#include "util/gtk_icon.hpp"
namespace waybar {
AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name,
const std::string& id, const std::string& format, uint16_t interval,
bool ellipsize, bool enable_click, bool enable_scroll)
: AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) {
// Icon size
if (config["icon-size"].isUInt()) {
app_icon_size_ = config["icon-size"].asUInt();
}
image_.set_pixel_size(app_icon_size_);
}
std::optional<std::string> getDesktopFilePath(const std::string& app_identifier,
const std::string& alternative_app_identifier) {
const auto data_dirs = Glib::get_system_data_dirs();
for (const auto& data_dir : data_dirs) {
const auto data_app_dir = data_dir + "applications/";
auto desktop_file_path = data_app_dir + app_identifier + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
if (!alternative_app_identifier.empty()) {
desktop_file_path = data_app_dir + alternative_app_identifier + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
}
}
return {};
}
std::optional<Glib::ustring> getIconName(const std::string& app_identifier,
const std::string& alternative_app_identifier) {
const auto desktop_file_path = getDesktopFilePath(app_identifier, alternative_app_identifier);
if (!desktop_file_path.has_value()) {
// Try some heuristics to find a matching icon
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) {
return app_identifier;
}
const auto app_identifier_desktop = app_identifier + "-desktop";
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) {
return app_identifier_desktop;
}
const auto to_lower = [](const std::string& str) {
auto str_cpy = str;
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
[](unsigned char c) { return std::tolower(c); });
return str;
};
const auto first_space = app_identifier.find_first_of(' ');
if (first_space != std::string::npos) {
const auto first_word = to_lower(app_identifier.substr(0, first_space));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
const auto first_dash = app_identifier.find_first_of('-');
if (first_dash != std::string::npos) {
const auto first_word = to_lower(app_identifier.substr(0, first_dash));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
return {};
}
try {
Glib::KeyFile desktop_file;
desktop_file.load_from_file(desktop_file_path.value());
return desktop_file.get_string("Desktop Entry", "Icon");
} catch (Glib::FileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
} catch (Glib::KeyFileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
}
return {};
}
void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
const std::string& alternative_app_identifier) {
if (!iconEnabled()) {
return;
}
const auto icon_name = getIconName(app_identifier, alternative_app_identifier);
if (icon_name.has_value()) {
app_icon_name_ = icon_name.value();
} else {
app_icon_name_ = "";
}
update_app_icon_ = true;
}
void AAppIconLabel::updateAppIcon() {
if (update_app_icon_) {
update_app_icon_ = false;
if (app_icon_name_.empty()) {
image_.set_visible(false);
} else {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true);
}
}
}
auto AAppIconLabel::update() -> void {
updateAppIcon();
AIconLabel::update();
}
} // namespace waybar

View File

@ -190,9 +190,8 @@ auto waybar::modules::Backlight::update() -> void {
event_box_.show();
const uint8_t percent =
best->get_max() == 0 ? 100 : round(best->get_actual() * 100.0f / best->get_max());
std::string desc =
fmt::format(fmt::runtime(format_), fmt::arg("percent", std::to_string(percent)),
fmt::arg("icon", getIcon(percent)));
std::string desc = fmt::format(fmt::runtime(format_), fmt::arg("percent", percent),
fmt::arg("icon", getIcon(percent)));
label_.set_markup(desc);
getState(percent);
if (tooltipEnabled()) {
@ -202,7 +201,7 @@ auto waybar::modules::Backlight::update() -> void {
}
if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format),
fmt::arg("percent", std::to_string(percent)),
fmt::arg("percent", percent),
fmt::arg("icon", getIcon(percent))));
} else {
label_.set_tooltip_text(desc);

View File

@ -17,13 +17,14 @@
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
current_time_zone_idx_(0),
is_calendar_in_tooltip_(false),
is_timezoned_list_in_tooltip_(false) {
current_time_zone_idx_{0},
is_calendar_in_tooltip_{false},
is_timezoned_list_in_tooltip_{false} {
if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
for (const auto& zone_name : config_["timezones"]) {
if (!zone_name.isString()) continue;
if (zone_name.asString().empty())
// local time should be shown
time_zones_.push_back(date::current_zone());
else
try {
@ -43,8 +44,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
}
// If all timezones are parsed and no one is good, add current time zone. nullptr in timezones
// vector means that local time should be shown
// If all timezones are parsed and no one is good
if (!time_zones_.size()) {
time_zones_.push_back(date::current_zone());
}
@ -52,10 +52,12 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
// Check if a particular placeholder is present in the tooltip format, to know what to calculate
// on update.
if (config_["tooltip-format"].isString()) {
std::string trimmed_format = config_["tooltip-format"].asString();
std::string trimmed_format{config_["tooltip-format"].asString()};
fmtMap_.insert({5, trimmed_format});
trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(),
[](unsigned char x) { return std::isspace(x); }),
trimmed_format.end());
if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) {
is_calendar_in_tooltip_ = true;
}
@ -156,53 +158,35 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
const date::time_zone* waybar::modules::Clock::current_timezone() {
return time_zones_[current_time_zone_idx_] ? time_zones_[current_time_zone_idx_]
: date::current_zone();
}
bool waybar::modules::Clock::is_timezone_fixed() {
return time_zones_[current_time_zone_idx_] != nullptr;
return time_zones_[current_time_zone_idx_];
}
auto waybar::modules::Clock::update() -> void {
const auto* time_zone = current_timezone();
auto now = std::chrono::system_clock::now();
auto ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now)};
const auto* tz{current_timezone()};
const date::zoned_time now{
tz,
date::floor<std::chrono::seconds>(
std::chrono::system_clock::now())}; // Define local time is based on provided time zone
const date::year_month_day today{
date::floor<date::days>(now.get_local_time())}; // Convert now to year_month_day
const date::year_month_day shiftedDay{today + cldCurrShift_}; // Shift today
// Define shift local time
const auto shiftedNow{date::make_zoned(
tz, date::local_days(shiftedDay) +
(now.get_local_time() - date::floor<date::days>(now.get_local_time())))};
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + cldCurrShift_;
if (cldCurrShift_.count()) {
shifted_date = date::year_month_day(shifted_date.year(), shifted_date.month(), date::day(1));
}
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
auto shifted_ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now_shifted)};
std::string text{""};
if (!is_timezone_fixed()) {
// As date dep is not fully compatible, prefer fmt
tzset();
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
text = fmt::format(locale_, fmt::runtime(format_), localtime);
} else {
text = fmt::format(locale_, fmt::runtime(format_), ztime);
}
label_.set_markup(text);
label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now));
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
std::string calendar_lines{""};
std::string timezoned_time_lines{""};
if (is_calendar_in_tooltip_) {
calendar_lines = get_calendar(ztime, shifted_ztime);
}
if (is_timezoned_list_in_tooltip_) {
timezoned_time_lines = timezones_text(&now);
}
auto tooltip_format = config_["tooltip-format"].asString();
text = fmt::format(locale_, fmt::runtime(tooltip_format), shifted_ztime,
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
label_.set_tooltip_markup(text);
}
const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time())
: ""};
const std::string cld_text{(is_calendar_in_tooltip_) ? get_calendar(today, shiftedDay, tz)
: ""};
const std::string text{fmt::format(locale_, fmt::runtime(fmtMap_[5]), shiftedNow,
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), tz_text),
fmt::arg(kCalendarPlaceholder.c_str(), cld_text))};
label_.set_tooltip_markup(text);
}
// Call parent update
@ -218,15 +202,15 @@ auto waybar::modules::Clock::doAction(const std::string& name) -> void {
}
// The number of weeks in calendar month layout plus 1 more for calendar titles
unsigned cldRowsInMonth(date::year_month const ym, date::weekday const firstdow) {
const unsigned cldRowsInMonth(const date::year_month& ym, const date::weekday& firstdow) {
using namespace date;
return static_cast<unsigned>(
ceil<weeks>((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) +
2;
}
auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, unsigned const line)
-> const date::year_month_weekday {
auto cldGetWeekForLine(const date::year_month& ym, const date::weekday& firstdow,
unsigned const line) -> const date::year_month_weekday {
unsigned index = line - 2;
auto sd = date::sys_days{ym / 1};
if (date::weekday{sd} == firstdow) ++index;
@ -234,8 +218,8 @@ auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow,
return ymdw;
}
auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym,
unsigned const line, date::weekday const firstdow,
auto getCalendarLine(const date::year_month_day& currDate, const date::year_month ym,
const unsigned line, const date::weekday& firstdow,
const std::locale* const locale_) -> std::string {
using namespace date::literals;
std::ostringstream res;
@ -317,10 +301,9 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const
return res.str();
}
auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
const date::zoned_seconds& wtime) -> std::string {
auto daypoint = date::floor<date::days>(wtime.get_local_time());
const auto ymd{date::year_month_day{daypoint}};
auto waybar::modules::Clock::get_calendar(const date::year_month_day& today,
const date::year_month_day& ymd,
const date::time_zone* tz) -> const std::string {
const auto ym{ymd.year() / ymd.month()};
const auto y{ymd.year()};
const auto d{ymd.day()};
@ -328,9 +311,6 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
const auto maxRows{12 / cldMonCols_};
std::ostringstream os;
std::ostringstream tmp;
// get currdate
daypoint = date::floor<date::days>(now.get_local_time());
const auto currDate{date::year_month_day{daypoint}};
if (cldMode_ == CldMode::YEAR) {
if (y / date::month{1} / 1 == cldYearShift_)
@ -360,6 +340,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
else
m = 0u;
}
for (auto row{0u}; row < maxRows; ++row) {
const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_),
std::begin(ml) + ((row + 1) * cldMonCols_));
@ -376,8 +357,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
if (line < ml[static_cast<unsigned>(ymTmp.month()) - 1u])
os << fmt::format(fmt::runtime(fmtMap_[4]),
(line == 2)
? date::sys_days{ymTmp / 1}
: date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)})
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
ymTmp, firstdow, line)}})
<< ' ';
else
os << std::string(cldWnLen_, ' ');
@ -386,7 +368,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
os << fmt::format(
fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"),
getCalendarLine(currDate, ymTmp, line, firstdow, &locale_),
getCalendarLine(today, ymTmp, line, firstdow, &locale_),
(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)));
// Week numbers on the right
@ -396,8 +378,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
os << ' '
<< fmt::format(fmt::runtime(fmtMap_[4]),
(line == 2)
? date::sys_days{ymTmp / 1}
: date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)});
? date::zoned_seconds{tz, date::local_days{ymTmp / 1}}
: date::zoned_seconds{tz, date::local_days{cldGetWeekForLine(
ymTmp, firstdow, line)}});
else
os << std::string(cldWnLen_, ' ');
}
@ -420,7 +403,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now,
os << fmt::format( // Apply days format
fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())),
// Apply today format
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", ymd.day()))));
fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d))));
if (cldMode_ == CldMode::YEAR)
cldYearCached_ = os.str();
@ -456,7 +439,7 @@ void waybar::modules::Clock::tz_down() {
current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1;
}
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point* now)
auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now)
-> std::string {
if (time_zones_.size() == 1) {
return "";
@ -470,7 +453,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
if (!timezone) {
timezone = date::current_zone();
}
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(*now)};
auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(now)};
os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n';
}
return os.str();

View File

@ -192,7 +192,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
return "";
}
response.append(buffer, sizeWritten);
} while (sizeWritten == 8192);
} while (sizeWritten > 0);
close(SERVERSOCKET);
return response;

View File

@ -38,7 +38,10 @@ auto Language::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
std::string layoutName = std::string{};
if (config_.isMember("format-" + layout_.short_description)) {
if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) {
const auto propName = "format-" + layout_.short_description + "-" + layout_.variant;
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
} else if (config_.isMember("format-" + layout_.short_description)) {
const auto propName = "format-" + layout_.short_description;
layoutName = fmt::format(fmt::runtime(format_), config_[propName].asString());
} else {

View File

@ -1,5 +1,8 @@
#include "modules/hyprland/window.hpp"
#include <glibmm/fileutils.h>
#include <glibmm/keyfile.h>
#include <glibmm/miscutils.h>
#include <spdlog/spdlog.h>
#include <algorithm>
@ -8,13 +11,12 @@
#include <vector>
#include "modules/hyprland/backend.hpp"
#include "util/json.hpp"
#include "util/rewrite_string.hpp"
namespace waybar::modules::hyprland {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "window", id, "{}", 0, true), bar_(bar) {
: AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) {
modulesReady = true;
separate_outputs = config["separate-outputs"].asBool();
@ -24,6 +26,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
queryActiveWorkspace();
update();
dp.emit();
// register for hyprland ipc
gIPC->registerForIPC("activewindow", this);
@ -44,28 +47,27 @@ auto Window::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title);
std::string window_address = workspace_.last_window;
if (window_name != last_title_) {
if (window_name.empty()) {
label_.get_style_context()->add_class("empty");
} else {
label_.get_style_context()->remove_class("empty");
}
last_title_ = window_name;
}
window_data_.title = window_name;
if (!format_.empty()) {
label_.show();
label_.set_markup(fmt::format(fmt::runtime(format_),
waybar::util::rewriteString(window_name, config_["rewrite"])));
label_.set_markup(waybar::util::rewriteString(
fmt::format(fmt::runtime(format_), fmt::arg("title", window_name),
fmt::arg("initialTitle", window_data_.initial_title),
fmt::arg("class", window_data_.class_name),
fmt::arg("initialClass", window_data_.initial_class_name)),
config_["rewrite"]));
} else {
label_.hide();
}
setClass("empty", workspace_.windows == 0);
setClass("solo", solo_);
setClass("fullscreen", fullscreen_);
setClass("floating", all_floating_);
setClass("swallowing", swallowing_);
setClass("fullscreen", fullscreen_);
if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) {
if (bar_.window.get_style_context()->has_class(last_solo_class_)) {
@ -80,7 +82,7 @@ auto Window::update() -> void {
}
last_solo_class_ = solo_class_;
ALabel::update();
AAppIconLabel::update();
}
auto Window::getActiveWorkspace() -> Workspace {
@ -102,9 +104,9 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
assert(workspaces.isArray());
auto workspace = std::find_if(monitors.begin(), monitors.end(),
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
[&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(monitors)) {
if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, "", ""};
}
@ -116,6 +118,13 @@ auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
value["lastwindowtitle"].asString()};
}
auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
return WindowData{value["floating"].asBool(), value["monitor"].asInt(),
value["class"].asString(), value["initialClass"].asString(),
value["title"].asString(), value["initialTitle"].asString(),
value["fullscreen"].asBool(), !value["grouped"].empty()};
}
void Window::queryActiveWorkspace() {
std::lock_guard<std::mutex> lg(mutex_);
@ -126,36 +135,57 @@ void Window::queryActiveWorkspace() {
}
if (workspace_.windows > 0) {
const auto clients = gIPC->getSocket1Reply("j/clients");
Json::Value json = parser_.parse(clients);
assert(json.isArray());
auto active_window = std::find_if(json.begin(), json.end(), [&](Json::Value window) {
const auto clients = gIPC->getSocket1JsonReply("clients");
assert(clients.isArray());
auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
return window["address"] == workspace_.last_window;
});
if (active_window == std::end(json)) {
if (active_window == std::end(clients)) {
return;
}
if (workspace_.windows == 1 && !(*active_window)["floating"].asBool()) {
solo_class_ = (*active_window)["class"].asString();
} else {
solo_class_ = "";
}
window_data_ = WindowData::parse(*active_window);
updateAppIconName(window_data_.class_name, window_data_.initial_class_name);
std::vector<Json::Value> workspace_windows;
std::copy_if(json.begin(), json.end(), std::back_inserter(workspace_windows),
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows),
[&](Json::Value window) {
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
});
solo_ = 1 == std::count_if(workspace_windows.begin(), workspace_windows.end(),
swallowing_ = std::any_of(workspace_windows.begin(), workspace_windows.end(),
[&](Json::Value window) { return !window["swallowing"].isNull(); });
std::vector<Json::Value> visible_windows;
std::copy_if(workspace_windows.begin(), workspace_windows.end(),
std::back_inserter(visible_windows),
[&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); });
all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(),
all_floating_ = std::all_of(visible_windows.begin(), visible_windows.end(),
[&](Json::Value window) { return window["floating"].asBool(); });
fullscreen_ = (*active_window)["fullscreen"].asBool();
fullscreen_ = window_data_.fullscreen;
// Fullscreen windows look like they are solo
if (fullscreen_) {
solo_ = true;
}
// Grouped windows have a tab bar and therefore don't look fullscreen or solo
if (window_data_.grouped) {
fullscreen_ = false;
solo_ = false;
}
if (solo_) {
solo_class_ = window_data_.class_name;
} else {
solo_class_ = "";
}
} else {
solo_class_ = "";
solo_ = false;
window_data_ = WindowData{};
all_floating_ = false;
swallowing_ = false;
fullscreen_ = false;
solo_ = false;
solo_class_ = "";
}
}

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,34 @@ 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);
}
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_);
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 +88,100 @@ 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();
}
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 +189,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 +283,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 +319,47 @@ 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");
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 +375,48 @@ 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;
}
} // namespace waybar::modules::hyprland

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

@ -17,13 +17,7 @@
namespace waybar::modules::sway {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
// Icon size
if (config_["icon-size"].isUInt()) {
app_icon_size_ = config["icon-size"].asUInt();
}
image_.set_pixel_size(app_icon_size_);
: AAppIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
ipc_.subscribe(R"(["window","workspace"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
@ -49,7 +43,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
auto output = payload["output"].isString() ? payload["output"].asString() : "";
std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) =
getFocusedNode(payload["nodes"], output);
updateAppIconName();
updateAppIconName(app_id_, app_class_);
dp.emit();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
@ -57,105 +51,6 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
}
}
std::optional<std::string> getDesktopFilePath(const std::string& app_id,
const std::string& app_class) {
const auto data_dirs = Glib::get_system_data_dirs();
for (const auto& data_dir : data_dirs) {
const auto data_app_dir = data_dir + "applications/";
auto desktop_file_path = data_app_dir + app_id + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
if (!app_class.empty()) {
desktop_file_path = data_app_dir + app_class + ".desktop";
if (std::filesystem::exists(desktop_file_path)) {
return desktop_file_path;
}
}
}
return {};
}
std::optional<Glib::ustring> getIconName(const std::string& app_id, const std::string& app_class) {
const auto desktop_file_path = getDesktopFilePath(app_id, app_class);
if (!desktop_file_path.has_value()) {
// Try some heuristics to find a matching icon
if (DefaultGtkIconThemeWrapper::has_icon(app_id)) {
return app_id;
}
const auto app_id_desktop = app_id + "-desktop";
if (DefaultGtkIconThemeWrapper::has_icon(app_id_desktop)) {
return app_id_desktop;
}
const auto to_lower = [](const std::string& str) {
auto str_cpy = str;
std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(),
[](unsigned char c) { return std::tolower(c); });
return str;
};
const auto first_space = app_id.find_first_of(' ');
if (first_space != std::string::npos) {
const auto first_word = to_lower(app_id.substr(0, first_space));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
const auto first_dash = app_id.find_first_of('-');
if (first_dash != std::string::npos) {
const auto first_word = to_lower(app_id.substr(0, first_dash));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
}
return {};
}
try {
Glib::KeyFile desktop_file;
desktop_file.load_from_file(desktop_file_path.value());
return desktop_file.get_string("Desktop Entry", "Icon");
} catch (Glib::FileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
} catch (Glib::KeyFileError& error) {
spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(),
error.what().c_str());
}
return {};
}
void Window::updateAppIconName() {
if (!iconEnabled()) {
return;
}
const auto icon_name = getIconName(app_id_, app_class_);
if (icon_name.has_value()) {
app_icon_name_ = icon_name.value();
} else {
app_icon_name_ = "";
}
update_app_icon_ = true;
}
void Window::updateAppIcon() {
if (update_app_icon_) {
update_app_icon_ = false;
if (app_icon_name_.empty()) {
image_.set_visible(false);
} else {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true);
}
}
}
auto Window::update() -> void {
spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_,
floating_count_);
@ -210,7 +105,7 @@ auto Window::update() -> void {
updateAppIcon();
// Call parent update
AIconLabel::update();
AAppIconLabel::update();
}
void Window::setClass(std::string classname, bool enable) {
@ -250,6 +145,40 @@ std::pair<int, int> leafNodesInWorkspace(const Json::Value& node) {
return {sum, floating_sum};
}
std::optional<std::reference_wrapper<const Json::Value>> getSingleChildNode(
const Json::Value& node) {
auto const& nodes = node["nodes"];
if (nodes.empty()) {
if (node["type"].asString() == "workspace")
return {};
else if (node["type"].asString() == "floating_con") {
return {};
} else {
return {std::cref(node)};
}
}
auto it = std::cbegin(nodes);
if (it == std::cend(nodes)) {
return {};
}
auto const& child = *it;
++it;
if (it != std::cend(nodes)) {
return {};
}
return {getSingleChildNode(child)};
}
std::tuple<std::string, std::string, std::string> getWindowInfo(const Json::Value& node) {
const auto app_id = node["app_id"].isString() ? node["app_id"].asString()
: node["window_properties"]["instance"].asString();
const auto app_class = node["window_properties"]["class"].isString()
? node["window_properties"]["class"].asString()
: "";
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
return {app_id, app_class, shell};
}
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_,
const Bar& bar_, Json::Value& parentWorkspace,
@ -286,12 +215,7 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu
// found node
spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name,
output, node["name"].asString());
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
: node["window_properties"]["instance"].asString();
const auto app_class = node["window_properties"]["class"].isString()
? node["window_properties"]["class"].asString()
: "";
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
const auto [app_id, app_class, shell] = getWindowInfo(node);
int nb = node.size();
int floating_count = 0;
std::string workspace_layout = "";
@ -331,15 +255,24 @@ gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Valu
std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent);
// using an empty string as default ensures that no window depending styles are set due to the
// checks above for !name.empty()
std::string app_id = "";
std::string app_class = "";
std::string workspace_layout = "";
if (all_leaf_nodes.first == 1) {
const auto single_child = getSingleChildNode(immediateParent);
if (single_child.has_value()) {
std::tie(app_id, app_class, workspace_layout) = getWindowInfo(single_child.value());
}
}
return {all_leaf_nodes.first,
all_leaf_nodes.second,
0,
(all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0)
? config_["offscreen-css-text"].asString()
: "",
"",
"",
"",
app_id,
app_class,
workspace_layout,
immediateParent["layout"].asString()};
}

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();
}
@ -327,7 +347,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
return true;
}
}
if (!config_["warp-on-scroll"].asBool()) {
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping none"));
}
try {
@ -335,7 +355,7 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
if (!config_["warp-on-scroll"].asBool()) {
if (!config_["warp-on-scroll"].isNull() && !config_["warp-on-scroll"].asBool()) {
ipc_.sendCmd(IPC_COMMAND, fmt::format("mouse_warping container"));
}
return true;

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,6 +1,7 @@
#include "util/prepare_for_sleep.h"
#include <gio/gio.h>
#include <spdlog/spdlog.h>
namespace {
class PrepareForSleep {
@ -9,7 +10,7 @@ class PrepareForSleep {
GError *error = NULL;
login1_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (!login1_connection) {
throw std::runtime_error("Unable to connect to the SYSTEM Bus!...");
spdlog::warn("Unable to connect to the SYSTEM Bus!...");
} else {
login1_id = g_dbus_connection_signal_subscribe(
login1_connection, "org.freedesktop.login1", "org.freedesktop.login1.Manager",

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