Compare commits

..

298 Commits
0.6.9 ... 0.9.2

Author SHA1 Message Date
b9338c72c9 chore: 0.9.2 2020-04-11 12:24:49 +02:00
8f6273e9d0 refactor(config): comment default config layer 2020-04-11 12:08:30 +02:00
1fd708cda8 Merge pull request #661 from BlueGone/layout-dockerfiles
feat(layout): add pugixml dependency to dockerfiles
2020-04-10 16:41:27 +02:00
ac3126b6cf feat(layout): add pugixml dependency to dockerfiles 2020-04-10 16:38:50 +02:00
7c4ea39774 fix: add missing comma 2020-04-06 12:49:41 +02:00
27fbea2b5a refactor(workspaces): default value unstripped, fix man 2020-04-06 12:42:29 +02:00
6570aefeec Merge pull request #651 from JohnHolmesII/pulse-patch
Pulse: Start wait for server
2020-04-05 20:52:12 +02:00
5c5031fd69 pulse: do not die when a server hasn't been started. wait first. 2020-04-05 11:42:27 -07:00
8e0f3c7ddf feat: full-at (#649)
* feat: full-at

* fix(man): typo
2020-04-05 16:56:51 +02:00
d5bd3be8de chore: use native git 2020-04-05 16:12:25 +02:00
bb2c16386b feat: format-icon for persistent workspaces 2020-04-04 21:13:25 +02:00
ec451b5908 fix(command): check WIFEXITED 2020-03-30 10:36:24 +02:00
ae3d4b9d28 Merge pull request #636 from masm11/fix/restore-sigchld-setting
restore SIGCHLD settings to SIG_DFL. #600
2020-03-29 11:00:43 +02:00
af96150f5c restore SIGCHLD settings to SIG_DFL. 2020-03-28 01:35:21 +09:00
10b152ac3e fix: process last line, restart-interval 2020-03-26 09:18:47 +01:00
9acf5587fa refactor(pulseaudio): fallback to default muted format 2020-03-25 22:53:09 +01:00
c302116e73 Merge pull request #634 from Alexays/restart-interval
feat(custom): restart_interval for continuous script
2020-03-25 22:42:31 +01:00
ff36154c4b fix: typo 2020-03-25 22:31:04 +01:00
d12ad1128e fix: man 2020-03-25 22:30:22 +01:00
cb2f5c154c feat(custon): restart_interval for continuous script 2020-03-25 22:25:30 +01:00
c3cdd516ef Merge pull request #629 from Xyene/pre-3.4-meminfo
fix(memory): provide better free memory approximation on old kernels
2020-03-24 20:57:23 +01:00
19743f3085 fix(memory): provide better free memory approximation on old kernels
The approximation should include SReclaimable, and subtract Shmem. To
prevent the parsing code from ballooning in size, this commit also
refactors the parsing into a map.
2020-03-20 17:37:22 -04:00
5db06d99bb Merge pull request #624 from BoostCookie/master
Added support for absolute device paths for the temperature module.
2020-03-15 11:56:00 +01:00
384200f27e Merge pull request #625 from Xyene/patch-1
Switch default Makefile rule from run to build
2020-03-15 11:54:22 +01:00
5e712ca255 Switch default Makefile rule from run to build
This commit makes it so that running `make` without arguments builds
rather than builds and runs, which is unconventional and surprising
behaviour.
2020-03-14 16:25:33 -04:00
d405f28622 Indent now uses spaces. 2020-03-13 16:42:05 +01:00
2f975f870a Added support for absolute device paths for the temperature module. 2020-03-12 22:04:00 +01:00
4471d4aba1 Merge pull request #615 from hypergig/master
readme: ubuntu dependencies
2020-03-08 10:34:07 +01:00
3945c77668 readme: adding libspdlog-dev to list of ubuntu dependencies, also sorting and cleaning up list to make it easier to read and copy 2020-03-05 08:57:19 -05:00
6c27af35e9 Merge pull request #612 from gdamjan/fix-service-ordering
systemd service: fix start up ordering
2020-03-04 16:29:42 +01:00
2a7e8f7d94 Merge pull request #613 from alebastr/set-exclusive-zone
fix(bar): set exclusive zone early for gtk-layer-shell
2020-03-04 16:29:06 +01:00
dd0144c3cd fix(bar): set exclusive zone early for gtk-layer-shell
If the bar is using initial size from the config (i.e both width and
height are set and resize is not required), GtkWindow configure event
is is not emitted. Initialize exclusive zone earlier for that case.

Fixes #609
2020-03-04 06:56:25 -08:00
03130b7565 systemd service: fix start up ordering
the service needs to have After=wayland-session.target otherwise it'll
be started in parallel to the compositor which might not be fully
configured
2020-03-03 22:35:48 +01:00
37b1b35035 Merge pull request #603 from layus/track_defaults
pulseaudio: track only the default sink and source
2020-02-24 10:53:26 +00:00
190b2dd922 pulseaudio: track only the default sink and source
When you have multiple sinks (resp. sources), the module used to display
the state of the most recently changed one. This changes remembers the
default sink name, and only records changes to that one.
2020-02-24 11:30:35 +01:00
2d8dc83480 Merge pull request #598 from layus/sni-watcher
Use the same StatusNotifierWatcher for all trays
2020-02-19 14:22:51 +01:00
382cf76503 Merge pull request #599 from layus/pulse-track
pulse: track default source/sink changes
2020-02-19 14:10:47 +01:00
9a5f5114c4 pulse: track default source/sink changes 2020-02-19 12:28:36 +01:00
047c2929c1 Use the same StatusNotifierWatcher for all trays 2020-02-19 12:06:35 +01:00
9b0660e751 Merge pull request #592 from Alexays/pulseaudio-alt
Fix alt on pulseaudio module
2020-02-16 21:53:36 +00:00
543589a97b Update pulseaudio.cpp 2020-02-16 21:48:22 +00:00
82d0c87934 Merge pull request #589 from JordanL2/master
Fix for 'Network label text not updated properly when formats contain…
2020-02-15 18:13:14 +00:00
4f8a396692 Fix for 'Network label text not updated properly when formats contain Unicode characters' (#588) 2020-02-15 16:51:18 +00:00
6141a26a09 Merge pull request #584 from DanySpin97/sway-fix
fix(sway): add missing unordered_map include
2020-02-11 17:46:32 +00:00
bb7596045f Merge pull request #583 from DanySpin97/cxxfs
fix(meson): Support libc++ >=9.0.0
2020-02-11 17:44:50 +00:00
16c68ee132 fix(meson): Support libc++ >=9.0.0
From LLVM libc++ documentation:
"Prior to LLVM 9.0, libc++ provides the implementation of the
filesystem library in a separate static library."

Now the filesystem library (not the experimental one) is shipped
inside the libc++.so library.

Check if '-lc++fs' link flag is needed and supported before adding
it.
2020-02-11 14:42:24 +01:00
e0c42ae415 fix(sway): add missing unordered_map include 2020-02-11 14:31:17 +01:00
aae105c998 chore: 0.9.1 2020-02-10 23:47:23 +00:00
b719569243 Merge pull request #579 from alebastr/date-fractional-seconds
fix(clock): lower precision of zoned_time to avoid fractional seconds in output
2020-02-06 21:27:01 +00:00
e70f8d8730 fix(clock): lower precision of zoned_time to avoid fractional seconds in output 2020-02-06 10:04:22 -08:00
e1215a6d17 Merge pull request #578 from alebastr/ipc-use-after-free
fix(sway): resolve destruction dependency between Ipc and SleeperThread
2020-02-06 17:36:11 +00:00
119446d538 Merge pull request #570 from skligys/simple_calendar
Current month calendar in clock tooltip.
2020-02-06 17:32:27 +00:00
d1f427618f Cache calendar per clock instance, weekdays properly handle locales. 2020-02-05 11:07:47 -08:00
3c268d83c2 Merge branch 'master' into simple_calendar 2020-02-05 11:07:09 -08:00
ae6ca36fa7 fix(sway): resolve destruction dependency between Ipc and SleeperThread
Ipc destructor closes socket and thus wakes up SleeperThread which was
waiting for socket data in Ipc::handleEvent.
Ipc::handleEvent then proceeds with sending signal to already destroyed
object, causing heap-use-after-free Address Sanitizer error.
2020-02-04 23:22:43 -08:00
83b12fc8a7 Merge pull request #577 from torstehu/finish-PA-audiotype
Finish #571
2020-02-04 19:36:32 +00:00
f107aaddc3 Finish #571 2020-02-04 12:16:50 +01:00
cd2db19267 Detect presence, call nl_langinfo() to get first day of week. 2020-02-03 17:18:34 -08:00
4c40f9c635 Stop using a mutex for guarding CachedCalendar. 2020-02-03 17:18:34 -08:00
84e5b0e8c2 Merge pull request #574 from torstehu/fix-typo
Fix typos
2020-02-03 10:46:51 +00:00
34a710cce3 Fix typos 2020-02-03 10:40:26 +01:00
218bb3bc2b Simpify calendar generation, single loop handles both first week and subsequent weeks. 2020-02-02 16:06:27 -08:00
f6b2005687 Cache calendar tooltip text to reduce computations. 2020-02-02 15:59:24 -08:00
ea9591baea Switch from utfcpp to Glib::ustring for UTF-8 string mangling. 2020-02-02 15:59:24 -08:00
af2528952b Merge branch 'master' of github.com:skligys/Waybar into temp 2020-02-02 15:58:40 -08:00
414bf741f3 Merge pull request #571 from Trundle/adapt-icon-names-to-form-factors
pulseaudio: adapt icon names to form factors
2020-02-01 11:20:32 +00:00
527fa982d2 pulseaudio: adapt icon names to form factors 2020-01-31 22:47:26 +01:00
8e05aab4d9 Current month calendar in clock tooltip. 2020-01-31 10:36:40 -08:00
e9b0365327 Merge pull request #560 from mjec/master
Add timezone support to clock module (closes #223)
2020-01-23 14:03:40 +00:00
97554b3532 Merge branch 'master' into master 2020-01-23 08:28:13 -05:00
1e969a48ae Use github instead of mesonbuild for hinnant-date patch 2020-01-23 08:27:13 -05:00
84b671f6b2 Attempt at supporting locale and timezones (#1) 2020-01-23 08:27:10 -05:00
6e30b7af3c Remove duplicate dependency, use current locale 2020-01-23 08:27:00 -05:00
3130a57622 Add timezone support to clock module (closes #223) 2020-01-23 08:26:49 -05:00
a555a72d7f Merge pull request #557 from thomaswucher/fix-meson-wrapdb-urls
Download patch files from Github instead of wrapdb
2020-01-16 10:34:20 +00:00
99dde1aff8 Download patch files from Github instead of wrapdb
Currently wrapdb.mesonbuild.com is offline and its not clear when it
will be up again. Github seems to be the more reliable source for these
files.
2020-01-16 11:09:32 +01:00
d5875c468f Merge pull request #550 from PlusMinus0/use_form_factor
Use PA_PROP_DEVICE_FORM_FACTOR for device icon.
2020-01-15 10:05:34 +00:00
9e877d3f57 Merge pull request #555 from alebastr/outputs-remove-unique_ptr
refactor(client): use std::list<waybar_output> to store outputs
2020-01-15 08:12:18 +00:00
f80270519b refactor(client): use std::list<waybar_output> to store outputs
std::unique_ptr is not required here as the only benefit it gives is
stability of address on vector resize and it's easy to invalidate it
accidentaly. std::list provides the same guarantee of stable addresses
of the elements and correct destruction while avoiding smart pointer
overhead.

Also fixes #554, caused by incorrect usage of std::remove_if.
2020-01-14 07:27:08 -08:00
8fb3211594 Use PA_PROP_DEVICE_FORM_FACTOR for device icon. 2020-01-13 11:34:33 +01:00
b9cd51a9cc Merge pull request #548 from Psykar/icon-width
Keep aspect ratio when scaling tray icons.
2020-01-09 09:00:09 +01:00
569f40de9b Keep aspect ratio when scaling tray icons. 2020-01-09 17:27:10 +10:30
9c8d0865d1 Merge pull request #546 from z3ntu/cassert
fix(network): add missing include
2020-01-07 20:30:02 +01:00
129713fe1b fix(network): add missing include
Fixes:
../src/modules/network.cpp:68:3: error: 'assert' was not declared in this scope
   68 |   assert(starts_with(read, category));
      |   ^~~~~~
../src/modules/network.cpp:6:1: note: 'assert' is defined in header '<cassert>'; did you forget to '#include <cassert>'?
    5 | #include "util/format.hpp"
  +++ |+#include <cassert>
    6 |
2020-01-07 20:27:31 +01:00
e66c3bc965 Merge pull request #543 from cole-h/master
mpd: add paused format string
2020-01-07 19:18:53 +01:00
75c6e2e7d5 mpd: add paused format string 2020-01-07 09:15:54 -08:00
d294352845 Merge pull request #529 from fuzxi/master
[Corrected] Add info on "on-click-middle" option to custom module man page
2020-01-06 13:37:08 +01:00
35f7fdf684 Update waybar-temperature.5.scd 2020-01-06 13:31:16 +01:00
33798c31d0 Update waybar-sway-window.5.scd 2020-01-06 13:31:06 +01:00
ee0db26021 Update waybar-sway-mode.5.scd 2020-01-06 13:30:56 +01:00
abcac464fa Update waybar-pulseaudio.5.scd 2020-01-06 13:30:48 +01:00
9602360d28 Update waybar-network.5.scd 2020-01-06 13:30:38 +01:00
1d087f96bd Update waybar-idle-inhibitor.5.scd 2020-01-06 13:30:28 +01:00
01b8527333 Update waybar-disk.5.scd 2020-01-06 13:30:19 +01:00
abeb406166 Update waybar-custom.5.scd 2020-01-06 13:30:11 +01:00
443281f0bc Update waybar-cpu.5.scd 2020-01-06 13:30:02 +01:00
de3be8b2ab Update waybar-clock.5.scd 2020-01-06 13:29:52 +01:00
4d7e19ae66 Update waybar-battery.5.scd 2020-01-06 13:29:41 +01:00
e8f2bd3ad1 Update waybar-backlight.5.scd 2020-01-06 13:29:20 +01:00
c41cedd407 Update waybar-mpd.5.scd 2020-01-06 13:29:04 +01:00
f6864e4a43 Update waybar-memory.5.scd 2020-01-06 13:28:31 +01:00
a833c51a28 Merge pull request #540 from jomority/network
fix(network): display of IPv6 address
2020-01-06 13:25:22 +01:00
1145788ab3 fix(network): display of IPv6 address 2020-01-06 13:15:28 +01:00
c0d4867421 Merge pull request #541 from jomority/man
man: adding and clarification of configuration options
2020-01-06 11:47:23 +01:00
d18ece13f2 Merge pull request #536 from xPMo/master
Fix: WindowId recieves 'u'
2020-01-06 09:21:26 +01:00
1f379fa5f6 Don't specify WindowId property
https://github.com/Alexays/Waybar/pull/536#issuecomment-570847035
2020-01-05 10:22:52 -06:00
1125119dc6 man: add icon and format-icon to network module 2020-01-04 04:33:04 +01:00
173a7bb8cd man: fix layout in battery module 2020-01-04 04:31:24 +01:00
4836333bff man: document family configuration option in network module 2020-01-04 03:26:06 +01:00
f01ddb9ab3 man: clarify options for layer configuration 2020-01-03 16:22:29 +01:00
2f6a70f34e man: document gtk-layer-shell configuration option 2020-01-03 16:20:56 +01:00
def4466953 Merge pull request #534 from AdrienLemaire/chore/mediaplayer
feat: show title if artist isn't available
2020-01-01 15:51:50 +01:00
db1136d647 feat: show title if artist isn't available 2020-01-01 13:50:29 +09:00
c8821a5e7f fix(Battery): replace spaces w/ dashes 2019-12-30 13:56:34 +01:00
d40cc6f23a fix(Battery): replace spaces w/ underscores 2019-12-30 13:55:49 +01:00
c885be369e feat(Network): hide on empty format 2019-12-30 13:46:12 +01:00
631695ec74 Merge pull request #525 from Akasch/master
add dependencies of gtk-layer-shell to readme
2019-12-28 15:27:46 +01:00
50275ae529 add dependencies of gtk-layer-shell to readme 2019-12-28 15:19:42 +01:00
35d806b801 chore: 0.9.0 2019-12-28 12:35:09 +01:00
220028051f chore: auto gtk-layer-shell option 2019-12-28 12:13:33 +01:00
7f73a8cd45 Merge pull request #441 from alebastr/gtk-layer-shell
Use gtk-layer-shell library for correct positioning of popups
2019-12-28 11:56:46 +01:00
dde700f2c9 feat: use gtk-layer-shell library for correct positioning of popups
To enable: use sway >= 1.2, compile waybar with `-Dgtk-layer-shell=enabled` meson option.
Original behavior could be restored at runtime by setting `"gtk-layer-shell": false` in waybar config.
2019-12-27 16:42:14 -08:00
0e87b3938a deps: meson dependencies for gtk-layer-shell feature 2019-12-27 16:42:13 -08:00
d1637d34cf refactor: fetch outputs from Gtk::Display instead of wl_registry.
gtk-layer-shell wants Gdk::Monitor instead of wl_output;
change code to deal with Gdk objects and slightly simplify it.
Requires gtkmm 3.22.0+ (first release with Gdk::Monitor support).
2019-12-27 16:42:12 -08:00
2277ddd156 Merge pull request #520 from Alexays/revert-517-master
Revert "Add info on "on-click-middle" option to custom module man page"
2019-12-17 11:58:42 +01:00
f9543e47dd Revert "Add info on "on-click-middle" option to custom module man page" 2019-12-17 11:58:30 +01:00
4c548a95e8 Merge pull request #517 from fuzxi/master
Add info on "on-click-middle" option to custom module man page
2019-12-16 15:19:44 +01:00
6c3cb7c85a Update waybar-temperature.5.scd 2019-12-15 15:11:06 -08:00
a307042d6b Update waybar-sway-window.5.scd 2019-12-15 15:10:26 -08:00
548589ee5d Update waybar-sway-mode.5.scd 2019-12-15 15:10:14 -08:00
b2cec74c04 Update waybar-pulseaudio.5.scd 2019-12-15 15:09:44 -08:00
6895b3b6b6 Update waybar-network.5.scd 2019-12-15 15:09:33 -08:00
ac8e892cbd Update waybar-mpd.5.scd 2019-12-15 15:09:08 -08:00
1a05e230b9 Update waybar-memory.5.scd 2019-12-15 15:08:41 -08:00
ad08130b52 Update waybar-idle-inhibitor.5.scd 2019-12-15 15:08:27 -08:00
a94d5c5ff2 Update waybar-disk.5.scd 2019-12-15 15:08:15 -08:00
ecce607afe Update waybar-cpu.5.scd 2019-12-15 15:07:38 -08:00
73bb42d40c Update waybar-clock.5.scd 2019-12-15 15:07:23 -08:00
3bb03852fe Update waybar-battery.5.scd 2019-12-15 15:07:07 -08:00
2aaf5510b8 Update waybar-backlight.5.scd 2019-12-15 15:06:23 -08:00
278f26e597 add info on "on-click-middle" option 2019-12-14 22:28:31 -08:00
03dab1bbdd Merge pull request #512 from josteink/patch-1
Provide easy bootstrap command for Ubuntu-users
2019-12-09 12:39:40 +00:00
0572f75055 Provide easy bootstrap command for Ubuntu-users 2019-12-09 13:20:29 +01:00
de0a3cb020 Merge pull request #510 from HardDie/master
Toggle opacity
2019-11-28 21:05:00 +00:00
eb4c76f5e4 Toggle opacity 2019-11-28 20:28:28 +03:00
502b33b64a Merge pull request #507 from xaiki/master
pulseaudio: export desc as a format
2019-11-25 21:33:36 +00:00
6d5b502bbb pulseaudio: export desc as a format
Signed-off-by: Niv Sardi <xaiki@evilgiggle.com>
2019-11-25 05:16:58 -03:00
2faf629c88 Merge pull request #502 from maxice8/master
Add Alpine Linux to list of distros
2019-11-11 13:39:57 +00:00
Leo
c0355a3fd3 Add Alpine Linux to list of distros 2019-11-11 07:46:10 -03:00
67f6dad717 feat: clang-format 2019-10-23 15:03:32 +01:00
7f7ebb5367 fix(workspaces): add int comparison 2019-10-19 16:19:35 +01:00
b97ca2ec9b Merge pull request #485 from Ventto/fix-on-click-options
fix: No need to format string with unused 'arg' named argument (#484)
2019-10-18 16:18:04 +01:00
358426cb1c fix: No need to format string with unused 'arg' named argument (#484)
The fmt::format() function looks for the "{arg}" named argument
in the given "format" string which does not exist. It will fail
if the string contains any {...} curled-brace substring.

Consequently, any "on-click*" option's command line containing for
instance substring like "${var}" or "awk '{...}'" will crash the program.

Signed-off-by: Thomas Venriès <thomas.venries@gmail.com>
2019-10-16 22:39:45 +02:00
5e4fdb1530 Detect timezone changes (#480)
Detect timezone changes
2019-10-07 08:23:00 +01:00
1e82982dbd Detect timezone changes
Fixes #479, because upstream does not intend to.

It may be less expensive to do that only once in a while, or to inotify-watch on /etc/timezone, but this is good enough.
2019-10-04 23:25:09 +02:00
048d8d328e refactor(Battery): ouput 'Plugged' instead of 'Charging' in the fallback 2019-10-03 09:37:37 +01:00
6d5e328928 feat(style): add comment about icons font 2019-10-02 10:27:46 +01:00
97e3226801 Merge pull request #475 from SJK132/SJK132-audio-bound
change audio scroll bound
2019-10-01 10:09:54 +01:00
3d1fd4f5ea change audio scroll bound
Change default behavior from 0% - 99% to 0% - 100%.
2019-09-28 18:05:31 -05:00
3131eb0774 Add a custom formatter for "pow" numbers. (#472)
Add a custom formatter for "pow" numbers.
2019-09-25 13:17:18 +01:00
f4d2ca2736 custom formatter for numbers in 'pow' units format 2019-09-25 12:53:06 +02:00
b3f9425d70 fix: ci 2019-09-25 09:59:07 +01:00
211b1c2785 fix: no need to wait on start 2019-09-25 09:32:22 +01:00
9e13161385 chore: update README 2019-09-25 09:27:53 +01:00
bd5a215d2a Add a disk module (#471)
Add a disk module
2019-09-25 09:23:02 +01:00
247589efbb Add some documentation for disks 2019-09-25 09:11:03 +02:00
1d39ef5c8e Add a disk module 2019-09-25 08:47:33 +02:00
4667afaa98 fix: avoid pulling submodule on ci 2019-09-25 00:02:07 +01:00
bae83ee4e3 fix: static_cast clamp 2019-09-19 22:07:38 +01:00
52db1e8e68 Merge pull request #467 from jspngh/bugfix/mpd_module
Add more locking in mpd module
2019-09-19 22:03:23 +01:00
334bc1e52a fix(Network): clamp signal strength 2019-09-19 21:59:28 +01:00
e6599d8ed5 modules/mpd: take lock in waitForEvent to prevent SIGABORT
+ replaced deprecated MPD_IDLE_PLAYLIST with MPD_IDLE_QUEUE
+ add mutex for periodic_updater
2019-09-19 21:43:12 +02:00
5ee4c54b13 Merge pull request #465 from krystiancha/fix-mpd-style
add padding for mpd module in default style
2019-09-17 19:38:10 +01:00
8830247b9e add padding for mpd module in default style
In the default style.css many modules (clock, battery, cpu, ...) get horizontal padding, but mpd module does not. This commit adds mpd to the list of modules that get the padding.
2019-09-17 19:50:53 +02:00
db74a46d7b Merge pull request #460 from gdamjan/master
service file: allow for waybar installed in different prefix
2019-09-12 17:40:03 +02:00
4b24c19fa7 add prefix to the configuration data 2019-09-10 14:12:52 +02:00
ce50249c04 service file: allow for waybar installed in different prefix 2019-09-10 13:56:34 +02:00
02811293e0 Revert "service file: allow for waybar installed in different pr… (#459)
Revert "service file: allow for waybar installed in different prefix"
2019-09-10 12:06:19 +02:00
5bb6fa384d Revert "service file: allow for waybar installed in different prefix" 2019-09-10 12:06:06 +02:00
b6cb02ce85 service file: allow for waybar installed in different prefix (#458)
service file: allow for waybar installed in different prefix
2019-09-10 11:56:20 +02:00
a0a218c6e2 Merge branch 'master' into master 2019-09-09 19:05:51 +02:00
f152ad9fa9 service file: allow for waybar installed in different prefix 2019-09-08 14:04:34 +02:00
05d083726c Merge pull request #456 from gdamjan/master
install a systemd user service unit
2019-09-08 10:25:09 +02:00
a0a3c01e79 install a systemd user unit
add a systemd --user unit/service file, so that one can run waybar as a
--user systemd service. when the service is enabled, Waybar will start
when the wayland-session.target is started.

this feature is automatically enabled if systemd is found, but can be disabled
with -Dsystemd=disabled
2019-09-07 22:16:03 +02:00
228078d538 chore: remove opensuse as broken atm 2019-09-07 11:52:14 +02:00
e43b63ff02 Merge pull request #455 from lolzballs/master
Add configurable time display for battery module
2019-09-07 11:50:58 +02:00
dc93fa8218 Documentation for format-time 2019-09-06 14:53:43 -04:00
a63bc84918 Add configurable time display for battery module
Adds a `format-time` configuration for the battery module so that users
can configure how they want their remaining time to be displayed.

The default format remains the same as before, i.e. `{H} h {M} min`,
but users can choose something like `{H}:{M:02d}` to give an output
like `4:29` if wanted.
2019-09-04 14:43:52 -04:00
060b614eb3 Merge pull request #449 from MichaelAquilina/fix_typo
Fix some issues in man pages
2019-09-03 13:06:34 +02:00
dfb5ec0c65 Fix formatting in waybar-puluseaudio 2019-09-03 10:34:12 +01:00
1f84936639 Add vim ft=Dockerfile for syntax highlight detection in editors 2019-09-02 18:07:49 +01:00
f9b65baa2e Build scdocs in travis to pervent breakages 2019-09-02 17:37:46 +01:00
ec23984c4d Fix minor typo in waybar-states 2019-09-02 17:33:31 +01:00
56d9cdc245 Merge pull request #447 from alebastr/exclusive-zone
Fix some issues with exclusive zone and margins
2019-09-02 10:24:29 +02:00
529daedcec fix: correct handling of margins on anchored axis 2019-09-01 01:08:15 -07:00
089d1299c4 fix: correct calculation of exclusive zone 2019-09-01 01:08:07 -07:00
36fc8365ee Merge pull request #446 from bschacht/fix-typo-persistant
Fix typo: persistant -> persistent
2019-08-31 21:13:31 +02:00
f2f1c4f9cb Merge pull request #445 from bschacht/patch-1
Fix typo
2019-08-31 21:11:38 +02:00
2646390e28 Fix typo: persistant -> persistent 2019-08-31 19:57:44 +02:00
66f2cbed51 Fix typo
ouput -> output
2019-08-31 17:50:56 +00:00
19d7bd4ecf Merge pull request #444 from MichaelAquilina/fix_memory_man
fix: correct man memory description
2019-08-30 13:57:17 +02:00
2e4de1ae3e fix: correct man memory description 2019-08-30 09:41:01 +01:00
ce909eea0b Merge pull request #443 from jbeich/man
Allow downstream to disable manpages even if scdoc is found
2019-08-29 13:27:27 +02:00
0b008154d9 Add man-pages option like swaywm/sway@ba16f16e4d 2019-08-29 10:25:13 +00:00
fe88a3fa81 chore: 0.8.0 2019-08-29 11:56:57 +02:00
02e43a38d2 Merge pull request #439 from jbeich/fmt
Unbreak with fmtlib 6.0.0
2019-08-29 11:29:54 +02:00
f592e3d38b Merge branch 'master' into fmt 2019-08-29 11:29:48 +02:00
c6d7a779b8 Merge pull request #442 from xPMo/master
sway/mode: enable pango markup on supported modes
2019-08-29 11:06:01 +02:00
ceedc689ec Merge branch 'master' into master 2019-08-29 11:05:55 +02:00
b314cb16a9 Merge pull request #437 from alebastr/asan-fixes
fix(network): stack-use-after-return found by address sanitizer
2019-08-29 11:04:30 +02:00
a257126685 Merge branch 'master' into asan-fixes 2019-08-29 11:04:24 +02:00
baaeda0ab7 Merge pull request #438 from alebastr/tray-fixes
Tray fixes
2019-08-29 11:03:09 +02:00
e3dff6e0d8 Merge branch 'master' into tray-fixes 2019-08-29 11:02:50 +02:00
e8a1e6ae35 Merge pull request #431 from MichaelAquilina/man_waybar
Add support for man pages
2019-08-29 11:00:59 +02:00
3a86125495 Merge pull request #1 from xPMo/man_waybar
Add supported xdg-spec directory
2019-08-29 09:39:51 +01:00
da43336409 man: add supported xdg-spec directory
$XDG_CONFIG_HOME
2019-08-28 22:05:03 -05:00
712424f9a8 Unbreak build with fmtlib 6.0.0
In file included from ../src/factory.cpp:1:
In file included from ../include/factory.hpp:4:
../include/modules/clock.hpp:5:10: fatal error: 'fmt/time.h' file not found
 #include "fmt/time.h"
          ^~~~~~~~~~~~
In file included from ../src/bar.cpp:4:
In file included from ../include/factory.hpp:4:
In file included from ../include/modules/clock.hpp:3:
In file included from /usr/include/fmt/chrono.h:12:
/usr/include/fmt/locale.h:19:35: error: parameter type 'fmt::v5::internal::buffer' (aka 'basic_buffer<char>') is an abstract class
    const std::locale& loc, buffer<Char>& buf,
                                  ^
/usr/include/spdlog/fmt/bundled/core.h:238:16: note: unimplemented pure virtual method 'grow' in 'basic_buffer'
  virtual void grow(std::size_t capacity) = 0;
               ^
In file included from ../src/modules/sni/host.cpp:3:
/usr/include/fmt/ostream.h:22:9: error: expected member name or ';' after declaration specifiers
  buffer<Char>& buffer_;
  ~~~~~~^
/usr/include/fmt/ostream.h:25:19: error: expected ')'
  formatbuf(buffer<Char>& buf) : buffer_(buf) {}
                  ^
/usr/include/fmt/ostream.h:25:12: note: to match this '('
  formatbuf(buffer<Char>& buf) : buffer_(buf) {}
           ^
/usr/include/fmt/ostream.h:25:42: error: use of undeclared identifier 'buf'; did you mean 'prettify_handler::buf'?
  formatbuf(buffer<Char>& buf) : buffer_(buf) {}
                                         ^~~
                                         prettify_handler::buf
/usr/include/spdlog/fmt/bundled/format-inl.h:551:11: note: 'prettify_handler::buf' declared here
  buffer &buf;
          ^
2019-08-29 01:38:54 +00:00
e38df047fd sway/mode: enable pango markup on supported modes
IPC reports {"mode": "mode_string", "pango_markup": bool}.
Use this to conditionally enable pango markup.
2019-08-28 19:48:33 -05:00
0b237246f0 Fixes to man pages 2019-08-27 10:40:19 +01:00
01ad3d96d8 fix(tray): pre-create dbusmenu for tray items
It seems that dbusmenu is not ready to display menu immediately and
needs some time to sync data via DBus.
Fixes LIBDBUSMENU-GLIB-CRITICAL: dbusmenu_menuitem_send_about_to_show:
assertion 'DBUSMENU_IS_MENUITEM(mi)' failed.
Also fixes initial render of the menu with layer shell popups support patch.
2019-08-27 00:34:00 -07:00
642fd48af1 fix(tray): restore Activate support for compliant SNI implementation
Set ItemIsMenu to true by default because libappindicator supports
neither ItemIsMenu nor Activate method and compiant SNI implementations
are expected to reset the flag during initial property fetch.
To be revisited if anyone finds the implementation that has Activate
but does not set ItemIsMenu.
2019-08-27 00:33:59 -07:00
8f9e6c132d fix(network): stack-use-after-return found by address sanitizer
Fixes compilation with clang.
2019-08-27 00:11:40 -07:00
ba5592c86a Add waybar-states(5) 2019-08-26 22:49:04 +01:00
2a3f40bc02 Minor fixes to waybar(5) 2019-08-26 22:34:19 +01:00
c60b905831 replace `` with ** 2019-08-26 22:31:05 +01:00
6536f7adb6 Consistent header names 2019-08-26 22:29:10 +01:00
8134215839 s/config/configuration where it makes sense 2019-08-26 22:29:05 +01:00
e6fa37164c List supported modules in waybar(5) 2019-08-26 18:20:22 +01:00
436fc94549 Add waybar-tray(5) 2019-08-26 18:15:58 +01:00
4f9c3d2413 Add waybar-temperature(5) 2019-08-26 18:13:48 +01:00
c19a63e85e Add waybar-sway-workspaces(5) 2019-08-26 18:08:59 +01:00
05e212f67a Add waybar-sway-window(5) 2019-08-26 18:00:46 +01:00
e01e3be488 Add waybar-sway-mode(5) 2019-08-26 17:55:20 +01:00
f380844d61 Add waybar-pulseaudio(5) 2019-08-26 17:50:16 +01:00
7f6e4801eb Add waybar-network(5) 2019-08-26 17:42:30 +01:00
ac461f38f4 Add waybar-mpd(5) 2019-08-26 15:27:32 +01:00
30efd28b6a Add waybar-memory(5) 2019-08-26 15:12:42 +01:00
87392ef653 Add waybar-idle-inhibitor(5) 2019-08-26 15:11:07 +01:00
db85224d59 Add waybar-custom(5) 2019-08-26 13:44:09 +01:00
07d6a8e936 Use tabs not spaces in scd files 2019-08-26 13:31:57 +01:00
53614ab50c Add waybar-cpu(5) 2019-08-26 13:23:11 +01:00
fba1f5c8af Move scd files to man/ folder 2019-08-26 13:11:06 +01:00
df83404c8c Format headers 2019-08-24 21:46:56 +01:00
dd3da7b6ef Minor fixes to waybar(5) 2019-08-24 21:44:11 +01:00
cda9eb683f Add waybar-clock(5) 2019-08-24 18:09:08 +01:00
f0ad918feb Add waybar-battery(5) 2019-08-24 17:53:56 +01:00
8696ac77f9 Replace table with simpler format in waybar.5.scd 2019-08-24 17:19:32 +01:00
9484cdff7d Add waybar-backlight.5.scd 2019-08-24 15:39:46 +01:00
9b9818e95d Set scdoc dependency to required: false 2019-08-22 17:13:04 +01:00
cf72dee60c Add scdoc dependency to Dockerfiles 2019-08-22 17:11:24 +01:00
2e037df045 Add scdoc to meson build process 2019-08-22 17:04:09 +01:00
798fe1a622 Add initial man 5 waybar 2019-08-20 19:58:33 +01:00
9d0842db48 Merge pull request #429 from tufteddeer/#420_mute_bt
add support for muted bluetooth audio, fix #420
2019-08-19 10:00:30 +02:00
d8b1b0d0af add support for muted bluetooth audio, fix #420 2019-08-18 16:15:21 +02:00
e9b6380c18 chore: 0.7.2 2019-08-08 12:25:31 +02:00
43beefb00d Merge pull request #421 from SibrenVasse/media_fix
fix(custom): ignore selected_player if not defined. Fixes #419
2019-07-31 12:13:45 +02:00
12f869ccba chore: add player filter example in config 2019-07-31 11:59:33 +02:00
7e9207d75c fix(custom): ignore selected_player if not defined. Fixes #419 2019-07-31 11:53:59 +02:00
7a2dee7377 Fix typo in log warning (#411)
Fix typo in log warning
2019-07-15 13:38:23 +02:00
21a89ac46d Typo 2019-07-15 12:21:31 +02:00
bb99e6cf5b fix: check before destroy 2019-07-15 10:06:01 +02:00
0834551161 chore: 0.7.1 2019-07-11 17:02:47 +02:00
ccd1586c65 Merge pull request #405 from crwxrws/fix-400
fix(pulseaudio): connect scroll event handler
2019-07-09 09:46:05 +02:00
617b370104 fix(pulseaudio): connect scroll event handler
Reconnect the event handler that was removed in commit 527144a.

Fixes Alexays#400
2019-07-06 15:51:00 +02:00
d607a4e33f Fix deadlock on workspace scrolling (#403)
Fix deadlock on workspace scrolling
2019-07-06 10:30:57 +02:00
a6c0bc5a52 Fix deadlock on workspace scrolling
Make the mutex guard lifecycle finish before the send ipc command
by adding scope around the code.

Fixes #395 .
2019-07-05 20:47:44 -07:00
67ad0e69ce Merge pull request #397 from Jamedjo/jej/add-css-classes-for-multiple-bars
Allow CSS to select specific waybars when multiple are in use
2019-07-04 14:20:03 +02:00
ae88d6bc3c feat(bar): individual bars can be named for CSS
Allows CSS to select individual waybars when multiple are configured
2019-07-04 02:15:56 +01:00
4f3c38c542 feat(bar): add CSS class for window position 2019-07-04 02:15:50 +01:00
a6980fca7f feat: ellipsize modules 2019-06-28 14:16:09 +02:00
bd5146fdcf fix(Label): ELLIPSIZE all label modules 2019-06-28 13:49:04 +02:00
22ddbde394 style(workspaces): update hover style 2019-06-28 13:41:53 +02:00
c916fe258e fix(network): no need to check family here 2019-06-25 07:55:55 +02:00
9c8e39c30c chore: 0.7.0 2019-06-22 18:17:16 +02:00
5b270dae0d refactor: AModule (#387)
refactor: AModule
2019-06-22 18:15:50 +02:00
c621afb0c4 Merge branch 'master' into clean 2019-06-22 18:15:34 +02:00
bcf4725349 Cleanup on clean branch (#391)
Cleanup on clean branch
2019-06-18 09:43:34 +02:00
12b30ca25f AModule::getScrollDir: convert reset if-else into switch 2019-06-17 20:42:19 +02:00
86d6668ed4 AModule::getScrollDir: convert if-else chain into switch statement 2019-06-17 20:40:13 +02:00
7c85aec8e0 AModule::getScrollDir: get deltas in a more C++ way 2019-06-17 20:29:37 +02:00
2c038d1977 AModule::getScrollDir: move dir inside the only scope it is relevant 2019-06-17 20:09:53 +02:00
ff9d598c16 fix: add proper mutex 2019-06-17 11:39:45 +02:00
71a9a75aad refactor: remove fix workaround 2019-06-16 15:14:31 +02:00
05f796158b fix: typo 2019-06-16 15:13:40 +02:00
1d2dd953e7 revert: default config 2019-06-16 15:09:26 +02:00
527144a440 refactor(pulseaudio): proper scroll override 2019-06-16 15:08:08 +02:00
cda6282277 Merge pull request #389 from Synthetica9/x-scroll
AModule: handle X axis scrolling
2019-06-16 14:54:53 +02:00
7f13478396 AModule: handle X axis scrolling 2019-06-16 13:17:34 +02:00
90a9c0e25f refactor: get rid of some mutex 2019-06-15 15:01:03 +02:00
340ec7be91 refactor: AModule 2019-06-15 14:57:52 +02:00
e7eef6b493 Merge pull request #385 from nenad/patch/add-total-memory
Expose total memory to the formatting directive
2019-06-15 11:16:30 +02:00
1b7068e61d Expose total memory to the formatting directive 2019-06-14 22:48:16 +02:00
dabe2bebbb feat(sway/window): handle floating nodes 2019-06-14 11:27:40 +02:00
486b5a5d38 fix(sway/window): check output recursively 2019-06-14 10:57:22 +02:00
11bbc3b24d Fix twitchy scrolling on touchpads (#381)
Fix twitchy scrolling on touchpads
2019-06-14 10:38:25 +02:00
396f7d4525 Workspaces: implement horizontal continuous scrolling 2019-06-11 18:44:54 +02:00
ae397c8fa2 ALabel: add smooth-scrolling-threshold 2019-06-11 17:56:10 +02:00
ed4521d113 Workspaces: fix twitchy scrolling on touchpads
Previously, any and all scroll events were interpreted as reason to switch
workspaces. This resulted in twitchy behaviour, where the scrolling was
practically unusable.

Now, we pool all scroll values, and only scroll if the value is larger than the
new config option "smooth-scrolling-threshold". If this option is not set, the
behaviour is unchanged.
2019-06-11 17:22:24 +02:00
80 changed files with 3375 additions and 648 deletions

View File

@ -3,18 +3,21 @@ sudo: false
services:
- docker
git:
submodules: false
env:
- distro: debian
- distro: archlinux
- distro: opensuse
- distro: fedora
- distro: alpine
before_install:
- docker pull alexays/waybar:${distro}
- find . -type f \( -name '*.cpp' -o -name '*.h' \) -print0 | xargs -r0 clang-format -i
script:
- echo FROM alexays/waybar:${distro} > Dockerfile
- echo ADD . /root >> Dockerfile
- docker build -t waybar .
- docker run waybar /bin/sh -c "cd /root && meson build && ninja -C build"
- docker run waybar /bin/sh -c "cd /root && meson build -Dman-pages=enabled && ninja -C build"

View File

@ -1,3 +1,5 @@
# vim: ft=Dockerfile
FROM alpine:latest
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev libnl3-dev pulseaudio-dev libmpdclient-dev
RUN apk add --no-cache git meson alpine-sdk libinput-dev wayland-dev wayland-protocols mesa-dev libxkbcommon-dev eudev-dev pixman-dev gtkmm3-dev jsoncpp-dev pugixml libnl3-dev pulseaudio-dev libmpdclient-dev scdoc

View File

@ -1,4 +1,6 @@
# vim: ft=Dockerfile
FROM archlinux/base:latest
RUN pacman -Syu --noconfirm && \
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp --noconfirm
pacman -S git meson base-devel libinput wayland wayland-protocols pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc --noconfirm

View File

@ -1,5 +1,7 @@
# vim: ft=Dockerfile
FROM debian:sid
RUN apt-get update && \
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev && \
apt-get install -y build-essential meson ninja-build git pkg-config libinput10 libpugixml-dev libinput-dev wayland-protocols libwayland-client0 libwayland-cursor0 libwayland-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libxkbcommon-dev libudev-dev libpixman-1-dev libgtkmm-3.0-dev libjsoncpp-dev scdoc && \
apt-get clean

View File

@ -1,5 +1,7 @@
# vim: ft=Dockerfile
FROM fedora:30
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel -y && \
RUN dnf install sway meson git libinput-devel wayland-devel wayland-protocols-devel pugixml-devel egl-wayland-devel mesa-libEGL-devel mesa-libGLES-devel mesa-libgbm-devel libxkbcommon-devel libudev-devel pixman-devel gtkmm30-devel jsoncpp-devel scdoc -y && \
dnf group install "C Development Tools and Libraries" -y && \
dnf clean all -y

View File

@ -1,5 +1,7 @@
# vim: ft=Dockerfile
FROM opensuse/tumbleweed:latest
RUN zypper -n up && \
zypper -n install -t pattern devel_C_C++ && \
zypper -n install git meson clang libinput10 libinput-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
zypper -n install git meson clang libinput10 libinput-devel libpugixml1 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 scdoc

View File

@ -1,6 +1,6 @@
.PHONY: build build-debug run clean default install
default: run
default: build
build:
meson build

View File

@ -2,7 +2,7 @@
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
> Available in Arch [community](https://www.archlinux.org/packages/community/x86_64/waybar/) or
[AUR](https://aur.archlinux.org/packages/waybar-git/) and [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar)<br>
[AUR](https://aur.archlinux.org/packages/waybar-git/), [openSUSE](https://build.opensuse.org/package/show/X11:Wayland/waybar), and [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=waybar)<br>
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
**Current features**
@ -12,6 +12,7 @@
- Battery
- Network
- Pulseaudio
- Disk
- Memory
- Cpu load average
- Temperature
@ -48,6 +49,9 @@ libsigc++
fmt
wayland
wlroots
libgtk-3-dev [gtk-layer-shell]
gobject-introspection [gtk-layer-shell]
libgirepository1.0-dev [gtk-layer-shell]
libpulse [Pulseaudio module]
libnl [Network module]
sway [Sway modules]
@ -55,6 +59,30 @@ libdbusmenu-gtk3 [Tray module]
libmpdclient [MPD module]
```
On Ubuntu 19.10 you can install all the relevant dependencies using this command:
```
sudo apt install \
clang-tidy \
gobject-introspection \
libdbusmenu-gtk3-dev \
libfmt-dev \
libgirepository1.0-dev \
libgtk-3-dev \
libgtkmm-3.0-dev \
libinput-dev \
libjsoncpp-dev \
libmpdclient-dev \
libnl-3-dev \
libnl-genl-3-dev \
libpulse-dev \
libsigc++-2.0-dev \
libspdlog-dev \
libwayland-dev \
scdoc
```
Contributions welcome! - have fun :)<br>
The style guidelines is [Google's](https://google.github.io/styleguide/cppguide.html)

View File

@ -1,41 +1,30 @@
#pragma once
#include <glibmm/markup.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/label.h>
#include <json/json.h>
#include "IModule.hpp"
#include "AModule.hpp"
namespace waybar {
class ALabel : public IModule {
class ALabel : public AModule {
public:
ALabel(const Json::Value &, const std::string &, const std::string &, const std::string &format,
uint16_t interval = 0);
virtual ~ALabel();
uint16_t interval = 0, bool ellipsize = false);
virtual ~ALabel() = default;
virtual auto update() -> void;
virtual std::string getIcon(uint16_t, const std::string &alt = "", uint16_t max = 0);
virtual operator Gtk::Widget &();
protected:
bool tooltipEnabled();
Gtk::EventBox event_box_;
Gtk::Label label_;
const Json::Value & config_;
std::string format_;
std::string click_param;
std::mutex mutex_;
const std::chrono::seconds interval_;
bool alt_ = false;
std::string default_format_;
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *);
virtual bool handleToggle(GdkEventButton *const &e);
virtual std::string getState(uint8_t value, bool lesser = false);
private:
std::vector<int> pid_;
};
} // namespace waybar

39
include/AModule.hpp Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <glibmm/dispatcher.h>
#include <glibmm/markup.h>
#include <gtkmm/eventbox.h>
#include <json/json.h>
#include "IModule.hpp"
namespace waybar {
class AModule : public IModule {
public:
AModule(const Json::Value &, const std::string &, const std::string &,
bool enable_click = false, bool enable_scroll = false);
virtual ~AModule();
virtual auto update() -> void;
virtual operator Gtk::Widget &();
Glib::Dispatcher dp;
protected:
enum SCROLL_DIR { NONE, UP, DOWN, LEFT, RIGHT };
SCROLL_DIR getScrollDir(GdkEventScroll *e);
bool tooltipEnabled();
const Json::Value &config_;
Gtk::EventBox event_box_;
virtual bool handleToggle(GdkEventButton *const &ev);
virtual bool handleScroll(GdkEventScroll *);
private:
std::vector<int> pid_;
gdouble distance_scrolled_y_;
gdouble distance_scrolled_x_;
};
} // namespace waybar

View File

@ -1,7 +1,5 @@
#pragma once
#include <glibmm/dispatcher.h>
#include <gtkmm/box.h>
#include <gtkmm/widget.h>
namespace waybar {
@ -11,7 +9,6 @@ class IModule {
virtual ~IModule() = default;
virtual auto update() -> void = 0;
virtual operator Gtk::Widget &() = 0;
Glib::Dispatcher dp; // Hmmm Maybe I should create an abstract class ?
};
} // namespace waybar

View File

@ -1,11 +1,13 @@
#pragma once
#include <gdkmm/monitor.h>
#include <glibmm/refptr.h>
#include <gtkmm/box.h>
#include <gtkmm/cssprovider.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include <json/json.h>
#include "IModule.hpp"
#include "AModule.hpp"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
@ -14,10 +16,11 @@ namespace waybar {
class Factory;
struct waybar_output {
struct wl_output * output;
std::string name;
uint32_t wl_name;
struct zxdg_output_v1 *xdg_output;
Glib::RefPtr<Gdk::Monitor> monitor;
std::string name;
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
nullptr, &zxdg_output_v1_destroy};
};
class Bar {
@ -29,13 +32,12 @@ class Bar {
auto toggle() -> void;
void handleSignal(int);
struct waybar_output * output;
Json::Value config;
Gtk::Window window;
struct wl_surface * surface;
struct zwlr_layer_surface_v1 *layer_surface;
bool visible = true;
bool vertical = false;
struct waybar_output *output;
Json::Value config;
Gtk::Window window;
struct wl_surface * surface;
bool visible = true;
bool vertical = false;
private:
static constexpr const char *MIN_HEIGHT_MSG =
@ -50,11 +52,14 @@ class Bar {
uint32_t, uint32_t);
static void layerSurfaceHandleClosed(void *, struct zwlr_layer_surface_v1 *);
void destroyOutput();
#ifdef HAVE_GTK_LAYER_SHELL
void initGtkLayerShell();
#endif
void onConfigure(GdkEventConfigure *ev);
void onRealize();
void onMap(GdkEventAny *ev);
void setMarginsAndZone(uint32_t height, uint32_t width);
void setExclusiveZone(uint32_t width, uint32_t height);
void setSurfaceSize(uint32_t width, uint32_t height);
auto setupWidgets() -> void;
void getModules(const Factory &, const std::string &);
void setupAltFormatKeyForModule(const std::string &module_name);
@ -66,6 +71,9 @@ class Bar {
int bottom = 0;
int left = 0;
} margins_;
struct zwlr_layer_surface_v1 *layer_surface_;
// use gtk-layer-shell instead of handling layer surfaces directly
bool use_gls_ = false;
uint32_t width_ = 0;
uint32_t height_ = 1;
uint8_t anchor_;
@ -73,9 +81,9 @@ class Bar {
Gtk::Box center_;
Gtk::Box right_;
Gtk::Box box_;
std::vector<std::unique_ptr<waybar::IModule>> modules_left_;
std::vector<std::unique_ptr<waybar::IModule>> modules_center_;
std::vector<std::unique_ptr<waybar::IModule>> modules_right_;
std::vector<std::unique_ptr<waybar::AModule>> modules_left_;
std::vector<std::unique_ptr<waybar::AModule>> modules_center_;
std::vector<std::unique_ptr<waybar::AModule>> modules_right_;
};
} // namespace waybar

View File

@ -30,26 +30,24 @@ class Client {
const std::string &style) const;
void bindInterfaces();
const std::string getValidPath(const std::vector<std::string> &paths) const;
void handleOutput(std::unique_ptr<struct waybar_output> &output);
bool isValidOutput(const Json::Value &config, std::unique_ptr<struct waybar_output> &output);
void handleOutput(struct waybar_output &output);
bool isValidOutput(const Json::Value &config, struct waybar_output &output);
auto setupConfig(const std::string &config_file) -> void;
auto setupCss(const std::string &css_file) -> void;
std::unique_ptr<struct waybar_output> &getOutput(uint32_t wl_name);
std::vector<Json::Value> getOutputConfigs(std::unique_ptr<struct waybar_output> &output);
struct waybar_output &getOutput(void *);
std::vector<Json::Value> getOutputConfigs(struct waybar_output &output);
static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version);
static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
static void handleLogicalPosition(void *, struct zxdg_output_v1 *, int32_t, int32_t);
static void handleLogicalSize(void *, struct zxdg_output_v1 *, int32_t, int32_t);
static void handleDone(void *, struct zxdg_output_v1 *);
static void handleName(void *, struct zxdg_output_v1 *, const char *);
static void handleDescription(void *, struct zxdg_output_v1 *, const char *);
static void handleOutputName(void *, struct zxdg_output_v1 *, const char *);
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
void handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor);
Json::Value config_;
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::vector<std::unique_ptr<struct waybar_output>> outputs_;
Json::Value config_;
Glib::RefPtr<Gtk::StyleContext> style_context_;
Glib::RefPtr<Gtk::CssProvider> css_provider_;
std::list<struct waybar_output> outputs_;
};
} // namespace waybar

View File

@ -13,6 +13,7 @@
#include "modules/cpu.hpp"
#include "modules/idle_inhibitor.hpp"
#include "modules/memory.hpp"
#include "modules/disk.hpp"
#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
#include "modules/sni/tray.hpp"
#endif
@ -37,7 +38,7 @@ namespace waybar {
class Factory {
public:
Factory(const Bar& bar, const Json::Value& config);
IModule* makeModule(const std::string& name) const;
AModule* makeModule(const std::string& name) const;
private:
const Bar& bar_;

View File

@ -1,12 +1,22 @@
#pragma once
#include <fmt/format.h>
#if FMT_VERSION < 60000
#include <fmt/time.h>
#else
#include <fmt/chrono.h>
#endif
#include <date/tz.h>
#include "ALabel.hpp"
#include "fmt/time.h"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
struct waybar_time {
std::locale locale;
date::zoned_seconds ztime;
};
class Clock : public ALabel {
public:
Clock(const std::string&, const Json::Value&);
@ -15,6 +25,15 @@ class Clock : public ALabel {
private:
util::SleeperThread thread_;
std::locale locale_;
const date::time_zone* time_zone_;
bool fixed_time_zone_;
date::year_month_day cached_calendar_ymd_;
std::string cached_calendar_text_;
auto calendar_text(const waybar_time& wtime) -> std::string;
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
auto first_day_of_week() -> date::weekday;
};
} // namespace waybar::modules

23
include/modules/disk.hpp Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <fmt/format.h>
#include <fstream>
#include <sys/statvfs.h>
#include "ALabel.hpp"
#include "util/sleeper_thread.hpp"
#include "util/format.hpp"
namespace waybar::modules {
class Disk : public ALabel {
public:
Disk(const std::string&, const Json::Value&);
~Disk() = default;
auto update() -> void;
private:
util::SleeperThread thread_;
std::string path_;
};
} // namespace waybar::modules

View File

@ -17,8 +17,7 @@ class Memory : public ALabel {
static inline const std::string data_dir_ = "/proc/meminfo";
void parseMeminfo();
unsigned long memtotal_;
unsigned long memfree_;
std::unordered_map<std::string, unsigned long> meminfo_;
util::SleeperThread thread_;
};

View File

@ -36,6 +36,7 @@ class MPD : public ALabel {
bool stopped();
bool playing();
bool paused();
const std::string module_name_;
@ -65,6 +66,9 @@ class MPD : public ALabel {
unique_status status_;
mpd_state state_;
unique_song song_;
// To make sure the previous periodic_updater stops before creating a new one
std::mutex periodic_lock_;
};
} // namespace waybar::modules

View File

@ -39,7 +39,7 @@ class Network : public ALabel {
void parseFreq(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
bool checkInterface(struct ifinfomsg* rtif, std::string name);
int getPreferredIface(int skip_idx = -1) const;
int getPreferredIface(int skip_idx = -1, bool wait = true) const;
auto getInfo() -> void;
void checkNewInterface(struct ifinfomsg* rtif);
const std::string getNetworkState() const;

View File

@ -21,28 +21,30 @@ class Pulseaudio : public ALabel {
static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data);
static void serverInfoCb(pa_context*, const pa_server_info*, void*);
static void volumeModifyCb(pa_context*, int, void*);
bool handleVolume(GdkEventScroll* e);
bool handleScroll(GdkEventScroll* e);
const std::string getPortIcon() const;
pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_;
pa_context* context_;
bool scrolling_;
// SINK
uint32_t sink_idx_{0};
uint16_t volume_;
pa_cvolume pa_volume_;
bool muted_;
std::string port_name_;
std::string form_factor_;
std::string desc_;
std::string monitor_;
std::string default_sink_name_;
// SOURCE
uint32_t source_idx_{0};
uint16_t source_volume_;
bool source_muted_;
std::string source_port_name_;
std::string source_desc_;
std::string default_source_name_;
};
} // namespace waybar::modules

View File

@ -46,7 +46,12 @@ class Item : public sigc::trackable {
std::string menu;
DbusmenuGtkMenu* dbus_menu = nullptr;
Gtk::Menu* gtk_menu = nullptr;
bool item_is_menu = false;
/**
* ItemIsMenu flag means that the item only supports the context menu.
* Default value is true because libappindicator supports neither ItemIsMenu nor Activate method
* while compliant SNI implementation would always reset the flag to desired value.
*/
bool item_is_menu = true;
private:
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
@ -60,7 +65,7 @@ class Item : public sigc::trackable {
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
void makeMenu(GdkEventButton* const& ev);
void makeMenu();
bool handleClick(GdkEventButton* const& /*ev*/);
Glib::RefPtr<Gio::DBus::Proxy> proxy_;

View File

@ -1,7 +1,7 @@
#pragma once
#include <fmt/format.h>
#include "IModule.hpp"
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/sni/host.hpp"
#include "modules/sni/watcher.hpp"
@ -9,21 +9,19 @@
namespace waybar::modules::SNI {
class Tray : public IModule {
class Tray : public AModule {
public:
Tray(const std::string&, const Bar&, const Json::Value&);
~Tray() = default;
auto update() -> void;
operator Gtk::Widget&();
private:
void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item);
static inline std::size_t nb_hosts_ = 0;
const Json::Value& config_;
Gtk::Box box_;
SNI::Watcher watcher_;
SNI::Watcher::singleton watcher_;
SNI::Host host_;
};

View File

@ -7,10 +7,24 @@
namespace waybar::modules::SNI {
class Watcher {
private:
Watcher();
public:
Watcher(std::size_t id);
~Watcher();
using singleton = std::shared_ptr<Watcher>;
static singleton getInstance() {
static std::weak_ptr<Watcher> weak;
std::shared_ptr<Watcher> strong = weak.lock();
if (!strong) {
strong = std::shared_ptr<Watcher>(new Watcher());
weak = strong;
}
return strong;
}
private:
typedef enum { GF_WATCH_TYPE_HOST, GF_WATCH_TYPE_ITEM } GfWatchType;
@ -34,7 +48,6 @@ class Watcher {
void updateRegisteredItems(SnWatcher *obj);
uint32_t bus_name_id_;
uint32_t watcher_id_;
GSList * hosts_ = nullptr;
GSList * items_ = nullptr;
SnWatcher *watcher_ = nullptr;

View File

@ -5,10 +5,10 @@
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <memory>
#include <mutex>
#include "ipc.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
@ -29,6 +29,7 @@ class Ipc {
void sendCmd(uint32_t type, const std::string &payload = "");
void subscribe(const std::string &payload);
void handleEvent();
void setWorker(std::function<void()> &&func);
protected:
static inline const std::string ipc_magic_ = "i3-ipc";
@ -39,9 +40,10 @@ class Ipc {
struct ipc_response send(int fd, uint32_t type, const std::string &payload = "");
struct ipc_response recv(int fd);
int fd_;
int fd_event_;
std::mutex mutex_;
int fd_;
int fd_event_;
std::mutex mutex_;
util::SleeperThread thread_;
};
} // namespace waybar::modules::sway

View File

@ -6,7 +6,6 @@
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
@ -18,13 +17,11 @@ class Mode : public ALabel, public sigc::trackable {
private:
void onEvent(const struct Ipc::ipc_response&);
void worker();
std::string mode_;
util::JsonParser parser_;
util::SleeperThread thread_;
Ipc ipc_;
std::mutex mutex_;
Ipc ipc_;
};
} // namespace waybar::modules::sway

View File

@ -7,7 +7,6 @@
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
@ -20,21 +19,19 @@ class Window : public ALabel, public sigc::trackable {
private:
void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&);
void worker();
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes);
std::tuple<std::size_t, int, std::string, std::string> getFocusedNode(const Json::Value& nodes,
std::string& output);
void getTree();
const Bar& bar_;
std::mutex mutex_;
std::string window_;
int windowId_;
std::string app_id_;
std::string old_app_id_;
std::size_t app_nb_;
util::JsonParser parser_;
util::SleeperThread thread_;
Ipc ipc_;
std::mutex mutex_;
Ipc ipc_;
};
} // namespace waybar::modules::sway

View File

@ -1,28 +1,26 @@
#pragma once
#include <unordered_map>
#include <fmt/format.h>
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include "IModule.hpp"
#include "AModule.hpp"
#include "bar.hpp"
#include "client.hpp"
#include "modules/sway/ipc/client.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
class Workspaces : public IModule, public sigc::trackable {
class Workspaces : public AModule, public sigc::trackable {
public:
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
~Workspaces() = default;
auto update() -> void;
operator Gtk::Widget&();
private:
void onCmd(const struct Ipc::ipc_response&);
void onEvent(const struct Ipc::ipc_response&);
void worker();
bool filterButtons();
Gtk::Button& addButton(const Json::Value&);
void onButtonReady(const Json::Value&, Gtk::Button&);
@ -33,17 +31,13 @@ class Workspaces : public IModule, public sigc::trackable {
bool handleScroll(GdkEventScroll*);
const Bar& bar_;
const Json::Value& config_;
std::vector<Json::Value> workspaces_;
std::vector<std::string> workspaces_order_;
std::mutex mutex_;
Gtk::Box box_;
util::JsonParser parser_;
bool scrolling_;
std::unordered_map<std::string, Gtk::Button> buttons_;
util::SleeperThread thread_;
Ipc ipc_;
std::mutex mutex_;
Ipc ipc_;
};
} // namespace waybar::modules::sway

View File

@ -72,7 +72,10 @@ inline struct res exec(std::string cmd) {
if (!fp) return {-1, ""};
auto output = command::read(fp);
auto stat = command::close(fp, pid);
return {WEXITSTATUS(stat), output};
if (WIFEXITED(stat)) {
return {WEXITSTATUS(stat), output};
}
return {-1, output};
}
inline int32_t forkExec(std::string cmd) {
@ -88,6 +91,7 @@ inline int32_t forkExec(std::string cmd) {
// Child executes the command
if (!pid) {
setpgid(pid, pid);
signal(SIGCHLD, SIG_DFL);
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
} else {

84
include/util/format.hpp Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include <fmt/format.h>
class pow_format {
public:
pow_format(long long val, std::string&& unit, bool binary = false):
val_(val), unit_(unit), binary_(binary) { };
long long val_;
std::string unit_;
bool binary_;
};
namespace fmt {
template <>
struct formatter<pow_format> {
char spec = 0;
int width = 0;
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) -> decltype (ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == ':') ++it;
if (*it == '>' || *it == '<' || *it == '=') {
spec = *it;
++it;
}
if (it == end || *it == '}') return it;
if ('0' <= *it && *it <= '9') {
// We ignore it for now, but keep it for compatibility with
// existing configs where the format for pow_format'ed numbers was
// 'string' and specifications such as {:>9} were valid.
// The rationale for ignoring it is that the only reason to specify
// an alignment and a with is to get a fixed width bar, and ">" is
// sufficient in this implementation.
width = parse_nonnegative_int(it, end, ctx);
}
return it;
}
template<class FormatContext>
auto format(const pow_format& s, FormatContext &ctx) -> decltype (ctx.out()) {
const char* units[] = { "", "k", "M", "G", "T", "P", nullptr};
auto base = s.binary_ ? 1024ull : 1000ll;
auto fraction = (double) s.val_;
int pow;
for (pow = 0; units[pow+1] != nullptr && fraction / base >= 1; ++pow) {
fraction /= base;
}
auto max_width = 4 // coeff in {:.3g} format
+ 1 // prefix from units array
+ s.binary_ // for the 'i' in GiB.
+ s.unit_.length();
const char * format;
std::string string;
switch (spec) {
case '>':
return format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width);
case '<':
return format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
case '=':
format = "{coefficient:<4.3g}{padding}{prefix}{unit}";
break;
case 0:
default:
format = "{coefficient:.3g}{prefix}{unit}";
break;
}
return format_to(ctx.out(), format
, fmt::arg("coefficient", fraction)
, fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : ""))
, fmt::arg("unit", s.unit_)
, fmt::arg("padding", pow ? "" : s.binary_ ? " " : " ")
);
}
};
}

View File

@ -0,0 +1,71 @@
waybar-backlight(5)
# NAME
waybar - backlight module
# DESCRIPTION
The *backlight* module displays the current backlight level.
# CONFIGURATION
*interval* ++
typeof: integer ++
default: 2 ++
The interval in which information gets polled.
*format* ++
typeof: string ++
default: {percent}% ++
The format, how information should be displayed. On {} data gets inserted.
*max-length* ++
typeof: integer ++
The maximum length in characters the module should display.
*rotate* ++
typeof: integer ++
Positive value to rotate the text label.
*states* ++
typeof: array ++
A number of backlight states which get activated on certain brightness levels.
*on-click* ++
typeof: string ++
Command to execute when the module is clicked.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right* ++
typeof: string ++
Command to execute when the module is right clicked.
*on-scroll-up* ++
typeof: string ++
Command to execute when performing a scroll up on the module.
*on-scroll-down* ++
typeof: string
Command to execute when performing a scroll down on the module.
*smooth-scrolling-threshold* ++
typeof: double
Threshold to be used when scrolling.
# EXAMPLE:
```
"backlight": {
"device": "intel_backlight",
"format": "{percent}% {icon}",
"format-icons": ["", ""]
}
```
# STYLE
- *#backlight*

144
man/waybar-battery.5.scd Normal file
View File

@ -0,0 +1,144 @@
waybar-battery(5)
# NAME
waybar - battery module
# DESCRIPTION
The *battery* module displays the current capacity and state (eg. charging) of your battery.
# CONFIGURATION
*bat* ++
typeof: string ++
The battery to monitor, as in /sys/class/power_supply/ instead of auto detect.
*adapter* ++
typeof: string ++
The adapter to monitor, as in /sys/class/power_supply/ instead of auto detect.
*full-at* ++
typeof: integer ++
Define the max percentage of the battery, usefull for an old battery, e.g. 96
*interval* ++
typeof: integer ++
default: 60 ++
The interval in which the information gets polled.
*states* ++
typeof: array ++
A number of battery states which get activated on certain capacity levels. See *waybar-states(5)*.
*format* ++
typeof: string ++
default: {capacity}% ++
The format, how the time should be displayed.
*format-time* ++
typeof: string ++
default: {H} h {M} min ++
The format, how the time should be displayed.
*format-icons*: ++
typeof: array/object ++
Based on the current capacity, the corresponding icon gets selected. ++
The order is *low* to *high*. Or by the state if it is an object.
*max-length*: ++
typeof: integer++
The maximum length in character the module should display.
*rotate*: ++
typeof: integer++
Positive value to rotate the text label.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right* ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold* ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip* ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# FORMAT REPLACEMENTS
*{capacity}*: Capacity in percentage
*{icon}*: Icon, as defined in *format-icons*.
*{time}*: Estimate of time until full or empty. Note that this is based on the power draw at the last refresh time, not an average.
# TIME FORMAT
The *battery* module allows you to define how time should be formatted via *format-time*.
The two arguments are:
*{H}*: Hours
*{M}*: Minutes
# CUSTOM FORMATS
The *battery* module allows to define custom formats based on up to two factors. The best fitting format will be selected.
*format-<state>*: With *states*, a custom format can be set depending on the capacity of your battery.
*format-<status>*: With the status, a custom format can be set depending on the status in /sys/class/power_supply/<bat>/status (in lowercase).
*format-<status>-<state>*: You can also set a custom format depending on both values.
# STATES
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state. Each class gets activated when the current capacity is equal or below the configured *<value>*.
- Also each state can have its own *format*. Those con be configured via *format-<name>*. Or if you want to differentiate a bit more even as *format-<status>-<state>*. For more information see *custom-formats*.
# EXAMPLES
```
"battery": {
"bat": "BAT2",
"interval": 60,
"states": {
"warning": 30,
"critical": 15
},
"format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""],
"max-length": 25
}
```
# STYLE
- *#battery*
- *#battery.<status>*
- *<status>* is the value of /sys/class/power_supply/<bat>/status in lowercase.
- *#battery.<state>*
- *<state>* can be defined in the *config*. For more information see *states*.
- *#battery.<status>.<state>*
- Combination of both *<status>* and *<state>*.

84
man/waybar-clock.5.scd Normal file
View File

@ -0,0 +1,84 @@
waybar-clock(5)
# NAME
waybar - clock module
# DESCRIPTION
The *clock* module displays the current date and time.
# CONFIGURATION
*interval*: ++
typeof: integer ++
default: 60 ++
The interval in which the information gets polled.
*format*: ++
typeof: string ++
default: {:%H:%M} ++
The format, how the date and time should be displayed. ++
It uses the format of the date library. See https://howardhinnant.github.io/date/date.html#to_stream_formatting for details.
*timezone*: ++
typeof: string ++
default: inferred local timezone ++
The timezone to display the time in, e.g. America/New_York.
*locale*: ++
typeof: string ++
default: inferred from current locale ++
A locale to be used to display the time. Intended to render times in custom timezones with the proper language and format.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
View all valid format options in *strftime(3)*.
# FORMAT REPLACEMENTS
*{calendar}*: Current month calendar
# EXAMPLES
```
"clock": {
"interval": 60,
"format": "{:%H:%M}",
"max-length": 25
}
```
# STYLE
- *#clock*

82
man/waybar-cpu.5.scd Normal file
View File

@ -0,0 +1,82 @@
waybar-cpu(5)
# NAME
waybar - cpu module
# DESCRIPTION
The *cpu* module displays the current cpu utilization.
# CONFIGURATION
*interval*: ++
typeof: integer ++
default: 10 ++
The interval in which the information gets polled.
*format*: ++
typeof: string ++
default: {usage}% ++
The format, how information should be displayed. On {} data gets inserted.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*states*: ++
typeof: array ++
A number of cpu usage states which get activated on certain usage levels. See *waybar-states(5)*.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# FORMAT REPLACEMENTS
*{load}*: Current cpu load.
*{usage}*: Current cpu usage.
# EXAMPLE
```
"cpu": {
"interval": 10,
"format": "{}% ",
"max-length": 10
}
```
# STYLE
- *#cpu*

200
man/waybar-custom.5.scd Normal file
View File

@ -0,0 +1,200 @@
waybar-custom(5)
# NAME
waybar - custom module
# DESCRIPTION
The *custom* module displays either the output of a script or static text.
To display static text, specify only the *format* field.
# CONFIGURATION
Addressed by *custom/<name>*
*exec*: ++
typeof: string ++
The path to the script, which should be executed.
*exec-if*: ++
typeof: string ++
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.
*return-type*: ++
typeof: string ++
See *return-type*
*interval*: ++
typeof: integer ++
The interval (in seconds) in which the information gets polled.
Use *once* if you want to execute the module only on startup.
You can update it manually with a signal. If no *interval* is defined,
it is assumed that the out script loops it self.
*restart-interval*: ++
typeof: integer ++
The restart interval (in seconds).
Can't be used with the *interval* option, so only with continuous scripts.
Once the script exit, it'll be re-executed after the *restart-interval*.
*signal*: ++
typeof: integer ++
The signal number used to update the module.
The number is valid between 1 and N, where *SIGRTMIN+N* = *SIGRTMAX*.
*format*: ++
typeof: string ++
default: {} ++
The format, how information should be displayed. On {} data gets inserted.
*format-icons*: ++
typeof: array ++
Based on the set percentage, the corresponding icon gets selected. The order is *low* to *high*.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*escape*: ++
typeof: bool ++
default: false ++
Option to enable escaping of script output.
# RETURN-TYPE
When *return-type* is set to *json*, Waybar expects the *exec*-script to output its data in JSON format.
This should look like this:
```
{"text": "$text", "tooltip": "$tooltip", "class": "$class", "percentage": $percentage }
```
The *class* parameter also accepts an array of strings.
If nothing or an invalid option is specified, Waybar expects i3blocks style output. Values are *newline* separated.
This should look like this:
```
$text\\n$tooltip\\n$class*
```
*class* is a CSS class, to apply different styles in *style.css*
# FORMAT REPLACEMENTS
*{}*: Output of the script.
*{percentage}* Percentage which can be set via a json return-type.
*{icon}*: An icon from 'format-icons' according to percentage.
# EXAMPLES
## Spotify:
```
"custom/spotify": {
"format": " {}",
"max-length": 40,
"interval": 30, // Remove this if your script is endless and write in loop
"exec": "$HOME/.config/waybar/mediaplayer.sh 2> /dev/null", // Script in resources folder
"exec-if": "pgrep spotify"
}
```
## mpd:
```
"custom/mpd": {
"format": "♪ {}",
//"max-length": 15,
"interval": 10,
"exec": "mpc current",
"exec-if": "pgrep mpd",
"on-click": "mpc toggle",
"on-click-right": "sonata"
}
```
## cmus:
```
"custom/cmus": {
"format": "♪ {}",
//"max-length": 15,
"interval": 10,
"exec": "cmus-remote -C \"format_print '%a - %t'\"", // artist - title
"exec-if": "pgrep cmus",
"on-click": "cmus-remote -u", //toggle pause
"escape": true //handle markup entities
}
```
## Pacman
```
"custom/pacman": {
"format": "{} ",
"interval": "once",
"exec": "pacman_packages",
"on-click": "update-system",
"signal": 8
}
```
## Alternate Pacman
```
"custom/pacman": {
"format": "{} ",
"interval": 3600, // every hour
"exec": "checkupdates | wc -l", // # of updates
"exec-if": "exit 0", // always run; consider advanced run conditions
"on-click": "termite -e 'sudo pacman -Syu'; pkill -SIGRTMIN+8 waybar", // update system
"signal": 8
}
```
You can use the signal and update the number of available packages with *pkill -RTMIN+8 waybar*.
# STYLE
- *#custom-<name>*
- *#custom-<name>.<class>*
- *<class>* can be set by the script. For more information see *return-type*

97
man/waybar-disk.5.scd Normal file
View File

@ -0,0 +1,97 @@
waybar-disk(5)
# NAME
waybar - disk module
# DESCRIPTION
The *disk* module displays the current disk space used.
# CONFIGURATION
Addressed by *disk*
*path*: ++
typeof: string ++
default: "/" ++
Any path residing in the filesystem or mountpoint for which the information should be displayed.
*interval*: ++
typeof: integer++
default: 30 ++
The interval in which the information gets polled.
*format*: ++
typeof: string ++
default: "{percentage_used}%" ++
The format, how information should be displayed.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
default: "{used} out of {total} used ({percentage_used}%)" ++
The format of the information displayed in the tooltip.
# FORMAT REPLACEMENTS
*{percentage_used}*: Percentage of disk in use.
*{percentage_free}*: Percentage of free disk space
*{total}*: Total amount of space on the disk, partition or mountpoint.
*{used}*: Amount of used disk space.
*{free}*: Amount of available disk space for normal users.
*{path}*: The path specified in the configuration.
# EXAMPLES
```
"disk": {
"interval": 30,
"format": "{percentage_free}% free on {path}",
}
```
# STYLE
- *#disk*

View File

@ -0,0 +1,75 @@
waybar-idle-inhibitor(5)
# NAME
waybar - idle_inhibitor module
# DESCRIPTION
The *idle_inhibitor* module can inhibiting the idle behavior such as screen blanking, locking, and
screensaving, also known as "presentation mode".
# CONFIGURATION
*format*: ++
typeof: string ++
The format, how the state should be displayed.
*format-icons*: ++
typeof: array ++
Based on the current state, the corresponding icon gets selected.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module. A click also toggles the state
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# FORMAT REPLACEMENTS
*{status}*: status (*activated* or *deactivated*)
*{icon}*: Icon, as defined in *format-icons*
# EXAMPLES
```
"idle_inhibitor": {
"format": "{icon}",
"format-icons": {
"activated": "",
"deactivated": ""
}
}
```

97
man/waybar-memory.5.scd Normal file
View File

@ -0,0 +1,97 @@
waybar-memory(5)
# NAME
waybar - memory module
# DESCRIPTION
The *memory* module displays the current memory utilization.
# CONFIGURATION
Addressed by *memory*
*interval*: ++
typeof: integer++
default: 30 ++
The interval in which the information gets polled.
*format*: ++
typeof: string ++
default: {percentage}% ++
The format, how information should be displayed.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*states*: ++
typeof: array ++
A number of memory utilization states which get activated on certain percentage thresholds. See *waybar-states(5)*.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# FORMAT REPLACEMENTS
*{percentage}*: Percentage of memory in use.
*{total}*: Amount of total memory available in GiB.
*{used}*: Amount of used memory in GiB.
*{avail}*: Amount of available memory in GiB.
# EXAMPLES
```
"memory": {
"interval": 30,
"format": "{}% ",
"max-length": 10
}
```
## FORMATTED MEMORY VALUES
```
"memory": {
"interval": 30,
"format": "{used:0.1f}G/{total:0.1f}G "
}
```
# STYLE
- *#memory*

208
man/waybar-mpd.5.scd Normal file
View File

@ -0,0 +1,208 @@
waybar-mpd(5)
# NAME
waybar - mpd module
# DESCRIPTION
The *mpd* module displays information about a running "Music Player Daemon" instance.
# CONFIGURATION
Addressed by *mpd*
*server*: ++
typeof: string ++
The network address or Unix socket path of the MPD server. If empty, connect to the default host.
*port*: ++
typeof: integer ++
The port MPD listens to. If empty, use the default port.
*interval*: ++
typeof: integer++
default: 5 ++
The interval in which the connection to the MPD server is retried
*timeout*: ++
typeof: integer++
default: 30 ++
The timeout for the connection. Change this if your MPD server has a low `connection_timeout` setting
*unknown-tag*: ++
typeof: string ++
default: "N/A" ++
The text to display when a tag is not present in the current song, but used in `format`
*format*: ++
typeof: string ++
default: "{album} - {artist} - {title}" ++
Information displayed when a song is playing.
*format-stopped*: ++
typeof: string ++
default: "stopped" ++
Information displayed when the player is stopped.
*format-paused*: ++
typeof: string ++
This format is used when a song is paused.
*format-disconnected*: ++
typeof: string ++
default: "disconnected" ++
Information displayed when the MPD server can't be reached.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
default: "MPD (connected)" ++
Tooltip information displayed when connected to MPD.
*tooltip-format-disconnected*: ++
typeof: string ++
default: "MPD (disconnected)" ++
Tooltip information displayed when the MPD server can't be reached.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*state-icons*: ++
typeof: object ++
default: {} ++
Icon to show depending on the play/pause state of the player (*{ "playing": "...", "paused": "..." }*)
*consume-icons*: ++
typeof: object ++
default: {} ++
Icon to show depending on the "consume" option (*{ "on": "...", "off": "..." }*)
*random-icons*: ++
typeof: object ++
default: {} ++
Icon to show depending on the "random" option (*{ "on": "...", "off": "..." }*)
*repeat-icons*: ++
typeof: object ++
default: {} ++
Icon to show depending on the "repeat" option (*{ "on": "...", "off": "..." }*)
*single-icons*: ++
typeof: object ++
default: {} ++
Icon to show depending on the "single" option (*{ "on": "...", "off": "..." }*)
# FORMAT REPLACEMENTS
## WHEN PLAYING/PAUSED
*{artist}*: The artist of the current song
*{albumArtist}*: The artist of the current album
*{album}*: The album of the current song
*{title}*: The title of the current song
*{date}*: The date of the current song
*{elapsedTime}*: The current position of the current song. To format as a date/time (see example configuration)
*{totalTime}*: The length of the current song. To format as a date/time (see example configuration)
*{stateIcon}*: The icon corresponding the playing or paused status of the player (see *state-icons* option)
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
*{randomIcon}*: The icon corresponding the "random" option (see *random-icons* option)
*{repeatIcon}*: The icon corresponding the "repeat" option (see *repeat-icons* option)
*{singleIcon}*: The icon corresponding the "single" option (see *single-icons* option)
## WHEN STOPPED
*{consumeIcon}*: The icon corresponding the "consume" option (see *consume-icons* option)
*{randomIcon}*: The icon corresponding the "random" option (see *random-icons* option)
*{repeatIcon}*: The icon corresponding the "repeat" option (see *repeat-icons* option)
*{singleIcon}*: The icon corresponding the "single" option (see *single-icons* option)
## WHEN DISCONNECTED
Currently, no format replacements when disconnected.
# EXAMPLES
```
"mpd": {
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ",
"format-disconnected": "Disconnected ",
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
"interval": 2,
"consume-icons": {
"on": " " // Icon shows only when "consume" is on
},
"random-icons": {
"off": "<span color=\"#f53c3c\"></span> ", // Icon grayed out when "random" is off
"on": " "
},
"repeat-icons": {
"on": " "
},
"single-icons": {
"on": "1 "
},
"state-icons": {
"paused": "",
"playing": ""
},
"tooltip-format": "MPD (connected)",
"tooltip-format-disconnected": "MPD (disconnected)"
}
```
# STYLE
- *#mpd*
- *#mpd.disconnected*
- *#mpd.stopped*
- *#mpd.playing*
- *#mpd.paused*

159
man/waybar-network.5.scd Normal file
View File

@ -0,0 +1,159 @@
waybar-network(5)
# NAME
waybar - network module
# DESCRIPTION
The *network* module displays information about the current network connections.
# CONFIGURATION
Addressed by *network*
*interface*: ++
typeof: string ++
Use the defined interface instead of auto detection. Accepts wildcard.
*interval*: ++
typeof: integer ++
default: 60 ++
The interval in which the network information gets polled (e.g. signal strength).
*family*: ++
typeof: string ++
default: *ipv4* ++
The address family that is used for the format replacement {ipaddr} and to determine if a network connection is present.
*format*: ++
typeof: string ++
default: *{ifname}* ++
The format, how information should be displayed. This format is used when other formats aren't specified.
*format-ethernet*: ++
typeof: string ++
This format is used when a ethernet interface is displayed.
*format-wifi*: ++
typeof: string ++
This format is used when a wireless interface is displayed.
*format-linked*: ++
typeof: string ++
This format is used when a linked interface with no ip address is displayed.
*format-disconnected*: ++
typeof: string ++
This format is used when the displayed interface is disconnected.
*format-icons*: ++
typeof: array/object ++
Based on the current signal strength, the corresponding icon gets selected. ++
The order is *low* to *high*. Or by the state if it is an object.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: *true* ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified.
*tooltip-format-ethernet*: ++
typeof: string ++
This format is used when a ethernet interface is displayed.
*tooltip-format-wifi*: ++
typeof: string ++
This format is used when a wireless interface is displayed.
*tooltip-format-disconnected*: ++
typeof: string ++
This format is used when the displayed interface is disconnected.
# FORMAT REPLACEMENTS
*{ifname}*: Name of the network interface.
*{ipaddr}*: The first IP of the interface.
*{netmask}*: The subnetmask corresponding to the IP.
*{cidr}*: The subnetmask corresponding to the IP in CIDR notation.
*{essid}*: Name (SSID) of the wireless network.
*{signalStrength}*: Signal strength of the wireless network.
*{signaldBm}*: Signal strength of the wireless network in dBm.
*{frequency}*: Frequency of the wireless network in MHz.
*{bandwidthUpBits}*: Instant up speed in bits/seconds.
*{bandwidthDownBits}*: Instant down speed in bits/seconds.
*{bandwidthUpOctets}*: Instant up speed in octets/seconds.
*{bandwidthDownOctets}*: Instant down speed in octets/seconds.
*{icon}*: Icon, as defined in *format-icons*.
# EXAMPLES
```
"network": {
"interface": "wlp2s0",
"format": "{ifname}",
"format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ifname} ",
"format-disconnected": "", //An empty format will hide the module.
"tooltip-format": "{ifname}",
"tooltip-format-wifi": "{essid} ({signalStrength}%) ",
"tooltip-format-ethernet": "{ifname} ",
"tooltip-format-disconnected": "Disconnected",
"max-length": 50
}
```
# STYLE
- *#network*
- *#network.disconnected*
- *#network.linked*
- *#network.ethernet*
- *#network.wifi*

139
man/waybar-pulseaudio.5.scd Normal file
View File

@ -0,0 +1,139 @@
waybar-pulseaudio(5)
# NAME
waybar - pulseaudio module
# DESCRIPTION
The *pulseaudio* module displays the current volume reported by PulseAudio.
Additionally you can control the volume by scrolling *up* or *down* while the cursor is over the module.
# CONFIGURATION
*format*: ++
typeof: string ++
default: {volume}% ++
The format, how information should be displayed. This format is used when other formats aren't specified.
*format-bluetooth*: ++
typeof: string ++
This format is used when using bluetooth speakers.
*format-muted*: ++
typeof: string ++
This format is used when the sound is muted.
*format-source*: ++
typeof: string ++
default: {volume}% ++
This format used for the source.
*format-source-muted*: ++
typeof: string ++
This format is used when the source is muted.
*format-icons*: ++
typeof: array ++
Based on the current port-name and volume, the corresponding icon gets selected. The order is *low* to *high*. See *Icons*.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*states*: ++
typeof: array ++
A number of volume states which get activated on certain volume levels. See *waybar-states(5)*
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*scroll-step*: ++
typeof: float ++
default: 1.0 ++
The speed in which to change the volume when scrolling.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module. This replaces the default behaviour of volume control.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module. This replaces the default behaviour of volume control.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# FORMAT REPLACEMENTS
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
*{volume}*: Volume in percentage.
*{icon}*: Icon, as defined in *format-icons*.
*{format_source}*: Source format, *format-source*, *format-source-muted*.
# ICONS:
The following strings for *format-icons* are supported.
If they are found in the current PulseAudio port name, the corresponding icons will be selected.
- *default* (Shown, when no other port is found)
- *headphone*
- *speaker*
- *hdmi*
- *headset*
- *hands-free*
- *portable*
- *car*
- *hifi*
- *phone*
# EXAMPLES
```
"pulseaudio": {
"format": "{volume}% {icon}",
"format-bluetooth": "{volume}% {icon}",
"format-muted": "",
"format-icons": {
"headphones": "",
"handsfree": "",
"headset": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", ""]
},
"scroll-step": 1,
"on-click": "pavucontrol"
}
```
# STYLE
- *#pulseaudio*
- *#pulseaudio.bluetooth*
- *#pulseaudio.muted*

43
man/waybar-states.5.scd Normal file
View File

@ -0,0 +1,43 @@
waybar-states(5)
# OVERVIEW
Some modules support 'states' which allows percentage values to be used as styling triggers to
apply a class when the value matches the declared state value.
# STATES
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
- The state can be addressed as a CSS class in the *style.css*. The name of the CSS class is the *<name>* of the state.
Each class gets activated when the current capacity is equal or below the configured *<value>*.
- Also each state can have its own *format*.
Those con be configured via *format-<name>*.
Or if you want to differentiate a bit more even as *format-<status>-<state>*.
# EXAMPLE
```
"battery": {
"bat": "BAT2",
"interval": 60,
"states": {
"warning": 30,
"critical": 15
},
"format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""],
"max-length": 25
}
```
# STYLING STATES
- *#battery.<state>*
- *<state>* can be defined in the *config*.
# EXAMPLE:
- *#battery.warning: { background: orange; }*
- *#battery.critical: { background: red; }*

View File

@ -0,0 +1,68 @@
waybar-sway-mode(5)
# NAME
waybar - sway mode module
# DESCRIPTION
The *mode* module displays the current binding mode of Sway
# CONFIGURATION
Addressed by *sway/mode*
*format*: ++
typeof: string ++
default: {} ++
The format, how information should be displayed. On {} data gets inserted.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# EXAMPLES
```
"sway/window": {
"format": " {}",
"max-length": 50
}
```
# STYLE
- *#mode*

View File

@ -0,0 +1,71 @@
waybar-sway-window(5)
# NAME
waybar - sway window module
# DESCRIPTION
The *window* module displays the title of the currently focused window in Sway
# CONFIGURATION
Addressed by *sway/window*
*format*: ++
typeof: string ++
default: {} ++
The format, how information should be displayed. On {} data gets inserted.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*on-click*: ++
typeof: string ++
Command to execute when clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# EXAMPLES
```
"sway/window": {
"format": "{}",
"max-length": 50
}
```
# STYLE
- *#window*
- *window#waybar.empty* When no windows is in the workspace
- *window#waybar.solo* When one window is in the workspace
- *window#waybar.<app_id>* Where *app_id* is the app_id or *instance* name like (*chromium*) of the only window in the workspace

View File

@ -0,0 +1,126 @@
waybar-sway-workspaces(5)
# NAME
waybar - sway workspaces module
# DESCRIPTION
The *workspaces* module displays the currently used workspaces in Sway.
# CONFIGURATION
Addressed by *sway/workspaces*
*all-outputs*: ++
typeof: bool ++
default: false ++
If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output.
*format*: ++
typeof: string ++
default: {value} ++
The format, how information should be displayed.
*format-icons*: ++
typeof: array ++
Based on the workspace name and state, the corresponding icon gets selected. See *icons*.
*disable-scroll*: ++
typeof: bool ++
default: false ++
If set to false, you can scroll to cycle through workspaces. If set to true this behaviour is disabled.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*disable-scroll-wraparound*: ++
typeof: bool ++
default: false ++
If set to false, scrolling on the workspace indicator will wrap around to the first workspace when reading the end, and vice versa. If set to true this behavior is disabled.
*enable-bar-scroll*: ++
typeof: bool ++
default: false ++
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
*disable-markup*: ++
typeof: bool ++
default: false ++
If set to true, button label will escape pango markup.
*current-only*: ++
typeof: bool ++
default: false ++
If set to true. Only focused workspaces will be shown.
*persistent_workspaces*: ++
typeof: json (see below) ++
default: empty ++
Lists workspaces that should always be shown, even when non existent
# FORMAT REPLACEMENTS
*{value}*: Name of the workspace, as defined by sway.
*{name}*: Number stripped from workspace value.
*{icon}*: Icon, as defined in *format-icons*.
*{index}*: Index of the workspace.
# ICONS
Additional to workspace name matching, the following *format-icons* can be set.
- *default*: Will be shown, when no string matches is found.
- *urgent*: Will be shown, when workspace is flagged as urgent
- *focused*: Will be shown, when workspace is focused
- *persistent*: Will be shown, when workspace is persistent one.
# PERSISTENT WORKSPACES
Each entry of *persistent_workspace* names a workspace that should always be shown.
Associated with that value is a list of outputs indicating *where* the workspace should be shown,
an empty list denoting all outputs.
```
"sway/workspaces": {
"persistent_workspaces": {
"3": [], // Always show a workspace with name '3', on all outputs if it does not exists
"4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists
"5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists
}
}
```
n.b.: the list of outputs can be obtained from command line using *swaymsg -t get_outputs*
# EXAMPLES
```
"sway/workspaces": {
"disable-scroll": true,
"all-outputs": true,
"format": "{name}: {icon}",
"format-icons": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"urgent": "",
"focused": "",
"default": ""
}
}
```
# Style
- *#workspaces button*
- *#workspaces button.visible*
- *#workspaces button.focused*
- *#workspaces button.urgent*
- *#workspaces button.persistent*

View File

@ -0,0 +1,111 @@
waybar-temperature(5)
# NAME
waybar - temperature module
# DESCRIPTION
The *temperature* module displays the current temperature from a thermal zone.
# CONFIGURATION
Addressed by *temperature*
*thermal-zone*: ++
typeof: integer ++
The thermal zone, as in */sys/class/thermal/*.
*hwmon-path*: ++
typeof: string ++
The temperature path to use, e.g. */sys/class/hwmon/hwmon2/temp1_input* instead of one in */sys/class/thermal/*.
*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*.
*input-filename*: ++
typeof: string ++
The temperature filename of your *hwmon-path-abs*, e.g. *temp1_input*
*critical-threshold*: ++
typeof: integer ++
The threshold before it is considered critical (Celsius).
*interval*: ++
typeof: integer ++
default: 10 ++
The interval in which the information gets polled.
*format-critical*: ++
typeof: string ++
The format to use when temperature is considered critical
*format*: ++
typeof: string ++
default: {temperatureC}°C ++
The format (Celsius/Fahrenheit) in which the temperature should be displayed.
*format-icons*: ++
typeof: array ++
Based on the current temperature (Celsius) and *critical-threshold* if available, the corresponding icon gets selected. The order is *low* to *high*.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label.
*max-length*: ++
typeof: integer ++
The maximum length in characters the module should display.
*on-click*: ++
typeof: string ++
Command to execute when you clicked on the module.
*on-click-middle*: ++
typeof: string ++
Command to execute when middle-clicked on the module using mousewheel.
*on-click-right*: ++
typeof: string ++
Command to execute when you right clicked on the module.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
# FORMAT REPLACEMENTS
*{temperatureC}*: Temperature in Celsius.
*{temperatureF}*: Temperature in Fahrenheit.
# EXAMPLES
```
"temperature": {
// "thermal-zone": 2,
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
// "critical-threshold": 80,
// "format-critical": "{temperatureC}°C ",
"format": "{temperatureC}°C "
}
```
# STYLE
- *#temperature*
- *#temperature.critical*

35
man/waybar-tray.5.scd Normal file
View File

@ -0,0 +1,35 @@
waybar-tray(5)
# NAME
waybar - tray module
# DESCRIPTION
_WARNING_ *tray* is still in beta. There may me bugs. Breaking changes may occur.
# CONFIGURATION
Addressed by *tray*
*icon-size*: ++
typeof: integer ++
Defines the size of the tray icons.
*spacing*: ++
typeof: integer ++
Defines the spacing between the tray icons.
# EXAMPLES
```
"tray": {
"icon-size": 21,
"spacing": 10
}
```
# STYLE
- *#tray*

200
man/waybar.5.scd Normal file
View File

@ -0,0 +1,200 @@
waybar(5)
# NAME
waybar - configuration file
# DESCRIPTION
The configuration uses the JSON file format and is named *config*.
Valid locations for this file are:
- *$XDG_CONFIG_HOME/waybar/config*
- *~/.config/waybar/config*
- *~/waybar/config*
- */etc/xdg/waybar/config*
A good starting point is the default configuration found at https://github.com/Alexays/Waybar/blob/master/resources/config.
Also a minimal example configuration can be found on the at the bottom of this man page.
# BAR CONFIGURATION
*layer* ++
typeof: string ++
default: bottom ++
Decide if the bar is displayed in front (*top*) of the windows or behind (*bottom*)
them.
*output* ++
typeof: string|array ++
Specifies on which screen this bar will be displayed.
*position* ++
typeof: string ++
default: top ++
Bar position, can be *top*, *bottom*, *left*, *right*.
*height* ++
typeof: integer ++
Height to be used by the bar if possible. Leave blank for a dynamic value.
*width* ++
typeof: integer ++
Width to be used by the bar if possible. Leave blank for a dynamic value.
*modules-left* ++
typeof: array ++
Modules that will be displayed on the left.
*modules-center* ++
typeof: array ++
Modules that will be displayed in the center.
*modules-right* ++
typeof: array
Modules that will be displayed on the right.
*margin* ++
typeof: string ++
Margins value using the CSS format without units.
*margin-<top\|left\|bottom\|right>* ++
typeof: integer ++
Margins value without units.
*name* ++
typeof: string ++
Optional name added as a CSS class, for styling multiple waybars.
*gtk-layer-shell* ++
typeof: bool ++
default: true ++
Option to disable the use of gtk-layer-shell for popups.
Only functional if compiled with gtk-layer-shell support.
# MODULE FORMAT
You can use PangoMarkupFormat (See https://developer.gnome.org/pango/stable/PangoMarkupFormat.html#PangoMarkupFormat).
e.g.
```
"format": "<span style=\"italic\">{}</span>"
```
# MULTIPLE INSTANCES OF A MODULE
If you want to have a second instance of a module, you can suffix it by a '#' and a custom name.
For example if you want a second battery module, you can add *"battery#bat2"* to your modules.
To configure the newly added module, you then also add a module configuration with the same name.
This could then look something like this *(this is an incomplete example)*:
```
"modules-right": ["battery", "battery#bat2"],
"battery": {
"bat": "BAT1"
},
"battery#bat2": {
"bat": "BAT2"
}
```
# MINIMAL CONFIGURATION
A minimal *config* file could look like this:
```
{
"layer": "top",
"modules-left": ["sway/workspaces", "sway/mode"],
"modules-center": ["sway/window"],
"modules-right": ["battery", "clock"],
"sway/window": {
"max-length": 50
},
"battery": {
"format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""]
},
"clock": {
"format-alt": "{:%a, %d. %b %H:%M}"
}
}
```
# MULTI OUTPUT CONFIGURATION
## Limit a configuration to some outputs
```
{
"layer": "top",
"output": "eDP-1",
"modules-left": ["sway/workspaces", "sway/mode"],
...
}
```
```
{
"layer": "top",
"output": ["eDP-1", "VGA"],
"modules-left": ["sway/workspaces", "sway/mode"],
...
}
```
## Configuration of multiple outputs
Don't specify an output to create multiple bars on the same screen.
```
[{
"layer": "top",
"output": "eDP-1",
"modules-left": ["sway/workspaces", "sway/mode"],
...
}, {
"layer": "top",
"output": "VGA",
"modules-right": ["clock"],
...
}]
```
## Rotating modules
When positioning Waybar on the left or right side of the screen, sometimes it's useful to be able to rotate the contents of a module so the text runs vertically. This can be done using the "rotate" property of the module. Example:
```
{
"clock": {
"rotate": 90
}
}
```
Valid options for the "rotate" property are: 0, 90, 180 and 270.
# SUPPORTED MODULES
- *waybar-backlight(5)*
- *waybar-battery(5)*
- *waybar-clock(5)*
- *waybar-cpu(5)*
- *waybar-custom(5)*
- *waybar-idle-inhibitor(5)*
- *waybar-memory(5)*
- *waybar-mdp(5)*
- *waybar-network(5)*
- *waybar-pulseaudio(5)*
- *waybar-sway-mode(5)*
- *waybar-sway-window(5)*
- *waybar-sway-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*

View File

@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.6.9',
version: '0.9.2',
license: 'MIT',
default_options : [
'cpp_std=c++17',
@ -9,6 +9,8 @@ project(
],
)
compiler = meson.get_compiler('cpp')
cpp_args = []
cpp_link_args = []
@ -16,13 +18,14 @@ if get_option('libcxx')
cpp_args += ['-stdlib=libc++']
cpp_link_args += ['-stdlib=libc++', '-lc++abi']
cpp_link_args += ['-lc++fs']
if compiler.has_link_argument('-lc++fs')
cpp_link_args += ['-lc++fs']
endif
else
cpp_link_args += ['-lstdc++fs']
endif
compiler = meson.get_compiler('cpp')
git = find_program('git', required: false)
git = find_program('git', native: true, required: false)
if not git.found()
add_project_arguments('-DVERSION="@0@"'.format(meson.project_version()), language: 'cpp')
@ -42,6 +45,22 @@ if not compiler.has_header('filesystem')
endif
endif
code = '''
#include <langinfo.h>
#include <locale.h>
int main(int argc, char** argv) {
locale_t locale = newlocale(LC_ALL, "en_US.UTF-8", nullptr);
char* str;
str = nl_langinfo_l(_NL_TIME_WEEK_1STDAY, locale);
str = nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, locale);
freelocale(locale);
return 0;
}
'''
if compiler.links(code, name : 'nl_langinfo with _NL_TIME_WEEK_1STDAY, _NL_TIME_FIRST_WEEKDAY')
add_project_arguments('-DHAVE_LANGINFO_1STDAY', language: 'cpp')
endif
add_global_arguments(cpp_args, language : 'cpp')
add_global_link_arguments(cpp_link_args, language : 'cpp')
@ -52,7 +71,7 @@ spdlog = dependency('spdlog', version : ['>=1.3.1'], fallback : ['spdlog', 'spdl
wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor')
wayland_protos = dependency('wayland-protocols')
gtkmm = dependency('gtkmm-3.0')
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4', required: get_option('dbusmenu-gtk'))
giounix = dependency('gio-unix-2.0', required: get_option('dbusmenu-gtk'))
jsoncpp = dependency('jsoncpp')
@ -62,16 +81,38 @@ libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
libpulse = dependency('libpulse', required: get_option('pulseaudio'))
libudev = dependency('libudev', required: get_option('libudev'))
libmpdclient = dependency('libmpdclient', required: get_option('mpd'))
gtk_layer_shell = dependency('gtk-layer-shell-0',
required: get_option('gtk-layer-shell'),
fallback : ['gtk-layer-shell', 'gtk_layer_shell_dep'])
systemd = dependency('systemd', required: get_option('systemd'))
tz_dep = dependency('date', default_options : [ 'use_system_tzdb=true' ], fallback: [ 'date', 'tz_dep' ])
prefix = get_option('prefix')
conf_data = configuration_data()
conf_data.set('prefix', prefix)
if systemd.found()
user_units_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
configure_file(
configuration: conf_data,
input: './resources/waybar.service.in',
output: '@BASENAME@',
install_dir: user_units_dir
)
endif
src_files = files(
'src/factory.cpp',
'src/AModule.cpp',
'src/ALabel.cpp',
'src/modules/memory.cpp',
'src/modules/battery.cpp',
'src/modules/clock.cpp',
'src/modules/custom.cpp',
'src/modules/cpu.cpp',
'src/modules/idle_inhibitor.cpp',
'src/modules/disk.cpp',
'src/modules/idle_inhibitor.cpp',
'src/modules/temperature.cpp',
'src/main.cpp',
'src/bar.cpp',
@ -118,6 +159,10 @@ if libmpdclient.found()
src_files += 'src/modules/mpd.cpp'
endif
if gtk_layer_shell.found()
add_project_arguments('-DHAVE_GTK_LAYER_SHELL', language: 'cpp')
endif
subdir('protocol')
executable(
@ -140,7 +185,9 @@ executable(
libnlgen,
libpulse,
libudev,
libmpdclient
libmpdclient,
gtk_layer_shell,
tz_dep
],
include_directories: [include_directories('include')],
install: true,
@ -152,6 +199,51 @@ install_data(
install_dir: join_paths(get_option('out'), 'etc/xdg/waybar')
)
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
if scdoc.found()
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
sh = find_program('sh', native: true)
mandir = get_option('mandir')
man_files = [
'waybar.5.scd',
'waybar-backlight.5.scd',
'waybar-battery.5.scd',
'waybar-clock.5.scd',
'waybar-cpu.5.scd',
'waybar-custom.5.scd',
'waybar-disk.5.scd',
'waybar-idle-inhibitor.5.scd',
'waybar-memory.5.scd',
'waybar-mpd.5.scd',
'waybar-network.5.scd',
'waybar-pulseaudio.5.scd',
'waybar-sway-mode.5.scd',
'waybar-sway-window.5.scd',
'waybar-sway-workspaces.5.scd',
'waybar-temperature.5.scd',
'waybar-tray.5.scd',
'waybar-states.5.scd',
]
foreach filename : man_files
topic = filename.split('.')[-3].split('/')[-1]
section = filename.split('.')[-2]
output = '@0@.@1@'.format(topic, section)
custom_target(
output,
input: 'man/@0@'.format(filename),
output: output,
command: [
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
],
install: true,
install_dir: '@0@/man@1@'.format(mandir, section)
)
endforeach
endif
clangtidy = find_program('clang-tidy', required: false)
if clangtidy.found()

View File

@ -2,6 +2,9 @@ option('libcxx', type : 'boolean', value : false, description : 'Build with Clan
option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features')
option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features')
option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio')
option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit')
option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray')
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
option('mpd', type: 'feature', value: 'auto', description: 'Enable support for the Music Player Daemon')
option('out', type: 'string', value : '/', description: 'output prefix directory')
option('gtk-layer-shell', type: 'feature', value: 'auto', description: 'Use gtk-layer-shell library for popups support')

View File

@ -31,7 +31,9 @@
<property name='Id' type='s' access='read'/>
<property name='Title' type='s' access='read'/>
<property name='Status' type='s' access='read'/>
<!-- See discussion on pull #536
<property name='WindowId' type='u' access='read'/>
-->
<property name='IconThemePath' type='s' access='read'/>
<property name='IconName' type='s' access='read'/>
<property name='IconPixmap' type='a(iiay)' access='read'/>

View File

@ -1,5 +1,5 @@
{
"layer": "top", // Waybar at top layer
// "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar position (top|bottom|left|right)
"height": 30, // Waybar height (to be removed for auto height)
// "width": 1280, // Waybar width
@ -64,7 +64,8 @@
"spacing": 10
},
"clock": {
"tooltip-format": "{:%Y-%m-%d | %H:%M}",
// "timezone": "America/New_York",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format-alt": "{:%Y-%m-%d}"
},
"cpu": {
@ -116,12 +117,13 @@
// "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon} {format_source}",
"format-bluetooth-muted": " {icon} {format_source}",
"format-muted": " {format_source}",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": {
"headphones": "",
"handsfree": "",
"headphone": "",
"hands-free": "",
"headset": "",
"phone": "",
"portable": "",
@ -140,5 +142,6 @@
},
"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
}
}

View File

@ -38,6 +38,8 @@ def on_metadata(player, metadata, manager):
elif player.get_artist() != '' and player.get_title() != '':
track_info = '{artist} - {title}'.format(artist=player.get_artist(),
title=player.get_title())
else:
track_info = player.get_title()
if player.props.status != 'Playing' and track_info:
track_info = '' + track_info
@ -45,7 +47,7 @@ def on_metadata(player, metadata, manager):
def on_player_appeared(manager, player, selected_player=None):
if player is not None and player.name == selected_player:
if player is not None and (selected_player is None or player.name == selected_player):
init_player(manager, player)
else:
logger.debug("New player appeared, but it's not the selected player, skipping")
@ -77,7 +79,7 @@ def signal_handler(sig, frame):
def parse_arguments():
parser = argparse.ArgumentParser()
# Increase verbosity with every occurance of -v
# Increase verbosity with every occurence of -v
parser.add_argument('-v', '--verbose', action='count', default=0)
# Define for which player we're listening
@ -123,4 +125,3 @@ def main():
if __name__ == '__main__':
main()

View File

@ -1,6 +1,7 @@
* {
border: none;
border-radius: 0;
/* `otf-font-awesome` is required to be installed for icons */
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
min-height: 0;
@ -36,7 +37,6 @@ window#waybar.chromium {
border: none;
}
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button {
padding: 0 5px;
background-color: transparent;
@ -44,6 +44,13 @@ window#waybar.chromium {
border-bottom: 3px solid transparent;
}
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button:hover {
background: rgba(0, 0, 0, 0.2);
box-shadow: inherit;
border-bottom: 3px solid #ffffff;
}
#workspaces button.focused {
background-color: #64727D;
border-bottom: 3px solid #ffffff;
@ -58,9 +65,21 @@ window#waybar.chromium {
border-bottom: 3px solid #ffffff;
}
#clock, #battery, #cpu, #memory, #temperature, #backlight, #network, #pulseaudio, #custom-media, #tray, #mode, #idle_inhibitor {
#clock,
#battery,
#cpu,
#memory,
#temperature,
#backlight,
#network,
#pulseaudio,
#custom-media,
#tray,
#mode,
#idle_inhibitor,
#mpd {
padding: 0 10px;
margin: 0 5px;
margin: 0 4px;
color: #ffffff;
}

View File

@ -0,0 +1,13 @@
[Unit]
Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
Documentation=https://github.com/Alexays/Waybar/wiki/
PartOf=wayland-session.target
After=wayland-session.target
[Service]
Type=dbus
BusName=fr.arouillard.waybar
ExecStart=@prefix@/bin/waybar
[Install]
WantedBy=wayland-session.target

View File

@ -2,9 +2,11 @@
#include <fmt/format.h>
#include <util/command.hpp>
waybar::ALabel::ALabel(const Json::Value& config, const std::string& name, const std::string& id,
const std::string& format, uint16_t interval)
: config_(config),
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)
: AModule(config, name, id, config["format-alt"].isString()),
format_(config_["format"].isString() ? config_["format"].asString() : format),
interval_(config_["interval"] == "once"
? std::chrono::seconds(100000000)
@ -19,101 +21,20 @@ waybar::ALabel::ALabel(const Json::Value& config, const std::string& name, const
if (config_["max-length"].isUInt()) {
label_.set_max_width_chars(config_["max-length"].asUInt());
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
} else if (ellipsize && label_.get_max_width_chars() == -1) {
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
}
if (config_["rotate"].isUInt()) {
label_.set_angle(config["rotate"].asUInt());
}
if (config_["format-alt"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
}
// configure events' user commands
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
config_["on-click-right"].isString()) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &ALabel::handleToggle));
}
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &ALabel::handleScroll));
}
}
waybar::ALabel::~ALabel() {
for (const auto& pid : pid_) {
if (pid != -1) {
kill(-pid, 9);
}
}
}
auto waybar::ALabel::update() -> void {
auto ALabel::update() -> void {
// Nothing here
}
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
std::string format;
if (config_["on-click"].isString() && e->button == 1) {
format = config_["on-click"].asString();
} else if (config_["on-click-middle"].isString() && e->button == 2) {
format = config_["on-click-middle"].asString();
} else if (config_["on-click-right"].isString() && e->button == 3) {
format = config_["on-click-right"].asString();
} else if (config_["on-click-forward"].isString() && e->button == 8) {
format = config_["on-click-backward"].asString();
} else if (config_["on-click-backward"].isString() && e->button == 9) {
format = config_["on-click-forward"].asString();
}
if (!format.empty()) {
pid_.push_back(util::command::forkExec(fmt::format(format, fmt::arg("arg", click_param))));
}
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
alt_ = !alt_;
if (alt_ && config_["format-alt"].isString()) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
}
}
dp.emit();
return true;
}
bool waybar::ALabel::handleScroll(GdkEventScroll* e) {
// Avoid concurrent scroll event
std::lock_guard<std::mutex> lock(mutex_);
bool direction_up = false;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
}
if (e->direction == GDK_SCROLL_DOWN) {
direction_up = false;
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent*>(e), &delta_x, &delta_y);
if (delta_y < 0) {
direction_up = true;
} else if (delta_y > 0) {
direction_up = false;
}
}
if (direction_up && config_["on-scroll-up"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-scroll-up"].asString()));
} else if (config_["on-scroll-down"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-scroll-down"].asString()));
}
dp.emit();
return true;
}
std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_t max) {
auto format_icons = config_["format-icons"];
if (format_icons.isObject()) {
if (!alt.empty() && (format_icons[alt].isString() || format_icons[alt].isArray())) {
@ -133,7 +54,19 @@ std::string waybar::ALabel::getIcon(uint16_t percentage, const std::string& alt,
return "";
}
std::string waybar::ALabel::getState(uint8_t value, bool lesser) {
bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
if (config_["format-alt-click"].isUInt() && e->button == config_["format-alt-click"].asUInt()) {
alt_ = !alt_;
if (alt_ && config_["format-alt"].isString()) {
format_ = config_["format-alt"].asString();
} else {
format_ = default_format_;
}
}
return AModule::handleToggle(e);
}
std::string ALabel::getState(uint8_t value, bool lesser) {
if (!config_["states"].isObject()) {
return "";
}
@ -162,8 +95,4 @@ std::string waybar::ALabel::getState(uint8_t value, bool lesser) {
return valid_state;
}
bool waybar::ALabel::tooltipEnabled() {
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
}
waybar::ALabel::operator Gtk::Widget&() { return event_box_; }
} // namespace waybar

119
src/AModule.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "AModule.hpp"
#include <fmt/format.h>
#include <util/command.hpp>
namespace waybar {
AModule::AModule(const Json::Value& config, const std::string& name, const std::string& id,
bool enable_click, bool enable_scroll)
: config_(std::move(config)) {
// configure events' user commands
if (config_["on-click"].isString() || config_["on-click-middle"].isString() ||
config_["on-click-backward"].isString() || config_["on-click-forward"].isString() ||
config_["on-click-right"].isString() || enable_click) {
event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &AModule::handleToggle));
}
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString() || enable_scroll) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &AModule::handleScroll));
}
}
AModule::~AModule() {
for (const auto& pid : pid_) {
if (pid != -1) {
kill(-pid, 9);
}
}
}
auto AModule::update() -> void {
// Nothing here
}
bool AModule::handleToggle(GdkEventButton* const& e) {
std::string format;
if (config_["on-click"].isString() && e->button == 1) {
format = config_["on-click"].asString();
} else if (config_["on-click-middle"].isString() && e->button == 2) {
format = config_["on-click-middle"].asString();
} else if (config_["on-click-right"].isString() && e->button == 3) {
format = config_["on-click-right"].asString();
} else if (config_["on-click-forward"].isString() && e->button == 8) {
format = config_["on-click-backward"].asString();
} else if (config_["on-click-backward"].isString() && e->button == 9) {
format = config_["on-click-forward"].asString();
}
if (!format.empty()) {
pid_.push_back(util::command::forkExec(format));
}
dp.emit();
return true;
}
AModule::SCROLL_DIR AModule::getScrollDir(GdkEventScroll* e) {
switch (e -> direction) {
case GDK_SCROLL_UP: return SCROLL_DIR::UP;
case GDK_SCROLL_DOWN: return SCROLL_DIR::DOWN;
case GDK_SCROLL_LEFT: return SCROLL_DIR::LEFT;
case GDK_SCROLL_RIGHT: return SCROLL_DIR::RIGHT;
case GDK_SCROLL_SMOOTH: {
SCROLL_DIR dir{SCROLL_DIR::NONE};
distance_scrolled_y_ += e->delta_y;
distance_scrolled_x_ += e->delta_x;
gdouble threshold = 0;
if (config_["smooth-scrolling-threshold"].isNumeric()) {
threshold = config_["smooth-scrolling-threshold"].asDouble();
}
if (distance_scrolled_y_ < -threshold) {
dir = SCROLL_DIR::UP;
} else if (distance_scrolled_y_ > threshold) {
dir = SCROLL_DIR::DOWN;
} else if (distance_scrolled_x_ > threshold) {
dir = SCROLL_DIR::RIGHT;
} else if (distance_scrolled_x_ < -threshold) {
dir = SCROLL_DIR::LEFT;
}
switch (dir) {
case SCROLL_DIR::UP:
case SCROLL_DIR::DOWN:
distance_scrolled_y_ = 0;
break;
case SCROLL_DIR::LEFT:
case SCROLL_DIR::RIGHT:
distance_scrolled_x_ = 0;
break;
case SCROLL_DIR::NONE:
break;
}
return dir;
}
// Silence -Wreturn-type:
default: return SCROLL_DIR::NONE;
}
}
bool AModule::handleScroll(GdkEventScroll* e) {
auto dir = getScrollDir(e);
if (dir == SCROLL_DIR::UP && config_["on-scroll-up"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-scroll-up"].asString()));
} else if (dir == SCROLL_DIR::DOWN && config_["on-scroll-down"].isString()) {
pid_.push_back(util::command::forkExec(config_["on-scroll-down"].asString()));
}
dp.emit();
return true;
}
bool AModule::tooltipEnabled() {
return config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true;
}
AModule::operator Gtk::Widget&() { return event_box_; }
} // namespace waybar

View File

@ -1,14 +1,18 @@
#ifdef HAVE_GTK_LAYER_SHELL
#include <gtk-layer-shell.h>
#endif
#include "bar.hpp"
#include <spdlog/spdlog.h>
#include "client.hpp"
#include "factory.hpp"
#include <spdlog/spdlog.h>
waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
: output(w_output),
config(w_config),
window{Gtk::WindowType::WINDOW_TOPLEVEL},
surface(nullptr),
layer_surface(nullptr),
layer_surface_(nullptr),
anchor_(ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP),
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
@ -18,6 +22,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
window.set_name("waybar");
window.set_decorated(false);
window.get_style_context()->add_class(output->name);
window.get_style_context()->add_class(config["name"].asString());
window.get_style_context()->add_class(config["position"].asString());
if (config["position"] == "right" || config["position"] == "left") {
height_ = 0;
@ -26,11 +32,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
height_ = config["height"].isUInt() ? config["height"].asUInt() : height_;
width_ = config["width"].isUInt() ? config["width"].asUInt() : width_;
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
if (config["position"] == "bottom") {
anchor_ = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
} else if (config["position"] == "left") {
@ -51,74 +52,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
vertical = true;
}
setupWidgets();
if (window.get_realized()) {
onRealize();
}
window.show_all();
}
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
}
if (config["height"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Height");
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
}
if (config["width"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Width");
} else {
tmp_width = ev->width;
}
}
if (tmp_width != width_ || tmp_height != height_) {
zwlr_layer_surface_v1_set_size(layer_surface, tmp_width, tmp_height);
}
}
void waybar::Bar::onRealize() {
auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window);
}
void waybar::Bar::onMap(GdkEventAny* ev) {
auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
auto client = waybar::Client::inst();
auto layer =
config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
client->layer_shell, surface, output->output, layer, "waybar");
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, false);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor_);
zwlr_layer_surface_v1_set_size(layer_surface, width_, height_);
setMarginsAndZone(height_, width_);
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, this);
wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display);
}
void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
margins_ = {
@ -163,10 +96,170 @@ void waybar::Bar::setMarginsAndZone(uint32_t height, uint32_t width) {
auto gaps = config["margin"].asInt();
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
}
#ifdef HAVE_GTK_LAYER_SHELL
use_gls_ = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
if (use_gls_) {
initGtkLayerShell();
}
#endif
window.signal_realize().connect_notify(sigc::mem_fun(*this, &Bar::onRealize));
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
window.set_size_request(width_, height_);
setupWidgets();
if (window.get_realized()) {
onRealize();
}
window.show_all();
}
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
auto tmp_height = height_;
auto tmp_width = width_;
if (ev->height > static_cast<int>(height_)) {
// Default minimal value
if (height_ != 1) {
spdlog::warn(MIN_HEIGHT_MSG, height_, ev->height);
}
if (config["height"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Height");
} else {
tmp_height = ev->height;
}
}
if (ev->width > static_cast<int>(width_)) {
// Default minimal value
if (width_ != 1) {
spdlog::warn(MIN_WIDTH_MSG, width_, ev->width);
}
if (config["width"].isUInt()) {
spdlog::info(SIZE_DEFINED, "Width");
} else {
tmp_width = ev->width;
}
}
if (use_gls_) {
width_ = tmp_width;
height_ = tmp_height;
spdlog::debug("Set surface size {}x{} for output {}", width_, height_, output->name);
setExclusiveZone(tmp_width, tmp_height);
} else if (tmp_width != width_ || tmp_height != height_) {
setSurfaceSize(tmp_width, tmp_height);
}
}
#ifdef HAVE_GTK_LAYER_SHELL
void waybar::Bar::initGtkLayerShell() {
auto gtk_window = window.gobj();
// this has to be executed before GtkWindow.realize
gtk_layer_init_for_window(gtk_window);
gtk_layer_set_keyboard_interactivity(gtk_window, FALSE);
auto layer = config["layer"] == "top" ? GTK_LAYER_SHELL_LAYER_TOP : GTK_LAYER_SHELL_LAYER_BOTTOM;
gtk_layer_set_layer(gtk_window, layer);
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
gtk_layer_set_namespace(gtk_window, "waybar");
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_TOP, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP);
gtk_layer_set_anchor(
gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_TOP, margins_.top);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_BOTTOM, margins_.bottom);
if (width_ > 1 && height_ > 1) {
/* configure events are not emitted if the bar is using initial size */
setExclusiveZone(width_, height_);
}
}
#endif
void waybar::Bar::onRealize() {
auto gdk_window = window.get_window()->gobj();
gdk_wayland_window_set_use_custom_surface(gdk_window);
}
void waybar::Bar::onMap(GdkEventAny* ev) {
auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
if (use_gls_) {
return;
}
auto client = waybar::Client::inst();
// owned by output->monitor; no need to destroy
auto wl_output = gdk_wayland_monitor_get_wl_output(output->monitor->gobj());
auto layer =
config["layer"] == "top" ? ZWLR_LAYER_SHELL_V1_LAYER_TOP : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
layer_surface_ = zwlr_layer_shell_v1_get_layer_surface(
client->layer_shell, surface, wl_output, layer, "waybar");
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface_, false);
zwlr_layer_surface_v1_set_anchor(layer_surface_, anchor_);
zwlr_layer_surface_v1_set_margin(
layer_surface, margins_.top, margins_.right, margins_.bottom, margins_.left);
auto zone = vertical ? width + margins_.right : height + margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
layer_surface_, margins_.top, margins_.right, margins_.bottom, margins_.left);
setSurfaceSize(width_, height_);
setExclusiveZone(width_, height_);
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layerSurfaceHandleConfigure,
.closed = layerSurfaceHandleClosed,
};
zwlr_layer_surface_v1_add_listener(layer_surface_, &layer_surface_listener, this);
wl_surface_commit(surface);
wl_display_roundtrip(client->wl_display);
}
void waybar::Bar::setExclusiveZone(uint32_t width, uint32_t height) {
auto zone = 0;
if (visible) {
// exclusive zone already includes margin for anchored edge,
// only opposite margin should be added
if (vertical) {
zone += width;
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) ? margins_.right : margins_.left;
} else {
zone += height;
zone += (anchor_ & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) ? margins_.bottom : margins_.top;
}
}
spdlog::debug("Set exclusive zone {} for output {}", zone, output->name);
#ifdef HAVE_GTK_LAYER_SHELL
if (use_gls_) {
gtk_layer_set_exclusive_zone(window.gobj(), zone);
} else
#endif
{
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface_, zone);
}
}
void waybar::Bar::setSurfaceSize(uint32_t width, uint32_t height) {
/* If the client is anchored to two opposite edges, layer_surface.configure will return
* size without margins for the axis.
* layer_surface.set_size, however, expects size with margins for the anchored axis.
* This is not specified by wlr-layer-shell and based on actual behavior of sway.
*/
if (vertical && height > 1) {
height += margins_.top + margins_.bottom;
}
if (!vertical && width > 1) {
width += margins_.right + margins_.left;
}
spdlog::debug("Set surface size {}x{} for output {}", width, height, output->name);
zwlr_layer_surface_v1_set_size(layer_surface_, width, height);
}
// Converting string to button code rn as to avoid doing it later
@ -238,8 +331,7 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
o->height_ = height;
o->window.set_size_request(o->width_, o->height_);
o->window.resize(o->width_, o->height_);
auto zone = o->vertical ? width + o->margins_.right : height + o->margins_.bottom;
zwlr_layer_surface_v1_set_exclusive_zone(o->layer_surface, zone);
o->setExclusiveZone(width, height);
spdlog::info(BAR_SIZE_MSG,
o->width_ == 1 ? "auto" : std::to_string(o->width_),
o->height_ == 1 ? "auto" : std::to_string(o->height_),
@ -251,9 +343,9 @@ void waybar::Bar::layerSurfaceHandleConfigure(void* data, struct zwlr_layer_surf
void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface_v1* /*surface*/) {
auto o = static_cast<waybar::Bar*>(data);
if (o->layer_surface) {
zwlr_layer_surface_v1_destroy(o->layer_surface);
o->layer_surface = nullptr;
if (o->layer_surface_) {
zwlr_layer_surface_v1_destroy(o->layer_surface_);
o->layer_surface_ = nullptr;
}
o->modules_left_.clear();
o->modules_center_.clear();
@ -262,13 +354,14 @@ void waybar::Bar::layerSurfaceHandleClosed(void* data, struct zwlr_layer_surface
auto waybar::Bar::toggle() -> void {
visible = !visible;
auto zone = visible ? height_ : 0;
if (!visible) {
window.get_style_context()->add_class("hidden");
window.set_opacity(0);
} else {
window.get_style_context()->remove_class("hidden");
window.set_opacity(1);
}
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, zone);
setExclusiveZone(width_, height_);
wl_surface_commit(surface);
}
@ -287,14 +380,11 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
modules_right_.emplace_back(module);
}
module->dp.connect([module, &name] {
// Fix https://github.com/Alexays/Waybar/issues/320, proper way?
Glib::signal_idle().connect_once([module, &name] {
try {
module->update();
} catch (const std::exception& e) {
spdlog::error("{}: {}", name.asString(), e.what());
}
});
try {
module->update();
} catch (const std::exception& e) {
spdlog::error("{}: {}", name.asString(), e.what());
}
});
} catch (const std::exception& e) {
spdlog::warn("module {}: {}", name.asString(), e.what());
@ -305,9 +395,9 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos) {
auto waybar::Bar::setupWidgets() -> void {
window.add(box_);
box_.pack_start(left_, true, true);
box_.pack_start(left_, false, false);
box_.set_center_widget(center_);
box_.pack_end(right_, true, true);
box_.pack_end(right_, false, false);
// Convert to button code for every module that is used.
setupAltFormatKeyForModuleList("modules-left");
@ -319,13 +409,13 @@ auto waybar::Bar::setupWidgets() -> void {
getModules(factory, "modules-center");
getModules(factory, "modules-right");
for (auto const& module : modules_left_) {
left_.pack_start(*module, false, true, 0);
left_.pack_start(*module, false, false);
}
for (auto const& module : modules_center_) {
center_.pack_start(*module, true, true, 0);
center_.pack_start(*module, false, false);
}
std::reverse(modules_right_.begin(), modules_right_.end());
for (auto const& module : modules_right_) {
right_.pack_end(*module, false, false, 0);
right_.pack_end(*module, false, false);
}
}

View File

@ -1,4 +1,5 @@
#include "client.hpp"
#include <fmt/ostream.h>
#include <spdlog/spdlog.h>
#include <fstream>
#include <iostream>
@ -33,11 +34,6 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint
if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
client->layer_shell = static_cast<struct zwlr_layer_shell_v1 *>(
wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version));
} else if (strcmp(interface, wl_output_interface.name) == 0) {
auto wl_output = static_cast<struct wl_output *>(
wl_registry_bind(registry, name, &wl_output_interface, version));
client->outputs_.emplace_back(new struct waybar_output({wl_output, "", name, nullptr}));
client->handleOutput(client->outputs_.back());
} else 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(
@ -50,85 +46,51 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint
void waybar::Client::handleGlobalRemove(void * data, struct wl_registry * /*registry*/,
uint32_t name) {
auto client = static_cast<Client *>(data);
for (auto it = client->bars.begin(); it != client->bars.end();) {
if ((*it)->output->wl_name == name) {
auto output_name = (*it)->output->name;
(*it)->window.close();
it = client->bars.erase(it);
spdlog::info("Bar removed from output: {}", output_name);
} else {
++it;
}
}
auto it = std::find_if(client->outputs_.begin(),
client->outputs_.end(),
[&name](const auto &output) { return output->wl_name == name; });
if (it != client->outputs_.end()) {
zxdg_output_v1_destroy((*it)->xdg_output);
wl_output_destroy((*it)->output);
client->outputs_.erase(it);
}
// Nothing here
}
void waybar::Client::handleOutput(std::unique_ptr<struct waybar_output> &output) {
void waybar::Client::handleOutput(struct waybar_output &output) {
static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = handleLogicalPosition,
.logical_size = handleLogicalSize,
.done = handleDone,
.name = handleName,
.description = handleDescription,
.logical_position = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
.logical_size = [](void *, struct zxdg_output_v1 *, int32_t, int32_t) {},
.done = [](void *, struct zxdg_output_v1 *) {},
.name = &handleOutputName,
.description = [](void *, struct zxdg_output_v1 *, const char *) {},
};
output->xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output->output);
zxdg_output_v1_add_listener(output->xdg_output, &xdgOutputListener, &output->wl_name);
// owned by output->monitor; no need to destroy
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);
}
void waybar::Client::handleLogicalPosition(void * /*data*/,
struct zxdg_output_v1 * /*zxdg_output_v1*/,
int32_t /*x*/, int32_t /*y*/) {
// Nothing here
}
void waybar::Client::handleLogicalSize(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/,
int32_t /*width*/, int32_t /*height*/) {
// Nothing here
}
void waybar::Client::handleDone(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/) {
// Nothing here
}
bool waybar::Client::isValidOutput(const Json::Value & config,
std::unique_ptr<struct waybar_output> &output) {
bool waybar::Client::isValidOutput(const Json::Value &config, struct waybar_output &output) {
bool found = true;
if (config["output"].isArray()) {
bool in_array = false;
for (auto const &output_conf : config["output"]) {
if (output_conf.isString() && output_conf.asString() == output->name) {
if (output_conf.isString() && output_conf.asString() == output.name) {
in_array = true;
break;
}
}
found = in_array;
}
if (config["output"].isString() && config["output"].asString() != output->name) {
if (config["output"].isString() && config["output"].asString() != output.name) {
found = false;
}
return found;
}
std::unique_ptr<struct waybar::waybar_output> &waybar::Client::getOutput(uint32_t wl_name) {
auto it = std::find_if(outputs_.begin(), outputs_.end(), [&wl_name](const auto &output) {
return output->wl_name == wl_name;
});
struct waybar::waybar_output &waybar::Client::getOutput(void *addr) {
auto it = std::find_if(
outputs_.begin(), outputs_.end(), [&addr](const auto &output) { return &output == addr; });
if (it == outputs_.end()) {
throw std::runtime_error("Unable to find valid output");
}
return *it;
}
std::vector<Json::Value> waybar::Client::getOutputConfigs(
std::unique_ptr<struct waybar_output> &output) {
std::vector<Json::Value> waybar::Client::getOutputConfigs(struct waybar_output &output) {
std::vector<Json::Value> configs;
if (config_.isArray()) {
for (auto const &config : config_) {
@ -142,21 +104,23 @@ std::vector<Json::Value> waybar::Client::getOutputConfigs(
return configs;
}
void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_output*/,
const char *name) {
auto wl_name = *static_cast<uint32_t *>(data);
void waybar::Client::handleOutputName(void * data, struct zxdg_output_v1 * /*xdg_output*/,
const char *name) {
auto client = waybar::Client::inst();
try {
auto &output = client->getOutput(wl_name);
output->name = name;
auto &output = client->getOutput(data);
output.name = name;
spdlog::debug("Output detected: {} ({} {})",
name,
output.monitor->get_manufacturer(),
output.monitor->get_model());
auto configs = client->getOutputConfigs(output);
if (configs.empty()) {
wl_output_destroy(output->output);
zxdg_output_v1_destroy(output->xdg_output);
output.xdg_output.reset();
} else {
wl_display_roundtrip(client->wl_display);
for (const auto &config : configs) {
client->bars.emplace_back(std::make_unique<Bar>(output.get(), config));
client->bars.emplace_back(std::make_unique<Bar>(&output, config));
Glib::RefPtr<Gdk::Screen> screen = client->bars.back()->window.get_screen();
client->style_context_->add_provider_for_screen(
screen, client->css_provider_, GTK_STYLE_PROVIDER_PRIORITY_USER);
@ -167,9 +131,25 @@ void waybar::Client::handleName(void * data, struct zxdg_output_v1 * /*xdg_
}
}
void waybar::Client::handleDescription(void * /*data*/, struct zxdg_output_v1 * /*zxdg_output_v1*/,
const char * /*description*/) {
// Nothing here
void waybar::Client::handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor) {
auto &output = outputs_.emplace_back();
output.monitor = monitor;
handleOutput(output);
}
void waybar::Client::handleMonitorRemoved(Glib::RefPtr<Gdk::Monitor> monitor) {
spdlog::debug("Output removed: {} {}", monitor->get_manufacturer(), monitor->get_model());
for (auto it = bars.begin(); it != bars.end();) {
if ((*it)->output->monitor == monitor) {
auto output_name = (*it)->output->name;
(*it)->window.close();
it = bars.erase(it);
spdlog::info("Bar removed from output: {}", output_name);
} else {
++it;
}
}
outputs_.remove_if([&monitor](const auto &output) { return output.monitor == monitor; });
}
std::tuple<const std::string, const std::string> waybar::Client::getConfigs(
@ -228,6 +208,14 @@ void waybar::Client::bindInterfaces() {
if (layer_shell == nullptr || xdg_output_manager == nullptr) {
throw std::runtime_error("Failed to acquire required resources.");
}
// add existing outputs and subscribe to updates
for (auto i = 0; i < gdk_display->get_n_monitors(); ++i) {
auto monitor = gdk_display->get_monitor(i);
handleMonitorAdded(monitor);
}
gdk_display->signal_monitor_added().connect(sigc::mem_fun(*this, &Client::handleMonitorAdded));
gdk_display->signal_monitor_removed().connect(
sigc::mem_fun(*this, &Client::handleMonitorRemoved));
}
int waybar::Client::main(int argc, char *argv[]) {

View File

@ -2,7 +2,7 @@
waybar::Factory::Factory(const Bar& bar, const Json::Value& config) : bar_(bar), config_(config) {}
waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
try {
auto hash_pos = name.find('#');
auto ref = name.substr(0, hash_pos);
@ -35,6 +35,9 @@ waybar::IModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "clock") {
return new waybar::modules::Clock(id, config_[name]);
}
if (ref == "disk") {
return new waybar::modules::Disk(id, config_[name]);
}
#if defined(HAVE_DBUSMENU) && !defined(NO_FILESYSTEM)
if (ref == "tray") {
return new waybar::modules::SNI::Tray(id, bar_, config_[name]);

View File

@ -115,6 +115,16 @@ const std::tuple<uint8_t, float, std::string> waybar::modules::Battery::getInfos
time_remaining = -(float)(total_energy_full - total_energy) / total_power;
}
uint16_t capacity = total / batteries_.size();
// Handle full-at
if (config_["full-at"].isUInt()) {
auto full_at = config_["full-at"].asUInt();
if (full_at < 100) {
capacity = static_cast<float>(capacity / full_at) * 100;
if (capacity > full_at) {
capacity = full_at;
}
}
}
return {capacity, time_remaining, status};
} catch (const std::exception& e) {
spdlog::error("Battery: {}", e.what());
@ -130,7 +140,7 @@ const std::string waybar::modules::Battery::getAdapterStatus(uint8_t capacity) c
return "Full";
}
if (online) {
return "Charging";
return "Plugged";
}
return "Discharging";
}
@ -141,7 +151,11 @@ const std::string waybar::modules::Battery::formatTimeRemaining(float hoursRemai
hoursRemaining = std::fabs(hoursRemaining);
uint16_t full_hours = static_cast<uint16_t>(hoursRemaining);
uint16_t minutes = static_cast<uint16_t>(60 * (hoursRemaining - full_hours));
return std::to_string(full_hours) + " h " + std::to_string(minutes) + " min";
auto format = std::string("{H} h {M} min");
if (config_["format-time"].isString()) {
format = config_["format-time"].asString();
}
return fmt::format(format, fmt::arg("H", full_hours), fmt::arg("M", minutes));
}
auto waybar::modules::Battery::update() -> void {
@ -159,7 +173,12 @@ auto waybar::modules::Battery::update() -> void {
}
label_.set_tooltip_text(tooltip_text);
}
// Transform to lowercase
std::transform(status.begin(), status.end(), status.begin(), ::tolower);
// Replace space with dash
std::transform(status.begin(), status.end(), status.begin(), [](char ch) {
return ch == ' ' ? '-' : ch;
});
auto format = format_;
auto state = getState(capacity, true);
if (!old_status_.empty()) {

View File

@ -1,7 +1,28 @@
#include "modules/clock.hpp"
#include <sstream>
#include <type_traits>
#ifdef HAVE_LANGINFO_1STDAY
#include <langinfo.h>
#include <locale.h>
#endif
using waybar::modules::waybar_time;
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60) {
: ALabel(config, "clock", id, "{:%H:%M}", 60)
, fixed_time_zone_(false)
{
if (config_["timezone"].isString()) {
time_zone_ = date::locate_zone(config_["timezone"].asString());
fixed_time_zone_ = true;
}
if (config_["locale"].isString()) {
locale_ = std::locale(config_["locale"].asString());
} else {
locale_ = std::locale("");
}
thread_ = [this] {
dp.emit();
auto now = std::chrono::system_clock::now();
@ -12,18 +33,113 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
}
auto waybar::modules::Clock::update() -> void {
auto now = std::chrono::system_clock::now();
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
auto text = fmt::format(format_, localtime);
if (!fixed_time_zone_) {
// Time zone can change. Be sure to pick that.
time_zone_ = date::current_zone();
}
auto now = std::chrono::system_clock::now();
waybar_time wtime = {locale_,
date::make_zoned(time_zone_, date::floor<std::chrono::seconds>(now))};
auto text = fmt::format(format_, wtime);
label_.set_markup(text);
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
const auto calendar = calendar_text(wtime);
auto tooltip_format = config_["tooltip-format"].asString();
auto tooltip_text = fmt::format(tooltip_format, localtime);
label_.set_tooltip_text(tooltip_text);
auto tooltip_text = fmt::format(tooltip_format, wtime, fmt::arg("calendar", calendar));
label_.set_tooltip_markup(tooltip_text);
} else {
label_.set_tooltip_text(text);
label_.set_tooltip_markup(text);
}
}
}
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string {
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time());
const auto ymd = date::year_month_day(daypoint);
if (cached_calendar_ymd_ == ymd) {
return cached_calendar_text_;
}
const date::year_month ym(ymd.year(), ymd.month());
const auto curr_day = ymd.day();
std::stringstream os;
const auto first_dow = first_day_of_week();
weekdays_header(first_dow, os);
// First week prefixed with spaces if needed.
auto wd = date::weekday(ym/1);
auto empty_days = (wd - first_dow).count();
if (empty_days > 0) {
os << std::string(empty_days * 3 - 1, ' ');
}
auto last_day = (ym/date::literals::last).day();
for (auto d = date::day(1); d <= last_day; ++d, ++wd) {
if (wd != first_dow) {
os << ' ';
} else if (unsigned(d) != 1) {
os << '\n';
}
if (d == curr_day) {
os << "<b><u>" << date::format("%e", d) << "</u></b>";
} else {
os << date::format("%e", d);
}
}
auto result = os.str();
cached_calendar_ymd_ = ymd;
cached_calendar_text_ = result;
return result;
}
auto waybar::modules::Clock::weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void {
auto wd = first_dow;
do {
if (wd != first_dow) os << ' ';
Glib::ustring wd_ustring(date::format(locale_, "%a", wd));
auto wd_len = wd_ustring.length();
if (wd_len > 2) {
wd_ustring = wd_ustring.substr(0, 2);
wd_len = 2;
}
const std::string pad(2 - wd_len, ' ');
os << pad << wd_ustring;
} while (++wd != first_dow);
os << "\n";
}
#ifdef HAVE_LANGINFO_1STDAY
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
template <typename T, auto fn>
using deleting_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
#endif
// Computations done similarly to Linux cal utility.
auto waybar::modules::Clock::first_day_of_week() -> date::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)};
if (posix_locale) {
const int i = (std::intptr_t) nl_langinfo_l(_NL_TIME_WEEK_1STDAY, posix_locale.get());
auto ymd = date::year(i / 10000)/(i / 100 % 100)/(i % 100);
auto wd = date::weekday(ymd);
uint8_t j = *nl_langinfo_l(_NL_TIME_FIRST_WEEKDAY, posix_locale.get());
return wd + date::days(j - 1);
}
#endif
return date::Sunday;
}
template <>
struct fmt::formatter<waybar_time> : fmt::formatter<std::tm> {
template <typename FormatContext>
auto format(const waybar_time& t, FormatContext& ctx) {
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(tm_format), t.ztime));
}
};

View File

@ -49,19 +49,24 @@ void waybar::modules::Custom::continuousWorker() {
thread_ = [&] {
char* buff = nullptr;
size_t len = 0;
bool restart = false;
if (getline(&buff, &len, fp_) == -1) {
int exit_code = 1;
if (fp_) {
exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
fp_ = nullptr;
}
thread_.stop();
if (exit_code != 0) {
output_ = {exit_code, ""};
dp.emit();
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
}
return;
if (config_["restart-interval"].isUInt()) {
restart = true;
} else {
thread_.stop();
return;
}
}
std::string output = buff;
@ -71,6 +76,14 @@ void waybar::modules::Custom::continuousWorker() {
}
output_ = {0, output};
dp.emit();
if (restart) {
pid_ = -1;
fp_ = util::command::open(cmd, pid_);
if (!fp_) {
throw std::runtime_error("Unable to open " + cmd);
}
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
}
};
}

76
src/modules/disk.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "modules/disk.hpp"
using namespace waybar::util;
waybar::modules::Disk::Disk(const std::string& id, const Json::Value& config)
: ALabel(config, "disk", id, "{}%", 30)
, path_("/")
{
thread_ = [this] {
dp.emit();
thread_.sleep_for(interval_);
};
if (config["path"].isString()) {
path_ = config["path"].asString();
}
}
auto waybar::modules::Disk::update() -> void {
struct statvfs /* {
unsigned long f_bsize; // filesystem block size
unsigned long f_frsize; // fragment size
fsblkcnt_t f_blocks; // size of fs in f_frsize units
fsblkcnt_t f_bfree; // # free blocks
fsblkcnt_t f_bavail; // # free blocks for unprivileged users
fsfilcnt_t f_files; // # inodes
fsfilcnt_t f_ffree; // # free inodes
fsfilcnt_t f_favail; // # free inodes for unprivileged users
unsigned long f_fsid; // filesystem ID
unsigned long f_flag; // mount flags
unsigned long f_namemax; // maximum filename length
}; */ stats;
int err = statvfs(path_.c_str(), &stats);
/* Conky options
fs_bar - Bar that shows how much space is used
fs_free - Free space on a file system
fs_free_perc - Free percentage of space
fs_size - File system size
fs_used - File system used space
*/
if (err != 0) {
event_box_.hide();
return;
}
auto free = pow_format(stats.f_bavail * stats.f_bsize, "B", true);
auto used = pow_format((stats.f_blocks - stats.f_bavail) * stats.f_bsize, "B", true);
auto total = pow_format(stats.f_blocks * stats.f_bsize, "B", true);
label_.set_markup(fmt::format(format_
, stats.f_bavail * 100 / stats.f_blocks
, fmt::arg("free", free)
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
, fmt::arg("used", used)
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
, fmt::arg("total", total)
, fmt::arg("path", path_)
));
if (tooltipEnabled()) {
std::string tooltip_format = "{used} used out of {total} on {path} ({percentage_used}%)";
if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
label_.set_tooltip_text(fmt::format(tooltip_format
, stats.f_bavail * 100 / stats.f_blocks
, fmt::arg("free", free)
, fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks)
, fmt::arg("used", used)
, fmt::arg("percentage_used", (stats.f_blocks - stats.f_bavail) * 100 / stats.f_blocks)
, fmt::arg("total", total)
, fmt::arg("path", path_)
));
}
event_box_.show();
}

View File

@ -10,14 +10,28 @@ waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config
auto waybar::modules::Memory::update() -> void {
parseMeminfo();
if (memtotal_ > 0 && memfree_ >= 0) {
int used_ram_percentage = 100 * (memtotal_ - memfree_) / memtotal_;
auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2);
auto available_ram_gigabytes = memfree_ / std::pow(1024, 2);
unsigned long memtotal = meminfo_["MemTotal"];
unsigned long memfree;
if (meminfo_.count("MemAvailable")) {
// New kernels (3.4+) have an accurate available memory field.
memfree = meminfo_["MemAvailable"];
} else {
// Old kernel; give a best-effort approximation of available memory.
memfree = meminfo_["MemFree"] + meminfo_["Buffers"] + meminfo_["Cached"] +
meminfo_["SReclaimable"] - meminfo_["Shmem"];
}
if (memtotal > 0 && memfree >= 0) {
auto total_ram_gigabytes = memtotal / std::pow(1024, 2);
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
auto used_ram_gigabytes = (memtotal - memfree) / std::pow(1024, 2);
auto available_ram_gigabytes = memfree / std::pow(1024, 2);
getState(used_ram_percentage);
label_.set_markup(fmt::format(format_,
used_ram_percentage,
fmt::arg("total", total_ram_gigabytes),
fmt::arg("percentage", used_ram_percentage),
fmt::arg("used", used_ram_gigabytes),
fmt::arg("avail", available_ram_gigabytes)));
@ -31,7 +45,6 @@ auto waybar::modules::Memory::update() -> void {
}
void waybar::modules::Memory::parseMeminfo() {
int64_t memfree = -1, membuffer = -1, memcache = -1, memavail = -1;
std::ifstream info(data_dir_);
if (!info.is_open()) {
throw std::runtime_error("Can't open " + data_dir_);
@ -42,23 +55,9 @@ void waybar::modules::Memory::parseMeminfo() {
if (posDelim == std::string::npos) {
continue;
}
std::string name = line.substr(0, posDelim);
int64_t value = std::stol(line.substr(posDelim + 1));
if (name.compare("MemTotal") == 0) {
memtotal_ = value;
} else if (name.compare("MemAvailable") == 0) {
memavail = value;
} else if (name.compare("MemFree") == 0) {
memfree = value;
} else if (name.compare("Buffers") == 0) {
membuffer = value;
} else if (name.compare("Cached") == 0) {
memcache = value;
}
if (memtotal_ > 0 && (memavail >= 0 || (memfree > -1 && membuffer > -1 && memcache > -1))) {
break;
}
meminfo_[name] = value;
}
memfree_ = memavail >= 0 ? memavail : memfree + membuffer + memcache;
}

View File

@ -41,6 +41,10 @@ auto waybar::modules::MPD::update() -> void {
if (connection_ != nullptr) {
try {
bool wasPlaying = playing();
if(!wasPlaying) {
// Wait until the periodic_updater has stopped
std::lock_guard periodic_guard(periodic_lock_);
}
fetchState();
if (!wasPlaying && playing()) {
periodic_updater().detach();
@ -75,6 +79,7 @@ std::thread waybar::modules::MPD::event_listener() {
std::thread waybar::modules::MPD::periodic_updater() {
return std::thread([this] {
std::lock_guard guard(periodic_lock_);
while (connection_ != nullptr && playing()) {
dp.emit();
std::this_thread::sleep_for(std::chrono::seconds(1));
@ -135,7 +140,9 @@ void waybar::modules::MPD::setLabel() {
if (playing()) {
label_.get_style_context()->add_class("playing");
label_.get_style_context()->remove_class("paused");
} else {
} else if (paused()) {
format =
config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString();
label_.get_style_context()->add_class("paused");
label_.get_style_context()->remove_class("playing");
}
@ -297,7 +304,7 @@ void waybar::modules::MPD::waitForEvent() {
// Wait for a player (play/pause), option (random, shuffle, etc.), or playlist
// change
if (!mpd_send_idle_mask(
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST))) {
conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
checkErrors(conn);
return;
}
@ -306,6 +313,10 @@ void waybar::modules::MPD::waitForEvent() {
// See issue #277:
// https://github.com/Alexays/Waybar/issues/277
mpd_recv_idle(conn, /* disable_timeout = */ false);
// See issue #281:
// https://github.com/Alexays/Waybar/issues/281
std::lock_guard guard(connection_lock_);
checkErrors(conn);
mpd_response_finish(conn);
@ -337,3 +348,5 @@ bool waybar::modules::MPD::stopped() {
}
bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; }
bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; }

View File

@ -2,9 +2,14 @@
#include <spdlog/spdlog.h>
#include <sys/eventfd.h>
#include <fstream>
#include <cassert>
#include "util/format.hpp"
namespace {
using namespace waybar::util;
constexpr const char *NETSTAT_FILE =
"/proc/net/netstat"; // std::ifstream does not take std::string_view as param
constexpr std::string_view BANDWIDTH_CATEGORY = "IpExt";
@ -98,7 +103,7 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
createEventSocket();
createInfoSocket();
auto default_iface = getPreferredIface();
auto default_iface = getPreferredIface(-1, false);
if (default_iface != -1) {
ifid_ = default_iface;
char ifname[IF_NAMESIZE];
@ -201,8 +206,9 @@ void waybar::modules::Network::worker() {
}
thread_timer_.sleep_for(interval_);
};
std::array<struct epoll_event, EPOLL_MAX> events{};
thread_ = [this, &events] {
thread_ = [this] {
std::array<struct epoll_event, EPOLL_MAX> events{};
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
if (ec > 0) {
for (auto i = 0; i < ec; i++) {
@ -258,26 +264,6 @@ auto waybar::modules::Network::update() -> void {
}
getState(signal_strength_);
auto pow_format = [](unsigned long long value, const std::string &unit) {
if (value > 2000ull * 1000ull * 1000ull) { // > 2G
auto go = value / (1000 * 1000 * 1000);
return std::to_string(go) + "." +
std::to_string((value - go * 1000 * 1000 * 1000) / (100 * 1000 * 1000)) + "G" + unit;
} else if (value > 2000ull * 1000ull) { // > 2M
auto mo = value / (1000 * 1000);
return std::to_string(mo) + "." + std::to_string((value - mo * 1000 * 1000) / (100 * 1000)) +
"M" + unit;
} else if (value > 2000ull) { // > 2k
auto ko = value / 1000;
return std::to_string(ko) + "." + std::to_string((value - ko * 1000) / 100) + "k" + unit;
} else {
return std::to_string(value) + unit;
}
};
auto text = fmt::format(
format_,
fmt::arg("essid", essid_),
@ -293,8 +279,13 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")));
if (text != label_.get_label()) {
if (text.compare(label_.get_label()) != 0) {
label_.set_markup(text);
if (text.empty()) {
event_box_.hide();
} else {
event_box_.show();
}
}
if (tooltipEnabled()) {
if (tooltip_format.empty() && config_["tooltip-format"].isString()) {
@ -449,7 +440,6 @@ out:
}
void waybar::modules::Network::getInterfaceAddress() {
unsigned int cidrRaw;
struct ifaddrs *ifaddr, *ifa;
cidr_ = 0;
int success = getifaddrs(&ifaddr);
@ -461,18 +451,34 @@ void waybar::modules::Network::getInterfaceAddress() {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
ifa->ifa_name == ifname_) {
char ipaddr[INET6_ADDRSTRLEN];
ipaddr_ = inet_ntop(family_,
&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET6_ADDRSTRLEN);
char netmask[INET6_ADDRSTRLEN];
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN);
cidrRaw = net_addr->sin_addr.s_addr;
unsigned int cidr = 0;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
if (family_ == AF_INET) {
ipaddr_ = inet_ntop(AF_INET,
&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET_ADDRSTRLEN);
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(AF_INET, &net_addr->sin_addr, netmask, INET_ADDRSTRLEN);
unsigned int cidrRaw = net_addr->sin_addr.s_addr;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
} else {
ipaddr_ = inet_ntop(AF_INET6,
&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
ipaddr,
INET6_ADDRSTRLEN);
auto net_addr = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask);
netmask_ = inet_ntop(AF_INET6, &net_addr->sin6_addr, netmask, INET6_ADDRSTRLEN);
for (size_t i = 0; i < sizeof(net_addr->sin6_addr.s6_addr); ++i) {
unsigned char cidrRaw = net_addr->sin6_addr.s6_addr[i];
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
}
}
cidr_ = cidr;
break;
@ -530,7 +536,7 @@ bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::strin
return false;
}
int waybar::modules::Network::getPreferredIface(int skip_idx) const {
int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const {
int ifid = -1;
if (config_["interface"].isString()) {
ifid = if_nametoindex(config_["interface"].asCString());
@ -546,8 +552,7 @@ int waybar::modules::Network::getPreferredIface(int skip_idx) const {
ifa = ifaddr;
ifid = -1;
while (ifa != nullptr) {
if (ifa->ifa_addr->sa_family == family_ &&
wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
ifid = if_nametoindex(ifa->ifa_name);
break;
}
@ -563,7 +568,9 @@ int waybar::modules::Network::getPreferredIface(int skip_idx) const {
if (ifid > 0) {
return ifid;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
if (wait) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
return -1;
}
@ -717,8 +724,9 @@ void waybar::modules::Network::parseSignal(struct nlattr **bss) {
// WiFi-hardware usually operates in the range -90 to -20dBm.
const int hardwareMax = -20;
const int hardwareMin = -90;
signal_strength_ =
((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
const int strength =
((signal_strength_dbm_ - hardwareMin) / double{hardwareMax - hardwareMin}) * 100;
signal_strength_ = std::clamp(strength, 0, 100);
}
if (bss[NL80211_BSS_SIGNAL_UNSPEC] != nullptr) {
signal_strength_ = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);

View File

@ -5,7 +5,6 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
mainloop_(nullptr),
mainloop_api_(nullptr),
context_(nullptr),
scrolling_(false),
sink_idx_(0),
volume_(0),
muted_(false),
@ -22,7 +21,7 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
if (context_ == nullptr) {
throw std::runtime_error("pa_context_new() failed.");
}
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr) < 0) {
if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
auto err =
fmt::format("pa_context_connect() failed: {}", pa_strerror(pa_context_errno(context_)));
throw std::runtime_error(err);
@ -32,13 +31,8 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string &id, const Json::Value
throw std::runtime_error("pa_mainloop_run() failed.");
}
pa_threaded_mainloop_unlock(mainloop_);
// define the pulse scroll events only when no user provided
// events are configured
if (!config["on-scroll-up"].isString() && !config["on-scroll-down"].isString()) {
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleVolume));
}
event_box_.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
event_box_.signal_scroll_event().connect(sigc::mem_fun(*this, &Pulseaudio::handleScroll));
}
waybar::modules::Pulseaudio::~Pulseaudio() {
@ -58,7 +52,8 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
pa_context_set_subscribe_callback(c, subscribeCb, data);
pa_context_subscribe(
c,
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
static_cast<enum pa_subscription_mask>(static_cast<int>(PA_SUBSCRIPTION_MASK_SERVER) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SINK) |
static_cast<int>(PA_SUBSCRIPTION_MASK_SOURCE)),
nullptr,
nullptr);
@ -74,50 +69,33 @@ void waybar::modules::Pulseaudio::contextStateCb(pa_context *c, void *data) {
}
}
bool waybar::modules::Pulseaudio::handleVolume(GdkEventScroll *e) {
// Avoid concurrent scroll event
if (scrolling_) {
return false;
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll *e) {
// change the pulse volume only when no user provided
// events are configured
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
return AModule::handleScroll(e);
}
bool direction_up = false;
double volume_tick = (double)PA_VOLUME_NORM / 100;
auto dir = AModule::getScrollDir(e);
if (dir == SCROLL_DIR::NONE) {
return true;
}
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
pa_volume_t change = volume_tick;
pa_cvolume pa_volume = pa_volume_;
scrolling_ = true;
if (e->direction == GDK_SCROLL_UP) {
direction_up = true;
}
if (e->direction == GDK_SCROLL_DOWN) {
direction_up = false;
}
if (e->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x, &delta_y);
if (delta_y < 0) {
direction_up = true;
} else if (delta_y > 0) {
direction_up = false;
}
}
// isDouble returns true for integers as well, just in case
if (config_["scroll-step"].isDouble()) {
change = round(config_["scroll-step"].asDouble() * volume_tick);
}
if (direction_up) {
if (volume_ + 1 < 100) {
if (dir == SCROLL_DIR::UP) {
if (volume_ + 1 <= 100) {
pa_cvolume_inc(&pa_volume, change);
}
} else {
} else if (dir == SCROLL_DIR::DOWN) {
if (volume_ - 1 >= 0) {
pa_cvolume_dec(&pa_volume, change);
}
}
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
return true;
}
@ -132,7 +110,9 @@ void waybar::modules::Pulseaudio::subscribeCb(pa_context * conte
if (operation != PA_SUBSCRIPTION_EVENT_CHANGE) {
return;
}
if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
if (facility == PA_SUBSCRIPTION_EVENT_SERVER) {
pa_context_get_server_info(context, serverInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
pa_context_get_sink_info_by_index(context, idx, sinkInfoCb, data);
} else if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) {
pa_context_get_source_info_by_index(context, idx, sourceInfoCb, data);
@ -154,15 +134,15 @@ void waybar::modules::Pulseaudio::volumeModifyCb(pa_context *c, int success, voi
*/
void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const pa_source_info *i,
int /*eol*/, void *data) {
if (i != nullptr) {
auto self = static_cast<waybar::modules::Pulseaudio *>(data);
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
if (i != nullptr && pa->default_source_name_ == i->name) {
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
self->source_volume_ = std::round(source_volume * 100.0F);
self->source_idx_ = i->index;
self->source_muted_ = i->mute != 0;
self->source_desc_ = i->description;
self->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
self->dp.emit();
pa->source_volume_ = std::round(source_volume * 100.0F);
pa->source_idx_ = i->index;
pa->source_muted_ = i->mute != 0;
pa->source_desc_ = i->description;
pa->source_port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
pa->dp.emit();
}
}
@ -170,9 +150,9 @@ void waybar::modules::Pulseaudio::sourceInfoCb(pa_context * /*context*/, const p
* Called when the requested sink information is ready.
*/
void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_sink_info *i,
int /*eol*/, void * data) {
if (i != nullptr) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
int /*eol*/, void *data) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
if (i != nullptr && pa->default_sink_name_ == i->name) {
pa->pa_volume_ = i->volume;
float volume = static_cast<float>(pa_cvolume_avg(&(pa->pa_volume_))) / float{PA_VOLUME_NORM};
pa->sink_idx_ = i->index;
@ -181,6 +161,9 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
pa->desc_ = i->description;
pa->monitor_ = i->monitor_source_name;
pa->port_name_ = i->active_port != nullptr ? i->active_port->name : "Unknown";
if (auto ff = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_FORM_FACTOR)) {
pa->form_factor_ = ff;
}
pa->dp.emit();
}
}
@ -191,16 +174,20 @@ void waybar::modules::Pulseaudio::sinkInfoCb(pa_context * /*context*/, const pa_
*/
void waybar::modules::Pulseaudio::serverInfoCb(pa_context *context, const pa_server_info *i,
void *data) {
auto pa = static_cast<waybar::modules::Pulseaudio *>(data);
pa->default_sink_name_ = i->default_sink_name;
pa->default_source_name_ = i->default_source_name;
pa_context_get_sink_info_by_name(context, i->default_sink_name, sinkInfoCb, data);
pa_context_get_source_info_by_name(context, i->default_source_name, sourceInfoCb, data);
}
static const std::array<std::string, 9> ports = {
"headphones",
"headphone",
"speaker",
"hdmi",
"headset",
"handsfree",
"hands-free",
"portable",
"car",
"hifi",
@ -208,7 +195,7 @@ static const std::array<std::string, 9> ports = {
};
const std::string waybar::modules::Pulseaudio::getPortIcon() const {
std::string nameLC = port_name_;
std::string nameLC = port_name_ + form_factor_;
std::transform(nameLC.begin(), nameLC.end(), nameLC.begin(), ::tolower);
for (auto const &port : ports) {
if (nameLC.find(port) != std::string::npos) {
@ -220,18 +207,26 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const {
auto waybar::modules::Pulseaudio::update() -> void {
auto format = format_;
if (muted_) {
format = config_["format-muted"].isString() ? config_["format-muted"].asString() : format;
label_.get_style_context()->add_class("muted");
} else {
label_.get_style_context()->remove_class("muted");
if (!alt_) {
std::string format_name = "format";
if (monitor_.find("a2dp_sink") != std::string::npos) {
format =
config_["format-bluetooth"].isString() ? config_["format-bluetooth"].asString() : format;
format_name = format_name + "-bluetooth";
label_.get_style_context()->add_class("bluetooth");
} else {
label_.get_style_context()->remove_class("bluetooth");
}
if (muted_) {
// Check muted bluetooth format exist, otherwise fallback to default muted format
if (format_name != "format" && !config_[format_name + "-muted"].isString()) {
format_name = "format";
}
format_name = format_name + "-muted";
label_.get_style_context()->add_class("muted");
} else {
label_.get_style_context()->remove_class("muted");
}
format =
config_[format_name].isString() ? config_[format_name].asString() : format;
}
// TODO: find a better way to split source/sink
std::string format_source = "{volume}%";
@ -242,6 +237,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
}
format_source = fmt::format(format_source, fmt::arg("volume", source_volume_));
label_.set_markup(fmt::format(format,
fmt::arg("desc", desc_),
fmt::arg("volume", volume_),
fmt::arg("format_source", format_source),
fmt::arg("icon", getIcon(volume_, getPortIcon()))));
@ -249,7 +245,4 @@ auto waybar::modules::Pulseaudio::update() -> void {
if (tooltipEnabled()) {
label_.set_tooltip_text(desc_);
}
if (scrolling_) {
scrolling_ = false;
}
}

View File

@ -1,6 +1,6 @@
#include "modules/sni/host.hpp"
#include <spdlog/spdlog.h>
#include <fmt/ostream.h>
#include <spdlog/spdlog.h>
namespace waybar::modules::SNI {
@ -130,7 +130,8 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
}
void Host::addRegisteredItem(std::string service) {
auto [bus_name, object_path] = getBusNameAndObjectPath(service);
std::string bus_name, object_path;
std::tie(bus_name, object_path) = getBusNameAndObjectPath(service);
auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) {
return bus_name == item->bus_name && object_path == item->object_path;
});

View File

@ -128,6 +128,7 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
}
} else if (name == "Menu") {
menu = get_variant<std::string>(value);
makeMenu();
} else if (name == "ItemIsMenu") {
item_is_menu = get_variant<bool>(value);
}
@ -264,7 +265,11 @@ void Item::updateImage() {
if (pixbuf->gobj() != nullptr) {
// An icon specified by path and filename may be the wrong size for
// the tray
pixbuf = pixbuf->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR);
// Keep the aspect ratio and scale to make the height equal to icon_size
// If people have non square icons, assume they want it to grow in width not height
int width = icon_size * pixbuf->get_width() / pixbuf->get_height();
pixbuf = pixbuf->scale_simple(width, icon_size, Gdk::InterpType::INTERP_BILINEAR);
image.set(pixbuf);
}
} else {
@ -319,7 +324,7 @@ void Item::onMenuDestroyed(Item* self, GObject* old_menu_pointer) {
}
}
void Item::makeMenu(GdkEventButton* const& ev) {
void Item::makeMenu() {
if (gtk_menu == nullptr && !menu.empty()) {
dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data());
if (dbus_menu != nullptr) {
@ -334,8 +339,8 @@ void Item::makeMenu(GdkEventButton* const& ev) {
bool Item::handleClick(GdkEventButton* const& ev) {
auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x), Glib::Variant<int>::create(ev->y)});
if ((ev->button == 1 && (item_is_menu || !menu.empty())) || ev->button == 3) {
makeMenu(ev);
if ((ev->button == 1 && item_is_menu) || ev->button == 3) {
makeMenu();
if (gtk_menu != nullptr) {
#if GTK_CHECK_VERSION(3, 22, 0)
gtk_menu->popup_at_pointer(reinterpret_cast<GdkEvent*>(ev));

View File

@ -4,15 +4,16 @@
namespace waybar::modules::SNI {
Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
: config_(config),
: AModule(config, "tray", id),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
watcher_(nb_hosts_),
watcher_(SNI::Watcher::getInstance()),
host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
box_.set_name("tray");
spdlog::warn(
"For a functionnal tray you must have libappindicator-* installed and export "
"For a functional tray you must have libappindicator-* installed and export "
"XDG_CURRENT_DESKTOP=Unity");
box_.set_name("tray");
event_box_.add(box_);
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
@ -41,6 +42,4 @@ auto Tray::update() -> void {
}
}
Tray::operator Gtk::Widget&() { return box_; }
} // namespace waybar::modules::SNI

View File

@ -3,14 +3,13 @@
using namespace waybar::modules::SNI;
Watcher::Watcher(std::size_t id)
Watcher::Watcher()
: bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION,
"org.kde.StatusNotifierWatcher",
sigc::mem_fun(*this, &Watcher::busAcquired),
Gio::DBus::SlotNameAcquired(), Gio::DBus::SlotNameLost(),
Gio::DBus::BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE)),
watcher_id_(id),
watcher_(sn_watcher_skeleton_new()) {}
Watcher::~Watcher() {
@ -23,6 +22,7 @@ Watcher::~Watcher() {
g_slist_free_full(items_, gfWatchFree);
items_ = nullptr;
}
Gio::DBus::unown_name(bus_name_id_);
auto iface = G_DBUS_INTERFACE_SKELETON(watcher_);
g_dbus_interface_skeleton_unexport(iface);
}
@ -34,7 +34,7 @@ void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib:
if (error != nullptr) {
// Don't print an error when a watcher is already present
if (error->code != 2) {
spdlog::error("Watcher {}: {}", watcher_id_, error->message);
spdlog::error("Watcher: {}", error->message);
}
g_error_free(error);
return;

View File

@ -10,19 +10,23 @@ Ipc::Ipc() {
}
Ipc::~Ipc() {
// To fail the IPC header
write(fd_, "close-sway-ipc", 14);
write(fd_event_, "close-sway-ipc", 14);
thread_.stop();
if (fd_ > 0) {
// To fail the IPC header
write(fd_, "close-sway-ipc", 14);
close(fd_);
fd_ = -1;
}
if (fd_event_ > 0) {
write(fd_event_, "close-sway-ipc", 14);
close(fd_event_);
fd_event_ = -1;
}
}
void Ipc::setWorker(std::function<void()>&& func) { thread_ = func; }
const std::string Ipc::getSocketPath() const {
const char* env = getenv("SWAYSOCK");
if (env != nullptr) {

View File

@ -3,19 +3,31 @@
namespace waybar::modules::sway {
Mode::Mode(const std::string& id, const Json::Value& config) : ALabel(config, "mode", id, "{}") {
Mode::Mode(const std::string& id, const Json::Value& config)
: ALabel(config, "mode", id, "{}", 0, true) {
ipc_.subscribe(R"(["mode"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent));
// Launch worker
worker();
ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Mode: {}", e.what());
}
});
dp.emit();
}
void Mode::onEvent(const struct Ipc::ipc_response& res) {
try {
auto payload = parser_.parse(res.payload);
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
if (payload["change"] != "default") {
mode_ = Glib::Markup::escape_text(payload["change"].asString());
if (payload["pango_markup"].asBool()) {
mode_ = payload["change"].asString();
} else {
mode_ = Glib::Markup::escape_text(payload["change"].asString());
}
} else {
mode_.clear();
}
@ -25,16 +37,6 @@ void Mode::onEvent(const struct Ipc::ipc_response& res) {
}
}
void Mode::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Mode: {}", e.what());
}
};
}
auto Mode::update() -> void {
if (mode_.empty()) {
event_box_.hide();

View File

@ -4,17 +4,20 @@
namespace waybar::modules::sway {
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: ALabel(config, "window", id, "{}"), bar_(bar), windowId_(-1) {
if (label_.get_max_width_chars() == -1) {
label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END);
}
: ALabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
ipc_.subscribe(R"(["window","workspace"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd));
// Get Initial focused window
getTree();
// Launch worker
worker();
ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
}
});
}
void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); }
@ -22,24 +25,15 @@ void Window::onEvent(const struct Ipc::ipc_response& res) { getTree(); }
void Window::onCmd(const struct Ipc::ipc_response& res) {
try {
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload);
auto payload = parser_.parse(res.payload);
auto output = payload["output"].isString() ? payload["output"].asString() : "";
std::tie(app_nb_, windowId_, window_, app_id_) = getFocusedNode(payload["nodes"], output);
dp.emit();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
}
}
void Window::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception& e) {
spdlog::error("Window: {}", e.what());
}
};
}
auto Window::update() -> void {
if (!old_app_id_.empty()) {
bar_.window.get_style_context()->remove_class(old_app_id_);
@ -69,20 +63,28 @@ auto Window::update() -> void {
}
std::tuple<std::size_t, int, std::string, std::string> Window::getFocusedNode(
const Json::Value& nodes) {
for (auto const& node : nodes["nodes"]) {
if (node["focused"].asBool() && node["type"] == "con") {
if ((!config_["all-outputs"].asBool() && nodes["output"] == bar_.output->name) ||
const Json::Value& nodes, std::string& output) {
for (auto const& node : nodes) {
if (node["output"].isString()) {
output = node["output"].asString();
}
if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) {
if ((!config_["all-outputs"].asBool() && output == bar_.output->name) ||
config_["all-outputs"].asBool()) {
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
: node["window_properties"]["instance"].asString();
return {nodes["nodes"].size(),
return {nodes.size(),
node["id"].asInt(),
Glib::Markup::escape_text(node["name"].asString()),
app_id};
}
}
auto [nb, id, name, app_id] = getFocusedNode(node);
auto [nb, id, name, app_id] = getFocusedNode(node["nodes"], output);
if (id > -1 && !name.empty()) {
return {nb, id, name, app_id};
}
// Search for floating node
std::tie(nb, id, name, app_id) = getFocusedNode(node["floating_nodes"], output);
if (id > -1 && !name.empty()) {
return {nb, id, name, app_id};
}

View File

@ -4,14 +4,14 @@
namespace waybar::modules::sway {
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
: bar_(bar),
config_(config),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
scrolling_(false) {
: AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()),
bar_(bar),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
event_box_.add(box_);
ipc_.subscribe(R"(["workspace"])");
ipc_.signal_event.connect(sigc::mem_fun(*this, &Workspaces::onEvent));
ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd));
@ -22,7 +22,13 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
// Launch worker
worker();
ipc_.setWorker([this] {
try {
ipc_.handleEvent();
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
});
}
void Workspaces::onEvent(const struct Ipc::ipc_response &res) {
@ -36,9 +42,9 @@ void Workspaces::onEvent(const struct Ipc::ipc_response &res) {
void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
if (res.type == IPC_GET_WORKSPACES) {
try {
auto payload = parser_.parse(res.payload);
if (payload.isArray()) {
{
std::lock_guard<std::mutex> lock(mutex_);
auto payload = parser_.parse(res.payload);
workspaces_.clear();
std::copy_if(payload.begin(),
payload.end(),
@ -49,9 +55,9 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
: true;
});
// adding persistant workspaces (as per the config file)
if (config_["persistant_workspaces"].isObject()) {
const Json::Value & p_workspaces = config_["persistant_workspaces"];
// adding persistent workspaces (as per the config file)
if (config_["persistent_workspaces"].isObject()) {
const Json::Value & p_workspaces = config_["persistent_workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) {
@ -88,32 +94,20 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
std::sort(workspaces_.begin(),
workspaces_.end(),
[](const Json::Value &lhs, const Json::Value &rhs) {
if (lhs["name"].isInt() && rhs["name"].isInt()) {
return lhs["name"].asInt() < rhs["name"].asInt();
}
return lhs["name"].asString() < rhs["name"].asString();
});
}
dp.emit();
}
dp.emit();
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
} else {
if (scrolling_) {
scrolling_ = false;
}
}
}
void Workspaces::worker() {
thread_ = [this] {
try {
ipc_.handleEvent();
} catch (const std::exception &e) {
spdlog::error("Workspaces: {}", e.what());
}
};
}
bool Workspaces::filterButtons() {
bool needReorder = false;
for (auto it = buttons_.begin(); it != buttons_.end();) {
@ -156,19 +150,20 @@ auto Workspaces::update() -> void {
button.get_style_context()->remove_class("urgent");
}
if ((*it)["target_output"].isString()) {
button.get_style_context()->add_class("persistant");
button.get_style_context()->add_class("persistent");
} else {
button.get_style_context()->remove_class("persistant");
button.get_style_context()->remove_class("persistent");
}
if (needReorder) {
box_.reorder_child(button, it - workspaces_.begin());
}
std::string output = getIcon((*it)["name"].asString(), *it);
std::string output = (*it)["name"].asString();
if (config_["format"].isString()) {
auto format = config_["format"].asString();
output = fmt::format(format,
fmt::arg("icon", output),
fmt::arg("name", trimWorkspaceName((*it)["name"].asString())),
fmt::arg("icon", getIcon(output, *it)),
fmt::arg("value", output),
fmt::arg("name", trimWorkspaceName(output)),
fmt::arg("index", (*it)["num"].asString()));
}
if (!config_["disable-markup"].asBool()) {
@ -181,8 +176,8 @@ auto Workspaces::update() -> void {
}
Gtk::Button &Workspaces::addButton(const Json::Value &node) {
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
auto &button = pair.first->second;
auto pair = buttons_.emplace(node["name"].asString(), node["name"].asString());
auto &&button = pair.first->second;
box_.pack_start(button, false, false, 0);
button.set_relief(Gtk::RELIEF_NONE);
button.signal_clicked().connect([this, node] {
@ -201,10 +196,6 @@ Gtk::Button &Workspaces::addButton(const Json::Value &node) {
spdlog::error("Workspaces: {}", e.what());
}
});
if (!config_["disable-scroll"].asBool()) {
button.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
button.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
return button;
}
@ -215,6 +206,8 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
if (config_["format-icons"][key].isString() && node[key].asBool()) {
return config_["format-icons"][key].asString();
}
} else if (config_["format_icons"]["persistent"].isString() && node["target_output"].isString()) {
return config_["format-icons"]["persistent"].asString();
} else if (config_["format-icons"][key].isString()) {
return config_["format-icons"][key].asString();
}
@ -223,45 +216,28 @@ std::string Workspaces::getIcon(const std::string &name, const Json::Value &node
}
bool Workspaces::handleScroll(GdkEventScroll *e) {
// Avoid concurrent scroll event
if (scrolling_) {
return false;
auto dir = AModule::getScrollDir(e);
if (dir == SCROLL_DIR::NONE) {
return true;
}
std::string name;
scrolling_ = true;
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto &workspace) {
return workspace["focused"].asBool();
});
if (it == workspaces_.end()) {
scrolling_ = false;
return false;
return true;
}
switch (e->direction) {
case GDK_SCROLL_DOWN:
case GDK_SCROLL_RIGHT:
name = getCycleWorkspace(it, false);
break;
case GDK_SCROLL_UP:
case GDK_SCROLL_LEFT:
name = getCycleWorkspace(it, true);
break;
case GDK_SCROLL_SMOOTH:
gdouble delta_x, delta_y;
gdk_event_get_scroll_deltas(reinterpret_cast<const GdkEvent *>(e), &delta_x, &delta_y);
if (delta_y < 0) {
name = getCycleWorkspace(it, true);
} else if (delta_y > 0) {
name = getCycleWorkspace(it, false);
}
break;
default:
break;
if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) {
name = getCycleWorkspace(it, false);
} else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
name = getCycleWorkspace(it, true);
} else {
return true;
}
if (name.empty() || name == (*it)["name"].asString()) {
scrolling_ = false;
return false;
if (name == (*it)["name"].asString()) {
return true;
}
}
try {
@ -311,6 +287,4 @@ void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) {
}
}
Workspaces::operator Gtk::Widget &() { return box_; }
} // namespace waybar::modules::sway

View File

@ -1,9 +1,12 @@
#include "modules/temperature.hpp"
#include <filesystem>
waybar::modules::Temperature::Temperature(const std::string& id, const Json::Value& config)
: ALabel(config, "temperature", id, "{temperatureC}°C", 10) {
if (config_["hwmon-path"].isString()) {
file_path_ = config_["hwmon-path"].asString();
} else if (config_["hwmon-path-abs"].isString() && config_["input-filename"].isString()) {
file_path_ = (*std::filesystem::directory_iterator(config_["hwmon-path-abs"].asString())).path().u8string() + "/" + config_["input-filename"].asString();
} else {
auto zone = config_["thermal-zone"].isInt() ? config_["thermal-zone"].asInt() : 0;
file_path_ = fmt::format("/sys/class/thermal/thermal_zone{}/temp", zone);

9
subprojects/date.wrap Normal file
View File

@ -0,0 +1,9 @@
[wrap-file]
source_url=https://github.com/HowardHinnant/date/archive/v2.4.1.tar.gz
source_filename=date-2.4.1.tar.gz
source_hash=98907d243397483bd7ad889bf6c66746db0d7d2a39cc9aacc041834c40b65b98
directory=date-2.4.1
patch_url = https://github.com/mesonbuild/hinnant-date/releases/download/2.4.1-1/hinnant-date.zip
patch_filename = hinnant-date-2.4.1-1-wrap.zip
patch_hash = 2061673a6f8e6d63c3a40df4da58fa2b3de2835fd9b3e74649e8279599f3a8f6

View File

@ -5,6 +5,6 @@ source_url = https://github.com/fmtlib/fmt/archive/5.3.0.tar.gz
source_filename = fmt-5.3.0.tar.gz
source_hash = defa24a9af4c622a7134076602070b45721a43c51598c8456ec6f2c4dbb51c89
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/5.3.0/1/get_zip
patch_url = https://github.com/mesonbuild/fmt/releases/download/5.3.0-1/fmt.zip
patch_filename = fmt-5.3.0-1-wrap.zip
patch_hash = 18f21a3b8833949c35d4ac88a7059577d5fa24b98786e4b1b2d3d81bb811440f

View File

@ -0,0 +1,5 @@
[wrap-file]
directory = gtk-layer-shell-0.1.0
source_filename = gtk-layer-shell-0.1.0.tar.gz
source_hash = f7569e27ae30b1a94c3ad6c955cf56240d6bc272b760d9d266ce2ccdb94a5cf0
source_url = https://github.com/wmww/gtk-layer-shell/archive/v0.1.0/gtk-layer-shell-0.1.0.tar.gz

View File

@ -5,6 +5,6 @@ source_url = https://github.com/gabime/spdlog/archive/v1.3.1.tar.gz
source_filename = v1.3.1.tar.gz
source_hash = 160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70
patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.3.1/1/get_zip
patch_url = https://github.com/mesonbuild/spdlog/releases/download/1.3.1-1/spdlog.zip
patch_filename = spdlog-1.3.1-1-wrap.zip
patch_hash = 715a0229781019b853d409cc0bf891ee4b9d3a17bec0cf87f4ad30b28bbecc87