Commit Graph
1142 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 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
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
Viktar Lukashonak 2d87bd5ba9 Drop mandatory hyprland dependency 2026-07-23 11:58:35 +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
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 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
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 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
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 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 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 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 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
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
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
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 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 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 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 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
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
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 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
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
Alex 41d86d92a8 fix(wwan): null-initialize pointer members to avoid UB in destructor 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 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 87a337ecdf Update tray.hpp 2026-07-04 01:31:39 +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 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 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