Merge remote-tracking branch 'origin/master' into pr-4320
# Conflicts: # include/modules/hyprland/workspaces.hpp # man/waybar-hyprland-workspaces.5.scd # src/modules/hyprland/workspace.cpp # src/modules/hyprland/workspaces.cpp
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
@@ -17,9 +19,13 @@ class EventHandler {
|
||||
virtual ~EventHandler() = default;
|
||||
};
|
||||
|
||||
/// If you want to use the Hyprland IPC, simply use IPC::inst() to get the singleton instance.
|
||||
/// Do not create multiple instances.
|
||||
class IPC {
|
||||
protected:
|
||||
IPC(); // use IPC::inst() instead.
|
||||
|
||||
public:
|
||||
IPC();
|
||||
~IPC();
|
||||
static IPC& inst();
|
||||
|
||||
@@ -30,21 +36,33 @@ class IPC {
|
||||
Json::Value getSocket1JsonReply(const std::string& rq);
|
||||
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:
|
||||
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:
|
||||
void socketListener();
|
||||
void parseIPC(const std::string&);
|
||||
|
||||
std::thread ipcThread_;
|
||||
std::mutex callbackMutex_;
|
||||
std::mutex socketMutex_;
|
||||
util::JsonParser parser_;
|
||||
std::list<std::pair<std::string, EventHandler*>> callbacks_;
|
||||
int socketfd_; // the hyprland socket file descriptor
|
||||
bool running_ = true;
|
||||
int socketfd_ = -1; // the hyprland socket file descriptor
|
||||
pid_t socketOwnerPid_ = -1;
|
||||
std::atomic<bool> running_ = true; // the ipcThread will stop running when this is false
|
||||
};
|
||||
|
||||
inline bool modulesReady = false;
|
||||
inline std::unique_ptr<IPC> gIPC;
|
||||
}; // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -30,6 +30,8 @@ class Language : public waybar::ALabel, public EventHandler {
|
||||
std::string short_description;
|
||||
};
|
||||
|
||||
auto removeXkbLayoutCssClass() -> void;
|
||||
auto addXkbLayoutCssClass() -> void;
|
||||
static auto getLayout(const std::string&) -> Layout;
|
||||
|
||||
std::mutex mutex_;
|
||||
|
||||
@@ -26,8 +26,11 @@ class Submap : public waybar::ALabel, public EventHandler {
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
std::string submap_;
|
||||
std::string icon_;
|
||||
std::string prev_submap_;
|
||||
bool always_on_ = false;
|
||||
std::string default_submap_ = "Default";
|
||||
std::unordered_map<std::string, std::string> icons_;
|
||||
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
@@ -20,8 +20,8 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
|
||||
private:
|
||||
struct Workspace {
|
||||
int id;
|
||||
int windows;
|
||||
int id = 0;
|
||||
int windows = 0;
|
||||
std::string last_window;
|
||||
std::string last_window_title;
|
||||
|
||||
@@ -29,14 +29,14 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
};
|
||||
|
||||
struct WindowData {
|
||||
bool floating;
|
||||
bool floating = false;
|
||||
int monitor = -1;
|
||||
std::string class_name;
|
||||
std::string initial_class_name;
|
||||
std::string title;
|
||||
std::string initial_title;
|
||||
bool fullscreen;
|
||||
bool grouped;
|
||||
bool fullscreen = false;
|
||||
bool grouped = false;
|
||||
|
||||
static auto parse(const Json::Value&) -> WindowData;
|
||||
};
|
||||
@@ -47,7 +47,7 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
void queryActiveWorkspace();
|
||||
void setClass(const std::string&, bool enable);
|
||||
|
||||
bool separateOutputs_;
|
||||
bool separateOutputs_ = false;
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
util::JsonParser parser_;
|
||||
@@ -55,11 +55,11 @@ class Window : public waybar::AAppIconLabel, public EventHandler {
|
||||
Workspace workspace_;
|
||||
std::string soloClass_;
|
||||
std::string lastSoloClass_;
|
||||
bool solo_;
|
||||
bool allFloating_;
|
||||
bool swallowing_;
|
||||
bool fullscreen_;
|
||||
bool focused_;
|
||||
bool solo_ = false;
|
||||
bool allFloating_ = false;
|
||||
bool swallowing_ = false;
|
||||
bool fullscreen_ = false;
|
||||
bool focused_ = false;
|
||||
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "AAppIconLabel.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "modules/hyprland/backend.hpp"
|
||||
#include "util/json.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
@@ -26,8 +25,8 @@ class WindowCount : public waybar::AAppIconLabel, public EventHandler {
|
||||
static auto parse(const Json::Value& value) -> Workspace;
|
||||
};
|
||||
|
||||
static auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||
static auto getActiveWorkspace() -> Workspace;
|
||||
auto getActiveWorkspace(const std::string&) -> Workspace;
|
||||
auto getActiveWorkspace() -> Workspace;
|
||||
void onEvent(const std::string& ev) override;
|
||||
void queryActiveWorkspace();
|
||||
void setClass(const std::string&, bool enable);
|
||||
@@ -36,6 +35,7 @@ class WindowCount : public waybar::AAppIconLabel, public EventHandler {
|
||||
std::mutex mutex_;
|
||||
const Bar& bar_;
|
||||
Workspace workspace_;
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -26,18 +26,32 @@ namespace waybar::modules::hyprland {
|
||||
|
||||
class Workspaces;
|
||||
|
||||
struct WindowRepr {
|
||||
std::string address;
|
||||
std::string window_class;
|
||||
std::string window_title;
|
||||
std::string repr_rewrite;
|
||||
bool isActive = false;
|
||||
|
||||
public:
|
||||
bool empty() const { return address.empty(); }
|
||||
void setActive(bool value) { isActive = value; }
|
||||
};
|
||||
|
||||
class WindowCreationPayload {
|
||||
public:
|
||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||
std::string window_repr);
|
||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||
std::string window_class, std::string window_title);
|
||||
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
|
||||
WindowRepr window_repr);
|
||||
WindowCreationPayload(const std::string& workspace_name, WindowAddress window_address,
|
||||
const std::string& window_class, const std::string& window_title,
|
||||
bool is_active);
|
||||
WindowCreationPayload(Json::Value const& client_data);
|
||||
|
||||
int incrementTimeSpentUncreated();
|
||||
bool isEmpty(Workspaces& workspace_manager);
|
||||
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
|
||||
std::string repr(Workspaces& workspace_manager);
|
||||
WindowRepr repr(Workspaces& workspace_manager);
|
||||
void setActive(bool value) { m_isActive = value; }
|
||||
|
||||
std::string getWorkspaceName() const { return m_workspaceName; }
|
||||
WindowAddress getAddress() const { return m_windowAddress; }
|
||||
@@ -48,12 +62,13 @@ class WindowCreationPayload {
|
||||
void clearAddr();
|
||||
void clearWorkspaceName();
|
||||
|
||||
using Repr = std::string;
|
||||
using Repr = WindowRepr;
|
||||
using ClassAndTitle = std::pair<std::string, std::string>;
|
||||
std::variant<Repr, ClassAndTitle> m_window;
|
||||
|
||||
WindowAddress m_windowAddress;
|
||||
std::string m_workspaceName;
|
||||
bool m_isActive = false;
|
||||
|
||||
int m_timeSpentUncreated = 0;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ class Workspace {
|
||||
public:
|
||||
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||
const Json::Value& clients_data = Json::Value::nullRef);
|
||||
~Workspace();
|
||||
std::string& selectIcon(std::map<std::string, std::string>& icons_map);
|
||||
Gtk::Button& button() { return m_button; };
|
||||
|
||||
@@ -42,10 +43,18 @@ class Workspace {
|
||||
bool isPersistentConfig() const { return m_isPersistentConfig; };
|
||||
bool isPersistentRule() const { return m_isPersistentRule; };
|
||||
bool isVisible() const { return m_isVisible; };
|
||||
bool isEmpty() const { return m_windows == 0; };
|
||||
bool isUrgent() const { return m_isUrgent; };
|
||||
|
||||
bool handleClicked(GdkEventButton* bt) const;
|
||||
|
||||
bool handleEnter(GdkEventCrossing* event);
|
||||
bool handleLeave(GdkEventCrossing* event);
|
||||
|
||||
void startHoverCheck();
|
||||
void stopHoverCheck();
|
||||
bool syncHoverClass();
|
||||
bool pointerInsideButton();
|
||||
|
||||
void setActive(bool value = true) { m_isActive = value; };
|
||||
void setPersistentRule(bool value = true) { m_isPersistentRule = value; };
|
||||
void setPersistentConfig(bool value = true) { m_isPersistentConfig = value; };
|
||||
@@ -54,21 +63,25 @@ class Workspace {
|
||||
void setWindows(uint value) { m_windows = value; };
|
||||
void setName(std::string const& value) { m_name = value; };
|
||||
void setOutput(std::string const& value) { m_output = value; };
|
||||
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
|
||||
bool containsWindow(WindowAddress const& addr) const {
|
||||
return std::ranges::any_of(m_windowMap,
|
||||
[&addr](const auto& window) { return window.address == addr; });
|
||||
};
|
||||
void insertWindow(WindowCreationPayload create_window_payload);
|
||||
std::string removeWindow(WindowAddress const& addr);
|
||||
void initializeWindowMap(const Json::Value& clients_data);
|
||||
void setActiveWindow(WindowAddress const& addr);
|
||||
|
||||
bool onWindowOpened(WindowCreationPayload const& create_window_payload);
|
||||
std::optional<std::string> closeWindow(WindowAddress const& addr);
|
||||
std::optional<WindowRepr> closeWindow(WindowAddress const& addr);
|
||||
|
||||
void update(const std::string& format, const std::string& icon);
|
||||
void update(const std::string& workspace_icon);
|
||||
|
||||
private:
|
||||
Workspaces& m_workspaceManager;
|
||||
|
||||
int m_id;
|
||||
std::string m_name;
|
||||
std::string m_prevNameClass;
|
||||
std::string m_output;
|
||||
uint m_windows;
|
||||
bool m_isActive = false;
|
||||
@@ -78,11 +91,19 @@ class Workspace {
|
||||
bool m_isUrgent = false;
|
||||
bool m_isVisible = false;
|
||||
|
||||
std::map<WindowAddress, std::string> m_windowMap;
|
||||
sigc::connection m_hoverCheckConnection;
|
||||
|
||||
std::vector<WindowRepr> m_windowMap;
|
||||
|
||||
Gtk::Button m_button;
|
||||
Gtk::Box m_content;
|
||||
Gtk::Label m_label;
|
||||
Gtk::Label m_labelBefore;
|
||||
Gtk::Label m_labelAfter;
|
||||
|
||||
bool isEmpty() const;
|
||||
void updateTaskbar(const std::string& workspace_icon);
|
||||
bool handleClick(const GdkEventButton* event_button, WindowAddress const& addr) const;
|
||||
bool shouldSkipWindow(const WindowRepr& window_repr) const;
|
||||
IPC& m_ipc;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/enums.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <json/value.h>
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
@@ -18,6 +20,7 @@
|
||||
#include "modules/hyprland/windowcreationpayload.hpp"
|
||||
#include "modules/hyprland/workspace.hpp"
|
||||
#include "util/enum.hpp"
|
||||
#include "util/icon_loader.hpp"
|
||||
#include "util/regex_collection.hpp"
|
||||
|
||||
using WindowAddress = std::string;
|
||||
@@ -36,18 +39,38 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto allOutputs() const -> bool { return m_allOutputs; }
|
||||
auto showSpecial() const -> bool { return m_showSpecial; }
|
||||
auto activeOnly() const -> bool { return m_activeOnly; }
|
||||
auto hideActive() const -> bool { return m_hideActive; }
|
||||
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
|
||||
auto persistentOnly() const -> bool { return m_persistentOnly; }
|
||||
auto moveToMonitor() const -> bool { return m_moveToMonitor; }
|
||||
auto uniqueIcons() const -> bool { return m_uniqueIcons; }
|
||||
auto enableTaskbar() const -> bool { return m_enableTaskbar; }
|
||||
auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; }
|
||||
auto barScroll() const -> bool { return m_barScroll; }
|
||||
|
||||
auto getBarOutput() const -> std::string { return m_bar.output->name; }
|
||||
auto formatBefore() const -> std::string { return m_formatBefore; }
|
||||
auto formatAfter() const -> std::string { return m_formatAfter; }
|
||||
auto taskbarFormatBefore() const -> std::string { return m_taskbarFormatBefore; }
|
||||
auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; }
|
||||
auto taskbarIconSize() const -> int { return m_taskbarIconSize; }
|
||||
auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; }
|
||||
auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; }
|
||||
auto onClickWindow() const -> std::string { return m_onClickWindow; }
|
||||
auto getIgnoredWindows() const -> std::vector<std::regex> { return m_ignoreWindows; }
|
||||
auto maxWindows() const -> int { return m_maxWindows; }
|
||||
|
||||
std::string getRewrite(std::string window_class, std::string window_title);
|
||||
enum class ActiveWindowPosition { NONE, FIRST, LAST };
|
||||
auto activeWindowPosition() const -> ActiveWindowPosition { return m_activeWindowPosition; }
|
||||
|
||||
std::string getRewrite(const std::string& window_class, const std::string& window_title);
|
||||
std::string& getWindowSeparator() { return m_formatWindowSeparator; }
|
||||
auto windowRewriteGroupThreshold() const -> int { return m_windowRewriteGroupThreshold; }
|
||||
auto const& getWindowRewriteGroupFormat() const { return m_windowRewriteGroupFormat; }
|
||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||
|
||||
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
||||
const IconLoader& iconLoader() const { return m_iconLoader; }
|
||||
|
||||
private:
|
||||
void onEvent(const std::string& e) override;
|
||||
@@ -71,6 +94,8 @@ class Workspaces : public AModule, public EventHandler {
|
||||
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
|
||||
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
|
||||
auto populateWindowRewriteConfig(const Json::Value& config) -> void;
|
||||
auto populateMaxWindowsConfig(const Json::Value& config) -> void;
|
||||
auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void;
|
||||
|
||||
void registerIpc();
|
||||
|
||||
@@ -93,6 +118,7 @@ class Workspaces : public AModule, public EventHandler {
|
||||
void onWindowMoved(std::string const& payload);
|
||||
|
||||
void onWindowTitleEvent(std::string const& payload);
|
||||
void onActiveWindowChanged(WindowAddress const& payload);
|
||||
|
||||
void onConfigReloaded();
|
||||
|
||||
@@ -104,6 +130,8 @@ class Workspaces : public AModule, public EventHandler {
|
||||
static std::pair<std::string, std::string> splitDoublePayload(std::string const& payload);
|
||||
static std::tuple<std::string, std::string, std::string> splitTriplePayload(
|
||||
std::string const& payload);
|
||||
// scroll events
|
||||
bool handleScroll(GdkEventScroll* e) override;
|
||||
|
||||
// Update methods
|
||||
void doUpdate();
|
||||
@@ -124,16 +152,18 @@ class Workspaces : public AModule, public EventHandler {
|
||||
bool m_allOutputs = false;
|
||||
bool m_showSpecial = false;
|
||||
bool m_activeOnly = false;
|
||||
bool m_hideActive = false;
|
||||
bool m_specialVisibleOnly = false;
|
||||
bool m_persistentOnly = false;
|
||||
bool m_moveToMonitor = false;
|
||||
bool m_uniqueIcons = false;
|
||||
bool m_barScroll = false;
|
||||
Json::Value m_persistentWorkspaceConfig;
|
||||
|
||||
// Map for windows stored in workspaces not present in the current bar.
|
||||
// This happens when the user has multiple monitors (hence, multiple bars)
|
||||
// and doesn't share windows across bars (a.k.a `all-outputs` = false)
|
||||
std::map<WindowAddress, std::string> m_orphanWindowMap;
|
||||
std::map<WindowAddress, WindowRepr, std::less<>> m_orphanWindowMap;
|
||||
|
||||
enum class SortMethod { ID, NAME, NUMBER, SPECIAL_CENTERED, DEFAULT };
|
||||
util::EnumParser<SortMethod> m_enumParser;
|
||||
@@ -144,12 +174,15 @@ class Workspaces : public AModule, public EventHandler {
|
||||
{"SPECIAL-CENTERED", SortMethod::SPECIAL_CENTERED},
|
||||
{"DEFAULT", SortMethod::DEFAULT}};
|
||||
|
||||
std::string m_format;
|
||||
std::string m_formatBefore;
|
||||
std::string m_formatAfter;
|
||||
|
||||
std::map<std::string, std::string> m_iconsMap;
|
||||
util::RegexCollection m_windowRewriteRules;
|
||||
bool m_anyWindowRewriteRuleUsesTitle = false;
|
||||
std::string m_formatWindowSeparator;
|
||||
int m_windowRewriteGroupThreshold = 0;
|
||||
std::string m_windowRewriteGroupFormat = "{icon}×{count}";
|
||||
|
||||
bool m_withIcon;
|
||||
uint64_t m_monitorId;
|
||||
@@ -160,12 +193,38 @@ class Workspaces : public AModule, public EventHandler {
|
||||
std::vector<std::string> m_workspacesToRemove;
|
||||
std::vector<WindowCreationPayload> m_windowsToCreate;
|
||||
|
||||
IconLoader m_iconLoader;
|
||||
bool m_enableTaskbar = false;
|
||||
bool m_updateActiveWindow = false;
|
||||
bool m_taskbarWithIcon = false;
|
||||
bool m_taskbarWithTitle = false;
|
||||
std::string m_taskbarFormatBefore;
|
||||
std::string m_taskbarFormatAfter;
|
||||
int m_taskbarIconSize = 16;
|
||||
Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL;
|
||||
bool m_taskbarReverseDirection = false;
|
||||
util::EnumParser<ActiveWindowPosition> m_activeWindowEnumParser;
|
||||
ActiveWindowPosition m_activeWindowPosition = ActiveWindowPosition::NONE;
|
||||
std::map<std::string, ActiveWindowPosition> m_activeWindowPositionMap = {
|
||||
{"NONE", ActiveWindowPosition::NONE},
|
||||
{"FIRST", ActiveWindowPosition::FIRST},
|
||||
{"LAST", ActiveWindowPosition::LAST},
|
||||
};
|
||||
std::string m_onClickWindow;
|
||||
std::string m_currentActiveWindowAddress;
|
||||
int m_maxWindows = 0;
|
||||
|
||||
std::vector<std::regex> m_ignoreWorkspaces;
|
||||
std::vector<std::regex> m_ignoreWindows;
|
||||
|
||||
std::mutex m_mutex;
|
||||
const Bar& m_bar;
|
||||
Gtk::Box m_box;
|
||||
sigc::connection m_scrollEventConnection_;
|
||||
IPC& m_ipc;
|
||||
|
||||
sigc::connection m_debounceTimer;
|
||||
bool m_updatePending = false;
|
||||
};
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
Reference in New Issue
Block a user