Merge branch 'Alexays:master' into encode_icon_inside_label

This commit is contained in:
Rio Sanjaya
2026-03-12 13:17:40 +07:00
committed by GitHub
170 changed files with 2894 additions and 1959 deletions
+4 -8
View File
@@ -10,13 +10,9 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# TODO: bump to clang 19 release
# - uses: DoozyX/clang-format-lint-action@v0.18.2
- uses: DoozyX/clang-format-lint-action@558090054b3f39e3d6af24f0cd73b319535da809
- uses: actions/checkout@v6
- uses: RafikFarhad/clang-format-github-action@v6
name: clang-format
with:
source: "."
extensions: "hpp,h,cpp,c"
style: "file:.clang-format"
clangFormatVersion: 19
sources: "src/**/*.hpp,src/**/*.cpp"
style: "file"
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
container:
image: alexays/waybar:debian
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: configure
run: |
meson -Dcpp_std=c++20 build # necessary to generate compile_commands.json
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Login to Docker Hub
uses: docker/login-action@v3
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
# https://github.com/actions/runner/issues/385 - for FreeBSD runner support
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Test in FreeBSD VM
uses: cross-platform-actions/action@v0.28.0
timeout-minutes: 180
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
image: alexays/waybar:${{ matrix.distro }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: configure
run: meson setup -Dman-pages=enabled -Dcpp_std=${{matrix.cpp_std}} build
- name: build
+5 -2
View File
@@ -2,12 +2,15 @@ name: "Nix-Tests"
on:
pull_request:
push:
concurrency:
group: ${{ github.workflow }}-nix-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
nix-flake-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
- uses: actions/checkout@v6
- uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
experimental-features = nix-command flakes
+3 -3
View File
@@ -12,11 +12,11 @@ jobs:
if: github.event_name != 'schedule' || github.repository == 'Alexays/Waybar'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install Nix
uses: cachix/install-nix-action@v27
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v21
uses: DeterminateSystems/update-flake-lock@v28
+1
View File
@@ -51,3 +51,4 @@ result
result-*
.ccls-cache
_codeql_detected_source_root
+4
View File
@@ -151,6 +151,10 @@ Contributions welcome!<br>
Have fun :)<br>
The style guidelines are [Google's](https://google.github.io/styleguide/cppguide.html)
> [!CAUTION]
> Distributions of Waybar are only released on the [official GitHub page](https://github.com/Alexays/Waybar).<br/>
> Waybar does **not** have an official website. Do not trust any sites that claim to be official.
## License
Waybar is licensed under the MIT license. [See LICENSE for more information](https://github.com/Alexays/Waybar/blob/master/LICENSE).
+1
View File
@@ -0,0 +1 @@
.
Generated
+3 -3
View File
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1766902085,
"narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=",
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"type": "github"
},
"original": {
Binary file not shown.
+1 -1
View File
@@ -48,7 +48,7 @@ class AModule : public IModule {
virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
virtual bool handleScroll(GdkEventScroll*);
virtual bool handleRelease(GdkEventButton* const& ev);
GObject *menu_;
GObject* menu_ = nullptr;
private:
bool handleUserEvent(GdkEventButton* const& ev);
+1
View File
@@ -30,6 +30,7 @@ class Group : public AModule {
bool handleMouseEnter(GdkEventCrossing* const& ev) override;
bool handleMouseLeave(GdkEventCrossing* const& ev) override;
bool handleToggle(GdkEventButton* const& ev) override;
bool handleScroll(GdkEventScroll* e) override;
void show_group();
void hide_group();
};
+1 -1
View File
@@ -6,7 +6,7 @@
#if defined(__linux__)
#include <sys/inotify.h>
#endif
#include <sys/poll.h>
#include <poll.h>
#include <algorithm>
#include <fstream>
+1
View File
@@ -35,6 +35,7 @@ class Custom : public ALabel {
std::string id_;
std::string alt_;
std::string tooltip_;
std::string last_tooltip_markup_;
const bool tooltip_format_enabled_;
std::vector<std::string> class_;
int percentage_;
+1 -1
View File
@@ -22,7 +22,7 @@ class Disk : public ALabel {
std::string path_;
std::string unit_;
float calc_specific_divisor(const std::string divisor);
float calc_specific_divisor(const std::string& divisor);
};
} // namespace waybar::modules
+5 -3
View File
@@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <filesystem>
#include <list>
#include <mutex>
@@ -43,10 +44,11 @@ class IPC {
std::thread ipcThread_;
std::mutex callbackMutex_;
std::mutex socketMutex_;
util::JsonParser parser_;
std::list<std::pair<std::string, EventHandler*>> callbacks_;
int socketfd_; // the hyprland socket file descriptor
pid_t socketOwnerPid_;
bool running_ = true; // the ipcThread will stop running when this is false
int socketfd_ = -1; // the hyprland socket file descriptor
pid_t socketOwnerPid_ = -1;
std::atomic<bool> running_ = true; // the ipcThread will stop running when this is false
};
}; // namespace waybar::modules::hyprland
+1
View File
@@ -26,6 +26,7 @@ class Submap : public waybar::ALabel, public EventHandler {
const Bar& bar_;
util::JsonParser parser_;
std::string submap_;
std::string prev_submap_;
bool always_on_ = false;
std::string default_submap_ = "Default";
+11 -11
View File
@@ -20,8 +20,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
private:
struct Workspace {
int id;
int windows;
int id = 0;
int windows = 0;
std::string last_window;
std::string last_window_title;
@@ -29,14 +29,14 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
};
struct WindowData {
bool floating;
bool floating = false;
int monitor = -1;
std::string class_name;
std::string initial_class_name;
std::string title;
std::string initial_title;
bool fullscreen;
bool grouped;
bool fullscreen = false;
bool grouped = false;
static auto parse(const Json::Value&) -> WindowData;
};
@@ -47,7 +47,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
void queryActiveWorkspace();
void setClass(const std::string&, bool enable);
bool separateOutputs_;
bool separateOutputs_ = false;
std::mutex mutex_;
const Bar& bar_;
util::JsonParser parser_;
@@ -55,11 +55,11 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
Workspace workspace_;
std::string soloClass_;
std::string lastSoloClass_;
bool solo_;
bool allFloating_;
bool swallowing_;
bool fullscreen_;
bool focused_;
bool solo_ = false;
bool allFloating_ = false;
bool swallowing_ = false;
bool fullscreen_ = false;
bool focused_ = false;
IPC& m_ipc;
};
@@ -40,10 +40,11 @@ struct WindowRepr {
class WindowCreationPayload {
public:
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
WindowRepr window_repr);
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
std::string window_class, std::string window_title, bool is_active);
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
const std::string& window_class, const std::string& window_title,
bool is_active);
WindowCreationPayload(Json::Value const& client_data);
int incrementTimeSpentUncreated();
+7 -1
View File
@@ -4,6 +4,7 @@
#include <gtkmm/enums.h>
#include <gtkmm/label.h>
#include <json/value.h>
#include <sigc++/connection.h>
#include <cstdint>
#include <map>
@@ -43,6 +44,7 @@ class Workspaces : public AModule, public EventHandler {
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
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 formatBefore() const -> std::string { return m_formatBefore; }
@@ -58,7 +60,7 @@ class Workspaces : public AModule, public EventHandler {
enum class ActiveWindowPosition { NONE, FIRST, LAST };
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
std::string getRewrite(std::string window_class, std::string window_title);
std::string getRewrite(const std::string& window_class, const std::string& window_title);
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
bool isWorkspaceIgnored(std::string const& workspace_name);
@@ -122,6 +124,8 @@ class Workspaces : public AModule, public EventHandler {
static std::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
static std::tuple<std::string, std::string, std::string> splitTriplePayload(
std::string const& payload);
// scroll events
bool handleScroll(GdkEventScroll* e) override;
// Update methods
void doUpdate();
@@ -145,6 +149,7 @@ class Workspaces : public AModule, public EventHandler {
bool m_specialVisibleOnly = false;
bool m_persistentOnly = false;
bool m_moveToMonitor = false;
bool m_barScroll = false;
Json::Value m_persistentWorkspaceConfig;
// Map for windows stored in workspaces not present in the current bar.
@@ -204,6 +209,7 @@ class Workspaces : public AModule, public EventHandler {
std::mutex m_mutex;
const Bar& m_bar;
Gtk::Box m_box;
sigc::connection m_scrollEventConnection_;
IPC& m_ipc;
};
+2
View File
@@ -3,6 +3,7 @@
#include <fmt/chrono.h>
#include <gtkmm/label.h>
#include <mutex>
#include <set>
#include <unordered_map>
@@ -41,6 +42,7 @@ class KeyboardState : public AModule {
struct libinput* libinput_;
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
std::mutex devices_mutex_; // protects libinput_devices_
std::set<int> binding_keys;
util::SleeperThread libinput_thread_, hotplug_thread_;
+1 -1
View File
@@ -44,7 +44,7 @@ class MPD : public ALabel {
std::string getFilename() const;
void setLabel();
std::string getStateIcon() const;
std::string getOptionIcon(std::string optionName, bool activated) const;
std::string getOptionIcon(const std::string& optionName, bool activated) const;
// GUI-side methods
bool handlePlayPause(GdkEventButton* const&);
+1
View File
@@ -78,6 +78,7 @@ class Mpris : public ALabel {
PlayerctlPlayerManager* manager;
PlayerctlPlayer* player;
PlayerctlPlayer* last_active_player_ = nullptr;
std::string lastStatus;
std::string lastPlayer;
+1 -1
View File
@@ -17,7 +17,7 @@ class EventHandler {
class IPC {
public:
IPC() { startIPC(); }
IPC();
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler);
+7 -2
View File
@@ -16,7 +16,7 @@ class Host {
public:
Host(const std::size_t id, const Json::Value&, const Bar&,
const std::function<void(std::unique_ptr<Item>&)>&,
const std::function<void(std::unique_ptr<Item>&)>&);
const std::function<void(std::unique_ptr<Item>&)>&, const std::function<void()>&);
~Host();
private:
@@ -28,9 +28,13 @@ class Host {
static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer);
static void itemUnregistered(SnWatcher*, const gchar*, gpointer);
void itemReady(Item&);
void itemInvalidated(Item&);
void removeItem(std::vector<std::unique_ptr<Item>>::iterator);
void clearItems();
std::tuple<std::string, std::string> getBusNameAndObjectPath(const std::string);
void addRegisteredItem(std::string service);
void addRegisteredItem(const std::string& service);
std::vector<std::unique_ptr<Item>> items_;
const std::string bus_name_;
@@ -43,6 +47,7 @@ class Host {
const Bar& bar_;
const std::function<void(std::unique_ptr<Item>&)> on_add_;
const std::function<void(std::unique_ptr<Item>&)> on_remove_;
const std::function<void()> on_update_;
};
} // namespace waybar::modules::SNI
+21 -2
View File
@@ -11,6 +11,7 @@
#include <libdbusmenu-gtk/dbusmenu-gtk.h>
#include <sigc++/trackable.h>
#include <functional>
#include <set>
#include <string_view>
@@ -25,9 +26,13 @@ struct ToolTip {
class Item : public sigc::trackable {
public:
Item(const std::string&, const std::string&, const Json::Value&, const Bar&);
Item(const std::string&, const std::string&, const Json::Value&, const Bar&,
const std::function<void(Item&)>&, const std::function<void(Item&)>&,
const std::function<void()>&);
~Item();
bool isReady() const;
std::string bus_name;
std::string object_path;
@@ -43,7 +48,9 @@ class Item : public sigc::trackable {
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
Glib::RefPtr<Gtk::IconTheme> icon_theme;
std::string overlay_icon_name;
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
std::string attention_icon_name;
Glib::RefPtr<Gdk::Pixbuf> attention_icon_pixmap;
std::string attention_movie_name;
std::string icon_theme_path;
std::string menu;
@@ -62,6 +69,8 @@ class Item : public sigc::trackable {
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
void setStatus(const Glib::ustring& value);
void setReady();
void invalidate();
void setCustomIcon(const std::string& id);
void getUpdatedProperties();
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
@@ -69,8 +78,13 @@ class Item : public sigc::trackable {
const Glib::VariantContainerBase& arguments);
void updateImage();
Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
static Glib::RefPtr<Gdk::Pixbuf> extractPixBuf(GVariant* variant);
Glib::RefPtr<Gdk::Pixbuf> getIconPixbuf();
Glib::RefPtr<Gdk::Pixbuf> getAttentionIconPixbuf();
Glib::RefPtr<Gdk::Pixbuf> getOverlayIconPixbuf();
Glib::RefPtr<Gdk::Pixbuf> loadIconFromNameOrFile(const std::string& name, bool log_failure);
static Glib::RefPtr<Gdk::Pixbuf> overlayPixbufs(const Glib::RefPtr<Gdk::Pixbuf>&,
const Glib::RefPtr<Gdk::Pixbuf>&);
Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
double getScaledIconSize();
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
@@ -86,8 +100,13 @@ class Item : public sigc::trackable {
gdouble distance_scrolled_y_ = 0;
// visibility of items with Status == Passive
bool show_passive_ = false;
bool ready_ = false;
Glib::ustring status_ = "active";
const Bar& bar_;
const std::function<void(Item&)> on_ready_;
const std::function<void(Item&)> on_invalidate_;
const std::function<void()> on_updated_;
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
Glib::RefPtr<Gio::Cancellable> cancellable_;
+1
View File
@@ -19,6 +19,7 @@ class Tray : public AModule {
private:
void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item);
void queueUpdate();
static inline std::size_t nb_hosts_ = 0;
bool show_passive_ = false;
+1 -1
View File
@@ -37,7 +37,7 @@ class BarIpcClient {
void onModeUpdate(bool visible_by_modifier);
void onUrgencyUpdate(bool visible_by_urgency);
void update();
bool isModuleEnabled(std::string name);
bool isModuleEnabled(const std::string& name);
Bar& bar_;
util::JsonParser parser_;
+6 -4
View File
@@ -12,6 +12,8 @@
#include <string>
#include "ipc.hpp"
#include "util/SafeSignal.hpp"
#include "util/scoped_fd.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::modules::sway {
@@ -27,8 +29,8 @@ class Ipc {
std::string payload;
};
sigc::signal<void, const struct ipc_response &> signal_event;
sigc::signal<void, const struct ipc_response &> signal_cmd;
::waybar::SafeSignal<const struct ipc_response&> signal_event;
::waybar::SafeSignal<const struct ipc_response&> signal_cmd;
void sendCmd(uint32_t type, const std::string& payload = "");
void subscribe(const std::string& payload);
@@ -44,8 +46,8 @@ class Ipc {
struct ipc_response send(int fd, uint32_t type, const std::string& payload = "");
struct ipc_response recv(int fd);
int fd_;
int fd_event_;
util::ScopedFd fd_;
util::ScopedFd fd_event_;
std::mutex mutex_;
util::SleeperThread thread_;
};
+1 -1
View File
@@ -47,7 +47,7 @@ class Language : public ALabel, public sigc::trackable {
void onEvent(const struct Ipc::ipc_response&);
void onCmd(const struct Ipc::ipc_response&);
auto set_current_layout(std::string current_layout) -> void;
auto set_current_layout(const std::string& current_layout) -> void;
auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;
const static std::string XKB_LAYOUT_NAMES_KEY;
+2 -2
View File
@@ -27,7 +27,7 @@ class Workspaces : public AModule, public sigc::trackable {
static constexpr std::string_view persistent_workspace_switch_cmd_ =
R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")";
static int convertWorkspaceNameToNum(std::string name);
static int convertWorkspaceNameToNum(const std::string& name);
static int windowRewritePriorityFunction(std::string const& window_rule);
void onCmd(const struct Ipc::ipc_response&);
@@ -40,7 +40,7 @@ class Workspaces : public AModule, public sigc::trackable {
std::string getIcon(const std::string&, const Json::Value&);
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
uint16_t getWorkspaceIndex(const std::string& name) const;
static std::string trimWorkspaceName(std::string);
static std::string trimWorkspaceName(const std::string&);
bool handleScroll(GdkEventScroll* /*unused*/) override;
const Bar& bar_;
+5 -19
View File
@@ -12,6 +12,8 @@
#include <unordered_map>
#include <utility>
#include "util/scoped_fd.hpp"
namespace waybar::modules::wayfire {
using EventHandler = std::function<void(const std::string& event)>;
@@ -71,25 +73,9 @@ struct State {
auto update_view(const Json::Value& view) -> void;
};
struct Sock {
int fd;
using Sock = util::ScopedFd;
Sock(int fd) : fd{fd} {}
~Sock() { close(fd); }
Sock(const Sock&) = delete;
auto operator=(const Sock&) = delete;
Sock(Sock&& rhs) noexcept {
fd = rhs.fd;
rhs.fd = -1;
}
auto& operator=(Sock&& rhs) noexcept {
fd = rhs.fd;
rhs.fd = -1;
return *this;
}
};
class IPC {
class IPC : public std::enable_shared_from_this<IPC> {
static std::weak_ptr<IPC> instance;
Json::CharReaderBuilder reader_builder;
Json::StreamWriterBuilder writer_builder;
@@ -98,7 +84,7 @@ class IPC {
State state;
std::mutex state_mutex;
IPC() { start(); }
IPC() = default;
static auto connect() -> Sock;
auto receive(Sock& sock) -> Json::Value;
+2 -2
View File
@@ -33,8 +33,8 @@ class Taskbar;
class Task {
public:
Task(const waybar::Bar &, const Json::Value &, Taskbar *,
struct zwlr_foreign_toplevel_handle_v1 *, struct wl_seat *);
Task(const waybar::Bar&, const Json::Value&, Taskbar*, struct zwlr_foreign_toplevel_handle_v1*,
struct wl_seat*);
~Task();
public:
+20
View File
@@ -3,6 +3,7 @@
#include <glibmm/dispatcher.h>
#include <sigc++/signal.h>
#include <cstddef>
#include <functional>
#include <mutex>
#include <queue>
@@ -27,6 +28,12 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
public:
SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); }
void set_max_queued_events(std::size_t max_queued_events) {
std::unique_lock lock(mutex_);
max_queued_events_ = max_queued_events;
trim_queue_locked();
}
template <typename... EmitArgs>
void emit(EmitArgs&&... args) {
if (main_tid_ == std::this_thread::get_id()) {
@@ -41,6 +48,9 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
} else {
{
std::unique_lock lock(mutex_);
if (max_queued_events_ != 0 && queue_.size() >= max_queued_events_) {
queue_.pop();
}
queue_.emplace(std::forward<EmitArgs>(args)...);
}
dp_.emit();
@@ -60,6 +70,15 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
using signal_t::emit_reverse;
using signal_t::make_slot;
void trim_queue_locked() {
if (max_queued_events_ == 0) {
return;
}
while (queue_.size() > max_queued_events_) {
queue_.pop();
}
}
void handle_event() {
for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) {
auto args = queue_.front();
@@ -72,6 +91,7 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
Glib::Dispatcher dp_;
std::mutex mutex_;
std::queue<arg_tuple_t> queue_;
std::size_t max_queued_events_ = 4096;
const std::thread::id main_tid_ = std::this_thread::get_id();
// cache functor for signal emission to avoid recreating it on each event
const slot_t cached_fn_ = make_slot();
+16 -4
View File
@@ -20,6 +20,8 @@ extern std::list<pid_t> reap;
namespace waybar::util::command {
constexpr int kExecFailureExitCode = 127;
struct res {
int exit_code;
std::string out;
@@ -59,6 +61,7 @@ inline int close(FILE* fp, pid_t pid) {
spdlog::debug("Cmd continued");
} else if (ret == -1) {
spdlog::debug("waitpid failed: {}", strerror(errno));
break;
} else {
break;
}
@@ -109,11 +112,13 @@ inline FILE* open(const std::string& cmd, int& pid, const std::string& output_na
::close(fd[0]);
dup2(fd[1], 1);
setpgid(child_pid, child_pid);
if (output_name != "") {
if (!output_name.empty()) {
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
}
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
const int saved_errno = errno;
spdlog::error("execlp(/bin/sh) failed in open: {}", strerror(saved_errno));
_exit(kExecFailureExitCode);
} else {
::close(fd[1]);
}
@@ -138,7 +143,7 @@ inline struct res execNoRead(const std::string& cmd) {
return {WEXITSTATUS(stat), ""};
}
inline int32_t forkExec(const std::string& cmd) {
inline int32_t forkExec(const std::string& cmd, const std::string& output_name) {
if (cmd == "") return -1;
pid_t pid = fork();
@@ -157,8 +162,13 @@ inline int32_t forkExec(const std::string& cmd) {
err = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
if (err != 0) spdlog::error("pthread_sigmask in forkExec failed: {}", strerror(err));
setpgid(pid, pid);
if (!output_name.empty()) {
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
}
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0);
const int saved_errno = errno;
spdlog::error("execl(/bin/sh) failed in forkExec: {}", strerror(saved_errno));
_exit(kExecFailureExitCode);
} else {
reap_mtx.lock();
reap.push_back(pid);
@@ -169,4 +179,6 @@ inline int32_t forkExec(const std::string& cmd) {
return pid;
}
inline int32_t forkExec(const std::string& cmd) { return forkExec(cmd, ""); }
} // namespace waybar::util::command
+4 -3
View File
@@ -30,15 +30,16 @@ class JsonParser {
std::istringstream jsonStream(modifiedJsonStr);
std::string errs;
if (!Json::parseFromStream(m_readerBuilder, jsonStream, &root, &errs)) {
// Use local CharReaderBuilder for thread safety - the IPC singleton's
// parser can be called concurrently from multiple module threads
Json::CharReaderBuilder readerBuilder;
if (!Json::parseFromStream(readerBuilder, jsonStream, &root, &errs)) {
throw std::runtime_error("Error parsing JSON: " + errs);
}
return root;
}
private:
Json::CharReaderBuilder m_readerBuilder;
static std::string replaceHexadecimalEscape(const std::string& str) {
static std::regex re("\\\\x");
return std::regex_replace(str, re, "\\u00");
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include <unistd.h>
namespace waybar::util {
class ScopedFd {
public:
explicit ScopedFd(int fd = -1) : fd_(fd) {}
~ScopedFd() {
if (fd_ != -1) {
close(fd_);
}
}
// ScopedFd is non-copyable
ScopedFd(const ScopedFd&) = delete;
ScopedFd& operator=(const ScopedFd&) = delete;
// ScopedFd is moveable
ScopedFd(ScopedFd&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
ScopedFd& operator=(ScopedFd&& other) noexcept {
if (this != &other) {
if (fd_ != -1) {
close(fd_);
}
fd_ = other.fd_;
other.fd_ = -1;
}
return *this;
}
int get() const { return fd_; }
operator int() const { return fd_; }
void reset(int fd = -1) {
if (fd_ != -1) {
close(fd_);
}
fd_ = fd;
}
int release() {
int fd = fd_;
fd_ = -1;
return fd;
}
private:
int fd_;
};
} // namespace waybar::util
+30 -14
View File
@@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <ctime>
@@ -31,8 +32,8 @@ class SleeperThread {
SleeperThread(std::function<void()> func)
: thread_{[this, func] {
while (do_run_) {
signal_ = false;
while (do_run_.load(std::memory_order_relaxed)) {
signal_.store(false, std::memory_order_relaxed);
func();
}
}} {
@@ -42,9 +43,18 @@ class SleeperThread {
}
SleeperThread& operator=(std::function<void()> func) {
if (thread_.joinable()) {
stop();
thread_.join();
}
{
std::lock_guard<std::mutex> lck(mutex_);
do_run_.store(true, std::memory_order_relaxed);
signal_.store(false, std::memory_order_relaxed);
}
thread_ = std::thread([this, func] {
while (do_run_) {
signal_ = false;
while (do_run_.load(std::memory_order_relaxed)) {
signal_.store(false, std::memory_order_relaxed);
func();
}
});
@@ -56,12 +66,14 @@ class SleeperThread {
return *this;
}
bool isRunning() const { return do_run_; }
bool isRunning() const { return do_run_.load(std::memory_order_relaxed); }
auto sleep() {
std::unique_lock lk(mutex_);
CancellationGuard cancel_lock;
return condvar_.wait(lk, [this] { return signal_ || !do_run_; });
return condvar_.wait(lk, [this] {
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
});
}
auto sleep_for(std::chrono::system_clock::duration dur) {
@@ -73,7 +85,9 @@ class SleeperThread {
if (now < max_time_point - dur) {
wait_end = now + dur;
}
return condvar_.wait_until(lk, wait_end, [this] { return signal_ || !do_run_; });
return condvar_.wait_until(lk, wait_end, [this] {
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
});
}
auto sleep_until(
@@ -81,22 +95,24 @@ class SleeperThread {
time_point) {
std::unique_lock lk(mutex_);
CancellationGuard cancel_lock;
return condvar_.wait_until(lk, time_point, [this] { return signal_ || !do_run_; });
return condvar_.wait_until(lk, time_point, [this] {
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
});
}
void wake_up() {
{
std::lock_guard<std::mutex> lck(mutex_);
signal_ = true;
signal_.store(true, std::memory_order_relaxed);
}
condvar_.notify_all();
}
auto stop() {
void stop() {
{
std::lock_guard<std::mutex> lck(mutex_);
signal_ = true;
do_run_ = false;
signal_.store(true, std::memory_order_relaxed);
do_run_.store(false, std::memory_order_relaxed);
}
condvar_.notify_all();
auto handle = thread_.native_handle();
@@ -118,8 +134,8 @@ class SleeperThread {
std::thread thread_;
std::condition_variable condvar_;
std::mutex mutex_;
bool do_run_ = true;
bool signal_ = false;
std::atomic<bool> do_run_ = true;
std::atomic<bool> signal_ = false;
sigc::connection connection_;
};
+4 -1
View File
@@ -182,6 +182,7 @@ Every entry in the *events* object consists of a *<event-name>* (typeof: *string
- *on-<status>-<state>*
- *on-<status>-<capacity>*
- *on-<status>*
Where:
@@ -203,7 +204,9 @@ Where:
"events": {
"on-discharging-warning": "notify-send -u normal '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-icons": ["", "", "", "", ""],
+5
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.
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*: ++
typeof: array ++
default: [] ++
+2 -1
View File
@@ -26,7 +26,8 @@ The *image* module displays an image from a path.
*interval*: ++
typeof: integer or float ++
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. ++
If no *interval* is defined, the image will only be rendered once.
+1 -1
View File
@@ -36,7 +36,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
*expand-right* ++
typeof: bool ++
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* ++
typeof: string ++
+1 -1
View File
@@ -1,6 +1,6 @@
project(
'waybar', 'cpp', 'c',
version: '0.14.0',
version: '0.15.0',
license: 'MIT',
meson_version: '>= 0.59.0',
default_options : [
+8 -3
View File
@@ -151,19 +151,24 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
}
void AAppIconLabel::updateAppIcon() {
if (update_app_icon_) {
if (update_app_icon_ || (!iconEnabled() && image_.get_visible())) {
update_app_icon_ = false;
if (app_icon_name_.empty()) {
image_.set_visible(false);
} 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();
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(),
image_.get_window());
image_.set(surface);
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 {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true);
+17 -5
View File
@@ -26,7 +26,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
: std::chrono::milliseconds(
(config_["interval"].isNumeric()
? std::max(1L, // Minimum 1ms due to millisecond precision
static_cast<long>(config_["interval"].asDouble()) * 1000)
static_cast<long>(config_["interval"].asDouble() * 1000))
: 1000 * (long)interval))),
default_format_(format_) {
label_.set_name(name);
@@ -91,13 +91,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
// Make the GtkBuilder and check for errors in his parsing
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);
}
menu_ = gtk_builder_get_object(builder, "menu");
if (menu_ == nullptr) {
g_object_unref(builder);
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
}
// Keep the menu alive after dropping the transient GtkBuilder.
g_object_ref(menu_);
submenus_ = std::map<std::string, GtkMenuItem*>();
menuActionsMap_ = std::map<std::string, std::string>();
@@ -105,11 +109,17 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
for (Json::Value::const_iterator it = config_["menu-actions"].begin();
it != config_["menu-actions"].end(); ++it) {
std::string key = it.key().asString();
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str()));
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();
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
(gpointer)menuActionsMap_[key].c_str());
}
g_object_unref(builder);
} catch (std::runtime_error& e) {
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
}
@@ -141,7 +151,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
if (format_icons.isArray()) {
auto size = format_icons.size();
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];
}
}
@@ -167,7 +178,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
if (format_icons.isArray()) {
auto size = format_icons.size();
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];
}
}
@@ -190,7 +202,7 @@ bool waybar::ALabel::handleToggle(GdkEventButton* const& e) {
}
void ALabel::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) {
waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
waybar::util::command::forkExec((char*)data, "GtkMenu");
}
std::string ALabel::getState(uint8_t value, bool lesser) {
+6 -2
View File
@@ -88,6 +88,10 @@ AModule::~AModule() {
killpg(pid, SIGTERM);
}
}
if (menu_ != nullptr) {
g_object_unref(menu_);
menu_ = nullptr;
}
}
auto AModule::update() -> void {
@@ -166,9 +170,9 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
}
// 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
if (rec->second == config_["menu"].asString()) {
if (rec->second == config_["menu"].asString() && menu_ != nullptr) {
// Popup the menu
gtk_widget_show_all(GTK_WIDGET(menu_));
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
+2 -1
View File
@@ -229,7 +229,8 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
gtk_layer_init_for_window(gtk_window);
gtk_layer_set_keyboard_mode(gtk_window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
gtk_layer_set_monitor(gtk_window, output->monitor->gobj());
gtk_layer_set_namespace(gtk_window, "waybar");
gtk_layer_set_namespace(gtk_window,
config["name"].isString() ? config["name"].asCString() : "waybar");
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_LEFT, margins_.left);
gtk_layer_set_margin(gtk_window, GTK_LAYER_SHELL_EDGE_RIGHT, margins_.right);
+5
View File
@@ -124,6 +124,11 @@ auto Group::update() -> void {
// noop
}
bool Group::handleScroll(GdkEventScroll* e) {
// no scroll.
return true;
}
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
void Group::addWidget(Gtk::Widget& widget) {
+9 -5
View File
@@ -33,7 +33,10 @@ static void writeSignalToPipe(int signum) {
// to `signal_handler`.
static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
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];
signal_pipe_write_fd = fd[1];
@@ -137,15 +140,16 @@ static void handleSignalMainThread(int signum, bool& reload) {
break;
case SIGCHLD:
spdlog::debug("Received SIGCHLD in signalThread");
if (!reap.empty()) {
reap_mtx.lock();
for (auto it = reap.begin(); it != reap.end(); ++it) {
{
std::lock_guard<std::mutex> lock(reap_mtx);
for (auto it = reap.begin(); it != reap.end();) {
if (waitpid(*it, nullptr, WNOHANG) == *it) {
spdlog::debug("Reaped child with PID: {}", *it);
it = reap.erase(it);
} else {
++it;
}
}
reap_mtx.unlock();
}
break;
default:
+2 -2
View File
@@ -58,11 +58,11 @@ auto waybar::modules::Backlight::update() -> void {
tooltip_format = config_["tooltip-format"].asString();
}
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("icon", getIcon(percent))));
} else {
label_.set_tooltip_text(desc);
label_.set_tooltip_markup(desc);
}
}
} else {
+15 -10
View File
@@ -139,7 +139,9 @@ void waybar::modules::Battery::refreshBatteries() {
auto event_path = (node.path() / "uevent");
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
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;
}
@@ -686,7 +688,7 @@ auto waybar::modules::Battery::update() -> void {
status = getAdapterStatus(capacity);
}
auto status_pretty = status;
puts(status.c_str());
// Transform to lowercase and replace space with dash
std::ranges::transform(status.begin(), status.end(), status.begin(),
[](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()) {
return;
}
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging",
state.empty() ? std::to_string(capacity) : state);
auto exec = [](Json::Value const& event) {
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) {
spdlog::debug("battery: triggering event {}", event_name);
if (events[event_name].isString()) {
std::string exec = events[event_name].asString();
// Execute the command if it is not empty
if (!exec.empty()) {
util::command::exec(exec, "");
}
exec(events[event_name]);
if (!last_event_.empty() && last_event_[3] != event_name[3]) {
exec(events[status_name]);
}
last_event_ = event_name;
}
+12 -2
View File
@@ -111,13 +111,16 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
findConnectedDevices(cur_controller_->path, connected_devices_);
}
if (manager_) {
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this);
g_signal_connect(manager_.get(), "object-removed", G_CALLBACK(onObjectRemoved), this);
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
}
#ifdef WANT_RFKILL
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
@@ -264,7 +267,7 @@ auto waybar::modules::Bluetooth::update() -> void {
device_enumerate_.erase(0, 1);
}
}
label_.set_tooltip_text(fmt::format(
label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), fmt::arg("status", state_),
fmt::arg("num_connections", connected_devices_.size()),
fmt::arg("controller_address", cur_controller_ ? cur_controller_->address : "null"),
@@ -446,6 +449,10 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
std::optional<ControllerInfo> controller_info;
if (!manager_) {
return controller_info;
}
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) {
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,
std::vector<DeviceInfo>& connected_devices)
-> void {
if (!manager_) {
return;
}
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data);
+5 -3
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 {
Glib::signal_idle().connect_once([this, input]() {
if (silence_) {
silence_ = false;
label_.get_style_context()->remove_class("silent");
if (!label_.get_style_context()->has_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();
ALabel::update();
});
silence_ = false;
}
auto waybar::modules::cava::Cava::onSilence() -> void {
@@ -47,9 +47,11 @@ auto waybar::modules::cava::Cava::onSilence() -> void {
if (label_.get_style_context()->has_class("updated"))
label_.get_style_context()->remove_class("updated");
if (hide_on_silence_)
if (hide_on_silence_) {
// Clear the label markup before hiding to prevent GTK from rendering a NULL Pango layout
label_.set_markup("");
label_.hide();
else if (config_["format_silent"].isString())
} else if (config_["format_silent"].isString())
label_.set_markup(format_silent_);
silence_ = true;
label_.get_style_context()->add_class("silent");
+4 -4
View File
@@ -183,10 +183,10 @@ void waybar::modules::cava::CavaBackend::loadConfig() {
prm_.inAtty = 0;
auto const output{prm_.output};
// prm_.output = ::cava::output_method::OUTPUT_RAW;
if (config_["data_format"].isString()) {
if (prm_.data_format) free(prm_.data_format);
prm_.data_format = strdup(config_["data_format"].asString().c_str());
}
// Default to ascii for format-icons output; allow user override
prm_.data_format = strdup(
config_["data_format"].isString() ? config_["data_format"].asString().c_str() : "ascii");
if (config_["raw_target"].isString()) {
if (prm_.raw_target) free(prm_.raw_target);
prm_.raw_target = strdup(config_["raw_target"].asString().c_str());
@@ -218,7 +218,7 @@ void waybar::modules::cava::CavaBackend::loadConfig() {
prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str());
if (config_["source"].isString()) {
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_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();
+2 -1
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()};
}
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()};
}
// Optional functions
@@ -71,6 +71,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
// Prepare config_entries array
std::vector<ffi::wbcffi_config_entry> config_entries;
config_entries.reserve(keys.size());
for (size_t i = 0; i < keys.size(); i++) {
config_entries.push_back({keys[i].c_str(), config_entries_stringstor[i].c_str()});
}
+16 -7
View File
@@ -26,9 +26,7 @@ auto waybar::modules::Cpu::update() -> void {
auto [load1, load5, load15] = Load::getLoad();
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip);
}
auto format = format_;
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
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("min_frequency", min_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) {
auto core_i = i - 1;
auto core_format = fmt::format("usage{}", core_i);
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i]));
auto icon_format = fmt::format("icon{}", core_i);
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
arg_names.push_back(fmt::format("usage{}", core_i));
store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
arg_names.push_back(fmt::format("icon{}", core_i));
store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
}
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
+13 -6
View File
@@ -20,12 +20,7 @@ waybar::modules::CpuFrequency::CpuFrequency(const std::string& id, const Json::V
auto waybar::modules::CpuFrequency::update() -> void {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
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 state = getState(avg_frequency);
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("avg_frequency", avg_frequency));
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
+1 -1
View File
@@ -26,7 +26,7 @@ std::vector<float> waybar::modules::CpuFrequency::parseCpuFrequencies() {
if (std::filesystem::exists(cpufreq_dir)) {
std::vector<std::string> frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"};
for (auto& p : std::filesystem::directory_iterator(cpufreq_dir)) {
for (auto freq_file : frequency_files) {
for (const auto& freq_file : frequency_files) {
std::string freq_file_path = p.path().string() + freq_file;
if (std::filesystem::exists(freq_file_path)) {
std::string freq_value;
+20 -9
View File
@@ -20,9 +20,7 @@ waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& co
auto waybar::modules::CpuUsage::update() -> void {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip);
}
auto format = format_;
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
auto state = getState(total_usage);
@@ -38,14 +36,25 @@ auto waybar::modules::CpuUsage::update() -> void {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("usage", total_usage));
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) {
auto core_i = i - 1;
auto core_format = fmt::format("usage{}", core_i);
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i]));
auto icon_format = fmt::format("icon{}", core_i);
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons)));
arg_names.push_back(fmt::format("usage{}", core_i));
store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
arg_names.push_back(fmt::format("icon{}", core_i));
store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
}
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
@@ -71,7 +80,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
auto [prev_idle, prev_total] = prev_times[0];
const float delta_idle = curr_idle - prev_idle;
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);
usage.push_back(tmp);
} 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_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) {
tooltip = fmt::format("Total: {}%", tmp);
} else {
+14 -10
View File
@@ -2,6 +2,8 @@
#include <spdlog/spdlog.h>
#include <utility>
#include "util/scope_guard.hpp"
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
@@ -136,7 +138,7 @@ void waybar::modules::Custom::waitingWorker() {
}
void waybar::modules::Custom::refresh(int sig) {
if (sig == SIGRTMIN + config_["signal"].asInt()) {
if (config_["signal"].isInt() && sig == SIGRTMIN + config_["signal"].asInt()) {
thread_.wake_up();
}
}
@@ -180,20 +182,22 @@ auto waybar::modules::Custom::update() -> void {
} else {
label_.set_markup(str);
if (tooltipEnabled()) {
std::string tooltip_markup;
if (tooltip_format_enabled_) {
auto tooltip = config_["tooltip-format"].asString();
tooltip = fmt::format(
fmt::runtime(tooltip), fmt::arg("text", text_), fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_));
label_.set_tooltip_markup(tooltip);
tooltip_markup = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_),
fmt::arg("tooltip", tooltip_), fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)),
fmt::arg("percentage", percentage_));
} else if (text_ == tooltip_) {
if (label_.get_tooltip_markup() != str) {
label_.set_tooltip_markup(str);
}
tooltip_markup = str;
} else {
if (label_.get_tooltip_markup() != tooltip_) {
label_.set_tooltip_markup(tooltip_);
tooltip_markup = tooltip_;
}
if (last_tooltip_markup_ != tooltip_markup) {
label_.set_tooltip_markup(tooltip_markup);
last_tooltip_markup_ = std::move(tooltip_markup);
}
}
auto style = label_.get_style_context();
+3 -3
View File
@@ -41,7 +41,7 @@ auto waybar::modules::Disk::update() -> void {
fs_used - File system used space
*/
if (err != 0) {
if (err != 0 || stats.f_blocks == 0) {
event_box_.hide();
return;
}
@@ -81,7 +81,7 @@ auto waybar::modules::Disk::update() -> void {
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), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used),
fmt::arg("percentage_used", percentage_used), fmt::arg("total", total),
@@ -92,7 +92,7 @@ auto waybar::modules::Disk::update() -> void {
ALabel::update();
}
float waybar::modules::Disk::calc_specific_divisor(std::string divisor) {
float waybar::modules::Disk::calc_specific_divisor(const std::string& divisor) {
if (divisor == "kB") {
return 1000.0;
} else if (divisor == "kiB") {
+3 -2
View File
@@ -76,8 +76,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
}
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Tags *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Tags*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
@@ -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) {
if (tag >= buttons_.size()) return;
// First clear all occupied state
auto& button = buttons_[tag];
if (clients) {
+1 -1
View File
@@ -116,7 +116,7 @@ void Window::handle_frame() {
updateAppIconName(appid_, "");
updateAppIcon();
if (tooltipEnabled()) {
label_.set_tooltip_text(title_);
label_.set_tooltip_markup(title_);
}
}
+3 -1
View File
@@ -143,7 +143,9 @@ void WorkspaceManager::handle_finished() {
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() {
spdlog::debug("[ext/workspaces]: Updating state");
+4 -4
View File
@@ -128,9 +128,9 @@ void Gamemode::getData() {
Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters);
if (data && data.is_of_type(Glib::VariantType("(v)"))) {
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)) {
g_variant_get(variant.gobj_copy(), "i", &gameCount);
g_variant_get(const_cast<GVariant*>(variant.gobj()), "i", &gameCount);
return;
}
}
@@ -158,7 +158,7 @@ void Gamemode::prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection>& con
const Glib::VariantContainerBase& parameters) {
if (parameters.is_of_type(Glib::VariantType("(b)"))) {
gboolean sleeping;
g_variant_get(parameters.gobj_copy(), "(b)", &sleeping);
g_variant_get(const_cast<GVariant*>(parameters.gobj()), "(b)", &sleeping);
if (!sleeping) {
getData();
dp.emit();
@@ -212,7 +212,7 @@ auto Gamemode::update() -> void {
// Tooltip
if (tooltip) {
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
+85 -38
View File
@@ -9,9 +9,14 @@
#include <sys/un.h>
#include <unistd.h>
#include <array>
#include <cerrno>
#include <cstring>
#include <filesystem>
#include <string>
#include "util/scoped_fd.hpp"
namespace waybar::modules::hyprland {
std::filesystem::path IPC::socketFolder_;
@@ -20,11 +25,7 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
static std::mutex folderMutex;
std::unique_lock lock(folderMutex);
// socket path, specified by EventManager of Hyprland
if (!socketFolder_.empty()) {
return socketFolder_;
}
if (socketFolder_.empty()) {
const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR");
std::filesystem::path xdgRuntimeDir;
// Only set path if env variable is set
@@ -38,15 +39,15 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr");
socketFolder_ = std::filesystem::path("/tmp") / "hypr";
}
}
socketFolder_ = socketFolder_ / instanceSig;
return socketFolder_;
return socketFolder_ / instanceSig;
}
IPC::IPC() {
// will start IPC and relay events to parseIPC
ipcThread_ = std::thread([this]() { socketListener(); });
socketOwnerPid_ = getpid();
ipcThread_ = std::thread([this]() { socketListener(); });
}
IPC::~IPC() {
@@ -54,20 +55,21 @@ IPC::~IPC() {
// failed exec()) exits.
if (getpid() != socketOwnerPid_) return;
running_ = false;
running_.store(false, std::memory_order_relaxed);
spdlog::info("Hyprland IPC stopping...");
{
std::lock_guard<std::mutex> lock(socketMutex_);
if (socketfd_ != -1) {
spdlog::trace("Shutting down socket");
if (shutdown(socketfd_, SHUT_RDWR) == -1) {
if (shutdown(socketfd_, SHUT_RDWR) == -1 && errno != ENOTCONN) {
spdlog::error("Hyprland IPC: Couldn't shutdown socket");
}
spdlog::trace("Closing socket");
if (close(socketfd_) == -1) {
spdlog::error("Hyprland IPC: Couldn't close socket");
}
}
if (ipcThread_.joinable()) {
ipcThread_.join();
}
}
IPC& IPC::inst() {
static IPC ipc;
@@ -85,10 +87,10 @@ void IPC::socketListener() {
spdlog::info("Hyprland IPC starting");
struct sockaddr_un addr;
socketfd_ = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = {};
const int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketfd_ == -1) {
if (socketfd == -1) {
spdlog::error("Hyprland IPC: socketfd failed");
return;
}
@@ -96,33 +98,57 @@ void IPC::socketListener() {
addr.sun_family = AF_UNIX;
auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock";
if (socketPath.native().size() >= sizeof(addr.sun_path)) {
spdlog::error("Hyprland IPC: Socket path is too long: {}", socketPath.string());
close(socketfd);
return;
}
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
int l = sizeof(struct sockaddr_un);
if (connect(socketfd_, (struct sockaddr*)&addr, l) == -1) {
spdlog::error("Hyprland IPC: Unable to connect?");
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
spdlog::error("Hyprland IPC: Unable to connect? {}", std::strerror(errno));
close(socketfd);
return;
}
auto* file = fdopen(socketfd_, "r");
if (file == nullptr) {
spdlog::error("Hyprland IPC: Couldn't open file descriptor");
return;
{
std::lock_guard<std::mutex> lock(socketMutex_);
socketfd_ = socketfd;
}
while (running_) {
std::string pending;
while (running_.load(std::memory_order_relaxed)) {
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes
const ssize_t bytes_read = read(socketfd, buffer.data(), buffer.size());
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);
if (bytes_read == 0) {
if (running_.load(std::memory_order_relaxed)) {
spdlog::warn("Hyprland IPC: Socket closed by peer");
}
break;
}
if (receivedCharPtr == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (bytes_read < 0) {
if (errno == EINTR) {
continue;
}
if (!running_.load(std::memory_order_relaxed)) {
break;
}
spdlog::error("Hyprland IPC: read failed: {}", std::strerror(errno));
break;
}
std::string messageReceived(buffer.data());
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
pending.append(buffer.data(), static_cast<std::size_t>(bytes_read));
for (auto newline_pos = pending.find('\n'); newline_pos != std::string::npos;
newline_pos = pending.find('\n')) {
std::string messageReceived = pending.substr(0, newline_pos);
pending.erase(0, newline_pos + 1);
if (messageReceived.empty()) {
continue;
}
spdlog::debug("hyprland IPC received {}", messageReceived);
try {
@@ -132,8 +158,16 @@ void IPC::socketListener() {
} catch (...) {
throw;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
{
std::lock_guard<std::mutex> lock(socketMutex_);
if (socketfd_ != -1) {
if (close(socketfd_) == -1) {
spdlog::error("Hyprland IPC: Couldn't close socket");
}
socketfd_ = -1;
}
}
spdlog::debug("Hyprland IPC stopped");
}
@@ -178,7 +212,7 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
std::string IPC::getSocket1Reply(const std::string& rq) {
// basically hyprctl
const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
util::ScopedFd serverSocket(socket(AF_UNIX, SOCK_STREAM, 0));
if (serverSocket < 0) {
throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)");
@@ -198,8 +232,10 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock";
// Use snprintf to copy the socketPath string into serverAddress.sun_path
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) <
0) {
const auto socketPathLength =
snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str());
if (socketPathLength < 0 ||
socketPathLength >= static_cast<int>(sizeof(serverAddress.sun_path))) {
throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)");
}
@@ -208,28 +244,39 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
}
auto sizeWritten = write(serverSocket, rq.c_str(), rq.length());
std::size_t totalWritten = 0;
while (totalWritten < rq.length()) {
const auto sizeWritten =
write(serverSocket, rq.c_str() + totalWritten, rq.length() - totalWritten);
if (sizeWritten < 0) {
if (errno == EINTR) {
continue;
}
spdlog::error("Hyprland IPC: Couldn't write (4)");
return "";
}
if (sizeWritten == 0) {
spdlog::error("Hyprland IPC: Socket write made no progress");
return "";
}
totalWritten += static_cast<std::size_t>(sizeWritten);
}
std::array<char, 8192> buffer = {0};
std::string response;
ssize_t sizeWritten = 0;
do {
sizeWritten = read(serverSocket, buffer.data(), 8192);
if (sizeWritten < 0) {
spdlog::error("Hyprland IPC: Couldn't read (5)");
close(serverSocket);
return "";
}
response.append(buffer.data(), sizeWritten);
} while (sizeWritten > 0);
close(serverSocket);
return response;
}
+21 -5
View File
@@ -63,19 +63,35 @@ auto Language::update() -> void {
void Language::onEvent(const std::string& ev) {
std::lock_guard<std::mutex> lg(mutex_);
std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(','));
const auto payloadStart = ev.find(">>");
if (payloadStart == std::string::npos) {
spdlog::warn("hyprland language received malformed event: {}", ev);
return;
}
const auto payload = ev.substr(payloadStart + 2);
const auto kbSeparator = payload.find(',');
if (kbSeparator == std::string::npos) {
spdlog::warn("hyprland language received malformed event payload: {}", ev);
return;
}
std::string kbName = payload.substr(0, kbSeparator);
// Last comma before variants parenthesis, eg:
// activelayout>>micro-star-int'l-co.,-ltd.-msi-gk50-elite-gaming-keyboard,English (US, intl.,
// with dead keys)
std::string beforeParenthesis;
auto parenthesisPos = ev.find_last_of('(');
auto parenthesisPos = payload.find_last_of('(');
if (parenthesisPos == std::string::npos) {
beforeParenthesis = ev;
beforeParenthesis = payload;
} else {
beforeParenthesis = std::string(begin(ev), begin(ev) + parenthesisPos);
beforeParenthesis = payload.substr(0, parenthesisPos);
}
auto layoutName = ev.substr(beforeParenthesis.find_last_of(',') + 1);
const auto layoutSeparator = beforeParenthesis.find_last_of(',');
if (layoutSeparator == std::string::npos) {
spdlog::warn("hyprland language received malformed layout payload: {}", ev);
return;
}
auto layoutName = payload.substr(layoutSeparator + 1);
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
return; // ignore
+17 -7
View File
@@ -44,12 +44,23 @@ auto Submap::parseConfig(const Json::Value& config) -> void {
auto Submap::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
// Handle style class changes
if (!prev_submap_.empty()) {
label_.get_style_context()->remove_class(prev_submap_);
}
if (!submap_.empty()) {
label_.get_style_context()->add_class(submap_);
}
prev_submap_ = submap_;
if (submap_.empty()) {
event_box_.hide();
} else {
label_.set_markup(fmt::format(fmt::runtime(format_), submap_));
if (tooltipEnabled()) {
label_.set_tooltip_text(submap_);
label_.set_tooltip_markup(submap_);
}
event_box_.show();
}
@@ -64,11 +75,12 @@ void Submap::onEvent(const std::string& ev) {
return;
}
auto submapName = ev.substr(ev.find_first_of('>') + 2);
if (!submap_.empty()) {
label_.get_style_context()->remove_class(submap_);
const auto separator = ev.find(">>");
if (separator == std::string::npos) {
spdlog::warn("hyprland submap received malformed event: {}", ev);
return;
}
auto submapName = ev.substr(separator + 2);
submap_ = submapName;
@@ -76,8 +88,6 @@ void Submap::onEvent(const std::string& ev) {
submap_ = default_submap_;
}
label_.get_style_context()->add_class(submap_);
spdlog::debug("hyprland submap onevent with {}", submap_);
dp.emit();
+37 -38
View File
@@ -32,7 +32,6 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
windowIpcUniqueLock.unlock();
queryActiveWorkspace();
update();
dp.emit();
}
@@ -45,6 +44,8 @@ Window::~Window() {
auto Window::update() -> void {
std::shared_lock<std::shared_mutex> windowIpcShareLock(windowIpcSmtx);
queryActiveWorkspace();
std::string windowName = waybar::util::sanitize_string(workspace_.last_window_title);
std::string windowAddress = workspace_.last_window;
@@ -70,13 +71,13 @@ auto Window::update() -> void {
tooltip_format = config_["tooltip-format"].asString();
}
if (!tooltip_format.empty()) {
label_.set_tooltip_text(
label_.set_tooltip_markup(
fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName),
fmt::arg("initialTitle", windowData_.initial_title),
fmt::arg("class", windowData_.class_name),
fmt::arg("initialClass", windowData_.initial_class_name)));
} else if (!label_text.empty()) {
label_.set_tooltip_text(label_text);
label_.set_tooltip_markup(label_text);
}
}
@@ -122,7 +123,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto monitors = IPC::inst().getSocket1JsonReply("monitors");
if (monitors.isArray()) {
auto monitor = std::ranges::find_if(
monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; });
monitors, [&](const Json::Value& monitor) { return monitor["name"] == monitorName; });
if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{
@@ -137,7 +138,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto workspaces = IPC::inst().getSocket1JsonReply("workspaces");
if (workspaces.isArray()) {
auto workspace = std::ranges::find_if(
workspaces, [&](Json::Value workspace) { return workspace["id"] == id; });
workspaces, [&](const Json::Value& workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id);
return Workspace{
@@ -175,42 +176,56 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
}
void Window::queryActiveWorkspace() {
std::shared_lock<std::shared_mutex> windowIpcShareLock(windowIpcSmtx);
if (separateOutputs_) {
workspace_ = getActiveWorkspace(this->bar_.output->name);
} else {
workspace_ = getActiveWorkspace();
}
focused_ = true;
if (workspace_.windows > 0) {
const auto clients = m_ipc.getSocket1JsonReply("clients");
if (clients.isArray()) {
auto activeWindow = std::ranges::find_if(
clients, [&](Json::Value window) { return window["address"] == workspace_.last_window; });
if (activeWindow == std::end(clients)) {
focused_ = false;
windowData_ = WindowData{};
allFloating_ = false;
swallowing_ = false;
fullscreen_ = false;
solo_ = false;
soloClass_.clear();
if (workspace_.windows <= 0) {
return;
}
const auto clients = m_ipc.getSocket1JsonReply("clients");
if (!clients.isArray()) {
return;
}
auto activeWindow = std::ranges::find_if(clients, [&](const Json::Value& window) {
return window["address"] == workspace_.last_window;
});
if (activeWindow == std::end(clients)) {
return;
}
focused_ = true;
windowData_ = WindowData::parse(*activeWindow);
updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
std::vector<Json::Value> workspaceWindows;
std::ranges::copy_if(clients, std::back_inserter(workspaceWindows), [&](Json::Value window) {
std::ranges::copy_if(
clients, std::back_inserter(workspaceWindows), [&](const Json::Value& window) {
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
});
swallowing_ = std::ranges::any_of(workspaceWindows, [&](Json::Value window) {
swallowing_ = std::ranges::any_of(workspaceWindows, [&](const Json::Value& window) {
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
});
std::vector<Json::Value> visibleWindows;
std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows),
[&](Json::Value window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(),
[&](Json::Value window) { return !window["floating"].asBool(); });
[&](const Json::Value& window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(
visibleWindows.begin(), visibleWindows.end(),
[&](const Json::Value& window) { return !window["floating"].asBool(); });
allFloating_ = std::ranges::all_of(
visibleWindows, [&](Json::Value window) { return window["floating"].asBool(); });
visibleWindows, [&](const Json::Value& window) { return window["floating"].asBool(); });
fullscreen_ = windowData_.fullscreen;
// Fullscreen windows look like they are solo
@@ -220,26 +235,10 @@ void Window::queryActiveWorkspace() {
if (solo_) {
soloClass_ = windowData_.class_name;
} else {
soloClass_ = "";
}
}
} else {
focused_ = false;
windowData_ = WindowData{};
allFloating_ = false;
swallowing_ = false;
fullscreen_ = false;
solo_ = false;
soloClass_ = "";
}
}
void Window::onEvent(const std::string& ev) {
queryActiveWorkspace();
dp.emit();
}
void Window::onEvent(const std::string& ev) { dp.emit(); }
void Window::setClass(const std::string& classname, bool enable) {
if (enable) {
+6 -9
View File
@@ -39,6 +39,8 @@ WindowCount::~WindowCount() {
auto WindowCount::update() -> void {
std::lock_guard<std::mutex> lg(mutex_);
queryActiveWorkspace();
std::string format = config_["format"].asString();
std::string formatEmpty = config_["format-empty"].asString();
std::string formatWindowed = config_["format-windowed"].asString();
@@ -56,7 +58,7 @@ auto WindowCount::update() -> void {
} else if (!format.empty()) {
label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows));
} else {
label_.set_text(fmt::format("{}", workspace_.windows));
label_.set_markup(fmt::format("{}", workspace_.windows));
}
label_.show();
@@ -77,7 +79,7 @@ auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspac
const auto monitors = m_ipc.getSocket1JsonReply("monitors");
if (monitors.isArray()) {
auto monitor = std::ranges::find_if(
monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; });
monitors, [&](const Json::Value& monitor) { return monitor["name"] == monitorName; });
if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{
@@ -91,7 +93,7 @@ auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspac
const auto workspaces = m_ipc.getSocket1JsonReply("workspaces");
if (workspaces.isArray()) {
auto workspace = std::ranges::find_if(
workspaces, [&](Json::Value workspace) { return workspace["id"] == id; });
workspaces, [&](const Json::Value& workspace) { return workspace["id"] == id; });
if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id);
return Workspace{
@@ -116,8 +118,6 @@ auto WindowCount::Workspace::parse(const Json::Value& value) -> WindowCount::Wor
}
void WindowCount::queryActiveWorkspace() {
std::lock_guard<std::mutex> lg(mutex_);
if (separateOutputs_) {
workspace_ = getActiveWorkspace(this->bar_.output->name);
} else {
@@ -125,10 +125,7 @@ void WindowCount::queryActiveWorkspace() {
}
}
void WindowCount::onEvent(const std::string& ev) {
queryActiveWorkspace();
dp.emit();
}
void WindowCount::onEvent(const std::string& ev) { dp.emit(); }
void WindowCount::setClass(const std::string& classname, bool enable) {
if (enable) {
@@ -19,7 +19,7 @@ WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data)
clearWorkspaceName();
}
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
WindowCreationPayload::WindowCreationPayload(const std::string& workspace_name,
WindowAddress window_address, WindowRepr window_repr)
: m_window(std::move(window_repr)),
m_windowAddress(std::move(window_address)),
@@ -28,9 +28,10 @@ WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
clearWorkspaceName();
}
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
WindowAddress window_address, std::string window_class,
std::string window_title, bool is_active)
WindowCreationPayload::WindowCreationPayload(const std::string& workspace_name,
WindowAddress window_address,
const std::string& window_class,
const std::string& window_title, bool is_active)
: m_window(std::make_pair(std::move(window_class), std::move(window_title))),
m_windowAddress(std::move(window_address)),
m_workspaceName(std::move(workspace_name)),
+2 -2
View File
@@ -96,7 +96,7 @@ bool Workspace::handleClicked(GdkEventButton *bt) const {
void Workspace::initializeWindowMap(const Json::Value& clients_data) {
m_windowMap.clear();
for (auto client : clients_data) {
for (const auto& client : clients_data) {
if (client["workspace"]["id"].asInt() == id()) {
insertWindow({client});
}
@@ -300,7 +300,7 @@ void Workspace::updateTaskbar(const std::string &workspace_icon) {
}
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");
if (window_repr.isActive) {
window_box->get_style_context()->add_class("active");
+81 -22
View File
@@ -34,6 +34,9 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
}
Workspaces::~Workspaces() {
if (m_scrollEventConnection_.connected()) {
m_scrollEventConnection_.disconnect();
}
m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish
std::lock_guard<std::mutex> lg(m_mutex);
@@ -43,6 +46,17 @@ void Workspaces::init() {
m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt();
initializeWorkspaces();
if (m_scrollEventConnection_.connected()) {
m_scrollEventConnection_.disconnect();
}
if (barScroll()) {
auto& window = const_cast<Bar&>(m_bar).window;
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
m_scrollEventConnection_ =
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
}
dp.emit();
}
@@ -148,7 +162,8 @@ void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson)
}
}
std::string Workspaces::getRewrite(std::string window_class, std::string window_title) {
std::string Workspaces::getRewrite(const std::string& window_class,
const std::string& window_title) {
std::string windowReprKey;
if (windowRewriteConfigUsesTitle()) {
windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title);
@@ -189,7 +204,7 @@ void Workspaces::initializeWorkspaces() {
auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
auto const clientsJson = m_ipc.getSocket1JsonReply("clients");
for (Json::Value workspaceJson : workspacesJson) {
for (const auto& workspaceJson : workspacesJson) {
std::string workspaceName = workspaceJson["name"].asString();
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
(!workspaceName.starts_with("special") || showSpecial()) &&
@@ -263,7 +278,7 @@ void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJs
// key is the workspace and value is array of monitors to create on
for (const Json::Value& monitor : value) {
if (monitor.isString() && monitor.asString() == currentMonitor) {
persistentWorkspacesToCreate.emplace_back(currentMonitor);
persistentWorkspacesToCreate.emplace_back(key);
break;
}
}
@@ -324,8 +339,13 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &c
void Workspaces::onEvent(const std::string& ev) {
std::lock_guard<std::mutex> lock(m_mutex);
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
std::string payload = ev.substr(eventName.size() + 2);
const auto separator = ev.find(">>");
if (separator == std::string::npos) {
spdlog::warn("Malformed Hyprland workspace event: {}", ev);
return;
}
std::string eventName = ev.substr(0, separator);
std::string payload = ev.substr(separator + 2);
if (eventName == "workspacev2") {
onWorkspaceActivated(payload);
@@ -393,7 +413,7 @@ void Workspaces::onWorkspaceCreated(std::string const &payload, Json::Value cons
auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules");
auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
for (Json::Value workspaceJson : workspacesJson) {
for (auto workspaceJson : workspacesJson) {
const auto currentId = workspaceJson["id"].asInt();
if (currentId == *workspaceId) {
std::string workspaceName = workspaceJson["name"].asString();
@@ -488,19 +508,21 @@ void Workspaces::onMonitorFocused(std::string const &payload) {
void Workspaces::onWindowOpened(std::string const& payload) {
spdlog::trace("Window opened: {}", payload);
updateWindowCount();
size_t lastCommaIdx = 0;
size_t nextCommaIdx = payload.find(',');
std::string windowAddress = payload.substr(lastCommaIdx, nextCommaIdx - lastCommaIdx);
const auto firstComma = payload.find(',');
const auto secondComma =
firstComma == std::string::npos ? std::string::npos : payload.find(',', firstComma + 1);
const auto thirdComma =
secondComma == std::string::npos ? std::string::npos : payload.find(',', secondComma + 1);
if (firstComma == std::string::npos || secondComma == std::string::npos ||
thirdComma == std::string::npos) {
spdlog::warn("Malformed Hyprland openwindow payload: {}", payload);
return;
}
lastCommaIdx = nextCommaIdx;
nextCommaIdx = payload.find(',', nextCommaIdx + 1);
std::string workspaceName = payload.substr(lastCommaIdx + 1, nextCommaIdx - lastCommaIdx - 1);
lastCommaIdx = nextCommaIdx;
nextCommaIdx = payload.find(',', nextCommaIdx + 1);
std::string windowClass = payload.substr(lastCommaIdx + 1, nextCommaIdx - lastCommaIdx - 1);
std::string windowTitle = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx);
std::string windowAddress = payload.substr(0, firstComma);
std::string workspaceName = payload.substr(firstComma + 1, secondComma - firstComma - 1);
std::string windowClass = payload.substr(secondComma + 1, thirdComma - secondComma - 1);
std::string windowTitle = payload.substr(thirdComma + 1);
bool isActive = m_currentActiveWindowAddress == windowAddress;
m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowClass, windowTitle, isActive);
@@ -636,6 +658,7 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
populateBoolConfig(config, "persistent-only", m_persistentOnly);
populateBoolConfig(config, "active-only", m_activeOnly);
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
populateBoolConfig(config, "enable-bar-scroll", m_barScroll);
m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value());
populateSortByConfig(config);
@@ -939,7 +962,7 @@ void Workspaces::sortWorkspaces() {
case SortMethod::NUMBER:
try {
return std::stoi(a->name()) < std::stoi(b->name());
} catch (const std::invalid_argument &) {
} catch (const std::exception& e) {
// Handle the exception if necessary.
break;
}
@@ -993,10 +1016,12 @@ void Workspaces::sortWorkspaces() {
void Workspaces::setUrgentWorkspace(std::string const& windowaddress) {
const Json::Value clientsJson = m_ipc.getSocket1JsonReply("clients");
const std::string normalizedAddress =
windowaddress.starts_with("0x") ? windowaddress : fmt::format("0x{}", windowaddress);
int workspaceId = -1;
for (Json::Value clientJson : clientsJson) {
if (clientJson["address"].asString().ends_with(windowaddress)) {
for (const auto& clientJson : clientsJson) {
if (clientJson["address"].asString() == normalizedAddress) {
workspaceId = clientJson["workspace"]["id"].asInt();
break;
}
@@ -1125,7 +1150,11 @@ std::string Workspaces::makePayload(Args const &...args) {
}
std::pair<std::string, std::string> Workspaces::splitDoublePayload(std::string const& payload) {
const std::string part1 = payload.substr(0, payload.find(','));
const auto separator = payload.find(',');
if (separator == std::string::npos) {
throw std::invalid_argument("Expected a two-part Hyprland payload");
}
const std::string part1 = payload.substr(0, separator);
const std::string part2 = payload.substr(part1.size() + 1);
return {part1, part2};
}
@@ -1134,6 +1163,9 @@ std::tuple<std::string, std::string, std::string> Workspaces::splitTriplePayload
std::string const& payload) {
const size_t firstComma = payload.find(',');
const size_t secondComma = payload.find(',', firstComma + 1);
if (firstComma == std::string::npos || secondComma == std::string::npos) {
throw std::invalid_argument("Expected a three-part Hyprland payload");
}
const std::string part1 = payload.substr(0, firstComma);
const std::string part2 = payload.substr(firstComma + 1, secondComma - (firstComma + 1));
@@ -1151,4 +1183,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
+17 -12
View File
@@ -14,22 +14,27 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
size_ = config["size"].asInt();
interval_ = config_["interval"] == "once"
? std::chrono::milliseconds::max()
: std::chrono::milliseconds(std::max(
1L, // Minimum 1ms due to millisecond precision
static_cast<long>(
(config_["interval"].isNumeric() ? config_["interval"].asDouble() : 0) *
1000)));
const auto once = std::chrono::milliseconds::max();
if (!config_.isMember("interval") || config_["interval"].isNull() ||
config_["interval"] == "once") {
interval_ = once;
} else if (config_["interval"].isNumeric()) {
const auto interval_seconds = config_["interval"].asDouble();
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) {
size_ = 16;
}
if (interval_.count() == 0) {
interval_ = std::chrono::milliseconds::max();
}
delayWorker();
}
@@ -41,7 +46,7 @@ void waybar::modules::Image::delayWorker() {
}
void waybar::modules::Image::refresh(int sig) {
if (sig == SIGRTMIN + config_["signal"].asInt()) {
if (config_["signal"].isInt() && sig == SIGRTMIN + config_["signal"].asInt()) {
thread_.wake_up();
}
}
+3 -2
View File
@@ -85,7 +85,8 @@ auto getInhibitors(const Json::Value& config) -> std::string {
if (config["what"].isArray()) {
inhibitors = checkInhibitor(config["what"][0].asString());
for (decltype(config["what"].size()) i = 1; i < config["what"].size(); ++i) {
inhibitors += ":" + checkInhibitor(config["what"][i].asString());
inhibitors.append(":");
inhibitors.append(checkInhibitor(config["what"][i].asString()));
}
return inhibitors;
}
@@ -123,7 +124,7 @@ auto Inhibitor::update() -> void {
label_.get_style_context()->add_class(status_text);
if (tooltipEnabled()) {
label_.set_tooltip_text(status_text);
label_.set_tooltip_markup(status_text);
}
return ALabel::update();
+5 -2
View File
@@ -53,7 +53,7 @@ std::string JACK::JACKState() {
auto JACK::update() -> void {
std::string format;
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")) {
label_.get_style_context()->remove_class("xrun");
@@ -80,7 +80,7 @@ auto JACK::update() -> void {
if (tooltipEnabled()) {
std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms";
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::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_),
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) {
std::lock_guard<std::mutex> lock(mutex_);
bufsize_ = size;
return 0;
}
int JACK::sampleRate(jack_nframes_t rate) {
std::lock_guard<std::mutex> lock(mutex_);
samplerate_ = rate;
return 0;
}
int JACK::xrun() {
std::lock_guard<std::mutex> lock(mutex_);
xruns_ += 1;
state_ = "xrun";
return 0;
+15
View File
@@ -232,9 +232,12 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
}
tryAddDevice(dev_path);
} else if (event->mask & IN_DELETE) {
std::lock_guard<std::mutex> lock(devices_mutex_);
auto it = libinput_devices_.find(dev_path);
if (it != libinput_devices_.end()) {
spdlog::info("Keyboard {} has been removed.", dev_path);
libinput_path_remove_device(it->second);
libinput_device_unref(it->second);
libinput_devices_.erase(it);
}
}
@@ -245,6 +248,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
}
waybar::modules::KeyboardState::~KeyboardState() {
std::lock_guard<std::mutex> lock(devices_mutex_);
for (const auto& [_, dev_ptr] : libinput_devices_) {
libinput_path_remove_device(dev_ptr);
}
@@ -256,12 +260,18 @@ auto waybar::modules::KeyboardState::update() -> void {
try {
std::string dev_path;
{
std::lock_guard<std::mutex> lock(devices_mutex_);
if (libinput_devices_.empty()) {
return;
}
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);
auto dev = openDevice(fd);
numl = libevdev_get_event_value(dev, EV_LED, LED_NUML);
@@ -308,10 +318,15 @@ auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path)
auto dev = openDevice(fd);
if (supportsLockStates(dev)) {
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()) {
auto device = libinput_path_add_device(libinput_, dev_path.c_str());
if (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);
+1 -1
View File
@@ -22,7 +22,7 @@ auto waybar::modules::Load::update() -> void {
auto [load1, load5, load15] = Load::getLoad();
if (tooltipEnabled()) {
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 state = getState(load1);
+2 -2
View File
@@ -69,7 +69,7 @@ auto waybar::modules::Memory::update() -> void {
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
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::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes),
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("swapAvail", available_swap_gigabytes)));
} 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 {
+5 -3
View File
@@ -110,7 +110,7 @@ void waybar::modules::MPD::setLabel() {
? config_["tooltip-format-disconnected"].asString()
: "MPD (disconnected)";
// Nothing to format
label_.set_tooltip_text(tooltip_format);
label_.set_tooltip_markup(tooltip_format);
}
return;
}
@@ -210,7 +210,7 @@ void waybar::modules::MPD::setLabel() {
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
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) {
spdlog::warn("mpd: format error (tooltip): {}", e.what());
}
@@ -239,7 +239,8 @@ std::string waybar::modules::MPD::getStateIcon() const {
}
}
std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) const {
std::string waybar::modules::MPD::getOptionIcon(const std::string& optionName,
bool activated) const {
if (!config_[optionName + "-icons"].isObject()) {
return "";
}
@@ -323,6 +324,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
case MPD_ERROR_SYSTEM:
if (auto ec = mpd_connection_get_system_error(conn); ec != 0) {
mpd_connection_clear_error(conn);
connection_.reset();
throw std::system_error(ec, std::system_category());
}
G_GNUC_FALLTHROUGH;
+80 -31
View File
@@ -109,6 +109,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
player_ = config_["player"].asString();
}
if (config_["ignored-players"].isArray()) {
ignored_players_.reserve(config_["ignored-players"].size());
for (const auto& item : config_["ignored-players"]) {
if (item.isString()) {
ignored_players_.push_back(item.asString());
@@ -117,7 +118,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
}
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error) {
g_error_free(error);
}
@@ -161,8 +162,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
if (player) {
g_object_connect(player, "signal::play", G_CALLBACK(onPlayerPlay), this, "signal::pause",
G_CALLBACK(onPlayerPause), this, "signal::stop", G_CALLBACK(onPlayerStop),
this, "signal::stop", G_CALLBACK(onPlayerStop), this, "signal::metadata",
G_CALLBACK(onPlayerMetadata), this, NULL);
this, "signal::metadata", G_CALLBACK(onPlayerMetadata), this, NULL);
}
// allow setting an interval count that triggers periodic refreshes
@@ -178,8 +178,17 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
}
Mpris::~Mpris() {
if (manager != nullptr) g_object_unref(manager);
if (player != nullptr) g_object_unref(player);
if (manager != nullptr) {
g_signal_handlers_disconnect_by_data(manager, this);
}
if (player != nullptr) {
g_signal_handlers_disconnect_by_data(player, this);
}
if (last_active_player_ != nullptr && last_active_player_ != player) {
g_object_unref(last_active_player_);
}
g_clear_object(&manager);
g_clear_object(&player);
}
auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) -> std::string {
@@ -410,11 +419,14 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye
return;
}
if (mpris->player != nullptr) {
g_signal_handlers_disconnect_by_data(mpris->player, mpris);
g_clear_object(&mpris->player);
}
mpris->player = playerctl_player_new_from_name(player_name, nullptr);
g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause",
G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop),
mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::metadata",
G_CALLBACK(onPlayerMetadata), mpris, NULL);
mpris, "signal::metadata", G_CALLBACK(onPlayerMetadata), mpris, NULL);
mpris->dp.emit();
}
@@ -458,10 +470,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void {
if (!mpris) return;
spdlog::debug("mpris: player-stop callback");
// hide widget
mpris->event_box_.set_visible(false);
// update widget
// update widget (update() handles visibility)
mpris->dp.emit();
}
@@ -480,7 +489,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
}
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error) {
g_error_free(error);
}
@@ -488,7 +497,12 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
char* player_status = nullptr;
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_;
if (player_name == "playerctld") {
@@ -498,19 +512,52 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
}
// > get the list of players [..] in order of activity
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
players = g_list_first(players);
if (players)
player_name = static_cast<PlayerctlPlayerName*>(players->data)->name;
else
return std::nullopt; // no players found, hide the widget
}
PlayerctlPlayer* first_valid_player = nullptr;
std::string first_valid_name;
for (auto* p = g_list_first(players); p != nullptr; p = p->next) {
auto* pn = static_cast<PlayerctlPlayerName*>(p->data);
std::string name = pn->name;
if (std::any_of(ignored_players_.begin(), ignored_players_.end(),
[&](const std::string& ignored) { return name == ignored; })) {
spdlog::warn("mpris[{}]: ignoring player update", name);
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);
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
player_status[0] = std::tolower(player_status[0]);
@@ -524,28 +571,29 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
.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_);
info.artist = artist_;
g_free(artist_);
}
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_);
info.album = album_;
g_free(album_);
}
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_);
info.title = title_;
g_free(title_);
}
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_);
auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
@@ -557,7 +605,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
if (error) goto errorexit;
{
auto position_ = playerctl_player_get_position(player, &error);
auto position_ = playerctl_player_get_position(last_active_player_, &error);
if (error) {
// it's fine to have an error here because not all players report a position
g_error_free(error);
@@ -609,12 +657,13 @@ bool Mpris::handleToggle(GdkEventButton* const& e) {
});
// Command pattern: encapsulate each button's action
auto* target = last_active_player_ ? last_active_player_ : player;
const ButtonAction actions[] = {
{1, "on-click", [&]() { playerctl_player_play_pause(player, &error); }},
{2, "on-click-middle", [&]() { playerctl_player_previous(player, &error); }},
{3, "on-click-right", [&]() { playerctl_player_next(player, &error); }},
{8, "on-click-backward", [&]() { playerctl_player_previous(player, &error); }},
{9, "on-click-forward", [&]() { playerctl_player_next(player, &error); }},
{1, "on-click", [&]() { playerctl_player_play_pause(target, &error); }},
{2, "on-click-middle", [&]() { playerctl_player_previous(target, &error); }},
{3, "on-click-right", [&]() { playerctl_player_next(target, &error); }},
{8, "on-click-backward", [&]() { playerctl_player_previous(target, &error); }},
{9, "on-click-forward", [&]() { playerctl_player_next(target, &error); }},
};
for (const auto& action : actions) {
@@ -734,7 +783,7 @@ auto Mpris::update() -> void {
fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)),
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) {
spdlog::warn("mpris: format error (tooltip): {}", e.what());
}
+42 -16
View File
@@ -46,6 +46,7 @@ waybar::modules::Network::readBandwidthUsage() {
std::string ifacename;
iss >> ifacename; // ifacename contains "eth0:"
if (ifacename.empty()) continue;
ifacename.pop_back(); // remove trailing ':'
if (ifacename != ifname_) {
continue;
@@ -320,6 +321,7 @@ auto waybar::modules::Network::update() -> void {
} else if (addr_pref_ == ip_addr_pref::IPV6) {
final_ipaddr_ = ipaddr6_;
} else if (addr_pref_ == ip_addr_pref::IPV4_6) {
final_ipaddr_.reserve(ipaddr_.length() + ipaddr6_.length() + 1);
final_ipaddr_ = ipaddr_;
final_ipaddr_ += '\n';
final_ipaddr_ += ipaddr6_;
@@ -372,18 +374,26 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits",
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
pow_format(bandwidth_down * 8ull / (interval_.count() / 1000.0), "b/s")),
fmt::arg("bandwidthUpBits",
pow_format(bandwidth_up * 8ull / (interval_.count() / 1000.0), "b/s")),
fmt::arg("bandwidthTotalBits",
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")),
fmt::arg("bandwidthTotalOctets",
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")),
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")),
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")),
fmt::arg("bandwidthTotalBytes",
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s")));
pow_format((bandwidth_up + bandwidth_down) * 8ull / (interval_.count() / 1000.0),
"b/s")),
fmt::arg("bandwidthDownOctets",
pow_format(bandwidth_down / (interval_.count() / 1000.0), "o/s")),
fmt::arg("bandwidthUpOctets",
pow_format(bandwidth_up / (interval_.count() / 1000.0), "o/s")),
fmt::arg(
"bandwidthTotalOctets",
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "o/s")),
fmt::arg("bandwidthDownBytes",
pow_format(bandwidth_down / (interval_.count() / 1000.0), "B/s")),
fmt::arg("bandwidthUpBytes",
pow_format(bandwidth_up / (interval_.count() / 1000.0), "B/s")),
fmt::arg(
"bandwidthTotalBytes",
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "B/s")));
if (label_.get_tooltip_text() != tooltip_text) {
label_.set_tooltip_markup(tooltip_text);
}
@@ -625,18 +635,31 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
case IFA_LOCAL:
char ipaddr[INET6_ADDRSTRLEN];
if (!is_del_event) {
bool addr_changed = false;
std::string changed_ipaddr;
int changed_cidr = 0;
if ((net->addr_pref_ == ip_addr_pref::IPV4 ||
net->addr_pref_ == ip_addr_pref::IPV4_6) &&
net->cidr_ == 0 && ifa->ifa_family == AF_INET) {
net->ipaddr_ =
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr));
if (inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)) !=
nullptr) {
net->ipaddr_ = ipaddr;
net->cidr_ = ifa->ifa_prefixlen;
addr_changed = true;
changed_ipaddr = net->ipaddr_;
changed_cidr = net->cidr_;
}
} else if ((net->addr_pref_ == ip_addr_pref::IPV6 ||
net->addr_pref_ == ip_addr_pref::IPV4_6) &&
net->cidr6_ == 0 && ifa->ifa_family == AF_INET6) {
net->ipaddr6_ =
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr));
if (inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)) !=
nullptr) {
net->ipaddr6_ = ipaddr;
net->cidr6_ = ifa->ifa_prefixlen;
addr_changed = true;
changed_ipaddr = net->ipaddr6_;
changed_cidr = net->cidr6_;
}
}
switch (ifa->ifa_family) {
@@ -656,7 +679,10 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
net->netmask6_ = inet_ntop(ifa->ifa_family, &netmask6, ipaddr, sizeof(ipaddr));
}
}
spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_);
if (addr_changed) {
spdlog::debug("network: {}, new addr {}/{}", net->ifname_, changed_ipaddr,
changed_cidr);
}
} else {
net->ipaddr_.clear();
net->ipaddr6_.clear();
+9 -16
View File
@@ -17,19 +17,21 @@
#include "giomm/dataoutputstream.h"
#include "giomm/unixinputstream.h"
#include "giomm/unixoutputstream.h"
#include "util/scoped_fd.hpp"
namespace waybar::modules::niri {
IPC::IPC() { startIPC(); }
int IPC::connectToSocket() {
const char* socket_path = getenv("NIRI_SOCKET");
if (socket_path == nullptr) {
spdlog::warn("Niri is not running, niri IPC will not be available.");
return -1;
throw std::runtime_error("Niri IPC: NIRI_SOCKET was not set! (Is Niri running?)");
}
struct sockaddr_un addr;
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
util::ScopedFd socketfd(socket(AF_UNIX, SOCK_STREAM, 0));
if (socketfd == -1) {
throw std::runtime_error("socketfd failed");
@@ -44,26 +46,18 @@ int IPC::connectToSocket() {
int l = sizeof(struct sockaddr_un);
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
close(socketfd);
throw std::runtime_error("unable to connect");
}
return socketfd;
return socketfd.release();
}
void IPC::startIPC() {
// will start IPC and relay events to parseIPC
std::thread([&]() {
int socketfd;
try {
socketfd = connectToSocket();
} catch (std::exception &e) {
spdlog::error("Niri IPC: failed to start, reason: {}", e.what());
return;
}
if (socketfd == -1) return;
int socketfd = connectToSocket();
std::thread([this, socketfd]() {
spdlog::info("Niri IPC starting");
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
@@ -241,8 +235,7 @@ void IPC::unregisterForIPC(EventHandler *ev_handler) {
}
Json::Value IPC::send(const Json::Value& request) {
int socketfd = connectToSocket();
if (socketfd == -1) throw std::runtime_error("Niri is not running");
util::ScopedFd socketfd(connectToSocket());
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false);
+1
View File
@@ -32,6 +32,7 @@ void Language::updateFromIPC() {
auto ipcLock = gIPC->lockData();
layouts_.clear();
layouts_.reserve(gIPC->keyboardLayoutNames().size());
for (const auto& fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName));
current_idx_ = gIPC->keyboardLayoutCurrent();
+1 -1
View File
@@ -67,7 +67,7 @@ void Window::doUpdate() {
updateAppIconName(appId, "");
if (tooltipEnabled()) label_.set_tooltip_text(title);
if (tooltipEnabled()) label_.set_tooltip_markup(title);
const auto id = window["id"].asUInt64();
const auto workspaceId = window["workspace_id"].asUInt64();
+2 -2
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["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_active"].asBool() && icons["active"]) return icons["active"].asString();
if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
if (ws["name"]) {
const auto& name = ws["name"].asString();
+5 -1
View File
@@ -157,7 +157,7 @@ auto PowerProfilesDaemon::update() -> void {
store.push_back(fmt::arg("icon", getIcon(0, profile.name)));
label_.set_markup(fmt::vformat(format_, store));
if (tooltipEnabled()) {
label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store));
label_.set_tooltip_markup(fmt::vformat(tooltipFormat_, store));
}
// Set CSS class
@@ -176,6 +176,10 @@ auto PowerProfilesDaemon::update() -> void {
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
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 */ {
activeProfile_++;
if (activeProfile_ == availableProfiles_.end()) {
+1 -1
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("icon", getIcon(sink_volume, getPulseIcon()))));
} else {
label_.set_tooltip_text(sink_desc);
label_.set_tooltip_markup(sink_desc);
}
}
+3 -2
View File
@@ -86,8 +86,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Layout *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Layout*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
@@ -118,6 +118,7 @@ Layout::Layout(const std::string &id, const waybar::Bar &bar, const Json::Value
if (!seat_) {
spdlog::error("wl_seat not advertised");
return;
}
label_.hide();
+3 -2
View File
@@ -46,8 +46,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, version));
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Mode *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Mode*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
@@ -77,6 +77,7 @@ Mode::Mode(const std::string &id, const waybar::Bar &bar, const Json::Value &con
if (!seat_) {
spdlog::error("wl_seat not advertised");
return;
}
label_.hide();
+5 -2
View File
@@ -69,8 +69,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min(version, 1u);
static_cast<Tags *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Tags*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
@@ -101,10 +101,12 @@ Tags::Tags(const std::string &id, const waybar::Bar &bar, const Json::Value &con
if (!control_) {
spdlog::error("river_control_v1 not advertised");
return;
}
if (!seat_) {
spdlog::error("wl_seat not advertised");
return;
}
box_.set_name("tags");
@@ -168,6 +170,7 @@ Tags::~Tags() {
}
void Tags::handle_show() {
if (!status_manager_) return;
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);
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
+3 -2
View File
@@ -46,8 +46,8 @@ static void handle_global(void *data, struct wl_registry *registry, uint32_t nam
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
version = std::min<uint32_t>(version, 1);
static_cast<Window *>(data)->seat_ = static_cast<struct wl_seat *>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
static_cast<Window*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
@@ -78,6 +78,7 @@ Window::Window(const std::string &id, const waybar::Bar &bar, const Json::Value
if (!seat_) {
spdlog::error("wl_seat not advertised");
return;
}
label_.hide(); // hide the label until populated
+2 -2
View File
@@ -25,9 +25,9 @@ auto waybar::modules::Clock::update() -> void {
if (config_["tooltip-format"].isString()) {
auto tooltip_format = config_["tooltip-format"].asString();
auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime);
label_.set_tooltip_text(tooltip_text);
label_.set_tooltip_markup(tooltip_text);
} else {
label_.set_tooltip_text(text);
label_.set_tooltip_markup(text);
}
}
// Call parent update
+3 -1
View File
@@ -102,7 +102,9 @@ Sndio::~Sndio() { sioctl_close(hdl_); }
auto Sndio::update() -> void {
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) {
label_.get_style_context()->add_class("muted");
+50 -10
View File
@@ -8,7 +8,8 @@ namespace waybar::modules::SNI {
Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
const std::function<void(std::unique_ptr<Item>&)>& on_add,
const std::function<void(std::unique_ptr<Item>&)>& on_remove)
const std::function<void(std::unique_ptr<Item>&)>& on_remove,
const std::function<void()>& on_update)
: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" +
std::to_string(id)),
object_path_("/StatusNotifierHost/" + std::to_string(id)),
@@ -17,7 +18,8 @@ Host::Host(const std::size_t id, const Json::Value& config, const Bar& bar,
config_(config),
bar_(bar),
on_add_(on_add),
on_remove_(on_remove) {}
on_remove_(on_remove),
on_update_(on_update) {}
Host::~Host() {
if (bus_name_id_ > 0) {
@@ -54,12 +56,12 @@ void Host::nameVanished(const Glib::RefPtr<Gio::DBus::Connection>& conn, const G
g_cancellable_cancel(cancellable_);
g_clear_object(&cancellable_);
g_clear_object(&watcher_);
items_.clear();
clearItems();
}
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) {
g_error_free(error);
}
@@ -81,7 +83,7 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) {
g_error_free(error);
}
@@ -117,13 +119,50 @@ void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer d
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
host->on_remove_(*it);
host->items_.erase(it);
host->removeItem(it);
break;
}
}
}
void Host::itemReady(Item& item) {
auto it = std::find_if(items_.begin(), items_.end(),
[&item](const auto& candidate) { return candidate.get() == &item; });
if (it != items_.end() && (*it)->isReady()) {
on_add_(*it);
}
}
void Host::itemInvalidated(Item& item) {
auto it = std::find_if(items_.begin(), items_.end(),
[&item](const auto& candidate) { return candidate.get() == &item; });
if (it != items_.end()) {
removeItem(it);
}
}
void Host::removeItem(std::vector<std::unique_ptr<Item>>::iterator it) {
if ((*it)->isReady()) {
on_remove_(*it);
}
items_.erase(it);
}
void Host::clearItems() {
bool removed_ready_item = false;
for (auto& item : items_) {
if (item->isReady()) {
on_remove_(item);
removed_ready_item = true;
}
}
bool had_items = !items_.empty();
items_.clear();
if (had_items && !removed_ready_item) {
on_update_();
}
}
std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
auto it = service.find('/');
if (it != std::string::npos) {
@@ -132,15 +171,16 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
return {service, "/StatusNotifierItem"};
}
void Host::addRegisteredItem(std::string service) {
void Host::addRegisteredItem(const std::string& service) {
std::string bus_name, object_path;
std::tie(bus_name, object_path) = getBusNameAndObjectPath(service);
auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) {
return bus_name == item->bus_name && object_path == item->object_path;
});
if (it == items_.end()) {
items_.emplace_back(new Item(bus_name, object_path, config_, bar_));
on_add_(items_.back());
items_.emplace_back(new Item(
bus_name, object_path, config_, bar_, [this](Item& item) { itemReady(item); },
[this](Item& item) { itemInvalidated(item); }, on_update_));
}
}
+150 -48
View File
@@ -5,6 +5,7 @@
#include <gtkmm/tooltip.h>
#include <spdlog/spdlog.h>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <map>
@@ -37,13 +38,18 @@ namespace waybar::modules::SNI {
static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name;
static const unsigned UPDATE_DEBOUNCE_TIME = 10;
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar)
Item::Item(const std::string& bn, const std::string& op, const Json::Value& config, const Bar& bar,
const std::function<void(Item&)>& on_ready,
const std::function<void(Item&)>& on_invalidate, const std::function<void()>& on_updated)
: bus_name(bn),
object_path(op),
icon_size(16),
effective_icon_size(0),
icon_theme(Gtk::IconTheme::create()),
bar_(bar) {
bar_(bar),
on_ready_(on_ready),
on_invalidate_(on_invalidate),
on_updated_(on_updated) {
if (config["icon-size"].isUInt()) {
icon_size = config["icon-size"].asUInt();
}
@@ -79,7 +85,13 @@ Item::~Item() {
this->gtk_menu->popdown();
this->gtk_menu->detach();
}
if (this->dbus_menu != nullptr) {
g_object_weak_unref(G_OBJECT(this->dbus_menu), (GWeakNotify)onMenuDestroyed, this);
this->dbus_menu = nullptr;
}
}
bool Item::isReady() const { return ready_; }
bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
@@ -108,14 +120,18 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
if (this->id.empty() || this->category.empty()) {
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
invalidate();
return;
}
this->updateImage();
setReady();
} catch (const Glib::Error& err) {
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
invalidate();
} catch (const std::exception& err) {
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
invalidate();
}
}
@@ -180,11 +196,11 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
} else if (name == "OverlayIconName") {
overlay_icon_name = get_variant<std::string>(value);
} else if (name == "OverlayIconPixmap") {
// TODO: overlay_icon_pixmap
overlay_icon_pixmap = extractPixBuf(value.gobj());
} else if (name == "AttentionIconName") {
attention_icon_name = get_variant<std::string>(value);
} else if (name == "AttentionIconPixmap") {
// TODO: attention_icon_pixmap
attention_icon_pixmap = extractPixBuf(value.gobj());
} else if (name == "AttentionMovieName") {
attention_movie_name = get_variant<std::string>(value);
} else if (name == "ToolTip") {
@@ -213,18 +229,35 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
}
void Item::setStatus(const Glib::ustring& value) {
Glib::ustring lower = value.lowercase();
event_box.set_visible(show_passive_ || lower.compare("passive") != 0);
status_ = value.lowercase();
event_box.set_visible(show_passive_ || status_.compare("passive") != 0);
auto style = event_box.get_style_context();
for (const auto& class_name : style->list_classes()) {
style->remove_class(class_name);
}
if (lower.compare("needsattention") == 0) {
auto css_class = status_;
if (css_class.compare("needsattention") == 0) {
// convert status to dash-case for CSS
lower = "needs-attention";
css_class = "needs-attention";
}
style->add_class(lower);
style->add_class(css_class);
on_updated_();
}
void Item::setReady() {
if (ready_) {
return;
}
ready_ = true;
on_ready_(*this);
}
void Item::invalidate() {
if (ready_) {
ready_ = false;
}
on_invalidate_(*this);
}
void Item::setCustomIcon(const std::string& id) {
@@ -233,9 +266,13 @@ void Item::setCustomIcon(const std::string& id) {
std::string custom_icon = IconManager::instance().getIconForApp(id);
if (!custom_icon.empty()) {
if (std::filesystem::exists(custom_icon)) {
try {
Glib::RefPtr<Gdk::Pixbuf> custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon);
icon_name = ""; // icon_name has priority over pixmap
icon_pixmap = custom_pixbuf;
} catch (const Glib::Error& e) {
spdlog::error("Failed to load custom icon {}: {}", custom_icon, e.what());
}
} else { // if file doesn't exist it's most likely an icon_name
icon_name = custom_icon;
}
@@ -279,8 +316,8 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
{"NewTitle", {"Title"}},
{"NewIcon", {"IconName", "IconPixmap"}},
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
{"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
{"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
{"NewIconThemePath", {"IconThemePath"}},
{"NewToolTip", {"ToolTip"}},
{"NewStatus", {"Status"}},
@@ -328,11 +365,20 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
if (array != nullptr) {
g_free(array);
}
#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 68
array = static_cast<guchar*>(g_memdup2(data, size));
#else
array = static_cast<guchar*>(g_memdup(data, size));
#endif
// We must allocate our own array because the data from GVariant is read-only
// and we need to modify it to convert ARGB to RGBA.
array = static_cast<guchar*>(g_malloc(size));
// Copy and convert ARGB to RGBA in one pass to avoid g_memdup2 overhead
const guchar* src = static_cast<const guchar*>(data);
for (gsize i = 0; i < size; i += 4) {
guchar alpha = src[i];
array[i] = src[i + 1];
array[i + 1] = src[i + 2];
array[i + 2] = src[i + 3];
array[i + 3] = alpha;
}
lwidth = width;
lheight = height;
}
@@ -341,14 +387,6 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
}
g_variant_iter_free(it);
if (array != nullptr) {
/* argb to rgba */
for (uint32_t i = 0; i < 4U * lwidth * lheight; i += 4) {
guchar alpha = array[i];
array[i] = array[i + 1];
array[i + 1] = array[i + 2];
array[i + 2] = array[i + 3];
array[i + 3] = alpha;
}
return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB, true, 8, lwidth,
lheight, 4 * lwidth, &pixbuf_data_deleter);
}
@@ -357,47 +395,36 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
void Item::updateImage() {
auto pixbuf = getIconPixbuf();
if (!pixbuf) return;
auto scaled_icon_size = getScaledIconSize();
// 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
// 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.
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();
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
}
pixbuf = overlayPixbufs(pixbuf, getOverlayIconPixbuf());
auto surface =
Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), image.get_window());
image.set(surface);
}
Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
if (!icon_name.empty()) {
try {
std::ifstream temp(icon_name);
if (temp.is_open()) {
return Gdk::Pixbuf::create_from_file(icon_name);
}
} catch (Glib::Error& e) {
// Ignore because we want to also try different methods of getting an icon.
//
// But a warning is logged, as the file apparently exists, but there was
// a failure in creating a pixbuf out of it.
spdlog::warn("Item '{}': {}", id, static_cast<std::string>(e.what()));
}
try {
// Will throw if it can not find an icon.
return getIconByName(icon_name, getScaledIconSize());
} catch (Glib::Error& e) {
spdlog::trace("Item '{}': {}", id, static_cast<std::string>(e.what()));
if (status_ == "needsattention") {
if (auto attention_pixbuf = getAttentionIconPixbuf()) {
return attention_pixbuf;
}
}
// Return the pixmap only if an icon for the given name could not be found.
if (auto pixbuf = loadIconFromNameOrFile(icon_name, true)) {
return pixbuf;
}
if (icon_pixmap) {
return icon_pixmap;
}
@@ -412,9 +439,78 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
return getIconByName("image-missing", getScaledIconSize());
}
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
icon_theme->rescan_if_needed();
Glib::RefPtr<Gdk::Pixbuf> Item::getAttentionIconPixbuf() {
if (auto pixbuf = loadIconFromNameOrFile(attention_icon_name, false)) {
return pixbuf;
}
if (auto pixbuf = loadIconFromNameOrFile(attention_movie_name, false)) {
return pixbuf;
}
return attention_icon_pixmap;
}
Glib::RefPtr<Gdk::Pixbuf> Item::getOverlayIconPixbuf() {
if (auto pixbuf = loadIconFromNameOrFile(overlay_icon_name, false)) {
return pixbuf;
}
return overlay_icon_pixmap;
}
Glib::RefPtr<Gdk::Pixbuf> Item::loadIconFromNameOrFile(const std::string& name, bool log_failure) {
if (name.empty()) {
return {};
}
try {
std::ifstream temp(name);
if (temp.is_open()) {
return Gdk::Pixbuf::create_from_file(name);
}
} catch (const Glib::Error& e) {
if (log_failure) {
spdlog::warn("Item '{}': {}", id, static_cast<std::string>(e.what()));
}
}
try {
return getIconByName(name, getScaledIconSize());
} catch (const Glib::Error& e) {
if (log_failure) {
spdlog::trace("Item '{}': {}", id, static_cast<std::string>(e.what()));
}
}
return {};
}
Glib::RefPtr<Gdk::Pixbuf> Item::overlayPixbufs(const Glib::RefPtr<Gdk::Pixbuf>& base,
const Glib::RefPtr<Gdk::Pixbuf>& overlay) {
if (!base || !overlay) {
return base;
}
auto composed = base->copy();
if (!composed) {
return base;
}
int overlay_target_size =
std::max(1, std::min(composed->get_width(), composed->get_height()) / 2);
auto scaled_overlay = overlay->scale_simple(overlay_target_size, overlay_target_size,
Gdk::InterpType::INTERP_BILINEAR);
if (!scaled_overlay) {
return composed;
}
int dest_x = std::max(0, composed->get_width() - scaled_overlay->get_width());
int dest_y = std::max(0, composed->get_height() - scaled_overlay->get_height());
scaled_overlay->composite(composed, dest_x, dest_y, scaled_overlay->get_width(),
scaled_overlay->get_height(), dest_x, dest_y, 1.0, 1.0,
Gdk::InterpType::INTERP_BILINEAR, 255);
return composed;
}
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) {
if (!icon_theme_path.empty()) {
auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size,
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
@@ -456,6 +552,9 @@ void Item::makeMenu() {
}
bool Item::handleClick(GdkEventButton* const& ev) {
if (!proxy_) {
return false;
}
auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x_root + bar_.x_global),
Glib::Variant<int>::create(ev->y_root + bar_.y_global)});
@@ -483,6 +582,9 @@ bool Item::handleClick(GdkEventButton* const& ev) {
}
bool Item::handleScroll(GdkEventScroll* const& ev) {
if (!proxy_) {
return false;
}
int dx = 0, dy = 0;
switch (ev->direction) {
case GDK_SCROLL_UP:
+4 -1
View File
@@ -13,7 +13,8 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
box_(bar.orientation, 0),
watcher_(SNI::Watcher::getInstance()),
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1),
std::bind(&Tray::onRemove, this, std::placeholders::_1)) {
std::bind(&Tray::onRemove, this, std::placeholders::_1),
std::bind(&Tray::queueUpdate, this)) {
box_.set_name("tray");
event_box_.add(box_);
if (!id.empty()) {
@@ -33,6 +34,8 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
dp.emit();
}
void Tray::queueUpdate() { dp.emit(); }
void Tray::onAdd(std::unique_ptr<Item>& item) {
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
box_.pack_end(item->event_box);
+6 -5
View File
@@ -31,7 +31,7 @@ Watcher::~Watcher() {
void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() {
waybar::util::ScopeGuard error_deleter([&error]() {
if (error) {
g_error_free(error);
}
@@ -69,7 +69,7 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca
if (watch != nullptr) {
g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
bus_name, object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation);
sn_watcher_complete_register_host(obj->watcher_, invocation);
return TRUE;
}
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj);
@@ -98,8 +98,8 @@ gboolean Watcher::handleRegisterItem(Watcher* obj, GDBusMethodInvocation* invoca
}
auto watch = gfWatchFind(obj->items_, bus_name, object_path);
if (watch != nullptr) {
g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered",
bus_name, object_path);
spdlog::debug("Ignoring duplicate Status Notifier Item registration for '{}' at '{}'", bus_name,
object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation);
return TRUE;
}
@@ -158,7 +158,7 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpoint
watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
if (watch->watcher->hosts_ == nullptr) {
sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE);
sn_watcher_emit_host_registered(watch->watcher->watcher_);
sn_watcher_emit_host_unregistered(watch->watcher->watcher_);
}
} else if (watch->type == GF_WATCH_TYPE_ITEM) {
watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch);
@@ -167,6 +167,7 @@ void Watcher::nameVanished(GDBusConnection* connection, const char* name, gpoint
sn_watcher_emit_item_unregistered(watch->watcher->watcher_, tmp);
g_free(tmp);
}
gfWatchFree(watch);
}
void Watcher::updateRegisteredItems(SnWatcher* obj) {

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