Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Ansari
2026-05-27 12:13:51 +05:30
127 changed files with 2290 additions and 835 deletions
+1
View File
@@ -52,3 +52,4 @@ result-*
.ccls-cache .ccls-cache
_codeql_detected_source_root _codeql_detected_source_root
heaptrack*
+1 -1
View File
@@ -48,7 +48,7 @@ class AModule : public IModule {
virtual bool handleMouseLeave(GdkEventCrossing* const& ev); virtual bool handleMouseLeave(GdkEventCrossing* const& ev);
virtual bool handleScroll(GdkEventScroll*); virtual bool handleScroll(GdkEventScroll*);
virtual bool handleRelease(GdkEventButton* const& ev); virtual bool handleRelease(GdkEventButton* const& ev);
GObject* menu_; GObject* menu_ = nullptr;
private: private:
bool handleUserEvent(GdkEventButton* const& ev); bool handleUserEvent(GdkEventButton* const& ev);
+1
View File
@@ -30,6 +30,7 @@ class Group : public AModule {
bool handleMouseEnter(GdkEventCrossing* const& ev) override; bool handleMouseEnter(GdkEventCrossing* const& ev) override;
bool handleMouseLeave(GdkEventCrossing* const& ev) override; bool handleMouseLeave(GdkEventCrossing* const& ev) override;
bool handleToggle(GdkEventButton* const& ev) override; bool handleToggle(GdkEventButton* const& ev) override;
bool handleScroll(GdkEventScroll* e) override;
void show_group(); void show_group();
void hide_group(); void hide_group();
}; };
+1
View File
@@ -35,6 +35,7 @@ class Custom : public ALabel {
std::string id_; std::string id_;
std::string alt_; std::string alt_;
std::string tooltip_; std::string tooltip_;
std::string last_tooltip_markup_;
const bool tooltip_format_enabled_; const bool tooltip_format_enabled_;
std::vector<std::string> class_; std::vector<std::string> class_;
int percentage_; int percentage_;
+1 -1
View File
@@ -22,7 +22,7 @@ class Disk : public ALabel {
std::string path_; std::string path_;
std::string unit_; std::string unit_;
float calc_specific_divisor(const std::string divisor); float calc_specific_divisor(const std::string& divisor);
}; };
} // namespace waybar::modules } // namespace waybar::modules
+1 -1
View File
@@ -26,7 +26,7 @@ class Gamemode : public AModule {
const std::string DEFAULT_FORMAT = "{glyph}"; const std::string DEFAULT_FORMAT = "{glyph}";
const std::string DEFAULT_FORMAT_ALT = "{glyph} {count}"; const std::string DEFAULT_FORMAT_ALT = "{glyph} {count}";
const std::string DEFAULT_TOOLTIP_FORMAT = "Games running: {count}"; const std::string DEFAULT_TOOLTIP_FORMAT = "Games running: {count}";
const std::string DEFAULT_GLYPH = ""; const std::string DEFAULT_GLYPH = "󰊴";
void appear(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name, void appear(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name,
const Glib::ustring& name_owner); const Glib::ustring& name_owner);
+19 -3
View File
@@ -1,8 +1,10 @@
#pragma once #pragma once
#include <atomic>
#include <filesystem> #include <filesystem>
#include <list> #include <list>
#include <mutex> #include <mutex>
#include <optional>
#include <string> #include <string>
#include <thread> #include <thread>
#include <utility> #include <utility>
@@ -34,19 +36,33 @@ class IPC {
Json::Value getSocket1JsonReply(const std::string& rq); Json::Value getSocket1JsonReply(const std::string& rq);
static std::filesystem::path getSocketFolder(const char* instanceSig); static std::filesystem::path getSocketFolder(const char* instanceSig);
/// Dispatch a Hyprland command. Automatically uses the correct protocol
/// (legacy text or Lua-based) depending on the running Hyprland version.
static std::string dispatch(const std::string& dispatcher, const std::string& arg);
/// Build a Lua-format dispatch command string.
static std::string buildLuaDispatch(const std::string& dispatcher, const std::string& arg);
protected: protected:
static std::filesystem::path socketFolder_; static std::filesystem::path socketFolder_;
/// Detect whether the running Hyprland uses the Lua-based IPC protocol.
/// Returns true for Hyprland >= 0.54 (Lua config), false for older versions.
static bool isLuaProtocol();
static std::optional<bool> s_luaProtocolDetected_; // cached detection result
private: private:
void socketListener(); void socketListener();
void parseIPC(const std::string&); void parseIPC(const std::string&);
std::thread ipcThread_; std::thread ipcThread_;
std::mutex callbackMutex_; std::mutex callbackMutex_;
std::mutex socketMutex_;
util::JsonParser parser_; util::JsonParser parser_;
std::list<std::pair<std::string, EventHandler*>> callbacks_; std::list<std::pair<std::string, EventHandler*>> callbacks_;
int socketfd_; // the hyprland socket file descriptor int socketfd_ = -1; // the hyprland socket file descriptor
pid_t socketOwnerPid_; pid_t socketOwnerPid_ = -1;
bool running_ = true; // the ipcThread will stop running when this is false std::atomic<bool> running_ = true; // the ipcThread will stop running when this is false
}; };
}; // namespace waybar::modules::hyprland }; // namespace waybar::modules::hyprland
+11 -11
View File
@@ -20,8 +20,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
private: private:
struct Workspace { struct Workspace {
int id; int id = 0;
int windows; int windows = 0;
std::string last_window; std::string last_window;
std::string last_window_title; std::string last_window_title;
@@ -29,14 +29,14 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
}; };
struct WindowData { struct WindowData {
bool floating; bool floating = false;
int monitor = -1; int monitor = -1;
std::string class_name; std::string class_name;
std::string initial_class_name; std::string initial_class_name;
std::string title; std::string title;
std::string initial_title; std::string initial_title;
bool fullscreen; bool fullscreen = false;
bool grouped; bool grouped = false;
static auto parse(const Json::Value&) -> WindowData; static auto parse(const Json::Value&) -> WindowData;
}; };
@@ -47,7 +47,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
void queryActiveWorkspace(); void queryActiveWorkspace();
void setClass(const std::string&, bool enable); void setClass(const std::string&, bool enable);
bool separateOutputs_; bool separateOutputs_ = false;
std::mutex mutex_; std::mutex mutex_;
const Bar& bar_; const Bar& bar_;
util::JsonParser parser_; util::JsonParser parser_;
@@ -55,11 +55,11 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
Workspace workspace_; Workspace workspace_;
std::string soloClass_; std::string soloClass_;
std::string lastSoloClass_; std::string lastSoloClass_;
bool solo_; bool solo_ = false;
bool allFloating_; bool allFloating_ = false;
bool swallowing_; bool swallowing_ = false;
bool fullscreen_; bool fullscreen_ = false;
bool focused_; bool focused_ = false;
IPC& m_ipc; IPC& m_ipc;
}; };
@@ -40,10 +40,11 @@ struct WindowRepr {
class WindowCreationPayload { class WindowCreationPayload {
public: public:
WindowCreationPayload(std::string workspace_name, WindowAddress window_address, WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
WindowRepr window_repr); WindowRepr window_repr);
WindowCreationPayload(std::string workspace_name, WindowAddress window_address, WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
std::string window_class, std::string window_title, bool is_active); const std::string& window_class, const std::string& window_title,
bool is_active);
WindowCreationPayload(Json::Value const& client_data); WindowCreationPayload(Json::Value const& client_data);
int incrementTimeSpentUncreated(); int incrementTimeSpentUncreated();
+7 -1
View File
@@ -4,6 +4,7 @@
#include <gtkmm/enums.h> #include <gtkmm/enums.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <json/value.h> #include <json/value.h>
#include <sigc++/connection.h>
#include <cstdint> #include <cstdint>
#include <map> #include <map>
@@ -43,6 +44,7 @@ class Workspaces : public AModule, public EventHandler {
auto moveToMonitor() const -> bool { return m_moveToMonitor; } auto moveToMonitor() const -> bool { return m_moveToMonitor; }
auto enableTaskbar() const -> bool { return m_enableTaskbar; } auto enableTaskbar() const -> bool { return m_enableTaskbar; }
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; } auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
auto barScroll() const -> bool { return m_barScroll; }
auto getBarOutput() const -> std::string { return m_bar.output->name; } auto getBarOutput() const -> std::string { return m_bar.output->name; }
auto formatBefore() const -> std::string { return m_formatBefore; } auto formatBefore() const -> std::string { return m_formatBefore; }
@@ -58,7 +60,7 @@ class Workspaces : public AModule, public EventHandler {
enum class ActiveWindowPosition { NONE, FIRST, LAST }; enum class ActiveWindowPosition { NONE, FIRST, LAST };
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; } 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; } std::string& getWindowSeparator() { return m_formatWindowSeparator; }
bool isWorkspaceIgnored(std::string const& workspace_name); 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::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
static std::tuple<std::string, std::string, std::string> splitTriplePayload( static std::tuple<std::string, std::string, std::string> splitTriplePayload(
std::string const& payload); std::string const& payload);
// scroll events
bool handleScroll(GdkEventScroll* e) override;
// Update methods // Update methods
void doUpdate(); void doUpdate();
@@ -145,6 +149,7 @@ class Workspaces : public AModule, public EventHandler {
bool m_specialVisibleOnly = false; bool m_specialVisibleOnly = false;
bool m_persistentOnly = false; bool m_persistentOnly = false;
bool m_moveToMonitor = false; bool m_moveToMonitor = false;
bool m_barScroll = false;
Json::Value m_persistentWorkspaceConfig; Json::Value m_persistentWorkspaceConfig;
// Map for windows stored in workspaces not present in the current bar. // Map for windows stored in workspaces not present in the current bar.
@@ -204,6 +209,7 @@ class Workspaces : public AModule, public EventHandler {
std::mutex m_mutex; std::mutex m_mutex;
const Bar& m_bar; const Bar& m_bar;
Gtk::Box m_box; Gtk::Box m_box;
sigc::connection m_scrollEventConnection_;
IPC& m_ipc; IPC& m_ipc;
}; };
+2
View File
@@ -3,6 +3,7 @@
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <mutex>
#include <set> #include <set>
#include <unordered_map> #include <unordered_map>
@@ -41,6 +42,7 @@ class KeyboardState : public AModule {
struct libinput* libinput_; struct libinput* libinput_;
std::unordered_map<std::string, struct libinput_device*> libinput_devices_; std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
std::mutex devices_mutex_; // protects libinput_devices_
std::set<int> binding_keys; std::set<int> binding_keys;
util::SleeperThread libinput_thread_, hotplug_thread_; util::SleeperThread libinput_thread_, hotplug_thread_;
+4
View File
@@ -19,9 +19,13 @@ class Memory : public ALabel {
private: private:
void parseMeminfo(); void parseMeminfo();
static float calc_divisor(const std::string& divisor);
std::unordered_map<std::string, unsigned long> meminfo_; std::unordered_map<std::string, unsigned long> meminfo_;
util::SleeperThread thread_; util::SleeperThread thread_;
std::string unit_;
}; };
} // namespace waybar::modules } // namespace waybar::modules
+1 -1
View File
@@ -44,7 +44,7 @@ class MPD : public ALabel {
std::string getFilename() const; std::string getFilename() const;
void setLabel(); void setLabel();
std::string getStateIcon() const; 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 // GUI-side methods
bool handlePlayPause(GdkEventButton* const&); bool handlePlayPause(GdkEventButton* const&);
+1
View File
@@ -78,6 +78,7 @@ class Mpris : public ALabel {
PlayerctlPlayerManager* manager; PlayerctlPlayerManager* manager;
PlayerctlPlayer* player; PlayerctlPlayer* player;
PlayerctlPlayer* last_active_player_ = nullptr;
std::string lastStatus; std::string lastStatus;
std::string lastPlayer; std::string lastPlayer;
+3
View File
@@ -70,6 +70,9 @@ class Network : public ALabel {
unsigned long long bandwidth_down_total_{0}; unsigned long long bandwidth_down_total_{0};
unsigned long long bandwidth_up_total_{0}; unsigned long long bandwidth_up_total_{0};
unsigned long long bandwidth_down_prev_{0};
unsigned long long bandwidth_up_prev_{0};
std::chrono::steady_clock::time_point bandwidth_last_sample_time_;
std::string state_; std::string state_;
std::string essid_; std::string essid_;
+1 -1
View File
@@ -17,7 +17,7 @@ class EventHandler {
class IPC { class IPC {
public: public:
IPC() { startIPC(); } IPC();
void registerForIPC(const std::string& ev, EventHandler* ev_handler); void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler); void unregisterForIPC(EventHandler* handler);
+7 -2
View File
@@ -16,7 +16,7 @@ class Host {
public: public:
Host(const std::size_t id, const Json::Value&, const Bar&, 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(std::unique_ptr<Item>&)>&, const std::function<void()>&);
~Host(); ~Host();
private: private:
@@ -28,9 +28,13 @@ class Host {
static void registerHost(GObject*, GAsyncResult*, gpointer); static void registerHost(GObject*, GAsyncResult*, gpointer);
static void itemRegistered(SnWatcher*, const gchar*, gpointer); static void itemRegistered(SnWatcher*, const gchar*, gpointer);
static void itemUnregistered(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); 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_; std::vector<std::unique_ptr<Item>> items_;
const std::string bus_name_; const std::string bus_name_;
@@ -43,6 +47,7 @@ class Host {
const Bar& bar_; const Bar& bar_;
const std::function<void(std::unique_ptr<Item>&)> on_add_; 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_;
}; };
} // namespace waybar::modules::SNI } // namespace waybar::modules::SNI
+21 -2
View File
@@ -11,6 +11,7 @@
#include <libdbusmenu-gtk/dbusmenu-gtk.h> #include <libdbusmenu-gtk/dbusmenu-gtk.h>
#include <sigc++/trackable.h> #include <sigc++/trackable.h>
#include <functional>
#include <set> #include <set>
#include <string_view> #include <string_view>
@@ -25,9 +26,13 @@ struct ToolTip {
class Item : public sigc::trackable { class Item : public sigc::trackable {
public: 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(); ~Item();
bool isReady() const;
std::string bus_name; std::string bus_name;
std::string object_path; std::string object_path;
@@ -43,7 +48,9 @@ class Item : public sigc::trackable {
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap; Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
Glib::RefPtr<Gtk::IconTheme> icon_theme; Glib::RefPtr<Gtk::IconTheme> icon_theme;
std::string overlay_icon_name; std::string overlay_icon_name;
Glib::RefPtr<Gdk::Pixbuf> overlay_icon_pixmap;
std::string attention_icon_name; std::string attention_icon_name;
Glib::RefPtr<Gdk::Pixbuf> attention_icon_pixmap;
std::string attention_movie_name; std::string attention_movie_name;
std::string icon_theme_path; std::string icon_theme_path;
std::string menu; std::string menu;
@@ -62,6 +69,8 @@ class Item : public sigc::trackable {
void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result); void proxyReady(Glib::RefPtr<Gio::AsyncResult>& result);
void setProperty(const Glib::ustring& name, Glib::VariantBase& value); void setProperty(const Glib::ustring& name, Glib::VariantBase& value);
void setStatus(const Glib::ustring& value); void setStatus(const Glib::ustring& value);
void setReady();
void invalidate();
void setCustomIcon(const std::string& id); void setCustomIcon(const std::string& id);
void getUpdatedProperties(); void getUpdatedProperties();
void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result); void processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& result);
@@ -69,8 +78,13 @@ class Item : public sigc::trackable {
const Glib::VariantContainerBase& arguments); const Glib::VariantContainerBase& arguments);
void updateImage(); 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> 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); Glib::RefPtr<Gdk::Pixbuf> getIconByName(const std::string& name, int size);
double getScaledIconSize(); double getScaledIconSize();
static void onMenuDestroyed(Item* self, GObject* old_menu_pointer); static void onMenuDestroyed(Item* self, GObject* old_menu_pointer);
@@ -86,8 +100,13 @@ class Item : public sigc::trackable {
gdouble distance_scrolled_y_ = 0; gdouble distance_scrolled_y_ = 0;
// visibility of items with Status == Passive // visibility of items with Status == Passive
bool show_passive_ = false; bool show_passive_ = false;
bool ready_ = false;
Glib::ustring status_ = "active";
const Bar& bar_; 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::DBus::Proxy> proxy_;
Glib::RefPtr<Gio::Cancellable> cancellable_; Glib::RefPtr<Gio::Cancellable> cancellable_;
+1
View File
@@ -19,6 +19,7 @@ class Tray : public AModule {
private: private:
void onAdd(std::unique_ptr<Item>& item); void onAdd(std::unique_ptr<Item>& item);
void onRemove(std::unique_ptr<Item>& item); void onRemove(std::unique_ptr<Item>& item);
void queueUpdate();
static inline std::size_t nb_hosts_ = 0; static inline std::size_t nb_hosts_ = 0;
bool show_passive_ = false; bool show_passive_ = false;
+1 -1
View File
@@ -37,7 +37,7 @@ class BarIpcClient {
void onModeUpdate(bool visible_by_modifier); void onModeUpdate(bool visible_by_modifier);
void onUrgencyUpdate(bool visible_by_urgency); void onUrgencyUpdate(bool visible_by_urgency);
void update(); void update();
bool isModuleEnabled(std::string name); bool isModuleEnabled(const std::string& name);
Bar& bar_; Bar& bar_;
util::JsonParser parser_; util::JsonParser parser_;
+6 -4
View File
@@ -12,6 +12,8 @@
#include <string> #include <string>
#include "ipc.hpp" #include "ipc.hpp"
#include "util/SafeSignal.hpp"
#include "util/scoped_fd.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
namespace waybar::modules::sway { namespace waybar::modules::sway {
@@ -27,8 +29,8 @@ class Ipc {
std::string payload; std::string payload;
}; };
sigc::signal<void, const struct ipc_response&> signal_event; ::waybar::SafeSignal<const struct ipc_response&> signal_event;
sigc::signal<void, const struct ipc_response&> signal_cmd; ::waybar::SafeSignal<const struct ipc_response&> signal_cmd;
void sendCmd(uint32_t type, const std::string& payload = ""); void sendCmd(uint32_t type, const std::string& payload = "");
void subscribe(const std::string& payload); void subscribe(const std::string& payload);
@@ -44,8 +46,8 @@ class Ipc {
struct ipc_response send(int fd, uint32_t type, const std::string& payload = ""); struct ipc_response send(int fd, uint32_t type, const std::string& payload = "");
struct ipc_response recv(int fd); struct ipc_response recv(int fd);
int fd_; util::ScopedFd fd_;
int fd_event_; util::ScopedFd fd_event_;
std::mutex mutex_; std::mutex mutex_;
util::SleeperThread thread_; 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 onEvent(const struct Ipc::ipc_response&);
void onCmd(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; auto init_layouts_map(const std::vector<std::string>& used_layouts) -> void;
const static std::string XKB_LAYOUT_NAMES_KEY; 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_ = static constexpr std::string_view persistent_workspace_switch_cmd_ =
R"(workspace {} "{}"; move workspace to output "{}"; workspace {} "{}")"; 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); static int windowRewritePriorityFunction(std::string const& window_rule);
void onCmd(const struct Ipc::ipc_response&); 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 getIcon(const std::string&, const Json::Value&);
std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const; std::string getCycleWorkspace(std::vector<Json::Value>::iterator, bool prev) const;
uint16_t getWorkspaceIndex(const std::string& name) 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; bool handleScroll(GdkEventScroll* /*unused*/) override;
const Bar& bar_; const Bar& bar_;
+28 -8
View File
@@ -3,6 +3,7 @@
#include <giomm/dbusproxy.h> #include <giomm/dbusproxy.h>
#include <string> #include <string>
#include <vector>
#include "ALabel.hpp" #include "ALabel.hpp"
@@ -11,23 +12,42 @@ namespace waybar::modules {
class SystemdFailedUnits : public ALabel { class SystemdFailedUnits : public ALabel {
public: public:
SystemdFailedUnits(const std::string&, const Json::Value&); SystemdFailedUnits(const std::string&, const Json::Value&);
virtual ~SystemdFailedUnits(); virtual ~SystemdFailedUnits() = default;
auto update() -> void override; auto update() -> void override;
private: private:
bool hide_on_ok; struct FailedUnit {
std::string format_ok; std::string name;
std::string description;
std::string load_state;
std::string active_state;
std::string sub_state;
std::string scope;
};
bool update_pending; bool hide_on_ok_;
std::string system_state, user_state, overall_state; std::string format_ok_;
uint32_t nr_failed_system, nr_failed_user, nr_failed; std::string tooltip_format_;
std::string last_status; std::string tooltip_format_ok_;
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy; std::string tooltip_unit_format_;
bool update_pending_;
std::string system_state_, user_state_, overall_state_;
uint32_t nr_failed_system_, nr_failed_user_, nr_failed_;
std::string last_status_;
Glib::RefPtr<Gio::DBus::Proxy> system_props_proxy_, user_props_proxy_;
Glib::RefPtr<Gio::DBus::Proxy> system_manager_proxy_, user_manager_proxy_;
std::vector<FailedUnit> failed_units_;
void notify_cb(const Glib::ustring& sender_name, const Glib::ustring& signal_name, void notify_cb(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
const Glib::VariantContainerBase& arguments); const Glib::VariantContainerBase& arguments);
void RequestFailedUnits(); void RequestFailedUnits();
void RequestFailedUnitsList();
void RequestSystemState(); void RequestSystemState();
std::vector<FailedUnit> LoadFailedUnitsList(const char* kind,
Glib::RefPtr<Gio::DBus::Proxy>& proxy,
const std::string& scope);
std::string BuildTooltipFailedList() const;
void updateData(); void updateData();
}; };
+5 -19
View File
@@ -12,6 +12,8 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include "util/scoped_fd.hpp"
namespace waybar::modules::wayfire { namespace waybar::modules::wayfire {
using EventHandler = std::function<void(const std::string& event)>; using EventHandler = std::function<void(const std::string& event)>;
@@ -71,25 +73,9 @@ struct State {
auto update_view(const Json::Value& view) -> void; auto update_view(const Json::Value& view) -> void;
}; };
struct Sock { using Sock = util::ScopedFd;
int fd;
Sock(int fd) : fd{fd} {} class IPC : public std::enable_shared_from_this<IPC> {
~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 {
static std::weak_ptr<IPC> instance; static std::weak_ptr<IPC> instance;
Json::CharReaderBuilder reader_builder; Json::CharReaderBuilder reader_builder;
Json::StreamWriterBuilder writer_builder; Json::StreamWriterBuilder writer_builder;
@@ -98,7 +84,7 @@ class IPC {
State state; State state;
std::mutex state_mutex; std::mutex state_mutex;
IPC() { start(); } IPC() = default;
static auto connect() -> Sock; static auto connect() -> Sock;
auto receive(Sock& sock) -> Json::Value; auto receive(Sock& sock) -> Json::Value;
+20
View File
@@ -3,6 +3,7 @@
#include <glibmm/dispatcher.h> #include <glibmm/dispatcher.h>
#include <sigc++/signal.h> #include <sigc++/signal.h>
#include <cstddef>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
@@ -27,6 +28,12 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
public: public:
SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); } 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> template <typename... EmitArgs>
void emit(EmitArgs&&... args) { void emit(EmitArgs&&... args) {
if (main_tid_ == std::this_thread::get_id()) { if (main_tid_ == std::this_thread::get_id()) {
@@ -41,6 +48,9 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
} else { } else {
{ {
std::unique_lock lock(mutex_); std::unique_lock lock(mutex_);
if (max_queued_events_ != 0 && queue_.size() >= max_queued_events_) {
queue_.pop();
}
queue_.emplace(std::forward<EmitArgs>(args)...); queue_.emplace(std::forward<EmitArgs>(args)...);
} }
dp_.emit(); dp_.emit();
@@ -60,6 +70,15 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
using signal_t::emit_reverse; using signal_t::emit_reverse;
using signal_t::make_slot; 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() { void handle_event() {
for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) {
auto args = queue_.front(); auto args = queue_.front();
@@ -72,6 +91,7 @@ struct SafeSignal : sigc::signal<void(std::decay_t<Args>...)> {
Glib::Dispatcher dp_; Glib::Dispatcher dp_;
std::mutex mutex_; std::mutex mutex_;
std::queue<arg_tuple_t> queue_; std::queue<arg_tuple_t> queue_;
std::size_t max_queued_events_ = 4096;
const std::thread::id main_tid_ = std::this_thread::get_id(); const std::thread::id main_tid_ = std::this_thread::get_id();
// cache functor for signal emission to avoid recreating it on each event // cache functor for signal emission to avoid recreating it on each event
const slot_t cached_fn_ = make_slot(); const slot_t cached_fn_ = make_slot();
+9 -2
View File
@@ -20,6 +20,8 @@ extern std::list<pid_t> reap;
namespace waybar::util::command { namespace waybar::util::command {
constexpr int kExecFailureExitCode = 127;
struct res { struct res {
int exit_code; int exit_code;
std::string out; std::string out;
@@ -59,6 +61,7 @@ inline int close(FILE* fp, pid_t pid) {
spdlog::debug("Cmd continued"); spdlog::debug("Cmd continued");
} else if (ret == -1) { } else if (ret == -1) {
spdlog::debug("waitpid failed: {}", strerror(errno)); spdlog::debug("waitpid failed: {}", strerror(errno));
break;
} else { } else {
break; break;
} }
@@ -113,7 +116,9 @@ inline FILE* open(const std::string& cmd, int& pid, const std::string& output_na
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1); setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
} }
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); 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 { } else {
::close(fd[1]); ::close(fd[1]);
} }
@@ -161,7 +166,9 @@ inline int32_t forkExec(const std::string& cmd, const std::string& output_name)
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1); setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
} }
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); 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 { } else {
reap_mtx.lock(); reap_mtx.lock();
reap.push_back(pid); reap.push_back(pid);
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <string>
/**
* Result of transforming 8-bit hex codes to rgba().
*/
struct TransformResult {
std::string css;
bool was_transformed;
};
/**
* Reads a CSS file, searches for 8-bit hex codes (#RRGGBBAA),
* and transforms them into GTK-compatible rgba() syntax.
*/
TransformResult transform_8bit_to_hex(const std::string& file_path);
+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 #pragma once
#include <atomic>
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>
#include <ctime> #include <ctime>
@@ -31,8 +32,8 @@ class SleeperThread {
SleeperThread(std::function<void()> func) SleeperThread(std::function<void()> func)
: thread_{[this, func] { : thread_{[this, func] {
while (do_run_) { while (do_run_.load(std::memory_order_relaxed)) {
signal_ = false; signal_.store(false, std::memory_order_relaxed);
func(); func();
} }
}} { }} {
@@ -42,9 +43,18 @@ class SleeperThread {
} }
SleeperThread& operator=(std::function<void()> func) { 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] { thread_ = std::thread([this, func] {
while (do_run_) { while (do_run_.load(std::memory_order_relaxed)) {
signal_ = false; signal_.store(false, std::memory_order_relaxed);
func(); func();
} }
}); });
@@ -56,12 +66,14 @@ class SleeperThread {
return *this; return *this;
} }
bool isRunning() const { return do_run_; } bool isRunning() const { return do_run_.load(std::memory_order_relaxed); }
auto sleep() { auto sleep() {
std::unique_lock lk(mutex_); std::unique_lock lk(mutex_);
CancellationGuard cancel_lock; 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) { auto sleep_for(std::chrono::system_clock::duration dur) {
@@ -73,7 +85,9 @@ class SleeperThread {
if (now < max_time_point - dur) { if (now < max_time_point - dur) {
wait_end = now + 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( auto sleep_until(
@@ -81,22 +95,24 @@ class SleeperThread {
time_point) { time_point) {
std::unique_lock lk(mutex_); std::unique_lock lk(mutex_);
CancellationGuard cancel_lock; 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() { void wake_up() {
{ {
std::lock_guard<std::mutex> lck(mutex_); std::lock_guard<std::mutex> lck(mutex_);
signal_ = true; signal_.store(true, std::memory_order_relaxed);
} }
condvar_.notify_all(); condvar_.notify_all();
} }
auto stop() { void stop() {
{ {
std::lock_guard<std::mutex> lck(mutex_); std::lock_guard<std::mutex> lck(mutex_);
signal_ = true; signal_.store(true, std::memory_order_relaxed);
do_run_ = false; do_run_.store(false, std::memory_order_relaxed);
} }
condvar_.notify_all(); condvar_.notify_all();
auto handle = thread_.native_handle(); auto handle = thread_.native_handle();
@@ -118,8 +134,8 @@ class SleeperThread {
std::thread thread_; std::thread thread_;
std::condition_variable condvar_; std::condition_variable condvar_;
std::mutex mutex_; std::mutex mutex_;
bool do_run_ = true; std::atomic<bool> do_run_ = true;
bool signal_ = false; std::atomic<bool> signal_ = false;
sigc::connection connection_; 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>-<state>*
- *on-<status>-<capacity>* - *on-<status>-<capacity>*
- *on-<status>*
Where: Where:
@@ -203,7 +204,9 @@ Where:
"events": { "events": {
"on-discharging-warning": "notify-send -u normal 'Low Battery'", "on-discharging-warning": "notify-send -u normal 'Low Battery'",
"on-discharging-critical": "notify-send -u critical 'Very Low Battery'", "on-discharging-critical": "notify-send -u critical 'Very Low Battery'",
"on-charging-100": "notify-send -u normal 'Battery Full!'" "on-charging-100": "notify-send -u normal 'Battery Full!'",
"on-discharging": "notify-send -u normal 'Power Switch' Discharging",
"on-charging": "notify-send -u normal 'Power Switch' Charging'"
}, },
"format": "{capacity}% {icon}", "format": "{capacity}% {icon}",
"format-icons": ["", "", "", "", ""], "format-icons": ["", "", "", "", ""],
+14 -2
View File
@@ -204,7 +204,7 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
``` ```
"clock": { "clock": {
"format": "{:%H:%M}  ", "format": "{:%H:%M}  ",
"format-alt": "{:%A, %B %d, %Y (%R)} ", "format-alt": "{:%A, %B %d, %Y (%R)} 󰃰 ",
"tooltip-format": "<tt><small>{calendar}</small></tt>", "tooltip-format": "<tt><small>{calendar}</small></tt>",
"calendar": { "calendar": {
"mode" : "year", "mode" : "year",
@@ -259,7 +259,19 @@ View all valid format options in *strftime(3)* or have a look https://en.cpprefe
"tooltip-format": "{tz_list}" "tooltip-format": "{tz_list}"
} }
``` ```
5. Simple calendar tooltip
```
"clock": {
"format": "{:%H:%M}",
"tooltip-format": "<tt>{calendar}</tt>",
"calendar": {
"format": {
"today": "<span color='#ffcc66'><b>{}</b></span>"
}
}
}
```
# STYLE # STYLE
- *#clock* - *#clock*
@@ -287,7 +299,7 @@ Example of working config
``` ```
"clock": { "clock": {
"format": "{:%H:%M}  ", "format": "{:%H:%M}  ",
"format-alt": "{:%A, %B %d, %Y (%R)} ", "format-alt": "{:%A, %B %d, %Y (%R)} 󰃰 ",
"tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>", "tooltip-format": "\n<span size='9pt' font='WenQuanYi Zen Hei Mono'>{calendar}</span>",
"calendar": { "calendar": {
"mode" : "year", "mode" : "year",
+2 -2
View File
@@ -43,7 +43,7 @@ Feral Gamemode optimizations.
*glyph*: ++ *glyph*: ++
typeof: string ++ typeof: string ++
default: ++ default: 󰊴 ++
The string icon to display. Only visible if *use-icon* is set to false. The string icon to display. Only visible if *use-icon* is set to false.
*icon-name*: ++ *icon-name*: ++
@@ -82,7 +82,7 @@ Feral Gamemode optimizations.
"gamemode": { "gamemode": {
"format": "{glyph}", "format": "{glyph}",
"format-alt": "{glyph} {count}", "format-alt": "{glyph} {count}",
"glyph": "", "glyph": "󰊴",
"hide-not-running": true, "hide-not-running": true,
"use-icon": true, "use-icon": true,
"icon-name": "input-gaming-symbolic", "icon-name": "input-gaming-symbolic",
+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. Otherwise, the workspace will open on the monitor where it was previously assigned.
Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland. Analog to using `focusworkspaceoncurrentmonitor` dispatcher instead of `workspace` in Hyprland.
*enable-bar-scroll*: ++
typeof: bool ++
default: false ++
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
*ignore-workspaces*: ++ *ignore-workspaces*: ++
typeof: array ++ typeof: array ++
default: [] ++ default: [] ++
+12 -6
View File
@@ -102,23 +102,29 @@ Addressed by *memory*
default: false ++ default: false ++
Enables this module to consume all left over space dynamically. Enables this module to consume all left over space dynamically.
*unit*: ++
typeof: string ++
default: GiB ++
Used to specify unit for total, swapTotal, used, swapUsed, avail, swapAvail,
and swapState. Accepts B, kB, kiB, MB, MiB, GB, GiB, TB, and TiB.
# FORMAT REPLACEMENTS # FORMAT REPLACEMENTS
*{percentage}*: Percentage of memory in use. *{percentage}*: Percentage of memory in use.
*{swapPercentage}*: Percentage of swap in use. *{swapPercentage}*: Percentage of swap in use.
*{total}*: Amount of total memory available in GiB. *{total}*: Amount of total memory available. Defaults to GiB.
*{swapTotal}*: Amount of total swap available in GiB. *{swapTotal}*: Amount of total swap available. Defaults to GiB.
*{used}*: Amount of used memory in GiB. *{used}*: Amount of used memory. Defaults to GiB.
*{swapUsed}*: Amount of used swap in GiB. *{swapUsed}*: Amount of used swap. Defaults to GiB.
*{avail}*: Amount of available memory in GiB. *{avail}*: Amount of available memory. Defaults to GiB.
*{swapAvail}*: Amount of available swap in GiB. *{swapAvail}*: Amount of available swap. Defaults to GiB.
*{swapState}*: Signals if swap is activated or not *{swapState}*: Signals if swap is activated or not
+2 -2
View File
@@ -178,8 +178,8 @@ to be selected when the corresponding audio device is muted. This applies to *de
"alsa_output.pci-0000_00_1f.3.analog-stereo": "", "alsa_output.pci-0000_00_1f.3.analog-stereo": "",
"alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "", "alsa_output.pci-0000_00_1f.3.analog-stereo-muted": "",
"headphone": "", "headphone": "",
"hands-free": "", "hands-free": "󰂑",
"headset": "", "headset": "󰂑",
"phone": "", "phone": "",
"phone-muted": "", "phone-muted": "",
"portable": "", "portable": "",
+1 -1
View File
@@ -87,7 +87,7 @@ Addressed by *river/mode*
``` ```
"river/mode": { "river/mode": {
"format": " {}" "format": " {}"
} }
``` ```
+1 -1
View File
@@ -92,7 +92,7 @@ Addressed by *sway/mode*
``` ```
"sway/mode": { "sway/mode": {
"format": " {}", "format": " {}",
"max-length": 50 "max-length": 50
} }
``` ```
+36 -7
View File
@@ -19,7 +19,7 @@ Addressed by *systemd-failed-units*
*format-ok*: ++ *format-ok*: ++
typeof: string ++ typeof: string ++
This format is used when there is no failing units. This format is used when there are no failing units.
*user*: ++ *user*: ++
typeof: bool ++ typeof: bool ++
@@ -34,15 +34,30 @@ Addressed by *systemd-failed-units*
*hide-on-ok*: ++ *hide-on-ok*: ++
typeof: bool ++ typeof: bool ++
default: *true* ++ default: *true* ++
Option to hide this module when there is no failing units. Option to hide this module when there are no failed units.
*tooltip-format*: ++
typeof: string ++
default: *System: {system_state}\nUser: {user_state}\nFailed units ({nr_failed}):\n{failed_units_list}* ++
Tooltip format shown when there are failed units.
*tooltip-format-ok*: ++
typeof: string ++
default: *System: {system_state}\nUser: {user_state}* ++
Tooltip format used when there are no failed units.
*tooltip-unit-format*: ++
typeof: string ++
default: *{name}: {description}* ++
Format used to render each failed unit inside the tooltip. Each item is prefixed with a bullet.
*menu*: ++ *menu*: ++
typeof: string ++ typeof: string ++
Action that popups the menu. Action that pops up the menu.
*menu-file*: ++ *menu-file*: ++
typeof: string ++ typeof: string ++
Location of the menu descriptor file. There need to be an element of type Location of the menu descriptor file. There needs to be an element of type
GtkMenu with id *menu* GtkMenu with id *menu*
*menu-actions*: ++ *menu-actions*: ++
@@ -62,11 +77,23 @@ Addressed by *systemd-failed-units*
*{nr_failed}*: Number of total failed units. *{nr_failed}*: Number of total failed units.
*{systemd_state}:* State of the systemd system session *{system_state}:* State of the systemd system session.
*{user_state}:* State of the systemd user session *{user_state}:* State of the systemd user session.
*{overall_state}:* Overall state of the systemd and user session. ("Ok" or "Degraded") *{overall_state}:* Overall state of the systemd and user session. ("ok" or "degraded")
*{failed_units_list}:* Bulleted list of failed units using *tooltip-unit-format*. Empty when
there are no failed units.
The *tooltip-unit-format* string supports the following replacements:
*{name}*: Unit name ++
*{description}*: Unit description ++
*{load_state}*: Unit load state ++
*{active_state}*: Unit active state ++
*{sub_state}*: Unit sub state ++
*{scope}*: Either *system* or *user* depending on where the unit originated
# EXAMPLES # EXAMPLES
@@ -77,6 +104,8 @@ Addressed by *systemd-failed-units*
"format-ok": "✓", "format-ok": "✓",
"system": true, "system": true,
"user": false, "user": false,
"tooltip-format": "{nr_failed} failed units:\n{failed_units_list}",
"tooltip-unit-format": "{scope}: {name} ({active_state})",
} }
``` ```
+1 -1
View File
@@ -128,7 +128,7 @@ The *wireplumber* module displays the current volume reported by WirePlumber.
``` ```
"wireplumber#sink": { "wireplumber#sink": {
"format": "{volume}% {icon}", "format": "{volume}% {icon}",
"format-muted": "", "format-muted": "󰅶",
"format-icons": ["", "", ""], "format-icons": ["", "", ""],
"on-click": "helvum", "on-click": "helvum",
"on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", "on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
+6 -1
View File
@@ -36,7 +36,7 @@ The visual display elements for waybar use a CSS stylesheet, see *waybar-styles(
*expand-right* ++ *expand-right* ++
typeof: bool ++ typeof: bool ++
default: false ++ default: false ++
Enables the modules-left to consume all left over space dynamically. Enables the modules-right to consume all left over space dynamically.
*layer* ++ *layer* ++
typeof: string ++ typeof: string ++
@@ -363,6 +363,11 @@ A group may hide all but one element, showing them only on mouse hover. In order
default: false ++ default: false ++
Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events. Whether left click should reveal the content rather than mouse over. Note that grouped modules may still process their own on-click events.
*start-expanded*: ++
typeof: bool ++
default: false ++
Defines whether the drawer should initialize in an expanded state.
*transition-left-to-right*: ++ *transition-left-to-right*: ++
typeof: bool ++ typeof: bool ++
default: true ++ default: true ++
+2 -1
View File
@@ -185,7 +185,8 @@ src_files = files(
'src/util/gtk_icon.cpp', 'src/util/gtk_icon.cpp',
'src/util/icon_loader.cpp', 'src/util/icon_loader.cpp',
'src/util/regex_collection.cpp', 'src/util/regex_collection.cpp',
'src/util/css_reload_helper.cpp' 'src/util/css_reload_helper.cpp',
'src/util/transform_8bit_to_rgba.cpp'
) )
man_files = files( man_files = files(
+3 -3
View File
@@ -6,13 +6,13 @@
}: }:
let let
libcava = rec { libcava = rec {
version = "0.10.7-beta"; version = "0.10.7";
src = pkgs.fetchFromGitHub { src = pkgs.fetchFromGitHub {
owner = "LukashonakV"; owner = "LukashonakV";
repo = "cava"; repo = "cava";
# NOTE: Needs to match the cava.wrap # NOTE: Needs to match the cava.wrap
tag = "v${version}"; tag = "${version}";
hash = "sha256-IX1B375gTwVDRjpRfwKGuzTAZOV2pgDWzUd4bW2cTDU="; hash = "sha256-zkyj1vBzHtoypX4Bxdh1Vmwh967DKKxN751v79hzmgQ=";
}; };
}; };
in in
+9 -9
View File
@@ -128,7 +128,7 @@
"critical-threshold": 80, "critical-threshold": 80,
// "format-critical": "{temperatureC}°C {icon}", // "format-critical": "{temperatureC}°C {icon}",
"format": "{temperatureC}°C {icon}", "format": "{temperatureC}°C {icon}",
"format-icons": ["", "", ""] "format-icons": ["󰉬", "", "󰉪"]
}, },
"backlight": { "backlight": {
// "device": "acpi_video1", // "device": "acpi_video1",
@@ -143,7 +143,7 @@
}, },
"format": "{capacity}% {icon}", "format": "{capacity}% {icon}",
"format-full": "{capacity}% {icon}", "format-full": "{capacity}% {icon}",
"format-charging": "{capacity}% ", "format-charging": "{capacity}% 󰃨",
"format-plugged": "{capacity}% ", "format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}", "format-alt": "{time} {icon}",
// "format-good": "", // An empty format will hide the module // "format-good": "", // An empty format will hide the module
@@ -167,9 +167,9 @@
"network": { "network": {
// "interface": "wlp2*", // (Optional) To force the use of this interface // "interface": "wlp2*", // (Optional) To force the use of this interface
"format-wifi": "{essid} ({signalStrength}%) ", "format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ipaddr}/{cidr} ", "format-ethernet": "{ipaddr}/{cidr} 󰊗",
"tooltip-format": "{ifname} via {gwaddr} ", "tooltip-format": "{ifname} via {gwaddr} 󰊗",
"format-linked": "{ifname} (No IP) ", "format-linked": "{ifname} (No IP) 󰊗",
"format-disconnected": "Disconnected ⚠", "format-disconnected": "Disconnected ⚠",
"format-alt": "{ifname}: {ipaddr}/{cidr}" "format-alt": "{ifname}: {ipaddr}/{cidr}"
}, },
@@ -177,14 +177,14 @@
// "scroll-step": 1, // %, can be a float // "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon} {format_source}", "format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon} {format_source}", "format-bluetooth": "{volume}% {icon} {format_source}",
"format-bluetooth-muted": " {icon} {format_source}", "format-bluetooth-muted": "󰅶 {icon} {format_source}",
"format-muted": " {format_source}", "format-muted": "󰅶 {format_source}",
"format-source": "{volume}% ", "format-source": "{volume}% ",
"format-source-muted": "", "format-source-muted": "",
"format-icons": { "format-icons": {
"headphone": "", "headphone": "",
"hands-free": "", "hands-free": "󰂑",
"headset": "", "headset": "󰂑",
"phone": "", "phone": "",
"portable": "", "portable": "",
"car": "", "car": "",
+11 -6
View File
@@ -33,9 +33,7 @@ std::string toLowerCase(const std::string& input) {
std::optional<std::string> getFileBySuffix(const std::string& dir, const std::string& suffix, std::optional<std::string> getFileBySuffix(const std::string& dir, const std::string& suffix,
bool check_lower_case) { bool check_lower_case) {
if (!std::filesystem::exists(dir)) { try {
return {};
}
for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) { for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) {
if (entry.is_regular_file()) { if (entry.is_regular_file()) {
std::string filename = entry.path().filename().string(); std::string filename = entry.path().filename().string();
@@ -49,6 +47,8 @@ std::optional<std::string> getFileBySuffix(const std::string& dir, const std::st
} }
} }
} }
} catch (const std::filesystem::filesystem_error&) {
}
return {}; return {};
} }
@@ -151,19 +151,24 @@ void AAppIconLabel::updateAppIconName(const std::string& app_identifier,
} }
void AAppIconLabel::updateAppIcon() { void AAppIconLabel::updateAppIcon() {
if (update_app_icon_) { if (update_app_icon_ || (!iconEnabled() && image_.get_visible())) {
update_app_icon_ = false; update_app_icon_ = false;
if (app_icon_name_.empty()) { if (app_icon_name_.empty()) {
image_.set_visible(false); image_.set_visible(false);
} else if (app_icon_name_.front() == '/') { } else if (app_icon_name_.front() == '/') {
auto pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_); try {
int scaled_icon_size = app_icon_size_ * image_.get_scale_factor(); int scaled_icon_size = app_icon_size_ * image_.get_scale_factor();
pixbuf = Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size); auto pixbuf =
Gdk::Pixbuf::create_from_file(app_icon_name_, scaled_icon_size, scaled_icon_size);
auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(), auto surface = Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image_.get_scale_factor(),
image_.get_window()); image_.get_window());
image_.set(surface); image_.set(surface);
image_.set_visible(true); image_.set_visible(true);
} catch (const Glib::Exception& e) {
spdlog::warn("Failed to load app icon {}: {}", app_icon_name_, std::string(e.what()));
image_.set_visible(false);
}
} else { } else {
image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID);
image_.set_visible(true); image_.set_visible(true);
+16 -4
View File
@@ -26,7 +26,7 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
: std::chrono::milliseconds( : std::chrono::milliseconds(
(config_["interval"].isNumeric() (config_["interval"].isNumeric()
? std::max(1L, // Minimum 1ms due to millisecond precision ? 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))), : 1000 * (long)interval))),
default_format_(format_) { default_format_(format_) {
label_.set_name(name); 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 // Make the GtkBuilder and check for errors in his parsing
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) { if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
g_object_unref(builder);
throw std::runtime_error("Error found in the file " + menuFile); throw std::runtime_error("Error found in the file " + menuFile);
} }
menu_ = gtk_builder_get_object(builder, "menu"); menu_ = gtk_builder_get_object(builder, "menu");
if (menu_ == nullptr) { if (menu_ == nullptr) {
g_object_unref(builder);
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder"); throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
} }
// Keep the menu alive after dropping the transient GtkBuilder.
g_object_ref(menu_);
submenus_ = std::map<std::string, GtkMenuItem*>(); submenus_ = std::map<std::string, GtkMenuItem*>();
menuActionsMap_ = std::map<std::string, std::string>(); 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(); for (Json::Value::const_iterator it = config_["menu-actions"].begin();
it != config_["menu-actions"].end(); ++it) { it != config_["menu-actions"].end(); ++it) {
std::string key = it.key().asString(); std::string key = it.key().asString();
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str())); auto* item = gtk_builder_get_object(builder, key.c_str());
if (item == nullptr) {
spdlog::warn("Menu item '{}' not found in builder file", key);
continue;
}
submenus_[key] = GTK_MENU_ITEM(item);
menuActionsMap_[key] = it->asString(); menuActionsMap_[key] = it->asString();
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent), g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
(gpointer)menuActionsMap_[key].c_str()); (gpointer)menuActionsMap_[key].c_str());
} }
g_object_unref(builder);
} catch (std::runtime_error& e) { } catch (std::runtime_error& e) {
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what()); spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
} }
@@ -141,7 +151,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::string& alt, uint16_
if (format_icons.isArray()) { if (format_icons.isArray()) {
auto size = format_icons.size(); auto size = format_icons.size();
if (size != 0U) { if (size != 0U) {
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
format_icons = format_icons[idx]; format_icons = format_icons[idx];
} }
} }
@@ -167,7 +178,8 @@ std::string ALabel::getIcon(uint16_t percentage, const std::vector<std::string>&
if (format_icons.isArray()) { if (format_icons.isArray()) {
auto size = format_icons.size(); auto size = format_icons.size();
if (size != 0U) { if (size != 0U) {
auto idx = std::clamp(percentage / ((max == 0 ? 100 : max) / size), 0U, size - 1); auto divisor = std::max(1U, (max == 0 ? 100U : static_cast<unsigned>(max)) / size);
auto idx = std::clamp(percentage / divisor, 0U, size - 1);
format_icons = format_icons[idx]; format_icons = format_icons[idx];
} }
} }
+6 -2
View File
@@ -92,6 +92,10 @@ AModule::~AModule() {
killpg(pid, SIGTERM); killpg(pid, SIGTERM);
} }
} }
if (menu_ != nullptr) {
g_object_unref(menu_);
menu_ = nullptr;
}
} }
auto AModule::update() -> void { auto AModule::update() -> void {
@@ -170,9 +174,9 @@ bool AModule::handleUserEvent(GdkEventButton* const& e) {
} }
// Check that a menu has been configured // Check that a menu has been configured
if (config_["menu"].isString()) { if (rec != eventMap_.cend() && config_["menu"].isString()) {
// Check if the event is the one specified for the "menu" option // Check if the event is the one specified for the "menu" option
if (rec->second == config_["menu"].asString()) { if (rec->second == config_["menu"].asString() && menu_ != nullptr) {
// Popup the menu // Popup the menu
gtk_widget_show_all(GTK_WIDGET(menu_)); gtk_widget_show_all(GTK_WIDGET(menu_));
gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e)); gtk_menu_popup_at_pointer(GTK_MENU(menu_), reinterpret_cast<GdkEvent*>(e));
+6 -4
View File
@@ -541,13 +541,15 @@ void waybar::Bar::getModules(const Factory& factory, const std::string& pos,
auto vertical = (group != nullptr ? group->getBox().get_orientation() auto vertical = (group != nullptr ? group->getBox().get_orientation()
: box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL; : box_.get_orientation()) == Gtk::ORIENTATION_VERTICAL;
auto group_config = config[ref]; const Json::Value& group_config = config[ref];
if (group_config["modules"].isNull()) { if (group_config["modules"].isNull()) {
spdlog::warn("Group definition '{}' has not been found, group will be hidden", ref); spdlog::warn("Group definition '{}' has not been found, group will be hidden", ref);
} }
auto* group_module = new waybar::Group(id_name, class_name, group_config, vertical); auto group_module = std::make_unique<waybar::Group>(
getModules(factory, ref, group_module); id_name, class_name, group_config, vertical);
module = group_module;
getModules(factory, ref, group_module.get());
module = group_module.release();
} else { } else {
module = factory.makeModule(ref, pos); module = factory.makeModule(ref, pos);
} }
+18 -1
View File
@@ -10,6 +10,7 @@
#include "idle-inhibit-unstable-v1-client-protocol.h" #include "idle-inhibit-unstable-v1-client-protocol.h"
#include "util/clara.hpp" #include "util/clara.hpp"
#include "util/format.hpp" #include "util/format.hpp"
#include "util/hex_checker.hpp"
waybar::Client* waybar::Client::inst() { waybar::Client* waybar::Client::inst() {
static auto* c = new Client(); static auto* c = new Client();
@@ -19,11 +20,23 @@ waybar::Client* waybar::Client::inst() {
void waybar::Client::handleGlobal(void* data, struct wl_registry* registry, uint32_t name, void waybar::Client::handleGlobal(void* data, struct wl_registry* registry, uint32_t name,
const char* interface, uint32_t version) { const char* interface, uint32_t version) {
auto* client = static_cast<Client*>(data); auto* client = static_cast<Client*>(data);
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 &&
version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
if (client->xdg_output_manager != nullptr) {
zxdg_output_manager_v1_destroy(client->xdg_output_manager);
client->xdg_output_manager = nullptr;
}
client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1*>(wl_registry_bind( client->xdg_output_manager = static_cast<struct zxdg_output_manager_v1*>(wl_registry_bind(
registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION)); registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION));
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
if (client->idle_inhibit_manager != nullptr) {
zwp_idle_inhibit_manager_v1_destroy(client->idle_inhibit_manager);
client->idle_inhibit_manager = nullptr;
}
client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1*>( client->idle_inhibit_manager = static_cast<struct zwp_idle_inhibit_manager_v1*>(
wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1)); wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1));
} }
@@ -194,11 +207,15 @@ auto waybar::Client::setupCss(const std::string& css_file) -> void {
} }
css_provider_ = Gtk::CssProvider::create(); css_provider_ = Gtk::CssProvider::create();
auto [modified_css, was_transformed] = transform_8bit_to_hex(css_file);
if (was_transformed) {
css_provider_->load_from_data(modified_css);
} else {
if (!css_provider_->load_from_path(css_file)) { if (!css_provider_->load_from_path(css_file)) {
css_provider_.reset(); css_provider_.reset();
throw std::runtime_error("Can't open style file"); throw std::runtime_error("Can't open style file");
} }
}
Gtk::StyleContext::add_provider_for_screen(screen, css_provider_, Gtk::StyleContext::add_provider_for_screen(screen, css_provider_,
GTK_STYLE_PROVIDER_PRIORITY_USER); GTK_STYLE_PROVIDER_PRIORITY_USER);
} }
+14 -1
View File
@@ -64,11 +64,19 @@ Group::Group(const std::string& name, const std::string& id, const Json::Value&
: true); : true);
click_to_reveal = drawer_config["click-to-reveal"].asBool(); click_to_reveal = drawer_config["click-to-reveal"].asBool();
const bool start_expanded =
(drawer_config["start-expanded"].isBool() ? drawer_config["start-expanded"].asBool()
: false);
auto transition_type = getPreferredTransitionType(vertical); auto transition_type = getPreferredTransitionType(vertical);
revealer.set_transition_type(transition_type); revealer.set_transition_type(transition_type);
revealer.set_transition_duration(transition_duration); revealer.set_transition_duration(transition_duration);
revealer.set_reveal_child(false); revealer.set_reveal_child(start_expanded);
if (start_expanded) {
box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
}
revealer.get_style_context()->add_class("drawer"); revealer.get_style_context()->add_class("drawer");
@@ -124,6 +132,11 @@ auto Group::update() -> void {
// noop // noop
} }
bool Group::handleScroll(GdkEventScroll* e) {
// no scroll.
return true;
}
Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; } Gtk::Box& Group::getBox() { return is_drawer ? (is_first_widget ? box : revealer_box) : box; }
void Group::addWidget(Gtk::Widget& widget) { void Group::addWidget(Gtk::Widget& widget) {
+9 -5
View File
@@ -33,7 +33,10 @@ static void writeSignalToPipe(int signum) {
// to `signal_handler`. // to `signal_handler`.
static void catchSignals(waybar::SafeSignal<int>& signal_handler) { static void catchSignals(waybar::SafeSignal<int>& signal_handler) {
int fd[2]; int fd[2];
pipe(fd); if (pipe(fd) != 0) {
spdlog::error("Failed to create signal pipe: {}", strerror(errno));
return;
}
int signal_pipe_read_fd = fd[0]; int signal_pipe_read_fd = fd[0];
signal_pipe_write_fd = fd[1]; signal_pipe_write_fd = fd[1];
@@ -137,15 +140,16 @@ static void handleSignalMainThread(int signum, bool& reload) {
break; break;
case SIGCHLD: case SIGCHLD:
spdlog::debug("Received SIGCHLD in signalThread"); spdlog::debug("Received SIGCHLD in signalThread");
if (!reap.empty()) { {
reap_mtx.lock(); std::lock_guard<std::mutex> lock(reap_mtx);
for (auto it = reap.begin(); it != reap.end(); ++it) { for (auto it = reap.begin(); it != reap.end();) {
if (waitpid(*it, nullptr, WNOHANG) == *it) { if (waitpid(*it, nullptr, WNOHANG) == *it) {
spdlog::debug("Reaped child with PID: {}", *it); spdlog::debug("Reaped child with PID: {}", *it);
it = reap.erase(it); it = reap.erase(it);
} else {
++it;
} }
} }
reap_mtx.unlock();
} }
break; break;
default: default:
+2 -2
View File
@@ -58,11 +58,11 @@ auto waybar::modules::Backlight::update() -> void {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
label_.set_tooltip_text(fmt::format(fmt::runtime(tooltip_format), label_.set_tooltip_markup(fmt::format(fmt::runtime(tooltip_format),
fmt::arg("percent", percent), fmt::arg("percent", percent),
fmt::arg("icon", getIcon(percent)))); fmt::arg("icon", getIcon(percent))));
} else { } else {
label_.set_tooltip_text(desc); label_.set_tooltip_markup(desc);
} }
} }
} else { } else {
+56 -52
View File
@@ -117,17 +117,16 @@ void waybar::modules::Battery::refreshBatteries() {
if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) && if (((bat_defined && dir_name == config_["bat"].asString()) || !bat_defined) &&
(fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) && (fs::exists(node.path() / "capacity") || fs::exists(node.path() / "charge_now")) &&
fs::exists(node.path() / "uevent") && fs::exists(node.path() / "uevent") &&
(fs::exists(node.path() / "status") || bat_compatibility) && (fs::exists(node.path() / "status") || bat_compatibility)) {
fs::exists(node.path() / "type")) {
std::string type; std::string type;
std::ifstream(node.path() / "type") >> type; if (std::ifstream{node.path() / "type"} >> type && !type.compare("Battery")) {
if (!type.compare("Battery")) {
// Ignore non-system power supplies unless explicitly requested // Ignore non-system power supplies unless explicitly requested
if (!bat_defined && fs::exists(node.path() / "scope")) { if (!bat_defined) {
std::string scope; std::string scope;
std::ifstream(node.path() / "scope") >> scope; // for hotplug-in device, access it is always unstable because you may remove the
if (g_ascii_strcasecmp(scope.data(), "device") == 0) { // device anytime so just allow failure happen and do nothing
if (std::ifstream{node.path() / "scope"} >> scope &&
g_ascii_strcasecmp(scope.data(), "device") == 0) {
continue; continue;
} }
} }
@@ -139,7 +138,9 @@ void waybar::modules::Battery::refreshBatteries() {
auto event_path = (node.path() / "uevent"); auto event_path = (node.path() / "uevent");
auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS); auto wd = inotify_add_watch(battery_watch_fd_, event_path.c_str(), IN_ACCESS);
if (wd < 0) { if (wd < 0) {
throw std::runtime_error("Could not watch events for " + node.path().string()); spdlog::warn("Could not watch events for {} (device may have been removed)",
node.path().string());
continue;
} }
batteries_[node.path()] = wd; batteries_[node.path()] = wd;
} }
@@ -273,11 +274,11 @@ waybar::modules::Battery::getInfos() {
auto bat = item.first; auto bat = item.first;
std::string _status; std::string _status;
/* Check for adapter status if battery is not available */ {
if (!std::ifstream(bat / "status")) { std::ifstream f{bat / "status"};
if (!std::getline(f, _status)) {
std::getline(std::ifstream(adapter_ / "status"), _status); std::getline(std::ifstream(adapter_ / "status"), _status);
} else { }
std::getline(std::ifstream(bat / "status"), _status);
} }
// Some battery will report current and charge in μA/μAh. // Some battery will report current and charge in μA/μAh.
@@ -286,64 +287,64 @@ waybar::modules::Battery::getInfos() {
uint32_t current_now = 0; uint32_t current_now = 0;
int32_t _current_now_int = 0; int32_t _current_now_int = 0;
bool current_now_exists = false; bool current_now_exists = false;
if (fs::exists(bat / "current_now")) { if (std::ifstream current_now_f{bat / "current_now"}) {
current_now_exists = true; current_now_exists = true;
std::ifstream(bat / "current_now") >> _current_now_int; current_now_f >> _current_now_int;
} else if (fs::exists(bat / "current_avg")) { } else if (std::ifstream current_avg_f{bat / "current_avg"}) {
current_now_exists = true; current_now_exists = true;
std::ifstream(bat / "current_avg") >> _current_now_int; current_avg_f >> _current_now_int;
} }
// Documentation ABI allows a negative value when discharging, positive // Documentation ABI allows a negative value when discharging, positive
// value when charging. // value when charging.
current_now = std::abs(_current_now_int); current_now = std::abs(_current_now_int);
if (fs::exists(bat / "time_to_empty_now")) { if (std::ifstream f{bat / "time_to_empty_now"}) {
time_to_empty_now_exists = true; time_to_empty_now_exists = true;
std::ifstream(bat / "time_to_empty_now") >> time_to_empty_now; f >> time_to_empty_now;
} }
if (fs::exists(bat / "time_to_full_now")) { if (std::ifstream f{bat / "time_to_full_now"}) {
time_to_full_now_exists = true; time_to_full_now_exists = true;
std::ifstream(bat / "time_to_full_now") >> time_to_full_now; f >> time_to_full_now;
} }
uint32_t voltage_now = 0; uint32_t voltage_now = 0;
bool voltage_now_exists = false; bool voltage_now_exists = false;
if (fs::exists(bat / "voltage_now")) { if (std::ifstream voltage_now_f{bat / "voltage_now"}) {
voltage_now_exists = true; voltage_now_exists = true;
std::ifstream(bat / "voltage_now") >> voltage_now; voltage_now_f >> voltage_now;
} else if (fs::exists(bat / "voltage_avg")) { } else if (std::ifstream voltage_avg_f{bat / "voltage_avg"}) {
voltage_now_exists = true; voltage_now_exists = true;
std::ifstream(bat / "voltage_avg") >> voltage_now; voltage_avg_f >> voltage_now;
} }
uint32_t charge_full = 0; uint32_t charge_full = 0;
bool charge_full_exists = false; bool charge_full_exists = false;
if (fs::exists(bat / "charge_full")) { if (std::ifstream f{bat / "charge_full"}) {
charge_full_exists = true; charge_full_exists = true;
std::ifstream(bat / "charge_full") >> charge_full; f >> charge_full;
} }
uint32_t charge_full_design = 0; uint32_t charge_full_design = 0;
bool charge_full_design_exists = false; bool charge_full_design_exists = false;
if (fs::exists(bat / "charge_full_design")) { if (std::ifstream f{bat / "charge_full_design"}) {
charge_full_design_exists = true; charge_full_design_exists = true;
std::ifstream(bat / "charge_full_design") >> charge_full_design; f >> charge_full_design;
} }
uint32_t charge_now = 0; uint32_t charge_now = 0;
bool charge_now_exists = false; bool charge_now_exists = false;
if (fs::exists(bat / "charge_now")) { if (std::ifstream f{bat / "charge_now"}) {
charge_now_exists = true; charge_now_exists = true;
std::ifstream(bat / "charge_now") >> charge_now; f >> charge_now;
} }
uint32_t power_now = 0; uint32_t power_now = 0;
int32_t _power_now_int = 0; int32_t _power_now_int = 0;
bool power_now_exists = false; bool power_now_exists = false;
if (fs::exists(bat / "power_now")) { if (std::ifstream f{bat / "power_now"}) {
power_now_exists = true; power_now_exists = true;
std::ifstream(bat / "power_now") >> _power_now_int; f >> _power_now_int;
} }
// Some drivers (example: Qualcomm) exposes use a negative value when // Some drivers (example: Qualcomm) exposes use a negative value when
// discharging, positive value when charging. // discharging, positive value when charging.
@@ -351,28 +352,28 @@ waybar::modules::Battery::getInfos() {
uint32_t energy_now = 0; uint32_t energy_now = 0;
bool energy_now_exists = false; bool energy_now_exists = false;
if (fs::exists(bat / "energy_now")) { if (std::ifstream f{bat / "energy_now"}) {
energy_now_exists = true; energy_now_exists = true;
std::ifstream(bat / "energy_now") >> energy_now; f >> energy_now;
} }
uint32_t energy_full = 0; uint32_t energy_full = 0;
bool energy_full_exists = false; bool energy_full_exists = false;
if (fs::exists(bat / "energy_full")) { if (std::ifstream f{bat / "energy_full"}) {
energy_full_exists = true; energy_full_exists = true;
std::ifstream(bat / "energy_full") >> energy_full; f >> energy_full;
} }
uint32_t energy_full_design = 0; uint32_t energy_full_design = 0;
bool energy_full_design_exists = false; bool energy_full_design_exists = false;
if (fs::exists(bat / "energy_full_design")) { if (std::ifstream f{bat / "energy_full_design"}) {
energy_full_design_exists = true; energy_full_design_exists = true;
std::ifstream(bat / "energy_full_design") >> energy_full_design; f >> energy_full_design;
} }
uint16_t cycleCount = 0; uint16_t cycleCount = 0;
if (fs::exists(bat / "cycle_count")) { if (std::ifstream f{bat / "cycle_count"}) {
std::ifstream(bat / "cycle_count") >> cycleCount; f >> cycleCount;
} }
if (charge_full_design >= largestDesignCapacity) { if (charge_full_design >= largestDesignCapacity) {
largestDesignCapacity = charge_full_design; largestDesignCapacity = charge_full_design;
@@ -402,9 +403,9 @@ waybar::modules::Battery::getInfos() {
} else if (energy_now_exists && energy_full_exists && energy_full != 0) { } else if (energy_now_exists && energy_full_exists && energy_full != 0) {
capacity_exists = true; capacity_exists = true;
capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full; capacity = 100 * (uint64_t)energy_now / (uint64_t)energy_full;
} else if (fs::exists(bat / "capacity")) { } else if (std::ifstream f{bat / "capacity"}) {
capacity_exists = true; capacity_exists = true;
std::ifstream(bat / "capacity") >> capacity; f >> capacity;
} }
if (!voltage_now_exists) { if (!voltage_now_exists) {
@@ -686,7 +687,7 @@ auto waybar::modules::Battery::update() -> void {
status = getAdapterStatus(capacity); status = getAdapterStatus(capacity);
} }
auto status_pretty = status; auto status_pretty = status;
puts(status.c_str());
// Transform to lowercase and replace space with dash // Transform to lowercase and replace space with dash
std::ranges::transform(status.begin(), status.end(), status.begin(), std::ranges::transform(status.begin(), status.end(), status.begin(),
[](char ch) { return ch == ' ' ? '-' : std::tolower(ch); }); [](char ch) { return ch == ' ' ? '-' : std::tolower(ch); });
@@ -790,16 +791,19 @@ void waybar::modules::Battery::processEvents(std::string& state, std::string& st
if (!events.isObject() || events.empty()) { if (!events.isObject() || events.empty()) {
return; return;
} }
std::string event_name = fmt::format("on-{}-{}", status == "discharging" ? status : "charging", auto exec = [](Json::Value const& event) {
state.empty() ? std::to_string(capacity) : state); if (!event.isString()) return;
if (auto command = event.asString(); !command.empty()) {
util::command::exec(command, "");
}
};
std::string status_name = status == "discharging" ? "on-discharging" : "on-charging";
std::string event_name = status_name + '-' + (state.empty() ? std::to_string(capacity) : state);
if (last_event_ != event_name) { if (last_event_ != event_name) {
spdlog::debug("battery: triggering event {}", event_name); spdlog::debug("battery: triggering event {}", event_name);
if (events[event_name].isString()) { exec(events[event_name]);
std::string exec = events[event_name].asString(); if (!last_event_.empty() && last_event_[3] != event_name[3]) {
// Execute the command if it is not empty exec(events[status_name]);
if (!exec.empty()) {
util::command::exec(exec, "");
}
} }
last_event_ = event_name; last_event_ = event_name;
} }
+11 -1
View File
@@ -111,13 +111,16 @@ waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value&
findConnectedDevices(cur_controller_->path, connected_devices_); findConnectedDevices(cur_controller_->path, connected_devices_);
} }
if (manager_) {
g_signal_connect(manager_.get(), "object-added", G_CALLBACK(onObjectAdded), this); 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(), "object-removed", G_CALLBACK(onObjectRemoved), this);
g_signal_connect(manager_.get(), "interface-proxy-properties-changed", g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
G_CALLBACK(onInterfaceProxyPropertiesChanged), this); 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), g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
this); this);
}
#ifdef WANT_RFKILL #ifdef WANT_RFKILL
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
@@ -446,6 +449,10 @@ auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> { auto waybar::modules::Bluetooth::findCurController() -> std::optional<ControllerInfo> {
std::optional<ControllerInfo> controller_info; std::optional<ControllerInfo> controller_info;
if (!manager_) {
return controller_info;
}
GList* objects = g_dbus_object_manager_get_objects(manager_.get()); GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) { for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data); GDBusObject* object = G_DBUS_OBJECT(l->data);
@@ -465,6 +472,9 @@ auto waybar::modules::Bluetooth::findCurController() -> std::optional<Controller
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path, auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
std::vector<DeviceInfo>& connected_devices) std::vector<DeviceInfo>& connected_devices)
-> void { -> void {
if (!manager_) {
return;
}
GList* objects = g_dbus_object_manager_get_objects(manager_.get()); GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) { for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data); GDBusObject* object = G_DBUS_OBJECT(l->data);
+1 -1
View File
@@ -26,6 +26,7 @@ void waybar::modules::cava::Cava::pause_resume() { backend_->doPauseResume(); }
auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void { auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
Glib::signal_idle().connect_once([this, input]() { Glib::signal_idle().connect_once([this, input]() {
if (silence_) { if (silence_) {
silence_ = false;
label_.get_style_context()->remove_class("silent"); label_.get_style_context()->remove_class("silent");
if (!label_.get_style_context()->has_class("updated")) if (!label_.get_style_context()->has_class("updated"))
label_.get_style_context()->add_class("updated"); label_.get_style_context()->add_class("updated");
@@ -38,7 +39,6 @@ auto waybar::modules::cava::Cava::onUpdate(const std::string& input) -> void {
label_.show(); label_.show();
ALabel::update(); ALabel::update();
}); });
silence_ = false;
} }
auto waybar::modules::cava::Cava::onSilence() -> void { auto waybar::modules::cava::Cava::onSilence() -> void {
+1 -1
View File
@@ -218,7 +218,7 @@ void waybar::modules::cava::CavaBackend::loadConfig() {
prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str()); prm_.input = ::cava::input_method_by_name(config_["method"].asString().c_str());
if (config_["source"].isString()) { if (config_["source"].isString()) {
if (prm_.audio_source) free(prm_.audio_source); if (prm_.audio_source) free(prm_.audio_source);
prm_.audio_source = config_["source"].asString().data(); prm_.audio_source = strdup(config_["source"].asString().c_str());
} }
if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt(); if (config_["sample_rate"].isNumeric()) prm_.samplerate = config_["sample_rate"].asLargestInt();
if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt(); if (config_["sample_bits"].isInt()) prm_.samplebits = config_["sample_bits"].asInt();
+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()}; throw std::runtime_error{std::string{"Missing wbcffi_init function: "} + dlerror()};
} }
hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit")); hooks_.deinit = reinterpret_cast<DenitFn*>(dlsym(handle, "wbcffi_deinit"));
if (!hooks_.init) { if (!hooks_.deinit) {
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()}; throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
} }
// Optional functions // Optional functions
@@ -71,6 +71,7 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
// Prepare config_entries array // Prepare config_entries array
std::vector<ffi::wbcffi_config_entry> config_entries; std::vector<ffi::wbcffi_config_entry> config_entries;
config_entries.reserve(keys.size());
for (size_t i = 0; i < keys.size(); i++) { for (size_t i = 0; i < keys.size(); i++) {
config_entries.push_back({keys[i].c_str(), config_entries_stringstor[i].c_str()}); 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 [load1, load5, load15] = Load::getLoad();
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip);
}
auto format = format_; auto format = format_;
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
auto state = getState(total_usage); auto state = getState(total_usage);
@@ -48,14 +46,25 @@ auto waybar::modules::Cpu::update() -> void {
store.push_back(fmt::arg("max_frequency", max_frequency)); store.push_back(fmt::arg("max_frequency", max_frequency));
store.push_back(fmt::arg("min_frequency", min_frequency)); store.push_back(fmt::arg("min_frequency", min_frequency));
store.push_back(fmt::arg("avg_frequency", avg_frequency)); store.push_back(fmt::arg("avg_frequency", avg_frequency));
std::vector<std::string> arg_names;
arg_names.reserve(cpu_usage.size() * 2);
for (size_t i = 1; i < cpu_usage.size(); ++i) { for (size_t i = 1; i < cpu_usage.size(); ++i) {
auto core_i = i - 1; auto core_i = i - 1;
auto core_format = fmt::format("usage{}", core_i); arg_names.push_back(fmt::format("usage{}", core_i));
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
auto icon_format = fmt::format("icon{}", core_i); arg_names.push_back(fmt::format("icon{}", core_i));
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
} }
label_.set_markup(fmt::vformat(format, store)); label_.set_markup(fmt::vformat(format, store));
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
tooltip = config_["tooltip-format"].asString();
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
} else {
label_.set_tooltip_markup(tooltip);
}
}
} }
// Call parent update // Call parent update
+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 { auto waybar::modules::CpuFrequency::update() -> void {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency(); auto [max_frequency, min_frequency, avg_frequency] = CpuFrequency::getCpuFrequency();
if (tooltipEnabled()) {
auto tooltip =
fmt::format("Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n",
min_frequency, avg_frequency, max_frequency);
label_.set_tooltip_text(tooltip);
}
auto format = format_; auto format = format_;
auto state = getState(avg_frequency); auto state = getState(avg_frequency);
if (!state.empty() && config_["format-" + state].isString()) { if (!state.empty() && config_["format-" + state].isString()) {
@@ -43,6 +38,18 @@ auto waybar::modules::CpuFrequency::update() -> void {
store.push_back(fmt::arg("min_frequency", min_frequency)); store.push_back(fmt::arg("min_frequency", min_frequency));
store.push_back(fmt::arg("avg_frequency", avg_frequency)); store.push_back(fmt::arg("avg_frequency", avg_frequency));
label_.set_markup(fmt::vformat(format, store)); label_.set_markup(fmt::vformat(format, store));
if (tooltipEnabled()) {
std::string tooltip;
if (config_["tooltip-format"].isString()) {
tooltip = config_["tooltip-format"].asString();
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
} else {
tooltip = "Minimum frequency: {}\nAverage frequency: {}\nMaximum frequency: {}\n";
label_.set_tooltip_markup(
fmt::format(fmt::runtime(tooltip), min_frequency, avg_frequency, max_frequency));
}
}
} }
// Call parent update // Call parent update
+3 -5
View File
@@ -23,23 +23,21 @@ std::vector<float> waybar::modules::CpuFrequency::parseCpuFrequencies() {
if (frequencies.size() <= 0) { if (frequencies.size() <= 0) {
std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq"; std::string cpufreq_dir = "/sys/devices/system/cpu/cpufreq";
if (std::filesystem::exists(cpufreq_dir)) { try {
std::vector<std::string> frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"}; std::vector<std::string> frequency_files = {"/cpuinfo_min_freq", "/cpuinfo_max_freq"};
for (auto& p : std::filesystem::directory_iterator(cpufreq_dir)) { 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; std::string freq_file_path = p.path().string() + freq_file;
if (std::filesystem::exists(freq_file_path)) {
std::string freq_value; std::string freq_value;
std::ifstream freq(freq_file_path); std::ifstream freq(freq_file_path);
if (freq.is_open()) { if (freq.is_open()) {
getline(freq, freq_value); getline(freq, freq_value);
float frequency = std::strtol(freq_value.c_str(), nullptr, 10); float frequency = std::strtol(freq_value.c_str(), nullptr, 10);
frequencies.push_back(frequency / 1000); frequencies.push_back(frequency / 1000);
freq.close();
}
} }
} }
} }
} catch (const std::filesystem::filesystem_error&) {
} }
} }
+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 { auto waybar::modules::CpuUsage::update() -> void {
// TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_); auto [cpu_usage, tooltip] = CpuUsage::getCpuUsage(prev_times_);
if (tooltipEnabled()) {
label_.set_tooltip_text(tooltip);
}
auto format = format_; auto format = format_;
auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0];
auto state = getState(total_usage); auto state = getState(total_usage);
@@ -38,14 +36,25 @@ auto waybar::modules::CpuUsage::update() -> void {
fmt::dynamic_format_arg_store<fmt::format_context> store; fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("usage", total_usage)); store.push_back(fmt::arg("usage", total_usage));
store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); store.push_back(fmt::arg("icon", getIcon(total_usage, icons)));
std::vector<std::string> arg_names;
arg_names.reserve(cpu_usage.size() * 2);
for (size_t i = 1; i < cpu_usage.size(); ++i) { for (size_t i = 1; i < cpu_usage.size(); ++i) {
auto core_i = i - 1; auto core_i = i - 1;
auto core_format = fmt::format("usage{}", core_i); arg_names.push_back(fmt::format("usage{}", core_i));
store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); store.push_back(fmt::arg(arg_names.back().c_str(), cpu_usage[i]));
auto icon_format = fmt::format("icon{}", core_i); arg_names.push_back(fmt::format("icon{}", core_i));
store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); store.push_back(fmt::arg(arg_names.back().c_str(), getIcon(cpu_usage[i], icons)));
} }
label_.set_markup(fmt::vformat(format, store)); label_.set_markup(fmt::vformat(format, store));
if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) {
tooltip = config_["tooltip-format"].asString();
label_.set_tooltip_markup(fmt::vformat(tooltip, store));
} else {
label_.set_tooltip_markup(tooltip);
}
}
} }
// Call parent update // Call parent update
@@ -71,7 +80,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
auto [prev_idle, prev_total] = prev_times[0]; auto [prev_idle, prev_total] = prev_times[0];
const float delta_idle = curr_idle - prev_idle; const float delta_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total; const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total); uint16_t tmp =
(delta_total > 0) ? static_cast<uint16_t>(100 * (1 - delta_idle / delta_total)) : 0;
tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp); tooltip = fmt::format("Total: {}%\nCores: (pending)", tmp);
usage.push_back(tmp); usage.push_back(tmp);
} else { } else {
@@ -93,7 +103,8 @@ std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpu
} }
const float delta_idle = curr_idle - prev_idle; const float delta_idle = curr_idle - prev_idle;
const float delta_total = curr_total - prev_total; const float delta_total = curr_total - prev_total;
uint16_t tmp = 100 * (1 - delta_idle / delta_total); uint16_t tmp =
(delta_total > 0) ? static_cast<uint16_t>(100 * (1 - delta_idle / delta_total)) : 0;
if (i == 0) { if (i == 0) {
tooltip = fmt::format("Total: {}%", tmp); tooltip = fmt::format("Total: {}%", tmp);
} else { } else {
+14 -6
View File
@@ -2,6 +2,8 @@
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <utility>
#include "util/scope_guard.hpp" #include "util/scope_guard.hpp"
waybar::modules::Custom::Custom(const std::string& name, const std::string& id, waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
@@ -180,16 +182,22 @@ auto waybar::modules::Custom::update() -> void {
} else { } else {
label_.set_markup(str); label_.set_markup(str);
if (tooltipEnabled()) { if (tooltipEnabled()) {
std::string tooltip_markup;
if (tooltip_format_enabled_) { if (tooltip_format_enabled_) {
auto tooltip = config_["tooltip-format"].asString(); auto tooltip = config_["tooltip-format"].asString();
tooltip = fmt::format( tooltip_markup = fmt::format(fmt::runtime(tooltip), fmt::arg("text", text_),
fmt::runtime(tooltip), fmt::arg("text", text_), fmt::arg("alt", alt_), fmt::arg("tooltip", tooltip_), fmt::arg("alt", alt_),
fmt::arg("icon", getIcon(percentage_, alt_)), fmt::arg("percentage", percentage_)); fmt::arg("icon", getIcon(percentage_, alt_)),
label_.set_tooltip_markup(tooltip); fmt::arg("percentage", percentage_));
} else if (text_ == tooltip_) { } else if (text_ == tooltip_) {
label_.set_tooltip_markup(str); tooltip_markup = str;
} else { } else {
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(); 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 fs_used - File system used space
*/ */
if (err != 0) { if (err != 0 || stats.f_blocks == 0) {
event_box_.hide(); event_box_.hide();
return; return;
} }
@@ -81,7 +81,7 @@ auto waybar::modules::Disk::update() -> void {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free), fmt::runtime(tooltip_format), stats.f_bavail * 100 / stats.f_blocks, fmt::arg("free", free),
fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used), fmt::arg("percentage_free", stats.f_bavail * 100 / stats.f_blocks), fmt::arg("used", used),
fmt::arg("percentage_used", percentage_used), fmt::arg("total", total), fmt::arg("percentage_used", percentage_used), fmt::arg("total", total),
@@ -92,7 +92,7 @@ auto waybar::modules::Disk::update() -> void {
ALabel::update(); 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") { if (divisor == "kB") {
return 1000.0; return 1000.0;
} else if (divisor == "kiB") { } else if (divisor == "kiB") {
+25 -8
View File
@@ -70,17 +70,33 @@ static const zdwl_ipc_output_v2_listener output_status_listener_impl{
static void handle_global(void* data, struct wl_registry* registry, uint32_t name, static void handle_global(void* data, struct wl_registry* registry, uint32_t name,
const char* interface, uint32_t version) { const char* interface, uint32_t version) {
if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) { if (std::strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
static_cast<Tags*>(data)->status_manager_ = static_cast<struct zdwl_ipc_manager_v2*>( auto* self = static_cast<Tags*>(data);
(zdwl_ipc_manager_v2*)wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1));
} if (self->status_manager_) {
if (std::strcmp(interface, wl_seat_interface.name) == 0) { zdwl_ipc_manager_v2_destroy(self->status_manager_);
version = std::min<uint32_t>(version, 1); self->status_manager_ = nullptr;
static_cast<Tags*>(data)->seat_ =
static_cast<struct wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, version));
}
} }
self->status_manager_ = static_cast<struct zdwl_ipc_manager_v2*>(
wl_registry_bind(registry, name, &zdwl_ipc_manager_v2_interface, 1));
}
if (std::strcmp(interface, wl_seat_interface.name) == 0) {
auto* self = static_cast<Tags*>(data);
if (self->seat_) {
wl_seat_destroy(self->seat_);
self->seat_ = nullptr;
}
version = std::min<uint32_t>(version, 1);
self->seat_ = static_cast<struct wl_seat*>(
wl_registry_bind(registry, name, &wl_seat_interface, version));
}
}
static void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) { static void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) {
/* Ignore event */ /* Ignore event */
} }
@@ -179,6 +195,7 @@ bool Tags::handle_button_press(GdkEventButton* event_button, uint32_t tag) {
} }
void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) { void Tags::handle_view_tags(uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) {
if (tag >= buttons_.size()) return;
// First clear all occupied state // First clear all occupied state
auto& button = buttons_[tag]; auto& button = buttons_[tag];
if (clients) { if (clients) {
+1 -1
View File
@@ -116,7 +116,7 @@ void Window::handle_frame() {
updateAppIconName(appid_, ""); updateAppIconName(appid_, "");
updateAppIcon(); updateAppIcon();
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(title_); label_.set_tooltip_markup(title_);
} }
} }
+3 -1
View File
@@ -143,7 +143,9 @@ void WorkspaceManager::handle_finished() {
ext_manager_ = nullptr; ext_manager_ = nullptr;
} }
void WorkspaceManager::commit() const { ext_workspace_manager_v1_commit(ext_manager_); } void WorkspaceManager::commit() const {
if (ext_manager_) ext_workspace_manager_v1_commit(ext_manager_);
}
void WorkspaceManager::update() { void WorkspaceManager::update() {
spdlog::debug("[ext/workspaces]: Updating state"); spdlog::debug("[ext/workspaces]: Updating state");
+4 -4
View File
@@ -128,9 +128,9 @@ void Gamemode::getData() {
Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters); Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters);
if (data && data.is_of_type(Glib::VariantType("(v)"))) { if (data && data.is_of_type(Glib::VariantType("(v)"))) {
Glib::VariantBase variant; Glib::VariantBase variant;
g_variant_get(data.gobj_copy(), "(v)", &variant); g_variant_get(const_cast<GVariant*>(data.gobj()), "(v)", &variant);
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_INT32)) { if (variant && variant.is_of_type(Glib::VARIANT_TYPE_INT32)) {
g_variant_get(variant.gobj_copy(), "i", &gameCount); g_variant_get(const_cast<GVariant*>(variant.gobj()), "i", &gameCount);
return; return;
} }
} }
@@ -158,7 +158,7 @@ void Gamemode::prepareForSleep_cb(const Glib::RefPtr<Gio::DBus::Connection>& con
const Glib::VariantContainerBase& parameters) { const Glib::VariantContainerBase& parameters) {
if (parameters.is_of_type(Glib::VariantType("(b)"))) { if (parameters.is_of_type(Glib::VariantType("(b)"))) {
gboolean sleeping; gboolean sleeping;
g_variant_get(parameters.gobj_copy(), "(b)", &sleeping); g_variant_get(const_cast<GVariant*>(parameters.gobj()), "(b)", &sleeping);
if (!sleeping) { if (!sleeping) {
getData(); getData();
dp.emit(); dp.emit();
@@ -212,7 +212,7 @@ auto Gamemode::update() -> void {
// Tooltip // Tooltip
if (tooltip) { if (tooltip) {
std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount)); std::string text = fmt::format(fmt::runtime(tooltip_format), fmt::arg("count", gameCount));
box_.set_tooltip_text(text); box_.set_tooltip_markup(text);
} }
// Label format // Label format
+152 -38
View File
@@ -9,22 +9,25 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#include <array>
#include <cerrno>
#include <cstring>
#include <filesystem> #include <filesystem>
#include <optional>
#include <string> #include <string>
#include "util/scoped_fd.hpp"
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
std::filesystem::path IPC::socketFolder_; std::filesystem::path IPC::socketFolder_;
std::optional<bool> IPC::s_luaProtocolDetected_;
std::filesystem::path IPC::getSocketFolder(const char* instanceSig) { std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
static std::mutex folderMutex; static std::mutex folderMutex;
std::unique_lock lock(folderMutex); std::unique_lock lock(folderMutex);
// socket path, specified by EventManager of Hyprland if (socketFolder_.empty()) {
if (!socketFolder_.empty()) {
return socketFolder_;
}
const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR"); const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR");
std::filesystem::path xdgRuntimeDir; std::filesystem::path xdgRuntimeDir;
// Only set path if env variable is set // Only set path if env variable is set
@@ -38,15 +41,15 @@ std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr"); spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr");
socketFolder_ = std::filesystem::path("/tmp") / "hypr"; socketFolder_ = std::filesystem::path("/tmp") / "hypr";
} }
}
socketFolder_ = socketFolder_ / instanceSig; return socketFolder_ / instanceSig;
return socketFolder_;
} }
IPC::IPC() { IPC::IPC() {
// will start IPC and relay events to parseIPC // will start IPC and relay events to parseIPC
ipcThread_ = std::thread([this]() { socketListener(); });
socketOwnerPid_ = getpid(); socketOwnerPid_ = getpid();
ipcThread_ = std::thread([this]() { socketListener(); });
} }
IPC::~IPC() { IPC::~IPC() {
@@ -54,20 +57,21 @@ IPC::~IPC() {
// failed exec()) exits. // failed exec()) exits.
if (getpid() != socketOwnerPid_) return; if (getpid() != socketOwnerPid_) return;
running_ = false; running_.store(false, std::memory_order_relaxed);
spdlog::info("Hyprland IPC stopping..."); spdlog::info("Hyprland IPC stopping...");
{
std::lock_guard<std::mutex> lock(socketMutex_);
if (socketfd_ != -1) { if (socketfd_ != -1) {
spdlog::trace("Shutting down socket"); 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::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(); ipcThread_.join();
} }
}
IPC& IPC::inst() { IPC& IPC::inst() {
static IPC ipc; static IPC ipc;
@@ -85,10 +89,10 @@ void IPC::socketListener() {
spdlog::info("Hyprland IPC starting"); spdlog::info("Hyprland IPC starting");
struct sockaddr_un addr; struct sockaddr_un addr = {};
socketfd_ = socket(AF_UNIX, SOCK_STREAM, 0); const int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketfd_ == -1) { if (socketfd == -1) {
spdlog::error("Hyprland IPC: socketfd failed"); spdlog::error("Hyprland IPC: socketfd failed");
return; return;
} }
@@ -96,33 +100,57 @@ void IPC::socketListener() {
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock"; 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); 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); int l = sizeof(struct sockaddr_un);
if (connect(socketfd_, (struct sockaddr*)&addr, l) == -1) { if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
spdlog::error("Hyprland IPC: Unable to connect?"); spdlog::error("Hyprland IPC: Unable to connect? {}", std::strerror(errno));
close(socketfd);
return; return;
} }
auto* file = fdopen(socketfd_, "r");
if (file == nullptr) { {
spdlog::error("Hyprland IPC: Couldn't open file descriptor"); std::lock_guard<std::mutex> lock(socketMutex_);
return; 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 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) { if (bytes_read < 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (errno == EINTR) {
continue; continue;
} }
if (!running_.load(std::memory_order_relaxed)) {
break;
}
spdlog::error("Hyprland IPC: read failed: {}", std::strerror(errno));
break;
}
std::string messageReceived(buffer.data()); pending.append(buffer.data(), static_cast<std::size_t>(bytes_read));
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n')); 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); spdlog::debug("hyprland IPC received {}", messageReceived);
try { try {
@@ -132,8 +160,16 @@ void IPC::socketListener() {
} catch (...) { } catch (...) {
throw; 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"); spdlog::debug("Hyprland IPC stopped");
} }
@@ -178,7 +214,7 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
std::string IPC::getSocket1Reply(const std::string& rq) { std::string IPC::getSocket1Reply(const std::string& rq) {
// basically hyprctl // basically hyprctl
const auto serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); util::ScopedFd serverSocket(socket(AF_UNIX, SOCK_STREAM, 0));
if (serverSocket < 0) { if (serverSocket < 0) {
throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)"); throw std::runtime_error("Hyprland IPC: Couldn't open a socket (1)");
@@ -198,8 +234,10 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock"; std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock";
// Use snprintf to copy the socketPath string into serverAddress.sun_path // Use snprintf to copy the socketPath string into serverAddress.sun_path
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < const auto socketPathLength =
0) { 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)"); throw std::runtime_error("Hyprland IPC: Couldn't copy socket path (6)");
} }
@@ -208,28 +246,39 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
throw std::runtime_error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); 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 (sizeWritten < 0) {
if (errno == EINTR) {
continue;
}
spdlog::error("Hyprland IPC: Couldn't write (4)"); spdlog::error("Hyprland IPC: Couldn't write (4)");
return ""; 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::array<char, 8192> buffer = {0};
std::string response; std::string response;
ssize_t sizeWritten = 0;
do { do {
sizeWritten = read(serverSocket, buffer.data(), 8192); sizeWritten = read(serverSocket, buffer.data(), 8192);
if (sizeWritten < 0) { if (sizeWritten < 0) {
spdlog::error("Hyprland IPC: Couldn't read (5)"); spdlog::error("Hyprland IPC: Couldn't read (5)");
close(serverSocket);
return ""; return "";
} }
response.append(buffer.data(), sizeWritten); response.append(buffer.data(), sizeWritten);
} while (sizeWritten > 0); } while (sizeWritten > 0);
close(serverSocket);
return response; return response;
} }
@@ -243,4 +292,69 @@ Json::Value IPC::getSocket1JsonReply(const std::string& rq) {
return parser_.parse(reply); return parser_.parse(reply);
} }
bool IPC::isLuaProtocol() {
if (s_luaProtocolDetected_.has_value()) {
return *s_luaProtocolDetected_;
}
// Probe: send a harmless old-style dispatch and check the error.
// In Lua-based Hyprland (>= 0.54) the error contains "hl.dispatch".
// In older versions it returns "ok" or a different error.
auto reply = getSocket1Reply("dispatch workspace __waybar_probe__");
bool luaProto = reply.find("hl.dispatch") != std::string::npos;
if (luaProto) {
spdlog::info("Hyprland IPC: detected Lua-based dispatch protocol (Hyprland >= 0.54)");
} else {
spdlog::info("Hyprland IPC: detected legacy dispatch protocol");
}
s_luaProtocolDetected_ = luaProto;
return luaProto;
}
std::string IPC::buildLuaDispatch(const std::string& dispatcher, const std::string& arg) {
// Map old-style dispatchers to the new Lua hl.dsp API.
//
// Old format: dispatch workspace 1
// New format: /dispatch hl.dsp.focus({ workspace = "1" })
//
// Old format: dispatch focusworkspaceoncurrentmonitor 2
// New format: /dispatch hl.dsp.focus({ workspace = "2", monitor = "current" })
//
// Old format: dispatch togglespecialworkspace name
// New format: /dispatch hl.dsp.workspace.toggle_special("name")
if (dispatcher == "workspace") {
return "/dispatch hl.dsp.focus({ workspace = \"" + arg + "\" })";
}
if (dispatcher == "focusworkspaceoncurrentmonitor") {
return "/dispatch hl.dsp.focus({ workspace = \"" + arg + "\", monitor = \"current\" })";
}
if (dispatcher == "togglespecialworkspace") {
if (arg.empty()) {
return "/dispatch hl.dsp.workspace.toggle_special()";
}
return "/dispatch hl.dsp.workspace.toggle_special(\"" + arg + "\")";
}
// Fallback for any other dispatcher: try the old format wrapped in dispatch().
// This may not work for all dispatchers, but it's a reasonable default.
spdlog::warn("Hyprland IPC: unknown dispatcher '{}' in Lua mode, attempting generic format",
dispatcher);
return "/dispatch hl.dsp." + dispatcher + "(\"" + arg + "\")";
}
std::string IPC::dispatch(const std::string& dispatcher, const std::string& arg) {
if (isLuaProtocol()) {
return getSocket1Reply(buildLuaDispatch(dispatcher, arg));
}
// Legacy format: "dispatch <dispatcher> <arg>"
std::string cmd = "dispatch " + dispatcher;
if (!arg.empty()) {
cmd += " " + arg;
}
return getSocket1Reply(cmd);
}
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland
+21 -5
View File
@@ -63,19 +63,35 @@ auto Language::update() -> void {
void Language::onEvent(const std::string& ev) { void Language::onEvent(const std::string& ev) {
std::lock_guard<std::mutex> lg(mutex_); 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: // Last comma before variants parenthesis, eg:
// activelayout>>micro-star-int'l-co.,-ltd.-msi-gk50-elite-gaming-keyboard,English (US, intl., // activelayout>>micro-star-int'l-co.,-ltd.-msi-gk50-elite-gaming-keyboard,English (US, intl.,
// with dead keys) // with dead keys)
std::string beforeParenthesis; std::string beforeParenthesis;
auto parenthesisPos = ev.find_last_of('('); auto parenthesisPos = payload.find_last_of('(');
if (parenthesisPos == std::string::npos) { if (parenthesisPos == std::string::npos) {
beforeParenthesis = ev; beforeParenthesis = payload;
} else { } 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()) if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
return; // ignore return; // ignore
+7 -2
View File
@@ -60,7 +60,7 @@ auto Submap::update() -> void {
} else { } else {
label_.set_markup(fmt::format(fmt::runtime(format_), submap_)); label_.set_markup(fmt::format(fmt::runtime(format_), submap_));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(submap_); label_.set_tooltip_markup(submap_);
} }
event_box_.show(); event_box_.show();
} }
@@ -75,7 +75,12 @@ void Submap::onEvent(const std::string& ev) {
return; return;
} }
auto submapName = ev.substr(ev.find_first_of('>') + 2); 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; submap_ = submapName;
+37 -37
View File
@@ -19,21 +19,19 @@ std::shared_mutex windowIpcSmtx;
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
: AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), m_ipc(IPC::inst()) { : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), m_ipc(IPC::inst()) {
std::unique_lock<std::shared_mutex> windowIpcUniqueLock(windowIpcSmtx);
separateOutputs_ = config["separate-outputs"].asBool(); separateOutputs_ = config["separate-outputs"].asBool();
update();
// register for hyprland ipc // register for hyprland ipc
std::unique_lock<std::shared_mutex> windowIpcUniqueLock(windowIpcSmtx);
m_ipc.registerForIPC("activewindow", this); m_ipc.registerForIPC("activewindow", this);
m_ipc.registerForIPC("closewindow", this); m_ipc.registerForIPC("closewindow", this);
m_ipc.registerForIPC("movewindow", this); m_ipc.registerForIPC("movewindow", this);
m_ipc.registerForIPC("changefloatingmode", this); m_ipc.registerForIPC("changefloatingmode", this);
m_ipc.registerForIPC("fullscreen", this); m_ipc.registerForIPC("fullscreen", this);
windowIpcUniqueLock.unlock(); windowIpcUniqueLock.unlock();
queryActiveWorkspace();
update();
dp.emit(); dp.emit();
} }
@@ -72,13 +70,13 @@ auto Window::update() -> void {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
if (!tooltip_format.empty()) { if (!tooltip_format.empty()) {
label_.set_tooltip_text( label_.set_tooltip_markup(
fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName), fmt::format(fmt::runtime(tooltip_format), fmt::arg("title", windowName),
fmt::arg("initialTitle", windowData_.initial_title), fmt::arg("initialTitle", windowData_.initial_title),
fmt::arg("class", windowData_.class_name), fmt::arg("class", windowData_.class_name),
fmt::arg("initialClass", windowData_.initial_class_name))); fmt::arg("initialClass", windowData_.initial_class_name)));
} else if (!label_text.empty()) { } else if (!label_text.empty()) {
label_.set_tooltip_text(label_text); label_.set_tooltip_markup(label_text);
} }
} }
@@ -124,7 +122,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto monitors = IPC::inst().getSocket1JsonReply("monitors"); const auto monitors = IPC::inst().getSocket1JsonReply("monitors");
if (monitors.isArray()) { if (monitors.isArray()) {
auto monitor = std::ranges::find_if( 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)) { if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName); spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{ return Workspace{
@@ -139,7 +137,7 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
const auto workspaces = IPC::inst().getSocket1JsonReply("workspaces"); const auto workspaces = IPC::inst().getSocket1JsonReply("workspaces");
if (workspaces.isArray()) { if (workspaces.isArray()) {
auto workspace = std::ranges::find_if( 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)) { if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id); spdlog::warn("No workspace with id {}", id);
return Workspace{ return Workspace{
@@ -177,42 +175,56 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
} }
void Window::queryActiveWorkspace() { void Window::queryActiveWorkspace() {
std::shared_lock<std::shared_mutex> windowIpcShareLock(windowIpcSmtx);
if (separateOutputs_) { if (separateOutputs_) {
workspace_ = getActiveWorkspace(this->bar_.output->name); workspace_ = getActiveWorkspace(this->bar_.output->name);
} else { } else {
workspace_ = getActiveWorkspace(); 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; focused_ = false;
windowData_ = WindowData{};
allFloating_ = false;
swallowing_ = false;
fullscreen_ = false;
solo_ = false;
soloClass_.clear();
if (workspace_.windows <= 0) {
return; 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); windowData_ = WindowData::parse(*activeWindow);
updateAppIconName(windowData_.class_name, windowData_.initial_class_name); updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
std::vector<Json::Value> workspaceWindows; 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(); 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"; return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
}); });
std::vector<Json::Value> visibleWindows; std::vector<Json::Value> visibleWindows;
std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows), std::ranges::copy_if(workspaceWindows, std::back_inserter(visibleWindows),
[&](Json::Value window) { return !window["hidden"].asBool(); }); [&](const Json::Value& window) { return !window["hidden"].asBool(); });
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(), solo_ = 1 == std::count_if(
[&](Json::Value window) { return !window["floating"].asBool(); }); visibleWindows.begin(), visibleWindows.end(),
[&](const Json::Value& window) { return !window["floating"].asBool(); });
allFloating_ = std::ranges::all_of( 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_ = windowData_.fullscreen;
// Fullscreen windows look like they are solo // Fullscreen windows look like they are solo
@@ -222,18 +234,6 @@ void Window::queryActiveWorkspace() {
if (solo_) { if (solo_) {
soloClass_ = windowData_.class_name; soloClass_ = windowData_.class_name;
} else {
soloClass_ = "";
}
}
} else {
focused_ = false;
windowData_ = WindowData{};
allFloating_ = false;
swallowing_ = false;
fullscreen_ = false;
solo_ = false;
soloClass_ = "";
} }
} }
+3 -3
View File
@@ -58,7 +58,7 @@ auto WindowCount::update() -> void {
} else if (!format.empty()) { } else if (!format.empty()) {
label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows)); label_.set_markup(fmt::format(fmt::runtime(format), workspace_.windows));
} else { } else {
label_.set_text(fmt::format("{}", workspace_.windows)); label_.set_markup(fmt::format("{}", workspace_.windows));
} }
label_.show(); label_.show();
@@ -79,7 +79,7 @@ auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspac
const auto monitors = m_ipc.getSocket1JsonReply("monitors"); const auto monitors = m_ipc.getSocket1JsonReply("monitors");
if (monitors.isArray()) { if (monitors.isArray()) {
auto monitor = std::ranges::find_if( 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)) { if (monitor == std::end(monitors)) {
spdlog::warn("Monitor not found: {}", monitorName); spdlog::warn("Monitor not found: {}", monitorName);
return Workspace{ return Workspace{
@@ -93,7 +93,7 @@ auto WindowCount::getActiveWorkspace(const std::string& monitorName) -> Workspac
const auto workspaces = m_ipc.getSocket1JsonReply("workspaces"); const auto workspaces = m_ipc.getSocket1JsonReply("workspaces");
if (workspaces.isArray()) { if (workspaces.isArray()) {
auto workspace = std::ranges::find_if( 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)) { if (workspace == std::end(workspaces)) {
spdlog::warn("No workspace with id {}", id); spdlog::warn("No workspace with id {}", id);
return Workspace{ return Workspace{
@@ -19,7 +19,7 @@ WindowCreationPayload::WindowCreationPayload(Json::Value const& client_data)
clearWorkspaceName(); clearWorkspaceName();
} }
WindowCreationPayload::WindowCreationPayload(std::string workspace_name, WindowCreationPayload::WindowCreationPayload(const std::string& workspace_name,
WindowAddress window_address, WindowRepr window_repr) WindowAddress window_address, WindowRepr window_repr)
: m_window(std::move(window_repr)), : m_window(std::move(window_repr)),
m_windowAddress(std::move(window_address)), m_windowAddress(std::move(window_address)),
@@ -28,9 +28,10 @@ WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
clearWorkspaceName(); clearWorkspaceName();
} }
WindowCreationPayload::WindowCreationPayload(std::string workspace_name, WindowCreationPayload::WindowCreationPayload(const std::string& workspace_name,
WindowAddress window_address, std::string window_class, WindowAddress window_address,
std::string window_title, bool is_active) 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_window(std::make_pair(std::move(window_class), std::move(window_title))),
m_windowAddress(std::move(window_address)), m_windowAddress(std::move(window_address)),
m_workspaceName(std::move(workspace_name)), m_workspaceName(std::move(workspace_name)),
+8 -8
View File
@@ -71,20 +71,20 @@ bool Workspace::handleClicked(GdkEventButton* bt) const {
try { try {
if (id() > 0) { // normal if (id() > 0) { // normal
if (m_workspaceManager.moveToMonitor()) { if (m_workspaceManager.moveToMonitor()) {
m_ipc.getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id())); IPC::dispatch("focusworkspaceoncurrentmonitor", std::to_string(id()));
} else { } else {
m_ipc.getSocket1Reply("dispatch workspace " + std::to_string(id())); IPC::dispatch("workspace", std::to_string(id()));
} }
} else if (!isSpecial()) { // named (this includes persistent) } else if (!isSpecial()) { // named (this includes persistent)
if (m_workspaceManager.moveToMonitor()) { if (m_workspaceManager.moveToMonitor()) {
m_ipc.getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name()); IPC::dispatch("focusworkspaceoncurrentmonitor", "name:" + name());
} else { } else {
m_ipc.getSocket1Reply("dispatch workspace name:" + name()); IPC::dispatch("workspace", "name:" + name());
} }
} else if (id() != -99) { // named special } else if (id() != -99) { // named special
m_ipc.getSocket1Reply("dispatch togglespecialworkspace " + name()); IPC::dispatch("togglespecialworkspace", name());
} else { // special } else { // special
m_ipc.getSocket1Reply("dispatch togglespecialworkspace"); IPC::dispatch("togglespecialworkspace", "");
} }
return true; return true;
} catch (const std::exception& e) { } catch (const std::exception& e) {
@@ -96,7 +96,7 @@ bool Workspace::handleClicked(GdkEventButton* bt) const {
void Workspace::initializeWindowMap(const Json::Value& clients_data) { void Workspace::initializeWindowMap(const Json::Value& clients_data) {
m_windowMap.clear(); m_windowMap.clear();
for (auto client : clients_data) { for (const auto& client : clients_data) {
if (client["workspace"]["id"].asInt() == id()) { if (client["workspace"]["id"].asInt() == id()) {
insertWindow({client}); 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); auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
window_box->set_tooltip_text(window_repr.window_title); window_box->set_tooltip_markup(window_repr.window_title);
window_box->get_style_context()->add_class("taskbar-window"); window_box->get_style_context()->add_class("taskbar-window");
if (window_repr.isActive) { if (window_repr.isActive) {
window_box->get_style_context()->add_class("active"); window_box->get_style_context()->add_class("active");
+80 -21
View File
@@ -34,6 +34,9 @@ Workspaces::Workspaces(const std::string& id, const Bar& bar, const Json::Value&
} }
Workspaces::~Workspaces() { Workspaces::~Workspaces() {
if (m_scrollEventConnection_.connected()) {
m_scrollEventConnection_.disconnect();
}
m_ipc.unregisterForIPC(this); m_ipc.unregisterForIPC(this);
// wait for possible event handler to finish // wait for possible event handler to finish
std::lock_guard<std::mutex> lg(m_mutex); std::lock_guard<std::mutex> lg(m_mutex);
@@ -43,6 +46,17 @@ void Workspaces::init() {
m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt(); m_activeWorkspaceId = m_ipc.getSocket1JsonReply("activeworkspace")["id"].asInt();
initializeWorkspaces(); 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(); 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; std::string windowReprKey;
if (windowRewriteConfigUsesTitle()) { if (windowRewriteConfigUsesTitle()) {
windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title); 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 workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
auto const clientsJson = m_ipc.getSocket1JsonReply("clients"); auto const clientsJson = m_ipc.getSocket1JsonReply("clients");
for (Json::Value workspaceJson : workspacesJson) { for (const auto& workspaceJson : workspacesJson) {
std::string workspaceName = workspaceJson["name"].asString(); std::string workspaceName = workspaceJson["name"].asString();
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
(!workspaceName.starts_with("special") || showSpecial()) && (!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 // key is the workspace and value is array of monitors to create on
for (const Json::Value& monitor : value) { for (const Json::Value& monitor : value) {
if (monitor.isString() && monitor.asString() == currentMonitor) { if (monitor.isString() && monitor.asString() == currentMonitor) {
persistentWorkspacesToCreate.emplace_back(currentMonitor); persistentWorkspacesToCreate.emplace_back(key);
break; break;
} }
} }
@@ -328,8 +343,13 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value& c
void Workspaces::onEvent(const std::string& ev) { void Workspaces::onEvent(const std::string& ev) {
std::lock_guard<std::mutex> lock(m_mutex); std::lock_guard<std::mutex> lock(m_mutex);
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>')); const auto separator = ev.find(">>");
std::string payload = ev.substr(eventName.size() + 2); 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") { if (eventName == "workspacev2") {
onWorkspaceActivated(payload); onWorkspaceActivated(payload);
@@ -397,7 +417,7 @@ void Workspaces::onWorkspaceCreated(std::string const& payload, Json::Value cons
auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules"); auto const workspaceRules = m_ipc.getSocket1JsonReply("workspacerules");
auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces"); auto const workspacesJson = m_ipc.getSocket1JsonReply("workspaces");
for (Json::Value workspaceJson : workspacesJson) { for (auto workspaceJson : workspacesJson) {
const auto currentId = workspaceJson["id"].asInt(); const auto currentId = workspaceJson["id"].asInt();
if (currentId == *workspaceId) { if (currentId == *workspaceId) {
std::string workspaceName = workspaceJson["name"].asString(); std::string workspaceName = workspaceJson["name"].asString();
@@ -492,19 +512,21 @@ void Workspaces::onMonitorFocused(std::string const& payload) {
void Workspaces::onWindowOpened(std::string const& payload) { void Workspaces::onWindowOpened(std::string const& payload) {
spdlog::trace("Window opened: {}", payload); spdlog::trace("Window opened: {}", payload);
updateWindowCount(); updateWindowCount();
size_t lastCommaIdx = 0; const auto firstComma = payload.find(',');
size_t nextCommaIdx = payload.find(','); const auto secondComma =
std::string windowAddress = payload.substr(lastCommaIdx, nextCommaIdx - lastCommaIdx); 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; std::string windowAddress = payload.substr(0, firstComma);
nextCommaIdx = payload.find(',', nextCommaIdx + 1); std::string workspaceName = payload.substr(firstComma + 1, secondComma - firstComma - 1);
std::string workspaceName = payload.substr(lastCommaIdx + 1, nextCommaIdx - lastCommaIdx - 1); std::string windowClass = payload.substr(secondComma + 1, thirdComma - secondComma - 1);
std::string windowTitle = payload.substr(thirdComma + 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);
bool isActive = m_currentActiveWindowAddress == windowAddress; bool isActive = m_currentActiveWindowAddress == windowAddress;
m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowClass, windowTitle, isActive); m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowClass, windowTitle, isActive);
@@ -640,6 +662,7 @@ auto Workspaces::parseConfig(const Json::Value& config) -> void {
populateBoolConfig(config, "persistent-only", m_persistentOnly); populateBoolConfig(config, "persistent-only", m_persistentOnly);
populateBoolConfig(config, "active-only", m_activeOnly); populateBoolConfig(config, "active-only", m_activeOnly);
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor); populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
populateBoolConfig(config, "enable-bar-scroll", m_barScroll);
m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value()); m_persistentWorkspaceConfig = config.get("persistent-workspaces", Json::Value());
populateSortByConfig(config); populateSortByConfig(config);
@@ -997,10 +1020,12 @@ void Workspaces::sortWorkspaces() {
void Workspaces::setUrgentWorkspace(std::string const& windowaddress) { void Workspaces::setUrgentWorkspace(std::string const& windowaddress) {
const Json::Value clientsJson = m_ipc.getSocket1JsonReply("clients"); const Json::Value clientsJson = m_ipc.getSocket1JsonReply("clients");
const std::string normalizedAddress =
windowaddress.starts_with("0x") ? windowaddress : fmt::format("0x{}", windowaddress);
int workspaceId = -1; int workspaceId = -1;
for (Json::Value clientJson : clientsJson) { for (const auto& clientJson : clientsJson) {
if (clientJson["address"].asString().ends_with(windowaddress)) { if (clientJson["address"].asString() == normalizedAddress) {
workspaceId = clientJson["workspace"]["id"].asInt(); workspaceId = clientJson["workspace"]["id"].asInt();
break; break;
} }
@@ -1129,7 +1154,11 @@ std::string Workspaces::makePayload(Args const&... args) {
} }
std::pair<std::string, std::string> Workspaces::splitDoublePayload(std::string const& payload) { 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); const std::string part2 = payload.substr(part1.size() + 1);
return {part1, part2}; return {part1, part2};
} }
@@ -1138,6 +1167,9 @@ std::tuple<std::string, std::string, std::string> Workspaces::splitTriplePayload
std::string const& payload) { std::string const& payload) {
const size_t firstComma = payload.find(','); const size_t firstComma = payload.find(',');
const size_t secondComma = payload.find(',', firstComma + 1); 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 part1 = payload.substr(0, firstComma);
const std::string part2 = payload.substr(firstComma + 1, secondComma - (firstComma + 1)); const std::string part2 = payload.substr(firstComma + 1, secondComma - (firstComma + 1));
@@ -1155,4 +1187,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()) {
IPC::dispatch("workspace", "e+1");
} else {
IPC::dispatch("workspace", "m+1");
}
} else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
if (allOutputs()) {
IPC::dispatch("workspace", "e-1");
} else {
IPC::dispatch("workspace", "m-1");
}
}
return true;
}
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland
+3 -2
View File
@@ -85,7 +85,8 @@ auto getInhibitors(const Json::Value& config) -> std::string {
if (config["what"].isArray()) { if (config["what"].isArray()) {
inhibitors = checkInhibitor(config["what"][0].asString()); inhibitors = checkInhibitor(config["what"][0].asString());
for (decltype(config["what"].size()) i = 1; i < config["what"].size(); ++i) { 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; return inhibitors;
} }
@@ -123,7 +124,7 @@ auto Inhibitor::update() -> void {
label_.get_style_context()->add_class(status_text); label_.get_style_context()->add_class(status_text);
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(status_text); label_.set_tooltip_markup(status_text);
} }
return ALabel::update(); return ALabel::update();
+5 -2
View File
@@ -53,7 +53,7 @@ std::string JACK::JACKState() {
auto JACK::update() -> void { auto JACK::update() -> void {
std::string format; std::string format;
std::string state = JACKState(); std::string state = JACKState();
float latency = 1000 * (float)bufsize_ / (float)samplerate_; float latency = samplerate_ > 0 ? 1000.0f * (float)bufsize_ / (float)samplerate_ : 0.0f;
if (label_.get_style_context()->has_class("xrun")) { if (label_.get_style_context()->has_class("xrun")) {
label_.get_style_context()->remove_class("xrun"); label_.get_style_context()->remove_class("xrun");
@@ -80,7 +80,7 @@ auto JACK::update() -> void {
if (tooltipEnabled()) { if (tooltipEnabled()) {
std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms"; std::string tooltip_format = "{bufsize}/{samplerate} {latency}ms";
if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString(); if (config_["tooltip-format"].isString()) tooltip_format = config_["tooltip-format"].asString();
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)), fmt::runtime(tooltip_format), fmt::arg("load", std::round(load_)),
fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_), fmt::arg("bufsize", bufsize_), fmt::arg("samplerate", samplerate_),
fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_))); fmt::arg("latency", fmt::format("{:.2f}", latency)), fmt::arg("xruns", xruns_)));
@@ -91,16 +91,19 @@ auto JACK::update() -> void {
} }
int JACK::bufSize(jack_nframes_t size) { int JACK::bufSize(jack_nframes_t size) {
std::lock_guard<std::mutex> lock(mutex_);
bufsize_ = size; bufsize_ = size;
return 0; return 0;
} }
int JACK::sampleRate(jack_nframes_t rate) { int JACK::sampleRate(jack_nframes_t rate) {
std::lock_guard<std::mutex> lock(mutex_);
samplerate_ = rate; samplerate_ = rate;
return 0; return 0;
} }
int JACK::xrun() { int JACK::xrun() {
std::lock_guard<std::mutex> lock(mutex_);
xruns_ += 1; xruns_ += 1;
state_ = "xrun"; state_ = "xrun";
return 0; return 0;
+15
View File
@@ -232,9 +232,12 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
} }
tryAddDevice(dev_path); tryAddDevice(dev_path);
} else if (event->mask & IN_DELETE) { } else if (event->mask & IN_DELETE) {
std::lock_guard<std::mutex> lock(devices_mutex_);
auto it = libinput_devices_.find(dev_path); auto it = libinput_devices_.find(dev_path);
if (it != libinput_devices_.end()) { if (it != libinput_devices_.end()) {
spdlog::info("Keyboard {} has been removed.", dev_path); spdlog::info("Keyboard {} has been removed.", dev_path);
libinput_path_remove_device(it->second);
libinput_device_unref(it->second);
libinput_devices_.erase(it); libinput_devices_.erase(it);
} }
} }
@@ -245,6 +248,7 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
} }
waybar::modules::KeyboardState::~KeyboardState() { waybar::modules::KeyboardState::~KeyboardState() {
std::lock_guard<std::mutex> lock(devices_mutex_);
for (const auto& [_, dev_ptr] : libinput_devices_) { for (const auto& [_, dev_ptr] : libinput_devices_) {
libinput_path_remove_device(dev_ptr); libinput_path_remove_device(dev_ptr);
} }
@@ -256,12 +260,18 @@ auto waybar::modules::KeyboardState::update() -> void {
try { try {
std::string dev_path; std::string dev_path;
{
std::lock_guard<std::mutex> lock(devices_mutex_);
if (libinput_devices_.empty()) {
return;
}
if (config_["device-path"].isString() && if (config_["device-path"].isString() &&
libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) { libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) {
dev_path = config_["device-path"].asString(); dev_path = config_["device-path"].asString();
} else { } else {
dev_path = libinput_devices_.begin()->first; dev_path = libinput_devices_.begin()->first;
} }
}
int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
auto dev = openDevice(fd); auto dev = openDevice(fd);
numl = libevdev_get_event_value(dev, EV_LED, LED_NUML); 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); auto dev = openDevice(fd);
if (supportsLockStates(dev)) { if (supportsLockStates(dev)) {
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path); spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
std::lock_guard<std::mutex> lock(devices_mutex_);
if (libinput_devices_.find(dev_path) == libinput_devices_.end()) { if (libinput_devices_.find(dev_path) == libinput_devices_.end()) {
auto device = libinput_path_add_device(libinput_, dev_path.c_str()); auto device = libinput_path_add_device(libinput_, dev_path.c_str());
if (device) {
libinput_device_ref(device); libinput_device_ref(device);
libinput_devices_[dev_path] = device; libinput_devices_[dev_path] = device;
} else {
spdlog::warn("keyboard-state: Failed to add device to libinput: {}", dev_path);
}
} }
} }
libevdev_free(dev); libevdev_free(dev);
+1 -1
View File
@@ -22,7 +22,7 @@ auto waybar::modules::Load::update() -> void {
auto [load1, load5, load15] = Load::getLoad(); auto [load1, load5, load15] = Load::getLoad();
if (tooltipEnabled()) { if (tooltipEnabled()) {
auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15); auto tooltip = fmt::format("Load 1: {}\nLoad 5: {}\nLoad 15: {}", load1, load5, load15);
label_.set_tooltip_text(tooltip); label_.set_tooltip_markup(tooltip);
} }
auto format = format_; auto format = format_;
auto state = getState(load1); auto state = getState(load1);
+47 -21
View File
@@ -6,6 +6,9 @@ waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config
dp.emit(); dp.emit();
thread_.sleep_for(interval_); thread_.sleep_for(interval_);
}; };
if (config["unit"].isString()) {
unit_ = config["unit"].asString();
}
} }
auto waybar::modules::Memory::update() -> void { auto waybar::modules::Memory::update() -> void {
@@ -13,15 +16,15 @@ auto waybar::modules::Memory::update() -> void {
unsigned long memtotal = meminfo_["MemTotal"]; unsigned long memtotal = meminfo_["MemTotal"];
unsigned long swaptotal = 0; unsigned long swaptotal = 0;
if (meminfo_.count("SwapTotal")) { if (meminfo_.contains("SwapTotal")) {
swaptotal = meminfo_["SwapTotal"]; swaptotal = meminfo_["SwapTotal"];
} }
unsigned long memfree; unsigned long memfree;
unsigned long swapfree = 0; unsigned long swapfree = 0;
if (meminfo_.count("SwapFree")) { if (meminfo_.contains("SwapFree")) {
swapfree = meminfo_["SwapFree"]; swapfree = meminfo_["SwapFree"];
} }
if (meminfo_.count("MemAvailable")) { if (meminfo_.contains("MemAvailable")) {
// New kernels (3.4+) have an accurate available memory field. // New kernels (3.4+) have an accurate available memory field.
memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"]; memfree = meminfo_["MemAvailable"] + meminfo_["zfs_size"];
} else { } else {
@@ -31,18 +34,19 @@ auto waybar::modules::Memory::update() -> void {
} }
if (memtotal > 0 && memfree >= 0) { if (memtotal > 0 && memfree >= 0) {
float total_ram_gigabytes =
0.01 * round(memtotal / 10485.76); // 100*10485.76 = 2^20 = 1024^2 = GiB/KiB
float total_swap_gigabytes = 0.01 * round(swaptotal / 10485.76);
int used_ram_percentage = 100 * (memtotal - memfree) / memtotal; int used_ram_percentage = 100 * (memtotal - memfree) / memtotal;
int used_swap_percentage = 0; int used_swap_percentage = 0;
if (swaptotal) { if ((bool) swaptotal) {
used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal; used_swap_percentage = 100 * (swaptotal - swapfree) / swaptotal;
} }
float used_ram_gigabytes = 0.01 * round((memtotal - memfree) / 10485.76);
float used_swap_gigabytes = 0.01 * round((swaptotal - swapfree) / 10485.76); float divisor = calc_divisor(unit_);
float available_ram_gigabytes = 0.01 * round(memfree / 10485.76); float total_ram = memtotal / divisor;
float available_swap_gigabytes = 0.01 * round(swapfree / 10485.76); float total_swap = swaptotal / divisor;
float used_ram = (memtotal - memfree) / divisor;
float used_swap = (swaptotal - swapfree) / divisor;
float available_ram = memfree / divisor;
float available_swap = swapfree / divisor;
auto format = format_; auto format = format_;
auto state = getState(used_ram_percentage); auto state = getState(used_ram_percentage);
@@ -58,27 +62,27 @@ auto waybar::modules::Memory::update() -> void {
label_.set_markup(fmt::format( label_.set_markup(fmt::format(
fmt::runtime(format), used_ram_percentage, fmt::runtime(format), used_ram_percentage,
fmt::arg("icon", getIcon(used_ram_percentage, icons)), fmt::arg("icon", getIcon(used_ram_percentage, icons)),
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("total", total_ram), fmt::arg("swapTotal", total_swap),
fmt::arg("percentage", used_ram_percentage), fmt::arg("percentage", used_ram_percentage),
fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"), fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"),
fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram),
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), fmt::arg("swapUsed", used_swap), fmt::arg("avail", available_ram),
fmt::arg("swapAvail", available_swap_gigabytes))); fmt::arg("swapAvail", available_swap)));
} }
if (tooltipEnabled()) { if (tooltipEnabled()) {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_format = config_["tooltip-format"].asString();
label_.set_tooltip_text(fmt::format( label_.set_tooltip_markup(fmt::format(
fmt::runtime(tooltip_format), used_ram_percentage, fmt::runtime(tooltip_format), used_ram_percentage,
fmt::arg("total", total_ram_gigabytes), fmt::arg("swapTotal", total_swap_gigabytes), fmt::arg("total", total_ram), fmt::arg("swapTotal", total_swap),
fmt::arg("percentage", used_ram_percentage), fmt::arg("percentage", used_ram_percentage),
fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"), fmt::arg("swapState", swaptotal == 0 ? "Off" : "On"),
fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram_gigabytes), fmt::arg("swapPercentage", used_swap_percentage), fmt::arg("used", used_ram),
fmt::arg("swapUsed", used_swap_gigabytes), fmt::arg("avail", available_ram_gigabytes), fmt::arg("swapUsed", used_swap), fmt::arg("avail", available_ram),
fmt::arg("swapAvail", available_swap_gigabytes))); fmt::arg("swapAvail", available_swap)));
} else { } else {
label_.set_tooltip_text(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1)); label_.set_tooltip_markup(fmt::format("{:.{}f}GiB used", used_ram, 1));
} }
} }
} else { } else {
@@ -87,3 +91,25 @@ auto waybar::modules::Memory::update() -> void {
// Call parent update // Call parent update
ALabel::update(); ALabel::update();
} }
float waybar::modules::Memory::calc_divisor(const std::string& divisor) {
if (divisor == "kB") {
return 1.0;
} else if (divisor == "kiB") {
return 1.024;
} else if (divisor == "MB") {
return 1.000 * 1000.0;
} else if (divisor == "MiB") {
return 1.024 * 1024.0;
} else if (divisor == "GB") {
return 1.000 * 1000.0 * 1000.0;
} else if (divisor == "GiB") {
return 1.024 * 1024.0 * 1024.0;
} else if (divisor == "TB") {
return 1.000 * 1000.0 * 1000.0 * 1000.0;
} else if (divisor == "TiB") {
return 1.024 * 1024.0 * 1024.0 * 1024.0;
} else { // default to GiB if it is anything that we don't recongnise
return 1.024 * 1024.0 * 1024.0;
}
}
+5 -3
View File
@@ -110,7 +110,7 @@ void waybar::modules::MPD::setLabel() {
? config_["tooltip-format-disconnected"].asString() ? config_["tooltip-format-disconnected"].asString()
: "MPD (disconnected)"; : "MPD (disconnected)";
// Nothing to format // Nothing to format
label_.set_tooltip_text(tooltip_format); label_.set_tooltip_markup(tooltip_format);
} }
return; return;
} }
@@ -210,7 +210,7 @@ void waybar::modules::MPD::setLabel() {
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon),
fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri)); fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename), fmt::arg("uri", uri));
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} catch (fmt::format_error const& e) { } catch (fmt::format_error const& e) {
spdlog::warn("mpd: format error (tooltip): {}", e.what()); spdlog::warn("mpd: format error (tooltip): {}", e.what());
} }
@@ -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()) { if (!config_[optionName + "-icons"].isObject()) {
return ""; return "";
} }
@@ -323,6 +324,7 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
case MPD_ERROR_SYSTEM: case MPD_ERROR_SYSTEM:
if (auto ec = mpd_connection_get_system_error(conn); ec != 0) { if (auto ec = mpd_connection_get_system_error(conn); ec != 0) {
mpd_connection_clear_error(conn); mpd_connection_clear_error(conn);
connection_.reset();
throw std::system_error(ec, std::system_category()); throw std::system_error(ec, std::system_category());
} }
G_GNUC_FALLTHROUGH; G_GNUC_FALLTHROUGH;
+80 -31
View File
@@ -109,6 +109,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
player_ = config_["player"].asString(); player_ = config_["player"].asString();
} }
if (config_["ignored-players"].isArray()) { if (config_["ignored-players"].isArray()) {
ignored_players_.reserve(config_["ignored-players"].size());
for (const auto& item : config_["ignored-players"]) { for (const auto& item : config_["ignored-players"]) {
if (item.isString()) { if (item.isString()) {
ignored_players_.push_back(item.asString()); ignored_players_.push_back(item.asString());
@@ -117,7 +118,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
} }
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error) { if (error) {
g_error_free(error); g_error_free(error);
} }
@@ -161,8 +162,7 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
if (player) { if (player) {
g_object_connect(player, "signal::play", G_CALLBACK(onPlayerPlay), this, "signal::pause", g_object_connect(player, "signal::play", G_CALLBACK(onPlayerPlay), this, "signal::pause",
G_CALLBACK(onPlayerPause), this, "signal::stop", G_CALLBACK(onPlayerStop), G_CALLBACK(onPlayerPause), this, "signal::stop", G_CALLBACK(onPlayerStop),
this, "signal::stop", G_CALLBACK(onPlayerStop), this, "signal::metadata", this, "signal::metadata", G_CALLBACK(onPlayerMetadata), this, NULL);
G_CALLBACK(onPlayerMetadata), this, NULL);
} }
// allow setting an interval count that triggers periodic refreshes // 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() { Mpris::~Mpris() {
if (manager != nullptr) g_object_unref(manager); if (manager != nullptr) {
if (player != nullptr) g_object_unref(player); 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 { auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) -> std::string {
@@ -410,11 +419,14 @@ auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlaye
return; 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); mpris->player = playerctl_player_new_from_name(player_name, nullptr);
g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause", g_object_connect(mpris->player, "signal::play", G_CALLBACK(onPlayerPlay), mpris, "signal::pause",
G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop), G_CALLBACK(onPlayerPause), mpris, "signal::stop", G_CALLBACK(onPlayerStop),
mpris, "signal::stop", G_CALLBACK(onPlayerStop), mpris, "signal::metadata", mpris, "signal::metadata", G_CALLBACK(onPlayerMetadata), mpris, NULL);
G_CALLBACK(onPlayerMetadata), mpris, NULL);
mpris->dp.emit(); mpris->dp.emit();
} }
@@ -458,10 +470,7 @@ auto Mpris::onPlayerStop(PlayerctlPlayer* player, gpointer data) -> void {
if (!mpris) return; if (!mpris) return;
spdlog::debug("mpris: player-stop callback"); spdlog::debug("mpris: player-stop callback");
// update widget (update() handles visibility)
// hide widget
mpris->event_box_.set_visible(false);
// update widget
mpris->dp.emit(); mpris->dp.emit();
} }
@@ -480,7 +489,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
} }
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error) { if (error) {
g_error_free(error); g_error_free(error);
} }
@@ -488,7 +497,12 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
char* player_status = nullptr; char* player_status = nullptr;
auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED; auto player_playback_status = PLAYERCTL_PLAYBACK_STATUS_STOPPED;
g_object_get(player, "status", &player_status, "playback-status", &player_playback_status, NULL);
// Clean up previous fallback player
if (last_active_player_ && last_active_player_ != player) {
g_object_unref(last_active_player_);
last_active_player_ = nullptr;
}
std::string player_name = player_; std::string player_name = player_;
if (player_name == "playerctld") { if (player_name == "playerctld") {
@@ -498,19 +512,52 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
} }
// > get the list of players [..] in order of activity // > get the list of players [..] in order of activity
// https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249 // https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-common.c#L248-L249
players = g_list_first(players); PlayerctlPlayer* first_valid_player = nullptr;
if (players) std::string first_valid_name;
player_name = static_cast<PlayerctlPlayerName*>(players->data)->name; for (auto* p = g_list_first(players); p != nullptr; p = p->next) {
else auto* pn = static_cast<PlayerctlPlayerName*>(p->data);
return std::nullopt; // no players found, hide the widget std::string name = pn->name;
}
if (std::any_of(ignored_players_.begin(), ignored_players_.end(), 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; })) { [&](const std::string& pn) { return player_name == pn; })) {
spdlog::warn("mpris[{}]: ignoring player update", player_name); spdlog::warn("mpris[{}]: ignoring player update", player_name);
return std::nullopt; return std::nullopt;
} else {
last_active_player_ = player;
} }
g_object_get(last_active_player_, "status", &player_status, "playback-status",
&player_playback_status, NULL);
if (!player_status) {
spdlog::error("mpris: failed to get player status");
return std::nullopt;
}
// make status lowercase // make status lowercase
player_status[0] = std::tolower(player_status[0]); player_status[0] = std::tolower(player_status[0]);
@@ -524,28 +571,29 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
.length = std::nullopt, .length = std::nullopt,
}; };
if (auto* artist_ = playerctl_player_get_artist(player, &error)) { if (auto* artist_ = playerctl_player_get_artist(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); spdlog::debug("mpris[{}]: artist = {}", info.name, artist_);
info.artist = artist_; info.artist = artist_;
g_free(artist_); g_free(artist_);
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* album_ = playerctl_player_get_album(player, &error)) { if (auto* album_ = playerctl_player_get_album(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: album = {}", info.name, album_); spdlog::debug("mpris[{}]: album = {}", info.name, album_);
info.album = album_; info.album = album_;
g_free(album_); g_free(album_);
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* title_ = playerctl_player_get_title(player, &error)) { if (auto* title_ = playerctl_player_get_title(last_active_player_, &error)) {
spdlog::debug("mpris[{}]: title = {}", info.name, title_); spdlog::debug("mpris[{}]: title = {}", info.name, title_);
info.title = title_; info.title = title_;
g_free(title_); g_free(title_);
} }
if (error) goto errorexit; if (error) goto errorexit;
if (auto* length_ = playerctl_player_print_metadata_prop(player, "mpris:length", &error)) { if (auto* length_ =
playerctl_player_print_metadata_prop(last_active_player_, "mpris:length", &error)) {
spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_); spdlog::debug("mpris[{}]: mpris:length = {}", info.name, length_);
auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); auto len = std::chrono::microseconds(std::strtol(length_, nullptr, 10));
auto len_h = std::chrono::duration_cast<std::chrono::hours>(len); auto len_h = std::chrono::duration_cast<std::chrono::hours>(len);
@@ -557,7 +605,7 @@ auto Mpris::getPlayerInfo() -> std::optional<PlayerInfo> {
if (error) goto errorexit; if (error) goto errorexit;
{ {
auto position_ = playerctl_player_get_position(player, &error); auto position_ = playerctl_player_get_position(last_active_player_, &error);
if (error) { if (error) {
// it's fine to have an error here because not all players report a position // it's fine to have an error here because not all players report a position
g_error_free(error); g_error_free(error);
@@ -609,12 +657,13 @@ bool Mpris::handleToggle(GdkEventButton* const& e) {
}); });
// Command pattern: encapsulate each button's action // Command pattern: encapsulate each button's action
auto* target = last_active_player_ ? last_active_player_ : player;
const ButtonAction actions[] = { const ButtonAction actions[] = {
{1, "on-click", [&]() { playerctl_player_play_pause(player, &error); }}, {1, "on-click", [&]() { playerctl_player_play_pause(target, &error); }},
{2, "on-click-middle", [&]() { playerctl_player_previous(player, &error); }}, {2, "on-click-middle", [&]() { playerctl_player_previous(target, &error); }},
{3, "on-click-right", [&]() { playerctl_player_next(player, &error); }}, {3, "on-click-right", [&]() { playerctl_player_next(target, &error); }},
{8, "on-click-backward", [&]() { playerctl_player_previous(player, &error); }}, {8, "on-click-backward", [&]() { playerctl_player_previous(target, &error); }},
{9, "on-click-forward", [&]() { playerctl_player_next(player, &error); }}, {9, "on-click-forward", [&]() { playerctl_player_next(target, &error); }},
}; };
for (const auto& action : actions) { for (const auto& action : actions) {
@@ -734,7 +783,7 @@ auto Mpris::update() -> void {
fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)),
fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string)));
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} catch (fmt::format_error const& e) { } catch (fmt::format_error const& e) {
spdlog::warn("mpris: format error (tooltip): {}", e.what()); spdlog::warn("mpris: format error (tooltip): {}", e.what());
} }
+82 -40
View File
@@ -46,6 +46,7 @@ waybar::modules::Network::readBandwidthUsage() {
std::string ifacename; std::string ifacename;
iss >> ifacename; // ifacename contains "eth0:" iss >> ifacename; // ifacename contains "eth0:"
if (ifacename.empty()) continue;
ifacename.pop_back(); // remove trailing ':' ifacename.pop_back(); // remove trailing ':'
if (ifacename != ifname_) { if (ifacename != ifname_) {
continue; continue;
@@ -104,6 +105,7 @@ waybar::modules::Network::Network(const std::string& id, const Json::Value& conf
bandwidth_down_total_ = 0; bandwidth_down_total_ = 0;
bandwidth_up_total_ = 0; bandwidth_up_total_ = 0;
} }
bandwidth_last_sample_time_ = std::chrono::steady_clock::now();
if (!config_["interface"].isString()) { if (!config_["interface"].isString()) {
// "interface" isn't configured, then try to guess the external // "interface" isn't configured, then try to guess the external
@@ -276,10 +278,24 @@ const std::string waybar::modules::Network::getNetworkState() const {
auto waybar::modules::Network::update() -> void { auto waybar::modules::Network::update() -> void {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
std::string tooltip_format; std::string tooltip_format;
auto now = std::chrono::steady_clock::now();
auto elapsed_seconds = std::chrono::duration<double>(now - bandwidth_last_sample_time_).count();
auto bandwidth = readBandwidthUsage();
auto bandwidth_down = 0ull; auto bandwidth_down = 0ull;
auto bandwidth_up = 0ull; auto bandwidth_up = 0ull;
// Only recalculate bandwidth when enough time has elapsed since the last
// sample. Event-driven dp.emit() calls (link/addr/route changes) can
// trigger update() between timer intervals, which would consume the byte
// delta prematurely and show near-zero bandwidth.
auto min_elapsed = std::chrono::duration<double>(interval_).count() * 0.5;
if (elapsed_seconds >= min_elapsed) {
if (elapsed_seconds <= 0.0) {
elapsed_seconds = std::chrono::duration<double>(interval_).count();
}
bandwidth_last_sample_time_ = now;
auto bandwidth = readBandwidthUsage();
if (bandwidth.has_value()) { if (bandwidth.has_value()) {
auto down_octets = (*bandwidth).first; auto down_octets = (*bandwidth).first;
auto up_octets = (*bandwidth).second; auto up_octets = (*bandwidth).second;
@@ -289,6 +305,14 @@ auto waybar::modules::Network::update() -> void {
bandwidth_up = up_octets - bandwidth_up_total_; bandwidth_up = up_octets - bandwidth_up_total_;
bandwidth_up_total_ = up_octets; bandwidth_up_total_ = up_octets;
bandwidth_down_prev_ = bandwidth_down;
bandwidth_up_prev_ = bandwidth_up;
}
} else {
bandwidth_down = bandwidth_down_prev_;
bandwidth_up = bandwidth_up_prev_;
elapsed_seconds = std::chrono::duration<double>(interval_).count();
} }
if (!alt_) { if (!alt_) {
@@ -320,6 +344,7 @@ auto waybar::modules::Network::update() -> void {
} else if (addr_pref_ == ip_addr_pref::IPV6) { } else if (addr_pref_ == ip_addr_pref::IPV6) {
final_ipaddr_ = ipaddr6_; final_ipaddr_ = ipaddr6_;
} else if (addr_pref_ == ip_addr_pref::IPV4_6) { } else if (addr_pref_ == ip_addr_pref::IPV4_6) {
final_ipaddr_.reserve(ipaddr_.length() + ipaddr6_.length() + 1);
final_ipaddr_ = ipaddr_; final_ipaddr_ = ipaddr_;
final_ipaddr_ += '\n'; final_ipaddr_ += '\n';
final_ipaddr_ += ipaddr6_; final_ipaddr_ += ipaddr6_;
@@ -333,23 +358,18 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_),
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits", fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / elapsed_seconds, "b/s")),
pow_format(bandwidth_down * 8ull / (interval_.count() / 1000.0), "b/s")), fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / elapsed_seconds, "b/s")),
fmt::arg("bandwidthUpBits", fmt::arg("bandwidthTotalBits",
pow_format(bandwidth_up * 8ull / (interval_.count() / 1000.0), "b/s")), pow_format((bandwidth_up + bandwidth_down) * 8ull / elapsed_seconds, "b/s")),
fmt::arg( fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / elapsed_seconds, "o/s")),
"bandwidthTotalBits", fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / elapsed_seconds, "o/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", fmt::arg("bandwidthTotalOctets",
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "o/s")), pow_format((bandwidth_up + bandwidth_down) / elapsed_seconds, "o/s")),
fmt::arg("bandwidthDownBytes", fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / elapsed_seconds, "B/s")),
pow_format(bandwidth_down / (interval_.count() / 1000.0), "B/s")), fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / elapsed_seconds, "B/s")),
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / (interval_.count() / 1000.0), "B/s")),
fmt::arg("bandwidthTotalBytes", fmt::arg("bandwidthTotalBytes",
pow_format((bandwidth_up + bandwidth_down) / (interval_.count() / 1000.0), "B/s"))); pow_format((bandwidth_up + bandwidth_down) / elapsed_seconds, "B/s")));
if (text.compare(label_.get_label()) != 0) { if (text.compare(label_.get_label()) != 0) {
label_.set_markup(text); label_.set_markup(text);
if (text.empty()) { if (text.empty()) {
@@ -371,19 +391,18 @@ auto waybar::modules::Network::update() -> void {
fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_), fmt::arg("ipaddr", final_ipaddr_), fmt::arg("gwaddr", gwaddr_), fmt::arg("cidr", cidr_),
fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)), fmt::arg("cidr6", cidr6_), fmt::arg("frequency", fmt::format("{:.1f}", frequency_)),
fmt::arg("icon", getIcon(signal_strength_, state_)), fmt::arg("icon", getIcon(signal_strength_, state_)),
fmt::arg("bandwidthDownBits", fmt::arg("bandwidthDownBits", pow_format(bandwidth_down * 8ull / elapsed_seconds, "b/s")),
pow_format(bandwidth_down * 8ull / interval_.count(), "b/s")), fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / elapsed_seconds, "b/s")),
fmt::arg("bandwidthUpBits", pow_format(bandwidth_up * 8ull / interval_.count(), "b/s")),
fmt::arg("bandwidthTotalBits", fmt::arg("bandwidthTotalBits",
pow_format((bandwidth_up + bandwidth_down) * 8ull / interval_.count(), "b/s")), pow_format((bandwidth_up + bandwidth_down) * 8ull / elapsed_seconds, "b/s")),
fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / interval_.count(), "o/s")), fmt::arg("bandwidthDownOctets", pow_format(bandwidth_down / elapsed_seconds, "o/s")),
fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / interval_.count(), "o/s")), fmt::arg("bandwidthUpOctets", pow_format(bandwidth_up / elapsed_seconds, "o/s")),
fmt::arg("bandwidthTotalOctets", fmt::arg("bandwidthTotalOctets",
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "o/s")), pow_format((bandwidth_up + bandwidth_down) / elapsed_seconds, "o/s")),
fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / interval_.count(), "B/s")), fmt::arg("bandwidthDownBytes", pow_format(bandwidth_down / elapsed_seconds, "B/s")),
fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / interval_.count(), "B/s")), fmt::arg("bandwidthUpBytes", pow_format(bandwidth_up / elapsed_seconds, "B/s")),
fmt::arg("bandwidthTotalBytes", fmt::arg("bandwidthTotalBytes",
pow_format((bandwidth_up + bandwidth_down) / interval_.count(), "B/s"))); pow_format((bandwidth_up + bandwidth_down) / elapsed_seconds, "B/s")));
if (label_.get_tooltip_text() != tooltip_text) { if (label_.get_tooltip_text() != tooltip_text) {
label_.set_tooltip_markup(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} }
@@ -625,18 +644,31 @@ int waybar::modules::Network::handleEvents(struct nl_msg* msg, void* data) {
case IFA_LOCAL: case IFA_LOCAL:
char ipaddr[INET6_ADDRSTRLEN]; char ipaddr[INET6_ADDRSTRLEN];
if (!is_del_event) { if (!is_del_event) {
bool addr_changed = false;
std::string changed_ipaddr;
int changed_cidr = 0;
if ((net->addr_pref_ == ip_addr_pref::IPV4 || if ((net->addr_pref_ == ip_addr_pref::IPV4 ||
net->addr_pref_ == ip_addr_pref::IPV4_6) && net->addr_pref_ == ip_addr_pref::IPV4_6) &&
net->cidr_ == 0 && ifa->ifa_family == AF_INET) { net->cidr_ == 0 && ifa->ifa_family == AF_INET) {
net->ipaddr_ = if (inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)) !=
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)); nullptr) {
net->ipaddr_ = ipaddr;
net->cidr_ = ifa->ifa_prefixlen; 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 || } else if ((net->addr_pref_ == ip_addr_pref::IPV6 ||
net->addr_pref_ == ip_addr_pref::IPV4_6) && net->addr_pref_ == ip_addr_pref::IPV4_6) &&
net->cidr6_ == 0 && ifa->ifa_family == AF_INET6) { net->cidr6_ == 0 && ifa->ifa_family == AF_INET6) {
net->ipaddr6_ = if (inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)) !=
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)); nullptr) {
net->ipaddr6_ = ipaddr;
net->cidr6_ = ifa->ifa_prefixlen; net->cidr6_ = ifa->ifa_prefixlen;
addr_changed = true;
changed_ipaddr = net->ipaddr6_;
changed_cidr = net->cidr6_;
}
} }
switch (ifa->ifa_family) { switch (ifa->ifa_family) {
@@ -656,14 +688,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg* msg, void* data) {
net->netmask6_ = inet_ntop(ifa->ifa_family, &netmask6, ipaddr, sizeof(ipaddr)); 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 { } else {
if (ifa->ifa_family == AF_INET) {
net->ipaddr_.clear(); net->ipaddr_.clear();
net->ipaddr6_.clear();
net->cidr_ = 0; net->cidr_ = 0;
net->cidr6_ = 0;
net->netmask_.clear(); net->netmask_.clear();
} else if (ifa->ifa_family == AF_INET6) {
net->ipaddr6_.clear();
net->cidr6_ = 0;
net->netmask6_.clear(); net->netmask6_.clear();
}
spdlog::debug("network: {} addr deleted {}/{}", net->ifname_, spdlog::debug("network: {} addr deleted {}/{}", net->ifname_,
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)), inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta), ipaddr, sizeof(ipaddr)),
ifa->ifa_prefixlen); ifa->ifa_prefixlen);
@@ -718,16 +756,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg* msg, void* data) {
/* The destination address. /* The destination address.
* Should be either missing, or maybe all 0s. Accept both. * Should be either missing, or maybe all 0s. Accept both.
*/ */
const uint32_t nr_zeroes = (family == AF_INET) ? 4 : 16; auto* dest = (const unsigned char*)RTA_DATA(attr);
unsigned char c = 0; size_t dest_size = RTA_PAYLOAD(attr);
size_t dstlen = RTA_PAYLOAD(attr); for (size_t i = 0; i < dest_size; ++i) {
if (dstlen != nr_zeroes) { if (dest[i] != 0) {
has_destination = true;
break; break;
} }
for (uint32_t i = 0; i < dstlen; i += 1) {
c |= *((unsigned char*)RTA_DATA(attr) + i);
} }
has_destination = (c == 0);
if (rtm->rtm_dst_len != 0) {
// We have found a destination like 0.0.0.0/24, this is not a
// default gateway route.
has_destination = true;
}
break; break;
} }
case RTA_OIF: case RTA_OIF:
+9 -16
View File
@@ -17,19 +17,21 @@
#include "giomm/dataoutputstream.h" #include "giomm/dataoutputstream.h"
#include "giomm/unixinputstream.h" #include "giomm/unixinputstream.h"
#include "giomm/unixoutputstream.h" #include "giomm/unixoutputstream.h"
#include "util/scoped_fd.hpp"
namespace waybar::modules::niri { namespace waybar::modules::niri {
IPC::IPC() { startIPC(); }
int IPC::connectToSocket() { int IPC::connectToSocket() {
const char* socket_path = getenv("NIRI_SOCKET"); const char* socket_path = getenv("NIRI_SOCKET");
if (socket_path == nullptr) { if (socket_path == nullptr) {
spdlog::warn("Niri is not running, niri IPC will not be available."); throw std::runtime_error("Niri IPC: NIRI_SOCKET was not set! (Is Niri running?)");
return -1;
} }
struct sockaddr_un addr; struct sockaddr_un addr;
int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); util::ScopedFd socketfd(socket(AF_UNIX, SOCK_STREAM, 0));
if (socketfd == -1) { if (socketfd == -1) {
throw std::runtime_error("socketfd failed"); throw std::runtime_error("socketfd failed");
@@ -44,26 +46,18 @@ int IPC::connectToSocket() {
int l = sizeof(struct sockaddr_un); int l = sizeof(struct sockaddr_un);
if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) {
close(socketfd);
throw std::runtime_error("unable to connect"); throw std::runtime_error("unable to connect");
} }
return socketfd; return socketfd.release();
} }
void IPC::startIPC() { void IPC::startIPC() {
// will start IPC and relay events to parseIPC // will start IPC and relay events to parseIPC
std::thread([&]() { int socketfd = connectToSocket();
int socketfd;
try {
socketfd = connectToSocket();
} catch (std::exception& e) {
spdlog::error("Niri IPC: failed to start, reason: {}", e.what());
return;
}
if (socketfd == -1) return;
std::thread([this, socketfd]() {
spdlog::info("Niri IPC starting"); spdlog::info("Niri IPC starting");
auto unix_istream = Gio::UnixInputStream::create(socketfd, true); auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
@@ -241,8 +235,7 @@ void IPC::unregisterForIPC(EventHandler* ev_handler) {
} }
Json::Value IPC::send(const Json::Value& request) { Json::Value IPC::send(const Json::Value& request) {
int socketfd = connectToSocket(); util::ScopedFd socketfd(connectToSocket());
if (socketfd == -1) throw std::runtime_error("Niri is not running");
auto unix_istream = Gio::UnixInputStream::create(socketfd, true); auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false); auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false);
+1
View File
@@ -32,6 +32,7 @@ void Language::updateFromIPC() {
auto ipcLock = gIPC->lockData(); auto ipcLock = gIPC->lockData();
layouts_.clear(); layouts_.clear();
layouts_.reserve(gIPC->keyboardLayoutNames().size());
for (const auto& fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName)); for (const auto& fullName : gIPC->keyboardLayoutNames()) layouts_.push_back(getLayout(fullName));
current_idx_ = gIPC->keyboardLayoutCurrent(); current_idx_ = gIPC->keyboardLayoutCurrent();
+1 -1
View File
@@ -67,7 +67,7 @@ void Window::doUpdate() {
updateAppIconName(appId, ""); updateAppIconName(appId, "");
if (tooltipEnabled()) label_.set_tooltip_text(title); if (tooltipEnabled()) label_.set_tooltip_markup(title);
const auto id = window["id"].asUInt64(); const auto id = window["id"].asUInt64();
const auto workspaceId = window["workspace_id"].asUInt64(); const auto workspaceId = window["workspace_id"].asUInt64();
+7 -2
View File
@@ -114,6 +114,11 @@ void Workspaces::doUpdate() {
button.show(); button.show();
else else
button.hide(); button.hide();
} else if (config_["hide-empty"].asBool()) {
if (ws["active_window_id"].isNull() && !ws["is_focused"].asBool())
button.hide();
else
button.show();
} else { } else {
button.show(); button.show();
} }
@@ -174,11 +179,11 @@ std::string Workspaces::getIcon(const std::string& value, const Json::Value& ws)
if (ws["is_urgent"].asBool() && icons["urgent"]) return icons["urgent"].asString(); if (ws["is_urgent"].asBool() && icons["urgent"]) return icons["urgent"].asString();
if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString(); if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString();
if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString(); if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString();
if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString(); if (ws["active_window_id"].isNull() && icons["empty"]) return icons["empty"].asString();
if (ws["name"]) { if (ws["name"]) {
const auto& name = ws["name"].asString(); const auto& name = ws["name"].asString();
+5 -1
View File
@@ -157,7 +157,7 @@ auto PowerProfilesDaemon::update() -> void {
store.push_back(fmt::arg("icon", getIcon(0, profile.name))); store.push_back(fmt::arg("icon", getIcon(0, profile.name)));
label_.set_markup(fmt::vformat(format_, store)); label_.set_markup(fmt::vformat(format_, store));
if (tooltipEnabled()) { if (tooltipEnabled()) {
label_.set_tooltip_text(fmt::vformat(tooltipFormat_, store)); label_.set_tooltip_markup(fmt::vformat(tooltipFormat_, store));
} }
// Set CSS class // Set CSS class
@@ -176,6 +176,10 @@ auto PowerProfilesDaemon::update() -> void {
bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) { bool PowerProfilesDaemon::handleToggle(GdkEventButton* const& e) {
if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) { if (e->type == GdkEventType::GDK_BUTTON_PRESS && connected_) {
if (availableProfiles_.empty()) return true;
if (activeProfile_ == availableProfiles_.end()) {
activeProfile_ = availableProfiles_.begin();
}
if (e->button == 1) /* left click */ { if (e->button == 1) /* left click */ {
activeProfile_++; activeProfile_++;
if (activeProfile_ == availableProfiles_.end()) { if (activeProfile_ == availableProfiles_.end()) {
+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("source_volume", source_volume), fmt::arg("source_desc", source_desc),
fmt::arg("icon", getIcon(sink_volume, getPulseIcon())))); fmt::arg("icon", getIcon(sink_volume, getPulseIcon()))));
} else { } else {
label_.set_tooltip_text(sink_desc); label_.set_tooltip_markup(sink_desc);
} }
} }
+1
View File
@@ -118,6 +118,7 @@ Layout::Layout(const std::string& id, const waybar::Bar& bar, const Json::Value&
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
label_.hide(); label_.hide();
+1
View File
@@ -77,6 +77,7 @@ Mode::Mode(const std::string& id, const waybar::Bar& bar, const Json::Value& con
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
label_.hide(); label_.hide();
+3
View File
@@ -101,10 +101,12 @@ Tags::Tags(const std::string& id, const waybar::Bar& bar, const Json::Value& con
if (!control_) { if (!control_) {
spdlog::error("river_control_v1 not advertised"); spdlog::error("river_control_v1 not advertised");
return;
} }
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
box_.set_name("tags"); box_.set_name("tags");
@@ -168,6 +170,7 @@ Tags::~Tags() {
} }
void Tags::handle_show() { void Tags::handle_show() {
if (!status_manager_) return;
struct wl_output* output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); struct wl_output* output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output); output_status_ = zriver_status_manager_v1_get_river_output_status(status_manager_, output);
zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this); zriver_output_status_v1_add_listener(output_status_, &output_status_listener_impl, this);
+1
View File
@@ -78,6 +78,7 @@ Window::Window(const std::string& id, const waybar::Bar& bar, const Json::Value&
if (!seat_) { if (!seat_) {
spdlog::error("wl_seat not advertised"); spdlog::error("wl_seat not advertised");
return;
} }
label_.hide(); // hide the label until populated label_.hide(); // hide the label until populated
+2 -2
View File
@@ -25,9 +25,9 @@ auto waybar::modules::Clock::update() -> void {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_format = config_["tooltip-format"].asString();
auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime); auto tooltip_text = fmt::format(fmt::runtime(tooltip_format), localtime);
label_.set_tooltip_text(tooltip_text); label_.set_tooltip_markup(tooltip_text);
} else { } else {
label_.set_tooltip_text(text); label_.set_tooltip_markup(text);
} }
} }
// Call parent update // Call parent update
+3 -1
View File
@@ -102,7 +102,9 @@ Sndio::~Sndio() { sioctl_close(hdl_); }
auto Sndio::update() -> void { auto Sndio::update() -> void {
auto format = format_; auto format = format_;
unsigned int vol = 100. * static_cast<double>(volume_) / static_cast<double>(maxval_); unsigned int vol = (maxval_ > 0) ? static_cast<unsigned int>(100. * static_cast<double>(volume_) /
static_cast<double>(maxval_))
: 0;
if (volume_ == 0) { if (volume_ == 0) {
label_.get_style_context()->add_class("muted"); label_.get_style_context()->add_class("muted");
+52 -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, 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_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()) + "-" + : bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-" +
std::to_string(id)), std::to_string(id)),
object_path_("/StatusNotifierHost/" + 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), config_(config),
bar_(bar), bar_(bar),
on_add_(on_add), on_add_(on_add),
on_remove_(on_remove) {} on_remove_(on_remove),
on_update_(on_update) {}
Host::~Host() { Host::~Host() {
if (bus_name_id_ > 0) { 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_cancellable_cancel(cancellable_);
g_clear_object(&cancellable_); g_clear_object(&cancellable_);
g_clear_object(&watcher_); g_clear_object(&watcher_);
items_.clear(); clearItems();
} }
void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) { if (error != nullptr) {
g_error_free(error); g_error_free(error);
} }
@@ -81,7 +83,7 @@ void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) {
void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) {
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error != nullptr) { if (error != nullptr) {
g_error_free(error); g_error_free(error);
} }
@@ -117,13 +119,50 @@ void Host::itemUnregistered(SnWatcher* watcher, const gchar* service, gpointer d
auto [bus_name, object_path] = host->getBusNameAndObjectPath(service); auto [bus_name, object_path] = host->getBusNameAndObjectPath(service);
for (auto it = host->items_.begin(); it != host->items_.end(); ++it) { for (auto it = host->items_.begin(); it != host->items_.end(); ++it) {
if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) { if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) {
host->on_remove_(*it); host->removeItem(it);
host->items_.erase(it);
break; 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) { std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::string service) {
auto it = service.find('/'); auto it = service.find('/');
if (it != std::string::npos) { if (it != std::string::npos) {
@@ -132,15 +171,18 @@ std::tuple<std::string, std::string> Host::getBusNameAndObjectPath(const std::st
return {service, "/StatusNotifierItem"}; return {service, "/StatusNotifierItem"};
} }
void Host::addRegisteredItem(std::string service) { void Host::addRegisteredItem(const std::string& service) {
std::string bus_name, object_path; std::string bus_name, object_path;
std::tie(bus_name, object_path) = getBusNameAndObjectPath(service); std::tie(bus_name, object_path) = getBusNameAndObjectPath(service);
auto it = std::find_if(items_.begin(), items_.end(), [&bus_name, &object_path](const auto& item) { 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; return bus_name == item->bus_name && object_path == item->object_path;
}); });
if (it == items_.end()) { if (it == items_.end()) {
items_.emplace_back(new Item(bus_name, object_path, config_, bar_)); items_.emplace_back(std::make_unique<Item>(
on_add_(items_.back()); bus_name, object_path, config_, bar_,
[this](Item& item) { itemReady(item); },
[this](Item& item) { itemInvalidated(item); },
on_update_));
} }
} }
+142 -48
View File
@@ -5,6 +5,7 @@
#include <gtkmm/tooltip.h> #include <gtkmm/tooltip.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <map> #include <map>
@@ -37,13 +38,18 @@ namespace waybar::modules::SNI {
static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name; static const Glib::ustring SNI_INTERFACE_NAME = sn_item_interface_info()->name;
static const unsigned UPDATE_DEBOUNCE_TIME = 10; 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), : bus_name(bn),
object_path(op), object_path(op),
icon_size(16), icon_size(16),
effective_icon_size(0), effective_icon_size(0),
icon_theme(Gtk::IconTheme::create()), 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()) { if (config["icon-size"].isUInt()) {
icon_size = config["icon-size"].asUInt(); icon_size = config["icon-size"].asUInt();
} }
@@ -85,6 +91,8 @@ Item::~Item() {
} }
} }
bool Item::isReady() const { return ready_; }
bool Item::handleMouseEnter(GdkEventCrossing* const& e) { bool Item::handleMouseEnter(GdkEventCrossing* const& e) {
event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT); event_box.set_state_flags(Gtk::StateFlags::STATE_FLAG_PRELIGHT);
return false; return false;
@@ -112,14 +120,18 @@ void Item::proxyReady(Glib::RefPtr<Gio::AsyncResult>& result) {
if (this->id.empty() || this->category.empty()) { if (this->id.empty() || this->category.empty()) {
spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path); spdlog::error("Invalid Status Notifier Item: {}, {}", bus_name, object_path);
invalidate();
return; return;
} }
this->updateImage(); this->updateImage();
setReady();
} catch (const Glib::Error& err) { } catch (const Glib::Error& err) {
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
invalidate();
} catch (const std::exception& err) { } catch (const std::exception& err) {
spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what()); spdlog::error("Failed to create DBus Proxy for {} {}: {}", bus_name, object_path, err.what());
invalidate();
} }
} }
@@ -184,11 +196,11 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
} else if (name == "OverlayIconName") { } else if (name == "OverlayIconName") {
overlay_icon_name = get_variant<std::string>(value); overlay_icon_name = get_variant<std::string>(value);
} else if (name == "OverlayIconPixmap") { } else if (name == "OverlayIconPixmap") {
// TODO: overlay_icon_pixmap overlay_icon_pixmap = extractPixBuf(value.gobj());
} else if (name == "AttentionIconName") { } else if (name == "AttentionIconName") {
attention_icon_name = get_variant<std::string>(value); attention_icon_name = get_variant<std::string>(value);
} else if (name == "AttentionIconPixmap") { } else if (name == "AttentionIconPixmap") {
// TODO: attention_icon_pixmap attention_icon_pixmap = extractPixBuf(value.gobj());
} else if (name == "AttentionMovieName") { } else if (name == "AttentionMovieName") {
attention_movie_name = get_variant<std::string>(value); attention_movie_name = get_variant<std::string>(value);
} else if (name == "ToolTip") { } else if (name == "ToolTip") {
@@ -217,18 +229,35 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
} }
void Item::setStatus(const Glib::ustring& value) { void Item::setStatus(const Glib::ustring& value) {
Glib::ustring lower = value.lowercase(); status_ = value.lowercase();
event_box.set_visible(show_passive_ || lower.compare("passive") != 0); event_box.set_visible(show_passive_ || status_.compare("passive") != 0);
auto style = event_box.get_style_context(); auto style = event_box.get_style_context();
for (const auto& class_name : style->list_classes()) { for (const auto& class_name : style->list_classes()) {
style->remove_class(class_name); 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 // 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) { void Item::setCustomIcon(const std::string& id) {
@@ -287,8 +316,8 @@ void Item::processUpdatedProperties(Glib::RefPtr<Gio::AsyncResult>& _result) {
static const std::map<std::string_view, std::set<std::string_view>> signal2props = { static const std::map<std::string_view, std::set<std::string_view>> signal2props = {
{"NewTitle", {"Title"}}, {"NewTitle", {"Title"}},
{"NewIcon", {"IconName", "IconPixmap"}}, {"NewIcon", {"IconName", "IconPixmap"}},
// {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}}, {"NewAttentionIcon", {"AttentionIconName", "AttentionIconPixmap", "AttentionMovieName"}},
// {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}}, {"NewOverlayIcon", {"OverlayIconName", "OverlayIconPixmap"}},
{"NewIconThemePath", {"IconThemePath"}}, {"NewIconThemePath", {"IconThemePath"}},
{"NewToolTip", {"ToolTip"}}, {"NewToolTip", {"ToolTip"}},
{"NewStatus", {"Status"}}, {"NewStatus", {"Status"}},
@@ -336,11 +365,20 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
if (array != nullptr) { if (array != nullptr) {
g_free(array); g_free(array);
} }
#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 68 // We must allocate our own array because the data from GVariant is read-only
array = static_cast<guchar*>(g_memdup2(data, size)); // and we need to modify it to convert ARGB to RGBA.
#else array = static_cast<guchar*>(g_malloc(size));
array = static_cast<guchar*>(g_memdup(data, size));
#endif // 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; lwidth = width;
lheight = height; lheight = height;
} }
@@ -349,14 +387,6 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
} }
g_variant_iter_free(it); g_variant_iter_free(it);
if (array != nullptr) { 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, return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB, true, 8, lwidth,
lheight, 4 * lwidth, &pixbuf_data_deleter); lheight, 4 * lwidth, &pixbuf_data_deleter);
} }
@@ -365,47 +395,36 @@ Glib::RefPtr<Gdk::Pixbuf> Item::extractPixBuf(GVariant* variant) {
void Item::updateImage() { void Item::updateImage() {
auto pixbuf = getIconPixbuf(); auto pixbuf = getIconPixbuf();
if (!pixbuf) return;
auto scaled_icon_size = getScaledIconSize(); auto scaled_icon_size = getScaledIconSize();
// If the loaded icon is not square, assume that the icon height should match the // If the loaded icon is not square, assume that the icon height should match the
// requested icon size, but the width is allowed to be different. As such, if the // requested icon size, but the width is allowed to be different. As such, if the
// height of the image does not match the requested icon size, resize the icon such that // height of the image does not match the requested icon size, resize the icon such that
// the aspect ratio is maintained, but the height matches the requested icon size. // the aspect ratio is maintained, but the height matches the requested icon size.
if (pixbuf->get_height() != scaled_icon_size) { if (pixbuf->get_height() > 0 && pixbuf->get_height() != scaled_icon_size) {
int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height(); int width = scaled_icon_size * pixbuf->get_width() / pixbuf->get_height();
pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR); pixbuf = pixbuf->scale_simple(width, scaled_icon_size, Gdk::InterpType::INTERP_BILINEAR);
} }
pixbuf = overlayPixbufs(pixbuf, getOverlayIconPixbuf());
auto surface = auto surface =
Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), image.get_window()); Gdk::Cairo::create_surface_from_pixbuf(pixbuf, image.get_scale_factor(), image.get_window());
image.set(surface); image.set(surface);
} }
Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() { Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
if (!icon_name.empty()) { if (status_ == "needsattention") {
try { if (auto attention_pixbuf = getAttentionIconPixbuf()) {
std::ifstream temp(icon_name); return attention_pixbuf;
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()));
} }
} }
// 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) { if (icon_pixmap) {
return icon_pixmap; return icon_pixmap;
} }
@@ -420,9 +439,78 @@ Glib::RefPtr<Gdk::Pixbuf> Item::getIconPixbuf() {
return getIconByName("image-missing", getScaledIconSize()); return getIconByName("image-missing", getScaledIconSize());
} }
Glib::RefPtr<Gdk::Pixbuf> Item::getIconByName(const std::string& name, int request_size) { Glib::RefPtr<Gdk::Pixbuf> Item::getAttentionIconPixbuf() {
icon_theme->rescan_if_needed(); 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()) { if (!icon_theme_path.empty()) {
auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size, auto icon_info = icon_theme->lookup_icon(name.c_str(), request_size,
Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE);
@@ -464,6 +552,9 @@ void Item::makeMenu() {
} }
bool Item::handleClick(GdkEventButton* const& ev) { bool Item::handleClick(GdkEventButton* const& ev) {
if (!proxy_) {
return false;
}
auto parameters = Glib::VariantContainerBase::create_tuple( auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x_root + bar_.x_global), {Glib::Variant<int>::create(ev->x_root + bar_.x_global),
Glib::Variant<int>::create(ev->y_root + bar_.y_global)}); Glib::Variant<int>::create(ev->y_root + bar_.y_global)});
@@ -491,6 +582,9 @@ bool Item::handleClick(GdkEventButton* const& ev) {
} }
bool Item::handleScroll(GdkEventScroll* const& ev) { bool Item::handleScroll(GdkEventScroll* const& ev) {
if (!proxy_) {
return false;
}
int dx = 0, dy = 0; int dx = 0, dy = 0;
switch (ev->direction) { switch (ev->direction) {
case GDK_SCROLL_UP: 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), box_(bar.orientation, 0),
watcher_(SNI::Watcher::getInstance()), watcher_(SNI::Watcher::getInstance()),
host_(nb_hosts_, config, bar, std::bind(&Tray::onAdd, this, std::placeholders::_1), 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"); box_.set_name("tray");
event_box_.add(box_); event_box_.add(box_);
if (!id.empty()) { if (!id.empty()) {
@@ -33,6 +34,8 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
dp.emit(); dp.emit();
} }
void Tray::queueUpdate() { dp.emit(); }
void Tray::onAdd(std::unique_ptr<Item>& item) { void Tray::onAdd(std::unique_ptr<Item>& item) {
if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) { if (config_["reverse-direction"].isBool() && config_["reverse-direction"].asBool()) {
box_.pack_end(item->event_box); 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) { void Watcher::busAcquired(const Glib::RefPtr<Gio::DBus::Connection>& conn, Glib::ustring name) {
GError* error = nullptr; GError* error = nullptr;
waybar::util::ScopeGuard error_deleter([error]() { waybar::util::ScopeGuard error_deleter([&error]() {
if (error) { if (error) {
g_error_free(error); g_error_free(error);
} }
@@ -69,7 +69,7 @@ gboolean Watcher::handleRegisterHost(Watcher* obj, GDBusMethodInvocation* invoca
if (watch != nullptr) { if (watch != nullptr) {
g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered", g_warning("Status Notifier Host with bus name '%s' and object path '%s' is already registered",
bus_name, object_path); bus_name, object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation); sn_watcher_complete_register_host(obj->watcher_, invocation);
return TRUE; return TRUE;
} }
watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); 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); auto watch = gfWatchFind(obj->items_, bus_name, object_path);
if (watch != nullptr) { if (watch != nullptr) {
g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered", spdlog::debug("Ignoring duplicate Status Notifier Item registration for '{}' at '{}'", bus_name,
bus_name, object_path); object_path);
sn_watcher_complete_register_item(obj->watcher_, invocation); sn_watcher_complete_register_item(obj->watcher_, invocation);
return TRUE; 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); watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch);
if (watch->watcher->hosts_ == nullptr) { if (watch->watcher->hosts_ == nullptr) {
sn_watcher_set_is_host_registered(watch->watcher->watcher_, FALSE); 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) { } else if (watch->type == GF_WATCH_TYPE_ITEM) {
watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch); 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); sn_watcher_emit_item_unregistered(watch->watcher->watcher_, tmp);
g_free(tmp); g_free(tmp);
} }
gfWatchFree(watch);
} }
void Watcher::updateRegisteredItems(SnWatcher* obj) { void Watcher::updateRegisteredItems(SnWatcher* obj) {
+1 -1
View File
@@ -60,7 +60,7 @@ BarIpcClient::BarIpcClient(waybar::Bar& bar) : bar_{bar} {
}); });
} }
bool BarIpcClient::isModuleEnabled(std::string name) { bool BarIpcClient::isModuleEnabled(const std::string& name) {
for (const auto& section : {"modules-left", "modules-center", "modules-right"}) { for (const auto& section : {"modules-left", "modules-center", "modules-right"}) {
if (const auto& modules = bar_.config.get(section, {}); modules.isArray()) { if (const auto& modules = bar_.config.get(section, {}); modules.isArray()) {
for (const auto& module : modules) { for (const auto& module : modules) {

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