Merge branch 'master' into hyprland-window_no_title_from_special
This commit is contained in:
@@ -9,11 +9,38 @@
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
std::filesystem::path IPC::socketFolder_;
|
||||
|
||||
std::filesystem::path IPC::getSocketFolder(const char* instanceSig) {
|
||||
// socket path, specified by EventManager of Hyprland
|
||||
if (!socketFolder_.empty()) {
|
||||
return socketFolder_;
|
||||
}
|
||||
|
||||
const char* xdgRuntimeDirEnv = std::getenv("XDG_RUNTIME_DIR");
|
||||
std::filesystem::path xdgRuntimeDir;
|
||||
// Only set path if env variable is set
|
||||
if (xdgRuntimeDirEnv != nullptr) {
|
||||
xdgRuntimeDir = std::filesystem::path(xdgRuntimeDirEnv);
|
||||
}
|
||||
|
||||
if (!xdgRuntimeDir.empty() && std::filesystem::exists(xdgRuntimeDir / "hypr")) {
|
||||
socketFolder_ = xdgRuntimeDir / "hypr";
|
||||
} else {
|
||||
spdlog::warn("$XDG_RUNTIME_DIR/hypr does not exist, falling back to /tmp/hypr");
|
||||
socketFolder_ = std::filesystem::path("/tmp") / "hypr";
|
||||
}
|
||||
|
||||
socketFolder_ = socketFolder_ / instanceSig;
|
||||
return socketFolder_;
|
||||
}
|
||||
|
||||
void IPC::startIPC() {
|
||||
// will start IPC and relay events to parseIPC
|
||||
|
||||
@@ -40,9 +67,7 @@ void IPC::startIPC() {
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
// socket path, specified by EventManager of Hyprland
|
||||
std::string socketPath = "/tmp/hypr/" + std::string(his) + "/.socket2.sock";
|
||||
|
||||
auto socketPath = IPC::getSocketFolder(his) / ".socket2.sock";
|
||||
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
||||
|
||||
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
|
||||
@@ -54,22 +79,29 @@ void IPC::startIPC() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto file = fdopen(socketfd, "r");
|
||||
auto* file = fdopen(socketfd, "r");
|
||||
|
||||
while (true) {
|
||||
char buffer[1024]; // Hyprland socket2 events are max 1024 bytes
|
||||
std::array<char, 1024> buffer; // Hyprland socket2 events are max 1024 bytes
|
||||
|
||||
auto recievedCharPtr = fgets(buffer, 1024, file);
|
||||
auto* receivedCharPtr = fgets(buffer.data(), buffer.size(), file);
|
||||
|
||||
if (!recievedCharPtr) {
|
||||
if (receivedCharPtr == nullptr) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string messageRecieved(buffer);
|
||||
messageRecieved = messageRecieved.substr(0, messageRecieved.find_first_of('\n'));
|
||||
spdlog::debug("hyprland IPC received {}", messageRecieved);
|
||||
parseIPC(messageRecieved);
|
||||
std::string messageReceived(buffer.data());
|
||||
messageReceived = messageReceived.substr(0, messageReceived.find_first_of('\n'));
|
||||
spdlog::debug("hyprland IPC received {}", messageReceived);
|
||||
|
||||
try {
|
||||
parseIPC(messageReceived);
|
||||
} catch (std::exception& e) {
|
||||
spdlog::warn("Failed to parse IPC message: {}, reason: {}", messageReceived, e.what());
|
||||
} catch (...) {
|
||||
throw;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
@@ -78,9 +110,9 @@ void IPC::startIPC() {
|
||||
|
||||
void IPC::parseIPC(const std::string& ev) {
|
||||
std::string request = ev.substr(0, ev.find_first_of('>'));
|
||||
std::unique_lock lock(m_callbackMutex);
|
||||
std::unique_lock lock(callbackMutex_);
|
||||
|
||||
for (auto& [eventname, handler] : m_callbacks) {
|
||||
for (auto& [eventname, handler] : callbacks_) {
|
||||
if (eventname == request) {
|
||||
handler->onEvent(ev);
|
||||
}
|
||||
@@ -88,25 +120,25 @@ void IPC::parseIPC(const std::string& ev) {
|
||||
}
|
||||
|
||||
void IPC::registerForIPC(const std::string& ev, EventHandler* ev_handler) {
|
||||
if (!ev_handler) {
|
||||
if (ev_handler == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lock(m_callbackMutex);
|
||||
m_callbacks.emplace_back(ev, ev_handler);
|
||||
std::unique_lock lock(callbackMutex_);
|
||||
callbacks_.emplace_back(ev, ev_handler);
|
||||
}
|
||||
|
||||
void IPC::unregisterForIPC(EventHandler* ev_handler) {
|
||||
if (!ev_handler) {
|
||||
if (ev_handler == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lock(m_callbackMutex);
|
||||
std::unique_lock lock(callbackMutex_);
|
||||
|
||||
for (auto it = m_callbacks.begin(); it != m_callbacks.end();) {
|
||||
for (auto it = callbacks_.begin(); it != callbacks_.end();) {
|
||||
auto& [eventname, handler] = *it;
|
||||
if (handler == ev_handler) {
|
||||
m_callbacks.erase(it++);
|
||||
callbacks_.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
@@ -135,19 +167,17 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
}
|
||||
|
||||
// get the instance signature
|
||||
auto instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
auto* instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!instanceSig) {
|
||||
if (instanceSig == nullptr) {
|
||||
spdlog::error("Hyprland IPC: HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string instanceSigStr = std::string(instanceSig);
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock";
|
||||
std::string socketPath = IPC::getSocketFolder(instanceSig) / ".socket.sock";
|
||||
|
||||
// Use snprintf to copy the socketPath string into serverAddress.sun_path
|
||||
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) <
|
||||
@@ -169,18 +199,18 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char buffer[8192] = {0};
|
||||
std::array<char, 8192> buffer = {0};
|
||||
std::string response;
|
||||
|
||||
do {
|
||||
sizeWritten = read(serverSocket, buffer, 8192);
|
||||
sizeWritten = read(serverSocket, buffer.data(), 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
spdlog::error("Hyprland IPC: Couldn't read (5)");
|
||||
close(serverSocket);
|
||||
return "";
|
||||
}
|
||||
response.append(buffer, sizeWritten);
|
||||
response.append(buffer.data(), sizeWritten);
|
||||
} while (sizeWritten > 0);
|
||||
|
||||
close(serverSocket);
|
||||
@@ -188,7 +218,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
|
||||
}
|
||||
|
||||
Json::Value IPC::getSocket1JsonReply(const std::string& rq) {
|
||||
return m_parser.parse(getSocket1Reply("j/" + rq));
|
||||
std::string reply = getSocket1Reply("j/" + rq);
|
||||
|
||||
if (reply.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return parser_.parse(reply);
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
|
||||
@@ -13,7 +13,7 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con
|
||||
: ALabel(config, "language", id, "{}", 0, true), bar_(bar) {
|
||||
modulesReady = true;
|
||||
|
||||
if (!gIPC.get()) {
|
||||
if (!gIPC) {
|
||||
gIPC = std::make_unique<IPC>();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ Language::~Language() {
|
||||
auto Language::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
spdlog::debug("hyprland language update with full name {}", layout_.full_name);
|
||||
spdlog::debug("hyprland language update with short name {}", layout_.short_name);
|
||||
spdlog::debug("hyprland language update with short description {}", layout_.short_description);
|
||||
spdlog::debug("hyprland language update with variant {}", layout_.variant);
|
||||
|
||||
std::string layoutName = std::string{};
|
||||
if (config_.isMember("format-" + layout_.short_description + "-" + layout_.variant)) {
|
||||
const auto propName = "format-" + layout_.short_description + "-" + layout_.variant;
|
||||
@@ -50,6 +55,8 @@ auto Language::update() -> void {
|
||||
fmt::arg("variant", layout_.variant)));
|
||||
}
|
||||
|
||||
spdlog::debug("hyprland language formatted layout name {}", layoutName);
|
||||
|
||||
if (!format_.empty()) {
|
||||
label_.show();
|
||||
label_.set_markup(layoutName);
|
||||
@@ -63,7 +70,7 @@ auto Language::update() -> void {
|
||||
void Language::onEvent(const std::string& ev) {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
std::string kbName(begin(ev) + ev.find_last_of('>') + 1, begin(ev) + ev.find_first_of(','));
|
||||
auto layoutName = ev.substr(ev.find_first_of(',') + 1);
|
||||
auto layoutName = ev.substr(ev.find_last_of(',') + 1);
|
||||
|
||||
if (config_.isMember("keyboard-name") && kbName != config_["keyboard-name"].asString())
|
||||
return; // ignore
|
||||
@@ -102,11 +109,11 @@ void Language::initLanguage() {
|
||||
}
|
||||
|
||||
auto Language::getLayout(const std::string& fullName) -> Layout {
|
||||
const auto CONTEXT = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES);
|
||||
rxkb_context_parse_default_ruleset(CONTEXT);
|
||||
auto* const context = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES);
|
||||
rxkb_context_parse_default_ruleset(context);
|
||||
|
||||
rxkb_layout* layout = rxkb_layout_first(CONTEXT);
|
||||
while (layout) {
|
||||
rxkb_layout* layout = rxkb_layout_first(context);
|
||||
while (layout != nullptr) {
|
||||
std::string nameOfLayout = rxkb_layout_get_description(layout);
|
||||
|
||||
if (nameOfLayout != fullName) {
|
||||
@@ -115,21 +122,20 @@ auto Language::getLayout(const std::string& fullName) -> Layout {
|
||||
}
|
||||
|
||||
auto name = std::string(rxkb_layout_get_name(layout));
|
||||
auto variant_ = rxkb_layout_get_variant(layout);
|
||||
std::string variant = variant_ == nullptr ? "" : std::string(variant_);
|
||||
const auto* variantPtr = rxkb_layout_get_variant(layout);
|
||||
std::string variant = variantPtr == nullptr ? "" : std::string(variantPtr);
|
||||
|
||||
auto short_description_ = rxkb_layout_get_brief(layout);
|
||||
std::string short_description =
|
||||
short_description_ == nullptr ? "" : std::string(short_description_);
|
||||
const auto* descriptionPtr = rxkb_layout_get_brief(layout);
|
||||
std::string description = descriptionPtr == nullptr ? "" : std::string(descriptionPtr);
|
||||
|
||||
Layout info = Layout{nameOfLayout, name, variant, short_description};
|
||||
Layout info = Layout{nameOfLayout, name, variant, description};
|
||||
|
||||
rxkb_context_unref(CONTEXT);
|
||||
rxkb_context_unref(context);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
rxkb_context_unref(CONTEXT);
|
||||
rxkb_context_unref(context);
|
||||
|
||||
spdlog::debug("hyprland language didn't find matching layout");
|
||||
|
||||
|
||||
@@ -10,13 +10,22 @@ Submap::Submap(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: ALabel(config, "submap", id, "{}", 0, true), bar_(bar) {
|
||||
modulesReady = true;
|
||||
|
||||
if (!gIPC.get()) {
|
||||
parseConfig(config);
|
||||
|
||||
if (!gIPC) {
|
||||
gIPC = std::make_unique<IPC>();
|
||||
}
|
||||
|
||||
label_.hide();
|
||||
ALabel::update();
|
||||
|
||||
// Displays widget immediately if always_on_ assuming default submap
|
||||
// Needs an actual way to retrive current submap on startup
|
||||
if (always_on_) {
|
||||
submap_ = default_submap_;
|
||||
label_.get_style_context()->add_class(submap_);
|
||||
}
|
||||
|
||||
// register for hyprland ipc
|
||||
gIPC->registerForIPC("submap", this);
|
||||
dp.emit();
|
||||
@@ -28,6 +37,18 @@ Submap::~Submap() {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
}
|
||||
|
||||
auto Submap::parseConfig(const Json::Value& config) -> void {
|
||||
auto const& alwaysOn = config["always-on"];
|
||||
if (alwaysOn.isBool()) {
|
||||
always_on_ = alwaysOn.asBool();
|
||||
}
|
||||
|
||||
auto const& defaultSubmap = config["default-submap"];
|
||||
if (defaultSubmap.isString()) {
|
||||
default_submap_ = defaultSubmap.asString();
|
||||
}
|
||||
}
|
||||
|
||||
auto Submap::update() -> void {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
@@ -60,6 +81,10 @@ void Submap::onEvent(const std::string& ev) {
|
||||
|
||||
submap_ = submapName;
|
||||
|
||||
if (submap_.empty() && always_on_) {
|
||||
submap_ = default_submap_;
|
||||
}
|
||||
|
||||
label_.get_style_context()->add_class(submap_);
|
||||
|
||||
spdlog::debug("hyprland submap onevent with {}", submap_);
|
||||
|
||||
@@ -17,9 +17,9 @@ namespace waybar::modules::hyprland {
|
||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) {
|
||||
modulesReady = true;
|
||||
separate_outputs = config["separate-outputs"].asBool();
|
||||
separateOutputs_ = config["separate-outputs"].asBool();
|
||||
|
||||
if (!gIPC.get()) {
|
||||
if (!gIPC) {
|
||||
gIPC = std::make_unique<IPC>();
|
||||
}
|
||||
|
||||
@@ -45,41 +45,47 @@ auto Window::update() -> void {
|
||||
// fix ampersands
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title);
|
||||
std::string window_address = workspace_.last_window;
|
||||
std::string windowName = waybar::util::sanitize_string(workspace_.last_window_title);
|
||||
std::string windowAddress = workspace_.last_window;
|
||||
|
||||
window_data_.title = window_name;
|
||||
windowData_.title = windowName;
|
||||
|
||||
if (!format_.empty()) {
|
||||
label_.show();
|
||||
label_.set_markup(waybar::util::rewriteString(
|
||||
fmt::format(fmt::runtime(format_), fmt::arg("title", window_name),
|
||||
fmt::arg("initialTitle", window_data_.initial_title),
|
||||
fmt::arg("class", window_data_.class_name),
|
||||
fmt::arg("initialClass", window_data_.initial_class_name)),
|
||||
fmt::format(fmt::runtime(format_), fmt::arg("title", windowName),
|
||||
fmt::arg("initialTitle", windowData_.initial_title),
|
||||
fmt::arg("class", windowData_.class_name),
|
||||
fmt::arg("initialClass", windowData_.initial_class_name)),
|
||||
config_["rewrite"]));
|
||||
} else {
|
||||
label_.hide();
|
||||
}
|
||||
|
||||
if (focused_) {
|
||||
image_.show();
|
||||
} else {
|
||||
image_.hide();
|
||||
}
|
||||
|
||||
setClass("empty", workspace_.windows == 0);
|
||||
setClass("solo", solo_);
|
||||
setClass("floating", all_floating_);
|
||||
setClass("floating", allFloating_);
|
||||
setClass("swallowing", swallowing_);
|
||||
setClass("fullscreen", fullscreen_);
|
||||
|
||||
if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) {
|
||||
if (bar_.window.get_style_context()->has_class(last_solo_class_)) {
|
||||
bar_.window.get_style_context()->remove_class(last_solo_class_);
|
||||
spdlog::trace("Removing solo class: {}", last_solo_class_);
|
||||
if (!lastSoloClass_.empty() && soloClass_ != lastSoloClass_) {
|
||||
if (bar_.window.get_style_context()->has_class(lastSoloClass_)) {
|
||||
bar_.window.get_style_context()->remove_class(lastSoloClass_);
|
||||
spdlog::trace("Removing solo class: {}", lastSoloClass_);
|
||||
}
|
||||
}
|
||||
|
||||
if (!solo_class_.empty() && solo_class_ != last_solo_class_) {
|
||||
bar_.window.get_style_context()->add_class(solo_class_);
|
||||
spdlog::trace("Adding solo class: {}", solo_class_);
|
||||
if (!soloClass_.empty() && soloClass_ != lastSoloClass_) {
|
||||
bar_.window.get_style_context()->add_class(soloClass_);
|
||||
spdlog::trace("Adding solo class: {}", soloClass_);
|
||||
}
|
||||
last_solo_class_ = solo_class_;
|
||||
lastSoloClass_ = soloClass_;
|
||||
|
||||
AAppIconLabel::update();
|
||||
}
|
||||
@@ -109,8 +115,12 @@ auto Window::getActiveWorkspace(const std::string& monitorName = "") -> Workspac
|
||||
}
|
||||
|
||||
auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace {
|
||||
return Workspace{value["id"].asInt(), value["windows"].asInt(), value["lastwindow"].asString(),
|
||||
value["lastwindowtitle"].asString()};
|
||||
return Workspace{
|
||||
value["id"].asInt(),
|
||||
value["windows"].asInt(),
|
||||
value["lastwindow"].asString(),
|
||||
value["lastwindowtitle"].asString(),
|
||||
};
|
||||
}
|
||||
|
||||
auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
|
||||
@@ -123,42 +133,45 @@ auto Window::WindowData::parse(const Json::Value& value) -> Window::WindowData {
|
||||
void Window::queryActiveWorkspace() {
|
||||
std::lock_guard<std::mutex> lg(mutex_);
|
||||
|
||||
if (separate_outputs) {
|
||||
if (separateOutputs_) {
|
||||
workspace_ = getActiveWorkspace(this->bar_.output->name);
|
||||
} else {
|
||||
workspace_ = getActiveWorkspace();
|
||||
}
|
||||
|
||||
focused_ = true;
|
||||
if (workspace_.windows > 0) {
|
||||
const auto clients = gIPC->getSocket1JsonReply("clients");
|
||||
assert(clients.isArray());
|
||||
auto active_window = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
|
||||
auto activeWindow = std::find_if(clients.begin(), clients.end(), [&](Json::Value window) {
|
||||
return window["address"] == workspace_.last_window;
|
||||
});
|
||||
if (active_window == std::end(clients)) {
|
||||
|
||||
if (activeWindow == std::end(clients)) {
|
||||
focused_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
window_data_ = WindowData::parse(*active_window);
|
||||
updateAppIconName(window_data_.class_name, window_data_.initial_class_name);
|
||||
std::vector<Json::Value> workspace_windows;
|
||||
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows),
|
||||
windowData_ = WindowData::parse(*activeWindow);
|
||||
updateAppIconName(windowData_.class_name, windowData_.initial_class_name);
|
||||
std::vector<Json::Value> workspaceWindows;
|
||||
std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspaceWindows),
|
||||
[&](Json::Value window) {
|
||||
return window["workspace"]["id"] == workspace_.id && window["mapped"].asBool();
|
||||
});
|
||||
swallowing_ =
|
||||
std::any_of(workspace_windows.begin(), workspace_windows.end(), [&](Json::Value window) {
|
||||
std::any_of(workspaceWindows.begin(), workspaceWindows.end(), [&](Json::Value window) {
|
||||
return !window["swallowing"].isNull() && window["swallowing"].asString() != "0x0";
|
||||
});
|
||||
std::vector<Json::Value> visible_windows;
|
||||
std::copy_if(workspace_windows.begin(), workspace_windows.end(),
|
||||
std::back_inserter(visible_windows),
|
||||
std::vector<Json::Value> visibleWindows;
|
||||
std::copy_if(workspaceWindows.begin(), workspaceWindows.end(),
|
||||
std::back_inserter(visibleWindows),
|
||||
[&](Json::Value window) { return !window["hidden"].asBool(); });
|
||||
solo_ = 1 == std::count_if(visible_windows.begin(), visible_windows.end(),
|
||||
solo_ = 1 == std::count_if(visibleWindows.begin(), visibleWindows.end(),
|
||||
[&](Json::Value window) { return !window["floating"].asBool(); });
|
||||
all_floating_ = std::all_of(visible_windows.begin(), visible_windows.end(),
|
||||
[&](Json::Value window) { return window["floating"].asBool(); });
|
||||
fullscreen_ = window_data_.fullscreen;
|
||||
allFloating_ = std::all_of(visibleWindows.begin(), visibleWindows.end(),
|
||||
[&](Json::Value window) { return window["floating"].asBool(); });
|
||||
fullscreen_ = windowData_.fullscreen;
|
||||
|
||||
// Fullscreen windows look like they are solo
|
||||
if (fullscreen_) {
|
||||
@@ -166,23 +179,24 @@ void Window::queryActiveWorkspace() {
|
||||
}
|
||||
|
||||
// Grouped windows have a tab bar and therefore don't look fullscreen or solo
|
||||
if (window_data_.grouped) {
|
||||
if (windowData_.grouped) {
|
||||
fullscreen_ = false;
|
||||
solo_ = false;
|
||||
}
|
||||
|
||||
if (solo_) {
|
||||
solo_class_ = window_data_.class_name;
|
||||
soloClass_ = windowData_.class_name;
|
||||
} else {
|
||||
solo_class_ = "";
|
||||
soloClass_ = "";
|
||||
}
|
||||
} else {
|
||||
window_data_ = WindowData{};
|
||||
all_floating_ = false;
|
||||
focused_ = false;
|
||||
windowData_ = WindowData{};
|
||||
allFloating_ = false;
|
||||
swallowing_ = false;
|
||||
fullscreen_ = false;
|
||||
solo_ = false;
|
||||
solo_class_ = "";
|
||||
soloClass_ = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "modules/hyprland/windowcreationpayload.hpp"
|
||||
|
||||
#include <json/value.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "modules/hyprland/workspaces.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data)
|
||||
: m_window(std::make_pair(client_data["class"].asString(), client_data["title"].asString())),
|
||||
m_windowAddress(client_data["address"].asString()),
|
||||
m_workspaceName(client_data["workspace"]["name"].asString()) {
|
||||
clearAddr();
|
||||
clearWorkspaceName();
|
||||
}
|
||||
|
||||
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
||||
WindowAddress window_address, std::string window_repr)
|
||||
: m_window(std::move(window_repr)),
|
||||
m_windowAddress(std::move(window_address)),
|
||||
m_workspaceName(std::move(workspace_name)) {
|
||||
clearAddr();
|
||||
clearWorkspaceName();
|
||||
}
|
||||
|
||||
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
||||
WindowAddress window_address, std::string window_class,
|
||||
std::string window_title)
|
||||
: m_window(std::make_pair(std::move(window_class), std::move(window_title))),
|
||||
m_windowAddress(std::move(window_address)),
|
||||
m_workspaceName(std::move(workspace_name)) {
|
||||
clearAddr();
|
||||
clearWorkspaceName();
|
||||
}
|
||||
|
||||
void WindowCreationPayload::clearAddr() {
|
||||
// substr(2, ...) is necessary because Hyprland's JSON follows this format:
|
||||
// 0x{ADDR}
|
||||
// While Hyprland's IPC follows this format:
|
||||
// {ADDR}
|
||||
static const std::string ADDR_PREFIX = "0x";
|
||||
static const int ADDR_PREFIX_LEN = ADDR_PREFIX.length();
|
||||
|
||||
if (m_windowAddress.starts_with(ADDR_PREFIX)) {
|
||||
m_windowAddress =
|
||||
m_windowAddress.substr(ADDR_PREFIX_LEN, m_windowAddress.length() - ADDR_PREFIX_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowCreationPayload::clearWorkspaceName() {
|
||||
// The workspace name may optionally feature "special:" at the beginning.
|
||||
// If so, we need to remove it because the workspace is saved WITHOUT the
|
||||
// special qualifier. The reasoning is that not all of Hyprland's IPC events
|
||||
// use this qualifier, so it's better to be consistent about our uses.
|
||||
|
||||
static const std::string SPECIAL_QUALIFIER_PREFIX = "special:";
|
||||
static const int SPECIAL_QUALIFIER_PREFIX_LEN = SPECIAL_QUALIFIER_PREFIX.length();
|
||||
|
||||
if (m_workspaceName.starts_with(SPECIAL_QUALIFIER_PREFIX)) {
|
||||
m_workspaceName = m_workspaceName.substr(
|
||||
SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN);
|
||||
}
|
||||
|
||||
std::size_t spaceFound = m_workspaceName.find(' ');
|
||||
if (spaceFound != std::string::npos) {
|
||||
m_workspaceName.erase(m_workspaceName.begin() + spaceFound, m_workspaceName.end());
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowCreationPayload::isEmpty(Workspaces &workspace_manager) {
|
||||
if (std::holds_alternative<Repr>(m_window)) {
|
||||
return std::get<Repr>(m_window).empty();
|
||||
}
|
||||
if (std::holds_alternative<ClassAndTitle>(m_window)) {
|
||||
auto [window_class, window_title] = std::get<ClassAndTitle>(m_window);
|
||||
return (window_class.empty() &&
|
||||
(!workspace_manager.windowRewriteConfigUsesTitle() || window_title.empty()));
|
||||
}
|
||||
// Unreachable
|
||||
spdlog::error("WorkspaceWindow::isEmpty: Unreachable");
|
||||
throw std::runtime_error("WorkspaceWindow::isEmpty: Unreachable");
|
||||
}
|
||||
|
||||
int WindowCreationPayload::incrementTimeSpentUncreated() { return m_timeSpentUncreated++; }
|
||||
|
||||
void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) {
|
||||
m_workspaceName = new_workspace_name;
|
||||
}
|
||||
|
||||
std::string WindowCreationPayload::repr(Workspaces &workspace_manager) {
|
||||
if (std::holds_alternative<Repr>(m_window)) {
|
||||
return std::get<Repr>(m_window);
|
||||
}
|
||||
if (std::holds_alternative<ClassAndTitle>(m_window)) {
|
||||
auto [window_class, window_title] = std::get<ClassAndTitle>(m_window);
|
||||
return workspace_manager.getRewrite(window_class, window_title);
|
||||
}
|
||||
// Unreachable
|
||||
spdlog::error("WorkspaceWindow::repr: Unreachable");
|
||||
throw std::runtime_error("WorkspaceWindow::repr: Unreachable");
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <json/value.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "modules/hyprland/workspaces.hpp"
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager,
|
||||
const Json::Value &clients_data)
|
||||
: m_workspaceManager(workspace_manager),
|
||||
m_id(workspace_data["id"].asInt()),
|
||||
m_name(workspace_data["name"].asString()),
|
||||
m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
||||
m_windows(workspace_data["windows"].asInt()),
|
||||
m_isActive(true),
|
||||
m_isPersistentRule(workspace_data["persistent-rule"].asBool()),
|
||||
m_isPersistentConfig(workspace_data["persistent-config"].asBool()) {
|
||||
if (m_name.starts_with("name:")) {
|
||||
m_name = m_name.substr(5);
|
||||
} else if (m_name.starts_with("special")) {
|
||||
m_name = m_id == -99 ? m_name : m_name.substr(8);
|
||||
m_isSpecial = true;
|
||||
}
|
||||
|
||||
m_button.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||
m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked),
|
||||
false);
|
||||
|
||||
m_button.set_relief(Gtk::RELIEF_NONE);
|
||||
m_content.set_center_widget(m_label);
|
||||
m_button.add(m_content);
|
||||
|
||||
initializeWindowMap(clients_data);
|
||||
}
|
||||
|
||||
void addOrRemoveClass(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
||||
const std::string &class_name) {
|
||||
if (condition) {
|
||||
context->add_class(class_name);
|
||||
} else {
|
||||
context->remove_class(class_name);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> Workspace::closeWindow(WindowAddress const &addr) {
|
||||
if (m_windowMap.contains(addr)) {
|
||||
return removeWindow(addr);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool Workspace::handleClicked(GdkEventButton *bt) const {
|
||||
if (bt->type == GDK_BUTTON_PRESS) {
|
||||
try {
|
||||
if (id() > 0) { // normal
|
||||
if (m_workspaceManager.moveToMonitor()) {
|
||||
gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id()));
|
||||
} else {
|
||||
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
||||
}
|
||||
} else if (!isSpecial()) { // named (this includes persistent)
|
||||
if (m_workspaceManager.moveToMonitor()) {
|
||||
gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name());
|
||||
} else {
|
||||
gIPC->getSocket1Reply("dispatch workspace name:" + name());
|
||||
}
|
||||
} else if (id() != -99) { // named special
|
||||
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
|
||||
} else { // special
|
||||
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
||||
}
|
||||
return true;
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Workspace::initializeWindowMap(const Json::Value &clients_data) {
|
||||
m_windowMap.clear();
|
||||
for (auto client : clients_data) {
|
||||
if (client["workspace"]["id"].asInt() == id()) {
|
||||
insertWindow({client});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Workspace::insertWindow(WindowCreationPayload create_window_paylod) {
|
||||
if (!create_window_paylod.isEmpty(m_workspaceManager)) {
|
||||
m_windowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(m_workspaceManager);
|
||||
}
|
||||
};
|
||||
|
||||
bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) {
|
||||
if (create_window_paylod.getWorkspaceName() == name()) {
|
||||
insertWindow(create_window_paylod);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Workspace::removeWindow(WindowAddress const &addr) {
|
||||
std::string windowRepr = m_windowMap[addr];
|
||||
m_windowMap.erase(addr);
|
||||
return windowRepr;
|
||||
}
|
||||
|
||||
std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
|
||||
spdlog::trace("Selecting icon for workspace {}", name());
|
||||
if (isUrgent()) {
|
||||
auto urgentIconIt = icons_map.find("urgent");
|
||||
if (urgentIconIt != icons_map.end()) {
|
||||
return urgentIconIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive()) {
|
||||
auto activeIconIt = icons_map.find("active");
|
||||
if (activeIconIt != icons_map.end()) {
|
||||
return activeIconIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSpecial()) {
|
||||
auto specialIconIt = icons_map.find("special");
|
||||
if (specialIconIt != icons_map.end()) {
|
||||
return specialIconIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto namedIconIt = icons_map.find(name());
|
||||
if (namedIconIt != icons_map.end()) {
|
||||
return namedIconIt->second;
|
||||
}
|
||||
|
||||
if (isVisible()) {
|
||||
auto visibleIconIt = icons_map.find("visible");
|
||||
if (visibleIconIt != icons_map.end()) {
|
||||
return visibleIconIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty()) {
|
||||
auto emptyIconIt = icons_map.find("empty");
|
||||
if (emptyIconIt != icons_map.end()) {
|
||||
return emptyIconIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPersistent()) {
|
||||
auto persistentIconIt = icons_map.find("persistent");
|
||||
if (persistentIconIt != icons_map.end()) {
|
||||
return persistentIconIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto defaultIconIt = icons_map.find("default");
|
||||
if (defaultIconIt != icons_map.end()) {
|
||||
return defaultIconIt->second;
|
||||
}
|
||||
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void Workspace::update(const std::string &format, const std::string &icon) {
|
||||
// clang-format off
|
||||
if (this->m_workspaceManager.activeOnly() && \
|
||||
!this->isActive() && \
|
||||
!this->isPersistent() && \
|
||||
!this->isVisible() && \
|
||||
!this->isSpecial()) {
|
||||
// clang-format on
|
||||
// if activeOnly is true, hide if not active, persistent, visible or special
|
||||
m_button.hide();
|
||||
return;
|
||||
}
|
||||
if (this->m_workspaceManager.specialVisibleOnly() && this->isSpecial() && !this->isVisible()) {
|
||||
m_button.hide();
|
||||
return;
|
||||
}
|
||||
m_button.show();
|
||||
|
||||
auto styleContext = m_button.get_style_context();
|
||||
addOrRemoveClass(styleContext, isActive(), "active");
|
||||
addOrRemoveClass(styleContext, isSpecial(), "special");
|
||||
addOrRemoveClass(styleContext, isEmpty(), "empty");
|
||||
addOrRemoveClass(styleContext, isPersistent(), "persistent");
|
||||
addOrRemoveClass(styleContext, isUrgent(), "urgent");
|
||||
addOrRemoveClass(styleContext, isVisible(), "visible");
|
||||
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
|
||||
|
||||
std::string windows;
|
||||
auto windowSeparator = m_workspaceManager.getWindowSeparator();
|
||||
|
||||
bool isNotFirst = false;
|
||||
|
||||
for (auto &[_pid, window_repr] : m_windowMap) {
|
||||
if (isNotFirst) {
|
||||
windows.append(windowSeparator);
|
||||
}
|
||||
isNotFirst = true;
|
||||
windows.append(window_repr);
|
||||
}
|
||||
|
||||
m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
||||
fmt::arg("name", name()), fmt::arg("icon", icon),
|
||||
fmt::arg("windows", windows)));
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::hyprland
|
||||
+438
-678
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user