Compare commits

..

196 Commits

Author SHA1 Message Date
Alex
15e1547661 chore: 0.10.4 2024-07-17 23:04:05 +02:00
Alexis Rouillard
dcbcf90aef Update freebsd.yml 2024-07-17 22:52:39 +02:00
Alexis Rouillard
ed0ed398b7 Update freebsd.yml 2024-07-17 22:46:58 +02:00
Alexis Rouillard
152053e069 Merge pull request #3398 from khaneliman/cursor
AModule: Cursor config option
2024-07-17 22:40:12 +02:00
Alexis Rouillard
496dd05e14 Merge pull request #3443 from yangyingchao/master
fix #3442
2024-07-17 22:38:22 +02:00
Alexis Rouillard
3ff8fc8a10 Merge pull request #3447 from khaneliman/hyprland-disable
hyprland: disable modules instead of rendering empty
2024-07-17 22:37:48 +02:00
Alexis Rouillard
d061d2259e Merge pull request #3448 from khaneliman/sway
sway/workspaces: remove deprecated field and ignore empty rewrite rules
2024-07-17 22:36:58 +02:00
Austin Horstman
90ac7d5d2c sway/workspaces: support ignore window-rewrite
Similar to hyprland implementation to ignore "" empty rules
2024-07-16 22:50:26 -05:00
Austin Horstman
4295faa7c4 hyprland/backend: throw runtime_error instead of log
Allows us to disable modules entirely when socket connection isn't
working. This is similar to how sway handles their socket connections
disabling modules. This supports a single waybar config for multiple
IPCs.
2024-07-16 18:40:47 -05:00
Austin Horstman
9c40137d05 sway/workspaces: clang-tidy 2024-07-16 18:33:43 -05:00
Austin Horstman
17132b250d sway/workspaces: remove deprecated field
Was deprecated a long time ago, we removed the Hyprland version.
Removing this, as well.
2024-07-16 18:33:43 -05:00
Alexis Rouillard
3f61df4e66 Merge pull request #3416 from Ape/pr/ignored_sink_never_running
pulseaudio: Consider ignored sinks never running
2024-07-16 14:20:32 +02:00
Alexis Rouillard
2d9cc623c2 Merge pull request #3422 from RobertMueller2/issue_feat_3256
feat/issue 3256: Toggle drawer state
2024-07-16 14:19:30 +02:00
Alexis Rouillard
f6ff6492ef Merge pull request #3441 from tchaikov/fmt-format-const 2024-07-16 08:12:29 +02:00
yangyingchao
b65ca334a8 fix #3442 2024-07-16 09:07:39 +08:00
Kefu Chai
b71dfce1f7 Fix build with fmt11
Since fmt 11.0.0, formatter:format() is required to be const. Mark
all of the specializations as const to be compatible with fmt 11.

This change is implemented in the same spirit of 7725f6ed5a.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
2024-07-16 08:46:55 +08:00
Alexis Rouillard
5f3a9d9423 Merge pull request #3440 from khaneliman/hyprland-fix
hyprland/window: fix crash when no return from socket
2024-07-15 19:16:06 +02:00
Austin Horstman
895c870d02 network: use fmt for format
Fixes the gentoo build
2024-07-15 09:44:39 -05:00
Austin Horstman
47d7324a19 client: clang-format 2024-07-15 09:02:27 -05:00
Austin Horstman
b19890c0b1 network: clang-format 2024-07-15 09:02:27 -05:00
Austin Horstman
b41fcdedff hyprland/window: fix crash when no return from socket
Gracefully handle lack of response from the IPC. If socket isn't
available, we already log the IPC isn't running. We dont need to crash
program just because we couldn't get responses. We can just return an
empty object.
2024-07-15 09:02:27 -05:00
Austin Horstman
0a78da0315 flake.lock: update 2024-07-15 08:55:32 -05:00
Alexis Rouillard
3d0fd14f3f Merge pull request #3435 from grimsteel/network-bssid 2024-07-13 07:05:19 +02:00
Siddhant Kameswar
e117bd7cb6 network: add bssid format replacement 2024-07-12 20:46:26 -05:00
Alexis Rouillard
2655660509 Merge pull request #3432 from BSFishy/readme_update 2024-07-11 21:01:52 +02:00
Matt Provost
0dd6af5a7e chore: update meson build command
Signed-off-by: Matt Provost <mattprovost6@gmail.com>
2024-07-11 08:01:54 -05:00
Rene D. Obermueller
e2e5d4d447 feat/issue 3256: Toggle drawer state 2024-07-10 07:42:18 +02:00
Alexis Rouillard
44f39ca0ce Merge pull request #3429 from ziyao233/outgoing/fix-fmt11 2024-07-09 21:08:16 +02:00
Yao Zi
7725f6ed5a Fix build with fmt11
Since fmt 11.0.0, formatter:format() is required to be const.Mark
affected functions as const to stay compatible with fmt 11.

Signed-off-by: Yao Zi <ziyao@disroot.org>
2024-07-08 20:28:26 +00:00
Alexis Rouillard
b26ab1f982 Merge pull request #3417 from yangyingchao/master
(temperature) fix clang-tidy lint .
2024-07-06 09:40:02 +02:00
yangyingchao
21d42baa8e (temperature) fix clang-tidy lint . 2024-07-06 08:16:45 +08:00
Lauri Niskanen
23274a9d57 pulseaudio: Consider ignored sinks never running
If the current sink happens to be ignored it is never considered running
so it will be replaced with another sink.
2024-07-06 01:30:11 +03:00
Austin Horstman
f78f29ee66 AModule: retain existing default behavior when unconfigured 2024-07-03 08:20:48 -05:00
Lars-Ragnar A. Haugen
7e2d8ab2a3 fix(#3239): hide cursor type change behind config flag
also, statically configure the cursor type
2024-07-03 08:11:15 -05:00
Alexis Rouillard
6f994c849d Merge pull request #3407 from khaneliman/rewrite-ignore
modules/hyprland/workspace: ignore empty window-rewrite
2024-07-03 08:41:15 +02:00
Austin Horstman
702e10649e modules/hyprland/workspace: ignore empty window-rewrite
I'd like to ignore some windows from having icons or empty space taken
on the bar. By filtering out empty repr we can supply rewrite rules that
will ignore them from being processed and showing an empty space or
default icon.
2024-07-03 00:21:49 -05:00
Alexis Rouillard
ca3877d0b8 Merge pull request #3325 from williamwith4ms/escape_&_in_mediaplayer.py
fix: display titles with '&' correctly
2024-07-02 22:34:10 +02:00
Alexis Rouillard
18e67afe09 Merge pull request #3404 from khaneliman/clang-tidy
treewide: clang-tidy
2024-07-02 22:33:52 +02:00
Austin Horstman
d66685a3aa util: clang-tidy 2024-07-02 10:38:58 -05:00
Austin Horstman
14c3235c12 src: clang-tidy 2024-07-02 10:38:58 -05:00
Alexis Rouillard
034760e8b6 Merge pull request #3399 from RobertMueller2/discussion_3373
fix example in manpage for pulseaudio/slider
2024-07-01 19:27:02 +02:00
Rene D. Obermueller
8f64caceb5 fix example in manpage for pulseaudio/slider 2024-07-01 18:30:58 +02:00
Alexis Rouillard
9997155617 Merge pull request #3267 from luttermann/upower_model
Add config option to select UPower device based on device model.
2024-07-01 09:22:59 +02:00
Alexis Rouillard
ff0f408ab8 Merge pull request #3380 from alerque/debug-keyboard-layout
Add keyboard debug info to hyprland language module
2024-07-01 09:22:08 +02:00
Alexis Rouillard
3262b375c9 Merge pull request #3377 from fglinka/master
Fix build error on Ubuntu 22.04
2024-07-01 09:21:56 +02:00
Alexis Rouillard
494cedb3bc Merge pull request #3393 from RobertMueller2/issue_3366
workaround for icons not rendered for apps existing before waybar launch
2024-07-01 09:21:40 +02:00
Alexis Rouillard
93000e3fca Merge pull request #3395 from RobertMueller2/issue_33341
add hide-empty-text option to hide module whenever output is empty but format is not
2024-07-01 09:20:56 +02:00
Alexis Rouillard
abfb654f90 Merge pull request #3396 from Alexays/update_flake_lock_action
flake.lock: Update
2024-07-01 09:20:39 +02:00
Alexis Rouillard
9c7a275cd6 Merge pull request #3247 from benjamin-voisin/menu
Adding the ability to have dropdown menu for modules
2024-07-01 09:18:13 +02:00
github-actions[bot]
f609042ece flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/ad57eef4ef0659193044870c731987a6df5cf56b?narHash=sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs%3D' (2024-05-29)
  → 'github:NixOS/nixpkgs/b2852eb9365c6de48ffb0dc2c9562591f652242a?narHash=sha256-C8e9S7RzshSdHB7L%2Bv9I51af1gDM5unhJ2xO1ywxNH8%3D' (2024-06-27)
2024-07-01 00:09:58 +00:00
Rene D. Obermueller
8eee568731 manpage for PR #3395 2024-06-30 21:23:54 +02:00
Rene D. Obermueller
fb24e8cb1f add hide-empty-text option to hide module whenever output is empty but format is not 2024-06-30 21:16:52 +02:00
Rene D. Obermueller
64a3133083 workaround for icons not rendered for apps existing before waybar launch 2024-06-30 14:52:49 +02:00
Alexis Rouillard
64f54e1fce Merge pull request #3389 from khaneliman/cleanup 2024-06-28 21:50:08 +02:00
Austin Horstman
c08660d837 modules/hyprland/backend: handle empty json responses
Fixes https://github.com/Alexays/Waybar/issues/3388
2024-06-28 13:18:44 -05:00
Austin Horstman
f6482c36dc hyprland: clangd cleanup 2024-06-28 13:06:14 -05:00
Alexis Rouillard
ccc3c13212 Update archlinux 2024-06-24 08:58:29 +02:00
Alexis Rouillard
1003c220be Merge pull request #3381 from OpenSauce04/deprioritize-battery-capacity
modules/battery: Deprioritize `capacity` /sys value for battery calculation if other methods are available
2024-06-24 08:57:30 +02:00
OpenSauce04
d68bcbd292 modules/battery: Deprioritize capacity /sys value 2024-06-23 14:39:41 +01:00
Caleb Maclennan
4126502fe8 Add debug information for keyboard layout selection 2024-06-23 00:02:48 +03:00
Felix Glinka
136b207a12 Add suggestion by clang-format 2024-06-21 16:43:21 +02:00
Felix Glinka
c4d769a586 Add explicit constructor to struct Profile
Not adding the constructor causes a compilation error on Ubuntu 22.04
with both clang 14 and gcc 11:

/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/alloc_traits.h:518:4: error: no matching function for call to 'construct_at'
          std::construct_at(__p, std::forward<_Args>(__args)...);
          ^~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/vector.tcc:117:21: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<waybar::modules::Profile>>::construct<waybar::modules::Profile, Glib::ustring, Glib::ustring>' requested here
            _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                           ^
../src/modules/power_profiles_daemon.cpp:106:26: note: in instantiation of function template specialization 'std::vector<waybar::modules::Profile>::emplace_back<Glib::ustring, Glib::ustring>' requested here
      availableProfiles_.emplace_back(std::move(name), std::move(driver));
                         ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_construct.h:94:5: note: candidate template ignored: substitution failure [with _Tp = waybar::modules::Profile, _Args = <Glib::ustring, Glib::ustring>]: no matching constructor for initialization of 'waybar::modules::Profile'
    construct_at(_Tp* __location, _Args&&... __args)
    ^
2024-06-21 15:32:04 +02:00
Alexis Rouillard
1c1089bade Merge pull request #3364 from khaneliman/persistent-rename 2024-06-16 09:47:03 +02:00
Austin Horstman
b114b1155c treewide: clang-format 2024-06-15 18:44:46 -05:00
Austin Horstman
f9e693b2a2 modules/hyprland/backend: remove testing log warn 2024-06-15 18:37:25 -05:00
Austin Horstman
bac4d03813 modules/hyprland/workspaces: remove deprecated property 2024-06-15 18:34:45 -05:00
Alexis Rouillard
9ba5825009 Merge pull request #3362 from lukasfink1/bt-dyn-controller-pr
Fix device list not being updated on selecting new bluetooth controller
2024-06-14 14:54:22 +02:00
Lukas Fink
0df3c84c0f Fix device list not being updated on selecting new bluetooth controller 2024-06-14 14:00:55 +02:00
Alexis Rouillard
3f49725834 Merge pull request #3360 from lukasfink1/bt-dyn-controller-pr
Add hotplug detection of bluetooth controllers
2024-06-14 09:06:57 +02:00
Lukas Fink
ab91d0bac3 Add hotplug detection of bluetooth controllers 2024-06-14 02:24:24 +02:00
Alexis Rouillard
79a6229522 Merge pull request #3353 from locked-out/muted_icons
Support for muted icons for pulseaudio devices/ports
2024-06-13 08:47:53 +02:00
Oliver Locke
01438f71a4 Added muted icons usage to waybar-pulseaudio man page 2024-06-13 15:59:42 +10:00
Alex
0bc43c1aa7 fix: lint 2024-06-12 23:08:27 +02:00
Alexis Rouillard
e8a2882796 Merge pull request #3320 from rtgiskard/dev
fix mpris: hide on current player vanished
2024-06-12 23:08:00 +02:00
Alexis Rouillard
b955e92276 Merge pull request #3244 from rtgiskard/fix.privacy
fix privacy: consider only configured modules
2024-06-12 23:07:51 +02:00
Alexis Rouillard
562e1e59b1 Merge pull request #3331 from Eisfunke/eisfunke/regex-collection-replace
Enable using capture groups in window-rewrite
2024-06-12 23:02:52 +02:00
Oliver Locke
892042eb92 Support muted icons for pulseaudio devices/ports 2024-06-12 17:03:39 +10:00
Alexis Rouillard
0251e25f23 Merge pull request #3345 from khaneliman/testing
Organize tests and start Hyprland testing
2024-06-10 21:38:01 +02:00
Alexis Rouillard
9bd09aa1b3 Merge pull request #3318 from Alexays/update_flake_lock_action
flake.lock: Update
2024-06-10 21:36:26 +02:00
giskard
1cd013a09b clock: respect tooltip option 2024-06-10 17:31:00 +08:00
giskard
7721dcdae8 mpris: some clang-tidy fix 2024-06-10 17:31:00 +08:00
giskard
e8d91eb14b mpris: hide on current player vanished 2024-06-10 17:31:00 +08:00
giskard
0773786766 privacy: consider only configured modules
along with the local clang-tidy warning fixes
2024-06-10 17:31:00 +08:00
Austin Horstman
71bb2b64bf subprojects/spdlog: bump spdlog
Fixes alpine build and is a commonly distributed version
2024-06-09 15:08:43 -05:00
Austin Horstman
06fa931de9 Dockerfiles/opensuse: add python3-packaging dependency 2024-06-09 13:41:01 -05:00
Austin Horstman
16ff5ee99b .github/workflows/linux: fail-fast 2024-06-09 13:41:01 -05:00
Austin Horstman
08c5df3633 modules/sway/workspaces: clang-format fix 2024-06-09 13:05:47 -05:00
Austin Horstman
b365831839 test/hyprland/backend: fix 2024-06-09 13:04:09 -05:00
Austin Horstman
959422f143 modules/hyprland/backend: protect against crash when XDG_RUNTIME_DIR not set 2024-06-09 10:18:20 -05:00
Austin Horstman
fa2e21dfd5 modules/hyprland/backend: move getSocketFolder to class 2024-06-09 10:18:20 -05:00
Austin Horstman
58e7abba2c tests: split into separate binaries 2024-06-08 22:43:48 -05:00
Austin Horstman
87eaa75b8a test/hyprland/backend: init 2024-06-08 22:43:48 -05:00
Austin Horstman
749f46f86f test/fixtures: Add GlibTestsFixture 2024-06-08 22:18:23 -05:00
Austin Horstman
0055ee6910 modules/hyprland/workspaces: remove unneccesary visibleWorkspaces variable 2024-06-07 13:56:49 -05:00
Austin Horstman
1b3b45779a modules/hyprland/backend: add getSocketFolder to header 2024-06-07 13:56:49 -05:00
Austin Horstman
e1a6d513cc test/config: add hyprland-workspaces config 2024-06-07 13:56:49 -05:00
Alexis Rouillard
f4da203915 Merge pull request #3336 from pjones/pjones/current-only 2024-06-07 08:35:02 +02:00
Peter Jones
637b220f82 sway/workspaces: Correct behavior when "current-only" is set
The `current-only` workspace setting should display only the active
workspace name as determined by its `focused` attribute.  However,
according to the `get_tree` output, workspaces that contain a focused
window will report `"focused": false` and the window will report
`"focused": true.` In this case, Waybar will not display a workspace
name at all.

This change updates the logic for determining if a workspace is
focused by also looking for a focused window.
2024-06-06 15:29:33 -07:00
Alexis Rouillard
365e77af58 Merge pull request #3332 from zspher/patch-wlr-taskbar 2024-06-05 22:07:55 +02:00
zspher
1b1442e3ba fix: taskbar not applying empty class on empty 2024-06-06 03:23:47 +08:00
Nicolas Lenz
d0a8c1d90d document capturing in window-rewrite 2024-06-05 20:16:30 +02:00
Nicolas Lenz
76c2f3166e format RegexCollection output using match results 2024-06-05 19:58:27 +02:00
williammmm
02eaa8b46e escape & in mediaplayer 2024-06-03 19:46:55 +01:00
giskard
4fbd4f212a privacy: consider only configured modules
along with the local clang-tidy warning fixes
2024-06-02 04:07:12 +08:00
github-actions[bot]
1474cc626d flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/44d0940ea560dee511026a53f0e2e2cde489b4d4?narHash=sha256-YN/Ciidm%2BA0fmJPWlHBGvVkcarYWSC%2Bs3NTPk/P%2Bq3c%3D' (2024-03-23)
  → 'github:NixOS/nixpkgs/ad57eef4ef0659193044870c731987a6df5cf56b?narHash=sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs%3D' (2024-05-29)
2024-06-01 00:09:05 +00:00
Alexis Rouillard
7163752aa0 Merge pull request #3317 from jackwilsdon/patch-1
Fix format replacement names
2024-05-31 17:59:21 +02:00
Jack Wilsdon
e9350cf25f Fix format replacement names 2024-05-31 14:31:29 +00:00
Alexis Rouillard
1a1a61e917 Merge pull request #3313 from zjeffer/fix/zjeffer/dockerfiles 2024-05-30 20:28:55 +02:00
zjeffer
532a90259b Dont fail docker image builds when another build fails 2024-05-30 20:18:33 +02:00
zjeffer
cf66604f85 fix fedora image 2024-05-30 19:35:32 +02:00
Alexis Rouillard
9332697ec1 Merge pull request #3064 from alttabber/master
Hyprland/Workspaces: Added option to hide non-visible special workspaces
2024-05-29 10:23:26 +02:00
Alexis Rouillard
ab735f723c Merge pull request #3309 from khaneliman/workflows 2024-05-29 08:07:05 +02:00
Austin Horstman
c5b5b64dfa modules/temperature: remove unused import 2024-05-28 15:41:10 -05:00
Austin Horstman
381fe83008 Makefile: fix meson deprecations 2024-05-28 15:41:10 -05:00
Austin Horstman
8adb0a5644 .github/workflows: fix meson deprecations 2024-05-28 15:41:10 -05:00
Benjamin Voisin
885290d907 📝 improve waybar-menu file 2024-05-28 21:02:07 +02:00
Benjamin Voisin
8220dbb513 📝 add a wayba-menu entry for documenting popup menus. 2024-05-28 20:42:47 +02:00
Benjamin Voisin
d9f2e0f7d2 📝 add menu config informations in manpages 2024-05-28 20:42:12 +02:00
Benjamin Voisin
161c8c4c47 🥅 do not crash when unable to make the menu
When the menu cannot be built (file not existing, or wrongly formatted),
     the menu is not created and a warning with an explanaition is
     displayed.
2024-05-28 19:31:26 +02:00
Benjamin Voisin
f3ed5ca5af 🌱 update default config with a menu example 2024-05-28 18:18:51 +02:00
Alexis Rouillard
0d518cb5a3 Merge pull request #3308 from khaneliman/bugfix/3280
modules/hyprland/backend: use /tmp
2024-05-28 18:00:53 +02:00
Austin Horstman
24e8766aaa hyprland/backend: use /tmp
Was hardcoded to /tmp in previous versions
2024-05-28 11:00:20 -05:00
alttabber
29e3d8c371 Hide non-visible special workspaces 2024-05-28 17:45:01 +02:00
Benjamin Voisin
c3581fb66b 🥅 only check menu if speciifed in the conf 2024-05-28 17:33:44 +02:00
Alexis Rouillard
57f2a57dac Merge pull request #3306 from JohnRTitor/workflows-patch
workflows: add nix workflows
2024-05-28 10:21:39 +02:00
John Titor
b6ca3ea4d9 worflows: add update-flake-lock action
automatically updates the nix flake lock file

runs once a month
2024-05-28 13:29:25 +05:30
John Titor
1a9952d0c0 workflows: add nix-test workflow
Checks the flake
Builds and tests the package
2024-05-28 13:29:25 +05:30
Alex
a4a4be3381 fix: lint 2024-05-28 09:19:21 +02:00
Alexis Rouillard
b7ce0b05b1 Merge pull request #3281 from VlctM/master
fix(#3211) change layer for mode invisible to nullopt
2024-05-28 09:18:36 +02:00
Alexis Rouillard
af87388eb4 Update docker.yml 2024-05-28 09:13:11 +02:00
Alexis Rouillard
2aa8008ae2 Merge pull request #3259 from zjeffer/docker-images-action
Add GitHub action for nightly Docker image building
2024-05-28 09:11:07 +02:00
Alexis Rouillard
d4413f5fa6 Merge pull request #3255 from LukashonakV/ISSUE#2240
Fix Clock. Tooltip calendar text overflows(#2240)
2024-05-28 09:08:31 +02:00
Alexis Rouillard
af7945197e Merge pull request #3260 from zjeffer/clang-tidy
clang-tidy fixes in the privacy module
2024-05-28 09:07:57 +02:00
Alexis Rouillard
ac2fa9870f Merge pull request #3241 from rtgiskard/master
temperature: allow hwmon-path-abs as array
2024-05-27 09:03:08 +02:00
Alexis Rouillard
a3ac6cb099 Merge pull request #3258 from zjeffer/master
Fix debian dockerfile
2024-05-27 08:47:33 +02:00
Alexis Rouillard
dc203b8439 Merge pull request #3299 from khaneliman/cleanup
hyprland: refactor and cleanup
2024-05-27 08:47:09 +02:00
Austin Horstman
f5bb086460 hyprland/workspaces: sort methods 2024-05-24 14:41:59 -05:00
Austin Horstman
9ba9d57c8c hyprland/windowcreationpayload: sort methods 2024-05-24 14:30:31 -05:00
Austin Horstman
82ae474002 hyprland/workspace: sort methods 2024-05-24 14:29:03 -05:00
Austin Horstman
56319a4705 hyprland/workspaces: break up implementations 2024-05-24 14:21:31 -05:00
Austin Horstman
07c91c200a hyprland/workspaces: break up headers 2024-05-24 14:21:31 -05:00
Austin Horstman
d73051e980 hyprland/workspaces: break up doUpdate 2024-05-24 14:21:31 -05:00
Austin Horstman
9fe51af6b0 hyprland/workspaces: break up parseConfig 2024-05-24 14:21:31 -05:00
Austin Horstman
e4353e548a .gitignore: add .ccls-cache 2024-05-24 14:21:31 -05:00
Alexis Rouillard
63bb338343 Merge pull request #3296 from unrealhoang/nix_cava_bump 2024-05-24 06:16:41 +02:00
Unreal Hoang
d012124c03 cava bump: 0.10.2 for nix 2024-05-24 09:18:25 +09:00
Alexis Rouillard
10664922c9 Merge pull request #3294 from LukashonakV/cava_bump 2024-05-23 23:03:49 +02:00
Viktar Lukashonak
60a613ae51 cava bump: 0.10.2
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2024-05-23 16:15:48 +03:00
Alexis Rouillard
35a9ca476d Merge pull request #3289 from yangyingchao/yc-hacking 2024-05-22 09:17:07 +02:00
yangyingchao
b8e68b0e63 (hyprland) fix crash when failed to parse IPC message
IPC messages are parsed in a dedicated thread, and the thread terminates when
an exception is not caught, which causes the waybar process to crash with
SIGABORT.

While this issue might be related to Hyprland, it is really annoying to see
waybar crash. It would be better to catch those exceptions and report errors
instead of crashing.
2024-05-22 12:52:01 +08:00
wmlhwl
b61ea62732 change layer for mode invisible to nullopt 2024-05-19 13:53:09 +02:00
Alexis Rouillard
8ad7c75d79 Merge pull request #3278 from LukashonakV/ISSUE#3276_cava 2024-05-18 11:42:14 +02:00
Viktar Lukashonak
5a1454ab31 Cava. $XDG_CONFIG_HOME validation
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2024-05-18 11:28:10 +03:00
Viktar Lukashonak
b288fdf8c1 ISSUE#2240. Clock Gtk::Label as a calendar tooltip
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2024-05-17 20:17:33 +03:00
Lasse Luttermann
d2a719d67c Redo to minimize code duplication. 2024-05-16 12:37:53 +02:00
Lasse Luttermann
28ef5b7db2 Fix formatting 2024-05-14 10:21:24 +02:00
Lasse Luttermann
6413f25b8d Add config option to select UPower device based on device model. 2024-05-14 10:13:22 +02:00
Alexis Rouillard
912d0e83f3 Merge pull request #3266 from luttermann/master 2024-05-14 09:37:11 +02:00
Lasse Luttermann
3c075bcc53 Fixed formatting 2024-05-14 08:26:44 +02:00
Lasse Luttermann
ba8a88acfb Do not try to compare a string that may be a null-pointer 2024-05-14 08:16:10 +02:00
zjeffer
e27488b48c clang-tidy improvements in privacy module 2024-05-11 16:19:46 +02:00
zjeffer
1828a94b6c clang-tidy: comment case styling options 2024-05-11 16:19:46 +02:00
zjeffer
49afcdf715 Add GitHub action for nightly Dockerfiles building 2024-05-11 16:16:02 +02:00
zjeffer
ff84c6dbaf fix debian dockerfile 2024-05-11 15:59:20 +02:00
Alexis Rouillard
cb2d54a237 Merge pull request #3248 from LukashonakV/ISSUE3223_upower 2024-05-10 06:50:40 +02:00
Viktar Lukashonak
5fe99ea0e1 Upower. Fix segmentation fault
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2024-05-10 00:00:47 +03:00
Benjamin Voisin
21751b2faa 🎨 clang-tidy 2024-05-09 20:59:25 +02:00
Benjamin Voisin
3b87b83076 ♻️ move GMenu to ALabel class 2024-05-09 18:34:26 +02:00
Benjamin Voisin
884b909e7d add GtkMenu to the AModule class
You can configure what key launch the menu with the "menu" element in
the config, the xml file that describes the menu with the "menu-file"
element in the config, and the actions of each buttons with the
"menu-actions" field.
2024-05-09 17:28:08 +02:00
giskard
b980dab6df doc: update waybar-temperature manual page 2024-05-08 23:56:44 +08:00
giskard
e298bf922f temperature: allow hwmon-path-abs as array 2024-05-08 23:55:51 +08:00
ViktarL
2ead1bbf84 Upower refactoring (#3220)
Signed-off-by: Viktar Lukashonak <myxabeer@gmail.com>
2024-05-07 10:29:52 +02:00
Tuur Vanhoutte
29917fb073 Fix hyprland/language events not working with keyboard names with commas in them (#3224) 2024-05-07 08:26:05 +02:00
Alex
e627879b16 chore: 0.10.3 2024-05-06 10:54:52 +02:00
Alex
0572e02d7e fix: lint 2024-05-06 10:51:30 +02:00
Eldar Yusupov
df1a9c5509 Remove listener when window is destroyed (#3215) 2024-05-06 10:51:14 +02:00
Lars-Ragnar A. Haugen
e7779b5458 feat(#3174): hover for whole group (#3201)
* feat(#3174): hover for whole group

* fix: target eventbox for class also

* fix: actually no reason to add handler, just override AModule

* fix: actually remove existing handler as well

drawer functionality still works from my testing. anything else to think
abotu?

* revert: keep id and class on original box

* refactor: clang-format group.hpp

* dev: try stop workflow
2024-05-06 10:51:03 +02:00
Alex
fc6d708fb6 chore: disable cland-tidy for now 2024-05-06 10:50:55 +02:00
Lars-Ragnar A. Haugen
a453ea3c70 fix(#3210): tooltip-format on custom modules not working in some cases (#3213) 2024-05-06 10:47:52 +02:00
Lars-Ragnar A. Haugen
8e8ce0c6bc feat(#3182): style tray icon on hover (#3203) 2024-05-06 10:47:25 +02:00
Bintang
c4e0c569aa flake: fix overlay not actually being applied (#3208) 2024-05-06 10:46:10 +02:00
柳芭老爹
231d6972d7 fix: custom module mediaplayer doesn't respect argument (#3198)
fix custom module mediaplayer which doesn't consider  --exclude argument on player appear
2024-05-03 08:47:41 +02:00
Jacob Birkett
50476edc98 Nix Flake: Fix overlay (again) (#3196) 2024-05-03 08:31:39 +02:00
Lars-Ragnar A. Haugen
0b6476da32 fix: set cursor appropriately on user event hover (#3195) 2024-05-02 22:09:21 +02:00
Jan Beich
79ae530bd2 pipewire: unbreak build on FreeBSD (#3193) 2024-05-02 08:31:40 +02:00
Tuur Vanhoutte
f41458ea24 Fix Hyprland socketpath changed to XDG_RUNTIME_DIR (#3183) 2024-04-29 19:46:28 +02:00
clayton craft
2481f7a292 upower: fix segfault by initializing lastWarningLevel (#3171)
fixes bd8b215416
2024-04-25 10:36:43 +02:00
Alex
61ac7e4e10 fix: lint 2024-04-25 00:16:15 +02:00
Milo Mordaunt
9a3044a54f Cursor change to indicate module clickability (#3108)
* Indicate clickability on mouse hover

* Avoid messy overrides situation

* Update AModule.cpp

* Update AModule.cpp

* Update AModule.cpp

* Update AModule.cpp

---------

Co-authored-by: Alexis Rouillard <alexisr245@gmail.com>
2024-04-25 00:15:40 +02:00
Alexis Rouillard
a597a994d2 Merge pull request #3167 from kiriDevs/battery-health-fix
fix(battery): Fix `{health}` format replacement
2024-04-24 23:16:18 +02:00
Kiri
a2c5a8215b style(battery): Capitalize float 'F' suffix 2024-04-24 15:07:26 +02:00
Kiri
57197b8e01 feat(battery): Also support energy_full (instead of charge_full) 2024-04-24 14:57:11 +02:00
Kiri
5c4e368819 style(battery): Indent level
It's now *inconsistent* in the file, but clang-tidy should be happy, sooo...
2024-04-24 09:05:11 +00:00
Kiri
8d962430dd fix(battery): Remove duplicate line
This is what happens when you copy-paste from GitHub actions
2024-04-24 09:02:03 +00:00
Kiri
54a85ea15f style: Apply clang-format change
At least I hope I copy-pased it correctly
2024-04-24 08:56:24 +00:00
Kiri
efa7dc7ba4 fix(battery): Register health replacement for main format 2024-04-24 10:34:35 +02:00
137 changed files with 3327 additions and 2068 deletions

View File

@@ -15,15 +15,15 @@ Checks: >
-readability-redundant-member-init,
-readability-redundant-string-init,
-readability-identifier-length
CheckOptions:
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.FunctionCase, value: camelBack }
- { key: readability-identifier-naming.VariableCase, value: camelBack }
- { key: readability-identifier-naming.PrivateMemberCase, value: camelBack }
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
- { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE }
# CheckOptions:
# - { key: readability-identifier-naming.NamespaceCase, value: lower_case }
# - { key: readability-identifier-naming.ClassCase, value: CamelCase }
# - { key: readability-identifier-naming.StructCase, value: CamelCase }
# - { key: readability-identifier-naming.FunctionCase, value: camelBack }
# - { key: readability-identifier-naming.VariableCase, value: camelBack }
# - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack }
# - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
# - { key: readability-identifier-naming.EnumCase, value: CamelCase }
# - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
# - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
# - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE }

32
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Build and Push Docker Image
on:
schedule:
# run every night at midnight
- cron: '0 0 * * *'
jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
fail-fast: false # don't fail the other jobs if one of the images fails to build
matrix:
os: [ 'alpine', 'archlinux', 'debian', 'fedora', 'gentoo', 'opensuse' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfiles/${{ matrix.os }}
push: true
tags: alexays/waybar:${{ matrix.os }}

View File

@@ -14,23 +14,22 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Test in FreeBSD VM
uses: cross-platform-actions/action@v0.23.0
uses: cross-platform-actions/action@v0.25.0
timeout-minutes: 180
env:
CPPFLAGS: '-isystem/usr/local/include'
LDFLAGS: '-L/usr/local/lib'
with:
operating_system: freebsd
version: "13.2"
version: "14.1"
environment_variables: CPPFLAGS LDFLAGS
sync_files: runner-to-vm
run: |
sudo sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
sudo pkg install -y git # subprojects/date
sudo pkg install -y catch evdev-proto gtk-layer-shell gtkmm30 jsoncpp \
libdbusmenu libevdev libfmt libmpdclient libudev-devd meson \
pkgconf pulseaudio scdoc sndio spdlog wayland-protocols upower \
pkgconf pipewire pulseaudio scdoc sndio spdlog wayland-protocols upower \
libinotify
meson build -Dman-pages=enabled
meson setup build -Dman-pages=enabled
ninja -C build
meson test -C build --no-rebuild --print-errorlogs --suite waybar

View File

@@ -9,6 +9,7 @@ concurrency:
jobs:
build:
strategy:
fail-fast: false
matrix:
distro:
- alpine
@@ -26,7 +27,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: configure
run: meson -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
- name: build
run: ninja -C build
- name: test

17
.github/workflows/nix-tests.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: "Nix-Tests"
on:
pull_request:
push:
jobs:
nix-flake-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- run: nix flake show
- run: nix flake check --print-build-logs
- run: nix build --print-build-logs

View File

@@ -0,0 +1,21 @@
name: update-flake-lock
on:
workflow_dispatch: # allows manual triggering
schedule:
- cron: '0 0 1 * *' # Run monthly
push:
paths:
- 'flake.nix'
jobs:
lockfile:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v21

2
.gitignore vendored
View File

@@ -48,3 +48,5 @@ packagecache
# Nix
result
result-*
.ccls-cache

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 iniparser fftw && \
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols glib2-devel 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

@@ -34,7 +34,7 @@ RUN apt update && \
libudev-dev \
libupower-glib-dev \
libwayland-dev \
libwireplumber-0.4-dev \
libwireplumber-0.5-dev \
libxkbcommon-dev \
libxkbregistry-dev \
locales \

View File

@@ -29,6 +29,6 @@ RUN dnf install -y @c-development \
'pkgconfig(wayland-client)' \
'pkgconfig(wayland-cursor)' \
'pkgconfig(wayland-protocols)' \
'pkgconfig(wireplumber-0.4)' \
'pkgconfig(wireplumber-0.5)' \
'pkgconfig(xkbregistry)' && \
dnf clean all -y

View File

@@ -6,4 +6,4 @@ RUN zypper -n up && \
zypper addrepo https://download.opensuse.org/repositories/X11:Wayland/openSUSE_Tumbleweed/X11:Wayland.repo | echo 'a' && \
zypper -n refresh && \
zypper -n install -t pattern devel_C_C++ && \
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel
zypper -n install git meson clang libinput10 libinput-devel pugixml-devel libwayland-client0 libwayland-cursor0 wayland-protocols-devel wayland-devel Mesa-libEGL-devel Mesa-libGLESv2-devel libgbm-devel libxkbcommon-devel libudev-devel libpixman-1-0-devel gtkmm3-devel jsoncpp-devel libxkbregistry-devel scdoc playerctl-devel python3-packaging

View File

@@ -3,11 +3,11 @@
default: build
build:
meson build
meson setup build
ninja -C build
build-debug:
meson build --buildtype=debug
meson setup build --buildtype=debug
ninja -C build
install: build

View File

@@ -48,7 +48,7 @@ An Ubuntu PPA with more recent versions is available
```bash
$ git clone https://github.com/Alexays/Waybar
$ cd Waybar
$ meson build
$ meson setup build
$ ninja -C build
$ ./build/waybar
# If you want to install it

6
flake.lock generated
View File

@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1711163522,
"narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=",
"lastModified": 1720957393,
"narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4",
"rev": "693bc46d169f5af9c992095736e82c3488bf7dbb",
"type": "github"
},
"original": {

View File

@@ -16,7 +16,12 @@
"x86_64-linux"
"aarch64-linux"
]
(system: func (import nixpkgs { inherit system; }));
(system: func (import nixpkgs {
inherit system;
overlays = with self.overlays; [
waybar
];
}));
mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
@@ -46,22 +51,25 @@
};
});
overlays.default = final: prev: {
waybar = final.callPackage ./nix/default.nix {
# take the first "version: '...'" from meson.build
version =
(builtins.head (builtins.split "'"
(builtins.elemAt
(builtins.split " version: '" (builtins.readFile ./meson.build))
2)))
+ "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
overlays = {
default = self.overlays.waybar;
waybar = final: prev: {
waybar = final.callPackage ./nix/default.nix {
waybar = prev.waybar;
# take the first "version: '...'" from meson.build
version =
(builtins.head (builtins.split "'"
(builtins.elemAt
(builtins.split " version: '" (builtins.readFile ./meson.build))
2)))
+ "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
};
};
};
packages = genSystems (pkgs:
let packages = self.overlays.default pkgs pkgs;
in packages // {
default = packages.waybar;
});
packages = genSystems (pkgs: {
default = self.packages.${pkgs.stdenv.hostPlatform.system}.waybar;
inherit (pkgs) waybar;
});
};
}

View File

@@ -27,6 +27,10 @@ class ALabel : public AModule {
bool handleToggle(GdkEventButton *const &e) override;
virtual std::string getState(uint8_t value, bool lesser = false);
std::map<std::string, GtkMenuItem *> submenus_;
std::map<std::string, std::string> menuActionsMap_;
static void handleGtkMenuEvent(GtkMenuItem *menuitem, gpointer data);
};
} // namespace waybar

View File

@@ -2,6 +2,7 @@
#include <glibmm/dispatcher.h>
#include <glibmm/markup.h>
#include <gtkmm.h>
#include <gtkmm/eventbox.h>
#include <json/json.h>
@@ -13,9 +14,9 @@ class AModule : public IModule {
public:
static constexpr const char *MODULE_CLASS = "module";
virtual ~AModule();
~AModule() override;
auto update() -> void override;
virtual auto refresh(int) -> void{};
virtual auto refresh(int shouldRefresh) -> void{};
operator Gtk::Widget &() override;
auto doAction(const std::string &name) -> void override;
@@ -31,21 +32,25 @@ class AModule : public IModule {
enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT };
SCROLL_DIR getScrollDir(GdkEventScroll *e);
bool tooltipEnabled();
bool tooltipEnabled() const;
const std::string name_;
const Json::Value &config_;
Gtk::EventBox event_box_;
virtual void setCursor(Gdk::CursorType const &c);
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleMouseEnter(GdkEventCrossing *const &ev);
virtual bool handleMouseLeave(GdkEventCrossing *const &ev);
virtual bool handleScroll(GdkEventScroll *);
virtual bool handleRelease(GdkEventButton *const &ev);
GObject *menu_;
private:
bool handleUserEvent(GdkEventButton *const &ev);
const bool isTooltip;
bool hasUserEvents_;
std::vector<int> pid_;
gdouble distance_scrolled_y_;
gdouble distance_scrolled_x_;

View File

@@ -9,6 +9,7 @@
#include <json/json.h>
#include <memory>
#include <optional>
#include <vector>
#include "AModule.hpp"
@@ -41,7 +42,7 @@ struct bar_margins {
};
struct bar_mode {
bar_layer layer;
std::optional<bar_layer> layer;
bool exclusive;
bool passthrough;
bool visible;

View File

@@ -11,15 +11,13 @@ namespace waybar {
class Group : public AModule {
public:
Group(const std::string&, const std::string&, const Json::Value&, bool);
Group(const std::string &, const std::string &, const Json::Value &, bool);
virtual ~Group() = default;
auto update() -> void override;
operator Gtk::Widget&() override;
operator Gtk::Widget &() override;
virtual Gtk::Box& getBox();
void addWidget(Gtk::Widget& widget);
bool handleMouseHover(GdkEventCrossing* const& e);
virtual Gtk::Box &getBox();
void addWidget(Gtk::Widget &widget);
protected:
Gtk::Box box;
@@ -27,9 +25,13 @@ class Group : public AModule {
Gtk::Revealer revealer;
bool is_first_widget = true;
bool is_drawer = false;
bool click_to_reveal = false;
std::string add_class_to_drawer_children;
void addHoverHandlerTo(Gtk::Widget& widget);
bool handleMouseEnter(GdkEventCrossing *const &ev) override;
bool handleMouseLeave(GdkEventCrossing *const &ev) override;
bool handleToggle(GdkEventButton *const &ev) override;
void show_group();
void hide_group();
};
} // namespace waybar

View File

@@ -49,6 +49,9 @@ class Bluetooth : public ALabel {
auto update() -> void override;
private:
static auto onObjectAdded(GDBusObjectManager*, GDBusObject*, gpointer) -> void;
static auto onObjectRemoved(GDBusObjectManager*, GDBusObject*, gpointer) -> void;
static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*,
gpointer) -> void;
static auto onInterfaceProxyPropertiesChanged(GDBusObjectManagerClient*, GDBusObjectProxy*,

View File

@@ -21,10 +21,12 @@ class Clock final : public ALabel {
auto doAction(const std::string&) -> void override;
private:
const std::locale locale_;
const std::locale m_locale_;
// tooltip
const std::string tlpFmt_;
std::string tlpText_{""}; // tooltip text to print
const std::string m_tlpFmt_;
std::string m_tlpText_{""}; // tooltip text to print
const Glib::RefPtr<Gtk::Label> m_tooltip_; // tooltip as a separate Gtk::Label
bool query_tlp_cb(int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& tooltip);
// Calendar
const bool cldInTooltip_; // calendar in tooltip
/*

View File

@@ -35,6 +35,7 @@ class Custom : public ALabel {
std::string id_;
std::string alt_;
std::string tooltip_;
const bool tooltip_format_enabled_;
std::vector<std::string> class_;
int percentage_;
FILE* fp_;

View File

@@ -14,7 +14,7 @@ namespace waybar::modules::dwl {
class Window : public AAppIconLabel, public sigc::trackable {
public:
Window(const std::string &, const waybar::Bar &, const Json::Value &);
virtual ~Window() = default;
~Window();
void handle_layout(const uint32_t layout);
void handle_title(const char *title);

View File

@@ -1,5 +1,6 @@
#pragma once
#include <filesystem>
#include <list>
#include <memory>
#include <mutex>
@@ -25,6 +26,10 @@ class IPC {
static std::string getSocket1Reply(const std::string& rq);
Json::Value getSocket1JsonReply(const std::string& rq);
static std::filesystem::path getSocketFolder(const char* instanceSig);
protected:
static std::filesystem::path socketFolder_;
private:
void startIPC();

View File

@@ -0,0 +1,61 @@
#pragma once
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <json/value.h>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <regex>
#include <string>
#include <variant>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "util/enum.hpp"
#include "util/regex_collection.hpp"
using WindowAddress = std::string;
namespace waybar::modules::hyprland {
class Workspaces;
class WindowCreationPayload {
public:
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_repr);
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_class, std::string window_title);
WindowCreationPayload(Json::Value const& client_data);
int incrementTimeSpentUncreated();
bool isEmpty(Workspaces& workspace_manager);
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
std::string repr(Workspaces& workspace_manager);
std::string getWorkspaceName() const { return m_workspaceName; }
WindowAddress getAddress() const { return m_windowAddress; }
void moveToWorksace(std::string& new_workspace_name);
private:
void clearAddr();
void clearWorkspaceName();
using Repr = std::string;
using ClassAndTitle = std::pair<std::string, std::string>;
std::variant<Repr, ClassAndTitle> m_window;
WindowAddress m_windowAddress;
std::string m_workspaceName;
int m_timeSpentUncreated = 0;
};
} // namespace waybar::modules::hyprland

View File

@@ -0,0 +1,88 @@
#pragma once
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <json/value.h>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <regex>
#include <string>
#include <variant>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "modules/hyprland/windowcreationpayload.hpp"
#include "util/enum.hpp"
#include "util/regex_collection.hpp"
using WindowAddress = std::string;
namespace waybar::modules::hyprland {
class Workspaces;
class Workspace {
public:
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
const Json::Value& clients_data = Json::Value::nullRef);
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
Gtk::Button& button() { return m_button; };
int id() const { return m_id; };
std::string name() const { return m_name; };
std::string output() const { return m_output; };
bool isActive() const { return m_isActive; };
bool isSpecial() const { return m_isSpecial; };
bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; };
bool isPersistentConfig() const { return m_isPersistentConfig; };
bool isPersistentRule() const { return m_isPersistentRule; };
bool isVisible() const { return m_isVisible; };
bool isEmpty() const { return m_windows == 0; };
bool isUrgent() const { return m_isUrgent; };
bool handleClicked(GdkEventButton* bt) const;
void setActive(bool value = true) { m_isActive = value; };
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
void setUrgent(bool value = true) { m_isUrgent = value; };
void setVisible(bool value = true) { m_isVisible = value; };
void setWindows(uint value) { m_windows = value; };
void setName(std::string const& value) { m_name = value; };
void setOutput(std::string const& value) { m_output = value; };
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
void insertWindow(WindowCreationPayload create_window_paylod);
std::string removeWindow(WindowAddress const& addr);
void initializeWindowMap(const Json::Value& clients_data);
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
std::optional<std::string> closeWindow(WindowAddress const& addr);
void update(const std::string& format, const std::string& icon);
private:
Workspaces& m_workspaceManager;
int m_id;
std::string m_name;
std::string m_output;
uint m_windows;
bool m_isActive = false;
bool m_isSpecial = false;
bool m_isPersistentRule = false; // represents the persistent state in hyprland
bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config
bool m_isUrgent = false;
bool m_isVisible = false;
std::map<WindowAddress, std::string> m_windowMap;
Gtk::Button m_button;
Gtk::Box m_content;
Gtk::Label m_label;
};
} // namespace waybar::modules::hyprland

View File

@@ -4,19 +4,18 @@
#include <gtkmm/label.h>
#include <json/value.h>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <regex>
#include <string>
#include <variant>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "modules/hyprland/windowcreationpayload.hpp"
#include "modules/hyprland/workspace.hpp"
#include "util/enum.hpp"
#include "util/regex_collection.hpp"
@@ -26,97 +25,6 @@ namespace waybar::modules::hyprland {
class Workspaces;
class WindowCreationPayload {
public:
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_repr);
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_class, std::string window_title);
WindowCreationPayload(Json::Value const& client_data);
int incrementTimeSpentUncreated();
bool isEmpty(Workspaces& workspace_manager);
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
std::string repr(Workspaces& workspace_manager);
std::string getWorkspaceName() const { return m_workspaceName; }
WindowAddress getAddress() const { return m_windowAddress; }
void moveToWorksace(std::string& new_workspace_name);
private:
void clearAddr();
void clearWorkspaceName();
using Repr = std::string;
using ClassAndTitle = std::pair<std::string, std::string>;
std::variant<Repr, ClassAndTitle> m_window;
WindowAddress m_windowAddress;
std::string m_workspaceName;
int m_timeSpentUncreated = 0;
};
class Workspace {
public:
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
const Json::Value& clients_data = Json::Value::nullRef);
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
Gtk::Button& button() { return m_button; };
int id() const { return m_id; };
std::string name() const { return m_name; };
std::string output() const { return m_output; };
bool isActive() const { return m_isActive; };
bool isSpecial() const { return m_isSpecial; };
bool isPersistent() const { return m_isPersistentRule || m_isPersistentConfig; };
bool isPersistentConfig() const { return m_isPersistentConfig; };
bool isPersistentRule() const { return m_isPersistentRule; };
bool isVisible() const { return m_isVisible; };
bool isEmpty() const { return m_windows == 0; };
bool isUrgent() const { return m_isUrgent; };
bool handleClicked(GdkEventButton* bt) const;
void setActive(bool value = true) { m_isActive = value; };
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
void setUrgent(bool value = true) { m_isUrgent = value; };
void setVisible(bool value = true) { m_isVisible = value; };
void setWindows(uint value) { m_windows = value; };
void setName(std::string const& value) { m_name = value; };
void setOutput(std::string const& value) { m_output = value; };
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
void insertWindow(WindowCreationPayload create_window_paylod);
std::string removeWindow(WindowAddress const& addr);
void initializeWindowMap(const Json::Value& clients_data);
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
std::optional<std::string> closeWindow(WindowAddress const& addr);
void update(const std::string& format, const std::string& icon);
private:
Workspaces& m_workspaceManager;
int m_id;
std::string m_name;
std::string m_output;
uint m_windows;
bool m_isActive = false;
bool m_isSpecial = false;
bool m_isPersistentRule = false; // represents the persistent state in hyprland
bool m_isPersistentConfig = false; // represents the persistent state in the Waybar config
bool m_isUrgent = false;
bool m_isVisible = false;
std::map<WindowAddress, std::string> m_windowMap;
Gtk::Button m_button;
Gtk::Box m_content;
Gtk::Label m_label;
};
class Workspaces : public AModule, public EventHandler {
public:
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
@@ -127,6 +35,7 @@ class Workspaces : public AModule, public EventHandler {
auto allOutputs() const -> bool { return m_allOutputs; }
auto showSpecial() const -> bool { return m_showSpecial; }
auto activeOnly() const -> bool { return m_activeOnly; }
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
auto getBarOutput() const -> std::string { return m_bar.output->name; }
@@ -141,11 +50,24 @@ class Workspaces : public AModule, public EventHandler {
void onEvent(const std::string& e) override;
void updateWindowCount();
void sortWorkspaces();
void createWorkspace(Json::Value const& workspaceData,
Json::Value const& clientsData = Json::Value::nullRef);
void createWorkspace(Json::Value const& workspace_data,
Json::Value const& clients_data = Json::Value::nullRef);
static Json::Value createMonitorWorkspaceData(std::string const& name,
std::string const& monitor);
void removeWorkspace(std::string const& name);
void setUrgentWorkspace(std::string const& windowaddress);
// Config
void parseConfig(const Json::Value& config);
auto populateIconsMap(const Json::Value& formatIcons) -> void;
static auto populateBoolConfig(const Json::Value& config, const std::string& key, bool& member)
-> void;
auto populateSortByConfig(const Json::Value& config) -> void;
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
void registerIpc();
// workspace events
@@ -171,7 +93,13 @@ class Workspaces : public AModule, public EventHandler {
int windowRewritePriorityFunction(std::string const& window_rule);
// Update methods
void doUpdate();
void removeWorkspacesToRemove();
void createWorkspacesToCreate();
static std::vector<std::string> getVisibleWorkspaces();
void updateWorkspaceStates();
bool updateWindowsToCreate();
void extendOrphans(int workspaceId, Json::Value const& clientsJson);
void registerOrphanWindow(WindowCreationPayload create_window_payload);
@@ -184,6 +112,7 @@ class Workspaces : public AModule, public EventHandler {
bool m_allOutputs = false;
bool m_showSpecial = false;
bool m_activeOnly = false;
bool m_specialVisibleOnly = false;
bool m_moveToMonitor = false;
Json::Value m_persistentWorkspaceConfig;

View File

@@ -40,6 +40,7 @@ class Network : public ALabel {
void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**);
void parseFreq(struct nlattr**);
void parseBssid(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
bool checkInterface(std::string name);
auto getInfo() -> void;
@@ -69,6 +70,7 @@ class Network : public ALabel {
std::string state_;
std::string essid_;
std::string bssid_;
bool carrier_;
std::string ifname_;
std::string ipaddr_;

View File

@@ -10,6 +10,8 @@ namespace waybar::modules {
struct Profile {
std::string name;
std::string driver;
Profile(std::string n, std::string d) : name(std::move(n)), driver(std::move(d)) {}
};
class PowerProfilesDaemon : public ALabel {

View File

@@ -1,10 +1,7 @@
#pragma once
#include <iostream>
#include <map>
#include <string>
#include "ALabel.hpp"
#include "gtkmm/box.h"
#include "modules/privacy/privacy_item.hpp"
#include "util/pipewire/pipewire_backend.hpp"

View File

@@ -2,9 +2,6 @@
#include <json/value.h>
#include <iostream>
#include <map>
#include <mutex>
#include <string>
#include "gtkmm/box.h"

View File

@@ -76,6 +76,8 @@ class Item : public sigc::trackable {
void makeMenu();
bool handleClick(GdkEventButton* const& /*ev*/);
bool handleScroll(GdkEventScroll* const&);
bool handleMouseEnter(GdkEventCrossing* const&);
bool handleMouseLeave(GdkEventCrossing* const&);
// smooth scrolling threshold
gdouble scroll_threshold_ = 0;

View File

@@ -19,7 +19,7 @@ namespace waybar::modules::sway {
class Workspaces : public AModule, public sigc::trackable {
public:
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~Workspaces() = default;
~Workspaces() override = default;
auto update() -> void override;
private:
@@ -38,10 +38,10 @@ class Workspaces : public AModule, public sigc::trackable {
Gtk::Button& addButton(const Json::Value&);
void onButtonReady(const Json::Value&, Gtk::Button&);
std::string getIcon(const std::string&, const Json::Value&);
const std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
uint16_t getWorkspaceIndex(const std::string& name) const;
std::string trimWorkspaceName(std::string);
bool handleScroll(GdkEventScroll*) override;
static std::string trimWorkspaceName(std::string);
bool handleScroll(GdkEventScroll* /*unused*/) override;
const Bar& bar_;
std::vector<Json::Value> workspaces_;

View File

@@ -0,0 +1,94 @@
#pragma once
#include <giomm/dbusconnection.h>
#include <gtkmm/icontheme.h>
#include <libupower-glib/upower.h>
#include <unordered_map>
#include "AIconLabel.hpp"
namespace waybar::modules {
class UPower final : public AIconLabel {
public:
UPower(const std::string &, const Json::Value &);
virtual ~UPower();
auto update() -> void override;
private:
const std::string NO_BATTERY{"battery-missing-symbolic"};
// Config
bool showIcon_{true};
bool hideIfEmpty_{true};
int iconSize_{20};
int tooltip_spacing_{4};
int tooltip_padding_{4};
Gtk::Box contentBox_; // tooltip box
std::string tooltipFormat_;
// UPower device info
struct upDevice_output {
UpDevice *upDevice{NULL};
double percentage{0.0};
double temperature{0.0};
guint64 time_full{0u};
guint64 time_empty{0u};
gchar *icon_name{(char *)'\0'};
bool upDeviceValid{false};
UpDeviceState state;
UpDeviceKind kind;
char *nativePath{(char *)'\0'};
char *model{(char *)'\0'};
};
// Technical variables
std::string nativePath_;
std::string model_;
std::string lastStatus_;
Glib::ustring label_markup_;
std::mutex mutex_;
Glib::RefPtr<Gtk::IconTheme> gtkTheme_;
bool sleeping_;
// Technical functions
void addDevice(UpDevice *);
void removeDevice(const gchar *);
void removeDevices();
void resetDevices();
void setDisplayDevice();
const Glib::ustring getText(const upDevice_output &upDevice_, const std::string &format);
bool queryTooltipCb(int, int, bool, const Glib::RefPtr<Gtk::Tooltip> &);
// DBUS variables
guint watcherID_;
Glib::RefPtr<Gio::DBus::Connection> conn_;
guint subscrID_{0u};
// UPower variables
UpClient *upClient_;
upDevice_output upDevice_; // Device to display
typedef std::unordered_map<std::string, upDevice_output> Devices;
Devices devices_;
bool upRunning_{true};
// DBus callbacks
void getConn_cb(Glib::RefPtr<Gio::AsyncResult> &result);
void onAppear(const Glib::RefPtr<Gio::DBus::Connection> &, const Glib::ustring &,
const Glib::ustring &);
void onVanished(const Glib::RefPtr<Gio::DBus::Connection> &, const Glib::ustring &);
void prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name, const Glib::ustring &object_path,
const Glib::ustring &interface_name, const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters);
// UPower callbacks
static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data);
static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data);
static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data);
// UPower secondary functions
void getUpDeviceInfo(upDevice_output &upDevice_);
};
} // namespace waybar::modules

View File

@@ -1,82 +0,0 @@
#pragma once
#include <libupower-glib/upower.h>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
#include "ALabel.hpp"
#include "glibconfig.h"
#include "gtkmm/box.h"
#include "gtkmm/image.h"
#include "gtkmm/label.h"
#include "modules/upower/upower_tooltip.hpp"
namespace waybar::modules::upower {
class UPower : public AModule {
public:
UPower(const std::string &, const Json::Value &);
virtual ~UPower();
auto update() -> void override;
private:
typedef std::unordered_map<std::string, UpDevice *> Devices;
const std::string DEFAULT_FORMAT = "{percentage}";
const std::string DEFAULT_FORMAT_ALT = "{percentage} {time}";
static void deviceAdded_cb(UpClient *client, UpDevice *device, gpointer data);
static void deviceRemoved_cb(UpClient *client, const gchar *objectPath, gpointer data);
static void deviceNotify_cb(UpDevice *device, GParamSpec *pspec, gpointer user_data);
static void prepareForSleep_cb(GDBusConnection *system_bus, const gchar *sender_name,
const gchar *object_path, const gchar *interface_name,
const gchar *signal_name, GVariant *parameters,
gpointer user_data);
static void upowerAppear(GDBusConnection *conn, const gchar *name, const gchar *name_owner,
gpointer data);
static void upowerDisappear(GDBusConnection *connection, const gchar *name, gpointer user_data);
void removeDevice(const gchar *objectPath);
void addDevice(UpDevice *device);
void setDisplayDevice();
void resetDevices();
void removeDevices();
bool show_tooltip_callback(int, int, bool, const Glib::RefPtr<Gtk::Tooltip> &tooltip);
bool handleToggle(GdkEventButton *const &) override;
std::string timeToString(gint64 time);
const std::string getDeviceStatus(UpDeviceState &state);
Gtk::Box box_;
Gtk::Image icon_;
Gtk::Label label_;
// Config
bool hideIfEmpty = true;
bool tooltip_enabled = true;
uint tooltip_spacing = 4;
uint tooltip_padding = 4;
uint iconSize = 20;
std::string format = DEFAULT_FORMAT;
std::string format_alt = DEFAULT_FORMAT_ALT;
Devices devices;
std::mutex m_Mutex;
UpClient *client;
UpDevice *displayDevice = nullptr;
guint login1_id;
GDBusConnection *login1_connection;
std::unique_ptr<UPowerTooltip> upower_tooltip;
std::string lastStatus;
const char *lastWarningLevel;
bool showAltText;
bool showIcon = true;
bool upowerRunning;
guint upowerWatcher_id;
std::string nativePath_;
};
} // namespace waybar::modules::upower

View File

@@ -1,33 +0,0 @@
#pragma once
#include <libupower-glib/upower.h>
#include <memory>
#include <unordered_map>
#include "gtkmm/box.h"
#include "gtkmm/label.h"
#include "gtkmm/window.h"
namespace waybar::modules::upower {
class UPowerTooltip : public Gtk::Window {
private:
typedef std::unordered_map<std::string, UpDevice*> Devices;
const std::string getDeviceIcon(UpDeviceKind& kind);
std::unique_ptr<Gtk::Box> contentBox;
uint iconSize;
uint tooltipSpacing;
uint tooltipPadding;
public:
UPowerTooltip(uint iconSize, uint tooltipSpacing, uint tooltipPadding);
virtual ~UPowerTooltip();
uint updateTooltip(Devices& devices);
};
} // namespace waybar::modules::upower

View File

@@ -56,10 +56,10 @@ class BacklightBackend {
void set_previous_best_device(const BacklightDevice *device);
void set_brightness(std::string preferred_device, ChangeType change_type, double step);
void set_brightness(const std::string &preferred_device, ChangeType change_type, double step);
void set_scaled_brightness(std::string preferred_device, int brightness);
int get_scaled_brightness(std::string preferred_device);
void set_scaled_brightness(const std::string &preferred_device, int brightness);
int get_scaled_brightness(const std::string &preferred_device);
bool is_login_proxy_initialized() const { return static_cast<bool>(login_proxy_); }
@@ -70,7 +70,7 @@ class BacklightBackend {
std::mutex udev_thread_mutex_;
private:
void set_brightness_internal(std::string device_name, int brightness, int max_brightness);
void set_brightness_internal(const std::string &device_name, int brightness, int max_brightness);
std::function<void()> on_updated_cb_;
std::chrono::milliseconds polling_interval_;

View File

@@ -64,7 +64,7 @@ struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
}
template <typename FormatContext>
auto format(const date::zoned_time<Duration, TimeZonePtr>& ztime, FormatContext& ctx) {
auto format(const date::zoned_time<Duration, TimeZonePtr>& ztime, FormatContext& ctx) const {
if (ctx.locale()) {
const auto loc = ctx.locale().template get<std::locale>();
return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime));

View File

@@ -45,7 +45,7 @@ struct formatter<pow_format> {
}
template <class FormatContext>
auto format(const pow_format& s, FormatContext& ctx) -> decltype(ctx.out()) {
auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) {
const char* units[] = {"", "k", "M", "G", "T", "P", nullptr};
auto base = s.binary_ ? 1024ull : 1000ll;
@@ -92,7 +92,7 @@ struct formatter<pow_format> {
template <>
struct formatter<Glib::ustring> : formatter<std::string> {
template <typename FormatContext>
auto format(const Glib::ustring& value, FormatContext& ctx) {
auto format(const Glib::ustring& value, FormatContext& ctx) const {
return formatter<std::string>::format(static_cast<std::string>(value), ctx);
}
};

View File

@@ -2,6 +2,8 @@
#include <pipewire/pipewire.h>
#include <unordered_map>
#include "util/backend_common.hpp"
#include "util/pipewire/privacy_node_info.hpp"

View File

@@ -5,6 +5,7 @@
#include <functional>
#include <regex>
#include <string>
#include <utility>
namespace waybar::util {
@@ -17,7 +18,7 @@ struct Rule {
// See https://en.cppreference.com/w/cpp/compiler_support/20 "Parenthesized initialization of
// aggregates"
Rule(std::regex rule, std::string repr, int priority)
: rule(rule), repr(repr), priority(priority) {}
: rule(std::move(rule)), repr(std::move(repr)), priority(priority) {}
};
int default_priority_function(std::string& key);
@@ -36,16 +37,17 @@ class RegexCollection {
std::map<std::string, std::string> regex_cache;
std::string default_repr;
std::string& find_match(std::string& value, bool& matched_any);
std::string find_match(std::string& value, bool& matched_any);
public:
RegexCollection() = default;
RegexCollection(const Json::Value& map, std::string default_repr = "",
std::function<int(std::string&)> priority_function = default_priority_function);
RegexCollection(
const Json::Value& map, std::string default_repr = "",
const std::function<int(std::string&)>& priority_function = default_priority_function);
~RegexCollection() = default;
std::string& get(std::string& value, bool& matched_any);
std::string& get(std::string& value);
};
} // namespace waybar::util
} // namespace waybar::util

View File

@@ -81,6 +81,19 @@ The *backlight* module displays the current backlight level.
default: 1.0 ++
The speed at which to change the brightness when scrolling.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# EXAMPLE:
```

View File

@@ -109,6 +109,19 @@ The *battery* module displays the current capacity and state (eg. charging) of y
default: false ++
Option to enable battery compatibility if not detected.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{capacity}*: Capacity in percentage

View File

@@ -129,6 +129,19 @@ Addressed by *bluetooth*
typeof: string ++
This format is used to define how each connected device should be displayed within the *device_enumerate* format replacement in the tooltip menu.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{status}*: Status of the bluetooth device.

View File

@@ -120,6 +120,18 @@ libcava lives in:
:[ string
:[ /dev/stdout
:[ It's impossible to set it. Waybar sets it to = /dev/stdout for internal needs
|[ *menu*
:[ string
:[
:[ Action that popups the menu.
|[ *menu-file*
:[ string
:[
:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu*
|[ *menu-actions*
:[ array
:[
:[ The actions corresponding to the buttons of the menu.
Configuration can be provided as:
- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped

View File

@@ -84,6 +84,18 @@ $XDG_CONFIG_HOME/waybar/config ++
:[ string
:[ same as format
:[ Tooltip on hover
|[ *menu*
:[ string
:[
:[ Action that popups the menu.
|[ *menu-file*
:[ string
:[
:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu*
|[ *menu-actions*
:[ array
:[
:[ The actions corresponding to the buttons of the menu.
View all valid format options in *strftime(3)* or have a look https://en.cppreference.com/w/cpp/chrono/duration/formatter

View File

@@ -21,6 +21,10 @@ Addressed by *custom/<name>*
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.
*hide-empty-text*: ++
typeof: bool ++
Disables the module when output is empty, but format might contain additional static content.
*exec-on-event*: ++
typeof: bool ++
default: true ++
@@ -121,6 +125,19 @@ Addressed by *custom/<name>*
default: false ++
Option to enable escaping of script output.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# RETURN-TYPE
When *return-type* is set to *json*, Waybar expects the *exec*-script to output its data in JSON format.

View File

@@ -93,6 +93,19 @@ Addressed by *disk*
typeof: string ++
Use with specific_free, specific_used, and specific_total to force calculation to always be in a certain unit. Accepts kB, kiB, MB, Mib, GB, GiB, TB, TiB.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{percentage_used}*: Percentage of disk in use.

View File

@@ -25,6 +25,19 @@ Addressed by *hyprland/language*
typeof: string ++
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS

View File

@@ -80,6 +80,19 @@ Addressed by *hyprland/submap*
default: Default ++
Option to set the submap name to display when not in an active submap.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# EXAMPLES

View File

@@ -26,6 +26,7 @@ Addressed by *hyprland/workspaces*
Regex rules to map window class to an icon or preferred method of representation for a workspace's window.
Keys are the rules, while the values are the methods of representation. Values may use the placeholders {class} and {title} to use the window's original class and/or title respectively.
Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching.
You may assign an empty value to a rule to have it ignored from generating any representation in workspaces.
*window-rewrite-default*:
typeof: string ++
@@ -42,6 +43,11 @@ Addressed by *hyprland/workspaces*
default: false ++
If set to true, special workspaces will be shown.
*special-visible-only*: ++
typeof: bool ++
default: false ++
If this and show-special are to true, special workspaces will be shown only if visible.
*all-outputs*: ++
typeof: bool ++
default: false ++
@@ -142,6 +148,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
"class<firefox> title<.*github.*>": "", // Windows whose class is "firefox" and title contains "github". Note that "class" always comes first.
"foot": "", // Windows that contain "foot" in either class or title. For optimization reasons, it will only match against a title if at least one other window explicitly matches against a title.
"code": "󰨞",
"title<.* - (.*) - VSCodium>": "codium $1" // captures part of the window title and formats it into output
}
}
```

View File

@@ -89,6 +89,19 @@ screensaver, also known as "presentation mode".
typeof: string ++
This format is used when the inhibit is deactivated.
*menu*: ++
typeof: string ++
Action that popups the menu. Cannot be "on-click".
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{status}*: status (*activated* or *deactivated*)

View File

@@ -76,6 +76,19 @@ See *systemd-inhibit*(1) for more information.
default: true ++
Option to disable tooltip on hover.
*menu*: ++
typeof: string ++
Action that popups the menu. Cannot be "on-click".
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{status}*: status (*activated* or *deactivated*)

View File

@@ -85,6 +85,19 @@ Addressed by *jack*
typeof: string ++
Command to execute when the module is updated.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{load}*: The current CPU load estimated by JACK.

View File

@@ -84,6 +84,19 @@ Addressed by *memory*
default: true ++
Option to disable tooltip on hover.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{percentage}*: Percentage of memory in use.

153
man/waybar-menu.5.scd Normal file
View File

@@ -0,0 +1,153 @@
waybar-menu(5)
# NAME
waybar - menu property
# OVERVIEW
Some modules support a 'menu', which allows to have a popup menu whan a defined
click is done over the module.
# PROPERTIES
A module that implements a 'menu' needs 3 properties defined in its config :
*menu*: ++
typeof: string ++
Action that popups the menu. The possibles actions are :
[- *Option*
:- *Description*
|[ *on-click*
:< When you left-click on the module
|[ *on-click-release*
:< When you release left button on the module
|[ *on-double-click*
:< When you double left click on the module
|[ *on-triple-click*
:< When you triple left click on the module
|[ *on-click-middle*
:< When you middle click on the module using mousewheel
|[ *on-click-middle-release*
:< When you release mousewheel button on the module
|[ *on-double-click-middle*
:< When you double middle click on the module
|[ *on-triple-click-middle*
:< When you triple middle click on the module
|[ *on-click-right*
:< When you right click on the module using
|[ *on-click-right-release*
:< When you release right button on the module
|[ *on-double-click-right*
:< When you double right click on the module
|[ *on-triple-click-right*
:< When you triple middle click on the module
|[ *on-click-backward*
:< When you click on the module using mouse backward button
|[ *on-click-backward-release*
:< When you release mouse backward button on the module
|[ *on-double-click-backward*
:< When you double click on the module using mouse backward button
|[ *on-triple-click-backward*
:< When you triple click on the module using mouse backawrd button
|[ *on-click-forward*
:< When you click on the module using mouse forward button
|[ *on-click-forward-release*
:< When you release mouse forward button on the module
|[ *on-double-click-forward*
:< When you double click on the module using mouse forward button
|[ *on-triple-click-forward*
:< When you triple click on the module using mouse forward button
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*.
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu. The identifiers of
each actions needs to exists as an id in the 'menu-file' for it to be linked
properly.
# MENU-FILE
The menu-file is an `.xml` file representing a GtkBuilder. Documentation for it
can be found here : https://docs.gtk.org/gtk4/class.Builder.html
Here, it needs to have an element of type GtkMenu with id "menu". Eeach actions
in *menu-actions* are linked to elements in the *menu-file* file by the id of
the elements.
# EXAMPLE
Module config :
```
"custom/power": {
"format" : "⏻ ",
"tooltip": false,
"menu": "on-click",
"menu-file": "~/.config/waybar/power_menu.xml",
"menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend",
"hibernate": "systemctl hibernate",
},
},
```
~/.config/waybar/power_menu.xml :
```
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkMenu" id="menu">
<child>
<object class="GtkMenuItem" id="suspend">
<property name="label">Suspend</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="hibernat">
<property name="label">Hibernate</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="shutdown">
<property name="label">Shutdown</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
</child>
<child>
<object class="GtkMenuItem" id="reboot">
<property name="label">Reboot</property>
</object>
</child>
</object>
</interface>
```
# STYLING MENUS
- *menu*
Style for the menu
- *menuitem*
Style for items in the menu
# EXAMPLE:
```
menu {
border-radius: 15px;
background: #161320;
color: #B5E8E0;
}
menuitem {
border-radius: 15px;
}
```

View File

@@ -162,6 +162,19 @@ Addressed by *mpd*
default: {} ++
Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*)
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
## WHEN PLAYING/PAUSED

View File

@@ -142,11 +142,11 @@ The *mpris* module displays currently playing media via libplayerctl.
*player-icons*: ++
typeof: map[string]string ++
Allows setting _{player-icon}_ based on player-name property.
Allows setting _{player_icon}_ based on player-name property.
*status-icons*: ++
typeof: map[string]string ++
Allows setting _{status-icon}_ based on player status (playing, paused, stopped).
Allows setting _{status_icon}_ based on player status (playing, paused, stopped).
# FORMAT REPLACEMENTS

View File

@@ -129,6 +129,19 @@ Addressed by *network*
typeof: string ++
This format is used when the displayed interface is disabled.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{ifname}*: Name of the network interface.
@@ -143,6 +156,8 @@ Addressed by *network*
*{essid}*: Name (SSID) of the wireless network.
*{bssid}*: MAC address (BSSID) of the wireless access point.
*{signalStrength}*: Signal strength of the wireless network.
*{signaldBm}*: Signal strength of the wireless network in dBm.

View File

@@ -31,7 +31,7 @@ The volume can be controlled by dragging the slider across the bar or clicking o
```
"modules-right": [
"pulseaudio-slider",
"pulseaudio/slider",
],
"pulseaudio/slider": {
"min": 0,

View File

@@ -113,6 +113,19 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c
typeof: array ++
Sinks in this list will not be shown as active sink by Waybar. Entries should be the sink's description field.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
@@ -142,6 +155,8 @@ If they are found in the current PulseAudio port name, the corresponding icons w
- *hifi*
- *phone*
Additionally, suffixing a device name or port with *-muted* will cause the icon
to be selected when the corresponding audio device is muted. This applies to *default* as well.
# EXAMPLES
@@ -152,10 +167,12 @@ If they are found in the current PulseAudio port name, the corresponding icons w
"format-muted": "",
"format-icons": {
"alsa_output.pci-0000_00_1f.3.analog-stereo": "",
"alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "",
"headphones": "",
"handsfree": "",
"headset": "",
"phone": "",
"phone-muted": "",
"portable": "",
"car": "",
"default": ["", ""]

View File

@@ -51,6 +51,19 @@ Addressed by *river/layout*
typeof: string ++
Command to execute when you right-click on the module.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# EXAMPLE
```

View File

@@ -65,6 +65,19 @@ Addressed by *river/mode*
typeof: double ++
Threshold to be used when scrolling.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# EXAMPLES
```

View File

@@ -49,6 +49,19 @@ Addressed by *river/window*
typeof: string ++
Command to execute when you right-click on the module.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# EXAMPLES
```

View File

@@ -74,6 +74,19 @@ cursor is over the module, and clicking on the module toggles mute.
typeof: double ++
Threshold to be used when scrolling.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{volume}*: Volume in percentage.

View File

@@ -39,6 +39,40 @@ You can apply special styling to any module for when the cursor hovers it.
}
```
## Setting cursor style
Most, if not all, module types support setting the `cursor` option. This is
configured in your `config.jsonc`. If set to `false`, when hovering the module a
"pointer"(as commonly known from web CSS styling `cursor: pointer`) style cursor
will not be shown. Default behavior is to indicate an interaction event is
available.
There are more cursor types to choose from by setting the `cursor` option to
a number, see Gdk3 official docs for all possible cursor types:
https://docs.gtk.org/gdk3/enum.CursorType.html.
However, note that not all cursor options listed may be available on
your system. If you attempt to use a cursor which is not available, the
application will crash.
Example of disabling pointer(`Gdk::Hand2`) cursor type on a custom module:
```
"custom/my-custom-module": {
...
"cursor": false,
}
```
Example of setting cursor type to `Gdk::Boat`(according to
https://docs.gtk.org/gdk3/enum.CursorType.html#boat):
```
"custom/my-custom-module": {
...
"cursor": 8,
}
```
# SEE ALSO
- *waybar(5)*

View File

@@ -32,6 +32,19 @@ Addressed by *sway/language*
default: true ++
Option to disable tooltip on hover.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{short}*: Short name of layout (e.g. "us"). Equals to {}.

View File

@@ -70,6 +70,19 @@ Addressed by *sway/mode*
default: true ++
Option to disable tooltip on hover.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# EXAMPLES
```

View File

@@ -36,6 +36,19 @@ Addressed by *sway/scratchpad*
default: {app}: {title} ++
The format, how information in the tooltip should be displayed.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{icon}*: Icon, as defined in *format-icons*.

View File

@@ -87,6 +87,7 @@ warp-on-scroll: ++
Regex rules to map window class to an icon or preferred method of representation for a workspace's window.
Keys are the rules, while the values are the methods of representation.
Rules may specify `class<...>`, `title<...>`, or both in order to fine-tune the matching.
You may assign an empty value to a rule to have it ignored from generating any representation in workspaces.
*window-rewrite-default*:
typeof: string ++
@@ -171,6 +172,7 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
"window-rewrite": {
"class<firefox>": "",
"class<kitty>": "k",
"title<.* - (.*) - VSCodium>": "codium $1" // captures part of the window title and formats it into output
}
}
```

View File

@@ -36,6 +36,19 @@ Addressed by *systemd-failed-units*
default: *true* ++
Option to hide this module when there is no failing units.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd.

View File

@@ -25,6 +25,7 @@ Addressed by *temperature*
*hwmon-path-abs*: ++
typeof: string ++
The path of the hwmon-directory of the device, e.g. */sys/devices/pci0000:00/0000:00:18.3/hwmon*. (Note that the subdirectory *hwmon/hwmon#*, where *#* is a number is not part of the path!) Has to be used together with *input-filename*.
This can also be an array of strings, for which, it just works like *hwmon-path*.
*input-filename*: ++
typeof: string ++
@@ -110,6 +111,19 @@ Addressed by *temperature*
default: true ++
Option to disable tooltip on hover.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{temperatureC}*: Temperature in Celsius.

View File

@@ -17,6 +17,12 @@ compatible devices in the tooltip.
The battery to monitor. Refer to the https://upower.freedesktop.org/docs/UpDevice.html#UpDevice--native-path ++
Can be obtained using `upower --dump`
*model*: ++
typeof: string ++
default: ++
The battery to monitor, based on the model. (this option is ignored if *native-path* is given). ++
Can be obtained using `upower --dump`
*icon-size*: ++
typeof: integer ++
default: 20 ++
@@ -62,6 +68,19 @@ compatible devices in the tooltip.
default: true ++
Option to disable battery icon.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{percentage}*: The battery capacity in percentage

View File

@@ -87,6 +87,19 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
default: 100 ++
The maximum volume that can be set, in percentage.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
# FORMAT REPLACEMENTS
*{volume}*: Volume in percentage.

View File

@@ -278,6 +278,11 @@ A group may hide all but one element, showing them only on mouse hover. In order
default: "hidden" ++
Defines the CSS class to be applied to the hidden elements.
*click-to-reveal*: ++
typeof: bool ++
default: false ++
Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events.
*transition-left-to-right*: ++
typeof: bool ++
default: true ++

View File

@@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.10.2',
version: '0.10.4',
license: 'MIT',
meson_version: '>= 0.59.0',
default_options : [
@@ -192,6 +192,7 @@ man_files = files(
'man/waybar-idle-inhibitor.5.scd',
'man/waybar-image.5.scd',
'man/waybar-states.5.scd',
'man/waybar-menu.5.scd',
'man/waybar-temperature.5.scd',
)
@@ -305,7 +306,9 @@ if true
'src/modules/hyprland/language.cpp',
'src/modules/hyprland/submap.cpp',
'src/modules/hyprland/window.cpp',
'src/modules/hyprland/workspace.cpp',
'src/modules/hyprland/workspaces.cpp',
'src/modules/hyprland/windowcreationpayload.cpp',
)
man_files += files(
'man/waybar-hyprland-language.5.scd',
@@ -335,10 +338,7 @@ endif
if (upower_glib.found() and not get_option('logind').disabled())
add_project_arguments('-DHAVE_UPOWER', language: 'cpp')
src_files += files(
'src/modules/upower/upower.cpp',
'src/modules/upower/upower_tooltip.cpp',
)
src_files += files('src/modules/upower.cpp')
man_files += files('man/waybar-upower.5.scd')
endif
@@ -467,7 +467,7 @@ if get_option('experimental')
endif
cava = dependency('cava',
version : '>=0.10.1',
version : '>=0.10.2',
required: get_option('cava'),
fallback : ['cava', 'cava_dep'],
not_found_message: 'cava is not found. Building waybar without cava')

View File

@@ -5,12 +5,12 @@
}:
let
libcava = rec {
version = "0.10.1";
version = "0.10.2";
src = pkgs.fetchFromGitHub {
owner = "LukashonakV";
repo = "cava";
rev = version;
hash = "sha256-iIYKvpOWafPJB5XhDOSIW9Mb4I3A4pcgIIPQdQYEqUw=";
hash = "sha256-jU7RQV2txruu/nUUl0TzjK4nai7G38J1rcTjO7UXumY=";
};
};
in

View File

@@ -30,7 +30,8 @@
"battery",
"battery#bat2",
"clock",
"tray"
"tray",
"custom/power"
],
// Modules configuration
// "sway/workspaces": {
@@ -198,5 +199,17 @@
"escape": true,
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder
// "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
},
"custom/power": {
"format" : "⏻ ",
"tooltip": false,
"menu": "on-click",
"menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder
"menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend",
"hibernate": "systemctl hibernate"
}
}
}

View File

@@ -113,6 +113,7 @@ class PlayerManager:
player_name = player.props.player_name
artist = player.get_artist()
title = player.get_title()
title = title.replace("&", "&amp;")
track_info = ""
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]:
@@ -136,6 +137,10 @@ class PlayerManager:
def on_player_appeared(self, _, player):
logger.info(f"Player has appeared: {player.name}")
if player.name in self.excluded_player:
logger.debug(
"New player appeared, but it's in exclude player list, skipping")
return
if player is not None and (self.selected_player is None or player.name == self.selected_player):
self.init_player(player)
else:

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkMenu" id="menu">
<child>
<object class="GtkMenuItem" id="suspend">
<property name="label">Suspend</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="hibernate">
<property name="label">Hibernate</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="shutdown">
<property name="label">Shutdown</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="delimiter1"/>
</child>
<child>
<object class="GtkMenuItem" id="reboot">
<property name="label">Reboot</property>
</object>
</child>
</object>
</interface>

View File

@@ -96,14 +96,14 @@ std::optional<Glib::ustring> getIconName(const std::string& app_identifier,
return app_identifier;
}
const auto app_identifier_desktop = app_identifier + "-desktop";
auto app_identifier_desktop = app_identifier + "-desktop";
if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) {
return app_identifier_desktop;
}
const auto first_space = app_identifier.find_first_of(' ');
auto first_space = app_identifier.find_first_of(' ');
if (first_space != std::string::npos) {
const auto first_word = toLowerCase(app_identifier.substr(0, first_space));
auto first_word = toLowerCase(app_identifier.substr(0, first_space));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}
@@ -111,7 +111,7 @@ std::optional<Glib::ustring> getIconName(const std::string& app_identifier,
const auto first_dash = app_identifier.find_first_of('-');
if (first_dash != std::string::npos) {
const auto first_word = toLowerCase(app_identifier.substr(0, first_dash));
auto first_word = toLowerCase(app_identifier.substr(0, first_dash));
if (DefaultGtkIconThemeWrapper::has_icon(first_word)) {
return first_word;
}

View File

@@ -2,6 +2,8 @@
#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <util/command.hpp>
namespace waybar {
@@ -9,7 +11,9 @@ namespace waybar {
ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
const std::string& format, uint16_t interval, bool ellipsize, bool enable_click,
bool enable_scroll)
: AModule(config, name, id, config["format-alt"].isString() || enable_click, enable_scroll),
: AModule(config, name, id,
config["format-alt"].isString() || config["menu"].isString() || enable_click,
enable_scroll),
format_(config_["format"].isString() ? config_["format"].asString() : format),
interval_(config_["interval"] == "once"
? std::chrono::seconds::max()
@@ -51,6 +55,47 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
}
}
// If a GTKMenu is requested in the config
if (config_["menu"].isString()) {
// Create the GTKMenu widget
try {
// Check that the file exists
std::string menuFile = config_["menu-file"].asString();
// Read the menu descriptor file
std::ifstream file(menuFile);
if (!file.is_open()) {
throw std::runtime_error("Failed to open file: " + menuFile);
}
std::stringstream fileContent;
fileContent << file.rdbuf();
GtkBuilder* builder = gtk_builder_new();
// Make the GtkBuilder and check for errors in his parsing
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
throw std::runtime_error("Error found in the file " + menuFile);
}
menu_ = gtk_builder_get_object(builder, "menu");
if (menu_ == nullptr) {
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
}
submenus_ = std::map<std::string, GtkMenuItem*>();
menuActionsMap_ = std::map<std::string, std::string>();
// Linking actions to the GTKMenu based on
for (Json::Value::const_iterator it = config_["menu-actions"].begin();
it != config_["menu-actions"].end(); ++it) {
std::string key = it.key().asString();
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str()));
menuActionsMap_[key] = it->asString();
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
(gpointer)menuActionsMap_[key].c_str());
}
} catch (std::runtime_error& e) {
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
}
}
if (config_["justify"].isString()) {
auto justify_str = config_["justify"].asString();
if (justify_str == "left") {
@@ -76,7 +121,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
}
if (format_icons.isArray()) {
auto size = format_icons.size();
if (size) {
if (size != 0U) {
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
format_icons = format_icons[idx];
}
@@ -102,7 +147,7 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
}
if (format_icons.isArray()) {
auto size = format_icons.size();
if (size) {
if (size != 0U) {
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1);
format_icons = format_icons[idx];
}
@@ -125,6 +170,10 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
return AModule::handleToggle(e);
}
void ALabel::handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data) {
waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
}
std::string ALabel::getState(uint8_t value, bool lesser) {
if (!config_["states"].isObject()) {
return "";

View File

@@ -1,28 +1,33 @@
#include "AModule.hpp"
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include <util/command.hpp>
#include "gdk/gdk.h"
#include "gdkmm/cursor.h"
namespace waybar {
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
bool enable_click, bool enable_scroll)
: name_(std::move(name)),
config_(std::move(config)),
: name_(name),
config_(config),
isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true},
distance_scrolled_y_(0.0),
distance_scrolled_x_(0.0) {
// Configure module action Map
const Json::Value actions{config_["actions"]};
for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) {
if (it.key().isString() && it->isString())
if (eventActionMap_.count(it.key().asString()) == 0) {
if (!eventActionMap_.contains(it.key().asString())) {
eventActionMap_.insert({it.key().asString(), it->asString()});
enable_click = true;
enable_scroll = true;
} else
spdlog::warn("Dublicate action is ignored: {0}", it.key().asString());
spdlog::warn("Duplicate action is ignored: {0}", it.key().asString());
else
spdlog::warn("Wrong actions section configuration. See config by index: {}", it.index());
}
@@ -31,17 +36,20 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
event_box_.signal_leave_notify_event().connect(sigc::mem_fun(*this, &AModule::handleMouseLeave));
// configure events' user commands
// hasUserEvent is true if any element from eventMap_ is satisfying the condition in the lambda
bool hasUserEvent =
// hasUserEvents is true if any element from eventMap_ is satisfying the condition in the lambda
bool hasUserEvents =
std::find_if(eventMap_.cbegin(), eventMap_.cend(), [&config](const auto& eventEntry) {
// True if there is any non-release type event
return eventEntry.first.second != GdkEventType::GDK_BUTTON_RELEASE &&
config[eventEntry.second].isString();
}) != eventMap_.cend();
if (enable_click || hasUserEvent) {
if (enable_click || hasUserEvents) {
hasUserEvents_ = true;
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle));
} else {
hasUserEvents_ = false;
}
bool hasReleaseEvent =
@@ -60,6 +68,17 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll));
}
// Respect user configuration of cursor
if (config_.isMember("cursor")) {
if (config_["cursor"].isBool() && config_["cursor"].asBool()) {
setCursor(Gdk::HAND2);
} else if (config_["cursor"].isInt()) {
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
} else {
spdlog::warn("unknown cursor option configured on module {}", name_);
}
}
}
AModule::~AModule() {
@@ -86,10 +105,33 @@ auto AModule::doAction(const std::string& name) -> void {
}
}
void AModule::setCursor(Gdk::CursorType const& c) {
auto gdk_window = event_box_.get_window();
if (gdk_window) {
auto cursor = Gdk::Cursor::create(c);
gdk_window->set_cursor(cursor);
} else {
// window may not be accessible yet, in this case,
// schedule another call for setting the cursor in 1 sec
Glib::signal_timeout().connect_seconds(
[this, c]() {
setCursor(c);
return false;
},
1);
}
}
bool AModule::handleMouseEnter(GdkEventCrossing* const& e) {
if (auto* module = event_box_.get_child(); module != nullptr) {
module->set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
}
// Default behavior indicating event availability
if (hasUserEvents_ && !config_.isMember("cursor")) {
setCursor(Gdk::HAND2);
}
return false;
}
@@ -97,6 +139,12 @@ bool AModule::handleMouseLeave(GdkEventCrossing* const& e) {
if (auto* module = event_box_.get_child(); module != nullptr) {
module->unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
}
// Default behavior indicating event availability
if (hasUserEvents_ && !config_.isMember("cursor")) {
setCursor(Gdk::ARROW);
}
return false;
}
@@ -108,12 +156,23 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
std::string format{};
const std::map<std::pair<uint, GdkEventType>, std::string>::const_iterator& rec{
eventMap_.find(std::pair(e->button, e->type))};
if (rec != eventMap_.cend()) {
// First call module actions
this->AModule::doAction(rec->second);
format = rec->second;
}
// Check that a menu has been configured
if (config_["menu"].isString()) {
// Check if the event is the one specified for the "menu" option
if (rec->second == config_["menu"].asString()) {
// Popup the menu
gtk_widget_show_all(GTK_WIDGET(menu_));
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
}
}
// Second call user scripts
if (!format.empty()) {
if (config_[format].isString())
@@ -135,7 +194,7 @@ AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) {
// ignore reverse-scrolling if event comes from a mouse wheel
GdkDevice* device = gdk_event_get_source_device((GdkEvent*)e);
if (device != NULL && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) {
if (device != nullptr && gdk_device_get_source(device) == GDK_SOURCE_MOUSE) {
reverse = reverse_mouse;
}
@@ -213,7 +272,7 @@ bool AModule::handleScroll(GdkEventScroll* e) {
return true;
}
bool AModule::tooltipEnabled() { return isTooltip; }
bool AModule::tooltipEnabled() const { return isTooltip; }
AModule::operator Gtk::Widget&() { return event_box_; }

View File

@@ -43,7 +43,7 @@ const Bar::bar_mode_map Bar::PRESET_MODES = { //
.visible = true}},
{"invisible",
{//
.layer = bar_layer::BOTTOM,
.layer = std::nullopt,
.exclusive = false,
.passthrough = true,
.visible = false}},
@@ -59,7 +59,7 @@ const std::string Bar::MODE_INVISIBLE = "invisible";
const std::string_view DEFAULT_BAR_ID = "bar-0";
/* Deserializer for enum bar_layer */
void from_json(const Json::Value& j, bar_layer& l) {
void from_json(const Json::Value& j, std::optional<bar_layer>& l) {
if (j == "bottom") {
l = bar_layer::BOTTOM;
} else if (j == "top") {
@@ -72,16 +72,16 @@ void from_json(const Json::Value& j, bar_layer& l) {
/* Deserializer for struct bar_mode */
void from_json(const Json::Value& j, bar_mode& m) {
if (j.isObject()) {
if (auto v = j["layer"]; v.isString()) {
if (const auto& v = j["layer"]; v.isString()) {
from_json(v, m.layer);
}
if (auto v = j["exclusive"]; v.isBool()) {
if (const auto& v = j["exclusive"]; v.isBool()) {
m.exclusive = v.asBool();
}
if (auto v = j["passthrough"]; v.isBool()) {
if (const auto& v = j["passthrough"]; v.isBool()) {
m.passthrough = v.asBool();
}
if (auto v = j["visible"]; v.isBool()) {
if (const auto& v = j["visible"]; v.isBool()) {
m.visible = v.asBool();
}
}
@@ -118,7 +118,7 @@ Glib::ustring to_string(Gtk::PositionType pos) {
* Assumes that all the values in the object are deserializable to the same type.
*/
template <typename Key, typename Value,
typename = std::enable_if_t<std::is_convertible<std::string, Key>::value>>
typename = std::enable_if_t<std::is_convertible_v<std::string, Key>>>
void from_json(const Json::Value& j, std::map<Key, Value>& m) {
if (j.isObject()) {
for (auto it = j.begin(); it != j.end(); ++it) {
@@ -316,13 +316,13 @@ void waybar::Bar::setMode(const std::string& mode) {
void waybar::Bar::setMode(const struct bar_mode& mode) {
auto* gtk_window = window.gobj();
auto layer = GTK_LAYER_SHELL_LAYER_BOTTOM;
if (mode.layer == bar_layer::TOP) {
layer = GTK_LAYER_SHELL_LAYER_TOP;
if (mode.layer == bar_layer::BOTTOM) {
gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_BOTTOM);
} else if (mode.layer == bar_layer::TOP) {
gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_TOP);
} else if (mode.layer == bar_layer::OVERLAY) {
layer = GTK_LAYER_SHELL_LAYER_OVERLAY;
gtk_layer_set_layer(gtk_window, GTK_LAYER_SHELL_LAYER_OVERLAY);
}
gtk_layer_set_layer(gtk_window, layer);
if (mode.exclusive) {
gtk_layer_auto_exclusive_zone_enable(gtk_window);
@@ -393,19 +393,18 @@ void waybar::Bar::setPosition(Gtk::PositionType position) {
}
}
void waybar::Bar::onMap(GdkEventAny*) {
void waybar::Bar::onMap(GdkEventAny* /*unused*/) {
/*
* Obtain a pointer to the custom layer surface for modules that require it (idle_inhibitor).
*/
auto gdk_window = window.get_window()->gobj();
auto* gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window));
setPassThrough(passthrough_);
}
void waybar::Bar::setVisible(bool value) {
visible = value;
void waybar::Bar::setVisible(bool visible) {
if (auto mode = config.get("mode", {}); mode.isString()) {
setMode(visible ? config["mode"].asString() : MODE_INVISIBLE);
} else {
@@ -473,7 +472,7 @@ void waybar::Bar::handleSignal(int signal) {
void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
waybar::Group* group = nullptr) {
auto module_list = group ? config[pos]["modules"] : config[pos];
auto module_list = group != nullptr ? config[pos]["modules"] : config[pos];
if (module_list.isArray()) {
for (const auto& name : module_list) {
try {
@@ -485,10 +484,10 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
auto id_name = ref.substr(6, hash_pos - 6);
auto class_name = hash_pos != std::string::npos ? ref.substr(hash_pos + 1) : "";
auto vertical = (group ? group->getBox().get_orientation() : box_.get_orientation()) ==
Gtk::ORIENTATION_VERTICAL;
auto vertical = (group != nullptr ? group->getBox().get_orientation()
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
auto group_module = new waybar::Group(id_name, class_name, config[ref], vertical);
auto* group_module = new waybar::Group(id_name, class_name, config[ref], vertical);
getModules(factory, ref, group_module);
module = group_module;
} else {
@@ -497,7 +496,7 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
std::shared_ptr<AModule> module_sp(module);
modules_all_.emplace_back(module_sp);
if (group) {
if (group != nullptr) {
group->addWidget(*module);
} else {
if (pos == "modules-left") {

View File

@@ -4,6 +4,7 @@
#include <spdlog/spdlog.h>
#include <iostream>
#include <utility>
#include "gtkmm/icontheme.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
@@ -11,13 +12,13 @@
#include "util/format.hpp"
waybar::Client *waybar::Client::inst() {
static auto c = new Client();
static auto *c = new Client();
return c;
}
void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
auto client = static_cast<Client *>(data);
auto *client = static_cast<Client *>(data);
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1 *>(wl_registry_bind(
@@ -42,7 +43,7 @@ void waybar::Client::handleOutput(struct waybar_output &output) {
.description = &handleOutputDescription,
};
// owned by output->monitor; no need to destroy
auto wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj());
auto *wl_output = gdk_wayland_monitor_get_wl_output(output.monitor->gobj());
output.xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wl_output));
zxdg_output_v1_add_listener(output.xdg_output.get(), &xdgOutputListener, &output);
}
@@ -61,7 +62,7 @@ std::vector<Json::Value> waybar::Client::getOutputConfigs(struct waybar_output &
}
void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_output*/) {
auto client = waybar::Client::inst();
auto *client = waybar::Client::inst();
try {
auto &output = client->getOutput(data);
/**
@@ -85,24 +86,24 @@ void waybar::Client::handleOutputDone(void *data, struct zxdg_output_v1 * /*xdg_
}
}
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
std::cerr << e.what() << '\n';
}
}
void waybar::Client::handleOutputName(void *data, struct zxdg_output_v1 * /*xdg_output*/,
const char *name) {
auto client = waybar::Client::inst();
auto *client = waybar::Client::inst();
try {
auto &output = client->getOutput(data);
output.name = name;
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
std::cerr << e.what() << '\n';
}
}
void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 * /*xdg_output*/,
const char *description) {
auto client = waybar::Client::inst();
auto *client = waybar::Client::inst();
try {
auto &output = client->getOutput(data);
const char *open_paren = strrchr(description, '(');
@@ -111,18 +112,19 @@ void waybar::Client::handleOutputDescription(void *data, struct zxdg_output_v1 *
size_t identifier_length = open_paren - description;
output.identifier = std::string(description, identifier_length - 1);
} catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
std::cerr << e.what() << '\n';
}
}
void waybar::Client::handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor) {
auto &output = outputs_.emplace_back();
output.monitor = monitor;
output.monitor = std::move(monitor);
handleOutput(output);
}
void waybar::Client::handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor) {
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model());
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer().c_str(),
monitor->get_model().c_str());
/* This event can be triggered from wl_display_roundtrip called by GTK or our code.
* Defer destruction of bars for the output to the next iteration of the event loop to avoid
* deleting objects referenced by currently executed code.
@@ -154,15 +156,15 @@ const std::string waybar::Client::getStyle(const std::string &style,
std::vector<std::string> search_files;
switch (appearance.value_or(portal->getAppearance())) {
case waybar::Appearance::LIGHT:
search_files.push_back("style-light.css");
search_files.emplace_back("style-light.css");
break;
case waybar::Appearance::DARK:
search_files.push_back("style-dark.css");
search_files.emplace_back("style-dark.css");
break;
case waybar::Appearance::UNKNOWN:
break;
}
search_files.push_back("style.css");
search_files.emplace_back("style.css");
css_file = Config::findConfigPath(search_files);
} else {
css_file = style;
@@ -196,7 +198,7 @@ void waybar::Client::bindInterfaces() {
wl_registry_add_listener(registry, &registry_listener, this);
wl_display_roundtrip(wl_display);
if (!gtk_layer_is_supported()) {
if (gtk_layer_is_supported() == 0) {
throw std::runtime_error("The Wayland compositor does not support wlr-layer-shell protocol");
}
@@ -233,11 +235,11 @@ int waybar::Client::main(int argc, char *argv[]) {
return 1;
}
if (show_help) {
std::cout << cli << std::endl;
std::cout << cli << '\n';
return 0;
}
if (show_version) {
std::cout << "Waybar v" << VERSION << std::endl;
std::cout << "Waybar v" << VERSION << '\n';
return 0;
}
if (!log_level.empty()) {

View File

@@ -21,10 +21,10 @@ const std::vector<std::string> Config::CONFIG_DIRS = {
const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR";
std::optional<std::string> tryExpandPath(const std::string base, const std::string filename) {
std::optional<std::string> tryExpandPath(const std::string &base, const std::string &filename) {
fs::path path;
if (filename != "") {
if (!filename.empty()) {
path = fs::path(base) / fs::path(filename);
} else {
path = fs::path(base);
@@ -129,9 +129,9 @@ bool isValidOutput(const Json::Value &config, const std::string &name,
if (config_output.substr(0, 1) == "!") {
if (config_output.substr(1) == name || config_output.substr(1) == identifier) {
return false;
} else {
continue;
}
continue;
}
if (config_output == name || config_output == identifier) {
return true;
@@ -142,7 +142,9 @@ bool isValidOutput(const Json::Value &config, const std::string &name,
}
}
return false;
} else if (config["output"].isString()) {
}
if (config["output"].isString()) {
auto config_output = config["output"].asString();
if (!config_output.empty()) {
if (config_output.substr(0, 1) == "!") {

View File

@@ -70,7 +70,7 @@
#include "modules/gamemode.hpp"
#endif
#ifdef HAVE_UPOWER
#include "modules/upower/upower.hpp"
#include "modules/upower.hpp"
#endif
#ifdef HAVE_PIPEWIRE
#include "modules/privacy/privacy.hpp"
@@ -130,7 +130,7 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
#endif
#ifdef HAVE_UPOWER
if (ref == "upower") {
return new waybar::modules::upower::UPower(id, config_[name]);
return new waybar::modules::UPower(id, config_[name]);
}
#endif
#ifdef HAVE_PIPEWIRE

View File

@@ -4,7 +4,7 @@
#include <util/command.hpp>
#include "gdkmm/device.h"
#include "gtkmm/enums.h"
#include "gtkmm/widget.h"
namespace waybar {
@@ -19,9 +19,9 @@ const Gtk::RevealerTransitionType getPreferredTransitionType(bool is_vertical) {
if (is_vertical) {
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_UP;
} else {
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT;
}
return Gtk::RevealerTransitionType::REVEALER_TRANSITION_TYPE_SLIDE_LEFT;
}
Group::Group(const std::string& name, const std::string& id, const Json::Value& config,
@@ -62,6 +62,7 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
const bool left_to_right = (drawer_config["transition-left-to-right"].isBool()
? drawer_config["transition-left-to-right"].asBool()
: true);
click_to_reveal = drawer_config["click-to-reveal"].asBool();
auto transition_type = getPreferredTransitionType(vertical);
@@ -78,32 +79,47 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
} else {
box.pack_start(revealer);
}
addHoverHandlerTo(revealer);
}
event_box_.add(box);
}
bool Group::handleMouseHover(GdkEventCrossing* const& e) {
switch (e->type) {
case GDK_ENTER_NOTIFY:
revealer.set_reveal_child(true);
break;
case GDK_LEAVE_NOTIFY:
revealer.set_reveal_child(false);
break;
default:
break;
}
void Group::show_group() {
box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
revealer.set_reveal_child(true);
}
void Group::hide_group() {
box.unset_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
revealer.set_reveal_child(false);
}
bool Group::handleMouseEnter(GdkEventCrossing* const& e) {
if (!click_to_reveal) {
show_group();
}
return false;
}
bool Group::handleMouseLeave(GdkEventCrossing* const& e) {
if (!click_to_reveal) {
hide_group();
}
return false;
}
bool Group::handleToggle(GdkEventButton* const& e) {
if (!click_to_reveal || e->button != 1) {
return false;
}
if (box.get_state_flags() & Gtk::StateFlags::STATE_FLAG_PRELIGHT) {
hide_group();
} else {
show_group();
}
return true;
}
void Group::addHoverHandlerTo(Gtk::Widget& widget) {
widget.add_events(Gdk::EventMask::ENTER_NOTIFY_MASK | Gdk::EventMask::LEAVE_NOTIFY_MASK);
widget.signal_enter_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover));
widget.signal_leave_notify_event().connect(sigc::mem_fun(*this, &Group::handleMouseHover));
}
auto Group::update() -> void {
// noop
}
@@ -113,17 +129,13 @@ Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer
void Group::addWidget(Gtk::Widget& widget) {
getBox().pack_start(widget, false, false);
if (is_drawer) {
// Necessary because of GTK's hitbox detection
addHoverHandlerTo(widget);
if (!is_first_widget) {
widget.get_style_context()->add_class(add_class_to_drawer_children);
}
if (is_drawer && !is_first_widget) {
widget.get_style_context()->add_class(add_class_to_drawer_children);
}
is_first_widget = false;
}
Group::operator Gtk::Widget&() { return box; }
Group::operator Gtk::Widget&() { return event_box_; }
} // namespace waybar

View File

@@ -13,7 +13,8 @@ std::list<pid_t> reap;
volatile bool reload;
void* signalThread(void* args) {
int err, signum;
int err;
int signum;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
@@ -46,7 +47,7 @@ void* signalThread(void* args) {
}
}
void startSignalThread(void) {
void startSignalThread() {
int err;
sigset_t mask;
sigemptyset(&mask);
@@ -71,7 +72,7 @@ void startSignalThread(void) {
int main(int argc, char* argv[]) {
try {
auto client = waybar::Client::inst();
auto* client = waybar::Client::inst();
std::signal(SIGUSR1, [](int /*signal*/) {
for (auto& bar : waybar::Client::inst()->bars) {

View File

@@ -272,13 +272,6 @@ waybar::modules::Battery::getInfos() {
// Some battery will report current and charge in μA/μAh.
// Scale these by the voltage to get μW/μWh.
uint32_t capacity = 0;
bool capacity_exists = false;
if (fs::exists(bat / "capacity")) {
capacity_exists = true;
std::ifstream(bat / "capacity") >> capacity;
}
uint32_t current_now = 0;
bool current_now_exists = false;
if (fs::exists(bat / "current_now")) {
@@ -371,12 +364,30 @@ waybar::modules::Battery::getInfos() {
if (charge_full_exists && charge_full_design_exists) {
float batHealthPercent = ((float)charge_full / charge_full_design) * 100;
if (mainBatHealthPercent == 0.0f || batHealthPercent < mainBatHealthPercent) {
if (mainBatHealthPercent == 0.0F || batHealthPercent < mainBatHealthPercent) {
mainBatHealthPercent = batHealthPercent;
}
} else if (energy_full_exists && energy_full_design_exists) {
float batHealthPercent = ((float)energy_full / energy_full_design) * 100;
if (mainBatHealthPercent == 0.0F || batHealthPercent < mainBatHealthPercent) {
mainBatHealthPercent = batHealthPercent;
}
}
}
uint32_t capacity = 0;
bool capacity_exists = false;
if (charge_now_exists && charge_full_exists && charge_full != 0) {
capacity_exists = true;
capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full;
} else if (energy_now_exists && energy_full_exists && energy_full != 0) {
capacity_exists = true;
capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full;
} else if (fs::exists(bat / "capacity")) {
capacity_exists = true;
std::ifstream(bat / "capacity") >> capacity;
}
if (!voltage_now_exists) {
if (power_now_exists && current_now_exists && current_now != 0) {
voltage_now_exists = true;
@@ -417,13 +428,7 @@ waybar::modules::Battery::getInfos() {
}
if (!capacity_exists) {
if (charge_now_exists && charge_full_exists && charge_full != 0) {
capacity_exists = true;
capacity = 100 * (uint64_t)charge_now / (uint64_t)charge_full;
} else if (energy_now_exists && energy_full_exists && energy_full != 0) {
capacity_exists = true;
capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full;
} else if (charge_now_exists && energy_full_exists && voltage_now_exists) {
if (charge_now_exists && energy_full_exists && voltage_now_exists) {
if (!charge_full_exists && voltage_now != 0) {
charge_full_exists = true;
charge_full = 1000000 * (uint64_t)energy_full / (uint64_t)voltage_now;
@@ -711,10 +716,10 @@ auto waybar::modules::Battery::update() -> void {
} else {
event_box_.show();
auto icons = std::vector<std::string>{status + "-" + state, status, state};
label_.set_markup(
fmt::format(fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power),
fmt::arg("icon", getIcon(capacity, icons)),
fmt::arg("time", time_remaining_formatted), fmt::arg("cycles", cycles)));
label_.set_markup(fmt::format(
fmt::runtime(format), fmt::arg("capacity", capacity), fmt::arg("power", power),
fmt::arg("icon", getIcon(capacity, icons)), fmt::arg("time", time_remaining_formatted),
fmt::arg("cycles", cycles), fmt::arg("health", fmt::format("{:.3}", health))));
}
// Call parent update
ALabel::update();

View File

@@ -98,30 +98,30 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
std::back_inserter(device_preference_), [](auto x) { return x.asString(); });
}
// NOTE: assumption made that the controller that is selected stays unchanged
// for duration of the module
if (cur_controller_ = findCurController(); !cur_controller_) {
if (config_["controller-alias"].isString()) {
spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'",
config_["controller-alias"].asString());
spdlog::warn("no bluetooth controller found with alias '{}'",
config_["controller-alias"].asString());
} else {
spdlog::error("findCurController() failed: no bluetooth controller found");
spdlog::warn("no bluetooth controller found");
}
update();
} else {
// These calls only make sense if a controller could be found
// This call only make sense if a controller could be found
findConnectedDevices(cur_controller_->path, connected_devices_);
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
}
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this);
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
#ifdef WANT_RFKILL
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
#endif
}
dp.emit();
}
@@ -282,6 +282,46 @@ auto waybar::modules::Bluetooth::update() -> void {
ALabel::update();
}
auto waybar::modules::Bluetooth::onObjectAdded(GDBusObjectManager* manager, GDBusObject* object,
gpointer user_data) -> void {
ControllerInfo info;
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
if (!bt->cur_controller_.has_value() && bt->getControllerProperties(object, info) &&
(!bt->config_["controller-alias"].isString() ||
bt->config_["controller-alias"].asString() == info.alias)) {
bt->cur_controller_ = std::move(info);
bt->dp.emit();
}
}
auto waybar::modules::Bluetooth::onObjectRemoved(GDBusObjectManager* manager, GDBusObject* object,
gpointer user_data) -> void {
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
GDBusProxy* proxy_controller;
if (!bt->cur_controller_.has_value()) {
return;
}
proxy_controller = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1"));
if (proxy_controller != NULL) {
std::string object_path = g_dbus_object_get_object_path(object);
if (object_path == bt->cur_controller_->path) {
bt->cur_controller_ = bt->findCurController();
if (bt->cur_controller_.has_value()) {
bt->connected_devices_.clear();
bt->findConnectedDevices(bt->cur_controller_->path, bt->connected_devices_);
}
bt->dp.emit();
}
g_object_unref(proxy_controller);
}
}
// NOTE: only for when the org.bluez.Battery1 interface is added/removed after/before a device is
// connected/disconnected
auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* manager,
@@ -292,11 +332,13 @@ auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* m
std::string object_path = g_dbus_proxy_get_object_path(G_DBUS_PROXY(interface));
if (interface_name == "org.bluez.Battery1") {
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(),
[object_path](auto d) { return d.path == object_path; });
if (device != bt->connected_devices_.end()) {
device->battery_percentage = bt->getDeviceBatteryPercentage(object);
bt->dp.emit();
if (bt->cur_controller_.has_value()) {
auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(),
[object_path](auto d) { return d.path == object_path; });
if (device != bt->connected_devices_.end()) {
device->battery_percentage = bt->getDeviceBatteryPercentage(object);
bt->dp.emit();
}
}
}
}
@@ -309,6 +351,11 @@ auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged(
std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy));
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
if (!bt->cur_controller_.has_value()) {
return;
}
if (interface_name == "org.bluez.Adapter1") {
if (object_path == bt->cur_controller_->path) {
bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), *bt->cur_controller_);

View File

@@ -8,13 +8,7 @@ waybar::modules::Cava::Cava(const std::string& id, const Json::Value& config)
char cfgPath[PATH_MAX];
cfgPath[0] = '\0';
if (config_["cava_config"].isString()) {
std::string strPath{config_["cava_config"].asString()};
const std::string fnd{"XDG_CONFIG_HOME"};
const std::string::size_type npos{strPath.find("$" + fnd)};
if (npos != std::string::npos) strPath.replace(npos, fnd.length() + 1, getenv(fnd.c_str()));
strcpy(cfgPath, strPath.data());
}
if (config_["cava_config"].isString()) strcpy(cfgPath, config_["cava_config"].asString().data());
// Load cava config
error_.length = 0;

View File

@@ -1,5 +1,6 @@
#include "modules/clock.hpp"
#include <gtkmm/tooltip.h>
#include <spdlog/spdlog.h>
#include <chrono>
@@ -11,20 +12,22 @@
#ifdef HAVE_LANGINFO_1STDAY
#include <langinfo.h>
#include <locale.h>
#include <clocale>
#endif
namespace fmt_lib = waybar::util::date::format;
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")},
tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""},
cldInTooltip_{tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos},
tzInTooltip_{tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
m_locale_{std::locale(config_["locale"].isString() ? config_["locale"].asString() : "")},
m_tlpFmt_{(config_["tooltip-format"].isString()) ? config_["tooltip-format"].asString() : ""},
m_tooltip_{new Gtk::Label()},
cldInTooltip_{m_tlpFmt_.find("{" + kCldPlaceholder + "}") != std::string::npos},
tzInTooltip_{m_tlpFmt_.find("{" + kTZPlaceholder + "}") != std::string::npos},
tzCurrIdx_{0},
ordInTooltip_{tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
tlpText_ = tlpFmt_;
ordInTooltip_{m_tlpFmt_.find("{" + kOrdPlaceholder + "}") != std::string::npos} {
m_tlpText_ = m_tlpFmt_;
if (config_["timezones"].isArray() && !config_["timezones"].empty()) {
for (const auto& zone_name : config_["timezones"]) {
@@ -124,17 +127,28 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
}
if (tooltipEnabled()) {
label_.set_has_tooltip(true);
label_.signal_query_tooltip().connect(sigc::mem_fun(*this, &Clock::query_tlp_cb));
}
thread_ = [this] {
dp.emit();
thread_.sleep_for(interval_ - system_clock::now().time_since_epoch() % interval_);
};
}
bool waybar::modules::Clock::query_tlp_cb(int, int, bool,
const Glib::RefPtr<Gtk::Tooltip>& tooltip) {
tooltip->set_custom(*m_tooltip_.get());
return true;
}
auto waybar::modules::Clock::update() -> void {
const auto* tz = tzList_[tzCurrIdx_] != nullptr ? tzList_[tzCurrIdx_] : local_zone();
const zoned_time now{tz, floor<seconds>(system_clock::now())};
label_.set_markup(fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(now)));
label_.set_markup(fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(now)));
if (tooltipEnabled()) {
const year_month_day today{floor<days>(now.get_local_time())};
@@ -147,18 +161,19 @@ auto waybar::modules::Clock::update() -> void {
if (ordInTooltip_) ordText_ = get_ordinal_date(shiftedDay);
if (tzInTooltip_ || cldInTooltip_ || ordInTooltip_) {
// std::vformat doesn't support named arguments.
tlpText_ = std::regex_replace(tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_);
tlpText_ =
std::regex_replace(tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_);
tlpText_ =
std::regex_replace(tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_);
m_tlpText_ =
std::regex_replace(m_tlpFmt_, std::regex("\\{" + kTZPlaceholder + "\\}"), tzText_);
m_tlpText_ =
std::regex_replace(m_tlpText_, std::regex("\\{" + kCldPlaceholder + "\\}"), cldText_);
m_tlpText_ =
std::regex_replace(m_tlpText_, std::regex("\\{" + kOrdPlaceholder + "\\}"), ordText_);
} else {
tlpText_ = tlpFmt_;
m_tlpText_ = m_tlpFmt_;
}
tlpText_ = fmt_lib::vformat(locale_, tlpText_, fmt_lib::make_format_args(shiftedNow));
label_.set_tooltip_markup(tlpText_);
m_tlpText_ = fmt_lib::vformat(m_locale_, m_tlpText_, fmt_lib::make_format_args(shiftedNow));
m_tooltip_->set_markup(m_tlpText_);
label_.trigger_tooltip_query();
}
ALabel::update();
@@ -172,7 +187,7 @@ auto waybar::modules::Clock::getTZtext(sys_seconds now) -> std::string {
if (static_cast<int>(tz_idx) == tzCurrIdx_) continue;
const auto* tz = tzList_[tz_idx] != nullptr ? tzList_[tz_idx] : local_zone();
auto zt{zoned_time{tz, now}};
os << fmt_lib::vformat(locale_, format_, fmt_lib::make_format_args(zt)) << '\n';
os << fmt_lib::vformat(m_locale_, format_, fmt_lib::make_format_args(zt)) << '\n';
}
return os.str();
@@ -190,13 +205,13 @@ auto cldGetWeekForLine(const year_month& ym, const weekday& firstdow, const unsi
}
auto getCalendarLine(const year_month_day& currDate, const year_month ym, const unsigned line,
const weekday& firstdow, const std::locale* const locale_) -> std::string {
const weekday& firstdow, const std::locale* const m_locale_) -> std::string {
std::ostringstream os;
switch (line) {
// Print month and year title
case 0: {
os << date::format(*locale_, "{:L%B %Y}", ym);
os << date::format(*m_locale_, "{:L%B %Y}", ym);
break;
}
// Print weekday names title
@@ -206,7 +221,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const
Glib::ustring::size_type wdLen{0};
int clen{0};
do {
wdStr = date::format(*locale_, "{:L%a}", wd);
wdStr = date::format(*m_locale_, "{:L%a}", wd);
clen = ustring_clen(wdStr);
wdLen = wdStr.length();
while (clen > 2) {
@@ -229,7 +244,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const
os << std::string((wd - firstdow).count() * 3, ' ');
if (currDate != ym / d)
os << date::format(*locale_, "{:L%e}", d);
os << date::format(*m_locale_, "{:L%e}", d);
else
os << "{today}";
@@ -237,7 +252,7 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const
++d;
if (currDate != ym / d)
os << date::format(*locale_, " {:L%e}", d);
os << date::format(*m_locale_, " {:L%e}", d);
else
os << " {today}";
}
@@ -252,13 +267,13 @@ auto getCalendarLine(const year_month_day& currDate, const year_month ym, const
auto wd{firstdow};
if (currDate != ym / d)
os << date::format(*locale_, "{:L%e}", d);
os << date::format(*m_locale_, "{:L%e}", d);
else
os << "{today}";
while (++wd != firstdow && ++d <= dlast) {
if (currDate != ym / d)
os << date::format(*locale_, " {:L%e}", d);
os << date::format(*m_locale_, " {:L%e}", d);
else
os << " {today}";
}
@@ -328,7 +343,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
if (line > 1) {
if (line < ml[(unsigned)ymTmp.month() - 1u]) {
os << fmt_lib::vformat(
locale_, fmtMap_[4],
m_locale_, fmtMap_[4],
fmt_lib::make_format_args(
(line == 2)
? static_cast<const date::zoned_seconds&&>(
@@ -344,7 +359,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
os << Glib::ustring::format((cldWPos_ != WS::LEFT || line == 0) ? std::left : std::right,
std::setfill(L' '),
std::setw(cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0)),
getCalendarLine(today, ymTmp, line, firstdow, &locale_));
getCalendarLine(today, ymTmp, line, firstdow, &m_locale_));
// Week numbers on the right
if (cldWPos_ == WS::RIGHT && line > 0) {
@@ -352,7 +367,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
if (line < ml[(unsigned)ymTmp.month() - 1u])
os << ' '
<< fmt_lib::vformat(
locale_, fmtMap_[4],
m_locale_, fmtMap_[4],
fmt_lib::make_format_args(
(line == 2) ? static_cast<const date::zoned_seconds&&>(
zoned_seconds{tz, local_days{ymTmp / 1}})
@@ -368,7 +383,7 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
// Apply user's formats
if (line < 2)
tmp << fmt_lib::vformat(
locale_, fmtMap_[line],
m_locale_, fmtMap_[line],
fmt_lib::make_format_args(static_cast<const std::string_view&&>(os.str())));
else
tmp << os.str();
@@ -380,10 +395,10 @@ auto waybar::modules::Clock::get_calendar(const year_month_day& today, const yea
}
os << std::regex_replace(
fmt_lib::vformat(locale_, fmtMap_[2],
fmt_lib::vformat(m_locale_, fmtMap_[2],
fmt_lib::make_format_args(static_cast<const std::string_view&&>(tmp.str()))),
std::regex("\\{today\\}"),
fmt_lib::vformat(locale_, fmtMap_[3],
fmt_lib::vformat(m_locale_, fmtMap_[3],
fmt_lib::make_format_args(
static_cast<const std::string_view&&>(date::format("{:L%e}", d)))));
@@ -450,7 +465,7 @@ using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
auto waybar::modules::Clock::first_day_of_week() -> weekday {
#ifdef HAVE_LANGINFO_1STDAY
deleting_unique_ptr<std::remove_pointer<locale_t>::type, freelocale> posix_locale{
newlocale(LC_ALL, locale_.name().c_str(), nullptr)};
newlocale(LC_ALL, m_locale_.name().c_str(), nullptr)};
if (posix_locale) {
const auto i{(int)((std::intptr_t)nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get()))};
const weekday wd{year_month_day{year(i / 10000) / month(i / 100 % 100) / day(i % 100)}};

View File

@@ -10,6 +10,7 @@ waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
name_(name),
output_name_(output_name),
id_(id),
tooltip_format_enabled_{config_["tooltip-format"].isString()},
percentage_(0),
fp_(nullptr),
pid_(-1) {
@@ -161,21 +162,21 @@ auto waybar::modules::Custom::update() -> void {
auto str = fmt::format(fmt::runtime(format_), text_, fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
if (str.empty()) {
if ((config_["hide-empty-text"].asBool() && text_.empty()) || str.empty()) {
event_box_.hide();
} else {
label_.set_markup(str);
if (tooltipEnabled()) {
if (text_ == tooltip_) {
if (label_.get_tooltip_markup() != str) {
label_.set_tooltip_markup(str);
}
} else if (config_["tooltip-format"].isString()) {
if (tooltip_format_enabled_) {
auto tooltip = config_["tooltip-format"].asString();
tooltip = fmt::format(fmt::runtime(tooltip), text_, fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
label_.set_tooltip_markup(tooltip);
} else if (text_ == tooltip_) {
if (label_.get_tooltip_markup() != str) {
label_.set_tooltip_markup(str);
}
} else {
if (label_.get_tooltip_markup() != tooltip_) {
label_.set_tooltip_markup(tooltip_);

View File

@@ -80,7 +80,7 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config)
wl_registry_add_listener(registry, &registry_listener_impl, this);
wl_display_roundtrip(display);
if (!status_manager_) {
if (status_manager_ == nullptr) {
spdlog::error("dwl_status_manager_v2 not advertised");
return;
}
@@ -91,6 +91,12 @@ Window::Window(const std::string &id, const Bar &bar, const Json::Value &config)
zdwl_ipc_manager_v2_destroy(status_manager_);
}
Window::~Window() {
if (output_status_ != nullptr) {
zdwl_ipc_output_v2_destroy(output_status_);
}
}
void Window::handle_title(const char *title) { title_ = title; }
void Window::handle_appid(const char *appid) { appid_ = appid; }

View File

@@ -9,11 +9,38 @@
#include <sys/un.h>
#include <unistd.h>
#include <filesystem>
#include <string>
#include <thread>
namespace waybar::modules::hyprland {
std::filesystem::path IPC::socketFolder_;
std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
// socket path, specified by EventManager of Hyprland
if (!socketFolder_.empty()) {
return socketFolder_;
}
const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR");
std::filesystem::path xdgRuntimeDir;
// Only set path if env variable is set
if (xdgRuntimeDirEnv != nullptr) {
xdgRuntimeDir = std::filesystem::path(xdgRuntimeDirEnv);
}
if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) {
socketFolder_ = xdgRuntimeDir / "hypr";
} else {
spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr");
socketFolder_ = std::filesystem::path("/tmp") / "hypr";
}
socketFolder_ = socketFolder_ / instanceSig;
return socketFolder_;
}
void IPC::startIPC() {
// will start IPC and relay events to parseIPC
@@ -40,9 +67,7 @@ void IPC::startIPC() {
addr.sun_family = AF_UNIX;
// socket path, specified by EventManager of Hyprland
std::string socketPath = "/tmp/hypr/" + std::string(his) + "/.socket2.sock";
auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock";
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
@@ -69,7 +94,14 @@ void IPC::startIPC() {
std::string messageReceived(buffer.data());
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
spdlog::debug("hyprland IPC received {}", messageReceived);
parseIPC(messageReceived);
try {
parseIPC(messageReceived);
} catch (std::exception& e) {
spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what());
} catch (...) {
throw;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
@@ -121,8 +153,7 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (serverSocket < 0) {
spdlog::error("Hyprland IPC: Couldn't open a socket (1)");
return "";
throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)");
}
memset(&aiHints, 0, sizeof(struct addrinfo));
@@ -130,36 +161,31 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
aiHints.ai_socktype = SOCK_STREAM;
if (getaddrinfo("localhost", nullptr, &aiHints, &aiRes) != 0) {
spdlog::error("Hyprland IPC: Couldn't get host (2)");
return "";
throw std::runtime_error("Hyprland IPC: Couldn't get host (2)");
}
// get the instance signature
auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (instanceSig == nullptr) {
spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
return "";
throw std::runtime_error(
"Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
}
std::string instanceSigStr = std::string(instanceSig);
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock";
std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock";
// Use snprintf to copy the socketPath string into serverAddress.sun_path
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) <
0) {
spdlog::error("Hyprland IPC: Couldn't copy socket path (6)");
return "";
throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)");
}
if (connect(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) <
0) {
spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
return "";
throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
}
auto sizeWritten = write(serverSocket, rq.c_str(), rq.length());
@@ -188,7 +214,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
}
Json::Value IPC::getSocket1JsonReply(const std::string& rq) {
return parser_.parse(getSocket1Reply("j/" + rq));
std::string reply = getSocket1Reply("j/" + rq);
if (reply.empty()) {
return {};
}
return parser_.parse(reply);
}
} // namespace waybar::modules::hyprland

View File

@@ -36,6 +36,11 @@ Language::~Language() {
auto Language::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
spdlog::debug("hyprland language update with full name {}", layout_.full_name);
spdlog::debug("hyprland language update with short name {}", layout_.short_name);
spdlog::debug("hyprland language update with short description {}", layout_.short_description);
spdlog::debug("hyprland language update with variant {}", layout_.variant);
std::string layoutName = std::string{};
if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) {
const auto propName = "format-" + layout_.short_description + "-" + layout_.variant;
@@ -50,6 +55,8 @@ auto Language::update() -> void {
fmt::arg("variant", layout_.variant)));
}
spdlog::debug("hyprland language formatted layout name {}", layoutName);
if (!format_.empty()) {
label_.show();
label_.set_markup(layoutName);
@@ -63,7 +70,7 @@ auto Language::update() -> void {
void Language::onEvent(const std::string& ev) {
std::lock_guard<std::mutex> lg(mutex_);
std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(','));
auto layoutName = ev.substr(ev.find_first_of(',') + 1);
auto layoutName = ev.substr(ev.find_last_of(',') + 1);
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
return; // ignore

View File

@@ -38,12 +38,12 @@ Submap::~Submap() {
}
auto Submap::parseConfig(const Json::Value& config) -> void {
auto const alwaysOn = config["always-on"];
auto const& alwaysOn = config["always-on"];
if (alwaysOn.isBool()) {
always_on_ = alwaysOn.asBool();
}
auto const defaultSubmap = config["default-submap"];
auto const& defaultSubmap = config["default-submap"];
if (defaultSubmap.isString()) {
default_submap_ = defaultSubmap.asString();
}

View File

@@ -92,30 +92,39 @@ auto Window::update() -> void {
auto Window::getActiveWorkspace() -> Workspace {
const auto workspace = gIPC->getSocket1JsonReply("activeworkspace");
assert(workspace.isObject());
return Workspace::parse(workspace);
if (workspace.isObject()) {
return Workspace::parse(workspace);
}
return {};
}
auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto monitors = gIPC->getSocket1JsonReply("monitors");
assert(monitors.isArray());
auto monitor = std::find_if(monitors.begin(), monitors.end(),
[&](Json::Value monitor) { return monitor["name"] == monitorName; });
if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{-1, 0, "", ""};
}
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
if (monitors.isArray()) {
auto monitor = std::find_if(monitors.begin(), monitors.end(), [&](Json::Value monitor) {
return monitor["name"] == monitorName;
});
if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{-1, 0, "", ""};
}
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
assert(workspaces.isArray());
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
[&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, "", ""};
}
return Workspace::parse(*workspace);
const auto workspaces = gIPC->getSocket1JsonReply("workspaces");
if (workspaces.isArray()) {
auto workspace = std::find_if(workspaces.begin(), workspaces.end(),
[&](Json::Value workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id);
return Workspace{-1, 0, "", ""};
}
return Workspace::parse(*workspace);
};
};
return {};
}
auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
@@ -146,53 +155,54 @@ void Window::queryActiveWorkspace() {
focused_ = true;
if (workspace_.windows > 0) {
const auto clients = gIPC->getSocket1JsonReply("clients");
assert(clients.isArray());
auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
return window["address"] == workspace_.last_window;
});
if (clients.isArray()) {
auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
return window["address"] == workspace_.last_window;
});
if (activeWindow == std::end(clients)) {
focused_ = false;
return;
}
if (activeWindow == std::end(clients)) {
focused_ = false;
return;
}
windowData_ = WindowData::parse(*activeWindow);
updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
std::vector<Json::Value> workspaceWindows;
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows),
[&](Json::Value window) {
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
});
swallowing_ =
std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) {
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
});
std::vector<Json::Value> visibleWindows;
std::copy_if(workspaceWindows.begin(), workspaceWindows.end(),
std::back_inserter(visibleWindows),
[&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); });
allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return window["floating"].asBool(); });
fullscreen_ = windowData_.fullscreen;
windowData_ = WindowData::parse(*activeWindow);
updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
std::vector<Json::Value> workspaceWindows;
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows),
[&](Json::Value window) {
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
});
swallowing_ =
std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) {
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
});
std::vector<Json::Value> visibleWindows;
std::copy_if(workspaceWindows.begin(), workspaceWindows.end(),
std::back_inserter(visibleWindows),
[&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); });
allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return window["floating"].asBool(); });
fullscreen_ = windowData_.fullscreen;
// Fullscreen windows look like they are solo
if (fullscreen_) {
solo_ = true;
}
// 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 (windowData_.grouped) {
fullscreen_ = false;
solo_ = false;
}
// Grouped windows have a tab bar and therefore don't look fullscreen or solo
if (windowData_.grouped) {
fullscreen_ = false;
solo_ = false;
}
if (solo_) {
soloClass_ = windowData_.class_name;
} else {
soloClass_ = "";
}
if (solo_) {
soloClass_ = windowData_.class_name;
} else {
soloClass_ = "";
}
};
} else {
focused_ = false;
windowData_ = WindowData{};

Some files were not shown because too many files have changed in this diff Show More