Compare commits

...
798 Commits
Author SHA1 Message Date
Zander671 d30fa71ee0 Merge branch 'master' of https://github.com/Alexays/Waybar 2026-08-15 13:07:50 -07:00
Alexis Rouillard 09e69e0f48 Merge pull request #5232 from brian-brow/fix/hyprland-enable-bar-scroll-5229
fix(hyprland/workspaces): respect enable-bar-scroll config option
2026-08-14 23:34:07 +02:00
Alexis Rouillard 6c3bd01bae Merge pull request #5251 from daher13/fix/format-backlight-module
fix: fix module backlight format using clang
2026-08-13 14:24:41 +02:00
Alexis Rouillard f95987d5c1 Merge pull request #5252 from daher13/feat/clang-makefile-archlinux
feat: clang format options added on makefile and docker image for arc…
2026-08-13 14:24:27 +02:00
Guilherme Daher ca46b9321f feat: clang format options added on makefile and docker image for archlinux 2026-08-13 08:56:48 -03:00
Guilherme Daher ab08e22494 fix: fix module backlight format using clang 2026-08-13 08:50:04 -03:00
Alexis Rouillard 1039ed8ee2 Merge pull request #5248 from daher13/fix/refresh-on-changeid
fix(hyprland/workspaces): handle workspace ID changes
2026-08-13 10:17:16 +02:00
Alexis Rouillard 9ca345e32c Merge pull request #5249 from shivankgarg98/fix/privacy-timeout-use-after-free
privacy: disconnect pending timeouts on destruction
2026-08-13 09:35:07 +02:00
Guilherme Daher e92f280a94 fix: formatted with clang 2026-08-12 17:06:44 -03:00
Shivank Garg 38cc11c3b0 privacy: disconnect pending timeouts on destruction
The privacy module registers timeout callbacks in the GLib main context.
The GeoClue callback uses sigc::mem_fun() with the Privacy object, but the
connection remains active when the module is destroyed during a bar reload.

If the timeout runs after destruction, it dereferences the freed Privacy
object and crashes Waybar:

  #0 waybar::modules::privacy::Privacy::locationTimeout(bool)
  #4 g_main_context_iteration()
  #5 g_application_run()

Disconnect the GeoClue and visibility timeout connections in the Privacy
destructor so no callback can run after the module is gone.

The unpatched binary crashed after four SIGUSR2 reloads at 1.5-second
intervals. The patched binary survived ten reloads.

Fixes: eeb7bc702e ("Added GeoClue2 privacy item")
Assisted-by: Claude Opus + Cursor
2026-08-13 01:07:14 +05:30
Guilherme Daher 5ef2da41be fix(hyprland/workspaces): handle workspace ID changes 2026-08-12 16:09:48 -03:00
Alexis Rouillard 084d87401d Merge pull request #5230 from toadjaune/patch-1
Replace lone tab character in style.css with spaces for consistency
2026-08-03 23:07:00 +02:00
Alexis Rouillard 5e713c5619 Merge pull request #5209 from gitmpr/backlight-default-min-brightness
config: add min-brightness to backlight example
2026-08-03 23:05:54 +02:00
Alexis Rouillard 7ceb61a64d Merge pull request #5210 from gitmpr/fix/backlight-min-brightness-step-clamp
fix(backlight): clamp scroll step to min-brightness
2026-08-03 23:05:40 +02:00
Brian Brown d01f1f44ec fix(hyprland/workspaces): respect enable-bar-scroll config option
Removed hasScrollConfig boolean variable in Workspaces init function. It
was Or'd with barScroll boolean variable and overriding it when
enable-bar-scroll is declared false in waybar config. Setting any scroll
up or down events caused enable-bar-scroll to be overwritten.

Tested on Arch/Hyprland, removing hasScrollConfig and the or condition
restored expected behavior.

Fixes #5229
2026-07-31 22:27:44 -04:00
Arnaud Venturi e07ee87ab3 Replace lone tab character in style.css with spaces for consistency 2026-07-30 18:16:29 +02:00
Alexis Rouillard d44a27af10 Merge pull request #5219 from 10ne1/fix/sway-ipc-subscribe-race
fix(sway/ipc): don't mistake events for the subscribe reply
2026-07-30 10:11:18 +02:00
Alexis Rouillard d3fb8a621e Merge pull request #5216 from LukashonakV/enum-refactor
util/enum: make header-only and drop mandatory Hyprland dependency
2026-07-30 09:41:01 +02:00
Alexis Rouillard d7e0554b3c Merge pull request #5225 from JuustKiwi/fix-mpris-newlines
fix(mpris): sanitize metadata to prevent multiline UI expansion
2026-07-30 09:34:41 +02:00
Ibrahim Issa f9e2b1dea7 fix(mpris): sanitize metadata to prevent multiline UI expansion 2026-07-28 13:48:40 +02:00
Adrian RatiuandClaude Fable 5 6e0b50567e fix(sway/ipc): don't mistake events for the subscribe reply
Ipc::subscribe() assumes the next message on the event socket is the
reply to the IPC_SUBSCRIBE it just sent.

When a module subscribes more than once (sway/workspaces subscribes to
"workspace" and then to "window"), an event from the first subscription
can arrive before the reply to the second one. The payload check fails
and the thrown exception permanently disables the module for that bar.

This is easy to hit when bars are (re)created on output hotplug, since
sway emits a burst of workspace events at exactly that moment while
moving workspaces to the new output:

[warning] module sway/workspaces: Disabling module "sway/workspaces",
Unable to subscribe ipc event

Fix it by skipping over event messages (type high bit set) until the
subscribe reply is found, re-emitting them on signal_event so none are
lost.

Fixes: #5218
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
2026-07-24 19:22:32 +03:00
Viktar Lukashonak 2d87bd5ba9 Drop mandatory hyprland dependency 2026-07-23 11:58:35 +03:00
Alexis Rouillard 30610d3b68 Merge pull request #5212 from bjnobre/fix/dynamic-tooltip-updates
fix(tooltips): keep dynamic tooltips from resetting hover delay
2026-07-21 21:13:14 +02:00
Alexis Rouillard 7fae3d7d43 Merge pull request #5213 from LukashonakV/cavaBump
cava bump
2026-07-21 15:08:37 +02:00
Viktar Lukashonak 5bd3cd4805 cavaBump 2026-07-21 15:17:55 +03:00
Bene-Jázem B. Nobre d32e7fa86f fix(tooltips): keep dynamic tooltips from resetting hover delay
Cache ALabel tooltip markup and provide it through query-tooltip instead of repeatedly setting GtkWidget's tooltip-markup property. This prevents frequently updating modules from restarting GTK's display-wide tooltip timer.

Keep the active tooltip updated directly so dynamic tooltip contents continue refreshing while the pointer remains stationary.
2026-07-20 11:20:39 -04:00
Alexis Rouillard 456f78ecb1 Merge pull request #5208 from Mrpaoo/ref/rm-dpemit
refactor: rm direct dp.emit() call in constructor.
2026-07-19 19:15:51 +02:00
gitmpr 7409ff6976 backlight: clamp scroll step to min-brightness
When min-brightness is set, a scroll step that would cross it was not
clamped — only scrolling while already at or below min was blocked.
With scroll-step: 5 and min-brightness: 1, scrolling from 5% would
pass the guard (5 > 1) and set brightness to 0%.

Fix by computing the post-step value before applying it: if it would
fall below min-brightness, set exactly to min-brightness instead.
2026-07-18 20:41:40 +02:00
gitmpr 937aca0b72 config: add min-brightness to backlight example config 2026-07-18 18:38:50 +02:00
yubo 5b8c98729f refactor: rm direct dp.emit() call in constructor. 2026-07-18 21:14:08 +08:00
Alexis Rouillard 7f732f0553 Merge pull request #5201 from grishatop1/master
use nmcli implementation for calculating WiFi signal strength
2026-07-16 22:24:23 +02:00
grisha e60ffa4a9c clang format 2026-07-15 22:52:46 +02:00
grisha 73fdafb5d3 use nmcli implementation for calculating signal strength 2026-07-15 16:15:09 +02:00
Alexis Rouillard d4a4417210 Merge pull request #5199 from LukashonakV/cavaImpr_2
refactor(cava): fix thread-safety, resource leaks, and style violations
2026-07-15 15:23:43 +02:00
Viktar Lukashonak 50f636236a man(waybar-cava): refresh and polish documentation
Synchronize the cava module man page with the current implementation:

- Fix scdoc table syntax (correct header/body cell prefixes)
- Add missing Waybar-side options: format-icons, vertex_shader, fragment_shader
- Remove options not consumed by the module (data_format, raw_target)
- Clarify option scope: raw-only, GLSL-only, and cava-config-only settings
- Update descriptions to match backend behavior:
  - ascii_range is derived from format-icons length
  - bar_height is ignored by Waybar
  - bar_delimiter is used for raw output
- Polish grammar, tighten structure, and fix example filenames
- Refresh STYLE and FRONTENDS sections to match the actual source
2026-07-15 15:46:37 +03:00
Viktar Lukashonak 52a5f74cd2 docs: merge coding conventions into CONTRIBUTING.md
Consolidate the standalone `CONVENTION.md` into `CONTRIBUTING.md` so that
all contributor guidelines live in a single, discoverable file.

Added sections:
- Language, build system, and formatting rules (Google style, clang-format)
- Naming conventions (PascalCase types, snake_case_ members, lowerCamelCase methods)
- Include ordering and header self-sufficiency requirements
- Class/module design patterns (AModule hierarchy, SafeSignal threading)
- JSON configuration and string handling guidelines
- GTK/Glib and OpenGL patterns
- Platform portability and RAII conventions
- Thread-safety rules (GTK single-threaded, background thread marshalling)
- Unsafe patterns to avoid (C-style casts, unbounded buffer copies)
- Singleton lifetime guidance (store config by value, no dangling refs)docs: merge coding conventions into CONTRIBUTING.md
2026-07-14 09:29:06 +03:00
Viktar Lukashonak 5c979152de refactor(cava): fix thread-safety, resource leaks, and style violations
Comprehensive refactor of the cava module backend and frontends.

Style & naming
- Rename Cava -> CavaRaw; snake_case methods -> lowerCamelCase
- Replace NULL with nullptr; replace C-style casts with static_cast
- Add explicit standard-library includes (<memory>, <string>, <chrono>)
- Fix missing trailing underscores on member variables

Architecture
- Remove Gtk::GLArea multiple inheritance in CavaGLSL (composition)
- Return std::unique_ptr from factory; add doAction() to GLSL variant
- Encapsulate thread timing arithmetic in AdaptiveDelay struct

Thread safety & correctness
- Replace raw sigc::signal with SafeSignal for cross-thread marshalling
- Fix data race between loadConfig() and out_thread_ (recursive_mutex)
- Fix audio_raw shallow-copy use-after-free via deep-copy AudioRaw payload
- Make loadConfig() exception-safe with CavaConfigGuard RAII
- Fix blocking read_thread_ race on shutdown (condition_variable + timeout join)
- Fix isSilent() data race (acquire pthread mutex)
- Eliminate doUpdate() recursion (iteration instead)
- Guard audio_raw_clean() against uninitialized state
- Fix format-icons underflow and cava_config buffer overflow
- Store config by value to prevent dangling references on reload
- Cache frontend config and refresh on runtime changes
- Fix signed-char icon lookup bug on x86
- Prevent Json::Value mutation bloat via const-ref lookups
- Broaden exception catches in worker threads (std::exception)

Resource management & GL robustness
- Fix OpenGL resource leaks (persist VBO/IBO/VAO; explicit destructor cleanup)
- Fix shader error handling crashes (valid infoLog allocation)
- Cache uniform locations instead of querying per frame
- Fix gradient color uninitialized stack memory (zero-init + clamped count)
- Fix shader time uniform integer division bug (float arithmetic)
- Add explicit VAO bind in onRender()
- Handle runtime surface config changes independently of shader changes
- Clamp negative gradient_count before GL upload

Follow-up
- Singleton API split (inst() + configure()) intentionally deferred to a
  dedicated PR because it changes the public constructor contract.
2026-07-13 19:50:09 +03:00
Alexis Rouillard cf19c836d3 Merge pull request #5197 from VlkrS/openbsd
fix(idle_inhibitor.cpp): Fix build for systems without SIGRTMIN/SIGRTMAX
2026-07-12 09:30:12 +02:00
Volker Schlecht 795047ba8b idle_inhibitor.cpp: Fix build for systems without SIGRTMIN/SIGRTMAX
See https://github.com/Alexays/Waybar/pull/4992 for reference
2026-07-11 23:36:59 +02:00
Alexis Rouillard 98b2a563f3 Merge pull request #5187 from pajlada/fix/add-waybar-load-manpage
docs(man): add load
2026-07-06 17:22:02 +02:00
Rasmus Karlsson b5abbdc696 docs(man): add load 2026-07-06 16:56:17 +02:00
Alex 624623215d ci(smoke): keep the compositor alive between hardware cases
hardware.sh called smoke::stop between umockdev cases, but smoke::stop runs
`swaymsg exit` and tears down the shared compositor -- so only the first case
(backlight) had a display and the rest failed with "cannot open display".
Kill only waybar between cases; leave compositor teardown to the EXIT trap.
2026-07-06 02:08:06 +02:00
Alex eee2ab92fe style(bluetooth): clang-format the enumerate args 2026-07-05 23:12:38 +02:00
Alexis Rouillard b3e9545899 Merge pull request #5184 from Alexays/fix/smoke-state-hang
ci(smoke): fix state tier hang from bare wait on a lingering daemon
2026-07-05 23:12:19 +02:00
AlexandClaude Opus 4.8 4222d5a213 ci(smoke): fix state tier hang from bare wait on a lingering daemon
The state tier started actually running mpd in 74f0d33, which exposed a
deadlock in teardown: state.sh launches `mpd --no-daemon &` and never kills
it during the tier, and its cleanup() called smoke::stop *before* killing mpd.
smoke::stop ended in a bare `wait`, which reaps *every* background job of the
shell -- including the still-running mpd -- so it blocked until GitHub's 6h
job timeout. continue-on-error doesn't help: it catches failures, not hangs.

- lib.sh: smoke::stop now waits only on the PIDs it owns (waybar, compositor),
  so an unrelated daemon left running by a tier can't deadlock teardown.
- state.sh: cleanup() tears down mpd/pulseaudio before smoke::stop.
- smoke.yml: add timeout-minutes: 20 so a future hang fails fast instead of
  burning the default 6h runner budget.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 23:10:07 +02:00
Alex 6294ed2520 fix(hyprland/language): apply the CSS class on the main thread
onEvent() runs on the Hyprland IPC listener thread and mutated the label's
style context (add/remove class) directly, racing the GTK main thread's
drawing and corrupting the heap (double free / corrupted double-linked list).
Follow the Submap pattern: onEvent only stores the layout under the mutex and
emits the dispatcher; update() swaps the CSS class on the main thread, tracking
the previously applied class.

Fixes #4665
2026-07-05 23:06:37 +02:00
Alex 6c1a0fa185 fix(bluetooth): accept module-level placeholders in enumerate formats
tooltip-format-enumerate-connected[-battery] only received the device_* args,
so {status}, {num_connections} and {controller_*} threw fmt "argument not
found". Pass those args to the enumerate fmt::format as well.

Fixes #4384
2026-07-05 23:06:37 +02:00
Alex 6d5d5f0768 fix(cava): throw instead of exit() on a bad config
loadConfig() called exit(EXIT_FAILURE) when the cava config failed to load or
no input source was available, killing the whole bar. Throw std::runtime_error
instead: the factory/bar catch it at construction and disable only the cava
module. The read_thread_ also calls loadConfig() at runtime, so contain the
throw there too, logging instead of terminating.

Fixes #4456
2026-07-05 23:06:37 +02:00
Alex 699589b66a fix(river/tags): parse hide-vacant once, not in the wl callbacks
config_["hide-vacant"].asBool() was called from the river status listeners.
When the option is given as a string ("true") jsoncpp's asBool() throws
"Value is not convertible to bool", and that exception unwinding through
libwayland's C dispatch aborts the process. Parse it once in the constructor
into a bool member (accepting the string form) and read the cached value.

Fixes #4078
2026-07-05 23:06:37 +02:00
AlexandClaude Opus 4.8 a7b3a2c0db ci(smoke): fix state tier hang from bare wait on a lingering daemon
The state tier started actually running mpd in 74f0d33, which exposed a
deadlock in teardown: state.sh launches `mpd --no-daemon &` and never kills
it during the tier, and its cleanup() called smoke::stop *before* killing mpd.
smoke::stop ended in a bare `wait`, which reaps *every* background job of the
shell -- including the still-running mpd -- so it blocked until GitHub's 6h
job timeout. continue-on-error doesn't help: it catches failures, not hangs.

- lib.sh: smoke::stop now waits only on the PIDs it owns (waybar, compositor),
  so an unrelated daemon left running by a tier can't deadlock teardown.
- state.sh: cleanup() tears down mpd/pulseaudio before smoke::stop.
- smoke.yml: add timeout-minutes: 20 so a future hang fails fast instead of
  burning the default 6h runner budget.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 23:00:33 +02:00
Alex 74f0d3393a ci(smoke): fix the mpd / hardware / leak tiers so they actually run
The first run went green but the best-effort tiers silently didn't exercise
anything:

- state.sh: the mpd `audio_output { ... }` block was on one line, which mpd
  rejects ("Unknown tokens after '{'"), so mpd never started and the mpd
  sub-tier (the #5183 empty-queue repro) skipped. Split the block across lines.
  Also move teardown into a cleanup() with `|| true` so a dead-pid kill under
  `set -e` no longer makes the step exit 1.
- hardware.sh: umockdev-run's LD_PRELOAD lands ahead of the linked-in ASan
  runtime, so ASan aborted before main() ("ASan runtime does not come first")
  and waybar never started -- the backlight/slider (#5179) path wasn't tested.
  Set verify_asan_link_order=0 so the instrumented binary runs under the preload.
- leakcheck.sh: abort_on_error=1 turned LSan's exit report into a core dump;
  force abort_on_error=0 for this report-only tier.
- lib.sh: assert_clean now also flags "ASan runtime does not come first" so a
  future preload/link-order regression fails loudly instead of passing.
2026-07-05 22:39:35 +02:00
Alex 9a10949938 ci(smoke): cover teardown, state transitions and fake hardware
The smoke test only exercised steady-state rendering and killed waybar with
SIGTERM, so whole crash classes were invisible: exit-time use-after-free
(#5182), module state transitions (#5183) and hardware-backed modules (#5179).
Add tiers that hit those paths under ASan (+ _GLIBCXX_ASSERTIONS):

- lib.sh: assert_clean_exit (SIGINT teardown -> checks segfault/abort + ASan
  report emitted during destruction), output hotplug helpers, signal/reload,
  WAYBAR_WRAP hook, opt-in leak detection.
- lifecycle.sh (gating): clean exit, runtime output hotplug (Bar/module
  destroy), toggle/reload churn, fast-interval teardown race (#5182 class).
- fuzz.sh (gating): pathological custom-backend output (empty, nonzero exit,
  invalid JSON, huge, non-UTF8, empty format).
- coverage.sh (gating): every Factory module must be classified for smoke
  coverage; a new unlisted module fails the job (#5179 slipped through).
- modules.sh: also render the whole matrix inside a group; SIGINT teardown per
  module instead of SIGTERM.
- state.sh (best-effort): real mpd driven through an empty queue (#5183) +
  stop/clear; pulseaudio + slider on a null sink.
- hardware.sh (best-effort): backlight, backlight/slider (#5179) and battery
  via umockdev.
- leakcheck.sh (report-only): clean-exit run under LSan.
2026-07-05 22:21:46 +02:00
Alex 445e2aec1f fix(bar): avoid use-after-free segfault on exit
Members are destroyed in reverse declaration order, so modules_all_ (and
the modules it owns) are gone before the GtkWindow. Tearing down a mapped
window emits `unmap`, whose handler runs toggleSuspend() over the already
freed modules. Disconnect the map/unmap handlers in ~Bar first.

Fixes #5182
2026-07-05 22:03:28 +02:00
Alex 9d8a8356b5 fix(mpd): don't dereference a null song when the queue is cleared
mpd_run_current_song() returns NULL when there is no current song (e.g.
after `mpc clear`), leaving song_ null. setLabel() always evaluates the
fmt::format() tag arguments -- even for format-stopped -- so getTag() and
getFilename() would call mpd_song_get_tag()/mpd_song_get_uri() on a null
song and segfault. Guard both against a null song_.

Fixes #5183
2026-07-05 22:03:28 +02:00
Alexis Rouillard 6888243c50 Merge pull request #5180 from khaneliman/fix/taskbar-all-outputs
fix(wlr/taskbar): respect all-outputs
2026-07-05 18:32:05 +02:00
Austin Horstman ebbc7ea4dc fix(wlr/taskbar): re-show task when it no longer matches ignore-list
The un-ignore branch in hide_if_ignored() computed is_was_ignored
after the enclosing condition already required ignored_ to be false,
so it never ran, and a task whose app_id/title changed away from an
ignore-list entry stayed hidden forever.

Reset ignored_ and show the button again, subject to the same
all-outputs/output-membership check used everywhere else.
2026-07-05 10:23:58 -05:00
Austin Horstman cbad42bc9b fix(wlr/taskbar): stop forcing tasks onto every output's bar
hide_if_duplicate() unconditionally re-ran handle_output_enter() with
the bar's own wl_output for every non-squashed task, so any app_id or
title event made the task visible on all bars and "all-outputs": false
was effectively ignored. The un-squash path in handle_closed() showed
the replacement task unconditionally, with the same effect.

Track whether the toplevel is actually on the bar's output from the
protocol's output_enter/output_leave events, split the button
show/hide logic out of the protocol handlers, and gate every synthetic
re-show on all-outputs or the tracked output membership.

Fixes #5178
2026-07-05 10:23:06 -05:00
Alexis Rouillard bf59a21815 Merge pull request #5177 from Alexays/fix/recent-issue-bugs
fix: backlight I/O flooding (#5020) + network IP recovery after link flap (#5122)
2026-07-05 11:28:24 +02:00
Alex d518110e6f fix(network): recover IP address after a link flap / router reboot
The network module only populates the interface address from netlink
events (RTM_NEWADDR) or an explicit address dump. The interval timer
re-queries WiFi and bandwidth but never re-fetches the address, so the
module relies entirely on receiving the RTM_NEWADDR event.

Netlink multicast delivery is reliable unless the socket receive buffer
overflows, in which case the kernel drops notifications and reports
ENOBUFS. During a burst of link/address/route changes -- e.g. a router
reboot or a PPPoE redial -- this can drop the RTM_NEWADDR carrying the
interface's new IP (after the old one was removed by RTM_DELADDR). With
no overrun handling and no periodic resync, the address field stays
blank until Waybar is restarted (which re-dumps addresses).

Handle the overrun: when nl_recvmsgs_default reports ENOBUFS/NLE_NOMEM,
request a fresh link/address (and route, when auto-detecting) dump to
resynchronise, instead of silently continuing with lost state. Also
enlarge the event socket receive buffer to make overruns less likely in
the first place. The fix stays within the event thread, so it adds no
new locking or cross-thread socket access.

Fixes #5122.
2026-07-05 11:09:46 +02:00
Alex 5711b1f4ef fix(backlight): stop per-tick filesystem re-enumeration / I/O flooding
The backlight udev worker thread called enumerate_devices() on every
epoll_wait timeout, i.e. once per polling interval. enumerate_devices()
runs udev_enumerate_scan_devices(), which walks the entire
/sys/class/backlight and /sys/class/leds trees and opens/closes the
sysfs root and every device path. With no `interval` configured the
module polls on its default cadence, so this full re-scan ran
continuously even when brightness never changed, flooding the
filesystem (observed via fatrace as constant open/close of `/`).
Raising `interval` only lowered the cadence, which is why the reporter's
`interval: 10` workaround reduced the flood.

The full re-enumeration is redundant: the udev monitor already delivers
change/add/remove events for the backlight and leds subsystems. On the
timeout path, re-read only the sysfs attributes of the devices already
tracked (via udev_device_new_from_subsystem_sysname) instead of
re-scanning the whole tree. This keeps periodic refresh working for
firmware backlights such as acpi_video that may not emit udev change
events, while eliminating the tree-wide scan. Device discovery of
new/removed devices continues through the udev monitor.

Fixes #5020.
2026-07-05 11:09:46 +02:00
Alex 969855f99e ci(freebsd): provide a bzip2.pc stub for freetype2
Installing the bzip2 package does not ship a pkg-config file, so freetype2.pc
(a transitive requirement of gtkmm-3.0) still failed with 'Package bzip2 ...
not found'. bzip2 lives in the FreeBSD base system; write a minimal bzip2.pc
before meson setup so pkg-config can resolve it.
2026-07-05 10:46:58 +02:00
Alex d2493906df ci(freebsd): install bzip2 so freetype2's pkg-config resolves
The FreeBSD build broke on 'Package bzip2, required by freetype2, not found':
freetype2.pc lists bzip2 as a (private) requirement, but no bzip2.pc was
installed, so pkg-config could not generate cflags for gtkmm-3.0. Add bzip2
to the package list.
2026-07-05 10:43:57 +02:00
Alexis Rouillard 3868e06dee Merge pull request #5173 from Alexays/ci/smoke-plus
ci(smoke): sanitizer build + module/interaction/a11y/layout coverage
2026-07-05 10:39:29 +02:00
Alexis Rouillard 6fc23046f6 Merge pull request #5168 from Alexays/fix-2882
fix(wireplumber): reconnect when PipeWire/WirePlumber restarts (#2882)
2026-07-05 10:38:39 +02:00
Alexis Rouillard d8425b8fbb Merge pull request #5174 from Alexays/fix-5051
fix(image): don't crash at startup when the widget isn't realized yet (#5051)
2026-07-05 10:38:00 +02:00
Alexis Rouillard 61187b31c7 Merge pull request #5176 from Alexays/fix/review-lowsev
fix: resource-leak / correctness cleanups from the 0.15.0..master review (low severity)
2026-07-05 10:37:59 +02:00
Alexis Rouillard e92fe4b62a Merge pull request #5175 from Alexays/fix/post-0150-review
fix: batch of bugs found reviewing the 0.15.0..master diff
2026-07-05 10:35:13 +02:00
Alex a600fba538 fix(AGraph): unref transient GtkBuilder on all menu-build paths
The GtkBuilder created for menu construction was never unref'd on any
path (success or throw), leaking one builder per graph module with a
menu. Unref on each throw and at the end, and take an explicit ref on
menu_ so it survives dropping the builder (mirrors ALabel).
2026-07-05 10:27:28 +02:00
Alex 9a1cb6183a fix(ALabel): free g_strdup'd menu action string via closure notify
The per-menu-action string duplicated with g_strdup was never freed,
leaking one string per action on every menu build and reload. Use
g_signal_connect_data with (GClosureNotify)g_free so the copy is freed
when the closure is destroyed.
2026-07-05 10:27:28 +02:00
Alex e8e12cdfa1 fix(audio_backend): make sink-mapping selection order-independent
With a sink-mapping configured, sinkInfoCb could report the wrong sink's
volume depending on the order in which PulseAudio enumerated sinks during a
pa_context_get_sink_info_list sweep.

The mapping override was keyed on the mutable current_sink_name_ and ran
before the 'pick a running sink' fallback, which also mutated
current_sink_name_. If the default sink was running while the mapped target
was suspended, the fallback could reassign the selection to the default sink
after the mapping had already matched, so the reported sink depended on
enumeration order (and each sweep wrote the state twice, causing a flicker).

Resolve the target up front: key the mapping on the stable default_sink_name
and, when a mapping is in effect, treat the mapped target sink as the sole
definitive selection - every other sink is ignored and the running-sink
fallback is skipped. The default-sink + running-fallback behavior is
unchanged when no mapping applies. Verified in isolation across all sink
enumeration orders.
2026-07-05 10:27:28 +02:00
Alex b527376985 fix(audio_backend): release leaked pa_operation handles
Every pa_operation* returned by the PulseAudio context introspection,
subscribe and volume/mute calls was discarded without pa_operation_unref,
leaking one operation object per call. Over a long session the periodic
subscription events accumulate an unbounded number of these handles.

Capture each returned handle and unref it (guarded against NULL) at every
discard site. Callback behavior is unchanged; these calls already run under
the threaded-mainloop lock, where unref is safe.
2026-07-05 10:27:28 +02:00
Alex eb10a511f0 ci(smoke): sanitizer build + module/interaction/a11y/layout coverage
Turn the smoke test into a real runtime safety net. Waybar is now built with
AddressSanitizer and exercised end to end in a headless compositor:

- Tier 0: run everything under ASan; fail on ASan reports and Gtk/GLib
  criticals in the log (lib.sh assert_clean)
- Tier 1: per-module render matrix (modules.sh) — each headless-safe module
  rendered in isolation
- Tier 2: pointer interaction (interact.sh) — inject clicks via sway, assert
  on-click side effect and format-alt toggle; AT-SPI assertions (a11y.sh/.py)
  check module labels semantically instead of by pixels
- Tier 3: layout matrix (positions.sh) — top/bottom/left + HiDPI scale 2;
  second compositor run under labwc

Shared helpers extracted to lib.sh. Adds a workflow_dispatch `bless` input to
regenerate the golden reference in one click. Trigger push only on master to
avoid duplicate PR runs. AT-SPI and labwc steps are continue-on-error.
2026-07-05 10:25:49 +02:00
Alex 3f77c07875 fix(image): don't block the main loop with exec in the multiple-image path
MultipleImageStrategy::update() and handleClick() ran util::command::exec
(a blocking fork+exec+read) on the GTK main thread, so the whole bar froze
for the script's duration on every interval and on every click.

Move the exec into a new IStrategy::fetch() hook that the SleeperThread
worker runs before dp.emit(); update() now only parses the cached output
and draws on the main thread (mirroring how custom.cpp separates exec from
formatting). handleClick() uses forkExec() so clicks fire-and-forget instead
of blocking on the command's output. The entries and single-image paths are
unchanged.
2026-07-05 10:20:41 +02:00
Alex 9b093c53e9 fix(wireplumber): read gboolean into a gboolean, not a 1-byte bool (OOB write)
g_variant_lookup with the "b" format writes a gboolean (gint, 4 bytes),
but muted_ and source_muted_ are C++ bool members (1 byte). Passing their
addresses caused a 3-byte out-of-bounds write past the member (undefined
behavior). Read into a gboolean temporary and assign back to the bool,
preserving the prior value when "mute" is absent.
2026-07-05 10:19:51 +02:00
Alex fc6a567974 fix(systemd-failed-units): guard label/tooltip format against bad config
A malformed user format or tooltip-format (unknown {placeholder}) made
fmt::format throw fmt::format_error out of update(). Wrap the label and
tooltip format calls in try/catch that warn once and fall back to a safe
label / skip the tooltip instead of taking the module down.
2026-07-05 10:13:24 +02:00
Alex 34522b4ccd fix(clock): guard tooltip formatting against unsupported specifiers
Only the label vformat was wrapped in try/catch. An unsupported specifier
(e.g. %-I / %OI) in tooltip-format or the calendar format still threw out
of update() every tick via the calendar/tooltip vformat calls. Wrap the
tooltip-building section in try/catch that warns once and skips the
tooltip for that tick instead of letting the exception escape update().
2026-07-05 10:13:24 +02:00
Alex 88064137ca fix(mpris): clear stale GError in playerctld selection loop
When playerctl_player_new_from_name() fails for a candidate player, the
loop continued without clearing the GError. The stale non-NULL error then
leaked into the next GLib call (GLib-CRITICAL assertion) and made the
post-loop 'if (error) goto errorexit' fire even when a valid playing
player had been selected, blanking the whole module. Clear the error at
the discard point with g_clear_error().
2026-07-05 10:13:24 +02:00
Alex b17743ff6a network: treat negative/failed link speed as 0
/sys/class/net/<if>/speed reports -1 with no carrier. Reading it into a
uint32_t wrapped to 4294967295 (without setting failbit), so {linkSpeed}
showed an absurd value. Read into int64_t, check fail(), and treat negative
or failed reads as 0.
2026-07-05 10:13:24 +02:00
Alex c9012c4107 sway/window: escape window title in tooltip markup
setTooltipMarkup uses set_tooltip_markup without escaping. Raw window titles
routinely contain &, < and >, which break Pango markup parsing and the
tooltip. Escape the title with Glib::Markup::escape_text before passing it.
2026-07-05 10:13:24 +02:00
Alex 91ca603b04 keyboard_state: close fd when openDevice throws to fix fd leak
openDevice() throws without closing the fd if libevdev_new_from_fd fails.
In both update() and tryAddDevice() the outer catch only logged, so
closeFile(fd) was never reached and a descriptor leaked on every failing
tick. Guard openDevice with a try/catch that closes the fd before rethrowing.
2026-07-05 10:13:24 +02:00
Alex 8441d5e124 config: harden output-dimensions parsing against malformed values
The parser assumed each entry was '<dimension> <comparator> <value>'.
An entry with no space caused str.substr((size_t)-1) to throw out_of_range,
and a non-integer value made std::stoi throw invalid_argument, failing the
whole bar on that output. Validate spaces with find()!=npos and wrap stoi in
try/catch; log a warning and skip malformed entries instead of throwing.
2026-07-05 10:13:24 +02:00
Alex 892ab479ba group: disconnect pending reveal timeout in destructor to fix UAF
With reveal-delay set, handleMouseEnter arms a Glib::signal_timeout that
captures 'this'. sigc::connection's destructor does not remove the GLib
source, so a Group destroyed with a pending reveal timer would fire the
timeout on freed memory. Add a destructor that disconnects reveal_timeout_.
2026-07-05 10:13:24 +02:00
Alex 2f2479ca35 fix(mango): reconnect IPC event thread on disconnect
The IPC event thread had no reconnect: on POLLHUP/POLLERR/POLLNVAL or
read()==0/error it broke out of the loop and the thread exited
permanently, freezing every mango module with stale content until
Waybar was restarted. Wrap the connect + poll/read loop in a reconnect
loop with a bounded 2s backoff, re-establishing the socket and resuming
on disconnect, modeled on the niri backend.

Add an atomic running_ flag so the worker exits cleanly on teardown;
the destructor now sets it false before closing the socket so the
worker breaks out and joins, and leaves the final close to the
destructor to avoid a double close.
2026-07-05 10:13:24 +02:00
Alex 8db89cc289 fix(mango): use xkb brief for shortDescription in language module
Remove leftover unconditional assignment that clobbered the value
computed from rxkb_layout_get_brief() with short_name, which made
short_description always equal short_name and defeated
format-<shortDescription> / {shortDescription}.
2026-07-05 10:13:24 +02:00
Alex 66139e4440 fix(tray): stop reorderItems from re-adding items (iterator UAF + double add)
The item-ordering feature made Host::reorderItems() re-run the full
remove/add path over items_ via std::ranges::for_each(on_remove_/on_add_).
This caused two confirmed bugs:

BUG 1 (iterator invalidation / UAF): on_add_ (Tray::onAdd) calls
Host::checkIgnoreList, which erases from items_ while for_each is still
iterating items_, invalidating iterators/pointers. Triggered by a
non-empty ignore-list matching an item with >=2 items present.

BUG 2 (double add): reorderItems runs while an item's Id is resolved in
proxyReady, i.e. before setReady(). It added the not-yet-ready item
(re-parenting its event_box, pushing into Tray::items_, connecting
signal_show/hide), then setReady() -> itemReady -> onAdd added it again:
GTK 'widget already has a parent' critical, duplicate Item* and
signal handlers that accumulated unbounded.

Fixes:
- reorderItems() now only reorders already-added GTK box children via a
  dedicated on_reorder_ callback (Tray::reorderBox), never re-adding or
  removing. reorderBox stable-sorts items_ by order_ and repositions
  children with gtk_box_reorder_child (honouring reverse-direction).
- Tray::onAdd is idempotent (guards against an already-added item) and
  positions the new widget via reorderBox before the ignore-list check.
- signal_show/signal_hide connections are stored per item and
  disconnected in Tray::onRemove; onRemove is a no-op for items that were
  never added.
2026-07-05 10:13:24 +02:00
Alex 74cf45d530 fix(hyprland): detect Lua protocol without side-effecting dispatch
isLuaProtocol() probed the protocol by sending a real
"dispatch workspace __waybar_probe__". On Hyprland < 0.54 "workspace"
is a valid dispatcher, so the probe actually switched the user to a junk
workspace named __waybar_probe__ on the first workspace click/scroll.

Detect the protocol with the read-only "version" IPC query instead:
parse the numeric "version" field (falling back to the always-present
"tag" field) and treat Hyprland >= 0.54 as Lua. This has no side
effects. On any parse/query failure we log and fall back to the legacy
protocol, preserving prior behavior for older versions.
2026-07-05 10:13:24 +02:00
Alex 30dcd7a7ca fix(hyprland/workspaces): own debounce timer on main thread, fix UAF
The debounce timer added for flicker prevention was armed from the IPC
listener thread via Glib::signal_timeout().connect(), while its timeout
lambda and the m_updatePending flag ran on the GTK main thread — an
unsynchronized cross-thread data race on GLib timer/main-loop state.
Additionally ~Workspaces() never disconnected the timer, so a pending
timeout could fire on a freed 'this' (use-after-free).

Restore the pre-refactor threading model: onEvent now only mutates state
under m_mutex on the IPC thread and calls dp.emit() (Glib::Dispatcher is
thread-safe). The debounce timer is owned entirely by the main-thread
update() path, which arms/re-arms it on each dispatch and coalesces
bursts into a single refresh. ~Workspaces() disconnects the timer
(guarded) so none outlives the object. Debounce behavior is preserved.
2026-07-05 10:13:24 +02:00
Alex 4764a62afc fix(wwan): null members after unref and guard destructor to avoid double-free
The destructor unconditionally unref'd current_modem, manager and
connection, but the constructor can leave them NULL or already-unref'd:

- On the mm_manager_new_sync failure path the ctor unref'd connection
  without nulling it, so the dtor unref'd it a second time -> double-free.
- On the g_bus_get_sync failure path all three stay NULL, and in the
  common no-WWAN-hardware case current_modem is NULL, so the dtor ran
  g_object_unref(NULL) -> G_IS_OBJECT assertion criticals.

Use g_clear_object() in the failing ctor path (unref + null) and in the
destructor (NULL-safe unref + null). Teardown is now safe for every ctor
outcome (bus fail, MM fail, no modem, normal), and a normal run still
unrefs each owned ref exactly once.
2026-07-05 10:13:24 +02:00
Alex c16e7efa13 fix(niri): close the IPC socket fd once (ScopedFd owns it), not twice
IPC::send() wrapped the socket fd in a util::ScopedFd, which closes the
fd in its destructor. The input stream was created with close_fd=true,
so the stream also closed the same fd, resulting in a double-close. In
multithreaded Waybar another thread can open a new fd with the same
number between the two close() calls, which the second close() then
wrongly closes. Pass close_fd=false so ScopedFd is the sole owner and
the fd is closed exactly once. The streams are declared after socketfd,
so they flush and destruct while the fd is still open, then ScopedFd
closes it.
2026-07-05 10:13:24 +02:00
Alex 77734e9b02 custom-graph: fix worker crash, JSON UTF-8 validation, SIGRTMIN guard
Bring src/modules/custom_graph.cpp in line with the hardened custom.cpp:

- continuousWorker: on the restart path, an open() failure threw
  std::runtime_error out of the SleeperThread lambda, which escaped the
  thread and called std::terminate, killing all of Waybar. Log the error
  and stop the worker gracefully instead of throwing.
- parseOutputJson: validate/make_valid the text/alt/tooltip JSON string
  fields before they reach fmt markup / set_tooltip_markup. Invalid UTF-8
  from a script otherwise aborts the bar in g_utf8_* (parseOutputRaw
  already validated the same way).
- refresh: wrap the SIGRTMIN-based signal check in #ifdef SIGRTMIN so the
  module builds on platforms without SIGRTMIN (e.g. some BSDs).

Fixes a std::terminate crash on continuous-exec restart failure, an
invalid-UTF-8 bar abort via JSON output, and a build break on platforms
lacking SIGRTMIN.
2026-07-05 10:13:24 +02:00
Alex 48db4aa36e fix(bar): make disable-on-sleep DPMS suspend actually reach modules
toggleSuspend dynamic_cast<AModule*>-ed the children of the left/center/right
Gtk::Box. But modules are packed via AModule::operator Gtk::Widget&(), which
returns the member event_box_, so every box child is a Gtk::EventBox and the
cast is always null -- suspend()/resume() never ran, making disable-on-sleep a
silent no-op. Iterate modules_all_ (the real module pointers) instead.

Fixes disable-on-sleep DPMS suspend/resume never firing.
2026-07-05 10:13:24 +02:00
Alex afa6ab1fd8 fix(AModule): don't crash on click/scroll commands with literal braces
handleUserEvent ran the configured command through fmt::format(fmt::runtime(...))
to substitute {x}/{y}. Commands containing literal braces that aren't {x}/{y}
(e.g. `echo ${HOME}`, `awk '{print $1}'`, brace expansions) made libfmt throw
fmt::format_error. Uncaught inside a GTK signal handler this aborts the whole bar.

Only format when {x}/{y} is present and fall back to the raw command on failure.

Fixes bar abort/std::terminate on on-click/on-scroll commands containing braces.
2026-07-05 10:13:24 +02:00
Alex fc297a7df3 ci: only trigger on push to master to avoid duplicate PR runs
These workflows triggered on both push and pull_request with no branch
filter, so every push to a PR branch ran each workflow twice. Restrict the
push trigger to master; pull_request already covers PR branches.
2026-07-05 10:10:04 +02:00
Alex acc7060be6 fix(wireplumber): guard async callbacks by connection generation; wire scroll once
Addresses review on #5168:

- Generational aliasing (blocking): setupConnection()/onReconnectTimeout()
  rebuild wp_core_/om_/pending_plugins_ in place on the same self, but the
  async load/activate callbacks carried no generation, and isModuleAlive()
  only proves self still exists. If PipeWire dropped again while a previous
  connection's async chain was still in flight, a stale completion would run
  against the rebuilt connection (a stray --pending_plugins_, an out-of-order
  install_object_manager), re-creating #2882's stale/blank state. Each async
  call now carries an AsyncCall{self, generation}; connection_generation_ is
  bumped in setupConnection(), and every callback drops out when its
  generation no longer matches (checked after isModuleAlive short-circuits).

- Duplicate scroll handlers: onMixerApiLoaded re-runs on every reconnect and
  connected a new scroll handler each time (dead but accumulating). Moved the
  one-time wiring to the constructor; handleScroll no-ops while mixer_api_ is
  null, so wiring it before the first connect is safe.
2026-07-05 09:53:11 +02:00
Alex 0729823c13 fix(image): don't crash at startup when the widget isn't realized yet
The HiDPI code path builds a cairo surface from the pixbuf via
Gdk::Cairo::create_surface_from_pixbuf(pixbuf, scale, image_.get_window()),
which requires a realized Gtk::Image. During startup an image module can
run its first update() before the widget is realized, so get_window()
returns a null Gdk::Window and that path aborts before anything is
logged. The more image modules are configured, the more likely at least
one updates before realization, which is why >2 images reliably kills
startup.

Guard on get_window(): only take the surface path when a window is
available, otherwise fall back to image_.set(pixbuf) (the pre-HiDPI
behavior). This keeps HiDPI crispness once realized and never crashes at
startup.

Fixes #5051.
2026-07-05 09:47:47 +02:00
Alexis Rouillard 93cabeefc2 Merge pull request #5172 from khaneliman/fix/label-markup-cache-pua-collation
fix(label): compare markup cache by raw bytes, not collation
2026-07-05 09:40:47 +02:00
Austin Horstman a51bddab9e fix(label): compare markup cache by raw bytes, not collation
2190871a (perf(label): skip redundant markup updates) caches the last
label/tooltip markup as Glib::ustring and skips set_markup() when the
new markup compares equal. Glib::ustring::operator== goes through
g_utf8_collate(), and under the UTF-8 locale GTK sets at startup,
Unicode private-use codepoints carry no collation weight. All nerd-font
icons live in the PUA, so two labels that differ only in their icon
glyph collate as equal and the visual update is silently dropped.

idle_inhibitor is the visible victim (#5169): clicking toggles the
state class (highlight changes) but the {icon} glyph never switches
between the activated/deactivated icons, while plain-text icons like
"YES"/"NO" work. Any ALabel module whose consecutive updates differ
only by a PUA glyph is affected. The module only started routing
through this cache when db4941ef migrated it onto the shared
setLabelMarkup()/setTooltipMarkup() helpers, which is why it broke in
the latest batch of refactors.

Store the cache as raw UTF-8 bytes (std::string via ustring::raw())
and compare those instead, so the skip only triggers on byte-identical
markup. Reproduced and verified under a nested niri session: before the
fix RTMIN+n toggled the state class but left the sleep glyph unchanged;
after it the glyph flips as expected, and byte-identical updates are
still skipped.
2026-07-05 01:18:53 -05:00
Alexis Rouillard 57c1ac789f Merge pull request #5170 from layus/feat/pow-format-modifiers 2026-07-05 08:03:48 +02:00
Alexis Rouillard c63ece5c83 Merge pull request #5171 from khaneliman/fix/niri-ipc-initial-connect-regression 2026-07-05 08:01:24 +02:00
Austin Horstman 64f70caa46 fix(niri): keep the initial IPC connect synchronous
#5158 (6672e924) moved connectToSocket() off the constructing thread and
into the detached IPC worker's own try/catch, so a missing NIRI_SOCKET no
longer throws out of IPC::IPC(). That was needed to fix #5117 (the worker
should reconnect instead of dying when an established stream drops), but
it also meant the very first connection attempt can never fail anymore.

Factory::makeModule()/Bar::getModules() rely on that constructor throwing
to disable a module it can't construct. With niri/workspaces and
niri/window always constructing successfully now, they get added to every
bar regardless of which compositor is actually running, showing up as a
permanently-empty widget next to the real workspace modules under
Hyprland/Sway.

Restore the old semantics for the first connection: connectToSocket() runs
synchronously in IPC::IPC() again, so a missing socket still throws and
the module gets disabled as before. Only a drop *after* that succeeds
falls into the retrying reconnect loop, preserving the #5117 fix.
2026-07-05 00:29:27 -05:00
Guillaume Maudoux a3dc55f9ee feat(format): expand pow_format modifiers
Extend the pow_format spec parser beyond alignment so configs can shape the
rendered number, not just pad it. A forced scale (#, k, M, G, T, P) pins the SI
prefix instead of auto-selecting it and, since the author then knows both scale
and unit, hides the prefix and unit by default; U brings the unit back, u hides
it in auto mode, and i forces integer display. b and B override the decimal or
binary base independently of the call site, and a trailing width now fixes the
coefficient field when a scale is forced, overflowing to '#' when it does not
fit.

The old '>' and '<' branches rendered via fmt::format("{}", s), which discarded
every new field on the recursive call; they now build from a single render path
that reads the current formatter, so modifiers survive alignment. The auto path
is byte-identical to before, verified against the previous implementation.

Adds a Catch2 suite covering each modifier alone and combined, plus the
backward-compatible alignment cases, and documents the modifiers in the network
and disk man pages.
2026-07-04 23:08:42 +02:00
Alexis Rouillard 2d9b95e9de Merge pull request #4915 from khaneliman/custom-gio 2026-07-04 18:48:03 +02:00
Austin Horstman 1b6173ddda fix(util): keep GLib child setup fork-safe
Move WAYBAR_OUTPUT_NAME injection into the parent-provided spawn environment and strip logging and setenv() out of the GLib child-setup hook.

That keeps the helper's post-fork path limited to the process setup it actually needs, which is a safer fit for sanitizer-heavy platforms such as FreeBSD.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-07-04 08:41:35 -05:00
Austin Horstman f987c24144 style(custom): apply format and tidy cleanups
Run clang-format on the changed C++ files and fix the clang-tidy findings introduced by the custom command migration.

The only codegen-relevant change here is switching the new res assignments in custom.cpp to designated initializers. The rest is formatting only.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-07-04 08:41:35 -05:00
Austin Horstman fc01c03a20 fix(custom): avoid blocking child reaps in interval worker
Stop the interval worker from waiting synchronously on every pid in pid_children_ before it refreshes the module.

Switching this reap pass to waitpid(..., WNOHANG) keeps the worker responsive when an older event-triggered child is still running, while still removing children that have already exited.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-07-04 08:41:35 -05:00
Austin Horstman 5f4b96ad1d refactor(custom): move continuous exec onto GLib command stream
Replace the custom module's continuous getline() worker with the new GLib-backed command stream helper.

This moves line delivery, child exit handling, and restart scheduling onto the main loop so continuous commands no longer depend on a blocking FILE* read inside SleeperThread.

The behavior is kept aligned with the old module semantics: stdout lines still emit updates, non-zero exits still surface as errors, and restart-interval still respawns the command.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-07-04 08:41:35 -05:00
Austin Horstman 560f02509b test(util): cover command stream line delivery and EOF flushing
Add focused coverage for the new GLib command stream helper.

These tests verify that complete lines are emitted as they arrive and that EOF flushes a final unterminated line without duplicating a newline-terminated one.

That behavior is the contract the custom module will rely on when its continuous command handling moves onto this helper.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-07-04 08:40:31 -05:00
Austin Horstman 2355486cb3 feat(util): add GLib command stream for nonblocking line reads
Add a small GLib-backed helper for command stdout that integrates with the main loop instead of blocking on getline() in a worker thread.

The helper keeps the existing child setup semantics used by Waybar commands, including process groups, parent-death signaling, and WAYBAR_OUTPUT_NAME propagation.

This is the foundation for moving long-running custom commands away from manual poll/read logic in the module itself.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-07-04 08:40:31 -05:00
Alexis Rouillard 1471cb26b8 Merge pull request #5167 from Alexays/fix/bug-batch
fix: backlight leds subsystem (#2848), sway/ipc reconnect (#3166), ethernet unplug regression (#4364)
2026-07-04 13:53:28 +02:00
Alex faf6a62bcf fix(network): detect ethernet cable unplug again (carrier/operstate)
PR #4190 (merged as 93d85a0) reworked getNetworkState() so the rfkill
"disabled" state is evaluated whenever the module has no carrier. Because
the module always watches an RFKILL_TYPE_WLAN switch, a wired ethernet
module whose cable is unplugged (carrier lost) would return "disabled"
instead of "disconnected" whenever the system's WLAN radio happened to be
rfkill-blocked. With no format-disabled configured, that state falls back
to plain "format", so the interface kept looking connected after unplug.

rfkill only concerns wireless radios, so only honor it when there is no
interface at all or the current interface is actually wireless (detected
via /sys/class/net/<if>/phy80211 or /wireless). A wired interface that
lost its carrier now correctly reports "disconnected", while wifi rfkill
display from #4190 is preserved.

Fixes #4364.
2026-07-04 13:43:01 +02:00
Alex b0b46ec039 fix(sway/ipc): reconnect on disconnect instead of breaking + CPU-spinning
When sway's event-subscription send buffer overflows during an event
flood, sway closes the client connection. The sway IPC event worker
(SleeperThread running handleEvent -> recv) then threw on every
iteration and the SleeperThread immediately re-invoked it, leaving the
sway modules broken while busy-looping on a dead socket and pegging a
CPU.

Mirror the niri backend's reconnect loop: on a read/EOF/parse error from
the event socket, close the old connection, back off for a couple of
seconds (so we don't busy-spin), re-open the socket and replay the same
subscriptions, then resume. A running_ flag set at the start of teardown
makes the worker bail out cleanly instead of reconnecting to a socket
that is being closed on purpose. The IPC message protocol and event
parsing are unchanged.

Fixes #3166.
2026-07-04 13:43:01 +02:00
Alex 8ff8ceeca2 fix(backlight): also match the leds subsystem for keyboard backlights
The backlight module only enumerated and monitored the udev "backlight"
subsystem, so keyboard-backlight LEDs in the "leds" class (e.g.
white:kbd_backlight, platform::kbd_backlight) were never discovered and
the module fell back to the default when pointed at one.

Enumerate and monitor the "leds" subsystem in addition to "backlight".
Those LEDs expose the same brightness/max_brightness attributes, so the
read path is unchanged. Each device now records its subsystem so the
login1 SetBrightness call targets the correct one. Automatic device
selection still prefers a "backlight" device and only falls back to a
"leds" device when named explicitly or when no screen backlight exists.

Fixes #2848.
2026-07-04 13:43:01 +02:00
Alexis Rouillard 4c55819888 Merge pull request #5166 from Alexays/ci/smoke-test
ci: headless smoke + screenshot test for the bar
2026-07-04 13:41:57 +02:00
Alex 0b88b4ef0c fix(wireplumber): reconnect when PipeWire/WirePlumber restarts
Previously the wireplumber module connected to PipeWire once in its
constructor and had no handling for the connection being lost. When
PipeWire or the wireplumber service restarted (or crashed), the module
went stale/blank and never recovered until Waybar itself was restarted.

Connect to the WpCore "disconnected" signal and, on disconnect, schedule
a bounded main-loop retry (Glib::signal_timeout) that tears down the now
invalid core/object-manager/mixer-api references and rebuilds the whole
connection from scratch, re-running the async API and object-manager
setup. Connection setup/teardown is factored into setupConnection() and
teardownConnection() so startup and reconnect share one code path.

The reconnect timer is cancelled in the destructor and the existing
isModuleAlive() registry guard still protects in-flight async callbacks,
so teardown during a pending reconnect stays safe.

Fixes #2882.
2026-07-04 13:41:29 +02:00
Alex 0c059db0fd ci(smoke): add reference screenshot to enable golden comparison
Blessed from the CI artifact of the passing headless run (Waybar / CI smoke
test / 12:34 on a pinned DejaVu Sans bar). Regenerate from a new artifact when
an intended visual change trips the comparison — see test/smoke/README.md.
2026-07-04 13:28:48 +02:00
Alex e8638e761e ci(smoke): make custom modules emit text so the bar is not empty
The static custom modules used `exec: echo` (empty output), so the modules
rendered nothing and the bar strip was blank. Have exec print the label text
and use the default `{}` format.
2026-07-04 13:22:08 +02:00
AlexandClaude Opus 4.8 cf2f120ad7 ci: add headless smoke + screenshot test for the bar
Launch the real Waybar binary inside a headless, software-rendered sway
compositor and verify it actually runs and renders — something the unit
tests and build jobs never do.

- test/smoke/run.sh: boots sway (WLR_BACKENDS=headless, pixman), starts
  waybar, asserts it stays alive with no fatal log, optionally grabs a
  screenshot with grim
- level 1: real modules (clock/cpu/memory/disk) load without crashing
- level 2: deterministic config is screenshotted and checked to be non-blank
- level 3: screenshot compared to test/smoke/reference.png (fuzz 8%,
  800px tolerance); screenshot + diff uploaded as artifacts
- .github/workflows/smoke.yml runs it on push/PR

The reference image must be blessed from a CI artifact (see test/smoke/README.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 13:15:05 +02:00
Alexis Rouillard d9f2a437d0 Merge pull request #5165 from Alexays/fix/crash-batch3
fix: mpris resume SIGSEGV (#5124), wireplumber async UAF (#3974), stale reload batch state (#4129)
2026-07-04 13:12:17 +02:00
AlexandClaude Opus 4.8 54db6642ce docs: revamp README and add contributor docs
- README: quick-links bar, compositor-support table, module list grouped by
  theme with links to the wiki, getting-started section, collapsible
  dependency blocks, updated badges (CI, release), and a note that module
  docs live in man/ (auto-synced to the wiki)
- add CONTRIBUTING.md (dev build, code style, docs workflow, PR checklist)
- add issue templates (bug report, feature request) and a PR template

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 13:06:47 +02:00
Alexis Rouillard 6d3656a4a8 Merge pull request #5164 from Alexays/fix/tray-taskbar-mpd
fix: tray accel-group crash (#5142), wlr/taskbar shell-exec of built-in actions (#3284), MPD connect-timeout freeze (#1186)
2026-07-04 13:06:44 +02:00
Alex c19abf373b fix(mpris): defer widget visibility to update() to stop resume SIGSEGV
The name-vanished playerctl callback called event_box_.set_visible(false)
directly. On resume from suspend this runs in a re-entrant / torn-down
state and crashes in Gtk::Widget::set_visible. Only clear the player and
dp.emit() from the callback; update() (on the main thread) computes
visibility from player state and hides the module when there is no player,
matching the other hardened handlers.

Fixes #5124.
2026-07-04 13:04:04 +02:00
Alex 8664d9a963 fix(wireplumber): guard async load callbacks against use-after-free on teardown
The WirePlumber module registers three async callbacks (onDefaultNodesApiLoaded,
onMixerApiLoaded, onPluginActivated) that receive a raw self pointer with a NULL
GCancellable. WirePlumber cannot withdraw an in-flight callback, so if the module
is destroyed before a queued callback fires (e.g. a temporary output/bar is removed
while a component load is still pending, or during an audio route transition), the
callback dereferences the freed self, causing heap corruption / a crash.

Guard each of these callbacks with isModuleAlive(), which checks the existing static
modules registry. The destructor already removes this from the registry before any
teardown, so a missing entry means self is dangling and the callback bails out
without touching it.

A GCancellable cannot fix this cleanly here: every callback dereferences self on its
first line, and wp_core_load_component completes via a WpTransition (not a GTask), so
the cancellable is not recoverable from the GAsyncResult either. The liveness check
must not touch self at all.

Fixes #3974.
2026-07-04 13:04:04 +02:00
Alex 268d859043 fix(reload): reset pending bar-batch state on rebind
On reload the GApplication is recreated but the default main context (and any
queued PRIORITY_HIGH_IDLE createBarsBatch source) survives. pending_outputs_
was left holding dangling waybar_output* into the just-cleared outputs_ list;
createBarsBatch filters by address, which can mis-match once a freed slot is
reused. Clear pending_outputs_ and reset bars_scheduled_ in bindInterfaces so
the next run batches from a clean state. Mitigates the dangling-pointer path of
 #4129 (the cross-process app-id race in #4117 is separate).
2026-07-04 13:04:02 +02:00
AlexandClaude Opus 4.8 5180dec43e ci(wiki): fix STYLE lists rendering as blockquotes
Some pandoc versions (incl. the pinned 3.5) render scdoc bullet lists as a
series of `> ·` blockquotes instead of a Markdown list, which made the STYLE
sections look broken. Post-process the generated Markdown to collapse that
artifact back into real `- item` bullets, independent of the pandoc version.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 12:53:20 +02:00
Alexis Rouillard 0895465430 Merge pull request #5163 from Alexays/fix-2215
fix(keyboard-state): read lock LEDs from all devices, not just the first
2026-07-04 12:53:02 +02:00
Alex cb968c9369 fix(mpd): bound the connect timeout so an unreachable server can't freeze the bar
The MPD state machine drives all connection attempts from Glib::signal_timeout
callbacks, which run on the GTK main loop. tryConnect() called
mpd_connection_new() with the user-facing timeout_ (up to 30s by default), so an
unreachable server blocked the whole bar for the full connect timeout.

Bound the connect attempt to a short fixed timeout (2000 ms) so a dead server
fails fast, then restore the configured timeout_ for subsequent command reads so
slow-but-alive servers are unaffected.

Fixes #1186.
2026-07-04 12:50:30 +02:00
Alex b7a6fa8c91 fix(wlr/taskbar): dispatch built-in click actions via doAction, not the shell
wlr/taskbar reads on-click* config values (close, minimize, maximize,
fullscreen, minimize-raise, activate) directly as internal actions in
Task::handle_clicked, but never adopted the eventActionMap_/doAction
mechanism. As a result AModule::handleUserEvent additionally forkExec-ed
the same value as a shell command, e.g. on-click-middle: "close" ran the
action and then failed with "sh: line 1: close: command not found".

Register the taskbar built-in action names in eventActionMap_ so they are
recognized as module actions, and skip the shell forkExec in
handleUserEvent when the configured value is a recognized module action.
Non-action values are still run as user shell commands.

Fixes #3284.
2026-07-04 12:50:30 +02:00
Alex fae2b311de fix(tray): set an accel group on the dbusmenu client to stop Gtk-CRITICAL crash
When a tray item exports menu accelerators (e.g. Mattermost), libdbusmenu-gtk
calls gtk_widget_set_accel_path() with a NULL accel group because the
DbusmenuGtkClient never had one assigned. This raises a Gtk-CRITICAL that
corrupts menu state, and aborts Waybar when running under
G_DEBUG=fatal-criticals.

Assign a fresh GtkAccelGroup to the client right after the menu is created,
before it is populated or shown.

Fixes #5142.
2026-07-04 12:50:30 +02:00
AlexandClaude Opus 4.8 33223d5534 ci(wiki): render option tables, pin pandoc, add visible auto-gen note
Improve the generated wiki pages:
- normalize definition-style option blocks (*name*: / typeof: / default:)
  into scdoc tables, so every module renders options as a clean table like
  the table-style pages (Bluetooth, Network, ... no longer a wall of text)
- drop pandoc's empty leading table-header row so the real header shows
- pin pandoc to 3.5 in CI (the distro 2.x man reader mangled lists into
  blockquotes); matches local output
- prepend a visible "auto-generated from master, do not edit here" note to
  every page (was an invisible HTML comment)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 12:49:30 +02:00
Alex b2d15d1e3a fix(keyboard-state): read lock LEDs from all devices, not just the first
When no explicit device-path is configured, a multi-node keyboard can
expose several event devices where only one actually toggles the lock
LEDs. Previously update() read state from a single arbitrary device
(libinput_devices_.begin()), which often picked a node whose EV_LED
values never change, leaving the indicator stuck. Iterate all devices
and OR their NUML/CAPSL/SCROLLL values together so a lock is reported
on if any device reports it on. The single-device path is preserved
when device-path is set.

Fixes #2215.
2026-07-04 12:36:32 +02:00
Alexis Rouillard 8541ec8189 Merge pull request #5160 from Alexays/fix/clock-graceful
fix(clock): degrade gracefully on an invalid format specifier
2026-07-04 09:13:28 +02:00
Alexis Rouillard 368430208b Merge pull request #5161 from Alexays/fix-wireplumber
fix(wireplumber): correct linear→cubic volume conversion (0% on Bluetooth) + source-muted state
2026-07-04 09:13:11 +02:00
Alex 2650f062b5 fix(wireplumber): apply source-muted for Audio/Source modules
For a module configured node-type "Audio/Source", node_id_ and
source_node_id_ resolve to the same source node, so its primary mute
state is stored in muted_. update() unconditionally mapped muted_ to the
muted/sink-muted classes and source-muted only to the secondary
source_muted_ flag, so a source module could never receive source-muted
-- only the sink classes.

Gate the mute-class selection on the configured node-type: a source-type
module drives source-muted from its primary mute state, while a sink-type
module keeps muted/sink-muted for its sink and source-muted for the
secondary default source it tracks for {format_source}. The primary node
still feeds {volume} via updateVolume, so source-widget volume rendering
is unaffected.

Fixes #4523.
2026-07-04 08:57:36 +02:00
Alex 69b9a14b96 fix(wireplumber): show correct volume on Bluetooth sinks
The mixer-api is configured with the linear scale (0), so volume_ holds
the raw linear gain. The perceptual "cubic" value shown by wpctl and
exposed as {volume} is cbrt(linear), but update() computed pow(volume_, 3)
instead. Cubing under-reads every volume below max and collapses small
linear gains to 0% -- which is why the default Bluetooth sink (whose
normal levels map to low linear gains, e.g. wpctl 0.55 -> linear 0.166)
displayed 0% while wpctl reported a normal, unmuted volume.

Replace the inverted conversions with the correct cube-root/cube pair in
the display path, the scroll-scale conversions (cubic / cubic_percent),
and the max-volume ceiling mapping so scrolling and the cap stay
consistent with the corrected {volume}.

Fixes #5159.
2026-07-04 08:57:15 +02:00
Alex 23d63d2b84 fix(clock): degrade gracefully on an invalid format specifier
An unsupported specifier (e.g. the %-I / %OI no-leading-zero padding
modifiers, which the date/std::chrono formatter does not implement) threw out
of update() and the whole clock module failed to load. Catch it, warn once,
and fall back to {:%H:%M} so the bar still comes up. Addresses #1469.
2026-07-04 08:46:32 +02:00
Alexis Rouillard ea33ceb055 Merge pull request #5158 from Alexays/fix/crash-freeze
fix: batch of crash/freeze fixes (clock DST, interval:0, thread-safety, reconnects)
2026-07-04 08:36:26 +02:00
Alex 45ab8a1211 fix(core): fall back to default interval for periodic modules on interval:0
Addresses review: a zero interval_ must stay reserved for modules whose
default interval is already 0 (event-driven). Periodic modules (clock,
simpleclock, pollers) would otherwise do % interval_ (modulo by zero) or
sleep_for(0) in a tight loop. interval:0 on a periodic module now falls back
to its default interval.
2026-07-04 08:35:11 +02:00
Alex 5c06233340 style: clang-format the crash-fix changes 2026-07-04 03:17:33 +02:00
Alex 6672e924df fix(niri): reconnect IPC and stop per-event throttling to prevent freeze
The niri IPC worker slept 1ms per event and never reconnected. Under an
event burst the per-event cap back-pressures the socket, niri fills its
send buffer and drops the stream; read_line then returns false, the
detached thread exits and the module freezes permanently.

Remove the per-event sleep so events drain as fast as they arrive, and
wrap the socket setup and read loop in a reconnect loop that backs off
and re-establishes the stream on drop. A running_ flag lets the thread
exit cleanly on teardown.

Fixes #5117.
2026-07-04 03:14:13 +02:00
Alex 3831524ba8 fix(audio_backend): never throw across the PulseAudio callback boundary
connectContext() throws std::runtime_error when pa_context_connect() fails.
It was called directly from contextStateCb (the libpulse mainloop thread,
running pure-C callback frames) on the PA_CONTEXT_FAILED reconnect path, so on
a pipewire/pulse restart the exception unwound across the C callback boundary
and triggered std::terminate/SIGABRT.

Add reconnectContext() noexcept which wraps connectContext() and logs failures
instead of throwing, and use it from the callback. Guard against the
FAILED -> connect -> FAILED recursion/busy loop with a reentrancy flag. The
constructor-time connectContext() still throws as before.

Fixes #5141.
2026-07-04 03:14:13 +02:00
Alex d3bfec13cc fix(keyboard-state): make device removal idempotent to stop libinput abort
On device unplug the inotify IN_DELETE handler removed the libinput
device and unref'd it before erasing the entry from libinput_devices_.
A repeated IN_DELETE event for the same path (observed as the
"has been removed" log line printed twice) could reach an
already-unlinked device and trigger a libinput list_remove assertion
abort.

Erase the map entry first (under devices_mutex_) so a second delete for
the same path is a no-op, then call libinput_path_remove_device() and
libinput_device_unref() exactly once per device pointer.

Fixes #5143, #4443, #4566.
2026-07-04 03:14:13 +02:00
Alex 4229dc8ac2 fix(sway/window): resolve app icon on the main thread to stop icon-theme race
onCmd() runs on the sway IPC worker thread and called updateAppIconName(),
which touches the global Gtk::IconTheme cache. Concurrent access with the
main thread's draw (propagate_draw -> gtk_icon_theme_has_icon ->
g_hash_table_lookup) races and can segfault, notably on multi-monitor and
focus changes.

Move the icon-theme lookup into Window::update(), which runs on the main
thread via dp.emit(), and only store app_id_/app_class_ in onCmd().

Fixes #4108.
2026-07-04 03:14:13 +02:00
Alex c0a26104a5 fix(sway/language): apply CSS classes on the main thread to stop SIGSEGV
set_current_layout() mutated label_'s GTK style context (remove_class/
add_class) while being called from the sway IPC worker thread via
onEvent(). Off-main-thread GTK widget mutation caused a SIGSEGV.

Record only the target layout in set_current_layout() and apply the
matching CSS class in update(), which the dispatcher runs on the GTK
main thread. A new applied_class_ member tracks the currently applied
class so update() can swap it. The shared layout_/applied_class_ state
is guarded by the existing mutex_.

Fixes #3702.
2026-07-04 03:14:13 +02:00
Alex c29ed5f972 fix(client): don't exit when the desktop portal fails to start
The Portal constructor synchronously auto-starts org.freedesktop.portal.Desktop
via a Gio::DBus::Proxy. If that service fails or crashes on start it throws a
Glib::Error, which was previously uncaught and terminated Waybar. Wrap the
construction in a try/catch, log a warning and leave portal as nullptr on
failure, and null-guard every dereference so a missing portal simply disables
light/dark appearance detection instead of crashing.

Fixes #3140, #3601.
2026-07-04 03:14:13 +02:00
Alex d130ce6a66 fix(clock): disambiguate DST transitions to stop tooltip crash
Building a zoned_time/zoned_seconds from a local_time throws
ambiguous_local_time during the DST fall-back hour and nonexistent_local_time
across the spring-forward gap. update() runs this every minute with the
tooltip enabled by default and has no try/catch, so Waybar aborts every
minute during a DST transition. Pass choose::earliest at each construction to
resolve deterministically instead of throwing.

Fixes #2615; resolves the recurring DST-crash duplicates #5006, #5018, #5063,
#5096, #3024.
2026-07-04 03:10:55 +02:00
Alex bfe38bd1e2 fix(custom): validate JSON output UTF-8 and stop restart-interval:0 busy loop
- parseOutputJson() passed script text/alt/tooltip straight to Pango/GTK; an
  invalid-UTF-8 byte aborted the bar in g_utf8_collate. Validate/make_valid
  like parseOutputRaw already does. Fixes #2829.
- restart-interval:0 was floored to 1ms, respawning the script ~1000x/s and
  starving the main loop; a non-positive restart-interval now stops instead.
  Part of #4842.
2026-07-04 03:09:20 +02:00
Alex b5842d4f53 fix(core): don't floor an explicit interval:0 to 1ms
std::max(1L, interval*1000) turned a user's explicit "interval": 0 into a
1ms periodic refresh, i.e. a ~1000x/s busy loop that starves the GTK main
loop and leaks memory (mpris RSS growth, missing tooltips, frozen updates).
An explicit 0 now stays the 'no periodic refresh' sentinel. Fixes #4987,
#4842; helps #4864, #4917, #4998, #5145.
2026-07-04 03:09:20 +02:00
Alex 4eb2513cc0 fix(hyprland/language): pass named args in format-<lang> branches
The format-<short_description>[-<variant>] override branches only passed a
positional arg, so a format using {short}/{long}/{variant} threw 'argument
not found', which disabled the whole module. Now supply the same named args
as the fallback/tooltip branches. Fixes #5120.
2026-07-04 03:09:20 +02:00
Alex 1d86e8dfa9 fix(custom/graph): guard signal config with isInt() to avoid SIGABRT
refresh() called config_["signal"].asInt() unconditionally on every RT
signal; a non-integer "signal" value throws Json::LogicError and aborts
Waybar. Matches the guard already present in custom/image/idle_inhibitor.
Fixes #3514.
2026-07-04 03:09:20 +02:00
AlexandClaude Opus 4.8 4e76d7339f ci(wiki): auto-insert new module pages into the sidebar
generate.py now keeps _Sidebar.md in sync: any Module:-* page from the
mapping that is not yet linked is inserted alphabetically into the
Modules list. Existing entries (custom labels, nested sub-entries,
hand-written non-module links) are left untouched, so adding a module no
longer requires editing the sidebar by hand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 03:07:53 +02:00
AlexandClaude Opus 4.8 c76bfc018b ci: auto-sync GitHub wiki module pages from man pages
Add tooling that makes the scdoc man pages the single source of truth and
regenerates the wiki module pages from them:

- .github/wiki/mapping.json: man-page -> wiki-page mapping (aggregation-aware)
- .github/wiki/generate.py: scdoc -> pandoc -> gfm, strips man-only sections,
  concatenates aggregated pages, appends optional extras/<Page>.md
- .github/wiki/extras/: hand-kept appendices (screenshots) with no man equivalent
- .github/workflows/wiki.yml: on push to man/** (or the tooling), regenerate
  and push the wiki; other wiki pages are left untouched

Covers all 65 man pages -> 45 wiki pages (5 new: GPS, Inhibitor, Mango, Menu,
WWAN). Non-module and hand-written wiki pages are never modified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 03:02:10 +02:00
AlexandClaude Opus 4.8 a3b39cfcf6 docs(man): backfill man pages with wiki-only info (source-of-truth pass)
Port information that previously lived only in the GitHub wiki into the
authoritative scdoc man pages, so the man pages become the single source
of truth for module documentation.

33 man pages enriched (options, format replacements, actions, style
selectors, troubleshooting and implementation notes). Additions were
verified against the source; stale/incorrect wiki entries were
deliberately not ported. All pages still compile with scdoc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:55:57 +02:00
AlexandClaude Opus 4.8 72fdf77c19 docs: align man pages with code + fill documentation gaps (0.16.0 audit)
A full man<->code consistency audit surfaced options, format placeholders
and CSS classes that were implemented but undocumented, documented but not
implemented (some causing fmt crashes when copied from examples), and
defaults that disagreed with the code. This aligns the docs with the code
and fixes a few genuine code gaps.

Docs:
- Add the missing waybar-user(5) man page (and register it in meson.build)
- Document previously-undocumented options/placeholders/CSS across many
  modules (custom image-path/image-name/icon-size, graph_type/width/
  datapoints; battery smooth-power; wireplumber format-source/only-physical;
  mpris {position}/prefer-album-artist; network {signalStrengthApp}/compact
  bandwidth; pulseaudio {source_volume}/{source_desc}; upower {temperature}/
  {model}/{native-path}; wwan {power_state}/{imei}; tray ignore-list; and
  many CSS state classes: .sink-muted, .source-muted, .workspace-hover, etc.)
- Correct documented defaults to match the code (hyprland format {name},
  gamemode {count}, cpu-graph interval 5, cava input_delay 4, niri taskbar
  icon-size 16, disk/gps/wayfire formats, menu-actions object type, ...)
- Remove placeholders/options that do not apply (custom-graph {icon}/format/
  format-icons/rotate) and fix crashing examples (wwan {mode}, gps
  format-no-fix); note cava background/foreground/continuous_rendering are
  cava-config-file options

Code:
- bluetooth: accept the documented `controller` key as a synonym of
  `controller-alias` (the option was silently ignored)
- mango/workspaces: supply the documented `{name}` fmt arg (was missing ->
  fmt::format threw)
- privacy: read `tooltip` as a bool (was guarded on isString(), so the
  documented `tooltip: false` was silently ignored)

All man pages validated with scdoc 1.11.4. Not compiled locally (no gtkmm);
C++ build relies on CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:39:38 +02:00
AlexandClaude Opus 4.8 14cb78b835 ci: install systemd-dev for systemd.pc in build-full job
On Debian sid, `dependency('systemd')` needs systemd.pc, which is shipped by
the `systemd-dev` package, not `libsystemd-dev`. Add it so the forced
-Dsystemd=enabled configure step succeeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:34:31 +02:00
AlexandClaude Opus 4.8 45276f217e ci: add feature-complete build job to guarantee all modules compile
The linux matrix ran every distro with features in `auto` mode, so any
module whose dependency was missing from the image was silently skipped
and never compiled. Three modules (privacy/pipewire, gps, wwan) were built
by no CI job at all, and several others only on one or two distros.

Add a dedicated `build-full` job (Debian image) that installs the missing
dependencies inline and force-enables every optional feature, turning a
broken include or a missing dep into a hard build error instead of a silent
skip. Drop `debian` from the auto matrix since `build-full` uses the same
image and builds a strict superset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:32:33 +02:00
Alexis Rouillard ae968044a7 Merge pull request #5157 from Alexays/fix/0.16.0-review-and-ci
fix: backward-compat + bug fixes and man-page build fix for 0.16.0
2026-07-04 02:18:36 +02:00
AlexandClaude Opus 4.8 f72f84e011 fix: backward-compat + bug fixes and man-page build fix for 0.16.0
Post-0.15.0 review of the 0.15.0..HEAD range surfaced regressions and
bugs. This restores backward compatibility for existing configs/CSS,
fixes confirmed defects, and repairs the scdoc man-page build break on
master. Pango-markup tooltips are intentional and were kept.

Backward-compat restorations:
- AModule: honor legacy numeric Gdk::CursorType cursor values (int overload)
- memory: correct GiB divisor (was ~2.3% low); round bare {} placeholders
- wireplumber: scale max-volume into the linear domain so the cap works again
- idle_inhibitor: gate right/middle-click deactivate & scroll on dynamic-timeouts;
  accept both dynamic-timeout(s); widen timeout to double (no fractional truncation)
- custom: keep #custom-<name>.<class> CSS selectors working (classes on box_)
- image: don't wordexp-split a single path; fall back to the literal path
- niri/window: restore hide-when-empty (new show-empty opt-in); escape tooltip
- wlr/taskbar: plain-text tooltip when markup is disabled

Bug fixes:
- tray: fix use-after-free in onAdd; guard the watcher retry timeout
- hyprland: clamp max-windows iterator (OOB); drop duplicate language tooltip block
- niri/window: supply {col}/{max_col} args in the empty branch (fmt::format_error)
- mpris: escape {dynamic}/{player} tooltip; fix dangling player; albumArtist source
- mango: fix use-after-free race (dispatch under callback_mutex_)
- mpd: contain throwing checkErrors in noexcept idle paths (no std::terminate/UAF)
- keyboard_state: always render every lock label, with guarded defaults
- bluetooth: bound GATT ReadValue timeout, opt-in + services-resolved gating,
  preserve authoritative Battery1 percentage
- wireplumber: fix WpDevice reference leak / NULL handling
- battery, clock, dwl, wayfire, graph, custom_graph, transform, river: assorted
  crash/logic fixes

Man page / build:
- niri-workspaces: fix scdoc "indented by an amount greater than 1"
  (workspace-taskbar sub-options were mis-indented; breaks man-page build)
- document new show-empty (niri/window); correct network {txBitrate}/{rxBitrate}

Not compiled locally (no gtkmm on this host); C++ build relies on CI.
Man pages validated with scdoc 1.11.4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:14:13 +02:00
Alexis Rouillard 482cfab64e Merge pull request #2891 from awariac/link-speed
net: add ethernet link speed
2026-07-04 01:54:42 +02:00
Piotr Wegrzyn 810a8c0f94 feat(net): add {linkSpeed} format replacement
Adds an ethernet link speed ({linkSpeed}) read from
/sys/class/net/<iface>/speed, plus a skip-decimal option on pow_format
to drop a trailing .0. Rebased onto master's store-based network
formatting; existing compact bandwidth args updated for the new
pow_format signature.
2026-07-04 01:54:30 +02:00
Alexis Rouillard 237d6c7c7b Merge pull request #4997 from sewaustav/feat/niri-workspaces-taskbar
feat(niri): add workspace-taskbar support
2026-07-04 01:50:29 +02:00
Alexis Rouillard 7887cdacda Merge pull request #4504 from ErikReider/geoclue-privacy
Add GeoClue2 location privacy item
2026-07-04 01:50:22 +02:00
sewaustav 9cd09c2569 feat(niri): add workspace-taskbar support
Adds a per-workspace app-icon taskbar (Workspace class) to the
niri/workspaces module. Rebased onto master: integrated with the
existing window-rewrite feature so the {windows} and {total} format
replacements continue to work alongside the new taskbar.
2026-07-04 01:50:16 +02:00
Alexis Rouillard 16c79c8dc1 Merge pull request #4557 from adryzz/wwan-module
WWAN module (ModemManager)
2026-07-04 01:48:42 +02:00
Alexis Rouillard bff291889d Merge pull request #4536 from Esensats/master
feat: sway/workspaces: add custom workspace sorting functionality
2026-07-04 01:44:23 +02:00
Alexis Rouillard 8eb7ee46cb Merge pull request #4850 from yangyingchao/master
(sni) be able to control oder of tray items, and clean up sni module based on clang-tidy
2026-07-04 01:43:31 +02:00
Alexis Rouillard d6b6e72896 Merge pull request #4305 from edeustua/fix_hyprland_window_count
fix: Use workspace ID to handle hyprland/workspace updates
2026-07-04 01:43:07 +02:00
Emiliano Deustua 5f7629a36e format: Fix clang formatting 2026-07-04 01:42:54 +02:00
Emiliano Deustua 95664c7662 fix: Remove load persistent workspaces from workspace rules 2026-07-04 01:42:54 +02:00
Emiliano Deustua 7c86f1ccb4 fix: Use workspace ID to handle workspace updates 2026-07-04 01:42:54 +02:00
Alexis Rouillard 0bb207d56f Merge pull request #5115 from jaschiu/jaschiu-patch-1
fix(tray): segfault due to use-after-free from iterating children
2026-07-04 01:42:36 +02:00
Erik Reider eeb7bc702e Added GeoClue2 privacy item
Rebased onto current master and fixed the privacy module build:
- privacy_item.hpp no longer includes privacy.hpp (it does not use the
  Privacy class); this broke the circular include that left PrivacyItem
  undeclared when privacy.hpp was reached first from privacy_item.cpp.
- privacy_item.cpp now includes gtkmm/label.h explicitly, since Gtk::Label
  was previously only pulled in transitively via privacy.hpp.
2026-07-04 01:42:35 +02:00
Alexis Rouillard ddce791b43 Merge pull request #4396 from kabhijeet/master
feat (niri/workspaces): add dynamic window icons to workspaces matching hyprland/workspaces
2026-07-04 01:42:35 +02:00
Abhijeet 9e10a9aec9 feat(niri/workspaces): add window icons via window-rewrite
Adds format-window-separator, window-rewrite and window-rewrite-default
options plus the {windows} format replacement to the niri/workspaces
module (rebased onto master; coexists with ignore-workspaces).
2026-07-04 01:42:23 +02:00
Alexis Rouillard 7ffee464b3 Merge pull request #1358 from RyanGibb/output-dimensions
added output-dimensions option
2026-07-04 01:37:55 +02:00
Alex 5ecda40166 meson: fix libmm-glib dependency to use correct pkg-config name 'mm-glib'
The ModemManager libmm-glib library installs its pkg-config file as
mm-glib.pc (not libmm-glib.pc), so dependency('libmm-glib') never
resolved and the nix-flake-check build failed with the wwan module
enabled.
2026-07-04 01:37:11 +02:00
Alex 8fa58b1000 nix: add libmm-glib for the wwan module 2026-07-04 01:36:46 +02:00
Alex 247e18fb0b Fix clang-format 2026-07-04 01:36:46 +02:00
Alex 41d86d92a8 fix(wwan): null-initialize pointer members to avoid UB in destructor 2026-07-04 01:36:46 +02:00
Alex 2aa74a523c wwan: fix hide-disconnected default, plug dup() leaks, meson/man cleanup
- update(): only hide the module when hide-disconnected is set AND the
  modem is not in the CONNECTED state, instead of hiding it unconditionally
  (the module was invisible by default).
- free the gchar* returned by mm_modem_dup_physdev/
  mm_modem_dup_equipment_identifier/mm_sim_dup_operator_name.
- meson.build: drop duplicate libgps dependency, install the wwan man page.
- man: document hide-disconnected (replacing the unimplemented
  hide-failed/hide-disabled/hide-not-registered options).
- remove stray empty subprojects/.wraplock.
2026-07-04 01:36:46 +02:00
Lena 11fdbe8ae3 clang-format 2026-07-04 01:36:24 +02:00
Lena cd73e6cdfb clang complained 2026-07-04 01:36:24 +02:00
Lena d7274a041f improve manpage 2026-07-04 01:36:24 +02:00
Lena 7e65b091f2 cleanup 2026-07-04 01:36:24 +02:00
Lena fa5abc37f6 remove useless junk 2026-07-04 01:36:24 +02:00
Lena 90a0f46e37 almost done 2026-07-04 01:36:23 +02:00
Lena 949da4e829 create partial wwan module 2026-07-04 01:36:23 +02:00
yangyingchao c07f8ed0a4 (sni) Be able to control order for system tray items. 2026-07-04 01:35:50 +02:00
Esensats 41926e873f feat: add custom workspace sorting functionality 2026-07-04 01:35:05 +02:00
Jason Chiu 1facd4ef95 Update tray.cpp 2026-07-04 01:34:00 +02:00
Jason Chiu 87a337ecdf Update tray.hpp 2026-07-04 01:31:39 +02:00
Alex dbc0e1a00a fix(config): correct weight->width typo in output-dimensions filter 2026-07-04 01:25:49 +02:00
Alexis Rouillard 46e96dfbaa Merge pull request #4545 from mbroemme/2025-wlr-taskbar-truncate-to-ellipsize-long-button
feat(wlr/taskbar): add `truncate` option to ellipsize long button text
2026-07-04 01:24:40 +02:00
Ryan Gibb eddba950f8 better logic to find valid output dimensions 2026-07-04 01:22:01 +02:00
Ryan Gibb d3ef76ccbc added output-dimensions option 2026-07-04 01:22:01 +02:00
Maik Broemme 7a84583a76 wlr/taskbar: add truncate to ellipsize long button text 2026-07-04 01:16:45 +02:00
Alexis Rouillard e36824e7be Merge pull request #2157 from savely-krasovsky/mediaplayer-py-enhancement
feat: added dbus handler to update current player
2026-07-04 01:14:56 +02:00
Alex efe31dfecf build: install wayfire man pages (register in meson man_files)
The wayfire window/workspaces man pages existed but were never added to
man_files, so they were never installed. Fixes #4264.
2026-07-04 01:13:28 +02:00
Alexis Rouillard 1214776ca2 Merge pull request #3818 from Sduniii/patch-1
feat: Added source_desc argument to format_source in pulseaudio.cpp
2026-07-04 01:13:07 +02:00
Alexis Rouillard d6523df849 Merge pull request #3817 from JarKz/master
feat: add way to use environment value for 'output' option
2026-07-04 01:12:14 +02:00
Alexis Rouillard 37dbd59dac Merge pull request #3017 from 0x000C0A71/master
[hyprland/workspaces] Add tooltips to hyprland/workspaces
2026-07-04 01:12:10 +02:00
Alexis Rouillard 78b9a38532 Merge pull request #4320 from LoricAndre/master
feat(hyprland/workspaces): add uniqueIcons option
2026-07-04 01:12:05 +02:00
Alexis Rouillard 285e68c098 Merge pull request #3151 from VPavliashvili/module-images
feat: add new module 'images' to render multiple images under one gtk container
2026-07-04 01:10:43 +02:00
Alexis Rouillard 48c3f9712f Merge pull request #3311 from AtomToast/master
add format-icons to river/layout
2026-07-04 01:09:58 +02:00
Alexis Rouillard a560e6a6d3 Merge pull request #4265 from g-goessel/master
feat: Add wifi network link bitrate value
2026-07-04 01:09:20 +02:00
Alexis Rouillard a4380bb600 Merge pull request #3573 from rossmeier/master
add click coordinates to script
2026-07-04 01:08:26 +02:00
Alexis Rouillard d42e0503e2 Merge pull request #3740 from NoakPalander/feature/squash-list
Added a taskbar squash option
2026-07-04 01:07:25 +02:00
Alexis Rouillard 39fdf7b9fb Merge pull request #3370 from torgiren/idle_inhibitor
allow dynamic timeout setting for idle inhibitor
2026-07-04 01:02:30 +02:00
Alexis Rouillard 84e1577489 Merge pull request #4001 from hansp27/niri-workspaces-display-preference
feat: add display-condition configuration option
2026-07-04 01:02:19 +02:00
Alexis Rouillard 7484cb8db3 Merge pull request #4014 from piwonskp/4008/dynamic-drawer
feat(Group/Drawer): add dynamic drawer hiding modules based on module's class
2026-07-04 01:02:14 +02:00
Alex ca9bfe5bfa Merge remote-tracking branch 'origin/master' into pr-2157
# Conflicts:
#	resources/custom_modules/mediaplayer.py
2026-07-04 00:59:00 +02:00
Alexis Rouillard 01d1b5a47e Merge pull request #4940 from beyertom/clock/configurable-week-numbering
clock: add weeks-numbering option for configurable CW calculation
2026-07-04 00:56:29 +02:00
Alex 8276f6a629 Merge remote-tracking branch 'origin/master' into pr-3017
# Conflicts:
#	include/modules/hyprland/workspaces.hpp
#	man/waybar-hyprland-workspaces.5.scd
#	src/modules/hyprland/workspaces.cpp
2026-07-04 00:55:38 +02:00
Alexis Rouillard 24dba2bb24 Merge pull request #4694 from whymusticode/max-icons
feat(hyprland/workspaces): add deduplication and max-icons option to workspace-taskbar
2026-07-04 00:55:34 +02:00
Alexis Rouillard 2e9e0ba706 Merge pull request #4714 from PickNicko13/master
Implemented "only-physical" parameter in the wireplumber module.
2026-07-04 00:55:27 +02:00
Alex 35dd0af208 Merge remote-tracking branch 'origin/master' into pr-4001
# Conflicts:
#	man/waybar-niri-workspaces.5.scd
#	src/modules/niri/workspaces.cpp
2026-07-04 00:54:50 +02:00
Alex f7ff8eab58 Merge remote-tracking branch 'origin/master' into pr-3370
# Conflicts:
#	include/modules/idle_inhibitor.hpp
#	man/waybar-idle-inhibitor.5.scd
#	src/modules/idle_inhibitor.cpp
2026-07-04 00:54:41 +02:00
Alex 328b6dc017 Merge remote-tracking branch 'origin/master' into pr-4014
# Conflicts:
#	include/group.hpp
#	src/group.cpp
2026-07-04 00:54:02 +02:00
Alexis Rouillard 10add89802 Merge pull request #4624 from andresilva/tray-hide-icons
Support hiding tray icons
2026-07-04 00:53:31 +02:00
Alexis Rouillard 4e13253230 Merge pull request #4685 from AckslD/no-switch-output
feat: allow not switching output when clicking sway/workspace
2026-07-04 00:53:26 +02:00
Alex a45328359d Merge remote-tracking branch 'origin/master' into pr-4265
# Conflicts:
#	src/modules/network.cpp
2026-07-04 00:52:34 +02:00
Alexis Rouillard 40042f42ac Merge pull request #4537 from thomascrha/niri-workspace-ignore-workspaces
feat(niri/worspaces): add ignore-workspaces configuration option
2026-07-04 00:52:19 +02:00
Alex 90883da500 Merge remote-tracking branch 'origin/master' into pr-3151
# Conflicts:
#	include/modules/image.hpp
#	src/modules/image.cpp
2026-07-04 00:51:52 +02:00
Alexis Rouillard 49f6edf67b Merge pull request #4543 from mbroemme/2025-wlr-taskbar-evenly-distribute-buttons
feat(wlr/taskbar): add `homogeneous` option to evenly distribute button widths
2026-07-04 00:51:24 +02:00
Alex 05f32172df Merge remote-tracking branch 'origin/master' into pr-4320
# Conflicts:
#	include/modules/hyprland/workspaces.hpp
#	man/waybar-hyprland-workspaces.5.scd
#	src/modules/hyprland/workspace.cpp
#	src/modules/hyprland/workspaces.cpp
2026-07-04 00:51:21 +02:00
Alex 23762842c6 Merge remote-tracking branch 'origin/master' into pr-3573
# Conflicts:
#	src/AModule.cpp
2026-07-04 00:51:06 +02:00
Alex 3df1a13b7d Merge remote-tracking branch 'origin/master' into pr-3740
# Conflicts:
#	include/modules/wlr/taskbar.hpp
#	src/modules/wlr/taskbar.cpp
2026-07-04 00:50:23 +02:00
Alexis Rouillard 2709d293ee Merge pull request #4941 from sjudin/groups-add-empty-css-class
feat(group): Add .empty class to empty groups
2026-07-04 00:50:01 +02:00
Alex 29c014e3e5 Merge remote-tracking branch 'origin/master' into pr-3817
# Conflicts:
#	src/config.cpp
2026-07-04 00:49:37 +02:00
Alex 5c0f954db7 Merge remote-tracking branch 'origin/master' into pr-3311
# Conflicts:
#	src/modules/river/layout.cpp
2026-07-04 00:49:01 +02:00
Alex 2600e1e641 Merge remote-tracking branch 'origin/master' into pr-3818
# Conflicts:
#	src/modules/pulseaudio.cpp
2026-07-04 00:48:49 +02:00
Alexis Rouillard e9494bd271 Merge pull request #5153 from Grinchuelo/feat-customizable-icon-thresholds
feat(ALabel): allow custom percentage thresholds in format-icons
2026-07-04 00:44:34 +02:00
Alexis Rouillard 0e8d875079 Merge pull request #5033 from Habbie/css-import-semicolon
waybar-styles.5: add semicolon to css import statement
2026-07-04 00:42:56 +02:00
Alexis Rouillard 3cd93d849f Merge pull request #5032 from yyyumeniku/master
hyprland/workspaces: add debounce timer to prevent workspace button f…
2026-07-04 00:42:04 +02:00
Alex 08e457e3cd Merge remote-tracking branch 'origin/master' into pr-4694
# Conflicts:
#	src/modules/hyprland/workspace.cpp
2026-07-04 00:41:50 +02:00
Alexis Rouillard 15fd0b6c5d Merge pull request #4772 from szbmrk/feat-hyprland-submap-icon-handling
feat(hyprland/submap): add config option to specify different icons for submaps
2026-07-04 00:41:11 +02:00
Alex 9f3e5c09a3 Merge remote-tracking branch 'origin/master' into pr-4537
# Conflicts:
#	include/modules/niri/workspaces.hpp
#	src/modules/niri/workspaces.cpp
2026-07-04 00:40:14 +02:00
Alex df0edaf162 Merge remote-tracking branch 'origin/master' into pr-4624
# Conflicts:
#	include/modules/sni/item.hpp
#	src/modules/sni/item.cpp
#	src/modules/sni/tray.cpp
2026-07-04 00:39:23 +02:00
Alexis Rouillard 6cdd84031c Merge pull request #5049 from finitemonkey/fix-missing-docs
Add missing documentation for the "target" option of the PulseAudio-Slider module.
2026-07-04 00:39:18 +02:00
Alex b509e254da Merge remote-tracking branch 'origin/master' into pr-4714
# Conflicts:
#	include/modules/wireplumber.hpp
#	src/modules/wireplumber.cpp
2026-07-04 00:38:51 +02:00
Alex da5b02a405 Merge remote-tracking branch 'origin/master' into pr-4543
# Conflicts:
#	man/waybar-wlr-taskbar.5.scd
#	src/modules/wlr/taskbar.cpp
2026-07-04 00:38:45 +02:00
Alex 4ffc97efe9 Merge remote-tracking branch 'origin/master' into pr-4685
# Conflicts:
#	src/modules/sway/workspaces.cpp
2026-07-04 00:38:15 +02:00
Alex b851e1c8f9 Merge remote-tracking branch 'origin/master' into pr-4940
# Conflicts:
#	man/waybar-clock.5.scd
2026-07-04 00:37:39 +02:00
Alexis Rouillard 889cff224b Merge pull request #5156 from Alexays/refactor/generic-tooltip
refactor(ALabel): generic label+tooltip helper to remove per-module duplication
2026-07-04 00:37:19 +02:00
Alex ef050a343f Merge remote-tracking branch 'origin/master' into pr-4941
# Conflicts:
#	include/group.hpp
#	man/waybar.5.scd.in
2026-07-04 00:36:57 +02:00
Alexis Rouillard a3101a1ca0 Merge branch 'master' into feat-hyprland-submap-icon-handling 2026-07-04 00:32:12 +02:00
Alexis Rouillard 423ca224f5 Merge branch 'master' into master 2026-07-04 00:32:08 +02:00
Alexis Rouillard adfed58700 Merge branch 'master' into css-import-semicolon 2026-07-04 00:32:06 +02:00
Alexis Rouillard b477ffc95b Merge branch 'master' into fix-missing-docs 2026-07-04 00:32:03 +02:00
Alexis Rouillard fd11ce3b5e Merge branch 'master' into feat-customizable-icon-thresholds 2026-07-04 00:32:00 +02:00
Alexis Rouillard d304e2fdac Merge pull request #5133 from J0hannes101/master
feat(cursor): use cursor-shape-v1 cursor names instead of Gdk::CursorType
2026-07-04 00:30:33 +02:00
Alexis Rouillard 70d466d37c Merge pull request #4553 from VannTen/feat/native_journal_log
log: use native journald protocol for when running as a service
2026-07-04 00:30:26 +02:00
Alexis Rouillard 9a6009b0b4 Merge pull request #5068 from c4rlo/sway-ipc-fixes
Sway IPC client: robustness improvements & code cleanups
2026-07-04 00:30:22 +02:00
Alex da562a9172 Merge remote-tracking branch 'origin/master' into refactor/generic-tooltip
# Conflicts:
#	src/modules/wireplumber.cpp
2026-07-04 00:29:26 +02:00
Alexis Rouillard e921e255d1 Merge pull request #5110 from archion/master
feat(niri/workspaces): add {total} format token to workspaces
2026-07-04 00:27:31 +02:00
Alexis Rouillard ffc3bec905 Merge pull request #4565 from guttermonk/idle_inhibitor-activity-detection
Added Idle Inhibitor Features: Signal Inputs & Activity Detection
2026-07-04 00:27:29 +02:00
Alex 94a8b40071 Merge remote-tracking branch 'origin/master' into pr-4553 2026-07-04 00:23:24 +02:00
Alex c0a0608c43 Merge remote-tracking branch 'origin/master' into pr-5068 2026-07-04 00:22:43 +02:00
Alexis Rouillard f5164a9f30 Merge branch 'master' into master 2026-07-04 00:17:35 +02:00
Alex 003d531701 refactor(AModule): hoist format/tooltip resolution to the base module
Move resolveTooltipFormat() (and add resolveFormat()) from ALabel down into
AModule, and add a generic updateTooltip(Gtk::Widget&, ...) helper. This lets
modules that are not ALabel-derived reuse the shared tooltip logic instead of
re-implementing it. Migrate gamemode (the only non-ALabel module reading
tooltip-format) to updateTooltip(box_, ...). ALabel inherits the resolvers.
2026-07-04 00:17:14 +02:00
Alexis Rouillard 733ec6fdc4 Merge pull request #4712 from AntoineGS/feat/bt-multiple-batteries
feat: add support for bt peripheral device battery
2026-07-04 00:14:59 +02:00
Alexis Rouillard 2754ea6914 Merge pull request #5017 from nonamescm/add-static-river-tag-classes
River/tags: Add static CSS classes for all available tags.
2026-07-04 00:14:54 +02:00
Alexis Rouillard 3a5158590d Merge pull request #5069 from stlkr/master
feat(custom): add image
2026-07-04 00:14:00 +02:00
Alexis Rouillard 825d921725 Merge pull request #4060 from ErrorNoInternet/group-reveal-by-default
feat(group): add reveal-by-default option
2026-07-04 00:12:54 +02:00
Alexis Rouillard 6e1f551919 Merge pull request #4334 from theoratkin/master
feat(mpd): add playing-interval option
2026-07-04 00:11:01 +02:00
Alexis Rouillard 84cb232305 Merge pull request #5041 from yuukibarns/enable-bar-scroll
feat(niri/workspaces): Add `enable-bar-scroll` option
2026-07-04 00:10:51 +02:00
Alexis Rouillard b3f7617ed4 Merge pull request #4588 from Gazer/feat/basic-cpu-graph
feat: add customgraph module
2026-07-04 00:09:00 +02:00
Alex f059337beb Merge remote-tracking branch 'origin/mig-F' into refactor/generic-tooltip 2026-07-04 00:06:12 +02:00
Alex 0cd52b46ec Merge remote-tracking branch 'origin/mig-E' into refactor/generic-tooltip 2026-07-04 00:06:12 +02:00
Alex baa6ec0900 Merge remote-tracking branch 'origin/mig-D' into refactor/generic-tooltip 2026-07-04 00:06:12 +02:00
Alex 89167f5a2c Merge remote-tracking branch 'origin/mig-C' into refactor/generic-tooltip 2026-07-04 00:06:12 +02:00
Alex 0f64e42480 Merge remote-tracking branch 'origin/mig-B' into refactor/generic-tooltip 2026-07-04 00:06:12 +02:00
Alexis Rouillard 65effe9053 Merge pull request #4760 from akselaase/aksel.aase/clock-exec
feat(clock): Add action to execute external command
2026-07-04 00:05:52 +02:00
Alex 8dbba448ce refactor(modules): migrate state modules to ALabel tooltip helper
Migrate cpu, cpu_usage, cpu_frequency and memory to the generic
updateLabelAndTooltip/ForState helper so label and tooltip rendering go
through the dedup-aware setters and shared tooltip-format resolution.

Add store-accepting overloads of the helper to ALabel for modules that
build a dynamic fmt argument store (per-core cpu stats). cpu keeps its
tooltip-format-<state> selection via the state overload; the others keep
their existing tooltip-format-only behavior.
2026-07-04 00:05:17 +02:00
Alex 014c95a9fe refactor(modules): migrate audio/network/power modules to ALabel tooltip helpers
Migrate network, pulseaudio, wireplumber, jack and power_profiles_daemon
to the generic ALabel tooltip helpers:

- jack and power-profiles-daemon use updateLabelAndTooltip() since their
  label/tooltip share a single arg set and a format-string tooltip default.
- network, pulseaudio and wireplumber keep their rich in-module format
  selection but build a single fmt arg store, render the label/tooltip
  through the dedup-aware setLabelMarkup()/setTooltipMarkup() setters and
  resolveTooltipFormat(), preserving their custom fallbacks (label text,
  sink/node description) and visibility handling exactly.
2026-07-04 00:04:47 +02:00
Alexis Rouillard a57058ddab Merge pull request #4722 from ALSchwalm/wifi-state
Add '-<state>' support to wifi config
2026-07-04 00:03:33 +02:00
Alex 86b8dd6b1b refactor: migrate backlight/battery/bluetooth/gps to updateLabelAndTooltip 2026-07-04 00:01:17 +02:00
Alex 836449d253 refactor(modules): use ALabel dedup tooltip helper in simpleclock, systemd-failed-units, upower
simpleclock: migrate to updateLabelAndTooltip (label + tooltip share the
localtime arg; default tooltip falls back to the label format).
systemd-failed-units: use setLabelMarkup/setTooltipMarkup dedup setters;
label/tooltip formats are selected by different conditions so the combined
helper does not apply.
upower: use setLabelMarkup for the label; tooltip stays a custom GTK widget.
mpd already uses the dedup setters (label/tooltip use different truncated
args), so no change.
2026-07-04 00:00:17 +02:00
Alex db4941efe7 refactor(tooltip): migrate modules to ALabel generic tooltip helper
Migrate idle_inhibitor, sway/language, sway/scratchpad,
hyprland/language and hyprland/window to the shared label/tooltip
setters. idle_inhibitor uses updateLabelAndTooltipForState; the others
keep their custom label/tooltip resolution (trim, precomputed tooltip,
rewriteString, per-lang/variant formats) and switch to the dedup-aware
setLabelMarkup/setTooltipMarkup.
2026-07-04 00:00:15 +02:00
Alex cdfd3cb08a refactor: migrate complex-tooltip modules to ALabel dedup setters
Switch direct label_.set_markup/set_tooltip_markup calls to the
deduplicating setLabelMarkup/setTooltipMarkup helpers in clock, custom
and mpris, keeping their custom tooltip construction logic intact.
taskbar is left untouched: its tooltip lives on per-task Gtk::Button,
not on an ALabel label_.
2026-07-03 23:59:55 +02:00
Alexis Rouillard 0fd8a4c30f Merge pull request #4366 from winkelnp/wp-bluetooth
`format-bluetooth` and `device.form-factor` support for wireplumber module
2026-07-03 23:59:05 +02:00
Alexis Rouillard 5af4b83d19 Merge pull request #4861 from eonphi/master
feat(hyprland/workspaces): grouping icons
2026-07-03 23:59:01 +02:00
Alexis Rouillard f9ed55d0ab Merge pull request #3421 from Ape/pr/pulseaudio_mapping
pulseaudio: Add 'sink-mapping' config option
2026-07-03 23:58:57 +02:00
Alex 66ecba4e99 idle_inhibitor: apply clang-format 2026-07-03 23:58:46 +02:00
Alex a91dcbe595 Merge remote-tracking branch 'origin/master' into refactor/generic-tooltip
# Conflicts:
#	src/modules/disk.cpp
2026-07-03 23:56:44 +02:00
Alex 5c1dd9e9f1 Merge remote-tracking branch 'origin/master' into pr-5110 2026-07-03 23:56:28 +02:00
Alex 331fe0e963 Fix clang-format 2026-07-03 23:52:24 +02:00
Alex 885ced58fc Fix clang-format 2026-07-03 23:52:13 +02:00
Alex 15e0c9cb1c Merge remote-tracking branch 'origin/master' into pr-4760 2026-07-03 23:52:11 +02:00
Alex c4c9345fef Fix clang-format 2026-07-03 23:52:02 +02:00
Alex 846ce3e1a4 Merge remote-tracking branch 'origin/master' into pr-5069 2026-07-03 23:51:53 +02:00
Alex 913cde915a Fix clang-format 2026-07-03 23:51:45 +02:00
Alex 67cbeb35b4 Merge remote-tracking branch 'origin/master' into pr-4334 2026-07-03 23:51:40 +02:00
Alex c33b2f659c Merge remote-tracking branch 'origin/master' into pr-5017
# Conflicts:
#	man/waybar-river-tags.5.scd
2026-07-03 23:51:34 +02:00
Alex 733c688ded Merge remote-tracking branch 'origin/master' into pr-4588 2026-07-03 23:51:30 +02:00
Alex 8fafb53177 Merge remote-tracking branch 'origin/master' into pr-4712 2026-07-03 23:51:23 +02:00
Alex e792ed8f85 Merge remote-tracking branch 'origin/master' into pr-4861
# Conflicts:
#	src/modules/hyprland/workspace.cpp
2026-07-03 23:51:19 +02:00
Alex b13d76d346 Merge remote-tracking branch 'origin/master' into pr-4366
# Conflicts:
#	src/modules/wireplumber.cpp
2026-07-03 23:51:16 +02:00
Alex 5ad0eb3163 Merge remote-tracking branch 'origin/master' into pr-3421
# Conflicts:
#	src/modules/pulseaudio.cpp
2026-07-03 23:51:13 +02:00
Alex 2ac1f1c437 Fix clang-format 2026-07-03 23:51:10 +02:00
Alex d5f5a5b4df Merge remote-tracking branch 'origin/master' into pr-4722 2026-07-03 23:50:57 +02:00
Alex 665b07209e Merge remote-tracking branch 'origin/master' into pr-4060
# Conflicts:
#	man/waybar.5.scd.in
2026-07-03 23:50:55 +02:00
Alex b9d5b3b4a2 Merge remote-tracking branch 'origin/master' into pr-5041
# Conflicts:
#	src/modules/niri/workspaces.cpp
2026-07-03 23:50:53 +02:00
Alex 8eb916ac84 refactor(ALabel): add generic label+tooltip helper to remove duplication
Modules duplicated the same boilerplate to format their label and tooltip
(read tooltip-format, fmt::format, set_tooltip_markup) across ~29 modules.

Add updateLabelAndTooltip(labelFormat, tooltipDefault, args...) and its
state-aware variant updateLabelAndTooltipForState(state, ...) to ALabel: they
build a single fmt arg store, render the label and the resolved tooltip format
(tooltip-format[-state] or default) through the dedup-aware setters, honoring
the tooltip toggle. resolveTooltipFormat() centralizes the format resolution.

Migrate temperature and disk as the first adopters.
2026-07-03 23:46:31 +02:00
Alexis Rouillard 33a21705ee Merge pull request #1870 from nova-nowiz/feat/brightness-percent-exp
feat(backlight): added percentexp as format option
2026-07-03 23:43:24 +02:00
Alexis Rouillard e2056db932 Merge pull request #4084 from PandorasFox/master
add prefer-album-artist to mpris module
2026-07-03 23:43:11 +02:00
Alexis Rouillard 7ba2a7694d Merge pull request #4607 from zspher/feat-image-path
feat(image): expand `~` & `$HOME` in path
2026-07-03 23:43:00 +02:00
Alexis Rouillard 960a2069ee Merge pull request #4762 from szbmrk/feat-multiple-disks
feat(disk)!: handle multiple disks at once
2026-07-03 23:42:55 +02:00
Alexis Rouillard 325ee9e7f7 Merge pull request #4826 from AntGarSil/agd/ignore-tray-applets
feat (tray): Ignore Tray items by service name and when dbus properties load
2026-07-03 23:42:51 +02:00
Alexis Rouillard 4b9284ffd0 Merge pull request #3925 from casKd-dev/dwl/hide-vacant
dwl/tags: add hide-vacant option
2026-07-03 23:42:17 +02:00
Alex 1afb1a5229 docs(man): document options implemented but missing from man pages
Sync man pages with the code for options that were read but undocumented:
- sway/workspaces: ignore-workspaces
- niri/workspaces: sort-by-number, hide-empty
- wayfire/workspaces: disable-scroll, enable-bar-scroll
- river/tags: set-tags, toggle-tags
- hyprland/workspaces: persistent-workspaces (formalized)
- hyprland/window: fallback, tooltip-format
- cpu: format-<state>, tooltip-format, tooltip-format-<state>
- pulseaudio/slider: ignored-sinks
- hyprland/niri/mango language: format-<lang>-<variant> (+ hyprland tooltip variant)
2026-07-03 23:35:12 +02:00
Alexis Rouillard 2108c65bb2 Merge pull request #4255 from mBornand/wp_scales
wireplumber: add more volume formats
2026-07-03 23:20:41 +02:00
Alexis Rouillard 8587474821 Merge pull request #4896 from nickjj/feature-clock-calendar-css-classes
feat (clock): Add support for optional CSS classes to the clock module's calendar tooltip
2026-07-03 23:20:39 +02:00
Alexis Rouillard a4cba38c81 Merge pull request #3926 from casKd-dev/dwl/hide-title
dwl/window: allow hiding of title when either inactive or empty
2026-07-03 23:20:36 +02:00
Alexis Rouillard c7ca5eefeb Merge pull request #3706 from ico277/master
Add an option to search for name in hwmon directory for the temperature module
2026-07-03 23:20:34 +02:00
Alexis Rouillard 87230211e0 Merge pull request #4907 from jaypikay/master
hostname dependend module
2026-07-03 23:12:46 +02:00
Alexis Rouillard d2a082933a Merge pull request #4908 from 7FM/master
pulseaudio: add target option to control source (microphone) volume
2026-07-03 23:12:36 +02:00
Alexis Rouillard bd7a62b8ee Merge pull request #5054 from IVSOP/feat/cpu-icons-auto
feat(cpu): add {icons} format replacement for all per-core icons
2026-07-03 23:11:19 +02:00
Alexis Rouillard 0be7bbb730 Merge pull request #4532 from mbroemme/2025-wlr-taskbar-add-justify-option
feat(wlr/taskbar): add `justify` option to control horizontal alignment of the module box
2026-07-03 23:07:39 +02:00
Alexis Rouillard 41191802f1 Merge pull request #4994 from GlassyBridge/master
feat (hyprland/workspaces): support for state specific icon overrides
2026-07-03 23:03:05 +02:00
Alexis Rouillard 471d7563b3 Merge pull request #4530 from martinetd/niri-column
[RFC] niri/window: add col/max_col parameters
2026-07-03 23:01:36 +02:00
Alexis Rouillard 0791ddd7d1 Merge pull request #4977 from Diax170/drawer-state
Add CSS selector for expanded drawers
2026-07-03 23:01:26 +02:00
Alexis Rouillard fffb4b3159 Merge pull request #5012 from yihangwu539-png/feat/language-tooltip
feat(language): add tooltip support with configurable tooltip-format
2026-07-03 23:00:06 +02:00
Alexis Rouillard 2f6720c053 Merge pull request #4981 from xiyori/max-windows
Add max-windows option to hyprland/workspaces
2026-07-03 22:58:37 +02:00
Alexis Rouillard 8c4787bd88 Merge pull request #4529 from dimti/hyprland-language-class
Add css class to hyprland language module of short_name language code
2026-07-03 22:58:31 +02:00
Alexis Rouillard 45b3b0aadb Merge pull request #4193 from burbschat/output-classes
Add CSS classes for sway workspace indicators depending on associated output
2026-07-03 22:58:25 +02:00
Alexis Rouillard 67500c4f1d Merge pull request #4950 from Skyfrei/4655
feature: add on-click-copy clipboard functionality
2026-07-03 22:55:16 +02:00
Alexis Rouillard 825ce43088 Merge pull request #5073 from ar-turek/persistent_workspaces_by_monitor_make_and_model
[feat] `sway/workspaces`: refer to outputs by their monitor identifier in `persistent-workspaces`
2026-07-03 22:53:48 +02:00
Alexis Rouillard 3033e15868 Merge pull request #4958 from v-dermichev/feature/workspace-name-css-class
feat (hyprland/workspaces): expose workspace name as CSS class
2026-07-03 22:52:06 +02:00
Alexis Rouillard 168794b44b Merge pull request #4986 from maltherd/master
Add "ignore-workspaces" option to sway/workspaces module
2026-07-03 22:50:34 +02:00
Alexis Rouillard dca564ca52 Merge pull request #4463 from diego-velez/master
feat: Show Niri/window component when workspace is empty
2026-07-03 22:50:25 +02:00
Alexis Rouillard dd69400676 Merge pull request #5047 from awused/mpd-idle
mpd: Implement idle events for Playing state
2026-07-03 22:49:22 +02:00
Alexis Rouillard 9dfdedfd3c Merge pull request #5034 from Agnibha007/fix-tooltip-pango
feat: support Pango markup in remaining tooltips
2026-07-03 22:48:39 +02:00
Alexis Rouillard cb206a60f8 Merge pull request #4134 from Roc25/hide-active
Add hide-active
2026-07-03 22:47:15 +02:00
Alexis Rouillard 21fbb96446 Merge pull request #4683 from RaphaelKimmig/master
feat: allow sorting niri workspaces
2026-07-03 22:44:26 +02:00
Alexis Rouillard 6c87b1d9da Merge pull request #5078 from DreamMaoMao/mango
feat: add mango modules: workspace,language,keymode,window,layout
2026-07-03 22:44:04 +02:00
Alexis Rouillard 0094af54bf Merge pull request #4841 from Cprakhar/feat/first-day-of-week-calendar
feat: add support for configurable first day of the week in calendar
2026-07-03 22:43:11 +02:00
Alexis Rouillard 6e28b4ae54 Merge pull request #3436 from lilly-lizard/river-tags-monitor-focus
river/tags: Allow river tags to be styled based on monitor focus
2026-07-03 22:42:34 +02:00
Alexis Rouillard c354b9b568 Merge pull request #5015 from yui-915/compact-bandwidth
feat: add compact versions of bandwidthBytes to network module
2026-07-03 22:42:22 +02:00
Alex 51a6ac0497 Merge remote-tracking branch 'origin/master' into pr/pulseaudio_mapping
# Conflicts:
#	src/util/audio_backend.cpp
2026-07-03 22:41:19 +02:00
Alex 02c20cf339 Merge remote-tracking branch 'origin/master' into agd/ignore-tray-applets
# Conflicts:
#	include/modules/sni/tray.hpp
#	src/modules/sni/host.cpp
#	src/modules/sni/tray.cpp
2026-07-03 22:40:04 +02:00
Alex aecd254385 Merge remote-tracking branch 'origin/master' into feat-multiple-disks
# Conflicts:
#	src/modules/disk.cpp
2026-07-03 22:39:52 +02:00
Alex 44995bc5fa Merge remote-tracking branch 'origin/master' into pr-4084
# Conflicts:
#	src/modules/mpris/mpris.cpp
2026-07-03 22:39:20 +02:00
Alex a6821e428c Merge remote-tracking branch 'origin/master' into feat/brightness-percent-exp
# Conflicts:
#	src/modules/backlight.cpp
2026-07-03 22:39:10 +02:00
Alex 8101454819 Merge remote-tracking branch 'origin/master' into wp-bluetooth
# Conflicts:
#	src/modules/wireplumber.cpp
2026-07-03 22:38:57 +02:00
Alex ffd5a2eb48 Merge remote-tracking branch 'origin/master' into feat-image-path
# Conflicts:
#	src/modules/image.cpp
2026-07-03 22:38:51 +02:00
Alex e2d4f3c69e Merge remote-tracking branch 'origin/master' into group-reveal-by-default
# Conflicts:
#	src/group.cpp
2026-07-03 22:38:46 +02:00
Alex 517f45b01a Merge remote-tracking branch 'origin/master' into idle_inhibitor-activity-detection
# Conflicts:
#	include/client.hpp
2026-07-03 22:38:28 +02:00
Alexis Rouillard 7858d998ee Merge pull request #4993 from cebem1nt/master
feature: group - add reveal-delay config option for drawer
2026-07-03 22:35:29 +02:00
Alexis Rouillard 3cde8607a6 Merge pull request #3994 from jeevithakannan2/ppd-split-driver
power-profiles-daemon: split driver into cpu driver and platform driver
2026-07-03 22:33:20 +02:00
Alexis Rouillard a40c7e7035 Merge pull request #4719 from oisanjaya/encode_icon_inside_label
Encode icon inside label
2026-07-03 22:32:42 +02:00
Alexis Rouillard 421e85239b Merge pull request #4761 from Benji-clm/smooth-power
feat(battery): add option to smooth power for more stable time_remaining estimates
2026-07-03 22:32:31 +02:00
Alexis Rouillard 93ecfb9eb1 Merge pull request #5126 from iamcheyan/feat/wlr-taskbar-bar-css-states
feat(wlr/taskbar): expose active toplevel state on the bar window
2026-07-03 22:30:26 +02:00
Alex 21d222e8f7 Merge remote-tracking branch 'origin/master' into 7FM/master
# Conflicts:
#	include/modules/pulseaudio_slider.hpp
#	include/util/audio_backend.hpp
#	man/waybar-pulseaudio-slider.5.scd
#	src/modules/pulseaudio_slider.cpp
#	src/util/audio_backend.cpp
2026-07-03 22:29:33 +02:00
Alex d1a457c57a Merge remote-tracking branch 'origin/master' into ico277/master
# Conflicts:
#	src/modules/temperature.cpp
2026-07-03 22:27:43 +02:00
Alex 3cfa2e2c5d Merge remote-tracking branch 'origin/master' into J0hannes101/master
# Conflicts:
#	src/AModule.cpp
2026-07-03 22:27:24 +02:00
Alexis Rouillard 8b14336b75 Merge pull request #4883 from Cprakhar/feat/custom-format-icons-keyboard
feat: add custom format-icons for each key in keyboard-state module
2026-07-03 22:27:24 +02:00
Alexis Rouillard caa65e9f7b Merge pull request #4747 from cartok/feat/sway-workspaces-format-for-negative-indices
feat(sway/workspaces): add format for negative indices
2026-07-03 22:27:14 +02:00
Alex aea5b73fdd Merge remote-tracking branch 'origin/master' into theoratkin/master
# Conflicts:
#	include/modules/mpd/state.inl.hpp
2026-07-03 22:26:54 +02:00
Alex 00a3e490c6 Merge remote-tracking branch 'origin/master' into dwl/hide-title
# Conflicts:
#	include/modules/dwl/window.hpp
#	src/modules/dwl/window.cpp
2026-07-03 22:26:42 +02:00
Alex 78aafde6fd Merge remote-tracking branch 'origin/master' into 2025-wlr-taskbar-add-justify-option
# Conflicts:
#	man/waybar-wlr-taskbar.5.scd
2026-07-03 22:26:28 +02:00
Alex 3c9b019d51 Merge remote-tracking branch 'origin/master' into dwl/hide-vacant
# Conflicts:
#	include/modules/dwl/tags.hpp
#	src/modules/dwl/tags.cpp
2026-07-03 22:26:16 +02:00
Alexis Rouillard 596facca2c Merge branch 'master' into master 2026-07-03 22:22:48 +02:00
Alexis Rouillard 72c73a4c92 Merge branch 'master' into add-static-river-tag-classes 2026-07-03 22:22:46 +02:00
Alexis Rouillard 7c085c89a9 Merge branch 'master' into master 2026-07-03 22:22:43 +02:00
Alexis Rouillard a63df55dd4 Merge branch 'master' into feat/bt-multiple-batteries 2026-07-03 22:22:38 +02:00
Alexis Rouillard 86d8d33ec3 Merge branch 'master' into wifi-state 2026-07-03 22:22:35 +02:00
Alex 22f6e3c83c mpd: adapt Playing::on_io to post-refactor Context API
queryMPD() was removed from Context by the master-side dedup refactor
(perf(mpd): avoid duplicate playing-state updates). fetchState() is
already called earlier in on_io and emit() updates the GUI, so the
redundant queryMPD() call is dropped, matching the identical resolution
applied to Playing::on_timer on master.
2026-07-03 22:22:35 +02:00
Alexis Rouillard 405ffe3de6 Merge branch 'master' into feat/cpu-icons-auto 2026-07-03 22:22:32 +02:00
Alexis Rouillard a5d510ff8a Merge branch 'master' into enable-bar-scroll 2026-07-03 22:22:30 +02:00
Alexis Rouillard 7261d7d78f Merge branch 'master' into master 2026-07-03 22:22:28 +02:00
Alexis Rouillard 5ff20943a9 Merge branch 'master' into hide-active 2026-07-03 22:22:24 +02:00
Alexis Rouillard 93db40b829 Merge branch 'master' into master 2026-07-03 22:22:21 +02:00
Alexis Rouillard 22441ea8f6 Merge branch 'master' into master 2026-07-03 22:22:19 +02:00
Alexis Rouillard 4700f2b423 Merge branch 'master' into max-windows 2026-07-03 22:22:16 +02:00
Alexis Rouillard 96696d0ec4 Merge branch 'master' into feat/language-tooltip 2026-07-03 22:22:14 +02:00
Alexis Rouillard 8ec7bd0939 Merge branch 'master' into hyprland-language-class 2026-07-03 22:22:11 +02:00
Alexis Rouillard 54d28d3949 Merge branch 'master' into persistent_workspaces_by_monitor_make_and_model 2026-07-03 22:22:09 +02:00
Alexis Rouillard 315dc106f5 Merge branch 'master' into master 2026-07-03 22:22:07 +02:00
Alexis Rouillard bbf6af23e7 Merge branch 'master' into output-classes 2026-07-03 22:22:04 +02:00
Alexis Rouillard 6506228061 Merge branch 'master' into niri-column 2026-07-03 22:22:02 +02:00
Alexis Rouillard 17b9dc2a94 Merge branch 'master' into master 2026-07-03 22:22:00 +02:00
Alexis Rouillard d618b4125d Merge branch 'master' into fix-tooltip-pango 2026-07-03 22:21:57 +02:00
Alexis Rouillard 57689fe96b Merge branch 'master' into feature/workspace-name-css-class 2026-07-03 22:21:54 +02:00
Alexis Rouillard e7d19ad60f Merge branch 'master' into 4655 2026-07-03 22:21:52 +02:00
Alexis Rouillard 926f183184 Merge branch 'master' into groups-add-empty-css-class 2026-07-03 22:21:49 +02:00
Alexis Rouillard 85e4725d02 Merge branch 'master' into drawer-state 2026-07-03 22:21:47 +02:00
Alexis Rouillard 2a91a7e31f Merge branch 'master' into master 2026-07-03 22:21:44 +02:00
Alexis Rouillard 2916a0afa0 Merge branch 'master' into aksel.aase/clock-exec 2026-07-03 22:21:41 +02:00
Alexis Rouillard b7faea5884 Merge branch 'master' into clock/configurable-week-numbering 2026-07-03 22:21:38 +02:00
Alexis Rouillard 13f59a38c7 Merge branch 'master' into feat/first-day-of-week-calendar 2026-07-03 22:21:36 +02:00
Alex cd2b1802cb Apply clang-format to wireplumber module 2026-07-03 22:21:07 +02:00
Alex 573ad779ba Apply clang-format to clock.cpp 2026-07-03 22:21:03 +02:00
Alex ce5f6e77c1 Merge remote-tracking branch 'origin/master' into wp_scales 2026-07-03 22:20:52 +02:00
Alex 975fa9a54e Merge remote-tracking branch 'origin/master' into feature-clock-calendar-css-classes 2026-07-03 22:20:49 +02:00
Alex 03da065e87 Merge remote-tracking branch 'origin/master' into mpd-idle 2026-07-03 22:20:41 +02:00
Alexis Rouillard ba90205986 Merge pull request #4723 from kaosuran/features/temperature-hwmon-by-name
feat(temperature): add hwmon-name option for stable sensor selection
2026-07-03 22:17:50 +02:00
Alexis Rouillard c3f4c0077b Merge pull request #4114 from Duncaen/cpu-loadavg
cpu: add load{1,5,15} format variables
2026-07-03 22:17:46 +02:00
Alexis Rouillard 145e1f26e1 Merge branch 'master' into feat/basic-cpu-graph 2026-07-03 22:17:32 +02:00
Alexis Rouillard ed688659f4 Merge branch 'master' into mango 2026-07-03 22:17:29 +02:00
Prakhar Chhalotre 16430b44a9 Merge branch 'master' into pr-4841 2026-07-04 01:42:43 +05:30
Alex fe0e1c139b power-profiles-daemon: preserve backward compatibility for driver
Keep the {driver} placeholder working and support older
power-profiles-daemon versions that only expose a single Driver DBus
property, while still reading the new CpuDriver/PlatformDriver fields.

- Read the legacy Driver property and fall back to it when CpuDriver or
  PlatformDriver are missing (older daemon).
- Derive {driver} from CpuDriver when the daemon no longer exposes
  Driver (recent daemon), so existing configs using {driver} keep
  working.
- Align the default tooltip-format in the constructor with the
  documented default.
2026-07-03 22:11:51 +02:00
Alex 31035890e6 Merge remote-tracking branch 'origin/master' into feat/sway-workspaces-format-for-negative-indices
# Conflicts:
#	src/modules/sway/workspaces.cpp
2026-07-03 22:11:45 +02:00
Alex b7e5805f4b Merge remote-tracking branch 'origin/master' into RaphaelKimmig/master
# Conflicts:
#	include/modules/niri/workspaces.hpp
2026-07-03 22:11:34 +02:00
Alexis Rouillard d7fed82cd8 Merge branch 'master' into feat-customizable-icon-thresholds 2026-07-03 22:11:16 +02:00
Alexis Rouillard 69c09800d5 Merge branch 'master' into feat/custom-format-icons-keyboard 2026-07-03 22:11:13 +02:00
Alexis Rouillard 396e8b714f Merge branch 'master' into mpd-idle 2026-07-03 22:11:10 +02:00
Alexis Rouillard da91ca2457 Merge branch 'master' into smooth-power 2026-07-03 22:11:06 +02:00
Alexis Rouillard 96adf0ffe2 Merge branch 'master' into features/temperature-hwmon-by-name 2026-07-03 22:11:04 +02:00
Alexis Rouillard 9e9e16b7e4 Merge branch 'master' into cpu-loadavg 2026-07-03 22:11:02 +02:00
Alexis Rouillard fe653fef9d Merge branch 'master' into compact-bandwidth 2026-07-03 22:10:59 +02:00
Alexis Rouillard 35295b156b Merge branch 'master' into wp_scales 2026-07-03 22:10:57 +02:00
Alex 6dc168c4f2 river/tags: fix memory leak of output_status_/seat_status_
The constructor created output_status_ and seat_status_ without adding any
listener, then handle_show() recreated both (leaking the constructor-created
objects) before destroying status_manager_. Additionally seat_status_ was
never destroyed in ~Tags().

Create these objects lazily (with listeners) only in handle_show(), and
destroy seat_status_ in the destructor.
2026-07-03 22:10:46 +02:00
Alex 68097dacd4 Merge remote-tracking branch 'origin/master' into feat/wlr-taskbar-bar-css-states 2026-07-03 22:10:26 +02:00
Alex c5cefb5520 AIconLabel: make regex static and initialize label_contains_icon
Compile the icon-extraction std::regex objects once via static locals
instead of rebuilding them on every update() of every AIconLabel-derived
module (custom, mpris, ...). Also default-initialize label_contains_icon
to false at its declaration.
2026-07-03 22:10:16 +02:00
Alex e736a934ba fix(clock): correct parentheses in fmt::format for calendar tooltip color 2026-07-03 22:10:13 +02:00
Alex 31dfd44f0b Merge remote-tracking branch 'origin/master' into encode_icon_inside_label 2026-07-03 22:09:58 +02:00
Alex b9c82f5dd3 Merge remote-tracking branch 'origin/master' into ppd-split-driver 2026-07-03 22:09:51 +02:00
Alex 6cf6157048 Merge remote-tracking branch 'origin/master' into river-tags-monitor-focus 2026-07-03 22:09:45 +02:00
Alexis Rouillard fbb9a7eccd Merge pull request #5045 from finitemonkey/master
Additional options when muted for the PulseAudio-Slider module
2026-07-03 22:09:42 +02:00
Alex 8aa8de90d6 Merge remote-tracking branch 'origin/master' into feature-clock-calendar-css-classes 2026-07-03 22:09:30 +02:00
Alexis Rouillard 7df62686bf Merge pull request #4724 from hallvardnmbu/master
Allow tooltip formatting the CPU module.
2026-07-03 22:04:34 +02:00
Alexis Rouillard 9735efec5d Merge pull request #4353 from shibjyoti555/feature/if-empty
added a "fallback" option to hyprland/window when no window in focus
2026-07-03 22:04:05 +02:00
Alexis Rouillard 8e64ac59df Merge pull request #5131 from rmt/master
Force layer-shell commit after configure
2026-07-03 22:02:56 +02:00
Alexis Rouillard 0d19d1e5c6 Merge pull request #4524 from Gskartwii/niri-language-hide
Hide niri/language widget if formatted string is empty
2026-07-03 22:02:05 +02:00
Alexis Rouillard 535250111e Merge pull request #4951 from B2krobbery/fix-height-type-warning
bar: warn on invalid height type in configuration
2026-07-03 22:01:28 +02:00
Alex 4bf82b88a0 style(pulseaudio): clang-format audio_backend helpers 2026-07-03 22:01:20 +02:00
Alexis Rouillard 1ef0204b9a Merge pull request #5139 from ccjmne/master
Document light and dark stylesheets in `man`ual
2026-07-03 22:01:06 +02:00
Alexis Rouillard d3d036531c Merge pull request #4035 from fkobi/patch-1
meson_options: improve sndio's description
2026-07-03 22:00:14 +02:00
Alex f837615de2 Merge remote-tracking branch 'origin/master' into finitemonkey/master 2026-07-03 21:59:43 +02:00
Alexis Rouillard 3ba23f7b53 Merge pull request #5148 from JangXa/master
Fallback formatting when format-paused is missing uses ALabel
2026-07-03 21:58:18 +02:00
Alexis Rouillard 1f0be19245 Merge pull request #4930 from B2krobbery/patch-1
docs: clarify global tooltip styling behavior
2026-07-03 21:54:37 +02:00
Alexis Rouillard 8d27a05b9b Merge pull request #4983 from B2krobbery/refactor-bool-cleanup
Refactor: simplify conditional expressions in Hyprland workspace module
2026-07-03 21:54:33 +02:00
Alexis Rouillard 63febd4f12 Merge pull request #5111 from SaveTheRbtz/codex-label-update-hotpath
perf(label): skip redundant markup updates
2026-07-03 21:54:29 +02:00
Alex 25e57bc625 fix(hyprland/window): use 2-space indentation for fallback block (clang-format) 2026-07-03 21:51:20 +02:00
Alex 26c834a59b cpu: respect tooltip-format-<state> without overwriting it
The tooltip selection block redundantly reassigned tooltip_format to
config_["tooltip-format"], overwriting the state-specific format (and
emptying it when no global tooltip-format was set). A lone
tooltip-format-<state> therefore produced an empty tooltip.

Consolidate the tooltip handling into a single block that prefers
tooltip-format-<state>, falls back to the global tooltip-format, then to
the computed default tooltip, reusing the existing format arg store.
2026-07-03 21:51:07 +02:00
Alexis Rouillard c5ecfa4ce7 Merge pull request #5060 from EzequielColungaAcha/fix-wlr-taskbar-title-ellipsize
fix(wlr/taskbar): let task titles shrink within available space
2026-07-03 21:51:06 +02:00
Alexis Rouillard 2d368bb325 Merge pull request #4483 from jaschiu/patch-1
Fix clang build error
2026-07-03 21:50:11 +02:00
Alex cbc70d10f8 pulseaudio_slider: keep zero-on-mute applied on every update while muted
The zero-on-mute display_value = min_ assignment was nested inside the
!previously_muted transition branch, so subsequent updates while still
muted re-displayed the real volume. Apply it on every update as long as
the target is muted, keeping the CSS class add/remove on transitions only.
2026-07-03 21:49:54 +02:00
Alex 1c5ce111e1 Merge remote-tracking branch 'origin/master' into hallvardnmbu/master 2026-07-03 21:49:31 +02:00
Alex 8f7ed880c4 bar: fix indentation of height type warning 2026-07-03 21:49:25 +02:00
Alex 504ebfba62 Merge remote-tracking branch 'origin/master' into finitemonkey/master 2026-07-03 21:49:16 +02:00
Alex 4bba01c258 Merge remote-tracking branch 'origin/master' into feature/if-empty 2026-07-03 21:49:04 +02:00
Alex b88f88b457 Merge remote-tracking branch 'origin/master' into fix-height-type-warning 2026-07-03 21:49:00 +02:00
Alexis Rouillard 6f1d9d754e Merge branch 'master' into niri-language-hide 2026-07-03 21:47:57 +02:00
Alexis Rouillard 41dc297e6f Merge branch 'master' into patch-1 2026-07-03 21:47:54 +02:00
Alexis Rouillard 83f924fa8e Merge branch 'master' into patch-1 2026-07-03 21:47:51 +02:00
Alexis Rouillard e852360106 Merge branch 'master' into refactor-bool-cleanup 2026-07-03 21:47:49 +02:00
Alexis Rouillard ac08a7e856 Merge branch 'master' into codex-label-update-hotpath 2026-07-03 21:47:47 +02:00
Alexis Rouillard bd178458e9 Merge pull request #4837 from Cprakhar/fix/format-plugged-gt-format-full
fix: prioritize `plugged` status over `full` when battery is full and plugged in
2026-07-03 21:47:45 +02:00
Alexis Rouillard 4b7786811a Merge branch 'master' into master 2026-07-03 21:47:44 +02:00
Alexis Rouillard 5e9e880339 Merge branch 'master' into master 2026-07-03 21:47:42 +02:00
Alexis Rouillard dae3d1c9a8 Merge branch 'master' into master 2026-07-03 21:47:39 +02:00
Alexis Rouillard 0b50f654ca Merge pull request #5093 from oqlatulesba3/fix/sni-host-proxy-retry
fix(sni): retry Host proxy creation on transient errors
2026-07-03 21:47:39 +02:00
Alexis Rouillard ee9737bf94 Merge pull request #4692 from aaronred345/hyprland-special-workspaces-multi-monitor
fix(hyprland/workspaces) Special Workspace Multi-Monitor Support
2026-07-03 21:46:43 +02:00
Alexis Rouillard 683b704de1 Merge pull request #4649 from OlivierNeyret/FixFormattingPowerMenuXML
Fix indentation formatting in power_menu.xml
2026-07-03 21:45:10 +02:00
Alexis Rouillard f3ba57942a Merge pull request #4672 from Mental-Vortex/master
fix: Return when ID doesn't exist, we only care about the interest list.
2026-07-03 21:44:13 +02:00
Alexis Rouillard 4079c162eb Merge pull request #4957 from 85ae/fix-4955
fix: issue #4955 wrong behaviour of regex for sway/window 'rewrite'
2026-07-03 21:44:07 +02:00
Alexis Rouillard f57decf81c Merge pull request #4984 from Mervius/master
Fix timeout created by most recent gtk-layer-shell release when creating bars for multiple monitors.
2026-07-03 21:43:08 +02:00
Alexis Rouillard bc315b5a6f Merge pull request #5048 from Praczet/fix/hyprland-workspace-taskbar-hover
fix(hyprland): keep workspace hover over taskbar icons
2026-07-03 21:42:50 +02:00
Alexis Rouillard e1efa7473d Merge pull request #4992 from VlkrS/openbsd
fix: USR1/USR2 handling on OpenBSD
2026-07-03 21:41:12 +02:00
Alex 9ac3f9b951 clock: make calendar padding portable across FormatStream char types
std::setfill is templated on the fill char type, which must match the
Glib::ustring FormatStream's char type. That type is wchar_t with
libstdc++/glibmm but char with libc++, so no fixed literal (' ' or L' ')
compiles everywhere. The stream's default fill is already a space, so
drop std::setfill entirely; std::setw/std::left/std::right are
char-type-agnostic and keep the exact padding behavior.
2026-07-03 21:41:10 +02:00
Alex 31a3d7e1bb fix(battery): gate Full-as-Plugged behind full-at-plugged option, drop debug output
Reporting a full battery on AC as 'Plugged' is now opt-in via the
'full-at-plugged' config option (default false), so existing format-full /
tooltip-format-full setups keep working. Also documents the option.
2026-07-03 21:40:41 +02:00
Alex 488a75fada fix(wlr/taskbar): gate title shrink/expand behind 'expand' option
Only stretch buttons and ellipsize titles when the new boolean 'expand'
option is enabled and the bar is horizontal. Restores the previous
content-based sizing as the default and avoids truncating every title
to an ellipsis on vertical bars.
2026-07-03 21:39:48 +02:00
Alex 5181f84648 fix(sni): prevent use-after-free and infinite retry in Host proxy retry
Store the retry timeout connection in a Host member and disconnect it in
~Host, so a Host destroyed within the retry window no longer invokes the
timeout callback on freed memory. Cap the retries (MAX_RETRIES) so a
watcher that never reappears no longer spins forever every 200ms; reset
the counter on success and when the watcher name vanishes.
2026-07-03 21:39:36 +02:00
Alex eff483de1f Merge remote-tracking branch 'origin/master' into fix/format-plugged-gt-format-full 2026-07-03 21:39:32 +02:00
Alexis Rouillard 0d3f393ae8 Merge pull request #5088 from cogwheel/cogwheel/fix-untitled
custom_modules: Fix mediaplayer.py handling untitled track
2026-07-03 21:39:13 +02:00
Alex 806f633318 hyprland/workspaces: refresh locally instead of reloading Hyprland config
Replace the getSocket1JsonReply("reload") call in onSpecialWorkspaceActivated
with the module's normal local refresh path: the state update to
m_activeSpecialWorkspaceName is picked up by the update() render (triggered by
the dp.emit() already issued at the end of onEvent), which re-evaluates active
state, icons and CSS classes. This avoids reloading the entire Hyprland config
on every special workspace activation while keeping the special-active icon and
CSS improvements.
2026-07-03 21:38:46 +02:00
Alexis Rouillard f682231bc4 Merge pull request #4744 from Sayrus/sayrus-gh/sway-workspaces-markup
fix(sway/workspaces): escape markup in workspace if enabled
2026-07-03 21:38:22 +02:00
Alex b41e57e35a Merge remote-tracking branch 'origin/master' into fix-wlr-taskbar-title-ellipsize 2026-07-03 21:38:21 +02:00
Alexis Rouillard ed2bafcc51 Merge pull request #4804 from BBaoVanC/fix-appearance-change-css-watching
fix: Watch new CSS files on appearance change
2026-07-03 21:38:13 +02:00
Alex 05dbcb24a0 Merge remote-tracking branch 'origin/master' into fix/sni-host-proxy-retry 2026-07-03 21:38:09 +02:00
Alex 9930c7be8a Merge remote-tracking branch 'origin/master' into hyprland-special-workspaces-multi-monitor 2026-07-03 21:37:55 +02:00
Alex dc017d8fd3 fix(hyprland/workspaces): disconnect hover-check timeout on Workspace destruction
The hover-check Glib::signal_timeout re-arms every 50ms and is not owned by
the Workspace. Since Workspaces are destroyed at runtime, a workspace removed
while its check is armed would let the timeout fire on freed memory (use-after-
free, also touching the destroyed m_button). Add a destructor that calls
stopHoverCheck() to disconnect the source.
2026-07-03 21:32:55 +02:00
Alex 987d5758a6 Merge remote-tracking branch 'origin/master' into fix/hyprland-workspace-taskbar-hover 2026-07-03 21:32:20 +02:00
Alexis Rouillard 544b2e9ba1 Merge branch 'master' into FixFormattingPowerMenuXML 2026-07-03 21:31:01 +02:00
Alexis Rouillard 778ef49efd Merge branch 'master' into sayrus-gh/sway-workspaces-markup 2026-07-03 21:30:59 +02:00
Alexis Rouillard 98e2e34536 Merge branch 'master' into master 2026-07-03 21:30:57 +02:00
Alexis Rouillard 6e24923f6d Merge branch 'master' into patch-1 2026-07-03 21:30:54 +02:00
Alexis Rouillard a02f943d8d Merge branch 'master' into fix-appearance-change-css-watching 2026-07-03 21:30:52 +02:00
Alexis Rouillard 3fdaa8d5f4 Merge branch 'master' into fix-4955 2026-07-03 21:30:49 +02:00
Alexis Rouillard b88ed8aa15 Merge branch 'master' into master 2026-07-03 21:30:46 +02:00
Alexis Rouillard 2f3cae621f Merge branch 'master' into openbsd 2026-07-03 21:30:44 +02:00
Alexis Rouillard 35d0c59bd9 Merge branch 'master' into cogwheel/fix-untitled 2026-07-03 21:30:41 +02:00
Alexis Rouillard 8b5f5b3aea Merge pull request #5155 from Alexays/fix/hyprland-language-keyboard-name-commas
fix(hyprland/language): handle commas in keyboard-name config
2026-07-03 21:23:06 +02:00
Alexis Rouillard 1f2394aa78 Merge pull request #3698 from vpcano/hyprland-language-tooltip
Add tooltip functionality on hyprland/language module
2026-07-03 21:20:16 +02:00
Alexis Rouillard 2ee3817b7a Merge pull request #4383 from stkth/stkth/fix-4241
Fix  bug #4241
2026-07-03 21:18:35 +02:00
Alexis Rouillard 093dbe7392 Merge pull request #2965 from jramrath/hyprland-window_no_title_from_special
hyprland/window: fix title not showing for windows in special workspaces
2026-07-03 21:18:32 +02:00
Alexis Rouillard 62cbfabeb8 Merge pull request #5009 from EvansJahja/charon77/battery_fix
battery: guard _exists = true assignment inside if condition
2026-07-03 21:18:29 +02:00
Alex 6793b97eed fix(hyprland/language): handle commas in keyboard-name config
The active-layout event payload is `KEYBOARDNAME,LAYOUT`, and kbName was
taken as everything before the first comma. Keyboard names reported by
Hyprland can themselves contain commas (e.g. some MSI keyboards), so a
`keyboard-name` configured with a comma never matched the truncated value
and the module stopped updating.

Match the configured keyboard-name as a full prefix (followed by the ','
separator) instead. Layout parsing already handles commas via the
last-comma-before-parenthesis heuristic, so only the keyboard-name filter
needed fixing.

Addresses the keyboard-name case originally reported by @tirsek in #3406.
2026-07-03 21:15:35 +02:00
Alex f158f29acf Merge remote-tracking branch 'origin/master' into hyprland-window_no_title_from_special
# Conflicts:
#	src/modules/hyprland/window.cpp
2026-07-03 21:06:18 +02:00
Alex 16c2ee7041 Merge remote-tracking branch 'origin/master' into charon77/battery_fix
# Conflicts:
#	src/modules/battery.cpp
2026-07-03 21:05:56 +02:00
Alex f739cb7f75 Merge remote-tracking branch 'origin/master' into stkth/fix-4241
# Conflicts:
#	src/modules/systemd_failed_units.cpp
2026-07-03 21:05:32 +02:00
Alex 6de8871882 Merge remote-tracking branch 'origin/master' into hyprland-language-tooltip
# Conflicts:
#	man/waybar-hyprland-language.5.scd
2026-07-03 21:01:53 +02:00
Alexis Rouillard c43f9f25b8 Merge pull request #5039 from mil-ad/fix/sway-language-flag-disappears
fix(sway/language): keep flag visible across hot-plug and unknown layouts
2026-07-03 21:00:52 +02:00
Alexis Rouillard fd81ff299e Merge pull request #4753 from The-Robin-Hood/master
hyprland/workspaces persistent fix and improve cursor handling configuration
2026-07-03 21:00:47 +02:00
Alexis Rouillard 63424a75ae Merge pull request #5064 from Siriusuna/fix/mpd-markup-truncation
fix(mpd): truncate before sanitization and improve tooltip UX
2026-07-03 21:00:03 +02:00
Alexis Rouillard 1143444561 Merge pull request #5061 from buzz/fix/hyprland-workspaces-scroll
fix(hyprland): support custom scroll commands
2026-07-03 20:59:12 +02:00
Alexis Rouillard c8bcbfb057 Merge pull request #5072 from Lege19/master
(Fix) (sway workspace module) persistent workspaces always removed
2026-07-03 20:59:05 +02:00
Alexis Rouillard fce96987e1 Merge pull request #5028 from JeremieRodon/fix/tray-hide-when-empty
fix(tray): hide module when no items are visible
2026-07-03 20:58:33 +02:00
Alexis Rouillard 1058bdf0bd Merge pull request #5062 from buzz/fix/hyprland-workspaces-taskbar-hover
fix(hyprland): replace EventBox with Button for taskbar window items
2026-07-03 20:58:26 +02:00
Alexis Rouillard ae636a1ca8 Merge pull request #4742 from Sayrus/sayrus-gh/sway-workspaces-quotes
fix(sway/workspaces): use number when available to avoid parsing issues
2026-07-03 20:55:14 +02:00
Alexis Rouillard f4648c0fcd Merge pull request #5046 from awused/mpd-duration
fix: mpd specify timer interval in seconds
2026-07-03 20:53:33 +02:00
Alexis Rouillard c77c60e0b6 Merge pull request #5138 from runxiyu/runxiyu
fix(sni): ignore name/pixmap updates for custom icons
2026-07-03 20:52:16 +02:00
Alexis Rouillard 6c6b69b862 Merge pull request #5114 from jaschiu/patch-3
fix(privacy): segfault due to use-after-free from get_children()
2026-07-03 20:52:12 +02:00
Alexis Rouillard 439261f0e3 Merge pull request #5112 from jaschiu/patch-2
fix(niri/workspaces): segfault when setting workspace button markup
2026-07-03 20:52:09 +02:00
Alexis Rouillard 2e0c34af5e Merge branch 'master' into fix/mpd-markup-truncation 2026-07-03 20:45:15 +02:00
Alexis Rouillard 8bedd8443d Merge branch 'master' into fix/sway-language-flag-disappears 2026-07-03 20:45:13 +02:00
Alexis Rouillard dfe2d80496 Merge branch 'master' into fix/tray-hide-when-empty 2026-07-03 20:45:10 +02:00
Alexis Rouillard 279aa67206 Merge branch 'master' into fix/hyprland-workspaces-scroll 2026-07-03 20:45:08 +02:00
Alexis Rouillard 5fce3926e0 Merge branch 'master' into master 2026-07-03 20:45:06 +02:00
Alexis Rouillard 9b016d8158 Merge branch 'master' into fix/hyprland-workspaces-taskbar-hover 2026-07-03 20:45:03 +02:00
Alexis Rouillard fa79b931e9 Merge branch 'master' into sayrus-gh/sway-workspaces-quotes 2026-07-03 20:45:01 +02:00
Alexis Rouillard 555dcb1776 Merge branch 'master' into master 2026-07-03 20:44:58 +02:00
Alexis Rouillard 0445607fb6 Merge branch 'master' into mpd-duration 2026-07-03 20:44:45 +02:00
Alexis Rouillard 0116808f4b Merge pull request #5079
Fix (memory): Fix tooltip showing incorrect unit and add proper unit initialization
2026-07-03 20:43:20 +02:00
Alexis Rouillard e20864991c Merge pull request #5043
fix(hyprland/workspaces): fix IPC call when using move-to-monitor
2026-07-03 20:43:17 +02:00
Alexis Rouillard 3d64071bab Merge pull request #5108 from SaveTheRbtz/codex-json-parser-buffer
perf(json): parse directly from string buffers
2026-07-03 19:56:40 +02:00
Alexis Rouillard 260a68cf58 Merge pull request #5100 from SaveTheRbtz/codex-mpd-playing-update
perf(mpd): avoid duplicate playing-state updates
2026-07-03 19:55:23 +02:00
Alexis Rouillard c166d98b0d Merge pull request #5010 from Neltharion01/master
Fix connection loop in pulseaudio
2026-07-03 19:54:51 +02:00
Alexis Rouillard 2ece13775e Merge pull request #5058 from typedrifter/fix-cursor-crash
fix(amodule): prevent crash when cursor timeout fires after destruction
2026-07-03 19:54:25 +02:00
Alexis Rouillard 91b0b30aac Merge pull request #5101 from SaveTheRbtz/codex-mpd-song-warning
fix(mpd): avoid false no-song warning when stopped
2026-07-03 19:52:58 +02:00
Alexis Rouillard 9dd927e8da Merge pull request #5076 from muratyalcin-ce/master
Fix (wayfire/backend): Fixed unhandled exception (out_of_range) after…
2026-07-03 19:52:25 +02:00
Alexis Rouillard 6df22ab348 Merge pull request #5154 from JuustKiwi/feature/dpms-suspend
feat(modules): pause background threads when display is asleep
2026-07-03 19:49:56 +02:00
Alexis Rouillard 48e9dd3b64 Merge pull request #5123 from martuscellifaria/master
fix(delayWorker): stop loop for static modules with no exec command
2026-07-03 19:45:40 +02:00
Alexis Rouillard 44ec750b76 Merge pull request #5121 from InetAddress/fix-battery-hotplug-crash-1
fix(battery): prevent crash when removing a hot-plugged device
2026-07-03 19:44:01 +02:00
Ibrahim Issa 4d1a561cdc feat(modules): pause background threads when display is asleep
Currently, modules polling hardware nodes continuously wake up kernel drivers even when the monitor is asleep (e.g., DPMS off), flooding dmesg.

- Adds 'disable-on-sleep' config flag
- Hooks GTK window map/unmap signals to track DPMS state
- Propagates suspend/resume calls to AModule worker threads
2026-07-03 18:44:03 +02:00
Grin 601e84d159 feat(ALabel): allow custom percentage thresholds in format-icons
Previously, format-icons with N icons would divide the 0-100 range
into N equal parts. This made it impossible to define custom ranges
like 0%, 1-69%, 70-100%.

This adds support for an alternative object syntax in format-icons:

"format-icons": [
    { "icon": "▁", "max": 0},
    { "icon": "▅", "max": 69 },
    { "icon": "▇", "max": 100 }
]

The array is iterated in order and the first icon whose max value
is >= the current percentage is returned. If no threshold matches,
the last icon is used as fallback. The existing string array syntax
remains fully supported and unchanged.

Applies to both getIcon overloads, affecting all modules that use
format-icons (battery, pulseaudio, network, backlight, etc.).

Closes #5150
2026-07-01 17:23:51 -03:00
Muhammed Sahin 22ce588783 Fallback formatting when format-paused is missing uses ALabel
With the minimal change the paused state, if format-paused state is not
set, will show the Alabel if the Alabel was active.
2026-06-27 17:18:52 +02:00
Éric NICOLAS 42f2832d21 docs(man): document light and dark stylesheets 2026-06-20 17:42:23 +02:00
Runxi Yu e37fcee35d fix(sni): ignore name/pixmap updates for custom icons 2026-06-19 05:51:00 +00:00
Johannes Haase 47b2ff09b7 feat(cursor): use cursor-shape-v1 cursor names instead of Gdk::CursorType 2026-06-16 23:27:27 +02:00
Robert M. Thomson 942411d7a1 Force layer-shell commit after configure
Assisted-by: pi:gpt-5.5
2026-06-15 10:36:29 +02:00
iamcheyan 14e59daae4 feat(wlr/taskbar): expose active toplevel state on the bar window
Add opt-in bar-css-states option to wlr/taskbar module.

When enabled, the module tracks the active workspace and updates CSS
classes on window#waybar to reflect the current window state:
- toplevel-active: when a window is focused
- toplevel-maximized: when any non-minimized window on active workspace is maximized
- toplevel-minimized: when the active window is minimized
- toplevel-fullscreen: when any non-minimized window on active workspace is fullscreen

This feature requires compositor support for ext-workspace-v1 protocol
to correctly track workspace membership. Without it, classes fall back
to the active application state only.

Also includes active-only option implementation.

Changes:
- Add ext-workspace-v1 protocol support for workspace tracking
- Implement WorkspaceState tracking in Taskbar class
- Add workspace management lifecycle (create/remove/done)
- Add bar CSS class update logic with aggregation
- Add visible() getter to Task class
- Implement active-only task visibility control
- Update man page with documentation and CSS examples

Signed-off-by: iamcheyan <iamcheyan@users.noreply.github.com>
2026-06-13 11:34:29 +09:00
martuscellifaria 0bc664a99e Readded the whitespace from the original code 2026-06-12 09:52:03 +02:00
martuscellifaria eb3669decc Rearranging code in a non-redundant way. 2026-06-12 09:48:44 +02:00
martuscellifaria 4ef828f712 Fixing CPU usage spike on Waybar 2026-06-12 09:14:04 +02:00
InetAddress 61178f7b0e Change error handling to warning
Fixes a bug where removing a hot-plugged device mid-loop causes a filesystem error that turns into a fatal runtime error that crashes waybar entirely, changed to warning instead of runtime error.
2026-06-12 04:38:10 +00:00
Jason Chiu 2d9fee144e Update privacy.cpp 2026-06-09 06:51:14 +00:00
Jason Chiu 2506196274 Update privacy.hpp 2026-06-09 06:49:41 +00:00
Jason Chiu fb829c8a8e ## fix(niri/workspaces): crash (segfault) when setting workspace button markup
### Problem
Waybar segfaults on startup when `niri/workspaces` is the only configured
module (default options). Right after the first `WorkspacesChanged` event:
 
    Thread 1 "waybar" received signal SIGSEGV
    #0 gtk_label_set_markup ()
    #1 waybar::modules::niri::Workspaces::doUpdate () at src/modules/niri/workspaces.cpp:106
 
### Root cause
[doUpdate()](cci:1://file:///home/lj/Downloads/Waybar/src/modules/niri/window.cpp:27:0-90:1) set the label markup via:
 
    static_cast<Gtk::Label*>(button.get_children()[0])->set_markup(name);
 
The button's child *is* a valid `GtkLabel`, but gtkmm's `get_children()`
returns it wrapped as a generic `Gtk::Widget` (confirmed: `dynamic_cast`
to `Gtk::Label*` yields `nullptr`). The unchecked `static_cast` then
performs an invalid downcast, producing a corrupt pointer whose `gobj()`
is garbage (`0x1`), so `gtk_label_set_markup()` dereferences it and crashes.
 
This is reliably triggered when no other module has instantiated a
`Gtk::Label` yet (so the `Gtk::Label` wrapper isn't registered), which is
exactly the case for a `niri/workspaces`-only bar. The same idiom exists in
`sway/workspaces` and `wayfire/workspaces`; it's masked there because typical
configs include other label-using modules.

### Testing
- Reproduced the crash on 0.15.0 / current `master` with a minimal
  `"modules-left": ["niri/workspaces"]` config under niri.
- After the fix: no crash; three workspace buttons render with correct
  names/labels (`niri-workspace-1/2/3`).
2026-06-09 06:20:05 +00:00
Zuodong Yu f7b9d8c123 modules/niri: add {total} format token to workspaces 2026-06-07 17:35:37 +08:00
Alexey Ivanov bef7df8f08 fix(mpd): avoid false no-song warning when stopped 2026-06-05 08:40:49 -07:00
Alexey Ivanov 0cccf0d8b5 perf(mpd): avoid duplicate playing-state updates 2026-06-04 15:23:54 -07:00
Alexey Ivanov 2190871a68 perf(label): skip redundant markup updates 2026-06-04 15:18:43 -07:00
Alexey Ivanov 5cce616d3d perf(json): parse directly from string buffers 2026-06-04 15:03:40 -07:00
oqlatulesba3 3315631012 fix(sni): retry Host proxy creation on transient errors
When Host::proxyReady fails to create the SnWatcher proxy (e.g. because
the Watcher has not finished exporting /StatusNotifierWatcher yet), the
cancellable is left set, causing nameAppeared to early-return on every
subsequent event (see the // TODO marker). The Host is then stuck
without a watcher, and the tray module reports
'No such object path /StatusNotifierWatcher' until the bar is fully
restarted.

Clear the cancellable on non-CANCELLED errors and schedule a single
delayed retry of nameAppeared. The guard `watcher_ != nullptr` skips
the retry if a parallel call already succeeded.

Closes #3468
2026-06-02 10:51:05 +03:00
Matthew Orlando 0af3ca1a4e custom_modules: Fix mediaplayer.py handling untitled track
* Fix crash calling method on None
* Show artist if title is None
2026-05-31 10:01:25 -07:00
DreamMaoMao 3eeffbe389 feat: add mango modules: workspace,language,keymode,window,layout 2026-05-29 12:56:40 +08:00
muratyalcin-ce 5ea824c24c Use unit in config for memory module tooltip 2026-05-28 23:56:52 +03:00
muratyalcin-ce 31a161eb06 Fix (wayfire/backend): Fixed unhandled exception (out_of_range) after no valid outputs in unordered_map 2026-05-27 23:39:39 +03:00
Ansari 2ff2861e88 Merge remote-tracking branch 'upstream/master' 2026-05-27 12:13:51 +05:30
Artur Ciesielski 97acbf30d0 Update man pages for persistent-workspaces in sway/workspaces 2026-05-26 20:38:14 +02:00
Artur Ciesielski 415da7aef4 sway/workspaces: refer to outputs by their monitor identifier (make and model) 2026-05-26 19:39:03 +02:00
jacobs ec2651370e (Fix) (sway workspace module) persistent workspaces always removed 2026-05-26 11:09:45 +01:00
stalker 822071f36e feat(custom): add image 2026-05-25 08:18:12 +04:00
Carlo Teubner 3056bad95a Sway IPC: avoid reinterpret_cast 2026-05-24 08:22:22 +01:00
Carlo Teubner d8848a478b Sway IPC: code cleanups 2026-05-24 08:20:10 +01:00
Carlo Teubner 1bc633fc0e Sway IPC: more robust socket send/receive logic
Handle EINTR/EAGAIN, partial sends, etc.
2026-05-24 06:45:30 +01:00
Carlo Teubner 89b5456cd3 Sway IPC: fix socket path discovery
Ensure to only read actual "sway --get-socketpath" output, not
subsequent garbage in buffer.
2026-05-24 06:41:51 +01:00
Siriusuna 81fd9ee4ab fix(mpd): truncate before sanitization and improve tooltip UX
Fix an issue in the MPD module where text failed to render
when raw tags containing markup characters were truncated
mid-sequence after sanitization,
resulting in fragmented XML entities (such as `&amp;` cut into `&am`).

Resolve this by shifting the order of operations to
truncate the raw strings *before* running `sanitize_string`.
To keep `setLabel` decoupled and clean,
we introduce helper methods (`getArtistStr`, `getAlbumArtistStr`,
`getAlbumStr`, `getTitleStr`) inside the `MPD` class,
matching the architectural style of MPRIS.

Additionally:
- Introduce support for the customizable `ellipsis` config option
  (defaulting to `…`).
- Switch the truncation logic of the MPD module to be
  visual-width-aware using the newly extracted
  `waybar::util::utf8_truncate`.
- Decouple the tooltip text from the main label's truncated text.
  The tooltip now displays un-truncated, complete metadata
  (properly escaped), providing a significantly better user
  experience.
2026-05-22 03:15:26 +08:00
Siriusuna 435c212be1 fix(mpris): escape Pango markup in tooltip
Fix an issue where the mpris module's tooltip failed to render
when track metadata contained unescaped XML/HTML markup characters.

This occurred because
the mpris module formatted raw strings into the tooltip template
before passing the final string directly to `set_tooltip_markup()`,
triggering GTK/Pango parsing warnings and rendering failures.

Resolve this by wrapping tooltip metadata fields
in `Glib::Markup::escape_text` before formatting,
ensuring Pango-compliant strings are always delivered to the GTK
tooltip markup.
2026-05-22 02:59:09 +08:00
Siriusuna f8cc7173ca refactor: extract utf8 truncation logic to utility
Move the UTF-8 visual-width measurement and truncation helper functions
from the mpris module to a common utility
(include/util/utf8_string.hpp and src/util/utf8_string.cpp).

This decouples string truncation and width measurement
from the mpris module, allowing other modules (like mpd) to reuse.
The helper function `utf8_truncate` (formerly `truncate` in mpris)
and `utf8_width` is exported under the `waybar::util` namespace,
while the low-level `measure_and_truncate` (formerly `utf8_truncate`)
is encapsulated in an anonymous namespace in src/util/utf8_string.cpp
to avoid unnecessary API exposure.

No functional changes were made to the mpris module's behavior.
2026-05-22 02:52:40 +08:00
buzz 85163c6847 fix(hyprland/workspaces): replace EventBox with Button for taskbar windows 2026-05-21 18:50:43 +02:00
buzz ea36aab3ba docs(hyprland/workspaces): add on-scroll-up and on-scroll-down to man page 2026-05-21 17:34:07 +02:00
buzz eb3b86c4d8 fix(hyprland/workspaces): support on-scroll-up and on-scroll-down config options
The handleScroll method unconditionally dispatched workspace cycling commands
and never checked for custom on-scroll-up/on-scroll-down config values.

Register the scroll handler when custom scroll commands are configured (even
without enable-bar-scroll), and delegate to AModule::handleScroll so user
commands are executed, matching the pattern used by other modules.
2026-05-21 17:02:39 +02:00
Ezequiel Colunga AchaandCursor fcf1fb798c fix(wlr/taskbar): let task titles shrink within available space
The taskbar currently requests enough width for every task title, which can push right-side modules off-screen when many windows are open. Allow task buttons to share the taskbar allocation and ellipsize title labels instead of forcing the whole module to grow.

This keeps neighboring modules visible while preserving the existing taskbar click actions and formatting behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 15:06:41 -03:00
Francois Lachese 876eaaf70f fix(amodule): prevent crash when cursor timeout fires after destruction 2026-05-20 15:57:38 +02:00
Ivan Ribeiro 6bdafede07 feat(cpu): add {icons} format replacement for all per-core icons
Concatenates all per-core icons into a single {icons} placeholder,
so configs work across machines with different core counts without
manually specifying {icon0}{icon1}...{iconN}.

Closes #4240
2026-05-19 21:21:56 +01:00
finitemonkey 23e635acf8 Add missing documentation for the "target" option of the
PulseAudio-Slider module.
2026-05-17 08:43:46 +01:00
finitemonkey 0351ac8fb2 Additional options when muted for the PulseAudio-Slider module:
- Add "muted" class for styling the muted state.
- Add "zero-on-mute" boolean option to control slider position
  when muted.
- Add "unmute-on-volume-change" boolean option to control whether
  to automatically unmute when the volume changes.
2026-05-17 08:22:17 +01:00
Adam Druzd ff76e96786 fix(hyprland): keep workspace hover over taskbar icons 2026-05-17 09:12:08 +02:00
Desuwa f7001410d2 mpd: ensure emit is called after state changes 2026-05-16 16:32:42 -07:00
Desuwa 546b53d675 mpd: Implement idle events for Playing state 2026-05-16 15:41:09 -07:00
Desuwa 7e2afaa5b3 fix: mpd specify timer interval in seconds 2026-05-16 14:05:17 -07:00
Tom Benham 40c9d92e3d test(hyprland): fix dispatch test 2026-05-16 21:29:43 +02:00
Tom Benham cdb792af41 fix(hyprland/workspaces): fix IPC call when using "move-to-monitor": true 2026-05-16 18:03:09 +02:00
Jack Barnes 51becf2fbc feat(niri/workspaces): Add enable-bar-scroll option 2026-05-16 13:03:53 +08:00
Milad Alizadeh 0ec6d06472 Guard set_current_layout against unknown layout names (wtype, hot-plugged
devices) so transient events stop blanking layout_. Refresh layouts_map_
on "added"/"xkb_keymap" input events and union layouts across all
keyboards so new devices contribute their layouts. Release mutex_ before
the refresh sendCmd to avoid self-deadlock from the synchronous
signal_cmd emit.
2026-05-15 17:29:21 +01:00
Agnibha Mukherjee 3229c2c9fc feat: support Pango markup in remaining tooltips 2026-05-14 11:01:58 +05:30
Peter van Dijk 7105ad9bba waybar-styles.5: add semicolon to css import statement 2026-05-14 01:22:25 +02:00
Antoine Gaudreau Simard 3971bb6c00 feat: add support for multiple batteries in a single bluetooth device 2026-05-13 11:48:42 -04:00
yyyumeniku 2a05edaf6a hyprland/workspaces: add debounce timer to prevent workspace button flicker
When switching between workspaces rapidly (especially empty ones),
Hyprland sends createworkspace and destroyworkspace events slightly
out of order. This causes workspace buttons to render in wrong
positions for a split second before snapping to their correct spots.

Add a 7ms debounce timer that batches workspace events before
calling dp.emit(), preventing the visual glitch.

Fixes #4376
2026-05-12 20:09:13 -04:00
Jérémie Rodon 5c3ec1fd63 fix(tray): hide module when no items are visible
Tray::update() already had logic to hide the module when all child items were passive, but it was only re-run on item add/remove — never on a status change. When the last visible item transitioned to Passive, the item hid itself but the now-empty module remained visible.

Connect Tray to each item's Gtk::EventBox signal_show/signal_hide so update() runs on every visibility transition, and simplify update() to check child->get_visible() directly instead of inspecting the `passive` CSS class. The Tray-level `show-passive-items` read becomes redundant since Item already honours it when deciding its own visibility; remove it along with the now-unused Tray::show_passive_ member.

Fixes: #3721
2026-05-11 22:47:24 +02:00
Mint c5f834b881 Merge branch 'Alexays:master' into master 2026-05-10 11:02:22 -03:00
OliMoli 3a045ff3a1 Merge branch 'Alexays:master' into drawer-state 2026-05-08 06:35:35 +02:00
nonamescm d1d501450d Add static river tags 2026-05-04 03:38:17 -03:00
GlassyBridge114 23df491c53 Merge branch 'Alexays:master' into master 2026-05-03 15:49:10 +03:00
Yui c81a039a55 feat: add compact versions of bandwidthBytes to network module
Adds the following:
- bandwidthDownByesCompact
- bandwidthUpByesCompact

Differences from normal version:
- drop the "/s" suffix
- drop the deciaml part if less than 1MB/s
2026-05-01 22:31:03 +03:00
shiyi b28af75e62 feat(language): add tooltip support with configurable tooltip-format 2026-04-29 23:34:09 +08:00
Skyfrei c8f57d3f2c Merge branch 'master' into 4655 2026-04-29 11:06:25 +02:00
Neltharion a66d6b1596 Do not reconnect if connection was refused
Otherwise, it will try to reconnect recursively
2026-04-29 03:40:06 +03:00
Evans 255f17d13c battery: guard _exists = true assignment inside if condition 2026-04-28 04:53:19 +00:00
lilly-lizard 64e3e48270 Merge branch 'river-tags-monitor-focus' of github.com:lilly-lizard/Waybar-fork into river-tags-monitor-focus 2026-04-26 06:50:05 +12:00
OliMoli 1a88cf0823 Merge branch 'Alexays:master' into drawer-state 2026-04-23 15:47:12 +02:00
OliMoli 10206cf914 Merge branch 'Alexays:master' into drawer-state 2026-04-19 18:16:36 +02:00
GlassyBridge 7e0e99b5aa feat (hyprland/workspaces): add support for state specific icon overrides 2026-04-19 02:21:17 +03:00
cebem1nt ab7bc8de1c feature: group - add reveal-delay config option for drawer 2026-04-18 13:34:29 -03:00
Volker Schlecht 29979e9227 Fix build for systems without SIGRTMIN/SIGRTMAX 2026-04-17 19:35:31 +02:00
Volker Schlecht 729dbbb82e Fix build for systems without SIGRTMIN/SIGRTMAX 2026-04-17 19:33:27 +02:00
Volker Schlecht bd39795f80 Fix build for systems without SIGRTMIN/SIGRTMAX 2026-04-17 19:31:59 +02:00
Volker Schlecht 0241d0eac6 Remove redefinition or SIGRTMIN/SIGRTMAX for OpenBSD 2026-04-17 19:27:39 +02:00
Mervius e2d608e7fd Check if output still is in outputs before doing anything with it 2026-04-15 10:59:03 -05:00
Mervius d9b5d24284 Check if output still is in outputs before doing anything with it 2026-04-15 10:55:49 -05:00
Mervius 09b6df37ca Check if output still is in outputs before doing anything with it 2026-04-15 10:39:21 -05:00
Mervius 8b5b1eebbf Check if output still is in outputs before doing anything with it 2026-04-15 10:35:26 -05:00
lilly-lizard 0ee4fd4aa2 added output class to dwl/mangowm allowing tags to display differently when the output is focused (for multi-monitor setups) 2026-04-14 13:47:53 +12:00
lilly-lizard e9741829d8 Merge branch 'master' into river-tags-monitor-focus 2026-04-14 08:30:00 +12:00
lilly-lizard 1ab1d41e4d removed residual debug logging 2026-04-14 08:25:04 +12:00
Arnaud Gaudard 450b6a785f Add "ignore-workspaces" option to sway/workspaces module 2026-04-12 17:22:19 +02:00
Mervius efd245b552 Fix timeout with multiple monitors 2026-04-10 11:42:23 -05:00
Visal Vijay a8a46d1f1e Refactor: simplify complex conditional expressions in Hyprland workspace module 2026-04-09 19:40:05 +05:30
xiyori 503d99b128 Add max-windows option to hyprland/workspaces 2026-04-09 14:15:56 +03:00
Ricardo Markiewicz 7d965a874f pr fixes 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz 132ca3ac45 fix: datapoints should be positive 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz dbf1cfb0f1 fix: typo reading datapoints 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz 283515901e fix: id read and name set 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz 01c6ebdf9e feat: update man pages 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz dfc26364e1 feat: use the foreground color for cleaner look 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz 4d6354af48 feat: add gauge and stacked bar graph type 2026-04-08 23:36:47 +02:00
Ricardo Markiewicz efadb82c56 feat: basic line graph component and cpu graph module 2026-04-08 23:36:47 +02:00
OliMoli 7feb23c3dd Update manpage to mention the .expanded CSS class 2026-04-07 16:29:59 +02:00
OliMoli 66cb828bf8 Add CSS selector for expanded drawers 2026-04-07 14:02:10 +02:00
vdermichev 2c2901e8f7 hyprland/workspaces: expose workspace name as CSS class
Enable per-workspace styling by adding the workspace name as a
CSS class to each workspace button. The name is sanitized to a
valid CSS identifier: lowercased, non-alphanumeric characters
collapsed to hyphens, digit-leading names prefixed with "ws-".

Examples: "IDE" → .ide, "special:telegram" → .telegram, "1" → .ws-1

Allows users to style individual workspaces:
  #workspaces button.telegram { color: #54a0e0; }
2026-04-05 11:44:56 +02:00
DreamMaoMao 34e9f2580b fix: Correct the error in converting network speed units 2026-04-01 12:12:27 +02:00
g_goessel aa3d82a1c0 Add missing rx/tx_bitrate reset 2026-04-01 12:12:04 +02:00
g_goessel 39656deab3 feat: Add wifi network link bitrate value
In order to obtain the RX and TX bitrates from the kernel netlink we need to open a second info socket to get the station data. The values are formatted like "1.9Gb/s" and then exposed as `rx_bitrate` and `tx_bitrate`.
This code is closely following the `iw` implementation.
2026-04-01 12:12:04 +02:00
hecate cantus c866c4cd24 Merge branch 'master' into master 2026-03-30 14:25:41 -07:00
Jolan Oubrier a49990c266 fix: issue #4955 wrong behaviour of regex for sway/window 'rewrite' 2026-03-27 18:31:05 +01:00
Visal Vijay 0332da0cef bar: warn on invalid height type in configuration 2026-03-25 15:18:00 +05:30
Skyfrei fff1495efb adding a new keyword 'on-click-copy' that accepts a bool and copies to clipboard on true' 2026-03-24 20:12:30 +01:00
Visal Vijay d2aad467a7 Update waybar.5.scd.in 2026-03-21 23:12:36 +05:30
Thomas BeyerandClaude Sonnet 4.6 86d516c74d clock: add weeks-numbering option for configurable CW calculation
The iso8601 flag previously controlled both the calendar grid layout
and the week number format as a coupled boolean. This adds a new
weeks-numbering option (iso/monday/sunday) that explicitly overrides
just the week number calculation method, independent of iso8601 and
locale settings. Existing behaviour is fully preserved when the option
is not set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:43:17 +01:00
Jakob Sjudin e07a2e9197 Update manpages 2026-03-20 11:40:36 +01:00
Jakob Sjudin ba23ef6487 Add .empty css class to group if it is empty 2026-03-20 11:27:58 +01:00
Visal Vijay c62e987dbd Update waybar.5.scd.in 2026-03-19 01:22:34 +05:30
Rio Sanjaya 0053b27125 Merge branch 'Alexays:master' into encode_icon_inside_label 2026-03-12 13:17:40 +07:00
eonphi 8a3a78676f docs(hyprland/workspaces): icon grouping man page entries 2026-03-10 15:01:47 +01:00
eonphi f5d2ef1c4c Merge remote-tracking branch 'upstream/master' 2026-03-10 14:27:40 +01:00
7FM ab5b1a6cd4 pulseaudio: add target option to control source (microphone) volume
The pulseaudio/slider module had partial support for a "target" config
option but changeVolume() in the audio backend was hardcoded to only
modify sink volume. This adds full source volume control:

- Add AudioTarget enum (Sink/Source) to audio_backend
- Extend changeVolume() overloads with AudioTarget parameter
- Store and use pa_cvolume for source (with mono channel default)
- Add sourceVolumeModifyCb for source volume change confirmation
- Add "target" config option to the pulseaudio module for scroll control
- Document "target" in both pulseaudio and pulseaudio-slider man pages

Usage:
  "pulseaudio/slider#in": { "target": "source" }
  "pulseaudio#in": { "target": "source" }
2026-03-06 00:41:42 +01:00
jpk 78bd6c8b87 Remove duplicate from source include 2026-03-05 14:15:29 +01:00
jpk 88e1f85341 Simplified logic 2026-03-05 13:29:14 +01:00
jpk 49db7adc0c Per host checks 2026-03-05 12:29:11 +01:00
jpk 5de21f4a07 Added support for hostname based module addition 2026-03-05 09:04:21 +01:00
Nick Janetakis c85f2a23de Add readability improvements and optimizations
Replace the inner loop with regex_replace. We're already using this
elsewhere and the performance difference isn't worth the loss of human
readability.

Use static const to pre-calc the search string.
2026-03-02 21:29:15 -05:00
Nick Janetakis 0c8acbf39e Add support for optional CSS classes 2026-03-02 19:10:55 -05:00
Prakhar Chhalotre a64876b928 docs: update man page 2026-02-24 19:59:47 +05:30
Prakhar Chhalotre f2cfcb85dd feat(keyboard-state): add custom icons in format-icons 2026-02-24 19:21:13 +05:30
Prakhar Chhalotre 15d33f232b docs: update man page 2026-02-24 12:30:59 +05:30
Prakhar Chhalotre a6a65cb6b7 feat: add support for configurable first day of the week in calendar 2026-02-24 12:30:59 +05:30
BBaoVanC 55fa8cf185 Make CssReloadHelper watch CSS new style on appearance change
If you have reload_style_on_change enabled, and use dynamic appearance
styling (style-dark.css and style-light.css), then changing the
appearance doesn't update the files that are being watched for reload.

Add a method to CssReloadHelper to change the CSS file being watched,
and also provide the CSS file as a parameter to the callback so setupCss
can be called on the right file when the file watcher triggers.
2026-02-21 21:42:34 -06:00
eonphi 736c053350 feat(hyprland/workspaces): grouping icons 2026-02-14 16:37:35 +01:00
Ansari c4f1aba11c Merge branch 'Alexays:master' into master 2026-02-10 20:52:57 +05:30
Prakhar Chhalotre e67b230dbe fix: prioritize plugged status over default when battery is full 2026-02-10 01:52:59 +05:30
lilly-lizard f4f139a43e Merge branch 'master' into river-tags-monitor-focus 2026-02-09 19:28:34 +13:00
anton 3655d73525 Ignore Tray entries by service name and when dbus properties load 2026-02-08 00:44:14 +00:00
szbmrk 2475052326 Merge branch 'master' into feat-hyprland-submap-icon-handling 2026-02-04 12:38:49 +01:00
szbmrk ca0b068ad8 feat(disk)!: handle multiple disks at once 2026-02-04 12:34:32 +01:00
Kaosu 946d6dff6e Merge branch 'master' into features/temperature-hwmon-by-name 2026-01-27 06:20:58 +01:00
Aaron Gerbert adeb82ff78 Merge branch 'hyprland-special-workspaces-multi-monitor' of https://github.com/aaronred345/Waybar into hyprland-special-workspaces-multi-monitor 2026-01-23 11:27:41 -05:00
Aaron Gerbert b192244f50 Commit Squash 2026-01-23 11:27:38 -05:00
Ansari 3df7a684fc Merge remote-tracking branch 'upstream/master' 2026-01-22 21:47:09 +05:30
Rio Sanjaya efc5fccda5 Merge branch 'Alexays:master' into encode_icon_inside_label 2026-01-22 08:58:54 +07:00
Hallvard Høyland Lavik a7b463f315 Merge branch 'Alexays:master' into master 2026-01-17 14:42:26 +01:00
szbmrk f3c3628254 docs(hyprland/submap): add config option to specify different icons for submaps 2026-01-15 00:37:04 +01:00
szbmrk c38024bdec feat(hyprland/submap): add config option to specify different icons for submaps 2026-01-15 00:29:49 +01:00
Benji-clm 7f63a905a1 feat(battery): add option to smooth power for more stable time_remaining estimates 2026-01-09 13:02:28 +00:00
Aksel Lunde Aase 5870c4ba0e feat(clock): Add action to execute external command 2026-01-09 13:26:27 +01:00
Ansari a8a41bd17c fix(workspaces): skip ignored workspaces in persistent rules 2026-01-03 11:20:03 +05:30
Ansari 49083131d1 fix(AModule): improve cursor configuration handling 2026-01-03 11:16:03 +05:30
PickNicko13 a2d597f17f Added better logic for different kinds of filter nodes for only-physical.
Added fallback in case the final node doesn't have the volume property.
2026-01-01 16:54:54 +01:00
Dennis Weiershaeuser 9e53174ef5 feat(sway/workspaces): add format for negative indices 2026-01-01 02:42:56 +01:00
Mathis Raguin c2a038c222 fix(sway/workspaces): escape markup in workspace if enabled 2025-12-31 16:16:38 +01:00
Mathis Raguin da4367df2f fix(sway/workspaces): use number when available to avoid parsing issues 2025-12-31 15:40:31 +01:00
Mathis Raguin 0a5da13fbf fix(workspaces/sway): handle workspaces with quotes 2025-12-31 15:28:10 +01:00
Hallvard Lavik 9dfcd206d7 Allow tooltip formatting the CPU module. 2025-12-27 12:11:43 +01:00
Rio Sanjaya 8e9ebb8bb1 use snake_case for variable name 2025-12-27 17:57:20 +07:00
Kaosu 61af3e95fa fix the doc 2025-12-27 08:38:14 +01:00
Kaosu ce029c3419 temperature: add hwmon-name option for stable sensor selection
Allow selecting a hwmon device for the temperature module by its
name instead of a fixed hwmon path.

This avoids fragile configurations that break when new hwmon
devices are added and the kernel changes hwmon ordering.

The hwmon-name option is mutually exclusive with hwmon-path and
hwmon-path-abs to prevent ambiguous configurations.
2025-12-27 08:16:03 +01:00
Adam Schwalm 11533ecf0a Add '-<state>' support to wifi config
Most modules support config fields like "format-<status>-<state>", so
the user can, for example, set a different format for batter when
unplugged _and_ under a given level. Wifi didn't support this. Change
the module to support format state based on signal strength.
2025-12-26 17:24:50 -06:00
Rio Sanjaya eb2090688c AIconLabel: support icon-size config 2025-12-24 08:43:32 +07:00
Rio Sanjaya b8da9ee94c Move extract icon into its own method 2025-12-24 08:43:03 +07:00
Rio Sanjaya c0b9635e28 Parse label_ to extract embedded icon markup 2025-12-24 08:42:02 +07:00
Rio Sanjaya c2c1a5d614 Add labelCOntainsIcon in AIconLabel 2025-12-24 08:40:42 +07:00
PickNicko13 5c9a1b08d9 Implemented "only-physical" parameter in the wireplumber module.
It makes it find the first node that has adevice.id linked to the
default output if the default output doesn't have it (i.e. it is a
virtual output).

Useful if you want to control (and see) the volume of the actual output
and not the effect sink.

May need some polishing and/or style fixing, but it works.
2025-12-21 23:46:15 +01:00
Max Gautier 2c9e83441a log: use native journald protocol for when running as a service
When running waybar as a systemd service, allows a slighty more readable
and searchable (in particular, by log level) using journal metadata.
2025-12-11 21:58:09 +01:00
mike b 85aac3fee7 feature to limit the number of icons displayed 2025-12-11 14:46:32 -05:00
Aaron Gerbert a2f277a0b1 Active special workspaces fallback on non-active icon before default 2025-12-11 12:04:10 -05:00
Aaron Gerbert a28fe0a9c4 bugfix(hyprland/workspaces): Special workspaces being opened on different monitors now updates correctly
Previously, if you were to open a special workspace on a different
monitor than it was created, the display for workspaces wouldn't update
properly\nI've implemented a somewhat hacky solution by just reloading
hyprland everytime onSpecialWorkspaceActivated is called.
2025-12-09 23:45:45 -05:00
Aaron Gerbert 55e1c4c4a9 Adds name of hyprland workspace to button class
Allows you to target named hyprland special workspaces\n
#workspaces button.special.\<workspace name\> {}
2025-12-09 20:47:04 -05:00
Aaron Gerbert 268e33e892 Unique icons for active and named workspaces
Allows you to set active icons for named workspaces:
"active:<workspace name>" = ""
2025-12-09 19:13:24 -05:00
tS 72855630d1 Merge branch 'Alexays:master' into patch-1 2025-12-09 10:15:36 +01:00
Axel Dahlberg 7777be0da6 feat: allow not switching output when clicking sway/workspace 2025-12-07 19:43:25 +01:00
Raphael Kimmig 85a8fca0de feat: allow sorting niri workspaces 2025-12-07 12:18:24 +01:00
Mental-Vortex b277b63999 fix: Return when ID doesn't exist, we only care about the interest list. 2025-11-30 15:35:10 +08:00
André Silva bbb7f37761 support hiding tray icons 2025-11-26 16:29:35 +00:00
OlivierNeyret 5b5b80a031 Fix indentation formatting in power_menu.xml 2025-11-22 17:47:03 +01:00
zspher d61bc859ae perf(image): set image from path only once 2025-10-29 17:10:15 -06:00
zspher 16d7c9d849 feat(image): expand ~ & $HOME in path 2025-10-28 13:36:54 -06:00
guttermonk 19afad8675 updated client to bind version 2 of the ext-idle-notifier protocol 2025-10-17 21:21:48 -05:00
guttermonk e1e9971680 fixed stale notification object issue 2025-10-17 20:49:24 -05:00
guttermonk 26922c7fbc Update idle_inhibitor.hpp 2025-10-17 20:07:28 -05:00
guttermonk 3e2ea1a870 fixed the type declarations 2025-10-17 19:58:43 -05:00
guttermonk d68f168e09 fixed the compilation error 2025-10-17 19:50:38 -05:00
guttermonk 173e7306d0 fix using the ext-idle-notify-v1 protocol 2025-10-17 19:42:53 -05:00
guttermonk 262f8d96f3 fixed the compilation errors in the idle_inhibitor.cpp file 2025-10-17 18:26:06 -05:00
guttermonk c831a352f2 Added signal support and wait-for-activity bool. 2025-10-17 18:11:50 -05:00
hecate cantus 72e6151f5e Merge branch 'Alexays:master' into master 2025-10-16 18:40:58 -07:00
winkelnp deb8a56eb1 [wireplumber] remove old version (bluetooth) and unnecessary checks (form-factor) 2025-10-14 00:40:56 +02:00
winkelnp 644a357541 [wireplumber] Add support for device.form-factor 2025-10-13 23:27:45 +02:00
lilly-lizard a96f1caa19 fixed bug after merge with master duplicating code in handle_show() 2025-10-11 10:25:50 +13:00
Maik Broemme 382b9daada wlr/taskbar: add homogeneous to evenly distribute button widths 2025-10-10 15:25:21 +02:00
dimti 4c12001569 do not need to remove css class from gtk label on init phase 2025-10-10 11:58:56 +03:00
dimti a36a5e89a9 Approach to dry pattern - separate remove/add css-classes logic to methods 2025-10-10 11:57:44 +03:00
dimti 2e6bf549a0 Change label css classes in initial and on event language class method. Used in getLabel - make a strange behaviour 2025-10-10 11:52:59 +03:00
dimti bdf501a8a5 make getLayout is not static and set css classes for short name keyboard layout in getLayout method 2025-10-10 11:35:47 +03:00
dimti cd820a100d also remove/add css class in onEvent method 2025-10-10 11:14:30 +03:00
lilly-lizard 9a873705d3 Merge branch 'river-tags-monitor-focus' of github.com:lilly-lizard/Waybar-fork into river-tags-monitor-focus 2025-10-09 20:21:25 +13:00
Thomas Crha e1d4967d8e feat(niri/worspaces): add ignore-workspaces configuration option 2025-10-09 18:03:24 +11:00
lilly-lizard 408a87fe89 Merge branch 'master' into river-tags-monitor-focus 2025-10-09 17:16:33 +13:00
Maik Broemme 730b502c76 wlr/taskbar: add justify option for Gtk::Box horizontal alignment
The `wlr/taskbar` module had no way to control its container's horizontal
alignment. This change adds a `justify` config key (left|center|right) that
maps to Gtk::Align::START/CENTER/END and is applied via set_halign() on the
module's Gtk::Box. Default behavior remains unchanged (left).
2025-10-08 08:52:19 +02:00
Dominique Martinet 632e30eb9f niri/window: add col/max_col parameters
niri v25.08 added window positions to IPC, so waybar actually has the
information stored in json around.

This handles the associated WindowLayoutsChanged IPC message to keep the
position up to date in memory, and adds a couple of format messages to
show the current column, so one can dislpay where we are in the
workspace (in the hope of improving this feeling of being lost...)

Ideally a visual representation of the workspace like the one
illustrated in the PR[1] would be even better but this will probably do
for now.

Link: https://github.com/YaLTeR/niri/pull/1265#issuecomment-2724621758 [1]
2025-10-07 22:56:30 +09:00
dimti 192baa4556 Add css class to hyprland language module of short_name language code 2025-10-07 10:41:19 +03:00
Aleksi Hannula 41c34b15d6 Hide niri/language widget if formatted string is empty 2025-10-03 09:16:02 +03:00
Noak Palander ae3420813c fixed bug; when the shown task is removed it promotes another one 2025-09-30 19:29:19 +02:00
Noak Palander d5e45615bb added a taskbar squash option 2025-09-30 19:29:18 +02:00
lilly-lizard 03e69a5efd Merge branch 'master' into river-tags-monitor-focus 2025-09-29 15:32:05 +13:00
Jason Chiu eee0362367 Fix clang build error 2025-09-17 22:09:49 +00:00
Diego Velez 946f175aa2 feat: Show Niri/window component when workspace is empty 2025-09-13 15:17:01 -04:00
Marc Bornand aaffebed4c wireplumber: add more volume formats
- Add the possibility to show the volume in linear, cubic, db, and
  cubic percent (default for backward compatibility).
- Add the ability to change the scale for the scrolling action.
- Make the minimal step flexible to fit any scale.
2025-08-21 13:48:00 +02:00
Steffen Kothe 3060141a65 fix: Fix hide_on_ok when user or system session is ignored
In case of an user ignoring the failed system or user units, hiding of
the waybar module did not work.

Ensure that the ignore case of a system or user bus is reflected
correctly by determining the configuration.

Fixes [1] where regression was introduced in [2].

Links:
[1] https://github.com/Alexays/Waybar/issues/4241
[2] https://github.com/Alexays/Waybar/issues/4240

Signed-off-by: Steffen Kothe <steffen.kothe@skothe.net>
2025-08-15 10:58:27 +00:00
Steffen Kothe 4a7da0cab2 fix: Fail early when neither user nor system is requested
The module can be configured to neither request the user nor the system
session.

In fact does this display non-valuable information to the user and can
be considered as abuse case of the configuration.

Therefore throw an exception when neither user nor system session are
requested.

Signed-off-by: Steffen Kothe <steffen.kothe@skothe.net>
2025-08-15 10:55:25 +00:00
ErrorNoInternet 2e23147215 feat(group): add reveal-by-default option 2025-08-15 12:25:38 +08:00
winkelnp e355b40c66 Add format-bluetooth support to wireplumber module 2025-08-11 17:34:50 +01:00
shibjyoti555 ca8f51a39f added a "fallback" option to hyprland/window when no window in focus 2025-08-09 03:27:01 +05:30
Theo Ratkin 929c817eb4 chore(man): add playing-interval option to mpd man page 2025-08-08 07:55:28 -04:00
Theo Ratkin 03eba63215 feat(mpd): add playing-interval option 2025-07-29 14:04:00 -04:00
LoricAndre af1ccf7d86 docs: add missing final period 2025-07-23 17:48:53 +02:00
LoricAndre d1f3c0ef95 docs: update man page 2025-07-23 17:46:46 +02:00
LoricAndre 383949795e feat(hyprland/workspaces): add uniqueIcons option 2025-07-23 17:38:25 +02:00
ico277 bc61af4ec7 Merge branch 'Alexays:master' into master 2025-07-17 15:04:15 +02:00
AtomToast b4c247a437 remove unused variable 2025-07-13 13:15:56 +02:00
AtomToast bcac5ffc23 doc(README): update river feature list 2025-07-13 13:15:56 +02:00
AtomToast ad0d1350fd feat(river/layout): add format-icons 2025-07-13 13:15:56 +02:00
burbschat d6f9dc911a Apply format according to .clang-format 2025-06-14 22:59:32 +09:00
burbschat 9b6590b972 Add CSS classes for sway workspace indicators depending on associated output
Allows for different styles of workspace indicators (buttons) depending
on the output the workspace is located at.
Using this one can for example color the indicators depending on the
output making it easy to infer on which output a workspace is located.

Example config:

"sway/workspaces": {
    "all-outputs": true,
    "format": "{name}",
    "output-classes": {
        "DP-1": "output1",
        "LVDS-2": "output2"
    }
}

Example CSS:

#workspaces button.output1{
    background-color: green;
}
#workspaces button.output2{
    background-color: blue;
}
2025-06-14 22:40:58 +09:00
ДмитрийandTuur Vanhoutte 5a075646d2 Update man
Co-authored-by: Tuur Vanhoutte <4633209+zjeffer@users.noreply.github.com>
2025-06-07 12:19:52 +03:00
gred 35b0d591a6 Update man page 2025-06-07 12:14:40 +03:00
gred e3e8077342 Add hide-active 2025-06-07 12:11:22 +03:00
Marcin Fabrykowski ba1988e0c2 allow dynamic timeout setting
Signed-off-by: Marcin Fabrykowski <git@fabrykowski.pl>
2025-06-02 12:50:08 +02:00
Duncan Overbruck f3ae13e446 cpu: add load{1,5,15} format variables 2025-05-13 21:42:03 +02:00
Alex Denes 5ab852ff52 dwl/window: allow hiding of title when either inactive or empty 2025-05-05 16:38:59 +00:00
hecate cantus 9b7a8a8c21 implement albumArtist
todo testing
2025-04-27 20:30:14 -07:00
lilly-lizard f094f085be Merge branch 'river-tags-monitor-focus' of github.com:lilly-lizard/Waybar-fork into river-tags-monitor-focus 2025-04-21 13:40:07 +12:00
lilly-lizard 4d15fcc6a8 Merge branch 'master' into river-tags-monitor-focus 2025-04-21 13:38:41 +12:00
fkobi 2faad27cad meson_options: improve sndio's description
Correct me if I am wrong but I think that is it's only use.
If that is the case the description would be less generic and thus better.
2025-04-07 14:45:31 +00:00
Piotr Piwoński 67df5f5838 feat(Group): enhance widget visibility management - set visibility instantly on module update using dynamic signal handling.
Improve performance - manage the visibility of updated module only instead of iterating over all modules on `hide_group`.
2025-03-30 14:49:07 +00:00
lilly-lizard 494e438f6c Merge branch 'master' into river-tags-monitor-focus 2025-03-30 18:57:27 +13:00
Piotr Piwoński 653732dfc6 fix(Group): update always visible modules logic to use dynamic class reference 2025-03-28 21:30:36 +00:00
Piotr Piwoński 6a7b4af78a feat(Group): add dynamic drawer hiding modules based on module's class.
Add `always-visible-class` to config.
Update the drawer on `hide_group`.
2025-03-28 20:56:22 +00:00
hansi 9556bdabda feat: add display-condition configuration option 2025-03-24 11:47:29 +04:00
Jeevitha Kannan K S feb6dfb9d6 feat(power-profiles-daemon): split driver into cpu driver and platform driver 2025-03-21 16:23:39 +05:30
Alex Denes 8685e88ec4 dwl/window: add .active styling class for active windows 2025-02-08 04:01:11 +00:00
Alex Denes 807208cba8 dwl/tags: add hide-vacant option 2025-02-08 03:53:28 +00:00
tS fe1a2c208d Added source_desc argument to format_source in pulseaudio.cpp
Maybe you want hide name and volume on mute :)
2024-12-09 21:15:52 +01:00
Pavel 8270b55e1c feat: add way to use environment value for 'output' option 2024-12-08 18:50:18 +03:00
lilly-lizard 966e34f2a9 Merge branch 'master' into river-tags-monitor-focus 2024-11-29 17:36:20 +13:00
Victor Perez Cano 552c26497d Merge branch 'master' into hyprland-language-tooltip 2024-10-25 19:00:39 +02:00
Victor Perez Cano 38cf981f78 fixed clang format for language.cpp 2024-10-25 18:58:55 +02:00
ico277 00e5cb7568 clang-format run 2024-10-22 18:56:10 +02:00
ico277 b92394cd4d man page edit for the configuration addition 2024-10-22 18:53:13 +02:00
ico277 8cae35d49f try to adhere more to the rest of the coding style 2024-10-22 18:23:24 +02:00
ico277 6e4160dc53 add an option to seach by hwmon name for the temperature module 2024-10-22 03:49:52 +02:00
Victor Perez Cano cb0ecfbddc updated man for hyprland/language 2024-10-19 18:04:07 +02:00
Victor Perez Cano 550d108a03 added tooltip-format-<short_description> on hyprland/language tooltip 2024-10-19 17:30:23 +02:00
Victor Perez Cano 696f7e1542 tooltip functionality on hyprland/language module 2024-10-18 20:41:13 +02:00
lilly-lizard 85e14314bb Merge branch 'master' into river-tags-monitor-focus 2024-09-14 09:30:18 +12:00
Justus Rossmeier 9f2684c26e add click coordinates to script
Allow the current relative mouse position {x} and {y} relative to the
widget in percent to be used as an argument to custom scripts
2024-09-07 13:14:13 +02:00
lilly-lizard 03c229be0e Merge branch 'master' into river-tags-monitor-focus 2024-09-05 16:27:21 +12:00
Lauri Niskanen f1acea521b pulseaudio: Add 'sink-mapping' config option
This commit adds a new optional config to the pulseaudio modules. It
maps sink names to other sink names so that if the current sink is a
key, the sink named by the value is considered to be the current sink
instead of it.

E.g.
  "sink-mapping": {
    "easyeffects_sink": "speakers_sink"
  }
2024-08-25 08:35:00 +03:00
VPavliashvili c7f499e372 image module reworked and added multiple image rendering capability 2024-07-21 21:34:29 +04:00
lilly-lizard 79ce23a9a7 docs for river tags output style 2024-07-13 14:12:54 +12:00
lilly-lizard 626c52cc79 add "output" class to river tags module when monitor is focused 2024-07-13 14:08:48 +12:00
Alexis Rouillard 47db2db2c8 Merge branch 'master' into hyprland-window_no_title_from_special 2024-07-02 23:19:39 +02:00
Marc Meier fe1d49518b Add tooltip selection based on name and state
One can set different tooltips for different workspaces. Selection works
the same as it does for Icons.
Also refactored the `selectIcon` function to a more general
`selectString` function.
Man page is updated too.
2024-03-20 15:54:52 +01:00
Marc Meier 01dce4609c Merge branch 'Alexays:master' into master 2024-03-20 14:50:40 +01:00
Marc Meier 0654034763 Add man pages for hyprland/workspaces tooltips 2024-03-17 19:41:19 +01:00
Marc Meier 065dd87713 Added tooltips to hyprland/workspaces
Simply slam a `"tooltip": true` and a `"tooltip-format": "<your format>"` in that config
2024-03-17 18:00:13 +01:00
Jannik 9f5c27c6e0 fix title not showing for special workspaces (seperate outputs: false 2024-02-24 18:16:43 +01:00
Jannik 0cbb280de6 fix title not showing for special workspaces (seperate outputs: true) 2024-02-24 15:00:39 +01:00
Alexis Rouillard bfa5c43313 Merge branch 'master' into feat/brightness-percent-exp 2023-07-04 23:02:18 +02:00
Savely Krasovsky e1411059ab fix: remove old signal handler 2023-05-06 00:39:17 +03:00
Savely Krasovsky 70901b149e feat: register well-known name 2023-05-06 00:35:34 +03:00
Savely Krasovsky f5a9a4781a fix: use session bus instead of system 2023-05-05 23:44:19 +03:00
Savely Krasovsky 9c161a8cbe fix: shift only if there is more than one player 2023-05-05 22:10:45 +03:00
Savely Krasovsky 8ce569b114 feat: choose another player after first was vanished 2023-05-05 22:05:43 +03:00
Savely Krasovsky 9315ccc4c6 feat: added dbus handler to update current player
it allows to send script a signal to shift current player after playerctld shift command
2023-05-05 21:36:13 +03:00
Narice dbb8a881be feat(backlight): Added icon_exp and changed syntax
changed syntax of percentexp to percent_exp for readability
2022-12-06 19:22:21 +01:00
Narice 0fdc4c11f3 feat(backlight): added percentexp as format option
Humans don't perceive light linearly and so an exponential curve is more
appropriate to gauge the perceived brightness of the screen.
2022-12-06 18:40:38 +01:00
275 changed files with 15644 additions and 2751 deletions
+187
View File
@@ -0,0 +1,187 @@
# Contributing to Waybar
Thanks for helping improve Waybar! This guide covers the essentials.
## Building for development
```bash
meson setup build
ninja -C build
./build/waybar # run your build directly
```
Enable all optional modules while developing:
```bash
meson setup build -Dexperimental=true
```
## Code style
Waybar follows [Google's C++ style guide](https://google.github.io/styleguide/cppguide.html).
Format your changes before committing:
```bash
clang-format -i <files>
```
CI runs `clang-format` and a full build on Linux and FreeBSD — please make sure
both pass.
## Documentation
Module documentation lives in [`man/`](man) as scdoc man pages, **not** in the
wiki. Editing a man page and merging to `master` regenerates the matching wiki
page automatically (see [`.github/wiki`](.github/wiki)). When you add a module,
add its man page and a line in [`.github/wiki/mapping.json`](.github/wiki/mapping.json).
## Pull requests
- Branch from `master` and keep each PR focused on one change.
- Describe what changed and why; link any related issues.
- Add or update the man page for every user-facing option you introduce.
- Build and test against the module(s) you touched.
Have fun :)
---
# Coding Conventions
## 1. Language & Build
- **Standard**: C++20.
- **Build system**: Meson (`meson.build`). Project version is defined there.
- **Compiler flags**: Added via `add_project_arguments()` in Meson. Feature flags use `HAVE_*` / `WANT_*` prefixes (e.g. `-DHAVE_NIRI`, `-DHAVE_HYPRLAND`, `-DHAVE_LIBUDEV`).
## 2. Formatting
- **Tool**: `.clang-format` is checked in. **Never bypass it.**
- **Style**: Google base style.
- **Indent**: 2 spaces. No tabs.
- **Column limit**: 100.
- **Braces**: K&R (opening brace on the same line).
- **Declaration alignment**: Disabled (`AlignConsecutiveDeclarations: false`).
- **Pointer/reference alignment**: Left (`const Json::Value& config`, `int* ptr`, not `int *ptr`).
## 3. Naming
### Files
- Match the primary exported class exactly: `AAppIconLabel.hpp`, `workspaces.cpp`, `backlight_backend.hpp`.
- Corresponding header and source should live in predictable paths:
- `include/<module/path>.hpp`
- `src/<module/path>.cpp`
### Types
- **Classes / Structs**: `PascalCase`.
- Abstract base classes are prefixed with `A` (e.g., `AModule`, `ALabel`, `AIconLabel`, `AAppIconLabel`).
- **Enums / Enum classes**: `PascalCase` name.
- Enumerators: `UPPER_SNAKE_CASE` (e.g., `SCROLL_DIR::NONE`, `KillSignalAction::RELOAD`, `ChangeType::Increase`).
- **Concepts / Type aliases**: `PascalCase`.
### Variables
- **Member variables**: `snake_case_` with a **trailing underscore**.
- Examples: `config_`, `bar_`, `label_`, `app_icon_size_`, `distance_scrolled_y_`, `on_updated_cb_`.
- **Function parameters & locals**: `snake_case` (no trailing underscore).
- Examples: `workspace_data`, `should_refresh`, `app_identifier`, `preferred_device`.
- **Static / constexpr constants**: `UPPER_SNAKE_CASE` or descriptive `kPascalCase`.
- Examples: `MODULE_CLASS`, `EPOLL_MAX_EVENTS`, `kExecFailureExitCode`.
### Functions & Methods
- **Free functions**: `snake_case`.
- Examples: `sanitize_string()`, `rewrite_string()`, `get_total_memory()`, `best_device()`.
- **Class methods**: `lowerCamelCase`.
- Examples: `update()`, `tooltipEnabled()`, `handleScroll()`, `getScrollDir()`, `resolveFormat()`, `setBrightness()`.
- **Virtual overrides**: Mark with `override` (and `final` where applicable). Header signatures often use a trailing return type:
```cpp
auto update() -> void override;
auto refresh(int should_refresh) -> void;
```
### Namespaces
- All lowercase, nested by module path:
```cpp
namespace waybar { }
namespace waybar::modules::niri { }
namespace waybar::util { }
```
- Close every namespace with a comment:
```cpp
} // namespace waybar::modules::niri
```
## 4. Includes & Headers
- Use `#pragma once` in all project headers.
- Include order in `.cpp` files:
1. Corresponding header first.
2. Blank line.
3. External library headers (`<fmt/...>`, `<spdlog/...>`, `<gtkmm/...>`, `<json/json.h>`).
4. Standard library headers (`<algorithm>`, `<vector>`, `<memory>`).
5. Blank line.
6. Other project headers (`"util/..."`, `"modules/..."`).
- Do not use `using namespace` in headers. In `.cpp` files it is acceptable for narrow scopes (e.g., `using namespace std::literals::chrono_literals;`).
- Headers that expose standard-library types in their public interface (e.g. `std::chrono::milliseconds` as a return type or `std::vector<T>` as a member) must `#include` the corresponding standard header directly. Do not rely on transitive includes from other headers.
## 5. Class & Module Design
### Base Class Patterns
- All UI modules ultimately derive from `AModule` (and often `ALabel` or `AIconLabel`).
- Accept configuration in constructors:
```cpp
MyModule(const Json::Value& config, const std::string& name, const std::string& id, ...);
```
### Signals & Threading
- Use `Glib::Dispatcher` (via `waybar::SafeSignal`) to marshal work to the GTK main thread.
- Use `sigc::signal` for normal GTK++ signals.
- If a scope must not be interrupted by `pthread_cancel`, guard it with `waybar::util::CancellationGuard`.
### State / IPC
- Modules that talk to a compositor often implement a small `EventHandler` interface (`onEvent(...)`) and delegate to a singleton backend (e.g., `gIPC`).
### RAII
- Prefer `std::unique_ptr` with custom deleters over raw `new/delete` for C-API resources (see `ScopedFd`, `UdevDeleter`, `UdevDeviceDeleter`, `ScopeGuard`).
## 6. JSON Configuration
- Every module receives `const Json::Value& config` (usually as the first constructor argument).
- Always validate node type before reading:
```cpp
if (config_["sort-by-id"].isBool()) { ... }
if (config.isMember("window-rewrite-default") && config["window-rewrite-default"].isString()) { ... }
```
- Use `waybar::util::JsonParser` if you need to pre-process JSON with non-standard escape sequences.
## 7. String & UI Formatting
- Use `fmt::format` / `fmt::join` for all string composition.
- Use `fmt::dynamic_format_arg_store<fmt::format_context>` when building arguments dynamically.
- Custom `fmt::formatter` specializations are allowed for domain types (e.g., `Glib::ustring`, project enums).
- Sanitize arbitrary text before inserting into Pango markup with `waybar::util::sanitize_string`.
- Use `waybar::util::rewriteString` for user-configurable regex rewrites.
- Truncate UTF-8 safely with `waybar::util::utf8_truncate` / `utf8_width`.
## 8. Error Handling & Logging
- Use `spdlog` for all logging:
- `spdlog::error("Context: {}", e.what());`
- `spdlog::warn("Deprecated key '{}', prefer '{}'", old, replacement);`
- `spdlog::debug("State changed to {}", value);`
- Throw `std::runtime_error` (or similar) for fatal initialization failures that should bubble up to `main()`.
## 9. GTK / Glib Patterns
- Prefer gtkmm-3.0 types (`Gtk::Button`, `Gtk::Label`, `Gdk::Pixbuf`, `Glib::RefPtr`, `Glib::ustring`) over raw C GTK APIs.
- Access the default icon theme through thread-safe wrappers if off the main thread (`DefaultGtkIconThemeWrapper`).
- Tooltips and labels should respect the module `tooltip` toggle (see `tooltipEnabled()` in `AModule`).
## 10. Platform Portability
- Isolate platform-specific code in dedicated files (e.g., `linux.cpp`, `bsd.cpp`).
- Use preprocessor guards for platform differences (`#if defined(__FreeBSD__)`, `#if defined(HAVE_LIBNL)`).
- Keep the common interface in a shared header or base class.
## 11. Thread Safety & Cross-Thread Communication
- GTK is strictly single-threaded. Never emit raw `sigc::signal` from background threads.
- Use `waybar::SafeSignal<T...>` to marshal events from worker threads to the GTK main loop.
- When a module manages background threads, use `std::mutex`, `std::recursive_mutex`, or atomic variables to protect shared state, and ensure the destructor joins or synchronizes with those threads before destroying resources.
## 12. Unsafe Patterns to Avoid
- Do not use `strcpy`, `strcat`, or `sprintf` into fixed-size buffers (e.g. `char buf[PATH_MAX]`). Prefer `std::string`, `std::vector<char>`, or `std::array` with bounds-safe operations.
- When passing a `std::vector<char>` buffer to a C API that expects a mutable `char*` string, always ensure the buffer is null-terminated and clamp the written length to `size() - 1`. Never use `std::copy` from an unbounded source into a fixed-size buffer.
## 13. Singleton Lifetime
- Singletons or objects with process-wide lifetime must not store references (`&`) or pointers to objects with shorter lifetime (e.g., configuration trees, GTK widgets, or bar instances) unless they are explicitly notified of destruction. Prefer storing configuration by value (`Json::Value`, `std::string`, etc.) if the singleton outlives the config loader.
+1 -1
View File
@@ -3,5 +3,5 @@
FROM archlinux:base-devel
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols glib2-devel pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw && \
pacman -S --noconfirm git meson base-devel libinput wayland wayland-protocols glib2-devel pixman libxkbcommon mesa gtkmm3 jsoncpp pugixml scdoc libpulse libdbusmenu-gtk3 libmpdclient gobject-introspection libxkbcommon playerctl iniparser fftw clang && \
sed -Ei 's/#(en_(US|GB)\.UTF)/\1/' /etc/locale.gen && locale-gen
+5
View File
@@ -27,5 +27,10 @@ test-detailed:
meson test -C build --verbose --print-errorlogs --test-args='--reporter console -s'
.PHONY: test-detailed
format:
git diff --name-only --diff-filter=ACMR | \
grep -E '\.(c|h|cpp|hpp)$$' | \
xargs -r clang-format -i
clean:
rm -rf build
+102 -122
View File
@@ -4,163 +4,143 @@ This is my fork of [Waybar](https://github.com/Alexays/Waybar). It has a few
changes with the biggest being that it works with my fork or river (the Wayland
compositor). In fact, it will probably **not** work with vanilla river.
# Waybar [![Licence](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Paypal Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)<br>![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png)
# Waybar
> Highly customizable Wayland bar for Sway and Wlroots based compositors.<br>
> Available in [all major distributions](https://github.com/Alexays/Waybar/wiki/Installation)<br>
> *Waybar [examples](https://github.com/Alexays/Waybar/wiki/Examples)*
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Donate](https://img.shields.io/badge/Donate-Paypal-2244dd.svg)](https://paypal.me/ARouillard)
[![CI](https://github.com/Alexays/Waybar/actions/workflows/linux.yml/badge.svg)](https://github.com/Alexays/Waybar/actions/workflows/linux.yml)
[![Release](https://img.shields.io/github/v/release/Alexays/Waybar)](https://github.com/Alexays/Waybar/releases)
#### Current features
- Sway (Workspaces, Binding mode, Focused window name)
- River (Mapping mode, Tags, Focused window name)
- Hyprland (Window Icons, Workspaces, Focused window name)
- Niri (Workspaces, Focused window name, Language)
- DWL (Tags, Focused window name) [requires dwl ipc patch](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/ipc)
- Tray [#21](https://github.com/Alexays/Waybar/issues/21)
- Local time
- Battery
- UPower
- Power profiles daemon
- Network
- Bluetooth
- Pulseaudio
- Privacy Info
- Wireplumber
- Disk
- Memory
- Cpu load average
- Temperature
- MPD
- Custom scripts
- Custom image
- Multiple output configuration
- And many more customizations
![Waybar](https://raw.githubusercontent.com/alexays/waybar/master/preview-2.png)
#### Configuration and Styling
> Highly customizable Wayland bar for Sway and wlroots-based compositors.<br>
> Available in [all major distributions](https://github.com/Alexays/Waybar/wiki/Installation).
[See the wiki for more details](https://github.com/Alexays/Waybar/wiki).
**[Installation](#installation) · [Wiki](https://github.com/Alexays/Waybar/wiki) · [Configuration](https://github.com/Alexays/Waybar/wiki/Configuration) · [Styling](https://github.com/Alexays/Waybar/wiki/Styling) · [Examples](https://github.com/Alexays/Waybar/wiki/Examples) · [FAQ](https://github.com/Alexays/Waybar/wiki/FAQ)**
### Installation
## Features
Waybar is available from a number of Linux distributions:
### Compositor integration
| Compositor | Workspaces / Tags | Window | Layout | Language | Mode |
| --- | :---: | :---: | :---: | :---: | :---: |
| [Sway](https://github.com/Alexays/Waybar/wiki/Module:-Sway) | ✅ | ✅ | | ✅ | ✅ |
| [River](https://github.com/Alexays/Waybar/wiki/Module:-River) | ✅ | ✅ | ✅ | | ✅ |
| [Hyprland](https://github.com/Alexays/Waybar/wiki/Module:-Hyprland) | ✅ | ✅ | | ✅ | ✅ |
| [Niri](https://github.com/Alexays/Waybar/wiki/Module:-Niri) | ✅ | ✅ | | ✅ | |
| [Mango](https://github.com/Alexays/Waybar/wiki/Module:-Mango) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [DWL](https://github.com/Alexays/Waybar/wiki/Module:-Dwl) | ✅ | ✅ | | | |
| [Wayfire](https://github.com/Alexays/Waybar/wiki/Module:-Wayfire) | ✅ | ✅ | | | |
> DWL requires the [dwl IPC patch](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/ipc).
### Modules
- **Power & hardware** — [Battery](https://github.com/Alexays/Waybar/wiki/Module:-Battery), [UPower](https://github.com/Alexays/Waybar/wiki/Module:-UPower), [Power profiles daemon](https://github.com/Alexays/Waybar/wiki/Module:-PowerProfilesDaemon), [Backlight](https://github.com/Alexays/Waybar/wiki/Module:-Backlight), [CPU](https://github.com/Alexays/Waybar/wiki/Module:-CPU), [Memory](https://github.com/Alexays/Waybar/wiki/Module:-Memory), [Disk](https://github.com/Alexays/Waybar/wiki/Module:-Disk), [Temperature](https://github.com/Alexays/Waybar/wiki/Module:-Temperature)
- **Connectivity** — [Network](https://github.com/Alexays/Waybar/wiki/Module:-Network), [Bluetooth](https://github.com/Alexays/Waybar/wiki/Module:-Bluetooth), [GPS](https://github.com/Alexays/Waybar/wiki/Module:-GPS), [WWAN](https://github.com/Alexays/Waybar/wiki/Module:-WWAN)
- **Audio & media** — [PulseAudio](https://github.com/Alexays/Waybar/wiki/Module:-PulseAudio), [WirePlumber](https://github.com/Alexays/Waybar/wiki/Module:-WirePlumber), [JACK](https://github.com/Alexays/Waybar/wiki/Module:-JACK), [sndio](https://github.com/Alexays/Waybar/wiki/Module:-Sndio), [Cava](https://github.com/Alexays/Waybar/wiki/Module:-Cava), [MPD](https://github.com/Alexays/Waybar/wiki/Module:-MPD), [MPRIS](https://github.com/Alexays/Waybar/wiki/Module:-MPRIS)
- **Desktop** — [Clock & calendar](https://github.com/Alexays/Waybar/wiki/Module:-Clock), [System tray](https://github.com/Alexays/Waybar/wiki/Module:-Tray), [Idle inhibitor](https://github.com/Alexays/Waybar/wiki/Module:-Idle-Inhibitor), [Keyboard state](https://github.com/Alexays/Waybar/wiki/Module:-Keyboard-State), [Privacy](https://github.com/Alexays/Waybar/wiki/Module:-Privacy), [Gamemode](https://github.com/Alexays/Waybar/wiki/Module:-Gamemode), [Systemd failed units](https://github.com/Alexays/Waybar/wiki/Module:-Systemd-failed-units), [Image](https://github.com/Alexays/Waybar/wiki/Module:-Image), [Custom scripts](https://github.com/Alexays/Waybar/wiki/Module:-Custom)
…and more. Every module is documented on the [wiki](https://github.com/Alexays/Waybar/wiki) (see the *Modules* sidebar).
## Getting started
```bash
git clone https://github.com/Alexays/Waybar
cd Waybar
meson setup build
ninja -C build
./build/waybar # run without installing
```
Waybar launches with a sensible [default config](resources/config.jsonc). To make
it yours, copy the default config and stylesheet into `~/.config/waybar/` and edit
them. The [Configuration](https://github.com/Alexays/Waybar/wiki/Configuration)
and [Styling](https://github.com/Alexays/Waybar/wiki/Styling) guides cover every
option, and [Examples](https://github.com/Alexays/Waybar/wiki/Examples) has
ready-to-use community setups.
## Installation
Waybar is packaged by most distributions:
[![Packaging status](https://repology.org/badge/vertical-allrepos/waybar.svg?columns=3&header=Waybar%20Downstream%20Packaging)](https://repology.org/project/waybar/versions)
An Ubuntu PPA with more recent versions is available
[here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar).
An Ubuntu PPA with more recent versions is available [here](https://launchpad.net/~nschloe/+archive/ubuntu/waybar).
#### Building from source
### Building from source
```bash
$ git clone https://github.com/Alexays/Waybar
$ cd Waybar
$ meson setup build
$ ninja -C build
$ ./build/waybar
# If you want to install it
$ ninja -C build install
$ waybar
git clone https://github.com/Alexays/Waybar
cd Waybar
meson setup build
ninja -C build
ninja -C build install # optional
```
**Dependencies**
<details>
<summary><b>Runtime dependencies</b></summary>
```
gtkmm3
jsoncpp
libsigc++
fmt
wayland
chrono-date
spdlog
libgtk-3-dev [gtk-layer-shell]
gobject-introspection [gtk-layer-shell]
libgirepository1.0-dev [gtk-layer-shell]
libpulse [Pulseaudio module]
libnl [Network module]
gtkmm3 jsoncpp libsigc++ fmt wayland
chrono-date spdlog xkbregistry libgtk-3-dev upower
libpulse [Pulseaudio module]
libnl [Network module]
libappindicator-gtk3 [Tray module]
libdbusmenu-gtk3 [Tray module]
libmpdclient [MPD module]
libsndio [sndio module]
libevdev [KeyboardState module]
xkbregistry
upower [UPower battery module]
libdbusmenu-gtk3 [Tray module]
libmpdclient [MPD module]
libsndio [sndio module]
libevdev [KeyboardState module]
```
</details>
**Build dependencies**
<details>
<summary><b>Build dependencies</b></summary>
```
cmake
meson
scdoc
wayland-protocols
cmake meson scdoc wayland-protocols
```
</details>
On Ubuntu, you can install all the relevant dependencies using this command (tested with 19.10 and 20.04):
<details>
<summary><b>Install dependencies — Ubuntu</b></summary>
```
```bash
sudo apt install \
clang-tidy \
gobject-introspection \
libdbusmenu-gtk3-dev \
libevdev-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 \
upower \
clang-tidy gobject-introspection libdbusmenu-gtk3-dev libevdev-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 upower \
libxkbregistry-dev
```
</details>
On Arch, you can use this command:
<details>
<summary><b>Install dependencies — Arch</b></summary>
```
```bash
pacman -S --asdeps \
gtkmm3 \
jsoncpp \
libsigc++ \
fmt \
wayland \
chrono-date \
spdlog \
gtk3 \
gobject-introspection \
libgirepository \
libpulse \
libnl \
libappindicator-gtk3 \
libdbusmenu-gtk3 \
libmpdclient \
sndio \
libevdev \
libxkbcommon \
upower \
meson \
cmake \
scdoc \
wayland-protocols \
glib2-devel
gtkmm3 jsoncpp libsigc++ fmt wayland chrono-date spdlog gtk3 \
gobject-introspection libgirepository libpulse libnl libappindicator-gtk3 \
libdbusmenu-gtk3 libmpdclient sndio libevdev libxkbcommon upower meson \
cmake scdoc wayland-protocols glib2-devel
```
</details>
## Contributing
Contributions welcome!<br>
Have fun :)<br>
The style guidelines are [Google's](https://google.github.io/styleguide/cppguide.html)
Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The style
guidelines are [Google's C++ style](https://google.github.io/styleguide/cppguide.html).
> **Docs live in the man pages.** Module documentation is written in
> [`man/`](man) (scdoc) and auto-synced to the wiki. Edit the man page, not the
> wiki — see [`.github/wiki`](.github/wiki).
> [!CAUTION]
> Distributions of Waybar are only released on the [official GitHub page](https://github.com/Alexays/Waybar).<br/>
> Waybar does **not** have an official website. Do not trust any sites that claim to be official.
> Distributions of Waybar are only released on the [official GitHub page](https://github.com/Alexays/Waybar).<br>
> Waybar does **not** have an official website. Do not trust any site claiming to be official.
## License
Waybar is licensed under the MIT license. [See LICENSE for more information](https://github.com/Alexays/Waybar/blob/master/LICENSE).
Waybar is licensed under the MIT license. [See LICENSE for details](https://github.com/Alexays/Waybar/blob/master/LICENSE).
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include <glibmm/markup.h>
#include <gtkmm/label.h>
#include <json/json.h>
#include <deque>
#include <vector>
#include "AModule.hpp"
namespace waybar {
enum class GraphType { LINE, BAR, GAUGE };
class AGraph : public AModule {
public:
AGraph(const Json::Value&, const std::string&, const std::string&, uint16_t interval = 0,
bool enable_click = false, bool enable_scroll = false);
virtual ~AGraph() = default;
auto update() -> void override;
protected:
Gtk::DrawingArea graph_;
std::deque<int> values_;
uint16_t datapoints_ = 20;
GraphType graph_type_ = GraphType::LINE;
void addValue(const int n);
const std::chrono::milliseconds interval_;
bool onDraw(const Cairo::RefPtr<Cairo::Context>& cr);
std::map<std::string, GtkMenuItem*> submenus_;
std::map<std::string, std::string> menuActionsMap_;
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
private:
void drawFilledArea(const Cairo::RefPtr<Cairo::Context>& cr,
const std::vector<std::pair<double, double>>& points, double height,
const Gdk::RGBA& bg_color);
void drawLine(const Cairo::RefPtr<Cairo::Context>& cr,
const std::vector<std::pair<double, double>>& points, const Gdk::RGBA& fg_color);
void drawPath(const Cairo::RefPtr<Cairo::Context>& cr,
const std::vector<std::pair<double, double>>& points);
void drawBars(const Cairo::RefPtr<Cairo::Context>& cr, double width, double height,
int current_value, const Gdk::RGBA& fg_color);
void drawGauge(const Cairo::RefPtr<Cairo::Context>& cr, double width, double height,
int current_value, const Gdk::RGBA& fg_color);
};
} // namespace waybar
+4
View File
@@ -14,10 +14,14 @@ class AIconLabel : public ALabel {
bool enable_click = false, bool enable_scroll = false);
virtual ~AIconLabel() = default;
auto update() -> void override;
static std::tuple<std::string, std::string> extractIcon(const std::string& input);
protected:
Gtk::Image image_;
Gtk::Box box_;
unsigned app_icon_size_{24};
bool label_contains_icon{false};
bool iconEnabled() const;
};
+63
View File
@@ -1,9 +1,16 @@
#pragma once
#include <fmt/args.h>
#include <fmt/format.h>
#include <glibmm/markup.h>
#include <gtkmm/label.h>
#include <gtkmm/tooltip.h>
#include <json/json.h>
#include <optional>
#include <string>
#include <utility>
#include "AModule.hpp"
namespace waybar {
@@ -25,12 +32,68 @@ class ALabel : public AModule {
bool alt_ = false;
std::string default_format_;
bool setLabelMarkup(const Glib::ustring& markup);
bool setTooltipMarkup(const Glib::ustring& markup);
// resolveTooltipFormat() / resolveFormat() are inherited from AModule.
// Combined label + tooltip helper. Builds a single fmt argument store from
// `args`, renders `labelFormat` into the label and the resolved tooltip format
// into the tooltip, both through the dedup-aware setters. Honors the `tooltip`
// toggle. This replaces the label/tooltip formatting boilerplate that modules
// used to duplicate. `state` selects `tooltip-format-<state>` when non-empty.
template <typename... Args>
void updateLabelAndTooltipForState(const std::string& state, const std::string& labelFormat,
const std::string& tooltipDefault, Args&&... args) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
(store.push_back(std::forward<Args>(args)), ...);
setLabelMarkup(fmt::vformat(labelFormat, store));
if (tooltipEnabled()) {
setTooltipMarkup(fmt::vformat(resolveTooltipFormat(tooltipDefault, state), store));
}
}
template <typename... Args>
void updateLabelAndTooltip(const std::string& labelFormat, const std::string& tooltipDefault,
Args&&... args) {
updateLabelAndTooltipForState("", labelFormat, tooltipDefault, std::forward<Args>(args)...);
}
// Overloads accepting a pre-built argument store, for modules that must
// assemble a dynamic set of format arguments (e.g. per-core CPU stats) that
// cannot be expressed through a fixed variadic call.
// A non-const reference is used so this overload is preferred over the
// variadic template above (which would otherwise bind the store as a single
// forwarded argument).
void updateLabelAndTooltipForState(const std::string& state, const std::string& labelFormat,
const std::string& tooltipDefault,
fmt::dynamic_format_arg_store<fmt::format_context>& store) {
setLabelMarkup(fmt::vformat(labelFormat, store));
if (tooltipEnabled()) {
setTooltipMarkup(fmt::vformat(resolveTooltipFormat(tooltipDefault, state), store));
}
}
void updateLabelAndTooltip(const std::string& labelFormat, const std::string& tooltipDefault,
fmt::dynamic_format_arg_store<fmt::format_context>& store) {
updateLabelAndTooltipForState("", labelFormat, tooltipDefault, store);
}
bool handleToggle(GdkEventButton* const& e) override;
void copyToClipboard(const std::string&);
virtual std::string getState(uint8_t value, bool lesser = false);
std::map<std::string, GtkMenuItem*> submenus_;
std::map<std::string, std::string> menuActionsMap_;
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
private:
// Raw UTF-8 bytes, not Glib::ustring: ustring::operator== collates with
// g_utf8_collate(), which gives private-use codepoints (nerd-font icons)
// no collation weight, so two different icons compare equal.
std::optional<std::string> last_label_markup_;
std::optional<std::string> last_tooltip_markup_;
Glib::RefPtr<Gtk::Tooltip> active_tooltip_;
};
} // namespace waybar
+58 -2
View File
@@ -1,11 +1,16 @@
#pragma once
#include <fmt/args.h>
#include <fmt/format.h>
#include <glibmm/dispatcher.h>
#include <glibmm/markup.h>
#include <gtkmm.h>
#include <gtkmm/eventbox.h>
#include <json/json.h>
#include <string>
#include <utility>
#include "IModule.hpp"
namespace waybar {
@@ -15,6 +20,7 @@ class AModule : public IModule {
static constexpr const char* MODULE_CLASS = "module";
~AModule() override;
sigc::signal<void, AModule*> signal_updated;
auto update() -> void override;
virtual auto refresh(int shouldRefresh) -> void {};
operator Gtk::Widget&() override;
@@ -25,6 +31,10 @@ class AModule : public IModule {
bool expandEnabled() const;
virtual void suspend() {};
virtual void resume() {};
bool shouldSuspend() const { return disable_on_sleep_; }
protected:
// Don't need to make an object directly
// Derived classes are able to use it
@@ -36,11 +46,47 @@ class AModule : public IModule {
SCROLL_DIR getScrollDir(GdkEventScroll* e);
bool tooltipEnabled() const;
// --- Generic format/tooltip resolution (config-only, usable by any module,
// ALabel-derived or not). Prefers `<key>-<state>`, then `<key>`, then default.
std::string resolveFormat(const std::string& defaultFormat, const std::string& state = "") const {
if (!state.empty() && config_["format-" + state].isString()) {
return config_["format-" + state].asString();
}
if (config_["format"].isString()) {
return config_["format"].asString();
}
return defaultFormat;
}
std::string resolveTooltipFormat(const std::string& defaultFormat,
const std::string& state = "") const {
if (!state.empty() && config_["tooltip-format-" + state].isString()) {
return config_["tooltip-format-" + state].asString();
}
if (config_["tooltip-format"].isString()) {
return config_["tooltip-format"].asString();
}
return defaultFormat;
}
// Generic tooltip for any widget: honors the `tooltip` toggle and
// `tooltip-format`, formats with the given args and applies it. Lets modules
// that are not ALabel-derived (e.g. gamemode) reuse the shared logic.
template <typename... Args>
void updateTooltip(Gtk::Widget& widget, const std::string& defaultFormat, Args&&... args) {
if (!tooltipEnabled()) {
return;
}
widget.set_tooltip_markup(
fmt::format(fmt::runtime(resolveTooltipFormat(defaultFormat)), std::forward<Args>(args)...));
}
std::vector<int> pid_children_;
const std::string name_;
const Json::Value& config_;
Gtk::EventBox event_box_;
virtual void setCursor(std::string const& c);
// Backward-compat overload for legacy numeric Gdk::CursorType configs (pre-0.16)
virtual void setCursor(Gdk::CursorType const& c);
virtual bool handleToggle(GdkEventButton* const& ev);
@@ -48,8 +94,17 @@ class AModule : public IModule {
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
virtual bool handleScroll(GdkEventScroll*);
virtual bool handleRelease(GdkEventButton* const& ev);
bool disable_on_sleep_{false};
GObject* menu_ = nullptr;
// Maps a configured event name (e.g. "on-click-middle") to a built-in module
// action name. Populated from the `actions` config section, and by modules
// that interpret on-click* config values as internal actions (e.g.
// wlr/taskbar). Entries here are dispatched through doAction() instead of
// being run as shell commands.
std::map<std::string, std::string> eventActionMap_;
private:
bool handleUserEvent(GdkEventButton* const& ev);
const bool isTooltip;
@@ -57,7 +112,7 @@ class AModule : public IModule {
bool hasUserEvents_;
gdouble distance_scrolled_y_;
gdouble distance_scrolled_x_;
std::map<std::string, std::string> eventActionMap_;
sigc::connection cursor_timeout_conn_;
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
{std::make_pair(1, GdkEventType::GDK_BUTTON_RELEASE), "on-click-release"},
@@ -78,7 +133,8 @@ class AModule : public IModule {
{std::make_pair(9, GdkEventType::GDK_BUTTON_PRESS), "on-click-forward"},
{std::make_pair(9, GdkEventType::GDK_BUTTON_RELEASE), "on-click-forward-release"},
{std::make_pair(9, GdkEventType::GDK_2BUTTON_PRESS), "on-double-click-forward"},
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"}};
{std::make_pair(9, GdkEventType::GDK_3BUTTON_PRESS), "on-triple-click-forward"},
{std::make_pair(10, GdkEventType::GDK_BUTTON_PRESS), "on-click-copy"}};
};
} // namespace waybar
+9
View File
@@ -24,6 +24,8 @@ struct waybar_output {
Glib::RefPtr<Gdk::Monitor> monitor;
std::string name;
std::string identifier;
int32_t width;
int32_t height;
std::unique_ptr<struct zxdg_output_v1, decltype(&zxdg_output_v1_destroy)> xdg_output = {
nullptr, &zxdg_output_v1_destroy};
@@ -75,6 +77,8 @@ class Bar : public sigc::trackable {
util::KillSignalAction getOnSigusr1Action();
util::KillSignalAction getOnSigusr2Action();
void toggleSuspend(bool suspend);
struct waybar_output* output;
Json::Value config;
struct wl_surface* surface;
@@ -99,6 +103,7 @@ class Bar : public sigc::trackable {
void setMode(const bar_mode&);
void setPassThrough(bool passthrough);
void setPosition(Gtk::PositionType position);
void forceLayerCommit();
void onConfigure(GdkEventConfigure* ev);
void configureGlobalOffset(int width, int height);
void onOutputGeometryChanged();
@@ -126,6 +131,10 @@ class Bar : public sigc::trackable {
waybar::util::KillSignalAction onSigusr1 = util::SIGNALACTION_DEFAULT_SIGUSR1;
waybar::util::KillSignalAction onSigusr2 = util::SIGNALACTION_DEFAULT_SIGUSR2;
/* Disconnected in ~Bar before the modules are destroyed (#5182). */
sigc::connection map_conn_;
sigc::connection unmap_conn_;
};
} // namespace waybar
+6
View File
@@ -14,6 +14,7 @@
struct zwp_idle_inhibitor_v1;
struct zwp_idle_inhibit_manager_v1;
struct ext_idle_notifier_v1;
namespace waybar {
@@ -32,6 +33,7 @@ class Client {
struct wl_registry* registry = nullptr;
struct zxdg_output_manager_v1* xdg_output_manager = nullptr;
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager = nullptr;
struct ext_idle_notifier_v1* idle_notifier = nullptr;
std::vector<std::unique_ptr<Bar>> bars;
Config config;
std::string bar_id;
@@ -48,7 +50,9 @@ class Client {
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 handleOutputLogicalSize(void*, struct zxdg_output_v1*, int32_t, int32_t);
static void handleOutputDone(void*, struct zxdg_output_v1*);
void createBarsBatch();
static void handleOutputName(void*, struct zxdg_output_v1*, const char*);
static void handleOutputDescription(void*, struct zxdg_output_v1*, const char*);
void handleMonitorAdded(Glib::RefPtr<Gdk::Monitor> monitor);
@@ -65,6 +69,8 @@ class Client {
std::map<int, bool> signal_toggle_state;
sigc::connection monitor_added_connection_;
sigc::connection monitor_removed_connection_;
std::list<waybar_output*> pending_outputs_;
bool bars_scheduled_ = false;
};
} // namespace waybar
+2 -1
View File
@@ -29,7 +29,8 @@ class Config {
Json::Value& getConfig() { return config_; }
std::vector<Json::Value> getOutputConfigs(const std::string& name, const std::string& identifier);
std::vector<Json::Value> getOutputConfigs(const std::string& name, const std::string& identifier,
int32_t width, int32_t height);
private:
void setupConfig(Json::Value& dst, const std::string& config_file, int depth);
+11 -2
View File
@@ -12,15 +12,17 @@
namespace waybar {
class Group : public AModule {
sigc::connection reveal_timeout_;
public:
Group(const std::string&, const std::string&, const Json::Value&, bool);
~Group() override = default;
~Group() override;
auto update() -> void override;
operator Gtk::Widget&() override;
auto refresh(int sig) -> void override;
virtual Gtk::Box& getBox();
void addWidget(Gtk::Widget& widget);
void addWidget(AModule* module);
protected:
Gtk::Box box;
@@ -30,6 +32,9 @@ class Group : public AModule {
bool is_drawer = false;
bool click_to_reveal = false;
std::optional<int> toggle_signal;
std::string always_visible_class;
bool empty_if_drawer_empty = false;
int reveal_delay = 0;
std::string add_class_to_drawer_children;
bool handleMouseEnter(GdkEventCrossing* const& ev) override;
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
@@ -45,6 +50,10 @@ class Group : public AModule {
hide_group();
}
}
void manage_visibility(AModule* module);
void show_widget(Gtk::Widget& widget);
void hide_widget(Gtk::Widget& widget);
void hide_current_widget_if_inactive();
};
} // namespace waybar
+6
View File
@@ -9,6 +9,7 @@
#include <poll.h>
#include <algorithm>
#include <chrono>
#include <fstream>
#include <string>
#include <vector>
@@ -51,6 +52,11 @@ class Battery : public ALabel {
bool warnFirstTime_{true};
bool weightedAverage_{true};
const Bar& bar_;
bool smoothPowerEnable_{false};
double time_constant_s_{260.0};
double smooth_power_{0.0}; // µW
std::chrono::steady_clock::time_point last_t_{std::chrono::steady_clock::now()};
std::string old_status_raw_{""};
util::SleeperThread thread_;
util::SleeperThread thread_battery_update_;
+7
View File
@@ -41,6 +41,7 @@ class Bluetooth : public ALabel {
bool services_resolved;
// NOTE: experimental feature in bluez
std::optional<unsigned char> battery_percentage;
std::optional<unsigned char> battery_percentage_peripheral;
};
public:
@@ -59,6 +60,12 @@ class Bluetooth : public ALabel {
gpointer) -> void;
auto getDeviceBatteryPercentage(GDBusObject*) -> std::optional<unsigned char>;
auto getDeviceGattBatteryLevels(GDBusObject*, std::optional<unsigned char>&,
std::optional<unsigned char>&) -> void;
static auto processBatteryServiceCharacteristics(GList*, const std::string&, const std::string&,
const std::string&,
std::optional<unsigned char>&,
std::optional<unsigned char>&) -> void;
auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool;
auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool;
+46 -10
View File
@@ -1,36 +1,64 @@
#pragma once
#include <epoxy/gl.h>
#include <gtkmm/glarea.h>
#include <map>
#include <string>
#include <array>
#include <sigc++/connection.h>
#include "AModule.hpp"
#include "cava_backend.hpp"
namespace waybar::modules::cava {
class CavaGLSL final : public AModule, public Gtk::GLArea {
class CavaGLSL final : public AModule {
public:
CavaGLSL(const std::string&, const Json::Value&);
~CavaGLSL() = default;
~CavaGLSL();
auto doAction(const std::string& name) -> void override;
private:
using Action = void (CavaGLSL::*)();
Gtk::GLArea gl_area_;
std::shared_ptr<CavaBackend> backend_;
struct ::cava::config_params prm_;
int frame_counter{0};
// Cached config params (deep-copied strings to avoid dangling char* on backend reload)
int sdl_width_{0};
int sdl_height_{0};
int bar_width_{0};
int bar_spacing_{0};
int gradient_count_{0};
std::string vertex_shader_;
std::string fragment_shader_;
std::string bcolor_;
std::string color_;
std::array<std::string, 8> gradient_colors_;
int frame_counter_{0};
bool silence_{false};
bool hide_on_silence_{false};
bool mapped_{false};
// Cava method
auto onUpdate(const ::cava::audio_raw& input) -> void;
void pauseResume();
auto onUpdate(const CavaBackend::AudioRaw& input) -> void;
auto onSilence() -> void;
// Member variable to store the shared pointer
std::shared_ptr<::cava::audio_raw> m_data_;
GLuint shaderProgram_;
auto onBackendConfigChanged() -> void;
void cacheConfigParams(const ::cava::config_params& src);
// Member variable to store audio data
CavaBackend::AudioRaw m_data_;
GLuint shaderProgram_{0};
// OpenGL variables
GLuint fbo_;
GLuint texture_;
GLuint fbo_{0};
GLuint texture_{0};
GLuint vbo_{0};
GLuint ibo_{0};
GLuint vao_{0};
GLint uniform_bars_;
GLint uniform_previous_bars_;
GLint uniform_bars_count_;
GLint uniform_time_;
GLint uniform_input_texture_;
// Methods
void onRealize();
bool onRender(const Glib::RefPtr<Gdk::GLContext>& context);
@@ -39,5 +67,13 @@ class CavaGLSL final : public AModule, public Gtk::GLArea {
void initSurface();
void initGLSL();
GLuint loadShader(const std::string& fileName, GLenum type);
void cleanupGL();
// ModuleActionMap
static const std::map<std::string, Action> actionMap_;
sigc::connection audio_raw_update_conn_;
sigc::connection silence_conn_;
sigc::connection config_changed_conn_;
};
} // namespace waybar::modules::cava
+17 -9
View File
@@ -1,30 +1,38 @@
#pragma once
#include <map>
#include <string>
#include <sigc++/connection.h>
#include "ALabel.hpp"
#include "cava_backend.hpp"
namespace waybar::modules::cava {
class Cava final : public ALabel, public sigc::trackable {
class CavaRaw final : public ALabel {
public:
Cava(const std::string&, const Json::Value&);
~Cava() = default;
CavaRaw(const std::string&, const Json::Value&);
~CavaRaw();
auto doAction(const std::string& name) -> void override;
private:
using Action = void (CavaRaw::*)();
std::shared_ptr<CavaBackend> backend_;
// Text to display
Glib::ustring label_text_{""};
Glib::ustring label_text_;
bool silence_{false};
bool hide_on_silence_{false};
std::string format_silent_{""};
int ascii_range_{0};
std::string format_silent_;
// Cava method
void pause_resume();
void pauseResume();
auto onUpdate(const std::string& input) -> void;
auto onSilence() -> void;
// ModuleActionMap
static inline std::map<const std::string, void (waybar::modules::cava::Cava::* const)()>
actionMap_{{"mode", &waybar::modules::cava::Cava::pause_resume}};
static const std::map<std::string, Action> actionMap_;
sigc::connection update_conn_;
sigc::connection silence_conn_;
};
} // namespace waybar::modules::cava
+90 -21
View File
@@ -1,8 +1,17 @@
#pragma once
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <json/json.h>
#include <sigc++/sigc++.h>
#include "util/SafeSignal.hpp"
#include "util/sleeper_thread.hpp"
namespace cava {
@@ -21,7 +30,6 @@ extern "C" {
} // namespace cava
namespace waybar::modules::cava {
using namespace std::literals::chrono_literals;
class CavaBackend final {
public:
@@ -29,19 +37,38 @@ class CavaBackend final {
virtual ~CavaBackend();
// Methods
int getAsciiRange();
int getAsciiRange() const;
void doPauseResume();
void Update();
const struct ::cava::config_params* getPrm();
std::chrono::milliseconds getFrameTimeMilsec();
void update();
const ::cava::config_params& getPrm() const;
std::chrono::milliseconds getFrameTimeMilsec() const;
struct AudioRaw {
std::vector<float> bars_raw;
std::vector<float> previous_bars_raw;
int number_of_bars = 0;
AudioRaw() = default;
explicit AudioRaw(const ::cava::audio_raw& raw) {
number_of_bars = raw.number_of_bars;
if (raw.bars_raw != nullptr && number_of_bars > 0) {
bars_raw.assign(raw.bars_raw, raw.bars_raw + number_of_bars);
}
if (raw.previous_bars_raw != nullptr && number_of_bars > 0) {
previous_bars_raw.assign(raw.previous_bars_raw, raw.previous_bars_raw + number_of_bars);
}
}
};
// Signal accessor
using type_signal_update = sigc::signal<void(const std::string&)>;
type_signal_update signal_update();
using type_signal_audio_raw_update = sigc::signal<void(const ::cava::audio_raw&)>;
type_signal_audio_raw_update signal_audio_raw_update();
using type_signal_silence = sigc::signal<void()>;
type_signal_silence signal_silence();
using SignalUpdate = SafeSignal<const std::string&>;
SignalUpdate& signalUpdate();
using SignalAudioRawUpdate = SafeSignal<AudioRaw>;
SignalAudioRawUpdate& signalAudioRawUpdate();
using SignalSilence = SafeSignal<>;
SignalSilence& signalSilence();
using SignalConfigChanged = SafeSignal<>;
SignalConfigChanged& signalConfigChanged();
private:
CavaBackend(const Json::Value& config);
@@ -49,36 +76,78 @@ class CavaBackend final {
util::SleeperThread out_thread_;
// Cava API to read audio source
::cava::ptr input_source_{NULL};
::cava::ptr input_source_{nullptr};
struct ::cava::error_s error_{}; // cava errors
struct ::cava::config_params prm_{}; // cava parameters
struct ::cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
struct ::cava::audio_data audio_data_{}; // cava audio data
struct ::cava::cava_plan* plan_{NULL}; //{new cava_plan{}};
struct ::cava::cava_plan* plan_{nullptr}; //{new cava_plan{}};
std::chrono::seconds fetch_input_delay_{4};
// Delay to handle audio source
std::chrono::milliseconds frame_time_milsec_{1s};
const Json::Value& config_;
struct AdaptiveDelay {
std::chrono::milliseconds delay;
std::chrono::seconds delta{0};
explicit AdaptiveDelay(std::chrono::milliseconds initial = std::chrono::seconds(1))
: delay(initial) {}
bool increase() {
if (delta == std::chrono::seconds{0}) {
delta += std::chrono::seconds{1};
delay += delta;
return true;
}
return false;
}
bool decrease() {
if (delta > std::chrono::seconds{0}) {
delay -= delta;
delta -= std::chrono::seconds{1};
return true;
}
return false;
}
std::chrono::milliseconds current() const { return delay; }
void reset(std::chrono::milliseconds new_delay) {
delay = new_delay;
delta = std::chrono::seconds{0};
}
};
AdaptiveDelay adaptive_delay_;
Json::Value config_;
int re_paint_{0};
bool silence_{false};
bool silence_prev_{false};
std::chrono::seconds suspend_silence_delay_{0};
int sleep_counter_{0};
std::string output_{};
// Methods
void invoke();
void execute();
bool isSilence();
bool isSilent();
void doUpdate(bool force = false);
void loadConfig();
void freeBackend();
// Signal
type_signal_update m_signal_update_;
type_signal_audio_raw_update m_signal_audio_raw_;
type_signal_silence m_signal_silence_;
SignalUpdate m_signal_update_;
SignalAudioRawUpdate m_signal_audio_raw_;
SignalSilence m_signal_silence_;
SignalConfigChanged m_signal_config_changed_;
std::atomic<bool> shutdown_{false};
bool audio_raw_initialized_{false};
mutable std::recursive_mutex state_mutex_;
// Synchronization for joining read_thread_ during destruction
bool read_thread_exited_{false};
mutable std::mutex read_thread_exit_mutex_;
std::condition_variable read_thread_exit_cv_;
};
} // namespace waybar::modules::cava
+6 -4
View File
@@ -1,5 +1,7 @@
#pragma once
#include <memory>
#ifdef HAVE_LIBCAVA
#include "cavaRaw.hpp"
#include "cava_backend.hpp"
@@ -9,16 +11,16 @@
#endif
namespace waybar::modules::cava {
AModule* getModule(const std::string& id, const Json::Value& config) {
inline std::unique_ptr<AModule> getModule(const std::string& id, const Json::Value& config) {
#ifdef HAVE_LIBCAVA
const std::shared_ptr<CavaBackend> backend_{waybar::modules::cava::CavaBackend::inst(config)};
switch (backend_->getPrm()->output) {
switch (backend_->getPrm().output) {
#ifdef HAVE_LIBCAVAGLSL
case ::cava::output_method::OUTPUT_SDL_GLSL:
return new waybar::modules::cava::CavaGLSL(id, config);
return std::make_unique<waybar::modules::cava::CavaGLSL>(id, config);
#endif
default:
return new waybar::modules::cava::Cava(id, config);
return std::make_unique<waybar::modules::cava::CavaRaw>(id, config);
}
#else
throw std::runtime_error("Unknown module");
+6
View File
@@ -12,6 +12,7 @@ const std::string kOrdPlaceholder{"ordinal_date"};
enum class CldMode { MONTH, YEAR };
enum class WS { LEFT, RIGHT, HIDDEN };
enum class WeekNumbering { LOCALE, ISO, MONDAY, SUNDAY };
class Clock final : public ALabel {
public:
@@ -51,6 +52,7 @@ class Clock final : public ALabel {
date::day cldBaseDay_{0}; // calendar Cached day. Is used when today is changing(midnight)
std::string cldText_{""}; // calendar text to print
bool iso8601Calendar_{false}; // whether the calendar is in ISO8601
WeekNumbering weekNumbering_{WeekNumbering::LOCALE}; // week number calculation method
CldMode cldMode_{CldMode::MONTH};
auto get_calendar(const date::year_month_day& today, const date::year_month_day& ymd,
const date::time_zone* tz) -> const std::string;
@@ -80,6 +82,7 @@ class Clock final : public ALabel {
void cldShift_reset();
void tz_up();
void tz_down();
void action_exec(const std::string& action);
// Module Action Map
static inline std::map<const std::string, void (waybar::modules::Clock::* const)()> actionMap_{
{"mode", &waybar::modules::Clock::cldModeSwitch},
@@ -88,6 +91,9 @@ class Clock final : public ALabel {
{"shift_reset", &waybar::modules::Clock::cldShift_reset},
{"tz_up", &waybar::modules::Clock::tz_up},
{"tz_down", &waybar::modules::Clock::tz_down}};
static inline std::map<const std::string,
void (waybar::modules::Clock::* const)(const std::string& action)>
actionWithArgsMap_{{"exec", &waybar::modules::Clock::action_exec}};
};
} // namespace waybar::modules
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <fmt/format.h>
#include <cstdint>
#include <fstream>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include "AGraph.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class CpuGraph : public AGraph {
public:
CpuGraph(const std::string&, const Json::Value&);
virtual ~CpuGraph() = default;
auto update() -> void override;
private:
static constexpr const char* MODERATE_CLASS = "cpu-moderate";
static constexpr const char* HIGH_CLASS = "cpu-high";
static constexpr const char* INTENSIVE_CLASS = "cpu-intensive";
std::vector<std::tuple<size_t, size_t>> prev_times_;
util::SleeperThread thread_;
};
} // namespace waybar::modules
+12 -4
View File
@@ -3,16 +3,18 @@
#include <fmt/format.h>
#include <csignal>
#include <memory>
#include <string>
#include "ALabel.hpp"
#include "AIconLabel.hpp"
#include "util/command.hpp"
#include "util/command_line_stream.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class Custom : public ALabel {
class Custom : public AIconLabel {
public:
Custom(const std::string&, const std::string&, const Json::Value&, const std::string&);
virtual ~Custom();
@@ -22,6 +24,9 @@ class Custom : public ALabel {
private:
void delayWorker();
void continuousWorker();
void startContinuousProcess(bool throw_on_failure);
void handleContinuousProcessExit(int exit_code);
void scheduleContinuousRestart();
void waitingWorker();
void parseOutputRaw();
void parseOutputJson();
@@ -36,13 +41,16 @@ class Custom : public ALabel {
std::string alt_;
std::string tooltip_;
std::string last_tooltip_markup_;
std::string image_path_;
std::string image_name_;
unsigned app_icon_size_{24};
const bool tooltip_format_enabled_;
std::vector<std::string> class_;
int percentage_;
FILE* fp_;
int pid_;
util::command::res output_;
util::JsonParser parser_;
std::unique_ptr<util::command::LineStream> continuous_stream_;
sigc::connection restart_connection_;
util::SleeperThread thread_;
};
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <fmt/format.h>
#include <csignal>
#include <string>
#include "AGraph.hpp"
#include "util/command.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class CustomGraph : public AGraph {
public:
CustomGraph(const std::string&, const std::string&, const Json::Value&, const std::string&);
virtual ~CustomGraph();
auto update() -> void override;
void refresh(int /*signal*/) override;
private:
void delayWorker();
void continuousWorker();
void waitingWorker();
void parseOutputRaw();
void parseOutputJson();
void handleEvent();
bool handleScroll(GdkEventScroll* e) override;
bool handleToggle(GdkEventButton* const& e) override;
const std::string name_;
const std::string output_name_;
std::string text_;
std::string id_;
std::string alt_;
std::string tooltip_;
const bool tooltip_format_enabled_;
std::vector<std::string> class_;
int percentage_;
FILE* fp_;
int pid_;
util::command::res output_;
util::JsonParser parser_;
util::SleeperThread thread_;
};
} // namespace waybar::modules
+4 -1
View File
@@ -4,6 +4,7 @@
#include <sys/statvfs.h>
#include <fstream>
#include <vector>
#include "ALabel.hpp"
#include "util/format.hpp"
@@ -19,7 +20,9 @@ class Disk : public ALabel {
private:
util::SleeperThread thread_;
std::string path_;
std::string header_;
std::vector<std::string> paths_;
std::string separator_;
std::string unit_;
float calc_specific_divisor(const std::string& divisor);
+3
View File
@@ -21,6 +21,8 @@ class Tags : public waybar::AModule {
void handle_primary_clicked(uint32_t tag);
bool handle_button_press(GdkEventButton* event_button, uint32_t tag);
void handle_active_output(zdwl_ipc_output_v2* zdwl_output_v2, uint32_t active);
struct zdwl_ipc_manager_v2* status_manager_;
struct wl_seat* seat_;
@@ -28,6 +30,7 @@ class Tags : public waybar::AModule {
const waybar::Bar& bar_;
Gtk::Box box_;
std::vector<Gtk::Button> buttons_;
bool hide_vacant_;
struct zdwl_ipc_output_v2* output_status_;
};
+4
View File
@@ -19,6 +19,7 @@ class Window : public AAppIconLabel, public sigc::trackable {
void handle_layout(const uint32_t layout);
void handle_title(const char* title);
void handle_appid(const char* ppid);
void handle_active(const uint32_t active);
void handle_layout_symbol(const char* layout_symbol);
void handle_frame();
@@ -30,6 +31,9 @@ class Window : public AAppIconLabel, public sigc::trackable {
std::string title_;
std::string appid_;
std::string layout_symbol_;
bool active_;
bool hide_inactive_;
bool hide_empty_;
uint32_t layout_;
struct zdwl_ipc_output_v2* output_status_;
+1
View File
@@ -37,6 +37,7 @@ class Language : public waybar::ALabel, public EventHandler {
util::JsonParser parser_;
Layout layout_;
std::string prev_short_name_; // applied CSS class; touched only in update() (#4665)
IPC& m_ipc;
};
+2
View File
@@ -26,9 +26,11 @@ class Submap : public waybar::ALabel, public EventHandler {
const Bar& bar_;
util::JsonParser parser_;
std::string submap_;
std::string icon_;
std::string prev_submap_;
bool always_on_ = false;
std::string default_submap_ = "Default";
std::unordered_map<std::string, std::string> icons_;
IPC& m_ipc;
};
+16 -2
View File
@@ -30,7 +30,8 @@ class Workspace {
public:
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
const Json::Value& clients_data = Json::Value::nullRef);
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
~Workspace();
std::string& selectString(std::map<std::string, std::string>& string_map);
Gtk::Button& button() { return m_button; };
int id() const { return m_id; };
@@ -45,12 +46,22 @@ class Workspace {
bool isUrgent() const { return m_isUrgent; };
bool handleClicked(GdkEventButton* bt) const;
bool handleEnter(GdkEventCrossing* event);
bool handleLeave(GdkEventCrossing* event);
void startHoverCheck();
void stopHoverCheck();
bool syncHoverClass();
bool pointerInsideButton();
void setActive(bool value = true) { m_isActive = value; };
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
void setUrgent(bool value = true) { m_isUrgent = value; };
void setVisible(bool value = true) { m_isVisible = value; };
void setWindows(uint value) { m_windows = value; };
void setId(int value) { m_id = value; };
void setName(std::string const& value) { m_name = value; };
void setOutput(std::string const& value) { m_output = value; };
bool containsWindow(WindowAddress const& addr) const {
@@ -64,13 +75,14 @@ class Workspace {
bool onWindowOpened(WindowCreationPayload const& create_window_payload);
std::optional<WindowRepr> closeWindow(WindowAddress const& addr);
void update(const std::string& workspace_icon);
void update(const std::string& workspace_icon, const std::string& workspace_tooltip);
private:
Workspaces& m_workspaceManager;
int m_id;
std::string m_name;
std::string m_prevNameClass;
std::string m_output;
uint m_windows;
bool m_isActive = false;
@@ -80,6 +92,8 @@ class Workspace {
bool m_isUrgent = false;
bool m_isVisible = false;
sigc::connection m_hoverCheckConnection;
std::vector<WindowRepr> m_windowMap;
Gtk::Button m_button;
+27 -8
View File
@@ -39,9 +39,11 @@ class Workspaces : public AModule, public EventHandler {
auto allOutputs() const -> bool { return m_allOutputs; }
auto showSpecial() const -> bool { return m_showSpecial; }
auto activeOnly() const -> bool { return m_activeOnly; }
auto hideActive() const -> bool { return m_hideActive; }
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
auto persistentOnly() const -> bool { return m_persistentOnly; }
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
auto uniqueIcons() const -> bool { return m_uniqueIcons; }
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
auto barScroll() const -> bool { return m_barScroll; }
@@ -52,16 +54,20 @@ class Workspaces : public AModule, public EventHandler {
auto taskbarFormatBefore() const -> std::string { return m_taskbarFormatBefore; }
auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; }
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
auto taskbarMaxIcons() const -> int { return m_taskbarMaxIcons; }
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; }
auto onClickWindow() const -> std::string { return m_onClickWindow; }
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
auto maxWindows() const -> int { return m_maxWindows; }
enum class ActiveWindowPosition { NONE, FIRST, LAST };
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
std::string getRewrite(const std::string& window_class, const std::string& window_title);
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
auto windowRewriteGroupThreshold() const -> int { return m_windowRewriteGroupThreshold; }
auto const& getWindowRewriteGroupFormat() const { return m_windowRewriteGroupFormat; }
bool isWorkspaceIgnored(std::string const& workspace_name);
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
@@ -89,6 +95,7 @@ class Workspaces : public AModule, public EventHandler {
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
auto populateMaxWindowsConfig(const Json::Value& config) -> void;
auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void;
void registerIpc();
@@ -101,6 +108,7 @@ class Workspaces : public AModule, public EventHandler {
Json::Value const& clientsData = Json::Value::nullRef);
void onWorkspaceMoved(std::string const& payload);
void onWorkspaceRenamed(std::string const& payload);
void onWorkspaceIdChanged(std::string const& payload);
static std::optional<int> parseWorkspaceId(std::string const& workspaceIdStr);
// monitor events
@@ -146,9 +154,11 @@ class Workspaces : public AModule, public EventHandler {
bool m_allOutputs = false;
bool m_showSpecial = false;
bool m_activeOnly = false;
bool m_hideActive = false;
bool m_specialVisibleOnly = false;
bool m_persistentOnly = false;
bool m_moveToMonitor = false;
bool m_uniqueIcons = false;
bool m_barScroll = false;
Json::Value m_persistentWorkspaceConfig;
@@ -158,21 +168,25 @@ class Workspaces : public AModule, public EventHandler {
std::map<WindowAddress, WindowRepr, std::less<>> m_orphanWindowMap;
enum class SortMethod { ID, NAME, NUMBER, SPECIAL_CENTERED, DEFAULT };
util::EnumParser<SortMethod> m_enumParser;
SortMethod m_sortBy = SortMethod::DEFAULT;
std::map<std::string, SortMethod> m_sortMap = {{"ID", SortMethod::ID},
{"NAME", SortMethod::NAME},
{"NUMBER", SortMethod::NUMBER},
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
{"DEFAULT", SortMethod::DEFAULT}};
static inline const std::map<std::string, SortMethod> m_sortMap = {
{"ID", SortMethod::ID},
{"NAME", SortMethod::NAME},
{"NUMBER", SortMethod::NUMBER},
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
{"DEFAULT", SortMethod::DEFAULT}};
std::string m_formatBefore;
std::string m_formatAfter;
std::map<std::string, std::string> m_iconsMap;
std::map<std::string, std::string> m_tooltipMap;
bool m_withTooltip = false;
util::RegexCollection m_windowRewriteRules;
bool m_anyWindowRewriteRuleUsesTitle = false;
std::string m_formatWindowSeparator;
int m_windowRewriteGroupThreshold = 0;
std::string m_windowRewriteGroupFormat = "{icon}×{count}";
bool m_withIcon;
uint64_t m_monitorId;
@@ -191,17 +205,18 @@ class Workspaces : public AModule, public EventHandler {
std::string m_taskbarFormatBefore;
std::string m_taskbarFormatAfter;
int m_taskbarIconSize = 16;
int m_taskbarMaxIcons = 0; // 0 means unlimited
Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL;
bool m_taskbarReverseDirection = false;
util::EnumParser<ActiveWindowPosition> m_activeWindowEnumParser;
ActiveWindowPosition m_activeWindowPosition = ActiveWindowPosition::NONE;
std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
static inline std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
{"NONE", ActiveWindowPosition::NONE},
{"FIRST", ActiveWindowPosition::FIRST},
{"LAST", ActiveWindowPosition::LAST},
};
std::string m_onClickWindow;
std::string m_currentActiveWindowAddress;
int m_maxWindows = 0;
std::vector<std::regex> m_ignoreWorkspaces;
std::vector<std::regex> m_ignoreWindows;
@@ -211,6 +226,10 @@ class Workspaces : public AModule, public EventHandler {
Gtk::Box m_box;
sigc::connection m_scrollEventConnection_;
IPC& m_ipc;
// Coalesces bursts of Hyprland events into a single UI refresh. Armed and
// disconnected only on the GTK main thread (see Workspaces::update).
sigc::connection m_debounceTimer;
};
} // namespace waybar::modules::hyprland
+18 -1
View File
@@ -6,25 +6,42 @@
#include "bar.hpp"
#include "client.hpp"
struct ext_idle_notification_v1;
namespace waybar::modules {
class IdleInhibitor : public ALabel {
sigc::connection timeout_;
ext_idle_notification_v1* idle_notification_;
uint32_t idle_timeout_ms_;
public:
IdleInhibitor(const std::string&, const waybar::Bar&, const Json::Value&);
virtual ~IdleInhibitor();
auto update() -> void override;
auto refresh(int) -> void override;
static std::list<waybar::AModule*> modules;
static bool status;
static long deactivationTime;
private:
bool handleToggle(GdkEventButton* const& e) override;
void toggleStatus();
bool handleScroll(GdkEventScroll* e) override;
void toggleStatus(int force_status = -1);
void setupIdleNotification();
void teardownIdleNotification();
static void handleIdled(void* data, ext_idle_notification_v1* notification);
static void handleResumed(void* data, ext_idle_notification_v1* notification);
const Bar& bar_;
struct zwp_idle_inhibitor_v1* idle_inhibitor_;
int pid_;
bool dynamicTimeout = false;
double timeout;
double timeout_step;
bool wait_for_activity_;
};
} // namespace waybar::modules
+66 -8
View File
@@ -14,6 +14,69 @@
namespace waybar::modules {
namespace image {
class IStrategy {
public:
virtual ~IStrategy() = default;
// Runs on the worker thread before update(). Use it for blocking work (e.g.
// spawning a user script) so the GTK main loop isn't stalled. Default no-op.
virtual void fetch() {}
virtual void update() = 0;
};
class SingleImageStrategy : public IStrategy {
public:
SingleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&,
bool);
~SingleImageStrategy() override = default;
void update() override;
private:
void parseOutputRaw();
util::command::res output_;
Json::Value config_;
Gtk::Image image_;
std::string path_;
std::string tooltip_;
int size_;
Gtk::Box box_;
bool hasTooltip_;
};
class MultipleImageStrategy : public IStrategy {
public:
MultipleImageStrategy(const std::string&, const Json::Value&, const std::string&, Gtk::EventBox&);
~MultipleImageStrategy() override = default;
void fetch() override;
void update() override;
private:
struct ImageData {
std::string path;
std::string marker;
std::string tooltip;
std::string on_click;
std::shared_ptr<Gtk::Image> img;
std::shared_ptr<Gtk::Button> btn;
};
void setImagesData(const Json::Value&);
void setupAndDraw();
void resetBoxAndMemory();
void handleClick(const Glib::ustring& data);
Json::Value config_;
int size_;
Gtk::Box box_;
std::vector<ImageData> images_data_;
// stdout captured by fetch() on the worker thread and consumed by update()
std::string exec_output_;
};
} // namespace image
class Image : public AModule {
public:
Image(const std::string&, const Json::Value&);
@@ -24,16 +87,11 @@ class Image : public AModule {
private:
void delayWorker();
void handleEvent();
void parseOutputRaw();
static std::unique_ptr<image::IStrategy> getStrategy(const std::string&, const Json::Value&,
const std::string&, Gtk::EventBox&, bool);
Gtk::Box box_;
Gtk::Image image_;
std::string path_;
std::string tooltip_;
int size_;
std::chrono::milliseconds interval_;
util::command::res output_;
std::unique_ptr<image::IStrategy> strategy_;
util::SleeperThread thread_;
};
+1 -2
View File
@@ -36,8 +36,7 @@ class KeyboardState : public AModule {
std::string capslock_format_;
std::string scrolllock_format_;
const std::chrono::seconds interval_;
std::string icon_locked_;
std::string icon_unlocked_;
std::unordered_map<std::string, std::vector<std::string>> key_icon_states_;
std::string devices_path_;
struct libinput* libinput_;
+69
View File
@@ -0,0 +1,69 @@
// include/modules/mango/backend.hpp
#pragma once
#include <atomic>
#include <list>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include "util/json.hpp"
namespace waybar::modules::mango {
class EventHandler {
public:
virtual void onEvent(const Json::Value& ev) = 0;
virtual ~EventHandler() = default;
};
class IPC {
public:
static IPC& getInstance();
IPC(const IPC&) = delete;
IPC& operator=(const IPC&) = delete;
void registerForIPC(const std::string& ev, EventHandler* handler);
void unregisterForIPC(EventHandler* handler);
static Json::Value send(const Json::Value& request);
static void sendAsync(const Json::Value& request);
std::unique_lock<std::mutex> lockData() { return std::unique_lock<std::mutex>(data_mutex_); }
std::unordered_map<std::string, Json::Value> getMonitors() const;
Json::Value getMonitor(const std::string& name);
Json::Value getActiveClientForMonitor(const std::string& name) const;
std::string getKeyboardLayout() const;
std::string getKeymode() const;
std::string getLayoutSymbolForMonitor(const std::string& name) const;
private:
IPC();
~IPC();
void startIPC();
static int connectToSocket();
void parseIPC(const std::string& line);
void handleMonitorUpdate(const Json::Value& mon);
void updateFocusingClient(const Json::Value& client);
void updateKeyboardLayout(const std::string& layout);
static Json::Value sendCommand(const std::string& cmd);
std::atomic<bool> running_ = true;
int sockfd_ = -1;
std::thread ipc_thread_;
mutable std::mutex data_mutex_;
std::unordered_map<std::string, Json::Value> monitors_;
std::unordered_map<uint64_t, Json::Value> clients_;
uint64_t focusing_client_id_ = 0;
std::string keyboard_layout_;
std::string keymode_;
Json::Value active_client_;
std::mutex callback_mutex_;
std::list<std::pair<std::string, EventHandler*>> callbacks_;
};
} // namespace waybar::modules::mango
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <mutex>
#include <string>
#include "ALabel.hpp"
#include "bar.hpp"
#include "modules/mango/backend.hpp"
namespace waybar::modules::mango {
class Keymode : public ALabel, public EventHandler {
public:
Keymode(const std::string&, const Bar&, const Json::Value&);
~Keymode() override;
void update() override;
private:
void onEvent(const Json::Value& ev) override;
void doUpdate();
std::mutex mutex_;
const Bar& bar_;
std::string last_keymode_;
};
} // namespace waybar::modules::mango
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <xkbcommon/xkbregistry.h>
#include <string>
#include "ALabel.hpp"
#include "bar.hpp"
#include "modules/mango/backend.hpp"
namespace waybar::modules::mango {
class Language : public ALabel, public EventHandler {
public:
Language(const std::string&, const Bar&, const Json::Value&);
~Language() override;
void update() override;
private:
void updateFromIPC();
void onEvent(const Json::Value& ev) override;
void doUpdate();
struct Layout {
std::string full_name;
std::string short_name;
std::string variant;
std::string short_description;
};
Layout getLayout(const std::string& fullName);
std::mutex mutex_;
const Bar& bar_;
std::vector<Layout> layouts_;
unsigned current_idx_;
std::string last_short_name_;
struct rxkb_context* rxkb_ctx_ = nullptr;
};
} // namespace waybar::modules::mango
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <mutex>
#include <string>
#include "ALabel.hpp"
#include "bar.hpp"
#include "modules/mango/backend.hpp"
namespace waybar::modules::mango {
class Layout : public ALabel, public EventHandler {
public:
Layout(const std::string&, const Bar&, const Json::Value&);
~Layout() override;
void update() override;
private:
void onEvent(const Json::Value& ev) override;
void doUpdate();
std::mutex mutex_;
const Bar& bar_;
std::string last_symbol_;
};
} // namespace waybar::modules::mango
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <gtkmm/button.h>
#include <json/value.h>
#include <mutex>
#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "modules/mango/backend.hpp"
namespace waybar::modules::mango {
class Window : public AAppIconLabel, public EventHandler {
public:
Window(const std::string&, const Bar&, const Json::Value&);
~Window() override;
void update() override;
private:
void onEvent(const Json::Value& ev) override;
void doUpdate();
void setClass(const std::string& className, bool enable);
const Bar& bar_;
std::string oldAppId_;
std::mutex mutex_;
};
} // namespace waybar::modules::mango
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <gtkmm/button.h>
#include <json/value.h>
#include <unordered_map>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/mango/backend.hpp"
namespace waybar::modules::mango {
class Workspaces : public AModule, public EventHandler {
public:
Workspaces(const std::string&, const Bar&, const Json::Value&);
~Workspaces() override;
void update() override;
private:
void onEvent(const Json::Value& ev) override;
void doUpdate();
Gtk::Button& addButton(uint64_t idx);
void updateButtonState(Gtk::Button& button, const Json::Value& tag, const Json::Value& monitor);
std::string getIcon(const std::string& value, const Json::Value& tag);
bool handleButtonClick(GdkEventButton* event, uint64_t idx, bool isOverview);
const Bar& bar_;
Gtk::Box box_;
std::unordered_map<uint64_t, Gtk::Button> buttons_;
Gtk::Button* overview_button_ = nullptr;
std::string on_click_left_;
std::string on_click_middle_;
std::string on_click_right_;
};
} // namespace waybar::modules::mango
-2
View File
@@ -19,8 +19,6 @@ class Memory : public ALabel {
private:
void parseMeminfo();
static float calc_divisor(const std::string& divisor);
std::unordered_map<std::string, unsigned long> meminfo_;
util::SleeperThread thread_;
+8 -1
View File
@@ -28,11 +28,14 @@ class MPD : public ALabel {
unsigned timeout_;
unsigned playing_interval_;
detail::unique_connection connection_;
detail::unique_status status_;
mpd_state state_;
detail::unique_song song_;
std::string ellipsis_;
public:
MPD(const std::string&, const Json::Value&);
@@ -45,6 +48,10 @@ class MPD : public ALabel {
void setLabel();
std::string getStateIcon() const;
std::string getOptionIcon(const std::string& optionName, bool activated) const;
std::string getArtistStr(bool truncated) const;
std::string getAlbumArtistStr(bool truncated) const;
std::string getAlbumStr(bool truncated) const;
std::string getTitleStr(bool truncated) const;
// GUI-side methods
bool handlePlayPause(GdkEventButton* const&);
@@ -54,11 +61,11 @@ class MPD : public ALabel {
void tryConnect();
void checkErrors(mpd_connection* conn);
void fetchState();
void queryMPD();
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; }
inline unsigned playing_interval() const { return playing_interval_; }
};
#if !defined(MPD_NOINLINE)
+5 -1
View File
@@ -82,6 +82,7 @@ class Idle : public State {
class Playing : public State {
Context* const ctx_;
sigc::connection timer_connection_;
sigc::connection idle_connection_;
public:
Playing(Context* const ctx) : ctx_{ctx} {}
@@ -98,7 +99,10 @@ class Playing : public State {
Playing(Playing const&) = delete;
Playing& operator=(Playing const&) = delete;
void timer() noexcept;
void idle() noexcept;
bool on_timer();
bool on_io(Glib::IOCondition const&);
};
class Paused : public State {
@@ -194,10 +198,10 @@ class Context {
bool is_paused() const;
bool is_stopped() const;
constexpr std::size_t interval() const;
unsigned playing_interval() const;
void tryConnect() const;
void checkErrors(mpd_connection*) const;
void do_update();
void queryMPD() const;
void fetchState() const;
constexpr mpd_state state() const;
void emit() const;
+3 -2
View File
@@ -1,13 +1,15 @@
#pragma once
namespace detail {
using namespace std::literals::chrono_literals;
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
inline bool Context::is_playing() const { return mpd_module_->playing(); }
inline bool Context::is_paused() const { return mpd_module_->paused(); }
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); }
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_ / 1s; }
inline unsigned Context::playing_interval() const { return mpd_module_->playing_interval(); }
inline void Context::tryConnect() const { mpd_module_->tryConnect(); }
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
@@ -15,7 +17,6 @@ constexpr inline mpd_state Context::state() const { return mpd_module_->state_;
inline void Context::do_update() { mpd_module_->setLabel(); }
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
inline void Context::queryMPD() const { mpd_module_->queryMPD(); }
inline void Context::fetchState() const { mpd_module_->fetchState(); }
inline void Context::emit() const { mpd_module_->emit(); }
+3
View File
@@ -38,6 +38,7 @@ class Mpris : public ALabel {
std::optional<std::string> artist;
std::optional<std::string> album;
std::optional<std::string> album_artist;
std::optional<std::string> title;
std::optional<std::string> length; // as HH:MM:SS
std::optional<std::string> position; // same format
@@ -76,6 +77,8 @@ class Mpris : public ALabel {
std::string player_;
std::vector<std::string> ignored_players_;
bool prefer_album_artist_;
PlayerctlPlayerManager* manager;
PlayerctlPlayer* player;
PlayerctlPlayer* last_active_player_ = nullptr;
+9
View File
@@ -17,6 +17,8 @@
#include "util/rfkill.hpp"
#endif
#define ETH_ALEN 6
enum ip_addr_pref : uint8_t { IPV4, IPV6, IPV4_6 };
namespace waybar::modules {
@@ -34,6 +36,7 @@ class Network : public ALabel {
static int handleEvents(struct nl_msg*, void*);
static int handleEventsDone(struct nl_msg*, void*);
static int handleScan(struct nl_msg*, void*);
static int handleStationGet(struct nl_msg *msg, void *data);
void askForStateDump(void);
@@ -48,15 +51,18 @@ class Network : public ALabel {
bool matchInterface(const std::string& ifname, const std::vector<std::string>& altnames,
std::string& matched) const;
auto getInfo() -> void;
bool isWireless() const;
const std::string getNetworkState() const;
void clearIface();
std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage();
uint32_t readLinkSpeed() const;
int ifid_{-1};
ip_addr_pref addr_pref_{ip_addr_pref::IPV4};
struct sockaddr_nl nladdr_{0};
struct nl_sock* sock_{nullptr};
struct nl_sock* ev_sock_{nullptr};
struct nl_sock* station_sock_{nullptr};
int efd_{-1};
int ev_fd_{-1};
int nl80211_id_{-1};
@@ -90,6 +96,7 @@ class Network : public ALabel {
uint8_t signal_strength_;
std::string signal_strength_app_;
uint32_t route_priority;
uint32_t link_speed_{0};
util::SleeperThread thread_;
util::SleeperThread thread_timer_;
@@ -97,6 +104,8 @@ class Network : public ALabel {
util::Rfkill rfkill_{RFKILL_TYPE_WLAN};
#endif
float frequency_{0};
uint32_t tx_bitrate_{0};
uint32_t rx_bitrate_{0};
};
} // namespace waybar::modules
+5 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <list>
#include <mutex>
#include <string>
@@ -18,6 +19,7 @@ class EventHandler {
class IPC {
public:
IPC();
~IPC();
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler);
@@ -32,7 +34,7 @@ class IPC {
unsigned keyboardLayoutCurrent() const { return keyboardLayoutCurrent_; }
private:
void startIPC();
void startIPC(int initial_socketfd);
static int connectToSocket();
void parseIPC(const std::string&);
@@ -45,6 +47,8 @@ class IPC {
util::JsonParser parser_;
std::mutex callbackMutex_;
std::list<std::pair<std::string, EventHandler*>> callbacks_;
std::atomic<bool> running_{true};
};
inline std::unique_ptr<IPC> gIPC;
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/image.h>
#include <gtkmm/label.h>
#include <json/value.h>
#include <string>
namespace waybar::modules::niri {
class Workspaces;
class Workspace {
public:
Workspace(const Json::Value& workspace_data, Workspaces& manager);
~Workspace() = default;
Workspace(const Workspace&) = delete;
Workspace& operator=(const Workspace&) = delete;
Gtk::Button& button() { return button_; }
uint64_t id() const { return id_; }
void update(const Json::Value& workspace_data, const std::vector<Json::Value>& all_windows,
const std::string& windows_str, std::size_t total);
private:
void rebuildTaskbar(const std::vector<Json::Value>& my_windows);
Glib::RefPtr<Gdk::Pixbuf> loadIcon(const std::string& app_id, int size);
Workspaces& manager_;
uint64_t id_;
// Layout: button_
// └─ box_ (horizontal)
// ├─ label_ workspace label / icon
// └─ taskbar_box_ app icon buttons (shown only when taskbar enabled)
Gtk::Button button_;
Gtk::Box box_;
Gtk::Label label_;
Gtk::Box taskbar_box_;
};
} // namespace waybar::modules::niri
+38 -6
View File
@@ -1,30 +1,62 @@
#pragma once
#include <gtkmm/button.h>
#include <gtkmm/box.h>
#include <json/value.h>
#include <memory>
#include <regex>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/niri/backend.hpp"
#include "modules/niri/workspace.hpp"
#include "util/regex_collection.hpp" // Added for rewrite rules
namespace waybar::modules::niri {
class Workspaces : public AModule, public EventHandler {
public:
Workspaces(const std::string&, const Bar&, const Json::Value&);
Workspaces(const std::string& id, const Bar& bar, const Json::Value& config);
~Workspaces() override;
void update() override;
const Json::Value& config() const { return config_; }
const Bar& bar() const { return bar_; }
std::string getIcon(const std::string& value, const Json::Value& ws) const;
private:
void onEvent(const Json::Value& ev) override;
void doUpdate();
Gtk::Button& addButton(const Json::Value& ws);
std::string getIcon(const std::string& value, const Json::Value& ws);
void createWorkspace(const Json::Value& workspace_data);
void sortWorkspaces(std::vector<const Json::Value*>& workspaces) const;
bool isWorkspaceIgnored(const std::string& name);
bool handleScroll(GdkEventScroll* /*unused*/) override;
// Added for window rewrite
void populateWindowRewriteConfig();
void populateFormatWindowSeparatorConfig();
std::string getRewrite(const std::string& app_id, const std::string& title);
std::string getWindowsRepresentation(const Json::Value& ws);
const Bar& bar_;
Gtk::Box box_;
// Map from niri workspace id to button.
std::unordered_map<uint64_t, Gtk::Button> buttons_;
std::vector<std::unique_ptr<Workspace>> workspaces_;
// Vec of regex rules to ignore workspaces.
std::vector<std::regex> ignoreWorkspaces_;
bool sort_by_id_ = false;
bool sort_by_name_ = false;
bool sort_by_coordinates_ = false;
// Added for window rewrite
util::RegexCollection m_windowRewriteRules;
std::string m_windowRewriteDefault;
std::string m_formatWindowSeparator;
};
} // namespace waybar::modules::niri
+10 -1
View File
@@ -9,9 +9,18 @@ namespace waybar::modules {
struct Profile {
std::string name;
// Legacy driver field, kept for backward compatibility with the
// `{driver}` format placeholder and with older power-profiles-daemon
// versions that only expose a single `Driver` DBus property.
std::string driver;
std::string cpuDriver;
std::string platformDriver;
Profile(std::string n, std::string d) : name(std::move(n)), driver(std::move(d)) {}
Profile(std::string n, std::string d, std::string cd, std::string pd)
: name(std::move(n)),
driver(std::move(d)),
cpuDriver(std::move(cd)),
platformDriver(std::move(pd)) {}
};
class PowerProfilesDaemon : public ALabel {
+17 -7
View File
@@ -1,40 +1,50 @@
#pragma once
#include <atomic>
#include <string>
#include "gtkmm/box.h"
#include "modules/privacy/privacy_item.hpp"
#include "util/geoclue_backend.hpp"
#include "util/pipewire/pipewire_backend.hpp"
#include "util/pipewire/privacy_node_info.hpp"
using waybar::util::PipewireBackend::PrivacyNodeInfo;
using waybar::util::PipewireBackend::PrivacyNodeType;
using waybar::util::PipewireBackend::PWPrivacyNodeInfo;
namespace waybar::modules::privacy {
class Privacy : public AModule {
public:
Privacy(const std::string&, const Json::Value&, Gtk::Orientation, const std::string& pos);
~Privacy() override;
auto update() -> void override;
void onPrivacyNodesChanged();
private:
std::list<PrivacyNodeInfo*> nodes_screenshare; // Screen is being shared
std::list<PrivacyNodeInfo*> nodes_audio_in; // Application is using the microphone
std::list<PrivacyNodeInfo*> nodes_audio_out; // Application is outputting audio
std::list<PWPrivacyNodeInfo*> nodes_screenshare; // Screen is being shared
std::list<PWPrivacyNodeInfo*> nodes_audio_in; // Application is using the microphone
std::list<PWPrivacyNodeInfo*> nodes_audio_out; // Application is outputting audio
std::atomic<bool> location_in_use; // GeoClue is being used
std::mutex mutex_;
sigc::connection visibility_conn;
sigc::connection geoclue_timeout_conn;
// Config
Gtk::Box box_;
std::vector<PrivacyItem*> modules_;
uint iconSpacing = 4;
uint iconSize = 20;
uint transition_duration = 250;
std::set<std::pair<PrivacyNodeType, std::string>> ignore;
bool ignore_monitor = true;
std::shared_ptr<util::PipewireBackend::PipewireBackend> backend = nullptr;
std::shared_ptr<util::PipewireBackend::PipewireBackend> pw_backend = nullptr;
std::shared_ptr<util::GeoClueBackend::GeoClueBackend> geoclue_backend = nullptr;
void onPWPrivacyNodesChanged();
bool locationTimeout(bool in_use);
void onGeoCluePrivacyNodesChanged();
};
} // namespace waybar::modules::privacy
+35 -10
View File
@@ -9,35 +9,36 @@
#include "gtkmm/revealer.h"
#include "util/pipewire/privacy_node_info.hpp"
using waybar::util::PipewireBackend::PrivacyNodeInfo;
using waybar::util::PipewireBackend::PrivacyNodeType;
using waybar::util::PipewireBackend::PWPrivacyNodeInfo;
namespace waybar::modules::privacy {
class PrivacyItem : public Gtk::Revealer {
public:
protected:
PrivacyItem(const Json::Value& config_, enum PrivacyNodeType privacy_type_,
std::list<PrivacyNodeInfo*>* nodes, Gtk::Orientation orientation,
const std::string& pos, const uint icon_size, const uint transition_duration);
Gtk::Orientation orientation, const std::string& pos, const uint icon_size,
const uint transition_duration);
public:
virtual void set_tooltip() = 0;
enum PrivacyNodeType privacy_type;
void set_in_use(bool in_use);
private:
std::list<PrivacyNodeInfo*>* nodes;
sigc::connection signal_conn;
uint tooltipIconSize = 24;
Gtk::Box tooltip_window;
private:
sigc::connection signal_conn;
bool init = false;
bool in_use = false;
// Config
std::string iconName = "image-missing-symbolic";
bool tooltip = true;
uint tooltipIconSize = 24;
Gtk::Box box_;
Gtk::Image icon_;
@@ -45,4 +46,28 @@ class PrivacyItem : public Gtk::Revealer {
void update_tooltip();
};
class GeoCluePrivacyItem : public PrivacyItem {
public:
GeoCluePrivacyItem(const Json::Value& config_, Gtk::Orientation orientation,
const std::string& pos, const uint icon_size, const uint transition_duration)
: PrivacyItem(config_, util::PipewireBackend::PRIVACY_NODE_TYPE_LOCATION, orientation, pos,
icon_size, transition_duration) {}
void set_tooltip() override;
};
class PWPrivacyItem : public PrivacyItem {
public:
PWPrivacyItem(const Json::Value& config_, enum PrivacyNodeType privacy_type_,
std::list<PWPrivacyNodeInfo*>* nodes_, Gtk::Orientation orientation,
const std::string& pos, const uint icon_size, const uint transition_duration)
: PrivacyItem(config_, privacy_type_, orientation, pos, icon_size, transition_duration),
nodes(nodes_) {}
void set_tooltip() override;
private:
std::list<PWPrivacyNodeInfo*>* nodes;
};
} // namespace waybar::modules::privacy
+1
View File
@@ -22,6 +22,7 @@ class Pulseaudio : public ALabel {
const std::vector<std::string> getPulseIcon() const;
std::shared_ptr<util::AudioBackend> backend = nullptr;
util::PulseaudioTarget target = util::PulseaudioTarget::Sink;
};
} // namespace waybar::modules
+10 -7
View File
@@ -6,11 +6,6 @@
#include "util/audio_backend.hpp"
namespace waybar::modules {
enum class PulseaudioSliderTarget {
Sink,
Source,
};
class PulseaudioSlider : public ASlider {
public:
PulseaudioSlider(const std::string&, const Json::Value&);
@@ -21,7 +16,15 @@ class PulseaudioSlider : public ASlider {
private:
std::shared_ptr<util::AudioBackend> backend = nullptr;
PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink;
util::PulseaudioTarget target = util::PulseaudioTarget::Sink;
bool zero_on_mute = true;
bool unmute_on_volume_change = true;
// zero_on_mute and unmute_on_volume_change default to true
// in order to maintain previous behaviour when using a
// config in which these values are undefined
bool previously_muted = false;
};
} // namespace waybar::modules
} // namespace waybar::modules
+6 -4
View File
@@ -21,6 +21,8 @@ class Tags : public waybar::AModule {
void handle_view_tags(struct wl_array* tags);
void handle_urgent_tags(uint32_t tags);
void handle_focused_view(const char *title, uint32_t tags);
void handle_focused_output(struct wl_output* output);
void handle_unfocused_output(struct wl_output* output);
void handle_show();
void handle_primary_clicked(uint32_t tag);
@@ -28,17 +30,17 @@ class Tags : public waybar::AModule {
struct zriver_status_manager_v1* status_manager_;
struct zriver_control_v1* control_;
struct zriver_seat_status_v1 *seat_status_;
struct wl_seat* seat_;
// used to make sure the focused view tags are on the correct output
const wl_output* output_;
const wl_output* focused_output_;
private:
const waybar::Bar& bar_;
struct wl_output* focused_output_; // stores the focused output
struct wl_output* output_; // stores the output this module belongs to
Gtk::Box box_;
std::vector<Gtk::Button> buttons_;
struct zriver_output_status_v1* output_status_;
struct zriver_seat_status_v1* seat_status_;
bool hide_vacant_ = false; // parsed once; asBool() in a wl callback would throw (#4078)
};
} /* namespace waybar::modules::river */
+21 -6
View File
@@ -14,16 +14,22 @@ namespace waybar::modules::SNI {
class Host {
public:
Host(const std::size_t id, const Json::Value&, const Bar&,
Host(std::size_t id, const Json::Value&, const Bar&, const std::vector<std::string>&,
const std::function<void(std::unique_ptr<Item>&)>&,
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&,
const std::function<void()>&);
~Host();
void checkIgnoreList(const std::vector<std::string>& ignore_list,
const std::function<void(std::unique_ptr<Item>&)>& on_remove);
void reorderItems();
private:
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring,
void busAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
void nameAppeared(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&,
const Glib::ustring&);
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, Glib::ustring);
void nameVanished(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
static void proxyReady(GObject*, GAsyncResult*, gpointer);
static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
@@ -33,7 +39,7 @@ class Host {
void removeItem(std::vector<std::unique_ptr<Item>>::iterator);
void clearItems();
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
static std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string&);
void addRegisteredItem(const std::string& service);
std::vector<std::unique_ptr<Item>> items_;
@@ -43,10 +49,19 @@ class Host {
std::size_t watcher_id_;
GCancellable* cancellable_ = nullptr;
SnWatcher* watcher_ = nullptr;
sigc::connection retry_connection_;
unsigned retry_count_ = 0;
const Json::Value& config_;
const Bar& bar_;
const std::vector<std::string> ignore_list_;
const std::function<void(std::unique_ptr<Item>&)> on_add_;
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
// Re-applies the configured ordering to the already-added tray widgets. This
// must NOT re-run the add path (which would re-parent widgets and reconnect
// signals); it only reorders existing children.
const std::function<void()> on_reorder_;
ItemOrderMap orders_;
const std::function<void()> on_update_;
};
+10 -1
View File
@@ -5,6 +5,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
class IconManager {
public:
@@ -19,7 +20,10 @@ class IconManager {
std::string app_name = key;
const Json::Value& icon_value = icons_config[key];
if (icon_value.isString()) {
if (icon_value.isBool() && !icon_value.asBool()) {
// false value means hide this app
hidden_apps_.insert(app_name);
} else if (icon_value.isString()) {
std::string icon_path = icon_value.asString();
icons_map_[app_name] = icon_path;
}
@@ -37,7 +41,12 @@ class IconManager {
return "";
}
bool isHidden(const std::string& app_name) const {
return hidden_apps_.find(app_name) != hidden_apps_.end();
}
private:
IconManager() = default;
std::unordered_map<std::string, std::string> icons_map_;
std::unordered_set<std::string> hidden_apps_;
};
+12 -1
View File
@@ -24,11 +24,15 @@ struct ToolTip {
Glib::ustring text;
};
class Host;
using ItemOrderMap = std::unordered_map<std::string, int>;
class Item : public sigc::trackable {
public:
Item(const std::string&, const std::string&, const Json::Value&, const Bar&,
const std::function<void(Item&)>&, const std::function<void(Item&)>&,
const std::function<void()>&);
const std::function<void()>&, Host&, const ItemOrderMap&);
~Item();
bool isReady() const;
@@ -46,6 +50,7 @@ class Item : public sigc::trackable {
std::string title;
std::string icon_name;
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
bool has_custom_icon_ = false;
Glib::RefPtr<Gtk::IconTheme> icon_theme;
std::string overlay_icon_name;
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
@@ -63,6 +68,7 @@ class Item : public sigc::trackable {
* while compliant SNI implementation would always reset the flag to desired value.
*/
bool item_is_menu = true;
int order_ = -1; // -1 means not set
private:
void onConfigure(GdkEventConfigure* ev);
@@ -100,6 +106,8 @@ class Item : public sigc::trackable {
gdouble distance_scrolled_y_ = 0;
// visibility of items with Status == Passive
bool show_passive_ = false;
// hidden via config
bool is_hidden_ = false;
bool ready_ = false;
Glib::ustring status_ = "active";
@@ -111,6 +119,9 @@ class Item : public sigc::trackable {
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
Glib::RefPtr<Gio::Cancellable> cancellable_;
std::set<std::string_view> update_pending_;
Host& host_;
const ItemOrderMap& orders_;
};
} // namespace waybar::modules::SNI
+15 -2
View File
@@ -1,6 +1,10 @@
#pragma once
#include <fmt/format.h>
#include <sigc++/connection.h>
#include <unordered_map>
#include <utility>
#include "AModule.hpp"
#include "bar.hpp"
@@ -13,19 +17,28 @@ namespace waybar::modules::SNI {
class Tray : public AModule {
public:
Tray(const std::string&, const Bar&, const Json::Value&);
virtual ~Tray() = default;
~Tray() override = default;
auto update() -> void override;
private:
void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item);
// Reorders the already-added tray widgets by their configured order. Does not
// add or remove any widget.
void reorderBox();
void checkIgnoreList(std::unique_ptr<Item>* item);
std::vector<std::string> parseIgnoreList(const Json::Value& config);
void queueUpdate();
static inline std::size_t nb_hosts_ = 0;
bool show_passive_ = false;
Gtk::Box box_;
SNI::Watcher::singleton watcher_;
std::vector<std::string> ignore_list_;
SNI::Host host_;
std::vector<Item*> items_;
// signal_show/signal_hide connections owned per added item, so they can be
// disconnected on removal instead of leaking and accumulating.
std::unordered_map<Item*, std::pair<sigc::connection, sigc::connection>> item_connections_;
};
} // namespace waybar::modules::SNI
+15 -8
View File
@@ -1,15 +1,13 @@
#pragma once
#include <sigc++/sigc++.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include <memory>
#include <atomic>
#include <cstdint>
#include <functional>
#include <mutex>
#include <stdexcept>
#include <string>
#include <vector>
#include "ipc.hpp"
#include "util/SafeSignal.hpp"
@@ -41,11 +39,20 @@ class Ipc {
static inline const std::string ipc_magic_ = "i3-ipc";
static inline const size_t ipc_header_size_ = ipc_magic_.size() + 8;
const std::string getSocketPath() const;
int open(const std::string&) const;
static std::string getSocketPath();
static int open(const std::string&);
struct ipc_response send(int fd, uint32_t type, const std::string& payload = "");
struct ipc_response recv(int fd);
// Re-establish the event socket and re-subscribe after sway drops us, backing
// off between attempts so we don't busy-loop while sway is unavailable.
void reconnectEvent();
std::string socketPath_;
std::vector<std::string> subscribed_events_;
std::atomic<bool> running_{true};
util::ScopedFd fd_;
util::ScopedFd fd_event_;
std::mutex mutex_;
+3
View File
@@ -54,6 +54,9 @@ class Language : public ALabel, public sigc::trackable {
const static std::string XKB_ACTIVE_LAYOUT_NAME_KEY;
Layout layout_;
// CSS class currently applied to label_. Tracked so update() (main thread) can swap classes
// instead of set_current_layout() mutating the widget from the IPC worker thread (#3702).
std::string applied_class_;
std::string tooltip_format_ = "";
std::map<std::string, Layout> layouts_map_;
bool hide_single_;
+7
View File
@@ -4,6 +4,7 @@
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <optional>
#include <string_view>
#include <unordered_map>
@@ -24,12 +25,15 @@ class Workspaces : public AModule, public sigc::trackable {
private:
static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\"";
static constexpr std::string_view workspace_switch_number_cmd_ = "workspace {} number {}";
static constexpr std::string_view persistent_workspace_switch_cmd_ =
R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")";
static int convertWorkspaceNameToNum(const std::string& name);
static int windowRewritePriorityFunction(std::string const& window_rule);
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
bool isWorkspaceIgnored(std::string const& name);
void onCmd(const struct Ipc::ipc_response&);
void onEvent(const struct Ipc::ipc_response&);
bool filterButtons();
@@ -41,6 +45,7 @@ class Workspaces : public AModule, public sigc::trackable {
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
uint16_t getWorkspaceIndex(const std::string& name) const;
static std::string trimWorkspaceName(const std::string&);
std::optional<uint16_t> getCustomSortIndex(const std::string& name) const;
bool handleScroll(GdkEventScroll* /*unused*/) override;
const Bar& bar_;
@@ -49,9 +54,11 @@ class Workspaces : public AModule, public sigc::trackable {
std::vector<std::string> workspaces_order_;
Gtk::Box box_;
std::string m_formatWindowSeparator;
std::vector<std::regex> m_ignoreWorkspaces;
util::RegexCollection m_windowRewriteRules;
util::JsonParser parser_;
std::unordered_map<std::string, Gtk::Button> buttons_;
std::unordered_map<std::string, uint16_t> custom_sort_priorities_;
std::mutex mutex_;
Ipc ipc_;
};
+2
View File
@@ -14,6 +14,8 @@ class Temperature : public ALabel {
Temperature(const std::string&, const Json::Value&);
virtual ~Temperature() = default;
auto update() -> void override;
void suspend() override;
void resume() override;
private:
float getTemperature();
+27 -4
View File
@@ -17,6 +17,11 @@ class Wireplumber : public ALabel {
auto update() -> void override;
private:
bool setupConnection();
void teardownConnection();
void scheduleReconnect();
bool onReconnectTimeout();
static void onCoreDisconnected(waybar::modules::Wireplumber* self);
void asyncLoadRequiredApiModules();
void prepare(waybar::modules::Wireplumber* self);
void activatePlugins();
@@ -24,17 +29,25 @@ class Wireplumber : public ALabel {
static void updateNodeName(waybar::modules::Wireplumber* self, uint32_t id);
static void updateSourceVolume(waybar::modules::Wireplumber* self, uint32_t id);
static void updateSourceName(waybar::modules::Wireplumber* self, uint32_t id); // NEW
static void onPluginActivated(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res,
waybar::modules::Wireplumber* self);
static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, waybar::modules::Wireplumber* self);
static void onPluginActivated(WpObject* p, GAsyncResult* res, gpointer data);
static void onDefaultNodesApiLoaded(WpObject* p, GAsyncResult* res, gpointer data);
static void onMixerApiLoaded(WpObject* p, GAsyncResult* res, gpointer data);
static void onObjectManagerInstalled(waybar::modules::Wireplumber* self);
static void onMixerChanged(waybar::modules::Wireplumber* self, uint32_t id);
static void onDefaultNodesApiChanged(waybar::modules::Wireplumber* self);
bool handleScroll(GdkEventScroll* e) override;
std::vector<std::string> getWPIcon();
static std::list<waybar::modules::Wireplumber*> modules;
// Returns true while `self` is still a live module. Async load/activation callbacks use this to
// avoid dereferencing a `self` that was destroyed before the callback fired (see #3974).
static bool isModuleAlive(waybar::modules::Wireplumber* self);
uint32_t resolvePhysicalSink(uint32_t start_id);
uint32_t findPlaybackNodeId(const gchar* description);
uint32_t get_linked_sink_id(WpObjectManager* om, uint32_t from_node_id);
uint32_t get_linked_node_from_output_ports(WpObjectManager* om, uint32_t from_node_id);
WpCore* wp_core_;
GPtrArray* apis_;
@@ -43,6 +56,10 @@ class Wireplumber : public ALabel {
WpPlugin* def_nodes_api_;
gchar* default_node_name_;
uint32_t pending_plugins_;
// Bumped on every (re)connection. The async load/activate callbacks capture the generation they
// were scheduled under (via their user_data) and no-op if it no longer matches, so a completion
// from a connection that was already torn down cannot corrupt the new generation's state (#2882).
uint32_t connection_generation_{0};
bool muted_;
double volume_;
double min_step_;
@@ -54,6 +71,12 @@ class Wireplumber : public ALabel {
bool source_muted_;
double source_volume_;
gchar* default_source_name_;
bool only_physical_;
bool resolved_physical_;
std::string form_factor_;
// Timer used to retry connecting to PipeWire after it goes away; disconnected in the destructor
// so a pending attempt can't outlive the module. See #2882.
sigc::connection reconnect_timer_;
};
} // namespace waybar::modules
+44
View File
@@ -11,6 +11,7 @@
#include <map>
#include <memory>
#include <ranges>
#include <string>
#include <unordered_set>
#include <vector>
@@ -18,6 +19,7 @@
#include "AModule.hpp"
#include "bar.hpp"
#include "client.hpp"
#include "ext-workspace-v1-client-protocol.h"
#include "giomm/desktopappinfo.h"
#include "util/icon_loader.hpp"
#include "util/json.hpp"
@@ -68,6 +70,11 @@ class Task {
Glib::RefPtr<Gio::DesktopAppInfo> app_info_;
bool button_visible_ = false;
bool ignored_ = false;
bool squashed_ = false;
/* Whether the toplevel is on this bar's output, per the protocol's
* output_enter/output_leave events */
bool on_bar_output_ = false;
bool size_allocate_connected_ = false;
bool with_icon_ = false;
bool with_name_ = false;
@@ -80,6 +87,7 @@ class Task {
std::string title_;
std::string app_id_;
uint32_t state_ = 0;
struct ext_workspace_handle_v1* workspace_ = nullptr;
int32_t drag_start_x;
int32_t drag_start_y;
@@ -91,6 +99,9 @@ class Task {
void set_minimize_hint();
void on_button_size_allocated(Gtk::Allocation& alloc);
void hide_if_ignored();
void hide_if_duplicate();
void show_button();
void hide_button();
public:
/* Getter functions */
@@ -102,6 +113,9 @@ class Task {
bool minimized() const { return state_ & MINIMIZED; }
bool active() const { return state_ & ACTIVE; }
bool fullscreen() const { return state_ & FULLSCREEN; }
bool visible() const { return button_visible_; }
struct ext_workspace_handle_v1* workspace() const { return workspace_; }
void set_workspace(struct ext_workspace_handle_v1* workspace) { workspace_ = workspace; }
public:
/* Callbacks for the wlr protocol */
@@ -142,6 +156,12 @@ using TaskPtr = std::unique_ptr<Task>;
class Taskbar : public waybar::AModule {
public:
struct WorkspaceState {
Taskbar* taskbar;
struct ext_workspace_handle_v1* handle;
uint32_t state = 0;
};
Taskbar(const std::string&, const waybar::Bar&, const Json::Value&);
~Taskbar();
void update();
@@ -153,32 +173,56 @@ class Taskbar : public waybar::AModule {
IconLoader icon_loader_;
std::unordered_set<std::string> ignore_list_;
std::unordered_set<std::string> squash_list_;
std::map<std::string, std::string> app_ids_replace_map_;
struct zwlr_foreign_toplevel_manager_v1* manager_;
struct ext_workspace_manager_v1* workspace_manager_;
struct wl_seat* seat_;
std::vector<struct ext_workspace_group_handle_v1*> workspace_groups_;
std::vector<std::unique_ptr<WorkspaceState>> workspaces_;
struct ext_workspace_handle_v1* current_workspace_ = nullptr;
public:
/* Callbacks for global registration */
void register_manager(struct wl_registry*, uint32_t name, uint32_t version);
void register_workspace_manager(struct wl_registry*, uint32_t name, uint32_t version);
void register_seat(struct wl_registry*, uint32_t name, uint32_t version);
/* Callbacks for the wlr protocol */
void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1*);
void handle_finished();
void handle_workspace_group_create(struct ext_workspace_group_handle_v1*);
void handle_workspace_group_removed(struct ext_workspace_group_handle_v1*);
void handle_workspace_create(struct ext_workspace_handle_v1*);
void handle_workspace_done();
void handle_workspace_finished();
void handle_workspace_removed(struct ext_workspace_handle_v1*);
public:
void add_button(Gtk::Button&);
void move_button(Gtk::Button&, int);
void remove_button(Gtk::Button&);
void remove_task(uint32_t);
void assign_current_workspace(Task&);
void update_bar_css_classes();
bool show_output(struct wl_output*) const;
bool all_outputs() const;
const IconLoader& icon_loader() const;
const std::unordered_set<std::string>& ignore_list() const;
const std::unordered_set<std::string>& squash_list() const;
const std::map<std::string, std::string>& app_ids_replace_map() const;
std::size_t task_id_count(std::string_view id) const;
std::size_t task_title_count(std::string_view title) const;
auto tasks() {
return tasks_ | std::views::transform([](auto& task) -> Task& { return *task; });
}
private:
void set_bar_css_class(const std::string&, bool);
};
} /* namespace waybar::modules::wlr */
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <fmt/format.h>
#include <libmm-glib/libmm-glib.h>
#include <sys/statvfs.h>
#include <fstream>
#include "ALabel.hpp"
#include "util/format.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules {
class Wwan : public ALabel {
public:
Wwan(const std::string&, const Json::Value&);
virtual ~Wwan();
auto update() -> void override;
private:
void updateCurrentModem();
util::SleeperThread thread_;
std::string state_;
GDBusConnection* connection = nullptr;
MMManager* manager = nullptr;
MMModem* current_modem = nullptr;
bool hideDisconnected = true;
const std::string dbus_name = "org.freedesktop.ModemManager1";
const std::string dbus_obj_path = "/org/freedesktop/ModemManager1/";
const std::string dbus_modems_path = "/org/freedesktop/ModemManager1/Modems/";
};
} // namespace waybar::modules
-5
View File
@@ -12,11 +12,6 @@
#include <type_traits>
#include <utility>
#ifdef __OpenBSD__
#define SIGRTMIN SIGUSR1 - 1
#define SIGRTMAX SIGUSR1 + 1
#endif
namespace waybar {
/**
+25 -3
View File
@@ -14,6 +14,11 @@
namespace waybar::util {
enum class PulseaudioTarget {
Sink,
Source,
};
class AudioBackend {
private:
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
@@ -22,12 +27,21 @@ class AudioBackend {
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*);
static void sourceVolumeModifyCb(pa_context*, int, void*);
void connectContext();
// Non-throwing reconnect used from the PulseAudio callback thread. Throwing
// across the libpulse C callback boundary calls std::terminate, so this
// swallows any failure and reports it via the return value instead.
bool reconnectContext() noexcept;
pa_threaded_mainloop* mainloop_;
pa_mainloop_api* mainloop_api_;
pa_context* context_;
// Guards against the FAILED -> connect -> FAILED recursion / busy loop when a
// reconnect attempt fails synchronously inside pa_context_connect().
bool reconnecting_{false};
pa_cvolume pa_volume_;
pa_cvolume pa_source_volume_;
// SINK
uint32_t sink_idx_{0};
@@ -50,6 +64,7 @@ class AudioBackend {
std::string default_source_name_;
std::vector<std::string> ignored_sinks_;
std::map<std::string, std::string> sink_mapping_;
std::function<void()> on_updated_cb_ = NOOP;
@@ -67,10 +82,13 @@ class AudioBackend {
AudioBackend(std::function<void()> on_updated_cb, private_constructor_tag tag);
~AudioBackend();
void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100);
void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100);
void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100,
PulseaudioTarget target = PulseaudioTarget::Sink);
void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100,
PulseaudioTarget target = PulseaudioTarget::Sink);
void setIgnoredSinks(const Json::Value& config);
void setSinkMapping(const Json::Value& config);
std::string getSinkPortName() const { return port_name_; }
std::string getFormFactor() const { return form_factor_; }
@@ -92,7 +110,11 @@ class AudioBackend {
void toggleSourceMute();
void toggleSourceMute(bool);
uint16_t getVolume(PulseaudioTarget) const;
bool getMuted(PulseaudioTarget) const;
void unmute(PulseaudioTarget);
bool isBluetooth();
};
} // namespace waybar::util
} // namespace waybar::util
+6 -2
View File
@@ -27,9 +27,11 @@ namespace waybar::util {
class BacklightDevice {
public:
BacklightDevice() = default;
BacklightDevice(std::string name, int actual, int max, bool powered);
BacklightDevice(std::string name, int actual, int max, bool powered,
std::string subsystem = "backlight");
std::string name() const;
std::string subsystem() const;
int get_actual() const;
void set_actual(int actual);
int get_max() const;
@@ -45,6 +47,7 @@ class BacklightDevice {
int actual_ = 1;
int max_ = 1;
bool powered_ = true;
std::string subsystem_ = "backlight";
};
class BacklightBackend {
@@ -70,7 +73,8 @@ class BacklightBackend {
std::mutex udev_thread_mutex_;
private:
void set_brightness_internal(const std::string& device_name, int brightness, int max_brightness);
void set_brightness_internal(const std::string& device_name, int brightness, int max_brightness,
const std::string& subsystem = "backlight");
std::function<void()> on_updated_cb_;
std::chrono::milliseconds polling_interval_;
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <glibmm/main.h>
#include <glibmm/spawn.h>
#include <functional>
#include <string>
namespace waybar::util::command {
class LineStream {
public:
using OutputCallback = std::function<void(const std::string&)>;
using ExitCallback = std::function<void(int)>;
LineStream(std::string output_name, OutputCallback on_output, ExitCallback on_exit);
~LineStream();
void start(const std::string& cmd);
void stop();
bool running() const;
private:
bool handleStdout(Glib::IOCondition condition);
void handleExit(Glib::Pid pid, int status);
void closeStdout();
void drainStdout(bool flush_trailing_line);
static int statusToExitCode(int status);
std::string output_name_;
OutputCallback on_output_;
ExitCallback on_exit_;
std::string buffer_;
Glib::Pid pid_;
int stdout_fd_;
sigc::connection stdout_connection_;
sigc::connection child_connection_;
};
} // namespace waybar::util::command
+4 -2
View File
@@ -14,12 +14,14 @@ struct pollfd;
namespace waybar {
class CssReloadHelper {
public:
CssReloadHelper(std::string cssFile, std::function<void()> callback);
CssReloadHelper(std::string cssFile, std::function<void(const std::string&)> callback);
virtual ~CssReloadHelper() = default;
virtual void monitorChanges();
virtual void changeCssFile(const std::string& newCssFile);
protected:
std::vector<std::string> parseImports(const std::string& cssFile);
@@ -42,7 +44,7 @@ class CssReloadHelper {
private:
std::string m_cssFile;
std::function<void()> m_callback;
std::function<void(const std::string&)> m_callback;
std::vector<std::tuple<Glib::RefPtr<Gio::FileMonitor>>> m_fileMonitors;
};
+21 -7
View File
@@ -1,19 +1,33 @@
#pragma once
#include <algorithm>
#include <cctype>
#include <map>
#include <stdexcept>
#include <string>
#include "util/string.hpp"
namespace waybar::util {
template <typename EnumType>
struct EnumParser {
public:
EnumParser();
~EnumParser();
//struct EnumParser {
EnumType parseStringToEnum(const std::string& str,
const std::map<std::string, EnumType>& enumMap);
};
const std::map<std::string, EnumType>& enumMap) {
std::string uppercaseStr = capitalize(str);
std::map<std::string, EnumType> capitalizedEnumMap;
std::transform(
enumMap.begin(), enumMap.end(),
std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()),
[](const auto& pair) {
return std::make_pair(capitalize(pair.first), pair.second);
});
auto it = capitalizedEnumMap.find(uppercaseStr);
if (it != capitalizedEnumMap.end()) return it->second;
throw std::invalid_argument("Invalid string representation for enum");
// }
}
} // namespace waybar::util
+126 -42
View File
@@ -5,41 +5,69 @@
class pow_format {
public:
pow_format(long long val, std::string&& unit, bool binary = false)
: val_(val), unit_(unit), binary_(binary) {};
pow_format(long long val, std::string&& unit, bool binary = false, bool skip_decimal = false,
int min_pow_for_decimal = 0)
: val_(val),
unit_(unit),
binary_(binary),
skip_decimal_(skip_decimal),
min_pow_for_decimal_(min_pow_for_decimal) {};
long long val_;
std::string unit_;
bool binary_;
bool skip_decimal_;
int min_pow_for_decimal_;
};
namespace fmt {
template <>
struct formatter<pow_format> {
char spec = 0;
int width = 0;
char spec = 0; // alignment: '>', '<', '=' (0 = none)
int width = 0; // width digits; enforced only when scale_spec != 0
char scale_spec = 0; // forced scale: 0 = auto, else one of '#','k','M','G','T','P'
char unit_pref = 0; // unit tri-state: 0 = default, 'u' = hide, 'U' = show
char base_pref = 0; // base tri-state: 0 = call-site, 'b' = decimal, 'B' = binary
bool force_int = false; // 'i': force integer display
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 == '<' || *it == '=')) {
if (it != end && (*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.
// Consume scale/flag modifiers and the width in any order, until '}' or end.
// The width digits (parsed but only enforced when a scale is forced — see
// format()) may appear anywhere among the modifiers, so both {:=#3} and the
// more natural {:=3#} are accepted. On an unrecognised char we stop and let
// fmt raise its usual error.
while (it != end && *it != '}') {
char c = *it;
if (c == '#' || c == 'k' || c == 'M' || c == 'G' || c == 'T' || c == 'P') {
scale_spec = c;
++it;
} else if (c == 'u' || c == 'U') {
unit_pref = c;
++it;
} else if (c == 'b' || c == 'B') {
base_pref = c;
++it;
} else if (c == 'i') {
force_int = true;
++it;
} else if ('0' <= c && c <= '9') {
// Width kept for compatibility with existing configs such as {:>9}; only
// enforced (fixed field + '#' overflow) when a scale is forced.
#if FMT_VERSION < 80000
width = parse_nonnegative_int(it, end, ctx);
width = parse_nonnegative_int(it, end, ctx);
#else
width = detail::parse_nonnegative_int(it, end, -1);
width = detail::parse_nonnegative_int(it, end, -1);
#endif
} else {
break;
}
}
return it;
}
@@ -47,44 +75,100 @@ struct formatter<pow_format> {
template <class FormatContext>
auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) {
const char* units[] = {"", "k", "M", "G", "T", "P", nullptr};
const int max_pow = 5; // last valid index in units[]
auto base = s.binary_ ? 1024ull : 1000ll;
// Effective base: 'b'/'B' override the call-site binary_.
bool binary = base_pref == 'B' ? true : base_pref == 'b' ? false : s.binary_;
auto base = binary ? 1024ull : 1000ll;
auto div = 1ll;
auto fraction = (double)s.val_;
int pow;
for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) {
fraction /= base;
if (scale_spec != 0) {
// Forced scale: map the char to a fixed index into units[].
switch (scale_spec) {
case 'k':
pow = 1;
break;
case 'M':
pow = 2;
break;
case 'G':
pow = 3;
break;
case 'T':
pow = 4;
break;
case 'P':
pow = 5;
break;
default:
pow = 0;
break; // '#' -> base scale
}
if (pow > max_pow) pow = max_pow;
for (int i = 0; i < pow; ++i) div *= base;
fraction /= div;
} else {
for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) {
fraction /= base;
div *= base;
}
}
auto number_width = 5 // coeff in {:.1f} format
+ s.binary_; // potential 4th digit before the decimal point
auto max_width = number_width + 1 // prefix from units array
+ s.binary_ // for the 'i' in GiB.
+ s.unit_.length();
// Precision: 'i' forces 0; otherwise 1 (or 0 when skip_decimal_ divides
// evenly). min_pow_for_decimal_ keeps its default-branch-only effect.
int precision = force_int ? 0 : (s.skip_decimal_ && ((s.val_ % div) == 0)) ? 0 : 1;
if (!force_int && scale_spec == 0 && pow < s.min_pow_for_decimal_) precision = 0;
// Unit visibility: default on for auto scale, off for a forced scale; 'u'/'U'
// override. The binary 'i' is part of the scale prefix, so the unit is just
// unit_.
bool hide_unit = unit_pref == 'u' || (unit_pref == 0 && scale_spec != 0);
// Scale prefix (letter + binary 'i'), suppressed entirely when a scale is
// forced.
std::string prefix =
scale_spec != 0 ? "" : std::string(units[pow]) + ((binary && pow) ? "i" : "");
std::string unit = hide_unit ? "" : s.unit_;
auto number_width = 3 + precision // coeff in {:.{precision}f} format
+ (precision != 0) // float dot
+ binary; // potential digit before the decimal point
// In auto mode the prefix column is always reserved (letter + optional 'i'),
// matching the historical fixed max_width even at base scale (the '=' padding
// fills the gap). A forced scale drops the prefix column entirely.
auto prefix_col = scale_spec != 0 ? 0 : 1 + binary;
auto max_width = number_width + prefix_col + unit.length();
// The numeric coefficient string. When a scale is forced with a width and the
// number does not fit, it overflows to '#' (spreadsheet-style).
bool fixed_num = scale_spec != 0 && width > 0;
std::string number = fmt::format("{:.{}f}", fraction, precision);
if (fixed_num && (int)number.length() > width) number = std::string(width, '#');
// Base-scale compensation for the '=' column-align: only in auto mode, where
// the absent prefix (and binary 'i') would otherwise shift the unit column.
const char* padding = (scale_spec == 0 && pow == 0) ? (binary ? " " : " ") : "";
const char* format;
std::string string;
switch (spec) {
case '>':
return fmt::format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width);
case '<':
return fmt::format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
case '=':
format = "{coefficient:<{number_width}.1f}{padding}{prefix}{unit}";
break;
// Column-align: left-justify the coefficient within its column, then pad
// so the prefix/unit line up across values of different magnitude.
return fmt::format_to(ctx.out(), "{:<{}}{}{}{}", number, fixed_num ? width : number_width,
padding, prefix, unit);
case '>':
case '<':
case 0:
default:
format = "{coefficient:.1f}{prefix}{unit}";
break;
default: {
// Right-justify the numeric field to the fixed width when forced.
std::string body =
(fixed_num ? fmt::format("{:>{}}", number, width) : number) + prefix + unit;
if (spec == '>') return fmt::format_to(ctx.out(), "{:>{}}", body, max_width);
if (spec == '<') return fmt::format_to(ctx.out(), "{:<{}}", body, max_width);
return fmt::format_to(ctx.out(), "{}", body);
}
}
return fmt::format_to(
ctx.out(), fmt::runtime(format), fmt::arg("coefficient", fraction),
fmt::arg("number_width", number_width),
fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")),
fmt::arg("unit", s.unit_),
fmt::arg("padding", pow ? ""
: s.binary_ ? " "
: " "));
}
};
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <giomm/dbusconnection.h>
#include <atomic>
#include "giomm/dbusproxy.h"
namespace waybar::util::GeoClueBackend {
class GeoClueBackend {
private:
guint watcherID_;
sigc::connection signal_conn;
Glib::RefPtr<Gio::DBus::Proxy> proxy;
bool connected;
/* Hack to keep constructor inaccessible but still public.
* This is required to be able to use std::make_shared.
* It is important to keep this class only accessible via a reference-counted
* pointer because the destructor will manually free memory, and this could be
* a problem with C++20's copy and move semantics.
*/
struct PrivateConstructorTag {};
public:
sigc::signal<void> in_use_changed_signal_event;
std::atomic<bool> location_in_use; // GeoClue is being used
static std::shared_ptr<GeoClueBackend> getInstance();
// DBus callbacks
void onAppear(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&,
const Glib::ustring&);
void onVanished(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&);
void propertyChanged(const Gio::DBus::Proxy::MapChangedProperties& changedProperties,
const std::vector<Glib::ustring>& invalidatedProperties);
GeoClueBackend(PrivateConstructorTag tag);
~GeoClueBackend();
};
} // namespace waybar::util::GeoClueBackend
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <json/value.h>
namespace waybar::util {
bool valid_host(const Json::Value& config);
} // namespace waybar::util
+9 -3
View File
@@ -7,6 +7,7 @@
#include <codecvt>
#include <iostream>
#include <locale>
#include <memory>
#include <regex>
#if (FMT_VERSION >= 90000)
@@ -26,14 +27,19 @@ class JsonParser {
Json::Value root;
// replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences
std::string modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
std::string modifiedJsonStr;
const std::string* json = &jsonStr;
if (jsonStr.find("\\x") != std::string::npos) {
modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
json = &modifiedJsonStr;
}
std::istringstream jsonStream(modifiedJsonStr);
std::string errs;
// Use local CharReaderBuilder for thread safety - the IPC singleton's
// parser can be called concurrently from multiple module threads
Json::CharReaderBuilder readerBuilder;
if (!Json::parseFromStream(readerBuilder, jsonStream, &root, &errs)) {
auto reader = std::unique_ptr<Json::CharReader>(readerBuilder.newCharReader());
if (!reader->parse(json->data(), json->data() + json->size(), &root, &errs)) {
throw std::runtime_error("Error parsing JSON: " + errs);
}
return root;
+3 -3
View File
@@ -13,14 +13,14 @@ enum class KillSignalAction : std::uint8_t {
HIDE,
NOOP,
};
const std::map<std::string, KillSignalAction> userKillSignalActions = {
inline const std::map<std::string, KillSignalAction> userKillSignalActions = {
{"TOGGLE", KillSignalAction::TOGGLE},
{"RELOAD", KillSignalAction::RELOAD},
{"SHOW", KillSignalAction::SHOW},
{"HIDE", KillSignalAction::HIDE},
{"NOOP", KillSignalAction::NOOP}};
const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR1 = KillSignalAction::TOGGLE;
const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR2 = KillSignalAction::RELOAD;
inline const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR1 = KillSignalAction::TOGGLE;
inline const KillSignalAction SIGNALACTION_DEFAULT_SIGUSR2 = KillSignalAction::RELOAD;
}; // namespace waybar::util
+1 -1
View File
@@ -29,7 +29,7 @@ class PipewireBackend {
public:
sigc::signal<void> privacy_nodes_changed_signal_event;
std::unordered_map<uint32_t, PrivacyNodeInfo*> privacy_nodes;
std::unordered_map<uint32_t, PWPrivacyNodeInfo*> privacy_nodes;
std::mutex mutex_;
static std::shared_ptr<PipewireBackend> getInstance();
+3 -2
View File
@@ -12,10 +12,11 @@ enum PrivacyNodeType {
PRIVACY_NODE_TYPE_NONE,
PRIVACY_NODE_TYPE_VIDEO_INPUT,
PRIVACY_NODE_TYPE_AUDIO_INPUT,
PRIVACY_NODE_TYPE_AUDIO_OUTPUT
PRIVACY_NODE_TYPE_AUDIO_OUTPUT,
PRIVACY_NODE_TYPE_LOCATION
};
class PrivacyNodeInfo {
class PWPrivacyNodeInfo {
public:
PrivacyNodeType type = PRIVACY_NODE_TYPE_NONE;
uint32_t id;
+24
View File
@@ -79,6 +79,12 @@ class SleeperThread {
auto sleep_for(std::chrono::system_clock::duration dur) {
std::unique_lock lk(mutex_);
CancellationGuard cancel_lock;
condvar_.wait(lk, [this] {
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
!do_run_.load(std::memory_order_relaxed);
});
constexpr auto max_time_point = std::chrono::steady_clock::time_point::max();
auto wait_end = max_time_point;
auto now = std::chrono::steady_clock::now();
@@ -95,6 +101,12 @@ class SleeperThread {
time_point) {
std::unique_lock lk(mutex_);
CancellationGuard cancel_lock;
condvar_.wait(lk, [this] {
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
!do_run_.load(std::memory_order_relaxed);
});
return condvar_.wait_until(lk, time_point, [this] {
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
});
@@ -122,6 +134,17 @@ class SleeperThread {
}
}
void pause() {
std::lock_guard<std::mutex> lock(mutex_);
is_paused_ = true;
}
void resume() {
std::lock_guard<std::mutex> lock(mutex_);
is_paused_ = false;
condvar_.notify_all();
}
~SleeperThread() {
connection_.disconnect();
stop();
@@ -137,6 +160,7 @@ class SleeperThread {
std::atomic<bool> do_run_ = true;
std::atomic<bool> signal_ = false;
sigc::connection connection_;
bool is_paused_{false};
};
} // namespace waybar::util
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <string>
namespace waybar::util {
size_t utf8_width(const std::string& str);
void utf8_truncate(std::string& s, const std::string& ellipsis, size_t max_len);
} // namespace waybar::util
+18 -1
View File
@@ -29,13 +29,30 @@ The brightness can be controlled by dragging the slider across the bar or clicki
*device*: ++
typeof: string ++
The name of the preferred device to control. If left empty, a device will be chosen automatically.
The name of the preferred device to control. If left empty, a device will be chosen automatically. ++
Both screen backlights (the udev *backlight* subsystem) and keyboard backlights (LEDs in the udev *leds* subsystem, e.g. *white:kbd_backlight*) are supported; name such an LED here to control it. When left empty, a screen backlight is always preferred for automatic selection.
*interval*: ++
typeof: uint ++
default: 1000 ++
The interval in milliseconds at which the brightness is polled and the slider is updated.
*expand*: ++
typeof: bool ++
default: false ++
Enables this module to consume all left over space dynamically.
*Note*: As well as the JSON configuration, the slider modules are special in
that they *require* styling to work. You *need* to set *min-width* and/or
*min-height* (depending on whether your slider is vertical or not) for it to
display correctly. That is a GTK detail, not an issue with Waybar. See the
*STYLE* section below.
*Warning*: If *min* is set to *0* (default), the slider can set brightness to
*0*, which may completely disable the backlight on some devices, making the
screen fully black. Consider setting a small minimum value (e.g. *10*) or
configuring brightness keybinds as a fallback.
# EXAMPLES
```
+31 -1
View File
@@ -15,6 +15,11 @@ The *backlight* module displays the current backlight level.
default: 2 ++
The interval in which information gets polled.
*device*: ++
typeof: string ++
The name of the preferred backlight device to display. If left empty, a device will be chosen automatically. ++
Both screen backlights (the udev *backlight* subsystem) and keyboard backlights (LEDs in the udev *leds* subsystem, e.g. *white:kbd_backlight*) are supported; name such an LED here to control it. When left empty, a screen backlight is always preferred for automatic selection.
*format*: ++
typeof: string ++
default: {percent}% ++
@@ -74,7 +79,13 @@ The *backlight* module displays the current backlight level.
*reverse-scrolling*: ++
typeof: bool ++
Option to reverse the scroll direction.
default: false ++
Option to reverse the scroll direction for devices other than a mouse (touchpad, trackpad, etc).
*reverse-mouse-scrolling*: ++
typeof: bool ++
default: false ++
Option to reverse the scroll direction for mice.
*scroll-step*: ++
typeof: float ++
@@ -86,6 +97,15 @@ The *backlight* module displays the current backlight level.
default: 0.0 ++
The minimum brightness of the backlight.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
Text to be displayed in the tooltip.
*menu*: ++
typeof: string ++
Action that popups the menu.
@@ -104,6 +124,16 @@ The *backlight* module displays the current backlight level.
default: false ++
Enables this module to consume all left over space dynamically.
# FORMAT REPLACEMENTS
*{percent}*: The current brightness in percent.
*{percent_exp}*: The current brightness in percent, adjusted with a power curve to better match perceived brightness.
*{icon}*: The icon from *format-icons* chosen according to *{percent}*.
*{icon_exp}*: The icon from *format-icons* chosen according to *{percent_exp}*.
# EXAMPLE:
```
+45
View File
@@ -27,11 +27,26 @@ The *battery* module displays the current capacity and state (eg. charging) of y
default: false ++
Option to use the battery design capacity instead of its current maximal capacity.
*full-at-plugged*: ++
typeof: bool ++
default: false ++
When enabled, a battery that is *Full* while the adapter is online is reported with the *Plugged* status instead of *Full* (so you can style/format it separately). Disabled by default to preserve the existing *Full* behaviour.
*interval*: ++
typeof: integer ++
default: 60 ++
The interval in which the information gets polled.
*smooth-power*: ++
typeof: bool ++
default: false ++
Enable exponential smoothing of the battery power reading used to estimate the *{time}* remaining, so the estimate fluctuates less between refreshes.
*smooth-power-time-constant*: ++
typeof: double ++
default: 260.0 ++
The time constant (in seconds) of the *smooth-power* exponential filter. Only used when *smooth-power* is enabled. Values below 1.0 are clamped to 1.0.
*states*: ++
typeof: object ++
A number of battery states which get activated on certain capacity levels. See *waybar-states(5)*.
@@ -109,6 +124,11 @@ The *battery* module displays the current capacity and state (eg. charging) of y
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
default: {timeTo} ++
The tooltip format. See *CUSTOM FORMATS* for status/state-specific variants.
*bat-compatibility*: ++
typeof: bool ++
default: false ++
@@ -150,6 +170,12 @@ The *battery* module displays the current capacity and state (eg. charging) of y
*{health}*: The percentage of the highest-capacity battery's original maximum charge it can still hold.
The following additional replacement is available in *tooltip-format* (and its
custom variants) only:
*{timeTo}*: Either an estimate of time until full or empty, or "Full", "Plugged"
or "Empty" depending on the current battery status.
# TIME FORMAT
The *battery* module allows you to define how time should be formatted via *format-time*.
@@ -169,6 +195,11 @@ The *battery* module allows one to define custom formats based on up to two fact
*format-<status>-<state>*: You can also set a custom format depending on both values.
The tooltip format can be customized the same way. The best fitting from
*tooltip-format*, *tooltip-format-<state>*, *tooltip-format-<status>* and
*tooltip-format-<status>-<state>* will be used (using the same logic as
*format-\**).
# STATES
- Every entry (*state*) consists of a *<name>* (typeof: *string*) and a *<value>* (typeof: *integer*).
@@ -214,6 +245,20 @@ Where:
}
```
Custom icon set depending on the status (*format-icons* as an object):
```
"battery": {
"bat": "BAT2",
"interval": 60,
"format": "{capacity}% {icon}",
"format-icons": {
"default": ["󰂎", "󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"],
"charging": ["󰢟", "󰢜", "󰂆", "󰂇", "󰂈", "󰢝", "󰂉", "󰢞", "󰂊", "󰂋", "󰂅"]
}
}
```
# STYLE
- *#battery*
+19
View File
@@ -16,6 +16,10 @@ Addressed by *bluetooth*
typeof: string ++
Use the controller with the defined alias. Otherwise, a random controller is used. Recommended to define when there is more than 1 controller available to the system.
*controller-alias*: ++
typeof: string ++
Synonym for *controller*. If both are set, *controller-alias* takes precedence.
*format-device-preference*: ++
typeof: array ++
A ranking of bluetooth devices, addressed by their alias. The order is from *first displayed* to *last displayed*. ++
@@ -178,6 +182,9 @@ At the time of writing, the experimental features of BlueZ need to be turned on,
*{device_battery_percentage}*: Battery percentage of the displayed device if available. Use only in the config options defined below.
*{device_battery_percentage_peripheral}*: Battery percentage of the peripheral half of a split keyboard (e.g., ZMK keyboards with separate central and peripheral batteries). ++
This is read from GATT Battery Service characteristics that have a User Description descriptor. Use only in the config options defined below.
## CONFIGURATION
*format-connected-battery*: ++
@@ -220,6 +227,17 @@ At the time of writing, the experimental features of BlueZ need to be turned on,
}
```
Split keyboard with separate central/peripheral batteries (e.g., ZMK):
```
"bluetooth": {
"format-device-preference": [ "Keyball44" ],
"format": "",
"format-connected-battery": " {device_battery_percentage}%|{device_battery_percentage_peripheral}%",
"tooltip-format-connected": "{device_alias}\\nCentral: {device_battery_percentage}%\\nPeripheral: {device_battery_percentage_peripheral}%"
}
```
# STYLE
- *#bluetooth*
@@ -227,6 +245,7 @@ At the time of writing, the experimental features of BlueZ need to be turned on,
- *#bluetooth.off*
- *#bluetooth.on*
- *#bluetooth.connected*
- *#bluetooth.no-controller*
- *#bluetooth.discoverable*
- *#bluetooth.discovering*
- *#bluetooth.pairable*
+165 -156
View File
@@ -6,10 +6,10 @@ waybar - cava module
# DESCRIPTION
*cava* module for karlstav/cava project. See it on github: https://github.com/karlstav/cava.
Module supports two different frontends starting from the 0.15.0 release. The frontend that
will be used is managed by the method parameter in the [output] section of the cava configuration file.
The *cava* module integrates the *karlstav/cava* audio visualizer into Waybar.
It supports two frontends: a text-based *raw* frontend and a GPU-based *GLSL*
frontend. The active frontend is selected by the *method* option in the
*[output]* section of the cava configuration file.
# FILES
@@ -27,177 +27,194 @@ libcava lives in:
# CONFIGURATION
[- *Option*
:- *Typeof*
:- *Default*
:- *Description*
:[ *Type*
:[ *Default*
:[ *Description*
|[ *cava_config*
:[ string
:[
:< Path where cava configuration file is placed to
|[ *method* \[output\]
:[ string
:[
:< Manages which frontend Waybar cava module should use. Values: raw, sdl_glsl
:[ Path to the cava configuration file. When provided, cava settings are read from it first.
|[ *framerate*
:[ integer
:[ 30
:[ Frames per second. Is used as a replacement for *interval*
:[ Target frames per second. Replaces the generic *interval* option.
|[ *autosens*
:[ integer
:[ 1
:[ Will attempt to decrease sensitivity if the bars peak
:[ Automatically decrease sensitivity when the bars peak.
|[ *sensitivity*
:[ integer
:[ 100
:[ Manual sensitivity in %. If autosens is enabled, this will only be the initial value. 200 means double height. Accepts only non-negative values
:[ Manual sensitivity in %. If *autosens* is enabled, this is only the initial value. 200 means double height. Accepts only non-negative values.
|[ *bars*
:[ integer
:[ 12
:[ The number of bars
:[ The number of bars.
|[ *lower_cutoff_freq*
:[ long integer
:[ 50
:[ Lower cutoff frequencies for lowest bars the bandwidth of the visualizer
:[ Lower cutoff frequency for the visualizer bandwidth.
|[ *higher_cutoff_freq*
:[ long integer
:[ 10000
:[ Higher cutoff frequencies for highest bars the bandwidth of the visualizer
:[ Higher cutoff frequency for the visualizer bandwidth.
|[ *sleep_timer*
:[ integer
:[ 5
:[ Seconds with no input before cava main thread goes to sleep mode
:[ Seconds of silence before cava enters sleep mode.
|[ *hide_on_silence*
:[ bool
:[ false
:[ Hides the widget if no input (after sleep_timer elapsed)
:[ Hide the widget when silence lasts longer than *sleep_timer*.
|[ *format_silent*
:[ string
:[
:[ Widget's text after sleep_timer elapsed (hide_on_silence has to be false)
:[ Text shown when the module is silent and *hide_on_silence* is false. **Raw frontend only.**
|[ *format-icons*
:[ array
:[
:[ Array of characters used to render bar levels in the raw frontend. The number of items determines the dynamic range.
|[ *method* \[input\]
:[ string
:[ pulse
:[ Audio capturing method. Possible methods are: pipewire, pulse, alsa, fifo, sndio or shmem
:[ Audio capture backend. Supported values: pipewire, pulse, alsa, fifo, sndio, shmem.
|[ *source*
:[ string
:[ auto
:[ See cava configuration
:[ Audio source identifier. See the cava documentation for details.
|[ *sample_rate*
:[ long integer
:[ 44100
:[ See cava configuration
:[ See the cava documentation.
|[ *sample_bits*
:[ integer
:[ 16
:[ See cava configuration
:[ See the cava documentation.
|[ *stereo*
:[ bool
:[ true
:[ Visual channels
:[ Enable stereo visualization.
|[ *reverse*
:[ bool
:[ false
:[ Displays frequencies the other way around
:[ Reverse the bar order (highest frequencies on the left).
|[ *bar_delimiter*
:[ integer
:[ 0
:[ Each bar is separated by a delimiter. Use decimal value in ascii table(i.e. 59 = ";"). 0 means no delimiter
:[ Delimiter placed between bars in the raw output. Use a decimal ASCII value (e.g. 59 = ";"). 0 means no delimiter.
|[ *data_format*
:[ string
:[ ascii
:[ Raw data format. Can be 'binary' or 'ascii'. **Raw frontend only.**
|[ *raw_target*
:[ string
:[
:[ Raw output target. A fifo will be created if target does not exist. **Raw frontend only.**
|[ *monstercat*
:[ bool
:[ false
:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves"
:[ Enable Monstercat smoothing.
|[ *waves*
:[ bool
:[ false
:[ Disables or enables the so-called "Monstercat smoothing" with or without "waves"
:[ Enable the waves effect alongside Monstercat smoothing.
|[ *noise_reduction*
:[ integer
:[ 77
:[ Range between 0 - 100. The raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth. 100 will be very slow and smooth, 0 will be fast but noisy
:[ double
:[ 0.77
:[ Smoothing factor between 0.0 and 1.0. Higher values produce slower, smoother animation; lower values are more reactive but noisy.
|[ *input_delay*
:[ integer
:[ 2
:[ Sets the delay before fetching audio source thread start working. On author's machine, Waybar starts much faster than pipewire audio server, and without a little delay cava module fails because pipewire is not ready
|[ *ascii_max_range*
:[ integer
:[ 7
:[ It's impossible to set it directly. The value is dictated by the number of icons in the array *format-icons*
|[ *data_format*
:[ string
:[ asci
:[ Raw data format. Can be 'binary' or 'ascii'
|[ *raw_target*
:[ string
:[ /dev/stdout
:[ Raw output target. A fifo will be created if target does not exist
|[ *menu*
:[ string
:[
:[ Action that popups the menu.
|[ *menu-file*
:[ string
:[
:[ Location of the menu descriptor file. There need to be an element of type GtkMenu with id *menu*
|[ *menu-actions*
:[ array
:[
:[ The actions corresponding to the buttons of the menu.
|[ *bar_spacing*
:[ integer
:[
:[ Bars' space between bars in number of characters
:[ 4
:[ Delay in seconds before starting audio capture. Increase this if Waybar starts before the audio server (e.g. PipeWire).
|[ *bar_width*
:[ integer
:[
:[ Bars' width between bars in number of characters
:[ Bar width in pixels. **Used by the GLSL frontend.**
|[ *bar_spacing*
:[ integer
:[
:[ Space between bars in pixels. **Used by the GLSL frontend.**
|[ *bar_height*
:[ integer
:[
:[ Useless. bar_height is only used for output in "noritake" format
|[ *background*
:[ Ignored by Waybar. Used only by cava's "noritake" output format.
|[ *menu*
:[ string
:[
:[ GLSL actual. Support hex code colors only. Must be within ''
|[ *foreground*
:[ Action that opens the menu.
|[ *menu-file*
:[ string
:[
:[ GLSL actual. Support hex code colors only. Must be within ''
|[ *gradient*
:[ integer
:[ 0
:[ GLSL actual. Gradient mode(0/1 - on/off)
|[ *gradient_count*
:[ integer
:[ 0
:[ GLSL actual. The count of colors for the gradient
|[ *gradient_color_N*
:[ string
:[ Location of the menu descriptor file. There must be a GtkMenu element with id *menu*.
|[ *menu-actions*
:[ array
:[
:[ GLSL actual. N - the number of the gradient color between 1 and 8. Only hex defined colors are supported. Must be within ''
:[ Actions corresponding to the buttons of the menu.
|[ *method* \[output\]
:[ string
:[ raw
:[ Cava output method. Set to *raw* for the text frontend or *sdl_glsl* for the GPU frontend. **This is set inside the *[output]* section of the cava configuration file, not in Waybar's JSON.**
|[ *sdl_width*
:[ integer
:[
:[ GLSL actual. Manages the width of the waybar cava GLSL frontend module
:[ GLSL frontend width in pixels. **GLSL only.**
|[ *sdl_height*
:[ integer
:[
:[ GLSL actual. Manages the height of the waybar cava GLSL frontend module
:[ GLSL frontend height in pixels. **GLSL only.**
|[ *min-length*
:[ integer
:[
:[ Requested width of the GLSL widget in pixels. If not set, falls back to *max-length*, then *sdl_width*. **GLSL only.**
|[ *max-length*
:[ integer
:[
:[ Fallback width of the GLSL widget if *min-length* is not set. **GLSL only.**
|[ *vertex_shader*
:[ string
:[
:[ Path to the vertex shader. **GLSL only; set in the *[output]* section of the cava configuration file.**
|[ *fragment_shader*
:[ string
:[
:[ Path to the fragment shader. **GLSL only; set in the *[output]* section of the cava configuration file.**
|[ *continuous_rendering*
:[ integer
:[ 0
:[ GLSL actual. Keep rendering even if no audio. Recommended to set to 1
:[ Continue rendering when silent. Set to 1 for smooth animation. **GLSL only; set in the *[output]* section of the cava configuration file.**
|[ *background*
:[ string
:[
:[ Background color as a '#RRGGBB' hex string (must be quoted). **GLSL only; set in the *[color]* section of the cava configuration file.**
|[ *foreground*
:[ string
:[
:[ Foreground color as a '#RRGGBB' hex string (must be quoted). **GLSL only; set in the *[color]* section of the cava configuration file.**
|[ *gradient*
:[ integer
:[ 0
:[ Enable gradient mode (0 = off, 1 = on). **GLSL only.** Can also be set in the *[color]* section of the cava configuration file.
|[ *gradient_count*
:[ integer
:[ 0
:[ Number of gradient colors (up to 8). **GLSL only.** Can also be set in the *[color]* section of the cava configuration file.
|[ *gradient_color_N*
:[ string
:[
:[ Gradient color N (18) as a '#RRGGBB' hex string (must be quoted). **GLSL only; set in the *[color]* section of the cava configuration file.**
Configuration can be provided as:
- The only cava configuration file which is provided through *cava_config*. The rest configuration can be skipped
- Without cava configuration file. In such case cava should be configured through provided list of the configuration option
- Mix. When provided both And cava configuration file And configuration options. In such case, waybar applies configuration file first and then overrides particular options by the provided list of configuration options
Configuration can be provided in three ways:
- **Cava config only**: set *cava_config* to a cava configuration file and omit all other options.
- **Waybar JSON only**: leave out *cava_config* and set every option in Waybar's module configuration.
- **Mixed**: provide a *cava_config* and also set specific options in Waybar's JSON. Waybar reads the file first, then overrides any values present in the JSON.
# ACTIONS
[- *String*
:- *Action*
:[ *Action*
|[ *mode*
:< Switch main cava thread and fetch audio source thread from/to pause/resume
:[ Toggle pause/resume for the audio capture and output threads.
# DEPENDENCIES
@@ -207,26 +224,32 @@ Configuration can be provided as:
# SOLVING ISSUES
. On start Waybar throws an exception "error while loading shared libraries: libcava.so: cannot open shared object file: No such file or directory".
It might happen when libcava for some reason hasn't been registered in the system. sudo ldconfig should help
. Waybar is starting but cava module doesn't react to the music
1. In such cases at first need to make sure usual cava application is working as well
2. If so, need to comment all configuration options. Uncomment cava_config and provide the path to the working cava config
3. You might set too huge or too small input_delay. Try to setup to 4 seconds, restart waybar, and check again 4 seconds past. Usual even on weak machines it should be enough
4. You might accidentally switch action mode to pause mode
. At startup Waybar fails with *"error while loading shared libraries: libcava.so: cannot open shared object file: No such file or directory"*.
This happens when libcava has not been registered in the system library cache. Run *sudo ldconfig* to refresh the cache.
This can also occur when Waybar is installed under */usr/local* but libcava is elsewhere. To fix it:
1. Remove the local libcava installation: *sudo rm -rfv /usr/local/include/cava /usr/local/lib64/pkgconfig/cava.pc /usr/local/lib64/libcava.so*
2. Reconfigure Waybar to use the system prefix: *meson configure build -Dprefix="/usr"*
3. Rebuild Waybar: *ninja -C build*
4. Install Waybar: *sudo meson install -C build*
# RISING ISSUES
. Waybar starts but the cava module does not react to audio.
1. First, verify that standalone cava works correctly.
2. If it does, comment out all Waybar cava options, uncomment *cava_config*, and point it to the working cava configuration file.
3. The *input_delay* may be too large or too small. Try setting it to 4 seconds, restart Waybar, and check again after that delay. This is usually sufficient, even on slower machines.
4. You may have accidentally toggled pause mode via an action.
For clear understanding: this module is a cava API's consumer. So for any bugs related to cava engine you should contact Cava upstream(https://github.com/karlstav/cava) ++
with the one Exception. Cava upstream doesn't provide cava as a shared library. For that, this module author made a fork libcava(https://github.com/LukashonakV/cava). ++
So the order is:
. cava upstream
. libcava upstream.
In case when cava releases new version and you're wanna get it, it should be raised an issue to libcava(https://github.com/LukashonakV/cava) with title ++
\[Bump\]x.x.x where x.x.x is cava release version.
# REPORTING ISSUES
This module is a consumer of the cava API. For bugs in the cava engine itself, please report them to [cava upstream](https://github.com/karlstav/cava) first.
Upstream cava does not provide a shared library. The Waybar cava module uses [libcava](https://github.com/LukashonakV/cava), a fork maintained by the module author, to provide one. If the issue is specific to the shared library packaging, report it to libcava.
When requesting a new upstream cava release to be packaged in libcava, open an issue at libcava with the title `[Bump] x.x.x`, where `x.x.x` is the desired cava version.
# EXAMPLES
## Raw frontend
```
"cava": {
//"cava_config": "$XDG_CONFIG_HOME/cava/cava.conf",
@@ -240,7 +263,6 @@ In case when cava releases new version and you're wanna get it, it should be rai
"source": "auto",
"stereo": true,
"reverse": false,
"bar_delimiter": 0,
"monstercat": false,
"waves": false,
"noise_reduction": 0.77,
@@ -251,36 +273,6 @@ In case when cava releases new version and you're wanna get it, it should be rai
}
},
```
# STYLE
- *#cava*
- *#cava.silent* Applied after no sound has been detected for sleep_timer seconds
- *#cava.updated* Applied when a new frame is shown
# FRONTENDS
## RAW
The cava raw frontend uses ASCII characters to visualize incoming audio data. Each ASCII symbol position corresponds to the value of the audio power pulse.
Under the hood:
```
. Incoming audio power pulse list is : 12684
. Configured array of ASCII codes is: ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ]. See `format-icons` https://github.com/Alexays/Waybar/wiki/Module:-Cava#example
```
As a result cava frontend will give ▁▂▆█▄
Examples:
waybar config
```
"cava": {
"cava_config": "$XDG_CONFIG_HOME/cava/waybar_raw.conf",
"input_delay": 2,
"format-icons" : ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" ],
"actions": {
"on-click-right": "mode"
}
},
```
waybar_raw.conf
```
@@ -351,6 +343,7 @@ sleep_timer = 5
# README.md contains further information on how to setup CAVA for JACK.
#
# The options 'sample_rate', 'sample_bits', 'channels' and 'autoconnect' can be configured for some input methods:
# sample_rate: fifo, pipewire, sndio, oss
# sample_bits: fifo, pipewire, sndio, oss
@@ -452,7 +445,7 @@ bar_delimiter = 0
# Noise reduction, int 0 - 100. default 77
# the raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth
# 100 will be very slow and smooth, 0 will be fast but noisy.
# 100 will be very slow and smooth, 0 will be fast and noisy.
[eq]
@@ -461,33 +454,32 @@ bar_delimiter = 0
# Remember to uncomment more than one key! More keys = more precision.
# Look at readme.md on github for further explanations and examples.
```
## GLSL
The Cava GLSL frontend delegates the visualization of incoming audio data to the GPU via OpenGL.
There are some mandatory dependencies that need to be satisfied in order for Cava GLSL to be built and function properly:
## GLSL frontend
. epoxy library must be installed on the system
. Vertex and fragment shaders from the original project must be used. They should be downloaded, and the file paths must be configured correctly in the Waybar Cava configuration:
1. cava shaders [cava shaders](https://github.com/karlstav/cava/tree/master/output/shaders)
2. libcava shaders [libcava shaders](https://github.com/LukashonakV/cava/tree/master/output/shaders)
. It is highly recommended to have a separate cava configuration for the Waybar Cava GLSL module and to use this as the cava_config in the Waybar configuration.
. It is common for cava configurations to be placed in the XDG_CONFIG_HOME directory, including shaders as well. Consider keeping them in the $XDG_CONFIG_HOME/cava/shaders folder.
The GLSL frontend requires:
Key configuration options:
. The *epoxy* library.
. Vertex and fragment shaders from the cava project. Download them and place under _$XDG_CONFIG_HOME/cava/shaders_, then reference them in the cava configuration:
1. [cava shaders](https://github.com/karlstav/cava/tree/master/output/shaders)
2. [libcava shaders](https://github.com/LukashonakV/cava/tree/master/output/shaders)
. A separate cava configuration file is highly recommended.
. bars. The more values the parameter has, the more interesting the visualization becomes.
. method in output section must be set to sdl_glsl
. sdl_width and sdl_height manage the size of the module. Adjust them according to your needs.
. Shaders for sdl_glsl, located in $HOME/.config/cava/shaders. Example: "vertex_shader" = "pass_through.vert" "fragment_shader" = "spectrogram.frag"
. Set continuous_rendering to 1 to enable smooth rendering; set it to 0 otherwise. It is recommended to keep it set to 1.
. background, foreground, and gradient_color_N (where N is a number between 1 and 8) must be defined using hex code
Key cava configuration options for GLSL:
. *bars* — higher values produce more detailed visualization.
. *method* in *[output]* must be set to *sdl_glsl*.
. *sdl_width* and *sdl_height* control the module size.
. *vertex_shader* and *fragment_shader* point to the shader files under _$HOME/.config/cava/shaders_.
. *continuous_rendering* — set to 1 for smooth animation.
. *background*, *foreground*, and *gradient_color_N* must use hex codes inside single quotes.
Example:
waybar config
```
"cava": {
"cava_config": "$XDG_CONFIG_HOME/cava/waybar_cava#3.conf",
"cava_config": "$XDG_CONFIG_HOME/cava/waybar_cava.conf",
"input_delay": 2,
"actions": {
"on-click-right": "mode"
@@ -495,7 +487,7 @@ waybar config
},
```
waybar_raw.conf
waybar_cava.conf
```
## Configuration file for CAVA.
# Remove the ; to change parameters.
@@ -641,7 +633,7 @@ vertex_shader = pass_through.vert
fragment_shader = bar_spectrum.frag
; for glsl output mode, keep rendering even if no audio
continuous_rendering = 1;
continuous_rendering = 1
# disable console blank (screen saver) in tty
# (Not supported on FreeBSD)
@@ -705,4 +697,21 @@ gradient_color_2 = '#45475A'
# Look at readme.md on github for further explanations and examples.
```
Different waybar_cava#N.conf see at [cava GLSL](https://github.com/Alexays/Waybar/wiki/Module:-Cava:-GLSL)
More GLSL examples are available on the [cava GLSL wiki page](https://github.com/Alexays/Waybar/wiki/Module:-Cava:-GLSL).
# STYLE
- *#cava* Raw frontend widget
- *#cava.silent* Applied after no sound has been detected for *sleep_timer* seconds
- *#cava.updated* Applied when a new frame is shown
- *#cavaGLSL* GLSL frontend widget (used instead of *#cava* when the cava *method* is *sdl_glsl*)
- *#cavaGLSL.silent* Applied after no sound has been detected for *sleep_timer* seconds
- *#cavaGLSL.updated* Applied when a new frame is shown
# FRONTENDS
## RAW
The raw frontend maps each bar's amplitude to a character from *format-icons*. The final widget text is the concatenation of all characters, optionally separated by *bar_delimiter*. See the EXAMPLES section above for a complete configuration.
## GLSL
The GLSL frontend renders the visualization with OpenGL ES using user-provided shaders. It is selected by setting *method = sdl_glsl* in the cava configuration. See the EXAMPLES section above for a complete configuration.
+13
View File
@@ -37,6 +37,19 @@ Waybar config to enable the module:
```
# DEVELOPING CFFI MODULES
CFFI modules require a handful of functions and constants to be defined with C
linkage. The way to achieve this depends on the programming language being used
(search for FFI / Foreign Function Interface for that language).
The complete list of symbols to define can be found in the header file shipped with
Waybar at *resources/custom_modules/cffi_example/waybar_cffi_module.h*, and a full
example written in C is provided in *resources/custom_modules/cffi_example/*.
Language bindings exist for several languages, including Rust (the *waybar-cffi*
crate) and Zig.
# STYLE
The classes and IDs are managed by the cffi dynamic library.
+59 -11
View File
@@ -6,7 +6,16 @@ waybar - clock module
# DESCRIPTION
*clock* module displays current date and time
*clock* module displays current date and time.
There are two implementations:
- *clock*: Full-featured implementation, including the calendar and timezone
support described below. Enabled at build time when either C++20 concepts
(\_\_cpp\_concepts >= 201907, gcc >= 13) are available, or the HowardHinnant date
library <https://github.com/HowardHinnant/date> is installed.
- *simpleclock*: Fallback that provides date and time display only. Used when the
above build conditions are not met.
# FILES
@@ -27,7 +36,8 @@ $XDG_CONFIG_HOME/waybar/config ++
|[ *format*
:[ string
:[ *{:%H:%M}*
:[ The format, how the date and time should be displayed. See format options below
:[ The format, how the date and time should be displayed. See format options
below. Split the braces to insert literal text, e.g. *{0:%H}text{0:%M}*
|[ *timezone*
:[ string
:[
@@ -120,8 +130,9 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
:[ Calendar view mode. Possible values: year|month
|[ *mode-mon-col*
:[ integer
:[ 3
:[ Relevant for *mode=year*. Count of months per row
:[ 1
:[ Relevant for *mode=year*. Count of months per row. Must be a divisor of 12
(one of 1, 2, 3, 4, 6, 12); an invalid value falls back to 3
|[ *weeks-pos*
:[ string
:[
@@ -138,6 +149,18 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
:[ When enabled, the calendar follows the ISO 8601 standard: weeks begin on
Monday, and the first week of the year is numbered 1. The default week format is
'{:%V}'.
|[ *weeks-numbering*
:[ string
:[
:[ Override the week number calculation method, independent of *iso8601* and
locale settings. Possible values: *iso* (ISO 8601, {:%V}), *monday*
(Monday-based, {:%W}), *sunday* (Sunday-based, {:%U}). When not set, the
method is derived from *iso8601* or the locale.
|[ *first-day-of-week*
:[ integer
:[
:[ The first day of the week, where 0 is Sunday and 6 is Saturday.
When not set, the first day of the week is determined by the locale settings.
3. Addressed by *clock: calendar: format*
[- *Option*
@@ -155,8 +178,9 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
|[ *weeks*
:[ string
:[ *{:%U}*
:[ Format is applied to week numbers. When weekday format is not provided then
is used default format: '{:%W}' when week starts with Monday, '{:%U}' otherwise
:[ Format is applied to week numbers. The *{}* placeholder is replaced with the
format determined by *weeks-numbering* (if set), otherwise by *iso8601* or the
locale: '{:%V}' for ISO 8601, '{:%W}' when week starts with Monday, '{:%U}' otherwise
|[ *weekdays*
:[ string
:[
@@ -180,6 +204,10 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
:[ Switch to the next calendar month/year
|[ *shift_down*
:[ Switch to the previous calendar month/year
|[ *shift_reset*
:[ Reset the calendar shift back to the current month/year
|[ *exec <cmd>*
:[ Execute the specified command
# FORMAT REPLACEMENTS
@@ -207,11 +235,12 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
"format-alt": "{:%A, %B %d, %Y (%R)} 󰃰 ",
"tooltip-format": "<tt><small>{calendar}</small></tt>",
"calendar": {
"mode" : "year",
"mode-mon-col" : 3,
"weeks-pos" : "right",
"on-scroll" : 1,
"on-click-right": "mode",
"mode" : "year",
"mode-mon-col" : 3,
"weeks-pos" : "right",
"first-day-of-week": 1,
"on-scroll" : 1,
"on-click-right" : "mode",
"format": {
"months": "<span color='#ffead3'><b>{}</b></span>",
"days": "<span color='#ecc6d9'><b>{}</b></span>",
@@ -276,6 +305,21 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
- *#clock*
If displaying seconds causes other modules to shift side to side, the cause is
usually proportional-width digits. If your font supports it, add
*font-feature-settings: "tnum";* to the *#clock* style (or wherever you set the bar
font) to use fixed-width numbers.
The following classes are used only inside the *{calendar}* tooltip. Their
foreground *color* is read and applied to the corresponding calendar cells in
the tooltip markup:
- *.calendar-today*: The current day
- *.calendar-days*: The day numbers
- *.calendar-weeks*: The week numbers
- *.calendar-weekdays*: The weekday header (Su, Mo, ...)
- *.calendar-months*: The month header (January, February, ...)
# Troubleshooting
If clock module is disabled at startup with locale::facet::\_S\_create\_c\_locale ++
@@ -284,6 +328,10 @@ name not valid error message try one of the following:
- check if LC_TIME is set properly (glibc)
- set locale to C in the config file (musl)
When using *clock* instead of *simpleclock*, the locale defaults to *C* regardless
of the locale settings. To override this, prepend *L* to the format string, e.g.
*{:%a %m %d}* becomes *{:L%a %m %d}*.
The locale option must be set for {calendar} to use the correct start-of-week, regardless of system locale.
## Calendar in Chinese. Alignment
+85
View File
@@ -0,0 +1,85 @@
waybar-cpu-graph(5)
# NAME
waybar - cpu graph module
# DESCRIPTION
The *cpu graph* module displays a line graph with the CPU utilization.
# CONFIGURATION
*interval*: ++
typeof: integer or float ++
default: 5 ++
The interval in which the information gets polled.
*graph_type*: ++
typeof: string ++
default: line ++
The rendering style of the graph. One of 'line', 'bar', or 'gauge'.
*width*: ++
typeof: integer ++
The length in pixels the module should display.
*datapoints*: ++
typeof: integer ++
How many data points to show.
*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-click on the module.
*on-update*: ++
typeof: string ++
Command to execute when the module is updated.
*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.
*expand*: ++
typeof: bool ++
default: false ++
Enables this module to consume all left over space dynamically.
# EXAMPLES
Basic configuration:
```
"cpu_graph": {
"interval": 2,
"width": 10
}
```
# STYLE
- *#cpu_graph*
- *.cpu-intensive*
- *.cpu-high*
- *.cpu-moderate*
+30
View File
@@ -83,6 +83,18 @@ The *cpu* module displays the current CPU utilization.
default: true ++
Option to disable tooltip on hover.
*tooltip-format*: ++
typeof: string ++
The format of the tooltip shown on hover. Supports the same replacements as *format*.
*format-<state>*: ++
typeof: string ++
The format to use when the given *state* (see *states*) is active. Supports the same replacements as *format*.
*tooltip-format-<state>*: ++
typeof: string ++
The tooltip format to use when the given *state* (see *states*) is active. Takes precedence over *tooltip-format*.
*expand*: ++
typeof: bool ++
default: false ++
@@ -92,6 +104,12 @@ The *cpu* module displays the current CPU utilization.
*{load}*: Current CPU load.
*{load1}*: CPU load average over the last minute.
*{load5}*: CPU load average over the last 5 minutes.
*{load15}*: CPU load average over the last 15 minutes.
*{usage}*: Current overall CPU usage.
*{usage*{n}*}*: Current CPU core n usage. Cores are numbered from zero, so first core will be {usage0} and 4th will be {usage3}.
@@ -106,6 +124,8 @@ The *cpu* module displays the current CPU utilization.
*{icon*{n}*}*: Icon for CPU core n usage. Use like {icon0}.
*{icons}*: All per-core icons concatenated. Equivalent to {icon0}{icon1}...{icon*N*} but adapts to the number of cores automatically.
# EXAMPLES
Basic configuration:
@@ -128,6 +148,16 @@ CPU usage per core rendered as icons:
},
```
Automatically determine number of icons according to number of logical cores:
```
"cpu": {
"interval": 1,
"format": "{icons} {usage:>2}% ",
"format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"],
},
```
# STYLE
- *#cpu*
+192
View File
@@ -0,0 +1,192 @@
waybar-custom-graph(5)
# NAME
waybar - custom graph module
# DESCRIPTION
The *custom-graph* module displays a graph with the percentage output of a script.
# CONFIGURATION
Addressed by *custom-graph/<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.
*exec-on-event*: ++
typeof: bool ++
default: true ++
If an event command is set (e.g. *on-click* or *on-scroll-up*) then re-execute the script after executing the event command.
*return-type*: ++
typeof: string ++
See *return-type*
*interval*: ++
typeof: integer or float ++
The interval (in seconds) in which the information gets polled. ++
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
Use *once* if you want to execute the module only on startup. ++
You can update it manually with a signal. If no *interval* or *signal* is defined, it is assumed that the out script loops itself. ++
If a *signal* is defined then the script will run once on startup and will only update with a signal.
*restart-interval*: ++
typeof: integer ++
The restart interval (in whole seconds). ++
Can't be used with the *interval* option, so only with continuous scripts. ++
Once the script exits, 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*. ++
If no interval is defined then a signal will be the only way to update the module.
*graph_type*: ++
typeof: string ++
default: line ++
The style of graph to render. One of *line*, *bar* or *gauge*.
*width*: ++
typeof: integer ++
default: 100 ++
The width of the graph in pixels.
*datapoints*: ++
typeof: integer ++
default: 20 ++
The number of most recent values to retain and plot on the graph.
*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-click on the module.
*on-update*: ++
typeof: string ++
Command to execute when the module is updated.
*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 tooltip format. If specified, overrides any tooltip output from the script in *exec*. ++
See *FORMAT REPLACEMENTS*.
*escape*: ++
typeof: bool ++
default: false ++
Option to enable escaping of script output.
*menu*: ++
typeof: string ++
Action that popups the menu.
*menu-file*: ++
typeof: string ++
Location of the menu descriptor file. There need to be an element of type
GtkMenu with id *menu*
*menu-actions*: ++
typeof: array ++
The actions corresponding to the buttons of the menu.
*expand*: ++
typeof: bool ++
default: false ++
Enables this module to consume all left over space dynamically.
# 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
These replacements are available in *tooltip-format*.
*{text}*: Output of the script.
*{alt}*: The *alt* value from a json return type.
*{percentage}* Percentage which can be set via a json return type.
# EXAMPLES
## Memory:
```
"custom-graph/memory": {
"interval": 60,
"graph_type": "gauge",
"width": 52,
"exec": "/path/mem.sh",
"signal": 8,
"return-type": "json"
},
```
mem.sh:
```
#!/bin/bash
mem_info=$(cat /proc/meminfo)
mem_total=$(echo "$mem_info" | grep '^MemTotal:' | awk '{print $2}')
mem_available=$(echo "$mem_info" | grep '^MemAvailable:' | awk '{print $2}')
mem_used=$((mem_total - mem_available))
mem_percent=$((mem_used * 100 / mem_total))
echo "{\"text\": \"${mem_percent}%\", \"percentage\": ${mem_percent},\"tooltip\": \"Memory: ${mem_used}KB used / ${mem_total}KB total\"}'"
```
# STYLE
- *#custom-graph-<name>*
- *#custom-graph-<name>.<class>*
- *<class>* can be set by the script. For more information see *return-type*
+67 -3
View File
@@ -61,8 +61,11 @@ Addressed by *custom/<name>*
The format, how information should be displayed. On {text} data gets inserted.
*format-icons*: ++
typeof: array ++
Based on the set percentage, the corresponding icon gets selected. The order is *low* to *high*.
typeof: array or object or string ++
If the type is an array, then based on the set percentage, the corresponding icon gets selected (the order is *low* to *high*). ++
If the type is an object, then the icon is selected according to the *alt* string from the output. ++
If the type is a string, it is pasted as is. ++
Arrays can be nested into objects: icons are then selected first according to *alt*, then percentage.
*rotate*: ++
typeof: integer ++
@@ -145,17 +148,36 @@ Addressed by *custom/<name>*
default: false ++
Enables this module to consume all left over space dynamically.
*image-path*: ++
typeof: string ++
Path to an image file to display in the module.
*image-name*: ++
typeof: string ++
Name of a themed icon to display in the module.
*icon-size*: ++
typeof: integer ++
The size (in pixels) of the image set via *image-path* or *image-name*.
# 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 }
{"text": "$text", "alt": "$alt", "tooltip": "$tooltip", "class": "$class", "percentage": $percentage }
```
The whole JSON object must be printed on a single line. This can be achieved by
piping the output of your script through *jq --unbuffered --compact-output*.
The *class* parameter also accepts an array of strings.
To have multiline tooltips, use *\\r* in your script to separate the lines. If
using PowerShell for scripting, use the standard newline operator "\`n" in double
quotes; *\\r* and *\\n* will not work.
If nothing or an invalid option is specified, Waybar expects i3blocks style output. Values are *newline* separated.
This should look like this:
@@ -167,12 +189,21 @@ $text\\n$tooltip\\n$class*
# FORMAT REPLACEMENTS
*{}*: Output of the script. Equivalent to *{text}*.
*{text}*: Output of the script.
*{alt}*: The *alt* value from a json return type.
*{percentage}* Percentage which can be set via a json return type.
*{icon}*: An icon from 'format-icons' according to percentage.
The *{}* placeholder is special: it automatically displays the text output of your
script, but it cannot be combined with other placeholders like *{icon}* in the same
format string. To display both an icon and text, use *{icon}* together with *{text}*
explicitly (e.g. *"format": "{icon} {text}"*).
# EXAMPLES
## Spotify:
@@ -248,3 +279,36 @@ Under the premise that interval is not defined, you can use the signal and updat
- *#custom-<name>*
- *#custom-<name>.<class>*
- *<class>* can be set by the script. For more information see *return-type*
- *.flat* and *.text-button* are always applied to the module's label.
- *.image-button* is always applied to the module's image (see *image-path*/*image-name*).
# CONTINUOUS SCRIPTS
The *exec* script may be continuous (i.e. contain some kind of infinite loop). The
display is updated for each new line of data printed on stdout (following the chosen
*return-type*). The *interval* option does not apply to a continuous script; use
*restart-interval* instead to restart the script if it stops after some time.
Be aware that some languages buffer their output. If your module displays nothing
even though your script works as expected, the output may be held in a buffer. Look
up how to flush the output buffer for your language of choice (for example, in Ruby
call *$stdout.flush* after each print).
# OUTPUT NAME
The *exec* script is run with the *WAYBAR_OUTPUT_NAME* environment variable set to
the name of the output (monitor) the bar is displayed on.
# TROUBLESHOOTING
*Self-looping module does not show up*
If your module is self-looping and it does not even show up in the bar, check that:
- Its configuration does *not* include an *interval* parameter.
- Output to stdout is not buffered.
*Custom json class not displayed*
If a class set in your custom script is not picked up by *style.css*, ensure that
the output carrying the most variables is emitted first.
+61 -11
View File
@@ -6,17 +6,12 @@ waybar - disk module
# DESCRIPTION
The *disk* module displays the current disk space used.
The *disk* module displays information of multiple disks.
# 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 ++
@@ -24,8 +19,8 @@ Addressed by *disk*
*format*: ++
typeof: string ++
default: "{percentage_used}%" ++
The format, how information should be displayed.
default: "{}%" ++
The format, how information for each disk should be displayed. Note: in the default the positional field "{}" resolves to *{percentage_free}*.
*rotate*: ++
typeof: integer ++
@@ -75,6 +70,26 @@ Addressed by *disk*
typeof: string ++
Command to execute when scrolling down on the module.
*path*: ++
typeof: string ++
default: "/" ++
Deprecated path of filesystem or mountpoint to monitor.
*paths*: ++
typeof: array ++
default: ["/"] ++
Array of paths residing in the filesystem or mountpoint for which the information should be displayed.
*header*: ++
typeof: string ++
default: "" ++
Text to appear before the disk information defined in the format.
*separator*: ++
typeof: string ++
default: " " ++
Separator string between multiple disk information.
*smooth-scrolling-threshold*: ++
typeof: double ++
Threshold to be used when scrolling.
@@ -86,7 +101,7 @@ Addressed by *disk*
*tooltip-format*: ++
typeof: string ++
default: "{used} out of {total} used ({percentage_used}%)" ++
default: "{used} used out of {total} on {path} ({percentage_used}%)" ++
The format of the information displayed in the tooltip.
*unit*: ++
@@ -123,7 +138,7 @@ Addressed by *disk*
*{free}*: Amount of available disk space for normal users. Automatically selects unit based on size remaining.
*{path}*: The path specified in the configuration.
*{path}*: The path for each disk specified in the configuration.
*{specific_total}*: Total amount of space on the disk, partition, or mountpoint in a specific unit. Defaults to bytes.
@@ -131,6 +146,29 @@ Addressed by *disk*
*{specific_free}*: Amount of available disk space for normal users in a specific unit. Defaults to bytes.
# NUMBER FORMAT MODIFIERS
*{total}*, *{used}* and *{free}* auto-scale with a binary prefix (KiB, GiB, …).
Their rendering can be tuned with fmt-style modifiers, e.g. *"{free:>}"* or
*"{free:G}"*, combined in any order:
*<*, *=*, *>*: Alignment/padding (left, column-align, right).
*u* / *U*: Hide (*u*) or show (*U*) the unit suffix. Shown by default with an auto
scale, hidden by default when a scale is forced.
*#*, *k*, *M*, *G*, *T*, *P*: Force a fixed scale instead of auto-selecting
(*#* = base scale). Forcing a scale hides the scale prefix and, by default, the
unit. E.g. *"{free:G}"* always shows gibibytes.
*i*: Force integer display (no decimals).
*b* / *B*: Force decimal base 1000 (*b*) or binary base 1024 (*B*); disk values
are binary by default.
A trailing number is a fixed width for the coefficient when a scale is forced;
overflow is shown as *#* characters (e.g. *"{free:=3#}"**###*).
# EXAMPLES
```
@@ -143,10 +181,22 @@ Addressed by *disk*
```
"disk": {
"interval": 30,
"format": "{percentage_free}% free on {path}",
"header": "Disks: ",
"paths": ["/", "/home"],
"separator": " ",
}
```
```
"disk": {
"interval": 30,
"paths": ["/"],
"format": "{specific_free:0.2f} GB out of {specific_total:0.2f} GB available. Alternatively {free} out of {total} available",
"unit": "GB"
// 1434.25 GB out of 2000.00 GB available. Alternatively 1.4TiB out of 1.9TiB available.
}
// 1434.25 GB out of 2000.00 GB available. Alternatively 1.4TiB out of 1.9TiB available.
```
# STYLE
+12 -2
View File
@@ -8,6 +8,9 @@ waybar - dwl tags module
The *tags* module displays the current state of tags in dwl.
Using this module requires patching dwl with the IPC patch
<https://codeberg.org/dwl/dwl-patches/wiki/ipc>.
# CONFIGURATION
Addressed by *dwl/tags*
@@ -15,12 +18,18 @@ Addressed by *dwl/tags*
*num-tags*: ++
typeof: uint ++
default: 9 ++
The number of tags that should be displayed. Max 32.
The number of tags that should be displayed. Max 32. This should match the
number of tags configured in dwl.
*tag-labels*: ++
typeof: array ++
The label to display for each tag.
*hide-vacant*: ++
typeof: bool ++
default: false ++
If set to true, tags without clients and that are not active will be hidden.
*disable-click*: ++
typeof: bool ++
default: false ++
@@ -46,8 +55,9 @@ Addressed by *dwl/tags*
- *#tags button.empty*
- *#tags button.focused*
- *#tags button.urgent*
- *#tags button.output*
Note that occupied/focused/urgent status may overlap. That is, a tag may be
Note that occupied/focused/urgent/output status may overlap. That is, a tag may be
both occupied and focused at the same time.
# SEE ALSO
+18 -1
View File
@@ -8,15 +8,28 @@ waybar - dwl window module
The *window* module displays the title of the currently focused window in DWL
Using this module requires patching dwl with the IPC patch
<https://codeberg.org/dwl/dwl-patches/wiki/ipc>.
# CONFIGURATION
Addressed by *dwl/window*
*format*: ++
typeof: string ++
default: {title} ++
default: {} ++
The format, how information should be displayed.
*hide-empty*: ++
typeof: bool ++
default: false ++
Option to hide the module when the content would be empty.
*hide-inactive*: ++
typeof: bool ++
default: false ++
Option to hide the module when the window is unfocused.
*rotate*: ++
typeof: integer ++
Positive value to rotate the text label (in 90 degree increments).
@@ -109,6 +122,10 @@ If no expression matches, the format output is left unchanged.
Invalid expressions (e.g., mismatched parentheses) are skipped.
# STYLE
- *#window.active*
# EXAMPLES
```
+19
View File
@@ -8,6 +8,9 @@ waybar - wlr workspaces module
The *workspaces* module displays the currently used workspaces in wayland compositor.
To use this module, your compositor must implement the *ext-workspace-v1*
Wayland protocol.
# CONFIGURATION
Addressed by *ext/workspaces*
@@ -37,6 +40,10 @@ Addressed by *ext/workspaces*
default: false ++
Should workspaces be sorted by ID. Workspace ID will be sorted numerically when all ID are numbers. Takes precedence over any other sort-by option.
*sort-by-number*: ++
typeof: bool ++
Deprecated alias of *sort-by-id*. Setting it emits a warning; prefer *sort-by-id*.
*all-outputs*: ++
typeof: bool ++
default: false ++
@@ -52,6 +59,18 @@ Addressed by *ext/workspaces*
default: true ++
If set to false hidden workspaces will be shown.
*on-click*: ++
typeof: string ++
The action to perform on left-click. See *CLICK ACTIONS* for the possible values.
*on-click-middle*: ++
typeof: string ++
The action to perform on middle-click. See *CLICK ACTIONS* for the possible values.
*on-click-right*: ++
typeof: string ++
The action to perform on right-click. See *CLICK ACTIONS* for the possible values.
# FORMAT REPLACEMENTS
*{name}*: Name of workspace assigned by compositor.
+2 -2
View File
@@ -28,7 +28,7 @@ Feral Gamemode optimizations.
*tooltip-format*: ++
typeof: string ++
default: Games running: {glyph} ++
default: Games running: {count} ++
The text format of the tooltip.
*hide-not-running*: ++
@@ -54,7 +54,7 @@ Feral Gamemode optimizations.
*icon-size*: ++
typeof: unsigned integer ++
default: 20 ++
Defines the size of the icons.
Defines the size of the icons. Set to *0* for auto size.
*icon-spacing*: ++
typeof: unsigned integer ++
+5 -4
View File
@@ -26,7 +26,7 @@ libgps lives in:
*format*: ++
typeof: string ++
default: {glyph} ++
default: {mode} ++
The text format.
*tooltip*: ++
@@ -36,7 +36,7 @@ libgps lives in:
*tooltip-format*: ++
typeof: string ++
default: Games running: {glyph} ++
default: The value of *format* ++
The text format of the tooltip.
*interval*: ++
@@ -93,10 +93,10 @@ libgps lives in:
"gps": {
"format": "{mode}",
"format-disabled": "", // an empty format will hide the module
"format-no-fix": "No fix",
"format-fix-none": "No fix",
"format-fix-3d": "{status}",
"tooltip-format": "{mode}",
"tooltip-format-no-fix": "{satellites_visible} satellites visible",
"tooltip-format-fix-none": "{satellites_visible} satellites visible",
"tooltip-format-fix-2d": "{satellites_used}/{satellites_visible} satellites used",
"tooltip-format-fix-3d": "Altitude: {altitude_hae}m",
"hide-disconnected": false
@@ -106,6 +106,7 @@ libgps lives in:
- *#gps*
- *#gps.disabled* Applied when GPS is disabled.
- *#gps.disconnected* Applied when no GPS receiver is present.
- *#gps.fix-none* Applied when GPS is present, but there is no fix.
- *#gps.fix-2d* Applied when there is a 2D fix.
- *#gps.fix-3d* Applied when there is a 3D fix.
+46 -1
View File
@@ -21,6 +21,10 @@ Addressed by *hyprland/language*
typeof: string++
Provide an alternative name to display per language where <lang> is the language of your choosing. Can be passed multiple times with multiple languages as shown by the example below.
*format-<lang>-<variant>* ++
typeof: string ++
Like *format-<lang>* but also matches the layout variant, taking precedence over *format-<lang>* when both the language and variant match.
*keyboard-name*: ++
typeof: string ++
Specifies which keyboard to use from hyprctl devices output. Using the option that begins with "at-translated-set..." is recommended.
@@ -38,6 +42,24 @@ Addressed by *hyprland/language*
typeof: array ++
The actions corresponding to the buttons of the menu.
*tooltip*: ++
typeof: boolean ++
default: true ++
Enables or disables the tooltip for the language module. By default, the tooltip is enabled. Set to *false* to disable.
*tooltip-format*: ++
typeof: string ++
default: {long} ++
Specifies the format of the tooltip when it is enabled. It follows the same format replacement rules as the *format* key.
*tooltip-format-<lang>*: ++
typeof: string ++
Allows specifying a different tooltip format for each language. The *<lang>* should be replaced with the language code. This can be used to provide a custom tooltip for each language.
*tooltip-format-<lang>-<variant>*: ++
typeof: string ++
Like *tooltip-format-<lang>* but also matches the layout variant, taking precedence over *tooltip-format-<lang>* when both the language and variant match.
*expand*: ++
typeof: bool ++
default: false ++
@@ -46,7 +68,7 @@ Addressed by *hyprland/language*
# FORMAT REPLACEMENTS
*{short}*: Short name of layout (e.g. "us"). Equals to {}.
*{short}*: Short name of layout (e.g. "us").
*{shortDescription}*: Short description of layout (e.g. "en").
@@ -66,6 +88,29 @@ Addressed by *hyprland/language*
}
```
```
"hyprland/language": {
"format": "{}",
"format-en": "US",
"format-es": "ES",
"tooltip": true,
"tooltip-format": "{long}"
}
```
```
"hyprland/language": {
"format": "{}",
"format-en": "US",
"format-es": "ES",
"tooltip": true,
"tooltip-format": "{}",
"tooltip-format-es": "{Español}",
"tooltip-format-en": "{English (american)}"
}
```
# STYLE
- *#language*
- *#language.<layout>* (per-layout class derived from the layout short name, e.g. '.us')
+11 -3
View File
@@ -15,7 +15,7 @@ Addressed by *hyprland/submap*
*format*: ++
typeof: string ++
default: {} ++
The format, how information should be displayed. On {} the currently active submap is displayed.
The format, how information should be displayed. This format supports two placeholders: *{submap}* for the currently active submap name and *{icon}* for the icon associated with the submap.
*rotate*: ++
typeof: integer ++
@@ -80,6 +80,10 @@ Addressed by *hyprland/submap*
default: Default ++
Option to set the submap name to display when not in an active submap.
*icons*: ++
typeof: object ++
Based on the submap name, the corresponding icon will be selected from this map and made available as the *{icon}* placeholder in the format string. The keys are submap names, and the values are the icons or strings to display for those submaps.
*menu*: ++
typeof: string ++
Action that popups the menu.
@@ -103,9 +107,13 @@ Addressed by *hyprland/submap*
```
"hyprland/submap": {
"format": "✌️ {}",
"format": "{icon} {submap}",
"max-length": 8,
"tooltip": false
"tooltip": false,
"icons": {
"resize": "",
"pause": "",
}
}
```
+13
View File
@@ -17,6 +17,10 @@ Addressed by *hyprland/window*
default: {title} ++
The format, how information should be displayed. On {} the current window title is displayed.
*max-length*: ++
typeof: integer ++
The maximum length in character the module should display.
*rewrite*: ++
typeof: object ++
Rules to rewrite window title. See *rewrite rules*.
@@ -25,6 +29,15 @@ Addressed by *hyprland/window*
typeof: bool ++
Show the active window of the monitor the bar belongs to, instead of the focused window.
*fallback*: ++
typeof: string ++
Text to display when the focused window title is empty (for example when no window is focused).
*tooltip-format*: ++
typeof: string ++
The format of the tooltip shown on hover. Supports the same replacements as *format*. Requires *tooltip* to be enabled. When unset, the tooltip falls back to the formatted label text. ++
default: (the formatted label text)
*icon*: ++
typeof: bool ++
default: false ++
+134 -2
View File
@@ -14,11 +14,11 @@ Addressed by *hyprland/workspaces*
*format*: ++
typeof: string ++
default: {id} ++
default: {name} ++
The format, how information should be displayed.
*format-icons*: ++
typeof: array ++
typeof: object ++
Based on the workspace ID and state, the corresponding icon gets selected. See *icons*.
*window-rewrite*: ++
@@ -41,6 +41,19 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
The separator to be used between windows in a workspace. ++
This setting is ignored if *workspace-taskbar.enable* is set to true.
*window-rewrite-group-threshold*: ++
typeof: int ++
default: 0 ++
When a workspace contains at least this many windows with the same rewrite result, they are collapsed into a single one using *window-rewrite-group-format*. ++
Set to 0 to disable grouping. ++
This setting is ignored if *workspace-taskbar.enable* is set to true.
*window-rewrite-group-format*: ++
typeof: string ++
default: "{icon}×{count}" ++
The format used to represent a group of collapsed windows. Available placeholders are {icon} (the icon being grouped) and {count} (how many windows share it). ++
This setting is ignored if *workspace-taskbar.enable* is set to true.
*workspace-taskbar*: ++
typeof: object ++
Contains settings for the workspace taskbar, an alternative mode for the workspaces module which displays the window icons as images instead of text.
@@ -75,6 +88,11 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
default: 16 ++
Size of the icons in the workspace taskbar.
*max-icons*: ++
typeof: int ++
default: 0 (unlimited) ++
Maximum number of icons to show per workspace. When set, duplicate icons (windows with the same class) are removed first, then the list is trimmed to this limit. Set to 0 for unlimited icons.
*icon-theme*: ++
typeof: string | array ++
default: [] ++
@@ -98,6 +116,11 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
- {button} Pressed button number, see https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html. ++
See https://github.com/Alexays/Waybar/wiki/Module:-Hyprland#workspace-taskbars-example for a full example.
*max-windows*: ++
typeof: int ++
default: 0 (unlimited) ++
Maximum number of windows to show per workspace. When set, newest windows beyond the limit are not shown. Set to 0 for unlimited windows.
*show-special*: ++
typeof: bool ++
default: false ++
@@ -113,6 +136,12 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
default: false ++
If set to true, only persistent workspaces will be shown on bar.
*persistent-workspaces*: ++
typeof: object ++
default: empty ++
Lists workspaces that should always be shown, even when they do not exist. Keys are workspace names and values are arrays of output names on which the workspace should be shown (an empty array means all outputs). See the examples below. ++
Note: for persistent workspaces to actually work you must also declare them in your Hyprland config, e.g. *workspace = 1, monitor:eDP-1, persistent:true*.
*all-outputs*: ++
typeof: bool ++
default: false ++
@@ -123,6 +152,11 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
default: false ++
If set to true, only the active workspace will be shown.
*hide-active*: ++
typeof: bool ++
default: false ++
If set to true, the active workspace will be hidden. Unless a workspace is persistent or special.
*move-to-monitor*: ++
typeof: bool ++
default: false ++
@@ -130,11 +164,24 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
Otherwise, the workspace will open on the monitor where it was previously assigned.
Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland.
*unique-icons*: ++
typeof: bool ++
default: false ++
If set to true, only one instance of each window icon will be shown per workspace.
*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.
*on-scroll-up*: ++
typeof: string ++
Command to execute when scrolling up on the module. This replaces the default behaviour of workspace cycling.
*on-scroll-down*: ++
typeof: string ++
Command to execute when scrolling down on the module. This replaces the default behaviour of workspace cycling.
*ignore-workspaces*: ++
typeof: array ++
default: [] ++
@@ -149,6 +196,15 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
If set to special-centered, workspaces will sort by default with special workspaces in the center.
If none of those, workspaces will sort with default behavior.
*tooltip*: ++
typeof: bool ++
default: true ++
Option to disable tooltip on hover.
*tooltips*: ++
typeof: object ++
Based on the workspace ID and state, the corresponding tooltip gets selected. Selection works the same as *format-icons* do. Format replacements are supported. See *icons*.
*expand*: ++
typeof: bool ++
default: false ++
@@ -162,6 +218,34 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
*{icon}*: Icon, as defined in *format-icons*.
*{windows}*: The windows in the workspace, formatted according to *window-rewrite* and joined with *format-window-separator*.
# WINDOW REWRITE RULES
The rules in *window-rewrite* are regexes that may match against a window's
class, title, or both. There are four categories of rule, distinguished by how
they are written in the config:
[- *Rule*
:- *Category*
|[ *something*
:[ Vague
|[ *class<something>*
:[ Class-only
|[ *title<something>*
:[ Title-only
|[ *class<something1> title<something2>*
:[ Hybrid
When the config contains only "vague" rules, they are matched against window
*classes* only. This is both for backwards compatibility and for performance:
matching against the title requires listening to window title changes via
Hyprland's IPC, which is unnecessary when no title rule is in use.
When the config contains *at least one* "title-only" or "hybrid" rule, then all
"vague" rules match against *both* class and title. This lets you define vague
rules where it does not matter whether the class or the title matched.
# ICONS
Additional to workspace name matching, the following *format-icons* can be set.
@@ -172,6 +256,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *empty*: Will be shown on non-active, non-special empty persistent workspaces
- *visible*: Will be shown on workspaces that are visible but not active. For example: this is useful if you want your visible workspaces on other monitors to have the same look as active.
- *persistent*: Will be shown on non-empty persistent workspaces
- *urgent*: Will be shown on non-active urgent workspaces
# EXAMPLES
@@ -238,6 +323,51 @@ Additional to workspace name matching, the following *format-icons* can be set.
}
```
```
"hyprland/workspaces": {
"format": "{icon}",
"format-window-separator": ", ",
"tooltip": true,
"tooltips": {
"default": "{name}: {windows}",
"empty": "" // Will result in no tooltip
}
"format-icons": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"active": "",
"default": ""
},
// Window rewrites omitted for brevity
}
```
```
"hyprland/workspaces": {
"format": "{icon}",
"format-window-separator": ", ",
"tooltip": true,
"tooltips": {
"1": "This is the first workspace",
"2": "This is the second",
"2": "And this is the third"
}
"format-icons": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"active": "",
"default": ""
},
// Window rewrites omitted for brevity
}
```
# Style
- *#workspaces*
@@ -248,6 +378,8 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *#workspaces button.persistent*
- *#workspaces button.special*
- *#workspaces button.urgent*
- *#workspaces button.workspace-hover* (applied while the pointer is over the button)
- *#workspaces button.<workspace-name>* (per-workspace class derived from the workspace name, sanitized to a valid CSS class name, e.g. a workspace named "1" yields '.ws-1'; special workspaces also get the raw name class)
- *#workspaces button.hosting-monitor* (gets applied if workspace-monitor == waybar-monitor)
- *#workspaces .workspace-label*
- *#workspaces .taskbar-window* (each window in the taskbar, only if 'workspace-taskbar.enable' is true)

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