Merge branch 'master' into fix-appearance-change-css-watching
This commit is contained in:
@@ -52,3 +52,4 @@ result-*
|
|||||||
|
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
_codeql_detected_source_root
|
_codeql_detected_source_root
|
||||||
|
heaptrack*
|
||||||
|
|||||||
+8
-1
@@ -25,6 +25,10 @@ class AModule : public IModule {
|
|||||||
|
|
||||||
bool expandEnabled() const;
|
bool expandEnabled() const;
|
||||||
|
|
||||||
|
virtual void suspend() {};
|
||||||
|
virtual void resume() {};
|
||||||
|
bool shouldSuspend() const { return disable_on_sleep_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Don't need to make an object directly
|
// Don't need to make an object directly
|
||||||
// Derived classes are able to use it
|
// Derived classes are able to use it
|
||||||
@@ -48,7 +52,9 @@ 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_;
|
|
||||||
|
bool disable_on_sleep_{false};
|
||||||
|
GObject* menu_ = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleUserEvent(GdkEventButton* const& ev);
|
bool handleUserEvent(GdkEventButton* const& ev);
|
||||||
@@ -57,6 +63,7 @@ class AModule : public IModule {
|
|||||||
bool hasUserEvents_;
|
bool hasUserEvents_;
|
||||||
gdouble distance_scrolled_y_;
|
gdouble distance_scrolled_y_;
|
||||||
gdouble distance_scrolled_x_;
|
gdouble distance_scrolled_x_;
|
||||||
|
sigc::connection cursor_timeout_conn_;
|
||||||
std::map<std::string, std::string> eventActionMap_;
|
std::map<std::string, std::string> eventActionMap_;
|
||||||
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
static const inline std::map<std::pair<uint, GdkEventType>, std::string> eventMap_{
|
||||||
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
|
{std::make_pair(1, GdkEventType::GDK_BUTTON_PRESS), "on-click"},
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ class Bar : public sigc::trackable {
|
|||||||
util::KillSignalAction getOnSigusr1Action();
|
util::KillSignalAction getOnSigusr1Action();
|
||||||
util::KillSignalAction getOnSigusr2Action();
|
util::KillSignalAction getOnSigusr2Action();
|
||||||
|
|
||||||
|
void toggleSuspend(bool suspend);
|
||||||
|
|
||||||
struct waybar_output* output;
|
struct waybar_output* output;
|
||||||
Json::Value config;
|
Json::Value config;
|
||||||
struct wl_surface* surface;
|
struct wl_surface* surface;
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -59,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);
|
||||||
|
|
||||||
@@ -208,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class Memory : public ALabel {
|
|||||||
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
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class MPD : public ALabel {
|
|||||||
detail::unique_status status_;
|
detail::unique_status status_;
|
||||||
mpd_state state_;
|
mpd_state state_;
|
||||||
detail::unique_song song_;
|
detail::unique_song song_;
|
||||||
|
std::string ellipsis_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MPD(const std::string&, const Json::Value&);
|
MPD(const std::string&, const Json::Value&);
|
||||||
@@ -44,7 +45,11 @@ 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;
|
||||||
|
std::string getArtistStr(bool truncated) const;
|
||||||
|
std::string getAlbumArtistStr(bool truncated) const;
|
||||||
|
std::string getAlbumStr(bool truncated) const;
|
||||||
|
std::string getTitleStr(bool truncated) const;
|
||||||
|
|
||||||
// GUI-side methods
|
// GUI-side methods
|
||||||
bool handlePlayPause(GdkEventButton* const&);
|
bool handlePlayPause(GdkEventButton* const&);
|
||||||
@@ -54,7 +59,6 @@ class MPD : public ALabel {
|
|||||||
void tryConnect();
|
void tryConnect();
|
||||||
void checkErrors(mpd_connection* conn);
|
void checkErrors(mpd_connection* conn);
|
||||||
void fetchState();
|
void fetchState();
|
||||||
void queryMPD();
|
|
||||||
|
|
||||||
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
|
||||||
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
|
||||||
|
|||||||
@@ -197,7 +197,6 @@ class Context {
|
|||||||
void tryConnect() const;
|
void tryConnect() const;
|
||||||
void checkErrors(mpd_connection*) const;
|
void checkErrors(mpd_connection*) const;
|
||||||
void do_update();
|
void do_update();
|
||||||
void queryMPD() const;
|
|
||||||
void fetchState() const;
|
void fetchState() const;
|
||||||
constexpr mpd_state state() const;
|
constexpr mpd_state state() const;
|
||||||
void emit() const;
|
void emit() const;
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
|
inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
|
||||||
inline bool Context::is_playing() const { return mpd_module_->playing(); }
|
inline bool Context::is_playing() const { return mpd_module_->playing(); }
|
||||||
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
inline bool Context::is_paused() const { return mpd_module_->paused(); }
|
||||||
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
|
inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
|
||||||
|
|
||||||
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); }
|
constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_ / 1s; }
|
||||||
inline void Context::tryConnect() const { mpd_module_->tryConnect(); }
|
inline void Context::tryConnect() const { mpd_module_->tryConnect(); }
|
||||||
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
inline unique_connection& Context::connection() { return mpd_module_->connection_; }
|
||||||
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
|
constexpr inline mpd_state Context::state() const { return mpd_module_->state_; }
|
||||||
@@ -15,7 +16,6 @@ constexpr inline mpd_state Context::state() const { return mpd_module_->state_;
|
|||||||
inline void Context::do_update() { mpd_module_->setLabel(); }
|
inline void Context::do_update() { mpd_module_->setLabel(); }
|
||||||
|
|
||||||
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
|
inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
|
||||||
inline void Context::queryMPD() const { mpd_module_->queryMPD(); }
|
|
||||||
inline void Context::fetchState() const { mpd_module_->fetchState(); }
|
inline void Context::fetchState() const { mpd_module_->fetchState(); }
|
||||||
inline void Context::emit() const { mpd_module_->emit(); }
|
inline void Context::emit() const { mpd_module_->emit(); }
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class Privacy : public AModule {
|
|||||||
|
|
||||||
// Config
|
// Config
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
|
std::vector<PrivacyItem*> modules_;
|
||||||
uint iconSpacing = 4;
|
uint iconSpacing = 4;
|
||||||
uint iconSize = 20;
|
uint iconSize = 20;
|
||||||
uint transition_duration = 250;
|
uint transition_duration = 250;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -41,9 +46,12 @@ class Item : public sigc::trackable {
|
|||||||
std::string title;
|
std::string title;
|
||||||
std::string icon_name;
|
std::string icon_name;
|
||||||
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
Glib::RefPtr<Gdk::Pixbuf> icon_pixmap;
|
||||||
|
bool has_custom_icon_ = false;
|
||||||
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 +70,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 +79,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 +101,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_;
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ 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;
|
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
SNI::Watcher::singleton watcher_;
|
SNI::Watcher::singleton watcher_;
|
||||||
SNI::Host host_;
|
SNI::Host host_;
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
#include "util/sleeper_thread.hpp"
|
|
||||||
#include "util/SafeSignal.hpp"
|
#include "util/SafeSignal.hpp"
|
||||||
|
#include "util/scoped_fd.hpp"
|
||||||
|
#include "util/sleeper_thread.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::sway {
|
namespace waybar::modules::sway {
|
||||||
|
|
||||||
@@ -45,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_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ class Workspaces : public AModule, public sigc::trackable {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\"";
|
static constexpr std::string_view workspace_switch_cmd_ = "workspace {} \"{}\"";
|
||||||
|
static constexpr std::string_view workspace_switch_number_cmd_ = "workspace {} number {}";
|
||||||
static constexpr std::string_view persistent_workspace_switch_cmd_ =
|
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 +41,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_;
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class Temperature : public ALabel {
|
|||||||
Temperature(const std::string&, const Json::Value&);
|
Temperature(const std::string&, const Json::Value&);
|
||||||
virtual ~Temperature() = default;
|
virtual ~Temperature() = default;
|
||||||
auto update() -> void override;
|
auto update() -> void override;
|
||||||
|
void suspend() override;
|
||||||
|
void resume() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float getTemperature();
|
float getTemperature();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include <memory>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#if (FMT_VERSION >= 90000)
|
#if (FMT_VERSION >= 90000)
|
||||||
@@ -26,14 +27,19 @@ class JsonParser {
|
|||||||
Json::Value root;
|
Json::Value root;
|
||||||
|
|
||||||
// replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences
|
// replace all occurrences of "\x" with "\u00", because JSON doesn't allow "\x" escape sequences
|
||||||
std::string modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
|
std::string modifiedJsonStr;
|
||||||
|
const std::string* json = &jsonStr;
|
||||||
|
if (jsonStr.find("\\x") != std::string::npos) {
|
||||||
|
modifiedJsonStr = replaceHexadecimalEscape(jsonStr);
|
||||||
|
json = &modifiedJsonStr;
|
||||||
|
}
|
||||||
|
|
||||||
std::istringstream jsonStream(modifiedJsonStr);
|
|
||||||
std::string errs;
|
std::string errs;
|
||||||
// Use local CharReaderBuilder for thread safety - the IPC singleton's
|
// Use local CharReaderBuilder for thread safety - the IPC singleton's
|
||||||
// parser can be called concurrently from multiple module threads
|
// parser can be called concurrently from multiple module threads
|
||||||
Json::CharReaderBuilder readerBuilder;
|
Json::CharReaderBuilder readerBuilder;
|
||||||
if (!Json::parseFromStream(readerBuilder, jsonStream, &root, &errs)) {
|
auto reader = std::unique_ptr<Json::CharReader>(readerBuilder.newCharReader());
|
||||||
|
if (!reader->parse(json->data(), json->data() + json->size(), &root, &errs)) {
|
||||||
throw std::runtime_error("Error parsing JSON: " + errs);
|
throw std::runtime_error("Error parsing JSON: " + errs);
|
||||||
}
|
}
|
||||||
return root;
|
return root;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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,24 +66,34 @@ 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) {
|
||||||
std::unique_lock lk(mutex_);
|
std::unique_lock lk(mutex_);
|
||||||
CancellationGuard cancel_lock;
|
CancellationGuard cancel_lock;
|
||||||
|
|
||||||
|
condvar_.wait(lk, [this] {
|
||||||
|
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
|
||||||
|
!do_run_.load(std::memory_order_relaxed);
|
||||||
|
});
|
||||||
|
|
||||||
constexpr auto max_time_point = std::chrono::steady_clock::time_point::max();
|
constexpr auto max_time_point = std::chrono::steady_clock::time_point::max();
|
||||||
auto wait_end = max_time_point;
|
auto wait_end = max_time_point;
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
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 +101,30 @@ 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_; });
|
|
||||||
|
condvar_.wait(lk, [this] {
|
||||||
|
return !is_paused_ || signal_.load(std::memory_order_relaxed) ||
|
||||||
|
!do_run_.load(std::memory_order_relaxed);
|
||||||
|
});
|
||||||
|
|
||||||
|
return condvar_.wait_until(lk, time_point, [this] {
|
||||||
|
return signal_.load(std::memory_order_relaxed) || !do_run_.load(std::memory_order_relaxed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
@@ -106,6 +134,17 @@ class SleeperThread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pause() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
is_paused_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
is_paused_ = false;
|
||||||
|
condvar_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
~SleeperThread() {
|
~SleeperThread() {
|
||||||
connection_.disconnect();
|
connection_.disconnect();
|
||||||
stop();
|
stop();
|
||||||
@@ -118,9 +157,10 @@ 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_;
|
||||||
|
bool is_paused_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace waybar::util {
|
||||||
|
size_t utf8_width(const std::string& str);
|
||||||
|
void utf8_truncate(std::string& s, const std::string& ellipsis, size_t max_len);
|
||||||
|
} // namespace waybar::util
|
||||||
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -38,6 +38,20 @@ Addressed by *hyprland/language*
|
|||||||
typeof: array ++
|
typeof: array ++
|
||||||
The actions corresponding to the buttons of the menu.
|
The actions corresponding to the buttons of the menu.
|
||||||
|
|
||||||
|
*tooltip*: ++
|
||||||
|
typeof: boolean ++
|
||||||
|
default: true ++
|
||||||
|
Enables or disables the tooltip for the language module. By default, the tooltip is enabled. Set to *false* to disable.
|
||||||
|
|
||||||
|
*tooltip-format*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: {long} ++
|
||||||
|
Specifies the format of the tooltip when it is enabled. It follows the same format replacement rules as the *format* key.
|
||||||
|
|
||||||
|
*tooltip-format-<lang>*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Allows specifying a different tooltip format for each language. The *<lang>* should be replaced with the language code. This can be used to provide a custom tooltip for each language.
|
||||||
|
|
||||||
*expand*: ++
|
*expand*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
@@ -66,6 +80,28 @@ Addressed by *hyprland/language*
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/language": {
|
||||||
|
"format": "{}",
|
||||||
|
"format-en": "US",
|
||||||
|
"format-es": "ES",
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-format": "{long}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
"hyprland/language": {
|
||||||
|
"format": "{}",
|
||||||
|
"format-en": "US",
|
||||||
|
"format-es": "ES",
|
||||||
|
"tooltip": true,
|
||||||
|
"tooltip-format": "{}",
|
||||||
|
"tooltip-format-es": "{Español}",
|
||||||
|
"tooltip-format-en": "{English (american)}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# STYLE
|
# STYLE
|
||||||
|
|
||||||
- *#language*
|
- *#language*
|
||||||
|
|||||||
@@ -135,6 +135,14 @@ This setting is ignored if *workspace-taskbar.enable* is set to true.
|
|||||||
default: false ++
|
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.
|
If set to false, you can't scroll to cycle throughout workspaces from the entire bar. If set to true this behaviour is enabled.
|
||||||
|
|
||||||
|
*on-scroll-up*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling up on the module. This replaces the default behaviour of workspace cycling.
|
||||||
|
|
||||||
|
*on-scroll-down*: ++
|
||||||
|
typeof: string ++
|
||||||
|
Command to execute when scrolling down on the module. This replaces the default behaviour of workspace cycling.
|
||||||
|
|
||||||
*ignore-workspaces*: ++
|
*ignore-workspaces*: ++
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
default: [] ++
|
default: [] ++
|
||||||
|
|||||||
+12
-6
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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": "",
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ Addressed by *river/mode*
|
|||||||
|
|
||||||
```
|
```
|
||||||
"river/mode": {
|
"river/mode": {
|
||||||
"format": " {}"
|
"format": " {}"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Addressed by *sway/mode*
|
|||||||
|
|
||||||
```
|
```
|
||||||
"sway/mode": {
|
"sway/mode": {
|
||||||
"format": " {}",
|
"format": " {}",
|
||||||
"max-length": 50
|
"max-length": 50
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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})",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 ++
|
||||||
|
|||||||
+3
-1
@@ -185,7 +185,9 @@ 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',
|
||||||
|
'src/util/utf8_string.cpp'
|
||||||
)
|
)
|
||||||
|
|
||||||
man_files = files(
|
man_files = files(
|
||||||
|
|||||||
+3
-3
@@ -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
|
||||||
|
|||||||
@@ -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": "",
|
||||||
|
|||||||
+10
-5
@@ -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 {};
|
||||||
}
|
}
|
||||||
@@ -156,14 +156,19 @@ void AAppIconLabel::updateAppIcon() {
|
|||||||
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
@@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-5
@@ -17,10 +17,14 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
|||||||
isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true},
|
isTooltip{config_["tooltip"].isBool() ? config_["tooltip"].asBool() : true},
|
||||||
isExpand{config_["expand"].isBool() ? config_["expand"].asBool() : false},
|
isExpand{config_["expand"].isBool() ? config_["expand"].asBool() : false},
|
||||||
distance_scrolled_y_(0.0),
|
distance_scrolled_y_(0.0),
|
||||||
distance_scrolled_x_(0.0) {
|
distance_scrolled_x_(0.0),
|
||||||
|
cursor_timeout_conn_() {
|
||||||
// Configure module action Map
|
// Configure module action Map
|
||||||
const Json::Value actions{config_["actions"]};
|
const Json::Value actions{config_["actions"]};
|
||||||
|
|
||||||
|
disable_on_sleep_ =
|
||||||
|
config_["disable-on-sleep"].isBool() ? config_["disable-on-sleep"].asBool() : false;
|
||||||
|
|
||||||
for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) {
|
for (Json::Value::const_iterator it = actions.begin(); it != actions.end(); ++it) {
|
||||||
if (it.key().isString() && it->isString())
|
if (it.key().isString() && it->isString())
|
||||||
if (!eventActionMap_.contains(it.key().asString())) {
|
if (!eventActionMap_.contains(it.key().asString())) {
|
||||||
@@ -72,8 +76,12 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
|||||||
|
|
||||||
// Respect user configuration of cursor
|
// Respect user configuration of cursor
|
||||||
if (config_.isMember("cursor")) {
|
if (config_.isMember("cursor")) {
|
||||||
if (config_["cursor"].isBool() && config_["cursor"].asBool()) {
|
if (config_["cursor"].isBool()) {
|
||||||
|
if (config_["cursor"].asBool()) {
|
||||||
setCursor(Gdk::HAND2);
|
setCursor(Gdk::HAND2);
|
||||||
|
} else {
|
||||||
|
setCursor(Gdk::ARROW);
|
||||||
|
}
|
||||||
} else if (config_["cursor"].isInt()) {
|
} else if (config_["cursor"].isInt()) {
|
||||||
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
|
setCursor(Gdk::CursorType(config_["cursor"].asInt()));
|
||||||
} else {
|
} else {
|
||||||
@@ -83,11 +91,18 @@ AModule::AModule(const Json::Value& config, const std::string& name, const std::
|
|||||||
}
|
}
|
||||||
|
|
||||||
AModule::~AModule() {
|
AModule::~AModule() {
|
||||||
|
if (cursor_timeout_conn_.connected()) {
|
||||||
|
cursor_timeout_conn_.disconnect();
|
||||||
|
}
|
||||||
for (const auto& pid : pid_children_) {
|
for (const auto& pid : pid_children_) {
|
||||||
if (pid != -1) {
|
if (pid != -1) {
|
||||||
killpg(pid, SIGTERM);
|
killpg(pid, SIGTERM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (menu_ != nullptr) {
|
||||||
|
g_object_unref(menu_);
|
||||||
|
menu_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AModule::update() -> void {
|
auto AModule::update() -> void {
|
||||||
@@ -114,7 +129,7 @@ void AModule::setCursor(Gdk::CursorType const& c) {
|
|||||||
} else {
|
} else {
|
||||||
// window may not be accessible yet, in this case,
|
// window may not be accessible yet, in this case,
|
||||||
// schedule another call for setting the cursor in 1 sec
|
// schedule another call for setting the cursor in 1 sec
|
||||||
Glib::signal_timeout().connect_seconds(
|
cursor_timeout_conn_ = Glib::signal_timeout().connect_seconds(
|
||||||
[this, c]() {
|
[this, c]() {
|
||||||
setCursor(c);
|
setCursor(c);
|
||||||
return false;
|
return false;
|
||||||
@@ -166,9 +181,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));
|
||||||
|
|||||||
+35
-4
@@ -263,6 +263,16 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
|
|||||||
|
|
||||||
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
window.signal_map_event().connect_notify(sigc::mem_fun(*this, &Bar::onMap));
|
||||||
|
|
||||||
|
window.signal_unmap().connect([this]() {
|
||||||
|
spdlog::debug("Output {} unmapped (DPMS off), suspending modules", output->name);
|
||||||
|
toggleSuspend(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.signal_map().connect([this]() {
|
||||||
|
spdlog::debug("Output {} mapped (DPMS on), resuming modules", output->name);
|
||||||
|
toggleSuspend(false);
|
||||||
|
});
|
||||||
|
|
||||||
#if HAVE_SWAY
|
#if HAVE_SWAY
|
||||||
if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) {
|
if (auto ipc = config["ipc"]; ipc.isBool() && ipc.asBool()) {
|
||||||
bar_id = Client::inst()->bar_id;
|
bar_id = Client::inst()->bar_id;
|
||||||
@@ -541,13 +551,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 =
|
||||||
getModules(factory, ref, group_module);
|
std::make_unique<waybar::Group>(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);
|
||||||
}
|
}
|
||||||
@@ -694,3 +706,22 @@ void waybar::Bar::configureGlobalOffset(int width, int height) {
|
|||||||
void waybar::Bar::onOutputGeometryChanged() {
|
void waybar::Bar::onOutputGeometryChanged() {
|
||||||
configureGlobalOffset(window.get_width(), window.get_height());
|
configureGlobalOffset(window.get_width(), window.get_height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waybar::Bar::toggleSuspend(bool suspend) {
|
||||||
|
auto process_modules = [suspend](Gtk::Box& module_box) {
|
||||||
|
for (auto* widget : module_box.get_children()) {
|
||||||
|
auto* module = dynamic_cast<waybar::AModule*>(widget);
|
||||||
|
if (module && module->shouldSuspend()) {
|
||||||
|
if (suspend) {
|
||||||
|
module->suspend();
|
||||||
|
} else {
|
||||||
|
module->resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process_modules(left_);
|
||||||
|
process_modules(center_);
|
||||||
|
process_modules(right_);
|
||||||
|
}
|
||||||
|
|||||||
+18
-1
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
+74
-55
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,7 @@ void waybar::modules::Battery::refreshBatteries() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (fs::filesystem_error& e) {
|
} catch (fs::filesystem_error& e) {
|
||||||
throw std::runtime_error(e.what());
|
spdlog::warn("Battery directory tracking failed: {}", e.what());
|
||||||
}
|
}
|
||||||
if (warnFirstTime_ && batteries_.empty()) {
|
if (warnFirstTime_ && batteries_.empty()) {
|
||||||
if (config_["bat"].isString()) {
|
if (config_["bat"].isString()) {
|
||||||
@@ -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,74 @@ 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"}) {
|
||||||
|
if (current_now_f >> _current_now_int) {
|
||||||
current_now_exists = true;
|
current_now_exists = true;
|
||||||
std::ifstream(bat / "current_now") >> _current_now_int;
|
}
|
||||||
} else if (fs::exists(bat / "current_avg")) {
|
} else if (std::ifstream current_avg_f{bat / "current_avg"}) {
|
||||||
|
if (current_avg_f >> _current_now_int) {
|
||||||
current_now_exists = true;
|
current_now_exists = true;
|
||||||
std::ifstream(bat / "current_avg") >> _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"}) {
|
||||||
|
if (f >> 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;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs::exists(bat / "time_to_full_now")) {
|
if (std::ifstream f{bat / "time_to_full_now"}) {
|
||||||
|
if (f >> 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;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"}) {
|
||||||
|
if (voltage_now_f >> voltage_now) {
|
||||||
voltage_now_exists = true;
|
voltage_now_exists = true;
|
||||||
std::ifstream(bat / "voltage_now") >> voltage_now;
|
}
|
||||||
} else if (fs::exists(bat / "voltage_avg")) {
|
} else if (std::ifstream voltage_avg_f{bat / "voltage_avg"}) {
|
||||||
|
if (voltage_avg_f >> voltage_now) {
|
||||||
voltage_now_exists = true;
|
voltage_now_exists = true;
|
||||||
std::ifstream(bat / "voltage_avg") >> 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"}) {
|
||||||
|
if (f >> charge_full) {
|
||||||
charge_full_exists = true;
|
charge_full_exists = true;
|
||||||
std::ifstream(bat / "charge_full") >> 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"}) {
|
||||||
|
if (f >> charge_full_design) {
|
||||||
charge_full_design_exists = true;
|
charge_full_design_exists = true;
|
||||||
std::ifstream(bat / "charge_full_design") >> 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"}) {
|
||||||
|
if (f >> charge_now) {
|
||||||
charge_now_exists = true;
|
charge_now_exists = true;
|
||||||
std::ifstream(bat / "charge_now") >> 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"}) {
|
||||||
|
if (f >> _power_now_int) {
|
||||||
power_now_exists = true;
|
power_now_exists = true;
|
||||||
std::ifstream(bat / "power_now") >> _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 +362,31 @@ 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"}) {
|
||||||
|
if (f >> energy_now) {
|
||||||
energy_now_exists = true;
|
energy_now_exists = true;
|
||||||
std::ifstream(bat / "energy_now") >> 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"}) {
|
||||||
|
if (f >> energy_full) {
|
||||||
energy_full_exists = true;
|
energy_full_exists = true;
|
||||||
std::ifstream(bat / "energy_full") >> 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"}) {
|
||||||
|
if (f >> energy_full_design) {
|
||||||
energy_full_design_exists = true;
|
energy_full_design_exists = true;
|
||||||
std::ifstream(bat / "energy_full_design") >> 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 +416,10 @@ 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"}) {
|
||||||
|
if (f >> capacity) {
|
||||||
capacity_exists = true;
|
capacity_exists = true;
|
||||||
std::ifstream(bat / "capacity") >> capacity;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!voltage_now_exists) {
|
if (!voltage_now_exists) {
|
||||||
@@ -584,13 +599,14 @@ waybar::modules::Battery::getInfos() {
|
|||||||
|
|
||||||
float calculated_capacity{0.0f};
|
float calculated_capacity{0.0f};
|
||||||
if (total_capacity_exists) {
|
if (total_capacity_exists) {
|
||||||
if (total_capacity > 0.0f)
|
if (total_capacity > 0.0f) {
|
||||||
calculated_capacity = (float)total_capacity / batteries_.size();
|
calculated_capacity = (float)total_capacity / batteries_.size();
|
||||||
else if (total_energy_full_exists && total_energy_exists) {
|
} else if (total_energy_full_exists && total_energy_exists) {
|
||||||
if (total_energy_full > 0.0f)
|
if (total_energy_full > 0.0f) {
|
||||||
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle weighted-average
|
// Handle weighted-average
|
||||||
if (weightedAverage_ && total_energy_exists && total_energy_full_exists) {
|
if (weightedAverage_ && total_energy_exists && total_energy_full_exists) {
|
||||||
@@ -790,16 +806,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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()});
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -46,12 +46,14 @@ 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));
|
||||||
|
|
||||||
|
|||||||
@@ -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&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,14 @@ 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));
|
||||||
|
|
||||||
@@ -78,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 {
|
||||||
@@ -100,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 {
|
||||||
|
|||||||
+19
-6
@@ -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,
|
||||||
@@ -37,6 +39,11 @@ waybar::modules::Custom::~Custom() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::Custom::delayWorker() {
|
void waybar::modules::Custom::delayWorker() {
|
||||||
|
if (!config_["exec"].isString() && !config_["exec-if"].isString()) {
|
||||||
|
dp.emit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
for (int i : this->pid_children_) {
|
for (int i : this->pid_children_) {
|
||||||
int status;
|
int status;
|
||||||
@@ -180,16 +187,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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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") {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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", on_current_monitor = true })
|
||||||
|
//
|
||||||
|
// 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 + "\", on_current_monitor = true })";
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|||||||
@@ -48,12 +48,39 @@ auto Language::update() -> void {
|
|||||||
fmt::arg("shortDescription", layout_.short_description),
|
fmt::arg("shortDescription", layout_.short_description),
|
||||||
fmt::arg("variant", layout_.variant)));
|
fmt::arg("variant", layout_.variant)));
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::debug("hyprland language formatted layout name {}", layoutName);
|
spdlog::debug("hyprland language formatted layout name {}", layoutName);
|
||||||
|
|
||||||
|
std::string tooltipContent = std::string{};
|
||||||
|
bool tooltip_enabled = tooltipEnabled();
|
||||||
|
if (tooltip_enabled) {
|
||||||
|
if (config_.isMember("tooltip-format")) {
|
||||||
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
|
if (config_.isMember("tooltip-format-" + layout_.short_description + "-" + layout_.variant)) {
|
||||||
|
const auto propName = "tooltip-format-" + layout_.short_description + "-" + layout_.variant;
|
||||||
|
tooltipContent = fmt::format(fmt::runtime(tooltip_format), config_[propName].asString());
|
||||||
|
} else if (config_.isMember("tooltip-format-" + layout_.short_description)) {
|
||||||
|
const auto propName = "tooltip-format-" + layout_.short_description;
|
||||||
|
tooltipContent = fmt::format(fmt::runtime(tooltip_format), config_[propName].asString());
|
||||||
|
} else {
|
||||||
|
tooltipContent =
|
||||||
|
trim(fmt::format(fmt::runtime(tooltip_format), fmt::arg("long", layout_.full_name),
|
||||||
|
fmt::arg("short", layout_.short_name),
|
||||||
|
fmt::arg("shortDescription", layout_.short_description),
|
||||||
|
fmt::arg("variant", layout_.variant)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if no tooltip format is provided, use the same text as the module
|
||||||
|
tooltipContent = layoutName;
|
||||||
|
}
|
||||||
|
spdlog::debug("hyprland language formatted tooltip content {}", tooltipContent);
|
||||||
|
}
|
||||||
|
|
||||||
if (!format_.empty()) {
|
if (!format_.empty()) {
|
||||||
label_.show();
|
label_.show();
|
||||||
label_.set_markup(layoutName);
|
label_.set_markup(layoutName);
|
||||||
|
if (tooltip_enabled) {
|
||||||
|
label_.set_tooltip_markup(tooltipContent);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
label_.hide();
|
label_.hide();
|
||||||
}
|
}
|
||||||
@@ -63,22 +90,43 @@ 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;
|
||||||
|
}
|
||||||
// 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")) {
|
||||||
|
const auto keyboardName = config_["keyboard-name"].asString();
|
||||||
|
// The keyboard name itself can contain commas, so match it as a full prefix
|
||||||
|
// (followed by the ',' separator) rather than comparing against the substring
|
||||||
|
// before the first comma, which would truncate such names and drop the event.
|
||||||
|
if (payload.size() <= keyboardName.size() || payload[keyboardName.size()] != ',' ||
|
||||||
|
payload.compare(0, keyboardName.size(), keyboardName) != 0)
|
||||||
return; // ignore
|
return; // ignore
|
||||||
|
}
|
||||||
|
|
||||||
layoutName = waybar::util::sanitize_string(layoutName);
|
layoutName = waybar::util::sanitize_string(layoutName);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,21 +108,14 @@ auto Window::update() -> void {
|
|||||||
AAppIconLabel::update();
|
AAppIconLabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Window::getActiveWorkspace() -> Workspace {
|
auto Window::getActiveWorkspace() -> Workspace { return getActiveWorkspace(""); }
|
||||||
const auto workspace = IPC::inst().getSocket1JsonReply("activeworkspace");
|
|
||||||
|
|
||||||
if (workspace.isObject()) {
|
|
||||||
return Workspace::parse(workspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
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, [&](const Json::Value& monitor) {
|
||||||
monitors, [&](Json::Value monitor) { return monitor["name"] == monitorName; });
|
return monitorName.empty() ? monitor["focused"].asBool() : 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{
|
||||||
@@ -134,12 +125,13 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace {
|
|||||||
.last_window_title = "",
|
.last_window_title = "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const int id = (*monitor)["activeWorkspace"]["id"].asInt();
|
const int special_id = (*monitor)["specialWorkspace"]["id"].asInt();
|
||||||
|
const int id = special_id != 0 ? special_id : (*monitor)["activeWorkspace"]["id"].asInt();
|
||||||
|
|
||||||
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 +169,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 +228,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_ = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
@@ -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});
|
||||||
}
|
}
|
||||||
@@ -301,15 +301,18 @@ 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_markup(window_repr.window_title);
|
window_box->set_tooltip_markup(window_repr.window_title);
|
||||||
window_box->get_style_context()->add_class("taskbar-window");
|
|
||||||
|
auto button = Gtk::manage(new Gtk::Button());
|
||||||
|
button->set_relief(Gtk::RELIEF_NONE);
|
||||||
|
button->add(*window_box);
|
||||||
|
button->get_style_context()->add_class("taskbar-window");
|
||||||
if (window_repr.isActive) {
|
if (window_repr.isActive) {
|
||||||
window_box->get_style_context()->add_class("active");
|
button->get_style_context()->add_class("active");
|
||||||
}
|
}
|
||||||
auto event_box = Gtk::manage(new Gtk::EventBox());
|
|
||||||
event_box->add(*window_box);
|
|
||||||
if (m_workspaceManager.onClickWindow() != "") {
|
if (m_workspaceManager.onClickWindow() != "") {
|
||||||
event_box->signal_button_press_event().connect(
|
button->signal_button_press_event().connect(
|
||||||
sigc::bind(sigc::mem_fun(*this, &Workspace::handleClick), window_repr.address));
|
sigc::bind(sigc::mem_fun(*this, &Workspace::handleClick), window_repr.address),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto text_before = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatBefore()),
|
auto text_before = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatBefore()),
|
||||||
@@ -334,8 +337,8 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) {
|
|||||||
window_box->pack_start(*window_label_after, true, true);
|
window_box->pack_start(*window_label_after, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_content.pack_start(*event_box, true, false);
|
m_content.pack_start(*button, true, false);
|
||||||
event_box->show_all();
|
button->show_all();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (m_workspaceManager.taskbarReverseDirection()) {
|
if (m_workspaceManager.taskbarReverseDirection()) {
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -44,9 +47,14 @@ void Workspaces::init() {
|
|||||||
|
|
||||||
initializeWorkspaces();
|
initializeWorkspaces();
|
||||||
|
|
||||||
if (barScroll()) {
|
if (m_scrollEventConnection_.connected()) {
|
||||||
|
m_scrollEventConnection_.disconnect();
|
||||||
|
}
|
||||||
|
bool hasScrollConfig = config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString();
|
||||||
|
if (barScroll() || hasScrollConfig) {
|
||||||
auto& window = const_cast<Bar&>(m_bar).window;
|
auto& window = const_cast<Bar&>(m_bar).window;
|
||||||
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||||
|
m_scrollEventConnection_ =
|
||||||
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
|
window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +163,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);
|
||||||
@@ -196,7 +205,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()) &&
|
||||||
@@ -270,7 +279,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,6 +327,10 @@ void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value& c
|
|||||||
// 2. the rule's monitor is the current monitor
|
// 2. the rule's monitor is the current monitor
|
||||||
// 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor
|
// 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor
|
||||||
if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) {
|
if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) {
|
||||||
|
// => skip ignore-workspaces even if its a persistent
|
||||||
|
if(isWorkspaceIgnored(workspace)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// => persistent workspace should be shown on this monitor
|
// => persistent workspace should be shown on this monitor
|
||||||
auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name);
|
auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name);
|
||||||
workspaceData["persistent-rule"] = true;
|
workspaceData["persistent-rule"] = true;
|
||||||
@@ -331,8 +344,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);
|
||||||
@@ -400,7 +418,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();
|
||||||
@@ -495,19 +513,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);
|
||||||
@@ -1001,10 +1021,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;
|
||||||
}
|
}
|
||||||
@@ -1133,7 +1155,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};
|
||||||
}
|
}
|
||||||
@@ -1142,6 +1168,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));
|
||||||
@@ -1164,6 +1193,12 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
|||||||
if (gdk_event_get_pointer_emulated((GdkEvent*)e)) {
|
if (gdk_event_get_pointer_emulated((GdkEvent*)e)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for custom scroll commands first; delegate to base class
|
||||||
|
if (config_["on-scroll-up"].isString() || config_["on-scroll-down"].isString()) {
|
||||||
|
return AModule::handleScroll(e);
|
||||||
|
}
|
||||||
|
|
||||||
auto dir = AModule::getScrollDir(e);
|
auto dir = AModule::getScrollDir(e);
|
||||||
if (dir == SCROLL_DIR::NONE) {
|
if (dir == SCROLL_DIR::NONE) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1171,15 +1206,15 @@ bool Workspaces::handleScroll(GdkEventScroll *e) {
|
|||||||
|
|
||||||
if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) {
|
if (dir == SCROLL_DIR::DOWN || dir == SCROLL_DIR::RIGHT) {
|
||||||
if (allOutputs()) {
|
if (allOutputs()) {
|
||||||
m_ipc.getSocket1Reply("dispatch workspace e+1");
|
IPC::dispatch("workspace", "e+1");
|
||||||
} else {
|
} else {
|
||||||
m_ipc.getSocket1Reply("dispatch workspace m+1");
|
IPC::dispatch("workspace", "m+1");
|
||||||
}
|
}
|
||||||
} else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
|
} else if (dir == SCROLL_DIR::UP || dir == SCROLL_DIR::LEFT) {
|
||||||
if (allOutputs()) {
|
if (allOutputs()) {
|
||||||
m_ipc.getSocket1Reply("dispatch workspace e-1");
|
IPC::dispatch("workspace", "e-1");
|
||||||
} else {
|
} else {
|
||||||
m_ipc.getSocket1Reply("dispatch workspace m-1");
|
IPC::dispatch("workspace", "m-1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,11 +1,32 @@
|
|||||||
#include "modules/memory.hpp"
|
#include "modules/memory.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const std::unordered_map<std::string, float> kUnits = {
|
||||||
|
{"kB", 1.000},
|
||||||
|
{"kiB", 1.024},
|
||||||
|
{"MB", 1.000 * 1000.0},
|
||||||
|
{"MiB", 1.024 * 1024.0},
|
||||||
|
{"GB", 1.000 * 1000.0 * 1000.0},
|
||||||
|
{"GiB", 1.024 * 1024.0 * 1024.0},
|
||||||
|
{"TB", 1.000 * 1000.0 * 1000.0 * 1000.0},
|
||||||
|
{"TiB", 1.024 * 1024.0 * 1024.0 * 1024.0}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
|
waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config)
|
||||||
: ALabel(config, "memory", id, "{}%", 30) {
|
: ALabel(config, "memory", id, "{}%", 30) {
|
||||||
thread_ = [this] {
|
thread_ = [this] {
|
||||||
dp.emit();
|
dp.emit();
|
||||||
thread_.sleep_for(interval_);
|
thread_.sleep_for(interval_);
|
||||||
};
|
};
|
||||||
|
if (config["unit"].isString()) {
|
||||||
|
unit_ = config["unit"].asString();
|
||||||
|
if (!kUnits.contains(unit_)) {
|
||||||
|
unit_ = "GiB";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unit_ = "GiB";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto waybar::modules::Memory::update() -> void {
|
auto waybar::modules::Memory::update() -> void {
|
||||||
@@ -13,15 +34,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 +52,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 = kUnits.at(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,12 +80,12 @@ 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()) {
|
||||||
@@ -71,14 +93,14 @@ auto waybar::modules::Memory::update() -> void {
|
|||||||
auto tooltip_format = config_["tooltip-format"].asString();
|
auto tooltip_format = config_["tooltip-format"].asString();
|
||||||
label_.set_tooltip_markup(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_markup(fmt::format("{:.{}f}GiB used", used_ram_gigabytes, 1));
|
label_.set_tooltip_markup(fmt::format("{:.{}f}{} used", used_ram, 1, unit_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+47
-35
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <util/sanitize_str.hpp>
|
#include <util/sanitize_str.hpp>
|
||||||
|
#include <util/utf8_string.hpp>
|
||||||
using namespace waybar::util;
|
using namespace waybar::util;
|
||||||
|
|
||||||
#include "modules/mpd/state.hpp"
|
#include "modules/mpd/state.hpp"
|
||||||
@@ -24,7 +25,8 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
|
|||||||
timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
|
timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
|
||||||
connection_(nullptr, &mpd_connection_free),
|
connection_(nullptr, &mpd_connection_free),
|
||||||
status_(nullptr, &mpd_status_free),
|
status_(nullptr, &mpd_status_free),
|
||||||
song_(nullptr, &mpd_song_free) {
|
song_(nullptr, &mpd_song_free),
|
||||||
|
ellipsis_(config_["ellipsis"].isString() ? config_["ellipsis"].asString() : "\u2026") {
|
||||||
if (!config_["port"].isNull() && !config_["port"].isUInt()) {
|
if (!config_["port"].isNull() && !config_["port"].isUInt()) {
|
||||||
spdlog::warn("{}: `port` configuration should be an unsigned int", module_name_);
|
spdlog::warn("{}: `port` configuration should be an unsigned int", module_name_);
|
||||||
}
|
}
|
||||||
@@ -51,21 +53,6 @@ auto waybar::modules::MPD::update() -> void {
|
|||||||
ALabel::update();
|
ALabel::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void waybar::modules::MPD::queryMPD() {
|
|
||||||
if (connection_ != nullptr) {
|
|
||||||
spdlog::trace("{}: fetching state information", module_name_);
|
|
||||||
try {
|
|
||||||
fetchState();
|
|
||||||
spdlog::trace("{}: fetch complete", module_name_);
|
|
||||||
} catch (std::exception const& e) {
|
|
||||||
spdlog::error("{}: {}", module_name_, e.what());
|
|
||||||
state_ = MPD_STATE_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
dp.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const {
|
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const {
|
||||||
std::string result =
|
std::string result =
|
||||||
config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
|
config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
|
||||||
@@ -117,15 +104,15 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
label_.get_style_context()->remove_class("disconnected");
|
label_.get_style_context()->remove_class("disconnected");
|
||||||
|
|
||||||
auto format = format_;
|
auto format = format_;
|
||||||
Glib::ustring artist, album_artist, album, title;
|
|
||||||
std::string date, filename, uri;
|
std::string date, filename, uri;
|
||||||
int song_pos = 0, queue_length = 0, volume = 0;
|
int song_pos = 0, queue_length = 0, volume = 0;
|
||||||
std::chrono::seconds elapsedTime, totalTime;
|
std::chrono::seconds elapsedTime, totalTime;
|
||||||
|
|
||||||
std::string stateIcon = "";
|
std::string stateIcon = "";
|
||||||
bool no_song = song_.get() == nullptr;
|
bool no_song = song_.get() == nullptr;
|
||||||
if (stopped() || no_song) {
|
bool is_stopped = stopped();
|
||||||
if (no_song) spdlog::warn("Bug in mpd: no current song but state is not stopped.");
|
if (is_stopped || no_song) {
|
||||||
|
if (no_song && !is_stopped) spdlog::warn("mpd: no current song while state is not stopped");
|
||||||
format =
|
format =
|
||||||
config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped";
|
config_["format-stopped"].isString() ? config_["format-stopped"].asString() : "stopped";
|
||||||
label_.get_style_context()->add_class("stopped");
|
label_.get_style_context()->add_class("stopped");
|
||||||
@@ -145,10 +132,6 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
|
|
||||||
stateIcon = getStateIcon();
|
stateIcon = getStateIcon();
|
||||||
|
|
||||||
artist = sanitize_string(getTag(MPD_TAG_ARTIST));
|
|
||||||
album_artist = sanitize_string(getTag(MPD_TAG_ALBUM_ARTIST));
|
|
||||||
album = sanitize_string(getTag(MPD_TAG_ALBUM));
|
|
||||||
title = sanitize_string(getTag(MPD_TAG_TITLE));
|
|
||||||
date = sanitize_string(getTag(MPD_TAG_DATE));
|
date = sanitize_string(getTag(MPD_TAG_DATE));
|
||||||
filename = sanitize_string(getFilename());
|
filename = sanitize_string(getFilename());
|
||||||
uri = mpd_song_get_uri(song_.get());
|
uri = mpd_song_get_uri(song_.get());
|
||||||
@@ -170,17 +153,12 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
std::string repeatIcon = getOptionIcon("repeat", repeatActivated);
|
std::string repeatIcon = getOptionIcon("repeat", repeatActivated);
|
||||||
bool singleActivated = mpd_status_get_single(status_.get());
|
bool singleActivated = mpd_status_get_single(status_.get());
|
||||||
std::string singleIcon = getOptionIcon("single", singleActivated);
|
std::string singleIcon = getOptionIcon("single", singleActivated);
|
||||||
if (config_["artist-len"].isInt()) artist = artist.substr(0, config_["artist-len"].asInt());
|
|
||||||
if (config_["album-artist-len"].isInt())
|
|
||||||
album_artist = album_artist.substr(0, config_["album-artist-len"].asInt());
|
|
||||||
if (config_["album-len"].isInt()) album = album.substr(0, config_["album-len"].asInt());
|
|
||||||
if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto text = fmt::format(
|
auto text = fmt::format(
|
||||||
fmt::runtime(format), fmt::arg("artist", artist.raw()),
|
fmt::runtime(format), fmt::arg("artist", getArtistStr(true)),
|
||||||
fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()),
|
fmt::arg("albumArtist", getAlbumArtistStr(true)), fmt::arg("album", getAlbumStr(true)),
|
||||||
fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume),
|
fmt::arg("title", getTitleStr(true)), fmt::arg("date", date), fmt::arg("volume", volume),
|
||||||
fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime),
|
fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime),
|
||||||
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
||||||
@@ -202,9 +180,9 @@ void waybar::modules::MPD::setLabel() {
|
|||||||
: "MPD (connected)";
|
: "MPD (connected)";
|
||||||
try {
|
try {
|
||||||
auto tooltip_text = fmt::format(
|
auto tooltip_text = fmt::format(
|
||||||
fmt::runtime(tooltip_format), fmt::arg("artist", artist.raw()),
|
fmt::runtime(tooltip_format), fmt::arg("artist", getArtistStr(false)),
|
||||||
fmt::arg("albumArtist", album_artist.raw()), fmt::arg("album", album.raw()),
|
fmt::arg("albumArtist", getAlbumArtistStr(false)), fmt::arg("album", getAlbumStr(false)),
|
||||||
fmt::arg("title", title.raw()), fmt::arg("date", date), fmt::arg("volume", volume),
|
fmt::arg("title", getTitleStr(false)), fmt::arg("date", date), fmt::arg("volume", volume),
|
||||||
fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime),
|
fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime),
|
||||||
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length),
|
||||||
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon),
|
||||||
@@ -239,7 +217,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 "";
|
||||||
}
|
}
|
||||||
@@ -256,6 +235,38 @@ std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool act
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string waybar::modules::MPD::getArtistStr(bool truncated) const {
|
||||||
|
std::string artist = getTag(MPD_TAG_ARTIST);
|
||||||
|
if (truncated && config_["artist-len"].isInt()) {
|
||||||
|
waybar::util::utf8_truncate(artist, ellipsis_, config_["artist-len"].asInt());
|
||||||
|
}
|
||||||
|
return sanitize_string(artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string waybar::modules::MPD::getAlbumArtistStr(bool truncated) const {
|
||||||
|
std::string album_artist = getTag(MPD_TAG_ALBUM_ARTIST);
|
||||||
|
if (truncated && config_["album-artist-len"].isInt()) {
|
||||||
|
waybar::util::utf8_truncate(album_artist, ellipsis_, config_["album-artist-len"].asInt());
|
||||||
|
}
|
||||||
|
return sanitize_string(album_artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string waybar::modules::MPD::getAlbumStr(bool truncated) const {
|
||||||
|
std::string album = getTag(MPD_TAG_ALBUM);
|
||||||
|
if (truncated && config_["album-len"].isInt()) {
|
||||||
|
waybar::util::utf8_truncate(album, ellipsis_, config_["album-len"].asInt());
|
||||||
|
}
|
||||||
|
return sanitize_string(album);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string waybar::modules::MPD::getTitleStr(bool truncated) const {
|
||||||
|
std::string title = getTag(MPD_TAG_TITLE);
|
||||||
|
if (truncated && config_["title-len"].isInt()) {
|
||||||
|
waybar::util::utf8_truncate(title, ellipsis_, config_["title-len"].asInt());
|
||||||
|
}
|
||||||
|
return sanitize_string(title);
|
||||||
|
}
|
||||||
|
|
||||||
static bool isServerUnavailable(const std::error_code& ec) {
|
static bool isServerUnavailable(const std::error_code& ec) {
|
||||||
if (ec.category() == std::system_category()) {
|
if (ec.category() == std::system_category()) {
|
||||||
switch (ec.value()) {
|
switch (ec.value()) {
|
||||||
@@ -323,6 +334,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;
|
||||||
|
|||||||
@@ -152,7 +152,6 @@ bool Playing::on_timer() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx_->queryMPD();
|
|
||||||
ctx_->emit();
|
ctx_->emit();
|
||||||
} catch (std::exception const& e) {
|
} catch (std::exception const& e) {
|
||||||
spdlog::warn("mpd: Playing: error: {}", e.what());
|
spdlog::warn("mpd: Playing: error: {}", e.what());
|
||||||
|
|||||||
+93
-87
@@ -7,6 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "util/scope_guard.hpp"
|
#include "util/scope_guard.hpp"
|
||||||
|
#include "util/utf8_string.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <playerctl/playerctl.h>
|
#include <playerctl/playerctl.h>
|
||||||
@@ -109,6 +110,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 +119,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 +163,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 +179,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 {
|
||||||
@@ -193,69 +203,22 @@ auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) ->
|
|||||||
// Wide characters count as two, zero-width characters count as zero
|
// Wide characters count as two, zero-width characters count as zero
|
||||||
// Modifies str in-place (unless width = std::string::npos)
|
// Modifies str in-place (unless width = std::string::npos)
|
||||||
// Returns the total width of the string pre-truncating
|
// Returns the total width of the string pre-truncating
|
||||||
size_t utf8_truncate(std::string& str, size_t width = std::string::npos) {
|
|
||||||
if (str.length() == 0) return 0;
|
|
||||||
|
|
||||||
const gchar* trunc_end = nullptr;
|
|
||||||
|
|
||||||
size_t total_width = 0;
|
|
||||||
|
|
||||||
for (gchar *data = str.data(), *end = data + str.size(); data != nullptr;) {
|
|
||||||
gunichar c = g_utf8_get_char_validated(data, end - data);
|
|
||||||
if (c == -1U || c == -2U) {
|
|
||||||
// invalid unicode, treat string as ascii
|
|
||||||
if (width != std::string::npos && str.length() > width) str.resize(width);
|
|
||||||
return str.length();
|
|
||||||
} else if (g_unichar_iswide(c)) {
|
|
||||||
total_width += 2;
|
|
||||||
} else if (!g_unichar_iszerowidth(c) && c != 0xAD) { // neither zero-width nor soft hyphen
|
|
||||||
total_width += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = g_utf8_find_next_char(data, end);
|
|
||||||
if (width != std::string::npos && total_width <= width && !g_unichar_isspace(c))
|
|
||||||
trunc_end = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trunc_end) str.resize(trunc_end - str.data());
|
|
||||||
|
|
||||||
return total_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t utf8_width(const std::string& str) { return utf8_truncate(const_cast<std::string&>(str)); }
|
|
||||||
|
|
||||||
void truncate(std::string& s, const std::string& ellipsis, size_t max_len) {
|
|
||||||
if (max_len == 0) {
|
|
||||||
s.resize(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t len = utf8_truncate(s, max_len);
|
|
||||||
if (len > max_len) {
|
|
||||||
size_t ellipsis_len = utf8_width(ellipsis);
|
|
||||||
if (max_len >= ellipsis_len) {
|
|
||||||
if (ellipsis_len) utf8_truncate(s, max_len - ellipsis_len);
|
|
||||||
s += ellipsis;
|
|
||||||
} else {
|
|
||||||
s.resize(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string {
|
auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string {
|
||||||
auto artist = info.artist.value_or(std::string());
|
auto artist = info.artist.value_or(std::string());
|
||||||
if (truncated && artist_len_ >= 0) truncate(artist, ellipsis_, artist_len_);
|
if (truncated && artist_len_ >= 0) waybar::util::utf8_truncate(artist, ellipsis_, artist_len_);
|
||||||
return artist;
|
return artist;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mpris::getAlbumStr(const PlayerInfo& info, bool truncated) -> std::string {
|
auto Mpris::getAlbumStr(const PlayerInfo& info, bool truncated) -> std::string {
|
||||||
auto album = info.album.value_or(std::string());
|
auto album = info.album.value_or(std::string());
|
||||||
if (truncated && album_len_ >= 0) truncate(album, ellipsis_, album_len_);
|
if (truncated && album_len_ >= 0) waybar::util::utf8_truncate(album, ellipsis_, album_len_);
|
||||||
return album;
|
return album;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mpris::getTitleStr(const PlayerInfo& info, bool truncated) -> std::string {
|
auto Mpris::getTitleStr(const PlayerInfo& info, bool truncated) -> std::string {
|
||||||
auto title = info.title.value_or(std::string());
|
auto title = info.title.value_or(std::string());
|
||||||
if (truncated && title_len_ >= 0) truncate(title, ellipsis_, title_len_);
|
if (truncated && title_len_ >= 0) waybar::util::utf8_truncate(title, ellipsis_, title_len_);
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,9 +246,9 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) ->
|
|||||||
// keep position format same as length format
|
// keep position format same as length format
|
||||||
auto position = getPositionStr(info, truncated && truncate_hours_ && length.length() < 6);
|
auto position = getPositionStr(info, truncated && truncate_hours_ && length.length() < 6);
|
||||||
|
|
||||||
size_t artistLen = utf8_width(artist);
|
size_t artistLen = waybar::util::utf8_width(artist);
|
||||||
size_t albumLen = utf8_width(album);
|
size_t albumLen = waybar::util::utf8_width(album);
|
||||||
size_t titleLen = utf8_width(title);
|
size_t titleLen = waybar::util::utf8_width(title);
|
||||||
size_t lengthLen = length.length();
|
size_t lengthLen = length.length();
|
||||||
size_t posLen = position.length();
|
size_t posLen = position.length();
|
||||||
|
|
||||||
@@ -304,7 +267,7 @@ auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) ->
|
|||||||
// Since the first element doesn't present a separator and we don't know a priori which one
|
// Since the first element doesn't present a separator and we don't know a priori which one
|
||||||
// it will be, we add a "virtual separatorLen" to the dynamicLen, since we are adding the
|
// it will be, we add a "virtual separatorLen" to the dynamicLen, since we are adding the
|
||||||
// separatorLen to all the other lengths.
|
// separatorLen to all the other lengths.
|
||||||
size_t separatorLen = utf8_width(dynamic_separator_);
|
size_t separatorLen = waybar::util::utf8_width(dynamic_separator_);
|
||||||
size_t dynamicLen = dynamic_len_ + separatorLen;
|
size_t dynamicLen = dynamic_len_ + separatorLen;
|
||||||
if (showArtist) artistLen += separatorLen;
|
if (showArtist) artistLen += separatorLen;
|
||||||
if (showAlbum) albumLen += separatorLen;
|
if (showAlbum) albumLen += separatorLen;
|
||||||
@@ -410,11 +373,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 +424,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 +443,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 +451,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 +466,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 +525,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 +559,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 +611,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) {
|
||||||
@@ -726,9 +729,12 @@ auto Mpris::update() -> void {
|
|||||||
auto tooltip_text = fmt::format(
|
auto tooltip_text = fmt::format(
|
||||||
fmt::runtime(tooltipstr), fmt::arg("player", info.name),
|
fmt::runtime(tooltipstr), fmt::arg("player", info.name),
|
||||||
fmt::arg("status", info.status_string),
|
fmt::arg("status", info.status_string),
|
||||||
fmt::arg("artist", getArtistStr(info, tooltip_len_limits_)),
|
fmt::arg("artist",
|
||||||
fmt::arg("title", getTitleStr(info, tooltip_len_limits_)),
|
std::string(Glib::Markup::escape_text(getArtistStr(info, tooltip_len_limits_)))),
|
||||||
fmt::arg("album", getAlbumStr(info, tooltip_len_limits_)),
|
fmt::arg("title",
|
||||||
|
std::string(Glib::Markup::escape_text(getTitleStr(info, tooltip_len_limits_)))),
|
||||||
|
fmt::arg("album",
|
||||||
|
std::string(Glib::Markup::escape_text(getAlbumStr(info, tooltip_len_limits_)))),
|
||||||
fmt::arg("length", tooltipLength), fmt::arg("position", tooltipPosition),
|
fmt::arg("length", tooltipLength), fmt::arg("position", tooltipPosition),
|
||||||
fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)),
|
fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)),
|
||||||
fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)),
|
fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)),
|
||||||
|
|||||||
+82
-40
@@ -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:
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -103,7 +103,9 @@ void Workspaces::doUpdate() {
|
|||||||
fmt::arg("output", ws["output"].asString()));
|
fmt::arg("output", ws["output"].asString()));
|
||||||
}
|
}
|
||||||
if (!config_["disable-markup"].asBool()) {
|
if (!config_["disable-markup"].asBool()) {
|
||||||
static_cast<Gtk::Label*>(button.get_children()[0])->set_markup(name);
|
auto* child = gtk_bin_get_child(GTK_BIN(button.gobj()));
|
||||||
|
if (child != nullptr && GTK_IS_LABEL(child))
|
||||||
|
gtk_label_set_markup(GTK_LABEL(child), name.c_str());
|
||||||
} else {
|
} else {
|
||||||
button.set_label(name);
|
button.set_label(name);
|
||||||
}
|
}
|
||||||
@@ -114,6 +116,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ Privacy::Privacy(const std::string& id, const Json::Value& config, Gtk::Orientat
|
|||||||
auto* item = Gtk::make_managed<PrivacyItem>(module, nodeType, nodePtr, orientation, pos,
|
auto* item = Gtk::make_managed<PrivacyItem>(module, nodeType, nodePtr, orientation, pos,
|
||||||
iconSize, transition_duration);
|
iconSize, transition_duration);
|
||||||
box_.add(*item);
|
box_.add(*item);
|
||||||
|
modules_.push_back(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,9 +149,7 @@ auto Privacy::update() -> void {
|
|||||||
bool useAudioOut = false;
|
bool useAudioOut = false;
|
||||||
|
|
||||||
mutex_.lock();
|
mutex_.lock();
|
||||||
for (Gtk::Widget* widget : box_.get_children()) {
|
for (PrivacyItem* module : modules_) {
|
||||||
auto* module = dynamic_cast<PrivacyItem*>(widget);
|
|
||||||
if (module == nullptr) continue;
|
|
||||||
switch (module->privacy_type) {
|
switch (module->privacy_type) {
|
||||||
case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT:
|
case util::PipewireBackend::PRIVACY_NODE_TYPE_VIDEO_INPUT:
|
||||||
setScreenshare = true;
|
setScreenshare = true;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+152
-48
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,17 +190,25 @@ void Item::setProperty(const Glib::ustring& name, Glib::VariantBase& value) {
|
|||||||
} else if (name == "Status") {
|
} else if (name == "Status") {
|
||||||
setStatus(get_variant<Glib::ustring>(value));
|
setStatus(get_variant<Glib::ustring>(value));
|
||||||
} else if (name == "IconName") {
|
} else if (name == "IconName") {
|
||||||
|
if (has_custom_icon_) {
|
||||||
|
spdlog::trace("Item '{}': ignoring IconName update, custom icon is set", id);
|
||||||
|
} else {
|
||||||
icon_name = get_variant<std::string>(value);
|
icon_name = get_variant<std::string>(value);
|
||||||
|
}
|
||||||
} else if (name == "IconPixmap") {
|
} else if (name == "IconPixmap") {
|
||||||
|
if (has_custom_icon_) {
|
||||||
|
spdlog::trace("Item '{}': ignoring IconPixmap update, custom icon is set", id);
|
||||||
|
} else {
|
||||||
icon_pixmap = this->extractPixBuf(value.gobj());
|
icon_pixmap = this->extractPixBuf(value.gobj());
|
||||||
|
}
|
||||||
} 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 +237,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) {
|
||||||
@@ -241,11 +278,13 @@ void Item::setCustomIcon(const std::string& id) {
|
|||||||
Glib::RefPtr<Gdk::Pixbuf> custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon);
|
Glib::RefPtr<Gdk::Pixbuf> custom_pixbuf = Gdk::Pixbuf::create_from_file(custom_icon);
|
||||||
icon_name = ""; // icon_name has priority over pixmap
|
icon_name = ""; // icon_name has priority over pixmap
|
||||||
icon_pixmap = custom_pixbuf;
|
icon_pixmap = custom_pixbuf;
|
||||||
|
has_custom_icon_ = true;
|
||||||
} catch (const Glib::Error& e) {
|
} catch (const Glib::Error& e) {
|
||||||
spdlog::error("Failed to load custom icon {}: {}", custom_icon, e.what());
|
spdlog::error("Failed to load custom icon {}: {}", custom_icon, e.what());
|
||||||
}
|
}
|
||||||
} else { // if file doesn't exist it's most likely an icon_name
|
} else { // if file doesn't exist it's most likely an icon_name
|
||||||
icon_name = custom_icon;
|
icon_name = custom_icon;
|
||||||
|
has_custom_icon_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,8 +326,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 +375,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 +397,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 +405,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 +449,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 +562,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 +592,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:
|
||||||
|
|||||||
@@ -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()) {
|
||||||
@@ -23,9 +24,6 @@ Tray::Tray(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||||||
if (config_["spacing"].isUInt()) {
|
if (config_["spacing"].isUInt()) {
|
||||||
box_.set_spacing(config_["spacing"].asUInt());
|
box_.set_spacing(config_["spacing"].asUInt());
|
||||||
}
|
}
|
||||||
if (config["show-passive-items"].isBool()) {
|
|
||||||
show_passive_ = config["show-passive-items"].asBool();
|
|
||||||
}
|
|
||||||
nb_hosts_ += 1;
|
nb_hosts_ += 1;
|
||||||
if (config_["icons"].isObject()) {
|
if (config_["icons"].isObject()) {
|
||||||
IconManager::instance().setIconsConfig(config_["icons"]);
|
IconManager::instance().setIconsConfig(config_["icons"]);
|
||||||
@@ -33,12 +31,16 @@ 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);
|
||||||
} else {
|
} else {
|
||||||
box_.pack_start(item->event_box);
|
box_.pack_start(item->event_box);
|
||||||
}
|
}
|
||||||
|
item->event_box.signal_show().connect([this] { dp.emit(); });
|
||||||
|
item->event_box.signal_hide().connect([this] { dp.emit(); });
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,17 +50,9 @@ void Tray::onRemove(std::unique_ptr<Item>& item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Tray::update() -> void {
|
auto Tray::update() -> void {
|
||||||
// Show tray only when items are available
|
|
||||||
std::vector<Gtk::Widget*> children = box_.get_children();
|
std::vector<Gtk::Widget*> children = box_.get_children();
|
||||||
if (show_passive_) {
|
event_box_.set_visible(std::any_of(children.begin(), children.end(),
|
||||||
event_box_.set_visible(!children.empty());
|
[](Gtk::Widget* child) { return child->get_visible(); }));
|
||||||
} else {
|
|
||||||
event_box_.set_visible(!std::all_of(children.begin(), children.end(), [](Gtk::Widget* child) {
|
|
||||||
return child->get_style_context()->has_class("passive");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call parent update
|
|
||||||
AModule::update();
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user