Compare commits

...

61 Commits

Author SHA1 Message Date
267c327db9 Merge upstream/master
Some checks failed
clang-format / lint (push) Has been cancelled
freebsd / build (push) Has been cancelled
linux / build (c++20, alpine) (push) Has been cancelled
linux / build (c++20, archlinux) (push) Has been cancelled
linux / build (c++20, debian) (push) Has been cancelled
linux / build (c++20, fedora) (push) Has been cancelled
linux / build (c++20, gentoo) (push) Has been cancelled
linux / build (c++20, opensuse) (push) Has been cancelled
Nix-Tests / nix-flake-check (push) Has been cancelled
2026-02-27 08:36:07 -08:00
Alex
7744320ab2 fix: build 2026-02-24 00:55:27 +01:00
Alex
802bf184fb fix: lint 2026-02-24 00:49:23 +01:00
Alex
ef3d55980e fix: some crashes 2026-02-24 00:49:08 +01:00
Alexis Rouillard
a32413a74f Merge pull request #4880 from Alexays/copilot/fix-mpd-module-crash
mpd: fix socket FD leak on system-level connection errors
2026-02-24 00:06:11 +01:00
copilot-swe-agent[bot]
82f076c6c2 mpd: fix FD leak by resetting connection on MPD_ERROR_SYSTEM errors
Co-authored-by: Alexays <13947260+Alexays@users.noreply.github.com>
2026-02-23 23:04:36 +00:00
Alex
e18939210b fix: lint 2026-02-24 00:00:57 +01:00
Alexis Rouillard
aacf0cbc99 Merge pull request #4881 from Alexays/copilot/fix-sigabrt-usb-unplug-issue
battery: fix SIGABRT on USB device unplug race condition
2026-02-24 00:00:19 +01:00
Alexis Rouillard
3e2a50d59f Merge pull request #4879 from Alexays/copilot/fix-waybar-bluetooth-crash
bluetooth: fix segfault when DBus manager fails to initialize
2026-02-23 23:58:51 +01:00
copilot-swe-agent[bot]
49d4049ea3 Fix SIGABRT on USB unplug race condition in battery module
Co-authored-by: Alexays <13947260+Alexays@users.noreply.github.com>
2026-02-23 22:55:05 +00:00
copilot-swe-agent[bot]
0c46818e95 Fix crash when bluetooth DBus manager returns null on timeout
Co-authored-by: Alexays <13947260+Alexays@users.noreply.github.com>
2026-02-23 22:54:23 +00:00
copilot-swe-agent[bot]
37d6541592 Initial plan 2026-02-23 22:53:14 +00:00
copilot-swe-agent[bot]
65fadcf94b Initial plan 2026-02-23 22:52:00 +00:00
copilot-swe-agent[bot]
f806ec03ed Initial plan 2026-02-23 22:51:45 +00:00
Alexis Rouillard
64ecdcfa87 Merge pull request #4846 from BlueManCZ/fix-mpris-fallback-player
fix(mpris): fall back to next non-ignored player and prefer playing players
2026-02-23 23:51:04 +01:00
Alexis Rouillard
4aa8f98552 Merge pull request #4821 from cebem1nt/group-scroll-cutout
fix: owerwrite handleScroll to remove any scrolling handling from group
2026-02-23 23:46:02 +01:00
Alexis Rouillard
730e558cf4 Merge pull request #4834 from khaneliman/niri
feat(niri): niri depends on socket
2026-02-23 23:43:30 +01:00
Alexis Rouillard
22fd2da40e Merge pull request #4836 from Cprakhar/fix/tooltip-return-type-in-format-strings
feat: add `{tooltip}` in format replacements in custom module
2026-02-23 23:26:07 +01:00
Alexis Rouillard
6fa8ad3430 Merge pull request #4849 from 44vladimirov/power_switch_events
battery: power switch events
2026-02-22 18:32:08 +01:00
Alexis Rouillard
54e7451cf0 Merge pull request #4856 from Cprakhar/feat/tooltip-format-cpu
feat: add tooltip-format config option in cpu module
2026-02-20 17:36:17 +01:00
Austin Horstman
ae60ca6233 refactor(niri): declared constructor
Move constructor from hpp to cpp to align with other modules

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-02-20 08:16:56 -06:00
Austin Horstman
a194b576be feat(niri): niri depends on socket
Don't attempt to use niri modules when socket connection fails. Prevents
rendering modules when running another compositor. In same concept as
previous Hyprland change
4295faa7c4

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-02-20 08:16:56 -06:00
Alexis Rouillard
d31b131f80 Merge pull request #4874 from GG2R10/fix/cava-silence-fix
fix(cava): remove silent CSS class on reactivation
2026-02-19 21:55:22 +01:00
GG2R10
9c871c90a7 fix: move silence_ = false inside idle lambda 2026-02-19 15:12:23 -05:00
Alexis Rouillard
a74adc54e5 Merge pull request #4863 from olliestone/fix-aappiconlabel-image-visibility
fix: ensure AAppIconLabel image remains not visibile if icons are disabled
2026-02-18 20:47:27 +01:00
Alexis Rouillard
14a30cd4b7 Merge pull request #4869 from cebem1nt/master
bump: niri/workspaces: change icons priority
2026-02-17 15:45:35 +01:00
cebem1nt
f1140f00f9 niri/workspaces: change icons priority 2026-02-17 11:23:27 -03:00
Ollie Stone
6f9dee979b fix: ensure AAppIconLabel image remains not visibile if icons are
disabled
2026-02-15 18:39:28 +00:00
Alexis Rouillard
e4e47cad5c Merge pull request #3088 from VAWVAW/hyprland-bar-scroll
hyprland/workspaces: Add `enable-bar-scroll` option
2026-02-13 23:43:12 +01:00
Alexis Rouillard
01628dda85 Merge pull request #4852 from tobixen/fix/keyboard-state-hotplug-crash
fix(keyboard-state): fix segfault on device hotplug removal
2026-02-13 23:27:05 +01:00
Alexis Rouillard
9e57d75fe5 Merge pull request #4854 from sliedes/ipc-safesignal
fix: use SafeSignal in ipc in order to not call GTK funcs from a thread
2026-02-13 23:26:46 +01:00
Prakhar Chhalotre
2337d308ce Merge branch 'master' into feat/tooltip-format-cpu 2026-02-14 03:28:29 +05:30
Alexis Rouillard
fc4e2a3534 Merge pull request #4857 from Cprakhar/fix/pango-markup-all
fix: use pango markup for consistent formatting in format and tooltip-format
2026-02-13 22:44:49 +01:00
Alexis Rouillard
a3d7902337 Merge pull request #4858 from chrisvittal/memory-module/tooltip-markup
memory: use markup rather than text for memory tooltip
2026-02-13 22:44:24 +01:00
vawvaw
5b595a4dfe hyprland/workspaces: Add enable-bar-scroll option 2026-02-13 20:48:59 +01:00
Chris Vittal
e0e36b6d81 memory: use markup rather than text for memory tooltip
Seems a little strange to me that we can format the tooltip text, but
can't use markup like we can elsewhere.
2026-02-13 14:17:48 -05:00
Prakhar Chhalotre
3e7976c8eb fix: use pango markup for consistent formatting in format and tooltip-format 2026-02-13 02:23:16 +05:30
Prakhar Chhalotre
4ac539206f feat: add tooltip-format config option in cpu module 2026-02-13 02:00:17 +05:30
Sami Liedes
a70651ed05 fix: use SafeSignal in ipc 2026-02-12 18:11:30 +01:00
Tobias Brox
13469a8847 fix(keyboard-state): fix segfault on device hotplug
The keyboard-state module crashes with SIGSEGV in libinput_device_ref
when a new input device appears in /dev/input/.

Three bugs fixed:

1. Missing NULL check: tryAddDevice() calls libinput_path_add_device()
   which returns NULL on failure, then immediately passes the result to
   libinput_device_ref() without checking.  On laptops, virtual input
   devices (power buttons, lid switch, etc.) appear and disappear in
   /dev/input/ triggering the hotplug handler; if libinput can't open
   one of these, the NULL return causes the segfault.

2. Missing cleanup on device removal: The IN_DELETE handler erased
   devices from the map without calling libinput_path_remove_device(),
   leaving dangling pointers in the libinput context.

3. Thread safety: libinput_devices_ was accessed from 3 threads
   (main/GTK, libinput_thread_, hotplug_thread_) without any mutex.

Fixes #4851

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:57:23 +01:00
Alexey Vladimirov
7110c455e2 fix of example config typos 2026-02-12 02:12:00 +03:00
Alexey Vladimirov
3bcadfdf5a battery: power switch events 2026-02-12 01:42:30 +03:00
Alexis Rouillard
03a77c592b Merge pull request #4844 from Cprakhar/fix/remove-battery-status-console-log
fix: remove unnecessary console log for battery status
2026-02-11 21:22:26 +01:00
Alexis Rouillard
3833028697 Merge pull request #4847 from esensar/patch-1
docs: fix typo in waybar.5 in `expand-right` field info
2026-02-11 21:21:49 +01:00
Ensar Sarajčić
c388208e21 docs: fix typo in waybar.5 in expand-right field info 2026-02-11 13:01:07 +01:00
BlueManCZ
a871d90161 Fix button action handling to consistently use the active player 2026-02-11 12:09:20 +01:00
BlueManCZ
0a50e82d0d Prioritize currently playing player 2026-02-11 11:47:05 +01:00
BlueManCZ
a69b7a5536 Handle fallback player for ignored MPRIS players 2026-02-11 10:53:48 +01:00
Prakhar Chhalotre
bd222984bb fix: remove unnecessary console log for battery status 2026-02-11 01:12:21 +05:30
Alexis Rouillard
d527ccd4c1 Merge pull request #4838 from khaneliman/image
fix(image): treat missing interval as once
2026-02-10 14:55:45 +01:00
Austin Horstman
3b478ee6a5 chore: format
Some unrelated files failed formatting.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-02-09 15:53:44 -06:00
Austin Horstman
c9a7cbbdb3 fix(image): treat missing interval as once
PR #4390 enabled millisecond intervals but changed image interval parsing so a missing interval could resolve to 1ms. That creates a hot update loop and can spike CPU and destabilize rendering in drawer/group setups (issue #4835).

Use explicit semantics: missing, null, non-numeric, or <=0 interval is treated as "once" (max sleep), while positive numeric values still support ms precision with a 1ms floor. This keeps the intended feature (sub-second polling when requested) without defaulting to busy looping.

Also align waybar-image(5) docs with runtime behavior.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-02-09 15:27:35 -06:00
Prakhar Chhalotre
f96fb53eeb feat: add tooltip in format replacments 2026-02-10 01:00:07 +05:30
Alexis Rouillard
306f970684 Merge pull request #4828 from hannesbraun/fix-poll-include
Fix include of poll.h
2026-02-08 21:07:26 +01:00
Alexis Rouillard
00ccfce6f5 Merge pull request #4831 from Cprakhar/fix/pango-markup-temperature
fix: update tooltip method to use pango markup formatting
2026-02-08 20:59:13 +01:00
Prakhar Chhalotre
7e6da1adb2 fix: update tooltip method to use pango markup formatting 2026-02-09 00:16:11 +05:30
Hannes Braun
f373ebfcbb Fix include of poll.h 2026-02-08 13:32:41 +01:00
Alexis Rouillard
2616785d18 Merge pull request #4822 from khaneliman/ci
ci: bump flake lock actions
2026-02-07 21:20:28 +01:00
Austin Horstman
833c40a84b feat(ci): enable concurrency for nix-tests
Don't let multiple tests run, cancel existing for same pr

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-02-06 15:19:04 -06:00
Austin Horstman
d95809e11b chore(ci): bump flake lock actions
Multiple releases old, keep up to date.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-02-06 15:16:20 -06:00
cebem1nt
40200afb68 fix: owerwrite handleScroll to remove any scrolling handling from group module 2026-02-06 14:43:12 -03:00
85 changed files with 539 additions and 263 deletions

View File

@@ -10,7 +10,7 @@ jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
- uses: RafikFarhad/clang-format-github-action@v6 - uses: RafikFarhad/clang-format-github-action@v6
name: clang-format name: clang-format
with: with:

View File

@@ -12,7 +12,7 @@ jobs:
container: container:
image: alexays/waybar:debian image: alexays/waybar:debian
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
- name: configure - name: configure
run: | run: |
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json

View File

@@ -17,7 +17,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3

View File

@@ -12,13 +12,13 @@ jobs:
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support # https://github.com/actions/runner/issues/385 - for FreeBSD runner support
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
- name: Test in FreeBSD VM - name: Test in FreeBSD VM
uses: cross-platform-actions/action@v0.28.0 uses: cross-platform-actions/action@v0.28.0
timeout-minutes: 180 timeout-minutes: 180
env: env:
CPPFLAGS: '-isystem/usr/local/include' CPPFLAGS: '-isystem/usr/local/include'
LDFLAGS: '-L/usr/local/lib' LDFLAGS: '-L/usr/local/lib'
with: with:
operating_system: freebsd operating_system: freebsd
version: "14.2" version: "14.2"

View File

@@ -25,7 +25,7 @@ jobs:
image: alexays/waybar:${{ matrix.distro }} image: alexays/waybar:${{ matrix.distro }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
- name: configure - name: configure
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
- name: build - name: build

View File

@@ -2,16 +2,19 @@ name: "Nix-Tests"
on: on:
pull_request: pull_request:
push: push:
concurrency:
group: ${{ github.workflow }}-nix-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
nix-flake-check: nix-flake-check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: cachix/install-nix-action@v27 - uses: cachix/install-nix-action@v31
with: with:
extra_nix_config: | extra_nix_config: |
experimental-features = nix-command flakes experimental-features = nix-command flakes
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- run: nix flake show - run: nix flake show
- run: nix flake check --print-build-logs - run: nix flake check --print-build-logs
- run: nix build --print-build-logs - run: nix build --print-build-logs

View File

@@ -4,19 +4,19 @@ on:
schedule: schedule:
- cron: '0 0 1 * *' # Run monthly - cron: '0 0 1 * *' # Run monthly
push: push:
paths: paths:
- 'flake.nix' - 'flake.nix'
jobs: jobs:
lockfile: lockfile:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar' if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v27 uses: cachix/install-nix-action@v31
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v21 uses: DeterminateSystems/update-flake-lock@v28

View File

@@ -48,7 +48,7 @@ class AModule : public IModule {
virtual bool handleMouseLeave(GdkEventCrossing* const& ev); virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
virtual bool handleScroll(GdkEventScroll*); virtual bool handleScroll(GdkEventScroll*);
virtual bool handleRelease(GdkEventButton* const& ev); virtual bool handleRelease(GdkEventButton* const& ev);
GObject* menu_; GObject* menu_ = nullptr;
private: private:
bool handleUserEvent(GdkEventButton* const& ev); bool handleUserEvent(GdkEventButton* const& ev);

View File

@@ -35,6 +35,7 @@ class Group : public AModule {
bool handleMouseLeave(GdkEventCrossing* const& ev) override; bool handleMouseLeave(GdkEventCrossing* const& ev) override;
bool handleToggle(GdkEventButton* const& ev) override; bool handleToggle(GdkEventButton* const& ev) override;
void toggle(); void toggle();
bool handleScroll(GdkEventScroll* e) override;
void show_group(); void show_group();
void hide_group(); void hide_group();
void set_visible(bool v) { void set_visible(bool v) {

View File

@@ -6,7 +6,7 @@
#if defined(__linux__) #if defined(__linux__)
#include <sys/inotify.h> #include <sys/inotify.h>
#endif #endif
#include <sys/poll.h> #include <poll.h>
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>

View File

@@ -43,6 +43,7 @@ class Workspaces : public AModule, public EventHandler {
auto moveToMonitor() const -> bool { return m_moveToMonitor; } auto moveToMonitor() const -> bool { return m_moveToMonitor; }
auto enableTaskbar() const -> bool { return m_enableTaskbar; } auto enableTaskbar() const -> bool { return m_enableTaskbar; }
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; } auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
auto barScroll() const -> bool { return m_barScroll; }
auto getBarOutput() const -> std::string { return m_bar.output->name; } auto getBarOutput() const -> std::string { return m_bar.output->name; }
auto formatBefore() const -> std::string { return m_formatBefore; } auto formatBefore() const -> std::string { return m_formatBefore; }
@@ -122,6 +123,8 @@ class Workspaces : public AModule, public EventHandler {
static std::pair<std::string, std::string> splitDoublePayload(std::string const& payload); static std::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
static std::tuple<std::string, std::string, std::string> splitTriplePayload( static std::tuple<std::string, std::string, std::string> splitTriplePayload(
std::string const& payload); std::string const& payload);
// scroll events
bool handleScroll(GdkEventScroll* e) override;
// Update methods // Update methods
void doUpdate(); void doUpdate();
@@ -145,6 +148,7 @@ class Workspaces : public AModule, public EventHandler {
bool m_specialVisibleOnly = false; bool m_specialVisibleOnly = false;
bool m_persistentOnly = false; bool m_persistentOnly = false;
bool m_moveToMonitor = false; bool m_moveToMonitor = false;
bool m_barScroll = false;
Json::Value m_persistentWorkspaceConfig; Json::Value m_persistentWorkspaceConfig;
// Map for windows stored in workspaces not present in the current bar. // Map for windows stored in workspaces not present in the current bar.

View File

@@ -3,6 +3,7 @@
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <mutex>
#include <set> #include <set>
#include <unordered_map> #include <unordered_map>
@@ -41,6 +42,7 @@ class KeyboardState : public AModule {
struct libinput* libinput_; struct libinput* libinput_;
std::unordered_map<std::string, struct libinput_device*> libinput_devices_; std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
std::mutex devices_mutex_; // protects libinput_devices_
std::set<int> binding_keys; std::set<int> binding_keys;
util::SleeperThread libinput_thread_, hotplug_thread_; util::SleeperThread libinput_thread_, hotplug_thread_;

View File

@@ -78,6 +78,7 @@ class Mpris : public ALabel {
PlayerctlPlayerManager* manager; PlayerctlPlayerManager* manager;
PlayerctlPlayer* player; PlayerctlPlayer* player;
PlayerctlPlayer* last_active_player_ = nullptr;
std::string lastStatus; std::string lastStatus;
std::string lastPlayer; std::string lastPlayer;

View File

@@ -17,7 +17,7 @@ class EventHandler {
class IPC { class IPC {
public: public:
IPC() { startIPC(); } IPC();
void registerForIPC(const std::string& ev, EventHandler* ev_handler); void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler); void unregisterForIPC(EventHandler* handler);

View File

@@ -12,6 +12,7 @@
#include <string> #include <string>
#include "ipc.hpp" #include "ipc.hpp"
#include "util/SafeSignal.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@@ -27,8 +28,8 @@ class Ipc {
std::string payload; std::string payload;
}; };
sigc::signal<void, const struct ipc_response&> signal_event; ::waybar::SafeSignal<const struct ipc_response&> signal_event;
sigc::signal<void, const struct ipc_response&> signal_cmd; ::waybar::SafeSignal<const struct ipc_response&> signal_cmd;
void sendCmd(uint32_t type, const std::string& payload = ""); void sendCmd(uint32_t type, const std::string& payload = "");
void subscribe(const std::string& payload); void subscribe(const std::string& payload);

View File

@@ -89,7 +89,7 @@ struct Sock {
} }
}; };
class IPC { class IPC : public std::enable_shared_from_this<IPC> {
static std::weak_ptr<IPC> instance; static std::weak_ptr<IPC> instance;
Json::CharReaderBuilder reader_builder; Json::CharReaderBuilder reader_builder;
Json::StreamWriterBuilder writer_builder; Json::StreamWriterBuilder writer_builder;
@@ -98,7 +98,7 @@ class IPC {
State state; State state;
std::mutex state_mutex; std::mutex state_mutex;
IPC() { start(); } IPC() = default;
static auto connect() -> Sock; static auto connect() -> Sock;
auto receive(Sock& sock) -> Json::Value; auto receive(Sock& sock) -> Json::Value;

View File

@@ -59,6 +59,7 @@ inline int close(FILE* fp, pid_t pid) {
spdlog::debug("Cmd continued"); spdlog::debug("Cmd continued");
} else if (ret == -1) { } else if (ret == -1) {
spdlog::debug("waitpid failed: {}", strerror(errno)); spdlog::debug("waitpid failed: {}", strerror(errno));
break;
} else { } else {
break; break;
} }
@@ -172,8 +173,6 @@ inline int32_t forkExec(const std::string& cmd, const std::string& output_name)
return pid; return pid;
} }
inline int32_t forkExec(const std::string& cmd) { inline int32_t forkExec(const std::string& cmd) { return forkExec(cmd, ""); }
return forkExec(cmd, "");
}
} // namespace waybar::util::command } // namespace waybar::util::command

View File

@@ -182,6 +182,7 @@ Every entry in the *events* object consists of a *<event-name>* (typeof: *string
- *on-<status>-<state>* - *on-<status>-<state>*
- *on-<status>-<capacity>* - *on-<status>-<capacity>*
- *on-<status>*
Where: Where:
@@ -203,7 +204,9 @@ Where:
"events": { "events": {
"on-discharging-warning": "notify-send -u normal 'Low Battery'", "on-discharging-warning": "notify-send -u normal 'Low Battery'",
"on-discharging-critical": "notify-send -u critical 'Very Low Battery'", "on-discharging-critical": "notify-send -u critical 'Very Low Battery'",
"on-charging-100": "notify-send -u normal 'Battery Full!'" "on-charging-100": "notify-send -u normal 'Battery Full!'",
"on-discharging": "notify-send -u normal 'Power Switch' Discharging",
"on-charging": "notify-send -u normal 'Power Switch' Charging'"
}, },
"format": "{capacity}% {icon}", "format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""], "format-icons": ["", "", "", "", ""],

View File

@@ -130,6 +130,11 @@ 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. Otherwise, the workspace will open on the monitor where it was previously assigned.
Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland. Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland.
*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.
*ignore-workspaces*: ++ *ignore-workspaces*: ++
typeof: array ++ typeof: array ++
default: [] ++ default: [] ++

View File

@@ -26,7 +26,8 @@ The *image* module displays an image from a path.
*interval*: ++ *interval*: ++
typeof: integer or float ++ typeof: integer or float ++
The interval (in seconds) to re-render the image. ++ The interval (in seconds) to re-render the image. ++
Minimum value is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++ If set to a positive value, the minimum is 0.001 (1ms). Values smaller than 1ms will be set to 1ms. ++
Zero or negative values are treated as "once". ++
This is useful if the contents of *path* changes. ++ This is useful if the contents of *path* changes. ++
If no *interval* is defined, the image will only be rendered once. If no *interval* is defined, the image will only be rendered once.

View File

@@ -36,7 +36,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
*expand-right* ++ *expand-right* ++
typeof: bool ++ typeof: bool ++
default: false ++ default: false ++
Enables the modules-left to consume all left over space dynamically. Enables the modules-right to consume all left over space dynamically.
*layer* ++ *layer* ++
typeof: string ++ typeof: string ++

View File

@@ -151,19 +151,24 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
} }
void AAppIconLabel::updateAppIcon() { void AAppIconLabel::updateAppIcon() {
if (update_app_icon_) { if (update_app_icon_ || (!iconEnabled() && image_.get_visible())) {
update_app_icon_ = false; update_app_icon_ = false;
if (app_icon_name_.empty()) { if (app_icon_name_.empty()) {
image_.set_visible(false); image_.set_visible(false);
} else if (app_icon_name_.front() == '/') { } else if (app_icon_name_.front() == '/') {
auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); try {
int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); int scaled_icon_size = app_icon_size_ * image_.get_scale_factor();
pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); auto pixbuf =
Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size);
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
image_.get_window()); image_.get_window());
image_.set(surface); image_.set(surface);
image_.set_visible(true); image_.set_visible(true);
} catch (const Glib::Exception& e) {
spdlog::warn("Failed to load app icon {}: {}", app_icon_name_, std::string(e.what()));
image_.set_visible(false);
}
} else { } else {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true); image_.set_visible(true);

View File

@@ -91,11 +91,13 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
// Make the GtkBuilder and check for errors in his parsing // Make the GtkBuilder and check for errors in his parsing
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) { if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
g_object_unref(builder);
throw std::runtime_error("Error found in the file " + menuFile); throw std::runtime_error("Error found in the file " + menuFile);
} }
menu_ = gtk_builder_get_object(builder, "menu"); menu_ = gtk_builder_get_object(builder, "menu");
if (menu_ == nullptr) { if (menu_ == nullptr) {
g_object_unref(builder);
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
} }
submenus_ = std::map<std::string, GtkMenuItem*>(); submenus_ = std::map<std::string, GtkMenuItem*>();
@@ -105,11 +107,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
for (Json::Value::const_iterator it = config_["menu-actions"].begin(); for (Json::Value::const_iterator it = config_["menu-actions"].begin();
it != config_["menu-actions"].end(); ++it) { it != config_["menu-actions"].end(); ++it) {
std::string key = it.key().asString(); std::string key = it.key().asString();
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); auto* item = gtk_builder_get_object(builder, key.c_str());
if (item == nullptr) {
spdlog::warn("Menu item '{}' not found in builder file", key);
continue;
}
submenus_[key] = GTK_MENU_ITEM(item);
menuActionsMap_[key] = it->asString(); menuActionsMap_[key] = it->asString();
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
(gpointer)menuActionsMap_[key].c_str()); (gpointer)menuActionsMap_[key].c_str());
} }
g_object_unref(builder);
} catch (std::runtime_error& e) { } catch (std::runtime_error& e) {
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what()); spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
} }
@@ -141,7 +149,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
if (format_icons.isArray()) { if (format_icons.isArray()) {
auto size = format_icons.size(); auto size = format_icons.size();
if (size != 0U) { if (size != 0U) {
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
format_icons = format_icons[idx]; format_icons = format_icons[idx];
} }
} }
@@ -167,7 +176,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
if (format_icons.isArray()) { if (format_icons.isArray()) {
auto size = format_icons.size(); auto size = format_icons.size();
if (size != 0U) { if (size != 0U) {
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
format_icons = format_icons[idx]; format_icons = format_icons[idx];
} }
} }

View File

@@ -166,9 +166,9 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
} }
// Check that a menu has been configured // Check that a menu has been configured
if (config_["menu"].isString()) { if (rec != eventMap_.cend() && config_["menu"].isString()) {
// Check if the event is the one specified for the "menu" option // Check if the event is the one specified for the "menu" option
if (rec->second == config_["menu"].asString()) { if (rec->second == config_["menu"].asString() && menu_ != nullptr) {
// Popup the menu // Popup the menu
gtk_widget_show_all(GTK_WIDGET(menu_)); gtk_widget_show_all(GTK_WIDGET(menu_));
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e)); gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));

View File

@@ -143,6 +143,11 @@ void Group::refresh(int sig) {
} }
} }
bool Group::handleScroll(GdkEventScroll* e) {
// no scroll.
return true;
}
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; } Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
void Group::addWidget(Gtk::Widget& widget) { void Group::addWidget(Gtk::Widget& widget) {

View File

@@ -33,7 +33,10 @@ static void writeSignalToPipe(int signum) {
// to `signal_handler`. // to `signal_handler`.
static void catchSignals(waybar::SafeSignal<int>& signal_handler) { static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
int fd[2]; int fd[2];
pipe(fd); if (pipe(fd) != 0) {
spdlog::error("Failed to create signal pipe: {}", strerror(errno));
return;
}
int signal_pipe_read_fd = fd[0]; int signal_pipe_read_fd = fd[0];
signal_pipe_write_fd = fd[1]; signal_pipe_write_fd = fd[1];
@@ -138,15 +141,16 @@ static void handleSignalMainThread(int signum, bool& reload) {
break; break;
case SIGCHLD: case SIGCHLD:
spdlog::debug("Received SIGCHLD in signalThread"); spdlog::debug("Received SIGCHLD in signalThread");
if (!reap.empty()) { {
reap_mtx.lock(); std::lock_guard<std::mutex> lock(reap_mtx);
for (auto it = reap.begin(); it != reap.end(); ++it) { for (auto it = reap.begin(); it != reap.end();) {
if (waitpid(*it, nullptr, WNOHANG) == *it) { if (waitpid(*it, nullptr, WNOHANG) == *it) {
spdlog::debug("Reaped child with PID: {}", *it); spdlog::debug("Reaped child with PID: {}", *it);
it = reap.erase(it); it = reap.erase(it);
} else {
++it;
} }
} }
reap_mtx.unlock();
} }
break; break;
default: default:

View File

@@ -58,11 +58,11 @@ auto waybar::modules::Backlight::update() -> void {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), label_.set_tooltip_markup(fmt::format(fmt::runtime(tooltip_format),
fmt::arg("percent", percent), fmt::arg("percent", percent),
fmt::arg("icon", getIcon(percent)))); fmt::arg("icon", getIcon(percent))));
} else { } else {
label_.set_tooltip_text(desc); label_.set_tooltip_markup(desc);
} }
} }
} else { } else {

View File

@@ -139,7 +139,9 @@ void waybar::modules::Battery::refreshBatteries() {
auto event_path = (node.path() / "uevent"); auto event_path = (node.path() / "uevent");
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS); auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
if (wd < 0) { if (wd < 0) {
throw std::runtime_error("Could not watch events for " + node.path().string()); spdlog::warn("Could not watch events for {} (device may have been removed)",
node.path().string());
continue;
} }
batteries_[node.path()] = wd; batteries_[node.path()] = wd;
} }
@@ -686,7 +688,7 @@ auto waybar::modules::Battery::update() -> void {
status = getAdapterStatus(capacity); status = getAdapterStatus(capacity);
} }
auto status_pretty = status; auto status_pretty = status;
puts(status.c_str());
// Transform to lowercase and replace space with dash // Transform to lowercase and replace space with dash
std::ranges::transform(status.begin(), status.end(), status.begin(), std::ranges::transform(status.begin(), status.end(), status.begin(),
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); }); [](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
@@ -790,16 +792,19 @@ void waybar::modules::Battery::processEvents(std::string& state, std::string& st
if (!events.isObject() || events.empty()) { if (!events.isObject() || events.empty()) {
return; return;
} }
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging", auto exec = [](Json::Value const& event) {
state.empty() ? std::to_string(capacity) : state); if (!event.isString()) return;
if (auto command = event.asString(); !command.empty()) {
util::command::exec(command, "");
}
};
std::string status_name = status == "discharging" ? "on-discharging" : "on-charging";
std::string event_name = status_name + '-' + (state.empty() ? std::to_string(capacity) : state);
if (last_event_ != event_name) { if (last_event_ != event_name) {
spdlog::debug("battery: triggering event {}", event_name); spdlog::debug("battery: triggering event {}", event_name);
if (events[event_name].isString()) { exec(events[event_name]);
std::string exec = events[event_name].asString(); if (!last_event_.empty() && last_event_[3] != event_name[3]) {
// Execute the command if it is not empty exec(events[status_name]);
if (!exec.empty()) {
util::command::exec(exec, "");
}
} }
last_event_ = event_name; last_event_ = event_name;
} }

View File

@@ -111,13 +111,16 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
findConnectedDevices(cur_controller_->path, connected_devices_); findConnectedDevices(cur_controller_->path, connected_devices_);
} }
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this); if (manager_) {
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this); g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this);
g_signal_connect(manager_.get(), "interface-proxy-properties-changed", g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
G_CALLBACK(onInterfaceProxyPropertiesChanged), this); g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this); G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved), g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved),
this); this);
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
}
#ifdef WANT_RFKILL #ifdef WANT_RFKILL
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
@@ -446,6 +449,10 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> { auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
std::optional<ControllerInfo> controller_info; std::optional<ControllerInfo> controller_info;
if (!manager_) {
return controller_info;
}
GList* objects = g_dbus_object_manager_get_objects(manager_.get()); GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) { for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data); GDBusObject* object = G_DBUS_OBJECT(l->data);
@@ -465,6 +472,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional<Controller
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path, auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
std::vector<DeviceInfo>& connected_devices) std::vector<DeviceInfo>& connected_devices)
-> void { -> void {
if (!manager_) {
return;
}
GList* objects = g_dbus_object_manager_get_objects(manager_.get()); GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) { for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data); GDBusObject* object = G_DBUS_OBJECT(l->data);

View File

@@ -26,6 +26,7 @@ void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void { auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
Glib::signal_idle().connect_once([this, input]() { Glib::signal_idle().connect_once([this, input]() {
if (silence_) { if (silence_) {
silence_ = false;
label_.get_style_context()->remove_class("silent"); label_.get_style_context()->remove_class("silent");
if (!label_.get_style_context()->has_class("updated")) if (!label_.get_style_context()->has_class("updated"))
label_.get_style_context()->add_class("updated"); label_.get_style_context()->add_class("updated");
@@ -38,7 +39,6 @@ auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
label_.show(); label_.show();
ALabel::update(); ALabel::update();
}); });
silence_ = false;
} }
auto waybar::modules::cava::Cava::onSilence() -> void { auto waybar::modules::cava::Cava::onSilence() -> void {

View File

@@ -218,7 +218,7 @@ void waybar::modules::cava::CavaBackend::loadConfig() {
prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str()); prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str());
if (config_["source"].isString()) { if (config_["source"].isString()) {
if (prm_.audio_source) free(prm_.audio_source); if (prm_.audio_source) free(prm_.audio_source);
prm_.audio_source = config_["source"].asString().data(); prm_.audio_source = strdup(config_["source"].asString().c_str());
} }
if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt(); if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt();
if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt(); if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();

View File

@@ -35,7 +35,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()}; throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()};
} }
hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit")); hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit"));
if (!hooks_.init) { if (!hooks_.deinit) {
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()}; throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
} }
// Optional functions // Optional functions

View File

@@ -26,9 +26,7 @@ auto waybar::modules::Cpu::update() -> void {
auto [load1, load5, load15] = Load::getLoad(); auto [load1, load5, load15] = Load::getLoad();
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip);
}
auto format = format_; auto format = format_;
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
auto state = getState(total_usage); auto state = getState(total_usage);
@@ -48,14 +46,25 @@ auto waybar::modules::Cpu::update() -> void {
store.push_back(fmt::arg("max_frequency", max_frequency)); store.push_back(fmt::arg("max_frequency", max_frequency));
store.push_back(fmt::arg("min_frequency", min_frequency)); store.push_back(fmt::arg("min_frequency", min_frequency));
store.push_back(fmt::arg("avg_frequency", avg_frequency)); store.push_back(fmt::arg("avg_frequency", avg_frequency));
std::vector<std::string> arg_names;
arg_names.reserve(cpu_usage.size() * 2);
for (size_t i = 1; i < cpu_usage.size(); ++i) { for (size_t i = 1; i < cpu_usage.size(); ++i) {
auto core_i = i - 1; auto core_i = i - 1;
auto core_format = fmt::format("usage{}", core_i); arg_names.push_back(fmt::format("usage{}", core_i));
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
auto icon_format = fmt::format("icon{}", core_i); arg_names.push_back(fmt::format("icon{}", core_i));
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
} }
label_.set_markup(fmt::vformat(format, store)); label_.set_markup(fmt::vformat(format, store));
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
tooltip = config_["tooltip-format"].asString();
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
} else {
label_.set_tooltip_markup(tooltip);
}
}
} }
// Call parent update // Call parent update

View File

@@ -20,12 +20,7 @@ waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::V
auto waybar::modules::CpuFrequency::update() -> void { auto waybar::modules::CpuFrequency::update() -> void {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
if (tooltipEnabled()) {
auto tooltip =
fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n",
min_frequency, avg_frequency, max_frequency);
label_.set_tooltip_text(tooltip);
}
auto format = format_; auto format = format_;
auto state = getState(avg_frequency); auto state = getState(avg_frequency);
if (!state.empty() && config_["format-" + state].isString()) { if (!state.empty() && config_["format-" + state].isString()) {
@@ -43,6 +38,18 @@ auto waybar::modules::CpuFrequency::update() -> void {
store.push_back(fmt::arg("min_frequency", min_frequency)); store.push_back(fmt::arg("min_frequency", min_frequency));
store.push_back(fmt::arg("avg_frequency", avg_frequency)); store.push_back(fmt::arg("avg_frequency", avg_frequency));
label_.set_markup(fmt::vformat(format, store)); label_.set_markup(fmt::vformat(format, store));
if (tooltipEnabled()) {
std::string tooltip;
if (config_["tooltip-format"].isString()) {
tooltip = config_["tooltip-format"].asString();
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
} else {
tooltip = "Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n";
label_.set_tooltip_markup(
fmt::format(fmt::runtime(tooltip), min_frequency, avg_frequency, max_frequency));
}
}
} }
// Call parent update // Call parent update

View File

@@ -20,9 +20,7 @@ waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& co
auto waybar::modules::CpuUsage::update() -> void { auto waybar::modules::CpuUsage::update() -> void {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip);
}
auto format = format_; auto format = format_;
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
auto state = getState(total_usage); auto state = getState(total_usage);
@@ -38,14 +36,25 @@ auto waybar::modules::CpuUsage::update() -> void {
fmt::dynamic_format_arg_store<fmt::format_context> store; fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("usage", total_usage)); store.push_back(fmt::arg("usage", total_usage));
store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
std::vector<std::string> arg_names;
arg_names.reserve(cpu_usage.size() * 2);
for (size_t i = 1; i < cpu_usage.size(); ++i) { for (size_t i = 1; i < cpu_usage.size(); ++i) {
auto core_i = i - 1; auto core_i = i - 1;
auto core_format = fmt::format("usage{}", core_i); arg_names.push_back(fmt::format("usage{}", core_i));
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
auto icon_format = fmt::format("icon{}", core_i); arg_names.push_back(fmt::format("icon{}", core_i));
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
} }
label_.set_markup(fmt::vformat(format, store)); label_.set_markup(fmt::vformat(format, store));
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
tooltip = config_["tooltip-format"].asString();
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
} else {
label_.set_tooltip_markup(tooltip);
}
}
} }
// Call parent update // Call parent update
@@ -71,7 +80,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
auto [prev_idle, prev_total] = prev_times[0]; auto [prev_idle, prev_total] = prev_times[0];
const float delta_idle = curr_idle - prev_idle; const float delta_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total; const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total); uint16_t tmp =
(delta_total > 0) ? static_cast<uint16_t>(100 * (1 - delta_idle / delta_total)) : 0;
tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp); tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp);
usage.push_back(tmp); usage.push_back(tmp);
} else { } else {
@@ -93,7 +103,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
} }
const float delta_idle = curr_idle - prev_idle; const float delta_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total; const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total); uint16_t tmp =
(delta_total > 0) ? static_cast<uint16_t>(100 * (1 - delta_idle / delta_total)) : 0;
if (i == 0) { if (i == 0) {
tooltip = fmt::format("Total: {}%", tmp); tooltip = fmt::format("Total: {}%", tmp);
} else { } else {

View File

@@ -182,9 +182,10 @@ auto waybar::modules::Custom::update() -> void {
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (tooltip_format_enabled_) { if (tooltip_format_enabled_) {
auto tooltip = config_["tooltip-format"].asString(); auto tooltip = config_["tooltip-format"].asString();
tooltip = fmt::format( tooltip = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_),
fmt::runtime(tooltip), fmt::arg("text", text_), fmt::arg("alt", alt_), fmt::arg("tooltip", tooltip_), fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
label_.set_tooltip_markup(tooltip); label_.set_tooltip_markup(tooltip);
} else if (text_ == tooltip_) { } else if (text_ == tooltip_) {
label_.set_tooltip_markup(str); label_.set_tooltip_markup(str);

View File

@@ -41,7 +41,7 @@ auto waybar::modules::Disk::update() -> void {
fs_used - File system used space fs_used - File system used space
*/ */
if (err != 0) { if (err != 0 || stats.f_blocks == 0) {
event_box_.hide(); event_box_.hide();
return; return;
} }
@@ -81,7 +81,7 @@ auto waybar::modules::Disk::update() -> void {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used),
fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), fmt::arg("percentage_used", percentage_used), fmt::arg("total", total),
@@ -112,4 +112,4 @@ float waybar::modules::Disk::calc_specific_divisor(std::string divisor) {
} else { // default to Bytes if it is anything that we don't recongnise } else { // default to Bytes if it is anything that we don't recongnise
return 1.0; return 1.0;
} }
} }

View File

@@ -179,6 +179,7 @@ bool Tags::handle_button_press(GdkEventButton* event_button, uint32_t tag) {
} }
void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) {
if (tag >= buttons_.size()) return;
// First clear all occupied state // First clear all occupied state
auto& button = buttons_[tag]; auto& button = buttons_[tag];
if (clients) { if (clients) {

View File

@@ -116,7 +116,7 @@ void Window::handle_frame() {
updateAppIconName(appid_, ""); updateAppIconName(appid_, "");
updateAppIcon(); updateAppIcon();
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(title_); label_.set_tooltip_markup(title_);
} }
} }

View File

@@ -143,7 +143,9 @@ void WorkspaceManager::handle_finished() {
ext_manager_ = nullptr; ext_manager_ = nullptr;
} }
void WorkspaceManager::commit() const { ext_workspace_manager_v1_commit(ext_manager_); } void WorkspaceManager::commit() const {
if (ext_manager_) ext_workspace_manager_v1_commit(ext_manager_);
}
void WorkspaceManager::update() { void WorkspaceManager::update() {
spdlog::debug("[ext/workspaces]: Updating state"); spdlog::debug("[ext/workspaces]: Updating state");

View File

@@ -128,9 +128,9 @@ void Gamemode::getData() {
Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters); Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters);
if (data && data.is_of_type(Glib::VariantType("(v)"))) { if (data && data.is_of_type(Glib::VariantType("(v)"))) {
Glib::VariantBase variant; Glib::VariantBase variant;
g_variant_get(data.gobj_copy(), "(v)", &variant); g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_INT32)) { if (variant && variant.is_of_type(Glib::VARIANT_TYPE_INT32)) {
g_variant_get(variant.gobj_copy(), "i", &gameCount); g_variant_get(const_cast<GVariant*>(variant.gobj()), "i", &gameCount);
return; return;
} }
} }
@@ -158,7 +158,7 @@ void Gamemode::prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection>& con
const Glib::VariantContainerBase& parameters) { const Glib::VariantContainerBase& parameters) {
if (parameters.is_of_type(Glib::VariantType("(b)"))) { if (parameters.is_of_type(Glib::VariantType("(b)"))) {
gboolean sleeping; gboolean sleeping;
g_variant_get(parameters.gobj_copy(), "(b)", &sleeping); g_variant_get(const_cast<GVariant*>(parameters.gobj()), "(b)", &sleeping);
if (!sleeping) { if (!sleeping) {
getData(); getData();
dp.emit(); dp.emit();
@@ -212,7 +212,7 @@ auto Gamemode::update() -> void {
// Tooltip // Tooltip
if (tooltip) { if (tooltip) {
std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount)); std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount));
box_.set_tooltip_text(text); box_.set_tooltip_markup(text);
} }
// Label format // Label format

View File

@@ -60,7 +60,7 @@ auto Submap::update() -> void {
} else { } else {
label_.set_markup(fmt::format(fmt::runtime(format_), submap_)); label_.set_markup(fmt::format(fmt::runtime(format_), submap_));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(submap_); label_.set_tooltip_markup(submap_);
} }
event_box_.show(); event_box_.show();
} }

View File

@@ -72,13 +72,13 @@ auto Window::update() -> void {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
label_.set_tooltip_text( label_.set_tooltip_markup(
fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName), fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName),
fmt::arg("initialTitle", windowData_.initial_title), fmt::arg("initialTitle", windowData_.initial_title),
fmt::arg("class", windowData_.class_name), fmt::arg("class", windowData_.class_name),
fmt::arg("initialClass", windowData_.initial_class_name))); fmt::arg("initialClass", windowData_.initial_class_name)));
} else if (!label_text.empty()) { } else if (!label_text.empty()) {
label_.set_tooltip_text(label_text); label_.set_tooltip_markup(label_text);
} }
} }
@@ -237,9 +237,7 @@ void Window::queryActiveWorkspace() {
} }
} }
void Window::onEvent(const std::string& ev) { void Window::onEvent(const std::string& ev) { dp.emit(); }
dp.emit();
}
void Window::setClass(const std::string& classname, bool enable) { void Window::setClass(const std::string& classname, bool enable) {
if (enable) { if (enable) {

View File

@@ -38,7 +38,7 @@ WindowCount::~WindowCount() {
auto WindowCount::update() -> void { auto WindowCount::update() -> void {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
queryActiveWorkspace(); queryActiveWorkspace();
std::string format = config_["format"].asString(); std::string format = config_["format"].asString();
@@ -58,7 +58,7 @@ auto WindowCount::update() -> void {
} else if (!format.empty()) { } else if (!format.empty()) {
label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows)); label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows));
} else { } else {
label_.set_text(fmt::format("{}", workspace_.windows)); label_.set_markup(fmt::format("{}", workspace_.windows));
} }
label_.show(); label_.show();
@@ -125,9 +125,7 @@ void WindowCount::queryActiveWorkspace() {
} }
} }
void WindowCount::onEvent(const std::string& ev) { void WindowCount::onEvent(const std::string& ev) { dp.emit(); }
dp.emit();
}
void WindowCount::setClass(const std::string& classname, bool enable) { void WindowCount::setClass(const std::string& classname, bool enable) {
if (enable) { if (enable) {

View File

@@ -300,7 +300,7 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) {
} }
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL); auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
window_box->set_tooltip_text(window_repr.window_title); window_box->set_tooltip_markup(window_repr.window_title);
window_box->get_style_context()->add_class("taskbar-window"); window_box->get_style_context()->add_class("taskbar-window");
if (window_repr.isActive) { if (window_repr.isActive) {
window_box->get_style_context()->add_class("active"); window_box->get_style_context()->add_class("active");

View File

@@ -43,6 +43,13 @@ void Workspaces::init() {
m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt(); m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt();
initializeWorkspaces(); initializeWorkspaces();
if (barScroll()) {
auto& window = const_cast<Bar&>(m_bar).window;
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
dp.emit(); dp.emit();
} }
@@ -636,6 +643,7 @@ auto Workspaces::parseConfig(const Json::Value& config) -> void {
populateBoolConfig(config, "persistent-only", m_persistentOnly); populateBoolConfig(config, "persistent-only", m_persistentOnly);
populateBoolConfig(config, "active-only", m_activeOnly); populateBoolConfig(config, "active-only", m_activeOnly);
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
populateBoolConfig(config, "enable-bar-scroll", m_barScroll);
m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value()); m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value());
populateSortByConfig(config); populateSortByConfig(config);
@@ -1151,4 +1159,31 @@ std::optional<int> Workspaces::parseWorkspaceId(std::string const& workspaceIdSt
} }
} }
bool Workspaces::handleScroll(GdkEventScroll* e) {
// Ignore emulated scroll events on window
if (gdk_event_get_pointer_emulated((GdkEvent*)e)) {
return false;
}
auto dir = AModule::getScrollDir(e);
if (dir == SCROLL_DIR::NONE) {
return true;
}
if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) {
if (allOutputs()) {
m_ipc.getSocket1Reply("dispatch workspace e+1");
} else {
m_ipc.getSocket1Reply("dispatch workspace m+1");
}
} else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
if (allOutputs()) {
m_ipc.getSocket1Reply("dispatch workspace e-1");
} else {
m_ipc.getSocket1Reply("dispatch workspace m-1");
}
}
return true;
}
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@@ -14,22 +14,27 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
size_ = config["size"].asInt(); size_ = config["size"].asInt();
interval_ = config_["interval"] == "once" const auto once = std::chrono::milliseconds::max();
? std::chrono::milliseconds::max() if (!config_.isMember("interval") || config_["interval"].isNull() ||
: std::chrono::milliseconds(std::max( config_["interval"] == "once") {
1L, // Minimum 1ms due to millisecond precision interval_ = once;
static_cast<long>( } else if (config_["interval"].isNumeric()) {
(config_["interval"].isNumeric() ? config_["interval"].asDouble() : 0) * const auto interval_seconds = config_["interval"].asDouble();
1000))); if (interval_seconds <= 0) {
interval_ = once;
} else {
interval_ =
std::chrono::milliseconds(std::max(1L, // Minimum 1ms due to millisecond precision
static_cast<long>(interval_seconds * 1000)));
}
} else {
interval_ = once;
}
if (size_ == 0) { if (size_ == 0) {
size_ = 16; size_ = 16;
} }
if (interval_.count() == 0) {
interval_ = std::chrono::milliseconds::max();
}
delayWorker(); delayWorker();
} }

View File

@@ -123,7 +123,7 @@ auto Inhibitor::update() -> void {
label_.get_style_context()->add_class(status_text); label_.get_style_context()->add_class(status_text);
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(status_text); label_.set_tooltip_markup(status_text);
} }
return ALabel::update(); return ALabel::update();

View File

@@ -53,7 +53,7 @@ std::string JACK::JACKState() {
auto JACK::update() -> void { auto JACK::update() -> void {
std::string format; std::string format;
std::string state = JACKState(); std::string state = JACKState();
float latency = 1000 * (float)bufsize_ / (float)samplerate_; float latency = samplerate_ > 0 ? 1000.0f * (float)bufsize_ / (float)samplerate_ : 0.0f;
if (label_.get_style_context()->has_class("xrun")) { if (label_.get_style_context()->has_class("xrun")) {
label_.get_style_context()->remove_class("xrun"); label_.get_style_context()->remove_class("xrun");
@@ -80,7 +80,7 @@ auto JACK::update() -> void {
if (tooltipEnabled()) { if (tooltipEnabled()) {
std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms"; std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms";
if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString(); if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString();
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)), fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)),
fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_), fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_),
fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_))); fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_)));
@@ -91,16 +91,19 @@ auto JACK::update() -> void {
} }
int JACK::bufSize(jack_nframes_t size) { int JACK::bufSize(jack_nframes_t size) {
std::lock_guard<std::mutex> lock(mutex_);
bufsize_ = size; bufsize_ = size;
return 0; return 0;
} }
int JACK::sampleRate(jack_nframes_t rate) { int JACK::sampleRate(jack_nframes_t rate) {
std::lock_guard<std::mutex> lock(mutex_);
samplerate_ = rate; samplerate_ = rate;
return 0; return 0;
} }
int JACK::xrun() { int JACK::xrun() {
std::lock_guard<std::mutex> lock(mutex_);
xruns_ += 1; xruns_ += 1;
state_ = "xrun"; state_ = "xrun";
return 0; return 0;

View File

@@ -232,9 +232,12 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
} }
tryAddDevice(dev_path); tryAddDevice(dev_path);
} else if (event->mask & IN_DELETE) { } else if (event->mask & IN_DELETE) {
std::lock_guard<std::mutex> lock(devices_mutex_);
auto it = libinput_devices_.find(dev_path); auto it = libinput_devices_.find(dev_path);
if (it != libinput_devices_.end()) { if (it != libinput_devices_.end()) {
spdlog::info("Keyboard {} has been removed.", dev_path); spdlog::info("Keyboard {} has been removed.", dev_path);
libinput_path_remove_device(it->second);
libinput_device_unref(it->second);
libinput_devices_.erase(it); libinput_devices_.erase(it);
} }
} }
@@ -245,6 +248,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
} }
waybar::modules::KeyboardState::~KeyboardState() { waybar::modules::KeyboardState::~KeyboardState() {
std::lock_guard<std::mutex> lock(devices_mutex_);
for (const auto& [_, dev_ptr] : libinput_devices_) { for (const auto& [_, dev_ptr] : libinput_devices_) {
libinput_path_remove_device(dev_ptr); libinput_path_remove_device(dev_ptr);
} }
@@ -256,11 +260,17 @@ auto waybar::modules::KeyboardState::update() -> void {
try { try {
std::string dev_path; std::string dev_path;
if (config_["device-path"].isString() && {
libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) { std::lock_guard<std::mutex> lock(devices_mutex_);
dev_path = config_["device-path"].asString(); if (libinput_devices_.empty()) {
} else { return;
dev_path = libinput_devices_.begin()->first; }
if (config_["device-path"].isString() &&
libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) {
dev_path = config_["device-path"].asString();
} else {
dev_path = libinput_devices_.begin()->first;
}
} }
int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
auto dev = openDevice(fd); auto dev = openDevice(fd);
@@ -308,10 +318,15 @@ auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path)
auto dev = openDevice(fd); auto dev = openDevice(fd);
if (supportsLockStates(dev)) { if (supportsLockStates(dev)) {
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path); spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
std::lock_guard<std::mutex> lock(devices_mutex_);
if (libinput_devices_.find(dev_path) == libinput_devices_.end()) { if (libinput_devices_.find(dev_path) == libinput_devices_.end()) {
auto device = libinput_path_add_device(libinput_, dev_path.c_str()); auto device = libinput_path_add_device(libinput_, dev_path.c_str());
libinput_device_ref(device); if (device) {
libinput_devices_[dev_path] = device; libinput_device_ref(device);
libinput_devices_[dev_path] = device;
} else {
spdlog::warn("keyboard-state: Failed to add device to libinput: {}", dev_path);
}
} }
} }
libevdev_free(dev); libevdev_free(dev);

View File

@@ -22,7 +22,7 @@ auto waybar::modules::Load::update() -> void {
auto [load1, load5, load15] = Load::getLoad(); auto [load1, load5, load15] = Load::getLoad();
if (tooltipEnabled()) { if (tooltipEnabled()) {
auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15);
label_.set_tooltip_text(tooltip); label_.set_tooltip_markup(tooltip);
} }
auto format = format_; auto format = format_;
auto state = getState(load1); auto state = getState(load1);

View File

@@ -69,7 +69,7 @@ auto waybar::modules::Memory::update() -> void {
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_format = config_["tooltip-format"].asString();
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), used_ram_percentage, fmt::runtime(tooltip_format), used_ram_percentage,
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
fmt::arg("percentage", used_ram_percentage), fmt::arg("percentage", used_ram_percentage),
@@ -78,7 +78,7 @@ auto waybar::modules::Memory::update() -> void {
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes),
fmt::arg("swapAvail", available_swap_gigabytes))); fmt::arg("swapAvail", available_swap_gigabytes)));
} else { } else {
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); label_.set_tooltip_markup(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
} }
} }
} else { } else {

View File

@@ -110,7 +110,7 @@ void waybar::modules::MPD::setLabel() {
? config_["tooltip-format-disconnected"].asString() ? config_["tooltip-format-disconnected"].asString()
: "MPD (disconnected)"; : "MPD (disconnected)";
// Nothing to format // Nothing to format
label_.set_tooltip_text(tooltip_format); label_.set_tooltip_markup(tooltip_format);
} }
return; return;
} }
@@ -210,7 +210,7 @@ void waybar::modules::MPD::setLabel() {
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri)); fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri));
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} catch (fmt::format_error const& e) { } catch (fmt::format_error const& e) {
spdlog::warn("mpd: format error (tooltip): {}", e.what()); spdlog::warn("mpd: format error (tooltip): {}", e.what());
} }
@@ -323,6 +323,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
case MPD_ERROR_SYSTEM: case MPD_ERROR_SYSTEM:
if (auto ec = mpd_connection_get_system_error(conn); ec != 0) { if (auto ec = mpd_connection_get_system_error(conn); ec != 0) {
mpd_connection_clear_error(conn); mpd_connection_clear_error(conn);
connection_.reset();
throw std::system_error(ec, std::system_category()); throw std::system_error(ec, std::system_category());
} }
G_GNUC_FALLTHROUGH; G_GNUC_FALLTHROUGH;

View File

@@ -117,7 +117,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
} }
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error) { if (error) {
g_error_free(error); g_error_free(error);
} }
@@ -178,6 +178,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
} }
Mpris::~Mpris() { Mpris::~Mpris() {
if (last_active_player_ && last_active_player_ != player) g_object_unref(last_active_player_);
if (manager != nullptr) g_object_unref(manager); if (manager != nullptr) g_object_unref(manager);
if (player != nullptr) g_object_unref(player); if (player != nullptr) g_object_unref(player);
} }
@@ -458,10 +459,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void {
if (!mpris) return; if (!mpris) return;
spdlog::debug("mpris: player-stop callback"); spdlog::debug("mpris: player-stop callback");
// update widget (update() handles visibility)
// hide widget
mpris->event_box_.set_visible(false);
// update widget
mpris->dp.emit(); mpris->dp.emit();
} }
@@ -480,7 +478,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
} }
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error) { if (error) {
g_error_free(error); g_error_free(error);
} }
@@ -488,7 +486,12 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
char* player_status = nullptr; char* player_status = nullptr;
auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED; auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED;
g_object_get(player, "status", &player_status, "playback-status", &player_playback_status, NULL);
// Clean up previous fallback player
if (last_active_player_ && last_active_player_ != player) {
g_object_unref(last_active_player_);
last_active_player_ = nullptr;
}
std::string player_name = player_; std::string player_name = player_;
if (player_name == "playerctld") { if (player_name == "playerctld") {
@@ -498,19 +501,52 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
} }
// > get the list of players [..] in order of activity // > get the list of players [..] in order of activity
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249 // https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
players = g_list_first(players); PlayerctlPlayer* first_valid_player = nullptr;
if (players) std::string first_valid_name;
player_name = static_cast<PlayerctlPlayerName*>(players->data)->name; for (auto* p = g_list_first(players); p != nullptr; p = p->next) {
else auto* pn = static_cast<PlayerctlPlayerName*>(p->data);
return std::nullopt; // no players found, hide the widget std::string name = pn->name;
} if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
[&](const std::string& ignored) { return name == ignored; })) {
if (std::any_of(ignored_players_.begin(), ignored_players_.end(), spdlog::warn("mpris[{}]: ignoring player update", name);
[&](const std::string& pn) { return player_name == pn; })) { continue;
}
auto* tmp = playerctl_player_new_from_name(pn, &error);
if (error || !tmp) continue;
if (!first_valid_player) {
first_valid_player = tmp;
first_valid_name = name;
}
PlayerctlPlaybackStatus status;
g_object_get(tmp, "playback-status", &status, NULL);
if (status == PLAYERCTL_PLAYBACK_STATUS_PLAYING) {
if (tmp != first_valid_player) g_object_unref(first_valid_player);
last_active_player_ = tmp;
player_name = name;
break;
}
if (tmp != first_valid_player) g_object_unref(tmp);
}
if (!last_active_player_) {
if (!first_valid_player) return std::nullopt;
last_active_player_ = first_valid_player;
player_name = first_valid_name;
}
} else if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
[&](const std::string& pn) { return player_name == pn; })) {
spdlog::warn("mpris[{}]: ignoring player update", player_name); spdlog::warn("mpris[{}]: ignoring player update", player_name);
return std::nullopt; return std::nullopt;
} else {
last_active_player_ = player;
} }
g_object_get(last_active_player_, "status", &player_status, "playback-status",
&player_playback_status, NULL);
if (!player_status) {
spdlog::error("mpris: failed to get player status");
return std::nullopt;
}
// make status lowercase // make status lowercase
player_status[0] = std::tolower(player_status[0]); player_status[0] = std::tolower(player_status[0]);
@@ -524,28 +560,29 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
.length = std::nullopt, .length = std::nullopt,
}; };
if (auto* artist_ = playerctl_player_get_artist(player, &error)) { if (auto* artist_ = playerctl_player_get_artist(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); spdlog::debug("mpris[{}]: artist = {}", info.name, artist_);
info.artist = artist_; info.artist = artist_;
g_free(artist_); g_free(artist_);
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* album_ = playerctl_player_get_album(player, &error)) { if (auto* album_ = playerctl_player_get_album(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: album = {}", info.name, album_); spdlog::debug("mpris[{}]: album = {}", info.name, album_);
info.album = album_; info.album = album_;
g_free(album_); g_free(album_);
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* title_ = playerctl_player_get_title(player, &error)) { if (auto* title_ = playerctl_player_get_title(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: title = {}", info.name, title_); spdlog::debug("mpris[{}]: title = {}", info.name, title_);
info.title = title_; info.title = title_;
g_free(title_); g_free(title_);
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) { if (auto* length_ =
playerctl_player_print_metadata_prop(last_active_player_, "mpris:length", &error)) {
spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_); spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_);
auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len); auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
@@ -557,7 +594,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
if (error) goto errorexit; if (error) goto errorexit;
{ {
auto position_ = playerctl_player_get_position(player, &error); auto position_ = playerctl_player_get_position(last_active_player_, &error);
if (error) { if (error) {
// it's fine to have an error here because not all players report a position // it's fine to have an error here because not all players report a position
g_error_free(error); g_error_free(error);
@@ -609,12 +646,13 @@ bool Mpris::handleToggle(GdkEventButton* const& e) {
}); });
// Command pattern: encapsulate each button's action // Command pattern: encapsulate each button's action
auto* target = last_active_player_ ? last_active_player_ : player;
const ButtonAction actions[] = { const ButtonAction actions[] = {
{1, "on-click", [&]() { playerctl_player_play_pause(player, &error); }}, {1, "on-click", [&]() { playerctl_player_play_pause(target, &error); }},
{2, "on-click-middle", [&]() { playerctl_player_previous(player, &error); }}, {2, "on-click-middle", [&]() { playerctl_player_previous(target, &error); }},
{3, "on-click-right", [&]() { playerctl_player_next(player, &error); }}, {3, "on-click-right", [&]() { playerctl_player_next(target, &error); }},
{8, "on-click-backward", [&]() { playerctl_player_previous(player, &error); }}, {8, "on-click-backward", [&]() { playerctl_player_previous(target, &error); }},
{9, "on-click-forward", [&]() { playerctl_player_next(player, &error); }}, {9, "on-click-forward", [&]() { playerctl_player_next(target, &error); }},
}; };
for (const auto& action : actions) { for (const auto& action : actions) {
@@ -734,7 +772,7 @@ auto Mpris::update() -> void {
fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)),
fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string)));
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} catch (fmt::format_error const& e) { } catch (fmt::format_error const& e) {
spdlog::warn("mpris: format error (tooltip): {}", e.what()); spdlog::warn("mpris: format error (tooltip): {}", e.what());
} }

View File

@@ -45,7 +45,8 @@ waybar::modules::Network::readBandwidthUsage() {
std::istringstream iss(line); std::istringstream iss(line);
std::string ifacename; std::string ifacename;
iss >> ifacename; // ifacename contains "eth0:" iss >> ifacename; // ifacename contains "eth0:"
if (ifacename.empty()) continue;
ifacename.pop_back(); // remove trailing ':' ifacename.pop_back(); // remove trailing ':'
if (ifacename != ifname_) { if (ifacename != ifname_) {
continue; continue;

View File

@@ -20,12 +20,13 @@
namespace waybar::modules::niri { namespace waybar::modules::niri {
IPC::IPC() { startIPC(); }
int IPC::connectToSocket() { int IPC::connectToSocket() {
const char* socket_path = getenv("NIRI_SOCKET"); const char* socket_path = getenv("NIRI_SOCKET");
if (socket_path == nullptr) { if (socket_path == nullptr) {
spdlog::warn("Niri is not running, niri IPC will not be available."); throw std::runtime_error("Niri IPC: NIRI_SOCKET was not set! (Is Niri running?)");
return -1;
} }
struct sockaddr_un addr; struct sockaddr_un addr;
@@ -54,16 +55,9 @@ int IPC::connectToSocket() {
void IPC::startIPC() { void IPC::startIPC() {
// will start IPC and relay events to parseIPC // will start IPC and relay events to parseIPC
std::thread([&]() { int socketfd = connectToSocket();
int socketfd;
try {
socketfd = connectToSocket();
} catch (std::exception& e) {
spdlog::error("Niri IPC: failed to start, reason: {}", e.what());
return;
}
if (socketfd == -1) return;
std::thread([this, socketfd]() {
spdlog::info("Niri IPC starting"); spdlog::info("Niri IPC starting");
auto unix_istream = Gio::UnixInputStream::create(socketfd, true); auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
@@ -242,7 +236,6 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
Json::Value IPC::send(const Json::Value& request) { Json::Value IPC::send(const Json::Value& request) {
int socketfd = connectToSocket(); int socketfd = connectToSocket();
if (socketfd == -1) throw std::runtime_error("Niri is not running");
auto unix_istream = Gio::UnixInputStream::create(socketfd, true); auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false);

View File

@@ -67,7 +67,7 @@ void Window::doUpdate() {
updateAppIconName(appId, ""); updateAppIconName(appId, "");
if (tooltipEnabled()) label_.set_tooltip_text(title); if (tooltipEnabled()) label_.set_tooltip_markup(title);
const auto id = window["id"].asUInt64(); const auto id = window["id"].asUInt64();
const auto workspaceId = window["workspace_id"].asUInt64(); const auto workspaceId = window["workspace_id"].asUInt64();

View File

@@ -174,11 +174,11 @@ std::string Workspaces::getIcon(const std::string& value, const Json::Value& ws)
if (ws["is_urgent"].asBool() && icons["urgent"]) return icons["urgent"].asString(); if (ws["is_urgent"].asBool() && icons["urgent"]) return icons["urgent"].asString();
if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString(); if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString();
if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString(); if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString();
if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString(); if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
if (ws["name"]) { if (ws["name"]) {
const auto& name = ws["name"].asString(); const auto& name = ws["name"].asString();

View File

@@ -157,7 +157,7 @@ auto PowerProfilesDaemon::update() -> void {
store.push_back(fmt::arg("icon", getIcon(0, profile.name))); store.push_back(fmt::arg("icon", getIcon(0, profile.name)));
label_.set_markup(fmt::vformat(format_, store)); label_.set_markup(fmt::vformat(format_, store));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); label_.set_tooltip_markup(fmt::vformat(tooltipFormat_, store));
} }
// Set CSS class // Set CSS class
@@ -176,6 +176,10 @@ auto PowerProfilesDaemon::update() -> void {
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) {
if (availableProfiles_.empty()) return true;
if (activeProfile_ == availableProfiles_.end()) {
activeProfile_ = availableProfiles_.begin();
}
if (e->button == 1) /* left click */ { if (e->button == 1) /* left click */ {
activeProfile_++; activeProfile_++;
if (activeProfile_ == availableProfiles_.end()) { if (activeProfile_ == availableProfiles_.end()) {

View File

@@ -138,7 +138,7 @@ auto waybar::modules::Pulseaudio::update() -> void {
fmt::arg("source_volume", source_volume), fmt::arg("source_desc", source_desc), fmt::arg("source_volume", source_volume), fmt::arg("source_desc", source_desc),
fmt::arg("icon", getIcon(sink_volume, getPulseIcon())))); fmt::arg("icon", getIcon(sink_volume, getPulseIcon()))));
} else { } else {
label_.set_tooltip_text(sink_desc); label_.set_tooltip_markup(sink_desc);
} }
} }

View File

@@ -118,6 +118,7 @@ Layout::Layout(const std::string& id, const waybar::Bar& bar, const Json::Value&
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
label_.hide(); label_.hide();

View File

@@ -77,6 +77,7 @@ Mode::Mode(const std::string& id, const waybar::Bar& bar, const Json::Value& con
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
if (config_["hidden-modes"].isArray()) { if (config_["hidden-modes"].isArray()) {

View File

@@ -141,10 +141,12 @@ Tags::Tags(const std::string& id, const waybar::Bar& bar, const Json::Value& con
if (!control_) { if (!control_) {
spdlog::error("river_control_v1 not advertised"); spdlog::error("river_control_v1 not advertised");
return;
} }
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_); seat_status_ = zriver_status_manager_v1_get_river_seat_status(status_manager_, seat_);
@@ -217,6 +219,7 @@ Tags::~Tags() {
} }
void Tags::handle_show() { void Tags::handle_show() {
if (!status_manager_) return;
struct wl_output* output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); struct wl_output* output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output); output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this); zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);

View File

@@ -82,6 +82,7 @@ Window::Window(const std::string& id, const waybar::Bar& bar, const Json::Value&
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
label_.hide(); // hide the label until populated label_.hide(); // hide the label until populated

View File

@@ -25,9 +25,9 @@ auto waybar::modules::Clock::update() -> void {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_format = config_["tooltip-format"].asString();
auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime); auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime);
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} else { } else {
label_.set_tooltip_text(text); label_.set_tooltip_markup(text);
} }
} }
// Call parent update // Call parent update

View File

@@ -102,7 +102,9 @@ Sndio::~Sndio() { sioctl_close(hdl_); }
auto Sndio::update() -> void { auto Sndio::update() -> void {
auto format = format_; auto format = format_;
unsigned int vol = 100. * static_cast<double>(volume_) / static_cast<double>(maxval_); unsigned int vol = (maxval_ > 0) ? static_cast<unsigned int>(100. * static_cast<double>(volume_) /
static_cast<double>(maxval_))
: 0;
if (volume_ == 0) { if (volume_ == 0) {
label_.get_style_context()->add_class("muted"); label_.get_style_context()->add_class("muted");

View File

@@ -59,7 +59,7 @@ void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const G
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) { if (error != nullptr) {
g_error_free(error); g_error_free(error);
} }
@@ -81,7 +81,7 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) { if (error != nullptr) {
g_error_free(error); g_error_free(error);
} }

View File

@@ -365,13 +365,14 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
void Item::updateImage() { void Item::updateImage() {
auto pixbuf = getIconPixbuf(); auto pixbuf = getIconPixbuf();
if (!pixbuf) return;
auto scaled_icon_size = getScaledIconSize(); auto scaled_icon_size = getScaledIconSize();
// If the loaded icon is not square, assume that the icon height should match the // If the loaded icon is not square, assume that the icon height should match the
// requested icon size, but the width is allowed to be different. As such, if the // requested icon size, but the width is allowed to be different. As such, if the
// height of the image does not match the requested icon size, resize the icon such that // height of the image does not match the requested icon size, resize the icon such that
// the aspect ratio is maintained, but the height matches the requested icon size. // the aspect ratio is maintained, but the height matches the requested icon size.
if (pixbuf->get_height() != scaled_icon_size) { if (pixbuf->get_height() > 0 && pixbuf->get_height() != scaled_icon_size) {
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
} }

View File

@@ -31,7 +31,7 @@ Watcher::~Watcher() {
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) { void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error) { if (error) {
g_error_free(error); g_error_free(error);
} }

View File

@@ -44,7 +44,7 @@ auto Mode::update() -> void {
} else { } else {
label_.set_markup(fmt::format(fmt::runtime(format_), mode_)); label_.set_markup(fmt::format(fmt::runtime(format_), mode_));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(mode_); label_.set_tooltip_markup(mode_);
} }
event_box_.show(); event_box_.show();
} }

View File

@@ -99,7 +99,7 @@ auto Window::update() -> void {
fmt::arg("shell", shell_), fmt::arg("marks", marks_)), fmt::arg("shell", shell_), fmt::arg("marks", marks_)),
config_["rewrite"])); config_["rewrite"]));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(window_); label_.set_tooltip_markup(window_);
} }
updateAppIcon(); updateAppIcon();

View File

@@ -77,9 +77,9 @@ void SystemdFailedUnits::RequestSystemState() {
Glib::VariantContainerBase data = proxy->call_sync("Get", parameters); Glib::VariantContainerBase data = proxy->call_sync("Get", parameters);
if (data && data.is_of_type(Glib::VariantType("(v)"))) { if (data && data.is_of_type(Glib::VariantType("(v)"))) {
Glib::VariantBase variant; Glib::VariantBase variant;
g_variant_get(data.gobj_copy(), "(v)", &variant); g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_STRING)) { if (variant && variant.is_of_type(Glib::VARIANT_TYPE_STRING)) {
return g_variant_get_string(variant.gobj_copy(), NULL); return g_variant_get_string(const_cast<GVariant*>(variant.gobj()), NULL);
} }
} }
} catch (Glib::Error& e) { } catch (Glib::Error& e) {
@@ -105,9 +105,9 @@ void SystemdFailedUnits::RequestFailedUnits() {
Glib::VariantContainerBase data = proxy->call_sync("Get", parameters); Glib::VariantContainerBase data = proxy->call_sync("Get", parameters);
if (data && data.is_of_type(Glib::VariantType("(v)"))) { if (data && data.is_of_type(Glib::VariantType("(v)"))) {
Glib::VariantBase variant; Glib::VariantBase variant;
g_variant_get(data.gobj_copy(), "(v)", &variant); g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) { if (variant && variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
return g_variant_get_uint32(variant.gobj_copy()); return g_variant_get_uint32(const_cast<GVariant*>(variant.gobj()));
} }
} }
} catch (Glib::Error& e) { } catch (Glib::Error& e) {

View File

@@ -101,7 +101,7 @@ auto waybar::modules::Temperature::update() -> void {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), fmt::arg("temperatureC", temperature_c), fmt::runtime(tooltip_format), fmt::arg("temperatureC", temperature_c),
fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k))); fmt::arg("temperatureF", temperature_f), fmt::arg("temperatureK", temperature_k)));
} }

View File

@@ -59,10 +59,13 @@ UPower::UPower(const std::string& id, const Json::Value& config)
sigc::mem_fun(*this, &UPower::getConn_cb)); sigc::mem_fun(*this, &UPower::getConn_cb));
// Make UPower client // Make UPower client
GError** gErr = NULL; GError* gErr = NULL;
upClient_ = up_client_new_full(NULL, gErr); upClient_ = up_client_new_full(NULL, &gErr);
if (upClient_ == NULL) if (upClient_ == NULL) {
spdlog::error("Upower. UPower client connection error. {}", (*gErr)->message); spdlog::error("Upower. UPower client connection error. {}",
gErr ? gErr->message : "unknown error");
if (gErr) g_error_free(gErr);
}
// Subscribe UPower events // Subscribe UPower events
g_signal_connect(upClient_, "device-added", G_CALLBACK(deviceAdded_cb), this); g_signal_connect(upClient_, "device-added", G_CALLBACK(deviceAdded_cb), this);

View File

@@ -9,6 +9,7 @@
#include <bit> #include <bit>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include <exception> #include <exception>
#include <ranges> #include <ranges>
#include <thread> #include <thread>
@@ -32,7 +33,13 @@ auto pack_and_write(Sock& sock, std::string&& buf) -> void {
auto read_exact(Sock& sock, size_t n) -> std::string { auto read_exact(Sock& sock, size_t n) -> std::string {
auto buf = std::string(n, 0); auto buf = std::string(n, 0);
for (size_t i = 0; i < n;) i += read(sock.fd, &buf[i], n - i); for (size_t i = 0; i < n;) {
auto r = read(sock.fd, &buf[i], n - i);
if (r <= 0) {
throw std::runtime_error("Wayfire IPC: read failed");
}
i += static_cast<size_t>(r);
}
return buf; return buf;
} }
@@ -53,6 +60,9 @@ auto State::Wset::locate_ws(const Json::Value& geo) -> Workspace& {
} }
auto State::Wset::locate_ws(const Json::Value& geo) const -> const Workspace& { auto State::Wset::locate_ws(const Json::Value& geo) const -> const Workspace& {
if (!output.has_value()) {
throw std::runtime_error("Wayfire IPC: wset has no output assigned");
}
const auto& out = output.value().get(); const auto& out = output.value().get();
auto [qx, rx] = std::div(geo["x"].asInt(), out.w); auto [qx, rx] = std::div(geo["x"].asInt(), out.w);
auto [qy, ry] = std::div(geo["y"].asInt(), out.h); auto [qy, ry] = std::div(geo["y"].asInt(), out.h);
@@ -88,7 +98,10 @@ auto State::update_view(const Json::Value& view) -> void {
auto IPC::get_instance() -> std::shared_ptr<IPC> { auto IPC::get_instance() -> std::shared_ptr<IPC> {
auto p = instance.lock(); auto p = instance.lock();
if (!p) instance = p = std::shared_ptr<IPC>(new IPC); if (!p) {
instance = p = std::shared_ptr<IPC>(new IPC);
p->start();
}
return p; return p;
} }
@@ -116,7 +129,9 @@ auto IPC::connect() -> Sock {
} }
auto IPC::receive(Sock& sock) -> Json::Value { auto IPC::receive(Sock& sock) -> Json::Value {
auto len = *reinterpret_cast<uint32_t*>(read_exact(sock, 4).data()); auto len_buf = read_exact(sock, 4);
uint32_t len;
std::memcpy(&len, len_buf.data(), sizeof(len));
if constexpr (std::endian::native != std::endian::little) len = byteswap(len); if constexpr (std::endian::native != std::endian::little) len = byteswap(len);
auto buf = read_exact(sock, len); auto buf = read_exact(sock, len);
@@ -153,15 +168,15 @@ auto IPC::start() -> void {
send("window-rules/get-focused-view", {}); send("window-rules/get-focused-view", {});
send("window-rules/get-focused-output", {}); send("window-rules/get-focused-output", {});
std::thread([&] { std::thread([self = shared_from_this()] {
auto sock = connect(); auto sock = connect();
{ {
Json::Value json; Json::Value json;
json["method"] = "window-rules/events/watch"; json["method"] = "window-rules/events/watch";
pack_and_write(sock, Json::writeString(writer_builder, json)); pack_and_write(sock, Json::writeString(self->writer_builder, json));
if (receive(sock)["result"] != "ok") { if (self->receive(sock)["result"] != "ok") {
spdlog::error( spdlog::error(
"Wayfire IPC: method \"window-rules/events/watch\"" "Wayfire IPC: method \"window-rules/events/watch\""
" have failed"); " have failed");
@@ -169,10 +184,10 @@ auto IPC::start() -> void {
} }
} }
while (auto json = receive(sock)) { while (auto json = self->receive(sock)) {
auto ev = json["event"].asString(); auto ev = json["event"].asString();
spdlog::debug("Wayfire IPC: received event \"{}\"", ev); spdlog::debug("Wayfire IPC: received event \"{}\"", ev);
root_event_handler(ev, json); self->root_event_handler(ev, json);
} }
}).detach(); }).detach();
} }

View File

@@ -34,8 +34,12 @@ auto Window::update() -> void {
auto Window::update_icon_label() -> void { auto Window::update_icon_label() -> void {
auto _ = ipc->lock_state(); auto _ = ipc->lock_state();
const auto& output = ipc->get_outputs().at(bar_.output->name); auto out_it = ipc->get_outputs().find(bar_.output->name);
const auto& wset = ipc->get_wsets().at(output.wset_idx); if (out_it == ipc->get_outputs().end()) return;
const auto& output = out_it->second;
auto wset_it = ipc->get_wsets().find(output.wset_idx);
if (wset_it == ipc->get_wsets().end()) return;
const auto& wset = wset_it->second;
const auto& views = ipc->get_views(); const auto& views = ipc->get_views();
auto ctx = bar_.window.get_style_context(); auto ctx = bar_.window.get_style_context();

View File

@@ -70,8 +70,12 @@ auto Workspaces::handleScroll(GdkEventScroll* e) -> bool {
Json::Value data; Json::Value data;
{ {
auto _ = ipc->lock_state(); auto _ = ipc->lock_state();
const auto& output = ipc->get_outputs().at(bar_.output->name); auto out_it = ipc->get_outputs().find(bar_.output->name);
const auto& wset = ipc->get_wsets().at(output.wset_idx); if (out_it == ipc->get_outputs().end()) return true;
const auto& output = out_it->second;
auto wset_it = ipc->get_wsets().find(output.wset_idx);
if (wset_it == ipc->get_wsets().end()) return true;
const auto& wset = wset_it->second;
auto n = wset.ws_w * wset.ws_h; auto n = wset.ws_w * wset.ws_h;
auto i = (wset.ws_idx() + delta + n) % n; auto i = (wset.ws_idx() + delta + n) % n;
data["x"] = Json::Value((uint64_t)i % wset.ws_w); data["x"] = Json::Value((uint64_t)i % wset.ws_w);
@@ -92,8 +96,12 @@ auto Workspaces::update_box() -> void {
auto _ = ipc->lock_state(); auto _ = ipc->lock_state();
const auto& output_name = bar_.output->name; const auto& output_name = bar_.output->name;
const auto& output = ipc->get_outputs().at(output_name); auto out_it = ipc->get_outputs().find(output_name);
const auto& wset = ipc->get_wsets().at(output.wset_idx); if (out_it == ipc->get_outputs().end()) return;
const auto& output = out_it->second;
auto wset_it = ipc->get_wsets().find(output.wset_idx);
if (wset_it == ipc->get_wsets().end()) return;
const auto& wset = wset_it->second;
auto output_focused = ipc->get_focused_output_name() == output_name; auto output_focused = ipc->get_focused_output_name() == output_name;
auto ws_w = wset.ws_w; auto ws_w = wset.ws_w;

View File

@@ -81,7 +81,7 @@ void waybar::modules::Wireplumber::updateNodeName(waybar::modules::Wireplumber*
if (proxy == nullptr) { if (proxy == nullptr) {
auto err = fmt::format("Object '{}' not found\n", id); auto err = fmt::format("Object '{}' not found\n", id);
spdlog::error("[{}]: {}", self->name_, err); spdlog::error("[{}]: {}", self->name_, err);
throw std::runtime_error(err); return;
} }
g_autoptr(WpProperties) properties = g_autoptr(WpProperties) properties =
@@ -153,7 +153,7 @@ void waybar::modules::Wireplumber::updateVolume(waybar::modules::Wireplumber* se
if (variant == nullptr) { if (variant == nullptr) {
auto err = fmt::format("Node {} does not support volume\n", id); auto err = fmt::format("Node {} does not support volume\n", id);
spdlog::error("[{}]: {}", self->name_, err); spdlog::error("[{}]: {}", self->name_, err);
throw std::runtime_error(err); return;
} }
g_variant_lookup(variant, "volume", "d", &self->volume_); g_variant_lookup(variant, "volume", "d", &self->volume_);
@@ -287,14 +287,14 @@ void waybar::modules::Wireplumber::onObjectManagerInstalled(waybar::modules::Wir
if (self->def_nodes_api_ == nullptr) { if (self->def_nodes_api_ == nullptr) {
spdlog::error("[{}]: default nodes api is not loaded.", self->name_); spdlog::error("[{}]: default nodes api is not loaded.", self->name_);
throw std::runtime_error("Default nodes API is not loaded\n"); return;
} }
self->mixer_api_ = wp_plugin_find(self->wp_core_, "mixer-api"); self->mixer_api_ = wp_plugin_find(self->wp_core_, "mixer-api");
if (self->mixer_api_ == nullptr) { if (self->mixer_api_ == nullptr) {
spdlog::error("[{}]: mixer api is not loaded.", self->name_); spdlog::error("[{}]: mixer api is not loaded.", self->name_);
throw std::runtime_error("Mixer api is not loaded\n"); return;
} }
// Get default sink // Get default sink
@@ -336,7 +336,7 @@ void waybar::modules::Wireplumber::onPluginActivated(WpObject* p, GAsyncResult*
if (wp_object_activate_finish(p, res, &error) == 0) { if (wp_object_activate_finish(p, res, &error) == 0) {
spdlog::error("[{}]: error activating plugin: {}", self->name_, error->message); spdlog::error("[{}]: error activating plugin: {}", self->name_, error->message);
throw std::runtime_error(error->message); return;
} }
if (--self->pending_plugins_ == 0) { if (--self->pending_plugins_ == 0) {
@@ -373,7 +373,7 @@ void waybar::modules::Wireplumber::onDefaultNodesApiLoaded(WpObject* p, GAsyncRe
if (success == FALSE) { if (success == FALSE) {
spdlog::error("[{}]: default nodes API load failed", self->name_); spdlog::error("[{}]: default nodes API load failed", self->name_);
throw std::runtime_error(error->message); return;
} }
spdlog::debug("[{}]: loaded default nodes api", self->name_); spdlog::debug("[{}]: loaded default nodes api", self->name_);
g_ptr_array_add(self->apis_, wp_plugin_find(self->wp_core_, "default-nodes-api")); g_ptr_array_add(self->apis_, wp_plugin_find(self->wp_core_, "default-nodes-api"));
@@ -392,7 +392,7 @@ void waybar::modules::Wireplumber::onMixerApiLoaded(WpObject* p, GAsyncResult* r
if (success == FALSE) { if (success == FALSE) {
spdlog::error("[{}]: mixer API load failed", self->name_); spdlog::error("[{}]: mixer API load failed", self->name_);
throw std::runtime_error(error->message); return;
} }
spdlog::debug("[{}]: loaded mixer API", self->name_); spdlog::debug("[{}]: loaded mixer API", self->name_);
@@ -479,12 +479,12 @@ auto waybar::modules::Wireplumber::update() -> void {
} }
if (!tooltipFormat.empty()) { if (!tooltipFormat.empty()) {
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltipFormat), fmt::arg("node_name", node_name_), fmt::arg("volume", vol), fmt::runtime(tooltipFormat), fmt::arg("node_name", node_name_), fmt::arg("volume", vol),
fmt::arg("icon", getIcon(vol)), fmt::arg("format_source", formatted_source), fmt::arg("icon", getIcon(vol)), fmt::arg("format_source", formatted_source),
fmt::arg("source_volume", source_vol), fmt::arg("source_desc", source_name_))); fmt::arg("source_volume", source_vol), fmt::arg("source_desc", source_name_)));
} else { } else {
label_.set_tooltip_text(node_name_); label_.set_tooltip_markup(node_name_);
} }
} }
@@ -524,6 +524,7 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
} }
} }
if (newVol != volume_) { if (newVol != volume_) {
if (mixer_api_ == nullptr) return true;
GVariant* variant = g_variant_new_double(newVol); GVariant* variant = g_variant_new_double(newVol);
gboolean ret; gboolean ret;
g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret); g_signal_emit_by_name(mixer_api_, "set-volume", node_id_, variant, &ret);

View File

@@ -437,7 +437,9 @@ void Task::handle_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context,
void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, void Task::handle_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y,
Gtk::SelectionData selection_data, guint info, guint time) { Gtk::SelectionData selection_data, guint info, guint time) {
spdlog::debug("drag_data_received"); spdlog::debug("drag_data_received");
gpointer handle = *(gpointer*)selection_data.get_data(); auto* raw = selection_data.get_data();
if (!raw || selection_data.get_length() < static_cast<int>(sizeof(gpointer))) return;
gpointer handle = *(gpointer*)raw;
auto dragged_button = (Gtk::Button*)handle; auto dragged_button = (Gtk::Button*)handle;
if (dragged_button == &this->button) return; if (dragged_button == &this->button) return;
@@ -509,7 +511,7 @@ void Task::update() {
if (markup) if (markup)
button.set_tooltip_markup(txt); button.set_tooltip_markup(txt);
else else
button.set_tooltip_text(txt); button.set_tooltip_markup(txt);
} }
} }

View File

@@ -237,9 +237,10 @@ void AudioBackend::sourceInfoCb(pa_context* /*context*/, const pa_source_info* i
*/ */
void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, void* data) { void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, void* data) {
auto* backend = static_cast<AudioBackend*>(data); auto* backend = static_cast<AudioBackend*>(data);
backend->current_sink_name_ = i->default_sink_name; if (i == nullptr) return;
backend->default_sink_name = i->default_sink_name; backend->current_sink_name_ = i->default_sink_name ? i->default_sink_name : "";
backend->default_source_name_ = i->default_source_name; backend->default_sink_name = i->default_sink_name ? i->default_sink_name : "";
backend->default_source_name_ = i->default_source_name ? i->default_source_name : "";
pa_context_get_sink_info_list(context, sinkInfoCb, data); pa_context_get_sink_info_list(context, sinkInfoCb, data);
pa_context_get_source_info_list(context, sourceInfoCb, data); pa_context_get_source_info_list(context, sourceInfoCb, data);
@@ -355,6 +356,7 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
} }
void AudioBackend::toggleSinkMute() { void AudioBackend::toggleSinkMute() {
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
muted_ = !muted_; muted_ = !muted_;
pa_threaded_mainloop_lock(mainloop_); pa_threaded_mainloop_lock(mainloop_);
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr, pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
@@ -363,6 +365,7 @@ void AudioBackend::toggleSinkMute() {
} }
void AudioBackend::toggleSinkMute(bool mute) { void AudioBackend::toggleSinkMute(bool mute) {
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
muted_ = mute; muted_ = mute;
pa_threaded_mainloop_lock(mainloop_); pa_threaded_mainloop_lock(mainloop_);
pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr, pa_context_set_sink_mute_by_index(context_, sink_idx_, static_cast<int>(muted_), nullptr,
@@ -371,7 +374,8 @@ void AudioBackend::toggleSinkMute(bool mute) {
} }
void AudioBackend::toggleSourceMute() { void AudioBackend::toggleSourceMute() {
source_muted_ = !muted_; if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
source_muted_ = !source_muted_;
pa_threaded_mainloop_lock(mainloop_); pa_threaded_mainloop_lock(mainloop_);
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_), pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),
nullptr, nullptr); nullptr, nullptr);
@@ -379,6 +383,7 @@ void AudioBackend::toggleSourceMute() {
} }
void AudioBackend::toggleSourceMute(bool mute) { void AudioBackend::toggleSourceMute(bool mute) {
if (context_ == nullptr || pa_context_get_state(context_) != PA_CONTEXT_READY) return;
source_muted_ = mute; source_muted_ = mute;
pa_threaded_mainloop_lock(mainloop_); pa_threaded_mainloop_lock(mainloop_);
pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_), pa_context_set_source_mute_by_index(context_, source_idx_, static_cast<int>(source_muted_),

View File

@@ -79,18 +79,38 @@ static void upsert_device(std::vector<BacklightDevice>& devices, udev_device* de
}); });
if (found != devices.end()) { if (found != devices.end()) {
if (actual != nullptr) { if (actual != nullptr) {
found->set_actual(std::stoi(actual)); try {
found->set_actual(std::stoi(actual));
} catch (const std::exception&) {
}
} }
if (max != nullptr) { if (max != nullptr) {
found->set_max(std::stoi(max)); try {
found->set_max(std::stoi(max));
} catch (const std::exception&) {
}
} }
if (power != nullptr) { if (power != nullptr) {
found->set_powered(std::stoi(power) == 0); try {
found->set_powered(std::stoi(power) == 0);
} catch (const std::exception&) {
}
} }
} else { } else {
const int actual_int = actual == nullptr ? 0 : std::stoi(actual); int actual_int = 0, max_int = 0;
const int max_int = max == nullptr ? 0 : std::stoi(max); bool power_bool = true;
const bool power_bool = power == nullptr ? true : std::stoi(power) == 0; try {
if (actual != nullptr) actual_int = std::stoi(actual);
} catch (const std::exception&) {
}
try {
if (max != nullptr) max_int = std::stoi(max);
} catch (const std::exception&) {
}
try {
if (power != nullptr) power_bool = std::stoi(power) == 0;
} catch (const std::exception&) {
}
devices.emplace_back(name, actual_int, max_int, power_bool); devices.emplace_back(name, actual_int, max_int, power_bool);
} }
} }
@@ -261,6 +281,11 @@ void BacklightBackend::set_brightness(const std::string& preferred_device, Chang
void BacklightBackend::set_brightness_internal(const std::string& device_name, int brightness, void BacklightBackend::set_brightness_internal(const std::string& device_name, int brightness,
int max_brightness) { int max_brightness) {
if (!login_proxy_) {
spdlog::error("Login proxy not available, cannot set brightness");
return;
}
brightness = std::clamp(brightness, 0, max_brightness); brightness = std::clamp(brightness, 0, max_brightness);
auto call_args = Glib::VariantContainerBase( auto call_args = Glib::VariantContainerBase(
@@ -273,6 +298,7 @@ int BacklightBackend::get_scaled_brightness(const std::string& preferred_device)
GET_BEST_DEVICE(best, (*this), preferred_device); GET_BEST_DEVICE(best, (*this), preferred_device);
if (best != nullptr) { if (best != nullptr) {
if (best->get_max() == 0) return 0;
return static_cast<int>(std::round(best->get_actual() * 100.0F / best->get_max())); return static_cast<int>(std::round(best->get_actual() * 100.0F / best->get_max()));
} }

View File

@@ -115,11 +115,15 @@ std::vector<std::string> waybar::CssReloadHelper::parseImports(const std::string
auto maxIterations = 100U; auto maxIterations = 100U;
do { do {
previousSize = imports.size(); previousSize = imports.size();
std::vector<std::string> to_parse;
for (const auto& [file, parsed] : imports) { for (const auto& [file, parsed] : imports) {
if (!parsed) { if (!parsed) {
parseImports(file, imports); to_parse.push_back(file);
} }
} }
for (const auto& file : to_parse) {
parseImports(file, imports);
}
} while (imports.size() > previousSize && maxIterations-- > 0); } while (imports.size() > previousSize && maxIterations-- > 0);

View File

@@ -5,8 +5,11 @@
std::vector<std::string> IconLoader::search_prefix() { std::vector<std::string> IconLoader::search_prefix() {
std::vector<std::string> prefixes = {""}; std::vector<std::string> prefixes = {""};
std::string home_dir = std::getenv("HOME"); const char* home_env = std::getenv("HOME");
prefixes.push_back(home_dir + "/.local/share/"); std::string home_dir = home_env ? home_env : "";
if (!home_dir.empty()) {
prefixes.push_back(home_dir + "/.local/share/");
}
auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS"); auto xdg_data_dirs = std::getenv("XDG_DATA_DIRS");
if (!xdg_data_dirs) { if (!xdg_data_dirs) {
@@ -139,7 +142,7 @@ bool IconLoader::image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::Icon
} }
if (pixbuf) { if (pixbuf) {
if (pixbuf->get_width() != scaled_icon_size) { if (pixbuf->get_width() != scaled_icon_size && pixbuf->get_height() > 0) {
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
} }

View File

@@ -54,11 +54,17 @@ PipewireBackend::PipewireBackend(PrivateConstructorTag tag)
context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0); context_ = pw_context_new(pw_thread_loop_get_loop(mainloop_), nullptr, 0);
if (context_ == nullptr) { if (context_ == nullptr) {
pw_thread_loop_unlock(mainloop_); pw_thread_loop_unlock(mainloop_);
pw_thread_loop_destroy(mainloop_);
mainloop_ = nullptr;
throw std::runtime_error("pa_context_new() failed."); throw std::runtime_error("pa_context_new() failed.");
} }
core_ = pw_context_connect(context_, nullptr, 0); core_ = pw_context_connect(context_, nullptr, 0);
if (core_ == nullptr) { if (core_ == nullptr) {
pw_thread_loop_unlock(mainloop_); pw_thread_loop_unlock(mainloop_);
pw_context_destroy(context_);
context_ = nullptr;
pw_thread_loop_destroy(mainloop_);
mainloop_ = nullptr;
throw std::runtime_error("pw_context_connect() failed"); throw std::runtime_error("pw_context_connect() failed");
} }
registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0); registry_ = pw_core_get_registry(core_, PW_VERSION_REGISTRY, 0);
@@ -136,7 +142,10 @@ void PipewireBackend::handleRegistryEventGlobal(uint32_t id, uint32_t permission
pw_proxy_add_object_listener(proxy, &pNodeInfo->object_listener, &NODE_EVENTS, pNodeInfo); pw_proxy_add_object_listener(proxy, &pNodeInfo->object_listener, &NODE_EVENTS, pNodeInfo);
privacy_nodes.insert_or_assign(id, pNodeInfo); {
std::lock_guard<std::mutex> lock(mutex_);
privacy_nodes.insert_or_assign(id, pNodeInfo);
}
} }
void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) { void PipewireBackend::handleRegistryEventGlobalRemove(uint32_t id) {

View File

@@ -58,21 +58,27 @@ void waybar::Portal::refreshAppearance() {
// xdg-desktop-portal 1.17 will fix this issue with a new `ReadOne` method, // xdg-desktop-portal 1.17 will fix this issue with a new `ReadOne` method,
// but this version is not yet released. // but this version is not yet released.
// TODO(xdg-desktop-portal v1.17): switch to ReadOne // TODO(xdg-desktop-portal v1.17): switch to ReadOne
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(response); try {
Glib::VariantBase modev; auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(response);
container.get_child(modev, 0); Glib::VariantBase modev;
auto mode = container.get_child(modev, 0);
Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::Variant<Glib::Variant<uint32_t>>>>(modev) auto mode =
.get() Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::Variant<Glib::Variant<uint32_t>>>>(
.get() modev)
.get(); .get()
auto newMode = Appearance(mode); .get()
if (newMode == currentMode) { .get();
auto newMode = Appearance(mode);
if (newMode == currentMode) {
return;
}
spdlog::info("Discovered appearance '{}'", newMode);
currentMode = newMode;
m_signal_appearance_changed.emit(currentMode);
} catch (const std::bad_cast& e) {
spdlog::error("Unexpected appearance variant format: {}", e.what());
return; return;
} }
spdlog::info("Discovered appearance '{}'", newMode);
currentMode = newMode;
m_signal_appearance_changed.emit(currentMode);
} }
waybar::Appearance waybar::Portal::getAppearance() { return currentMode; }; waybar::Appearance waybar::Portal::getAppearance() { return currentMode; };