From e0944806847a7f8862ba8c481e4ec00477bf6fef Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Sat, 2 Apr 2022 21:08:43 +0200 Subject: [PATCH 01/17] Very basic hypr window title module --- include/factory.hpp | 5 ++ include/modules/hypr/ipc.hpp | 22 +++++++++ include/modules/hypr/window.hpp | 23 +++++++++ meson.build | 6 +++ src/factory.cpp | 5 ++ src/modules/hypr/ipc.cpp | 86 +++++++++++++++++++++++++++++++++ src/modules/hypr/window.cpp | 27 +++++++++++ 7 files changed, 174 insertions(+) create mode 100644 include/modules/hypr/ipc.hpp create mode 100644 include/modules/hypr/window.hpp create mode 100644 src/modules/hypr/ipc.cpp create mode 100644 src/modules/hypr/window.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 3954dac..d3e0a08 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,5 +1,7 @@ #pragma once +#define HAVE_HYPR + #include #ifdef HAVE_LIBDATE #include "modules/clock.hpp" @@ -19,6 +21,9 @@ #ifdef HAVE_RIVER #include "modules/river/tags.hpp" #endif +#ifdef HAVE_HYPR +#include "modules/hypr/window.hpp" +#endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" #endif diff --git a/include/modules/hypr/ipc.hpp b/include/modules/hypr/ipc.hpp new file mode 100644 index 0000000..0cf061a --- /dev/null +++ b/include/modules/hypr/ipc.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bar.hpp" +#include "client.hpp" + +namespace waybar::modules::hypr { + + std::string makeRequest(std::string); + +} // namespace waybar::modules::hypr diff --git a/include/modules/hypr/window.hpp b/include/modules/hypr/window.hpp new file mode 100644 index 0000000..83f8650 --- /dev/null +++ b/include/modules/hypr/window.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" +#include "util/format.hpp" +#include "ipc.hpp" + +namespace waybar::modules::hypr { + +class Window : public ALabel { + public: + Window(const std::string&, const Json::Value&); + ~Window() = default; + auto update() -> void; + + private: + util::SleeperThread thread_; +}; + +} // namespace waybar::modules::hypr diff --git a/meson.build b/meson.build index e9daeca..645c1f6 100644 --- a/meson.build +++ b/meson.build @@ -187,6 +187,12 @@ src_files += [ 'src/modules/sway/workspaces.cpp' ] +add_project_arguments('-DHAVE_HYPR', language: 'cpp') +src_files += [ + 'src/modules/hypr/ipc.cpp', + 'src/modules/hypr/window.cpp', +] + if true add_project_arguments('-DHAVE_WLR', language: 'cpp') src_files += 'src/modules/wlr/taskbar.cpp' diff --git a/src/factory.cpp b/src/factory.cpp index ab0dc43..8a024d7 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -45,6 +45,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "river/tags") { return new waybar::modules::river::Tags(id, bar_, config_[name]); } +#endif +#ifdef HAVE_HYPR + if (ref == "hypr/window") { + return new waybar::modules::hypr::Window(id, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hypr/ipc.cpp b/src/modules/hypr/ipc.cpp new file mode 100644 index 0000000..40488ec --- /dev/null +++ b/src/modules/hypr/ipc.cpp @@ -0,0 +1,86 @@ +#include "modules/hypr/ipc.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/string.hpp" + +std::string waybar::modules::hypr::makeRequest(std::string req) { + const auto SERVERSOCKET = socket(AF_INET, SOCK_STREAM, 0); + + if (SERVERSOCKET < 0) { + spdlog::error("[Hypr IPC] Couldn't open a socket."); + return ""; + } + + const auto SERVER = gethostbyname("localhost"); + + if (!SERVER) { + spdlog::error("[Hypr IPC] Couldn't get localhost."); + return ""; + } + + sockaddr_in serverAddress = {0}; + serverAddress.sin_family = AF_INET; + bcopy((char*)SERVER->h_addr, (char*)&serverAddress.sin_addr.s_addr, SERVER->h_length); + + std::ifstream socketPortStream; + socketPortStream.open("/tmp/hypr/.socket"); + + if (!socketPortStream.good()) { + spdlog::error("[Hypr IPC] No socket file. Is Hyprland running?"); + return ""; + } + + std::string port = ""; + std::getline(socketPortStream, port); + socketPortStream.close(); + + int portInt = 0; + try { + portInt = std::stoi(port.c_str()); + } catch (...) { + spdlog::error("[Hypr IPC] Port not an int?!"); + return ""; + } + + if (portInt == 0) { + spdlog::error("[Hypr IPC] Port 0. Aborting."); + return ""; + } + + serverAddress.sin_port = portInt; + + if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { + spdlog::error("[Hypr IPC] Couldn't connect to port {} , is Hyprland running?", port); + return ""; + } + + auto sizeWritten = write(SERVERSOCKET, req.c_str(), req.length()); + + if (sizeWritten < 0) { + spdlog::error("[Hypr IPC] Couldn't write to the socket."); + return ""; + } + + char buffer[8192] = {0}; + + sizeWritten = read(SERVERSOCKET, buffer, 8192); + + if (sizeWritten < 0) { + spdlog::error("[Hypr IPC] Couldn't cread from the socket."); + return ""; + } + + close(SERVERSOCKET); + + return std::string(buffer); +} \ No newline at end of file diff --git a/src/modules/hypr/window.cpp b/src/modules/hypr/window.cpp new file mode 100644 index 0000000..ca224fc --- /dev/null +++ b/src/modules/hypr/window.cpp @@ -0,0 +1,27 @@ +#include "modules/hypr/window.hpp" +#include "modules/hypr/ipc.hpp" + +using namespace waybar::util; + +waybar::modules::hypr::Window::Window(const std::string& id, const Json::Value& config) : ALabel(config, "window", id, "{window}", 0.5f) { + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +auto waybar::modules::hypr::Window::update() -> void { + auto format = format_; + + std::string windowName = waybar::modules::hypr::makeRequest("activewindow"); + + if (windowName != "") + windowName = windowName.substr(windowName.find_first_of('>') + 2, windowName.find_first_of('\n') - windowName.find_first_of('>') - 3); + + event_box_.show(); + label_.set_markup(fmt::format(format, + fmt::arg("window", windowName))); + + // Call parent update + ALabel::update(); +} From 72f478c195b9ad527f577a6c012936a19108eac4 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 1 Jul 2022 12:46:28 +0200 Subject: [PATCH 02/17] added backend and hyprland/window --- include/factory.hpp | 4 + include/modules/hyprland/backend.hpp | 30 ++++++++ include/modules/hyprland/window.hpp | 29 ++++++++ meson.build | 6 ++ src/factory.cpp | 5 ++ src/modules/hyprland/backend.cpp | 105 +++++++++++++++++++++++++++ src/modules/hyprland/window.cpp | 54 ++++++++++++++ 7 files changed, 233 insertions(+) create mode 100644 include/modules/hyprland/backend.hpp create mode 100644 include/modules/hyprland/window.hpp create mode 100644 src/modules/hyprland/backend.cpp create mode 100644 src/modules/hyprland/window.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 1e79bd7..47ef530 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -21,6 +21,10 @@ #include "modules/river/tags.hpp" #include "modules/river/window.hpp" #endif +#ifdef HAVE_HYPRLAND +#include "modules/hyprland/backend.hpp" +#include "modules/hyprland/window.hpp" +#endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" #endif diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp new file mode 100644 index 0000000..b9d1c99 --- /dev/null +++ b/include/modules/hyprland/backend.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace waybar::modules::hyprland { +class IPC { +public: + IPC() { startIPC(); } + + void registerForIPC(const std::string&, std::function); + + std::string getSocket1Reply(const std::string& rq); + +private: + + void startIPC(); + void parseIPC(const std::string&); + + std::mutex callbackMutex; + std::deque>> callbacks; +}; + +inline std::unique_ptr gIPC; +inline bool modulesReady = false; +}; + diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp new file mode 100644 index 0000000..f80159b --- /dev/null +++ b/include/modules/hyprland/window.hpp @@ -0,0 +1,29 @@ +#include + +#include + +#include "ALabel.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "util/json.hpp" + +namespace waybar::modules::hyprland { + +class Window : public waybar::ALabel { +public: + Window(const std::string&, const waybar::Bar&, const Json::Value&); + ~Window() = default; + +private: + void onEvent(const std::string&); + + const Bar& bar_; + IPC ipc; + unsigned app_icon_size_{24}; + bool update_app_icon_{true}; + std::string app_icon_name_; + util::JsonParser parser_; + std::string lastView; +}; + +} \ No newline at end of file diff --git a/meson.build b/meson.build index 441ccfb..3c32006 100644 --- a/meson.build +++ b/meson.build @@ -201,6 +201,12 @@ if true src_files += 'src/modules/river/window.cpp' endif +if true + add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') + src_files += 'src/modules/hyprland/backend.cpp' + src_files += 'src/modules/hyprland/window.cpp' +endif + if libnl.found() and libnlgen.found() add_project_arguments('-DHAVE_LIBNL', language: 'cpp') src_files += 'src/modules/network.cpp' diff --git a/src/factory.cpp b/src/factory.cpp index 841465f..6df69d5 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -56,6 +56,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "river/window") { return new waybar::modules::river::Window(id, bar_, config_[name]); } +#endif +#ifdef HAVE_HYPRLAND + if (ref == "hyprland/window") { + return new waybar::modules::hyprland::Window(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp new file mode 100644 index 0000000..16deb18 --- /dev/null +++ b/src/modules/hyprland/backend.cpp @@ -0,0 +1,105 @@ +#include "modules/hyprland/backend.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +void waybar::modules::hyprland::IPC::startIPC() { + // will start IPC and relay events to parseIPC + + std::thread([&]() { + // check for hyprland + const char* HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); + + if (!HIS) { + spdlog::warn("Hyprland is not running, Hyprland IPC will not be available."); + return; + } + + if (!modulesReady) return; + + spdlog::info("Hyprland IPC starting"); + + struct sockaddr_un addr; + int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (socketfd == -1) { + spdlog::error("Hyprland IPC: socketfd failed"); + return; + } + + addr.sun_family = AF_UNIX; + + // socket path + std::string socketPath = "/tmp/hypr/" + std::string(HIS) + "/.socket2.sock"; + + strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); + + addr.sun_path[sizeof(addr.sun_path) - 1] = 0; + + int l = sizeof(struct sockaddr_un); + + if (connect(socketfd, (struct sockaddr*)&addr, l) == -1) { + spdlog::error("Hyprland IPC: Unable to connect?"); + return; + } + + auto file = fdopen(socketfd, "r"); + + while (1) { + // read + + char buffer[1024]; // Hyprland socket2 events are max 1024 bytes + auto recievedCharPtr = fgets(buffer, 1024, file); + + if (!recievedCharPtr) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + + callbackMutex.lock(); + + std::string messageRecieved(buffer); + + messageRecieved = messageRecieved.substr(0, messageRecieved.find_first_of('\n')); + + spdlog::debug("hyprland IPC received {}", messageRecieved); + + parseIPC(messageRecieved); + + callbackMutex.unlock(); + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }).detach(); +} + +void waybar::modules::hyprland::IPC::parseIPC(const std::string& ev) { + // todo + std::string request = ev.substr(0, ev.find_first_of('>')); + + for (auto& [eventname, handler] : callbacks) { + if (eventname == request) { + handler(ev); + } + } +} + +void waybar::modules::hyprland::IPC::registerForIPC(const std::string& ev, + std::function fn) { + callbackMutex.lock(); + + callbacks.emplace_back(std::make_pair(ev, fn)); + + callbackMutex.unlock(); +} \ No newline at end of file diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp new file mode 100644 index 0000000..96d07bd --- /dev/null +++ b/src/modules/hyprland/window.cpp @@ -0,0 +1,54 @@ +#include "modules/hyprland/backend.hpp" +#include "modules/hyprland/window.hpp" + +#include + +namespace waybar::modules::hyprland { + +Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) + : ALabel(config, "window", id, "{}", 0, true), bar_(bar) { + modulesReady = true; + + if (!gIPC.get()) { + gIPC = std::make_unique(); + } + + label_.hide(); + ALabel::update(); + + // register for hyprland ipc + gIPC->registerForIPC("activewindow", [&](const std::string& ev) { this->onEvent(ev); }); +} + +void Window::onEvent(const std::string& ev) { + auto windowName = ev.substr(ev.find_first_of(',') + 1).substr(0, 256); + + // fix ampersands + auto replaceAll = [](std::string str, const std::string& from, + const std::string& to) -> std::string { + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + return str; + }; + + windowName = replaceAll(windowName, "&", "&"); + + if (windowName == lastView) return; + + lastView = windowName; + + spdlog::debug("hyprland window onevent with {}", windowName); + + if (!format_.empty()) { + label_.show(); + label_.set_markup(fmt::format(format_, windowName)); + } else { + label_.hide(); + } + + ALabel::update(); +} +} \ No newline at end of file From c1f92d2a3c32e0db8846e0aae163e5317bcaac69 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 1 Jul 2022 15:16:54 +0200 Subject: [PATCH 03/17] added workspaces --- include/factory.hpp | 1 + include/modules/hyprland/window.hpp | 4 - include/modules/hyprland/workspaces.hpp | 45 +++++ meson.build | 1 + src/factory.cpp | 3 + src/modules/hyprland/backend.cpp | 79 +++++++- src/modules/hyprland/workspaces.cpp | 228 ++++++++++++++++++++++++ 7 files changed, 351 insertions(+), 10 deletions(-) create mode 100644 include/modules/hyprland/workspaces.hpp create mode 100644 src/modules/hyprland/workspaces.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 47ef530..c24e654 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -24,6 +24,7 @@ #ifdef HAVE_HYPRLAND #include "modules/hyprland/backend.hpp" #include "modules/hyprland/window.hpp" +#include "modules/hyprland/workspaces.hpp" #endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index f80159b..e21426a 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -18,10 +18,6 @@ private: void onEvent(const std::string&); const Bar& bar_; - IPC ipc; - unsigned app_icon_size_{24}; - bool update_app_icon_{true}; - std::string app_icon_name_; util::JsonParser parser_; std::string lastView; }; diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp new file mode 100644 index 0000000..8d182d5 --- /dev/null +++ b/include/modules/hyprland/workspaces.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include "AModule.hpp" +#include "bar.hpp" +#include "modules/hyprland/backend.hpp" +#include "util/json.hpp" +#include +#include +#include + +namespace waybar::modules::hyprland { + +class Workspaces : public AModule, public sigc::trackable { + public: + Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); + ~Workspaces() = default; + auto update() -> void; + + private: + void onEvent(const std::string&); + bool handleScroll(GdkEventScroll*); + void configOnLaunch(const Json::Value&); + void updateButtons(); + void parseInitHyprlandWorkspaces(); + Gtk::Button& addButton(const std::string&); + std::string getIcon(const std::string&); + std::deque getAllSortedWS(); + + bool isNumber(const std::string&); + + Gtk::Box box_; + const Bar& bar_; + std::deque workspaces; + std::deque persistentWorkspaces; + std::unordered_map buttons_; + std::string focusedWorkspace; + + std::mutex mutex_; +}; + +} // namespace waybar::modules::sway diff --git a/meson.build b/meson.build index 3c32006..39e9cdc 100644 --- a/meson.build +++ b/meson.build @@ -205,6 +205,7 @@ if true add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') src_files += 'src/modules/hyprland/backend.cpp' src_files += 'src/modules/hyprland/window.cpp' + src_files += 'src/modules/hyprland/workspaces.cpp' endif if libnl.found() and libnlgen.found() diff --git a/src/factory.cpp b/src/factory.cpp index 6df69d5..2c6fe63 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -61,6 +61,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "hyprland/window") { return new waybar::modules::hyprland::Window(id, bar_, config_[name]); } + if (ref == "hyprland/workspaces") { + return new waybar::modules::hyprland::Workspaces(id, bar_, config_[name]); + } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 16deb18..88705d1 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -1,8 +1,9 @@ #include "modules/hyprland/backend.hpp" -#include -#include +#include +#include #include +#include #include #include #include @@ -12,9 +13,13 @@ #include #include -#include +#include +#include +#include -void waybar::modules::hyprland::IPC::startIPC() { +namespace waybar::modules::hyprland { + +void IPC::startIPC() { // will start IPC and relay events to parseIPC std::thread([&]() { @@ -84,7 +89,7 @@ void waybar::modules::hyprland::IPC::startIPC() { }).detach(); } -void waybar::modules::hyprland::IPC::parseIPC(const std::string& ev) { +void IPC::parseIPC(const std::string& ev) { // todo std::string request = ev.substr(0, ev.find_first_of('>')); @@ -95,11 +100,73 @@ void waybar::modules::hyprland::IPC::parseIPC(const std::string& ev) { } } -void waybar::modules::hyprland::IPC::registerForIPC(const std::string& ev, +void IPC::registerForIPC(const std::string& ev, std::function fn) { callbackMutex.lock(); callbacks.emplace_back(std::make_pair(ev, fn)); callbackMutex.unlock(); +} + +std::string IPC::getSocket1Reply(const std::string& rq) { + // basically hyprctl + + const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); + + if (SERVERSOCKET < 0) { + spdlog::error("Hyprland IPC: Couldn't open a socket (1)"); + return ""; + } + + const auto SERVER = gethostbyname("localhost"); + + if (!SERVER) { + spdlog::error("Hyprland IPC: Couldn't get host (2)"); + return ""; + } + + // get the instance signature + auto instanceSig = getenv("HYPRLAND_INSTANCE_SIGNATURE"); + + if (!instanceSig) { + 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"; + + strcpy(serverAddress.sun_path, socketPath.c_str()); + + if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { + spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); + return ""; + } + + auto sizeWritten = write(SERVERSOCKET, rq.c_str(), rq.length()); + + if (sizeWritten < 0) { + spdlog::error("Hyprland IPC: Couldn't write (4)"); + return ""; + } + + char buffer[8192] = {0}; + + sizeWritten = read(SERVERSOCKET, buffer, 8192); + + if (sizeWritten < 0) { + spdlog::error("Hyprland IPC: Couldn't read (5)"); + return ""; + } + + close(SERVERSOCKET); + + return std::string(buffer); +} + } \ No newline at end of file diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp new file mode 100644 index 0000000..0dad13a --- /dev/null +++ b/src/modules/hyprland/workspaces.cpp @@ -0,0 +1,228 @@ +#include "modules/hyprland/workspaces.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace waybar::modules::hyprland { + +Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) + : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), + bar_(bar), + box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { + box_.set_name("workspaces"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } + + event_box_.add(box_); + + if (config["enable-bar-scroll"].asBool()) { + auto &window = const_cast(bar_).window; + window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); + window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); + } + + modulesReady = true; + + if (!gIPC.get()) { + gIPC = std::make_unique(); + } + + // register for hyprland ipc + gIPC->registerForIPC("createworkspace", [&](const std::string &ev) { this->onEvent(ev); }); + gIPC->registerForIPC("destroyworkspace", [&](const std::string &ev) { this->onEvent(ev); }); + gIPC->registerForIPC("activemon", [&](const std::string &ev) { this->onEvent(ev); }); + gIPC->registerForIPC("workspace", [&](const std::string &ev) { this->onEvent(ev); }); + + // parse cfg stuff + configOnLaunch(config); + + // parse workspaces already existing + parseInitHyprlandWorkspaces(); +} + +void Workspaces::parseInitHyprlandWorkspaces() { + std::istringstream WORKSPACES; + WORKSPACES.str(gIPC->getSocket1Reply("workspaces")); + + std::string line; + while (std::getline(WORKSPACES, line)) { + if (line.find("workspace ID") == 0) { + auto workspaceName = line.substr(line.find_first_of('(') + 1).substr(0, line.find_first_of(')') - line.find_first_of('(') - 1); + workspaces.emplace_back(workspaceName); + } + } +} + +void Workspaces::configOnLaunch(const Json::Value& cfg) { + if (cfg["persistent_workspaces"].isObject()) { + spdlog::info("persistent"); + const Json::Value &persistentWorkspacesJSON = cfg["persistent_workspaces"]; + + for (auto &wn : persistentWorkspacesJSON.getMemberNames()) { + persistentWorkspaces.emplace_back(wn); + spdlog::info("persistent ws {}", wn); + } + } +} + +std::deque Workspaces::getAllSortedWS() { + std::deque result; + for (auto& ws : workspaces) + result.emplace_back(ws); + for (auto& ws : persistentWorkspaces) + result.emplace_back(ws); + + std::sort(result.begin(), result.end(), [&](const std::string& ws1, const std::string& ws2) { + if (isNumber(ws1) && isNumber(ws2)) { + return std::stoi(ws1) < std::stoi(ws2); + } else if (isNumber(ws1)) { + return true; + } else { + return false; + } + }); + + return result; +} + +std::string Workspaces::getIcon(const std::string &name) { + return config_["format-icons"][name].asString(); +} + +auto Workspaces::update() -> void { + updateButtons(); +} + +void Workspaces::updateButtons() { + mutex_.lock(); + + auto ws = getAllSortedWS(); + + for (auto it = ws.begin(); it != ws.end(); ++it) { + auto bit = buttons_.find(*it); + + auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; + + if (focusedWorkspace == *it) { + button.get_style_context()->add_class("focused"); + } else { + button.get_style_context()->remove_class("focused"); + } + + std::string label = *it; + if (config_["format"].isString()) { + auto format = config_["format"].asString(); + label = fmt::format(format, fmt::arg("icon", getIcon(*it)), fmt::arg("name", *it)); + } + + button.set_label(label); + + button.show(); + } + + AModule::update(); + + mutex_.unlock(); +} + +bool Workspaces::isNumber(const std::string& str) { + for (auto &c : str) { + if (!(isdigit(c) != 0 || c == '-')) return false; + } + return true; +} + +Gtk::Button &Workspaces::addButton(const std::string& name) { + auto pair = buttons_.emplace(name, name); + auto &&button = pair.first->second; + box_.pack_start(button, false, false, 0); + button.set_name(name); + button.set_relief(Gtk::RELIEF_NONE); + if (!config_["disable-click"].asBool()) { + button.signal_pressed().connect([&, name]{ + if (isNumber(name)) { + gIPC->getSocket1Reply("dispatch workspace " + name); + spdlog::info("executing {}", "dispatch workspace " + name); + } + else { + gIPC->getSocket1Reply("dispatch workspace name:" + name); + spdlog::info("executing {}", "dispatch workspace name:" + name); + } + }); + } + + return button; +} + +void Workspaces::onEvent(const std::string& ev) { + const auto EVENT = ev.substr(0, ev.find_first_of('>')); + const auto WORKSPACE = ev.substr(ev.find_last_of('>') + 1); + + mutex_.lock(); + + if (EVENT == "activemon") { + focusedWorkspace = WORKSPACE.substr(WORKSPACE.find_first_of(',') + 1); + } else if (EVENT == "workspace") { + focusedWorkspace = WORKSPACE; + } else if (EVENT == "createworkspace") { + workspaces.emplace_back(WORKSPACE); + + // remove the buttons for reorder + buttons_.clear(); + } else { + const auto it = std::remove(workspaces.begin(), workspaces.end(), WORKSPACE); + + if (it != workspaces.end()) + workspaces.erase(it); + + // also remove the buttons + buttons_.clear(); + } + + dp.emit(); + + mutex_.unlock(); +} + +bool Workspaces::handleScroll(GdkEventScroll *e) { + if (gdk_event_get_pointer_emulated((GdkEvent *)e)) { + /** + * Ignore emulated scroll events on window + */ + return false; + } + auto dir = AModule::getScrollDir(e); + if (dir == SCROLL_DIR::NONE) { + return true; + } + + mutex_.lock(); + + if (dir == SCROLL_DIR::UP) { + gIPC->getSocket1Reply("dispatch workspace +1"); + } else if (dir == SCROLL_DIR::DOWN) { + gIPC->getSocket1Reply("dispatch workspace -1"); + } + + mutex_.unlock(); + + return true; +} +}; From 17b60bc73783f8f32a75b779ca0a867e9730b352 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 1 Jul 2022 15:35:25 +0200 Subject: [PATCH 04/17] minor changes --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 35 ++++++++++++++++++------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 8d182d5..93e9770 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -32,6 +32,8 @@ class Workspaces : public AModule, public sigc::trackable { bool isNumber(const std::string&); + bool needReorder = false; + Gtk::Box box_; const Bar& bar_; std::deque workspaces; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 0dad13a..92dabc9 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -118,6 +118,9 @@ void Workspaces::updateButtons() { for (auto it = ws.begin(); it != ws.end(); ++it) { auto bit = buttons_.find(*it); + if (bit == buttons_.end()) + needReorder = true; + auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; if (focusedWorkspace == *it) { @@ -132,6 +135,10 @@ void Workspaces::updateButtons() { label = fmt::format(format, fmt::arg("icon", getIcon(*it)), fmt::arg("name", *it)); } + if (needReorder) { + box_.reorder_child(button, it - workspaces.begin()); + } + button.set_label(label); button.show(); @@ -139,6 +146,8 @@ void Workspaces::updateButtons() { AModule::update(); + needReorder = false; + mutex_.unlock(); } @@ -183,22 +192,28 @@ void Workspaces::onEvent(const std::string& ev) { focusedWorkspace = WORKSPACE; } else if (EVENT == "createworkspace") { workspaces.emplace_back(WORKSPACE); - - // remove the buttons for reorder - buttons_.clear(); } else { - const auto it = std::remove(workspaces.begin(), workspaces.end(), WORKSPACE); + for (auto it = workspaces.begin(); it != workspaces.end(); it++) { + if (*it == WORKSPACE) { + workspaces.erase(it); + break; + } + } - if (it != workspaces.end()) - workspaces.erase(it); + // also remove the button + for (auto it = buttons_.begin(); it != buttons_.end(); it++) { + if (it->second.get_name() == WORKSPACE) + it = buttons_.erase(it); + + if (it == buttons_.end()) + break; + } - // also remove the buttons - buttons_.clear(); } - dp.emit(); - mutex_.unlock(); + + dp.emit(); } bool Workspaces::handleScroll(GdkEventScroll *e) { From 99ed2bb7fa6517a0f0a06595cc6202159d1c971e Mon Sep 17 00:00:00 2001 From: TheRealLorenz Date: Wed, 10 Aug 2022 10:34:51 +0200 Subject: [PATCH 05/17] Feature: sway/window can show 'shell' parameter --- include/modules/sway/window.hpp | 3 ++- src/modules/sway/window.cpp | 25 +++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index d80cc25..c99ba7f 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -21,7 +21,7 @@ class Window : public AIconLabel, public sigc::trackable { private: void onEvent(const struct Ipc::ipc_response&); void onCmd(const struct Ipc::ipc_response&); - std::tuple getFocusedNode( + std::tuple getFocusedNode( const Json::Value& nodes, std::string& output); void getTree(); std::string rewriteTitle(const std::string& title); @@ -35,6 +35,7 @@ class Window : public AIconLabel, public sigc::trackable { std::string app_class_; std::string old_app_id_; std::size_t app_nb_; + std::string shell_; unsigned app_icon_size_{24}; bool update_app_icon_{true}; std::string app_icon_name_; diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index dbff90a..d39c1fe 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -44,7 +44,7 @@ void Window::onCmd(const struct Ipc::ipc_response& res) { std::lock_guard lock(mutex_); auto payload = parser_.parse(res.payload); auto output = payload["output"].isString() ? payload["output"].asString() : ""; - std::tie(app_nb_, windowId_, window_, app_id_, app_class_) = + std::tie(app_nb_, windowId_, window_, app_id_, app_class_, shell_) = getFocusedNode(payload["nodes"], output); updateAppIconName(); dp.emit(); @@ -176,7 +176,7 @@ auto Window::update() -> void { bar_.window.get_style_context()->remove_class("empty"); } label_.set_markup( - fmt::format(format_, fmt::arg("title", rewriteTitle(window_)), fmt::arg("app_id", app_id_))); + fmt::format(format_, fmt::arg("title", rewriteTitle(window_)), fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } @@ -206,7 +206,7 @@ int leafNodesInWorkspace(const Json::Value& node) { return sum; } -std::tuple gfnWithWorkspace( +std::tuple gfnWithWorkspace( const Json::Value& nodes, std::string& output, const Json::Value& config_, const Bar& bar_, Json::Value& parentWorkspace) { for (auto const& node : nodes) { @@ -222,30 +222,35 @@ std::tuple gfnWithWorks const auto app_class = node["window_properties"]["class"].isString() ? node["window_properties"]["class"].asString() : ""; + + const auto shell = node["shell"].isString() + ? node["shell"].asString() + : ""; + int nb = node.size(); if (parentWorkspace != 0) nb = leafNodesInWorkspace(parentWorkspace); return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), app_id, - app_class}; + app_class, shell}; } } // iterate if (node["type"] == "workspace") parentWorkspace = node; - auto [nb, id, name, app_id, app_class] = + auto [nb, id, name, app_id, app_class, shell] = gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace); if (id > -1 && !name.empty()) { - return {nb, id, name, app_id, app_class}; + return {nb, id, name, app_id, app_class, shell}; } // Search for floating node - std::tie(nb, id, name, app_id, app_class) = + std::tie(nb, id, name, app_id, app_class, shell) = gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace); if (id > -1 && !name.empty()) { - return {nb, id, name, app_id, app_class}; + return {nb, id, name, app_id, app_class, shell}; } } - return {0, -1, "", "", ""}; + return {0, -1, "", "", "", ""}; } -std::tuple Window::getFocusedNode( +std::tuple Window::getFocusedNode( const Json::Value& nodes, std::string& output) { Json::Value placeholder = 0; return gfnWithWorkspace(nodes, output, config_, bar_, placeholder); From 5b1cd65e2072663334802ebd811ed8a05f96fe5d Mon Sep 17 00:00:00 2001 From: TheRealLorenz Date: Wed, 10 Aug 2022 10:41:18 +0200 Subject: [PATCH 06/17] Fix: better formatting --- src/modules/sway/window.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index d39c1fe..ebbfa4b 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -175,8 +175,8 @@ auto Window::update() -> void { bar_.window.get_style_context()->remove_class("solo"); bar_.window.get_style_context()->remove_class("empty"); } - label_.set_markup( - fmt::format(format_, fmt::arg("title", rewriteTitle(window_)), fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); + label_.set_markup(fmt::format(format_, fmt::arg("title", rewriteTitle(window_)), + fmt::arg("app_id", app_id_), fmt::arg("shell", shell_))); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } @@ -223,14 +223,12 @@ std::tuple ? node["window_properties"]["class"].asString() : ""; - const auto shell = node["shell"].isString() - ? node["shell"].asString() - : ""; + const auto shell = node["shell"].isString() ? node["shell"].asString() : ""; int nb = node.size(); if (parentWorkspace != 0) nb = leafNodesInWorkspace(parentWorkspace); - return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), app_id, - app_class, shell}; + return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), + app_id, app_class, shell}; } } // iterate @@ -250,8 +248,8 @@ std::tuple return {0, -1, "", "", "", ""}; } -std::tuple Window::getFocusedNode( - const Json::Value& nodes, std::string& output) { +std::tuple +Window::getFocusedNode(const Json::Value& nodes, std::string& output) { Json::Value placeholder = 0; return gfnWithWorkspace(nodes, output, config_, bar_, placeholder); } From c287b0c82bbeef4e97cb75b54aaf28efb6a90a64 Mon Sep 17 00:00:00 2001 From: TheRealLorenz Date: Wed, 10 Aug 2022 22:24:48 +0200 Subject: [PATCH 07/17] Update manpage for sway/window --- man/waybar-sway-window.5.scd | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index e475cea..345e779 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -14,7 +14,7 @@ Addressed by *sway/window* *format*: ++ typeof: string ++ - default: {} ++ + default: {title} ++ The format, how information should be displayed. On {} data gets inserted. *rotate*: ++ @@ -80,6 +80,15 @@ Addressed by *sway/window* default: 24 ++ Option to change the size of the application icon. +# FORMAT REPLACEMENTS + +*{title}*: The title of the focused window. + +*{app_id}*: The app_id of the focused window. + +*{shell}*: The shell of the focused window. It's 'xwayland' when the window is +running through xwayland, otherwise it's 'xdg-shell'. + # REWRITE RULES *rewrite* is an object where keys are regular expressions and values are From 6f3fe6d33963e51f7241643e331e9f467bda5ecd Mon Sep 17 00:00:00 2001 From: Lorenzo Bellina <59364991+TheRealLorenz@users.noreply.github.com> Date: Thu, 11 Aug 2022 08:41:10 +0200 Subject: [PATCH 08/17] Update waybar-sway-window.5.scd --- man/waybar-sway-window.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/waybar-sway-window.5.scd b/man/waybar-sway-window.5.scd index 345e779..6e5ebdb 100644 --- a/man/waybar-sway-window.5.scd +++ b/man/waybar-sway-window.5.scd @@ -15,7 +15,7 @@ Addressed by *sway/window* *format*: ++ typeof: string ++ default: {title} ++ - The format, how information should be displayed. On {} data gets inserted. + The format, how information should be displayed. *rotate*: ++ typeof: integer ++ From c64058c94704b9435b93407f535050a242319e34 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Wed, 17 Aug 2022 21:54:23 +0200 Subject: [PATCH 09/17] stabilize window module --- include/modules/hyprland/window.hpp | 3 +++ src/modules/hyprland/window.cpp | 25 ++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index e21426a..e844655 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -14,9 +14,12 @@ public: Window(const std::string&, const waybar::Bar&, const Json::Value&); ~Window() = default; + auto update() -> void; + private: void onEvent(const std::string&); + std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; std::string lastView; diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 96d07bd..f0462a0 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -20,10 +20,24 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) gIPC->registerForIPC("activewindow", [&](const std::string& ev) { this->onEvent(ev); }); } +auto Window::update() -> void { + // fix ampersands + std::lock_guard lg(mutex_); + + if (!format_.empty()) { + label_.show(); + label_.set_markup(fmt::format(format_, lastView)); + } else { + label_.hide(); + } + + ALabel::update(); +} + void Window::onEvent(const std::string& ev) { + std::lock_guard lg(mutex_); auto windowName = ev.substr(ev.find_first_of(',') + 1).substr(0, 256); - // fix ampersands auto replaceAll = [](std::string str, const std::string& from, const std::string& to) -> std::string { size_t start_pos = 0; @@ -42,13 +56,6 @@ void Window::onEvent(const std::string& ev) { spdlog::debug("hyprland window onevent with {}", windowName); - if (!format_.empty()) { - label_.show(); - label_.set_markup(fmt::format(format_, windowName)); - } else { - label_.hide(); - } - - ALabel::update(); + dp.emit(); } } \ No newline at end of file From 123ed36739603339d256204642eb1776247f91e2 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Wed, 17 Aug 2022 21:58:33 +0200 Subject: [PATCH 10/17] remove workspaces module as its buggy and unnecessary --- include/factory.hpp | 1 - include/modules/hyprland/workspaces.hpp | 47 ----- meson.build | 1 - src/factory.cpp | 3 - src/modules/hyprland/workspaces.cpp | 243 ------------------------ 5 files changed, 295 deletions(-) delete mode 100644 include/modules/hyprland/workspaces.hpp delete mode 100644 src/modules/hyprland/workspaces.cpp diff --git a/include/factory.hpp b/include/factory.hpp index c24e654..47ef530 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -24,7 +24,6 @@ #ifdef HAVE_HYPRLAND #include "modules/hyprland/backend.hpp" #include "modules/hyprland/window.hpp" -#include "modules/hyprland/workspaces.hpp" #endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp deleted file mode 100644 index 93e9770..0000000 --- a/include/modules/hyprland/workspaces.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "AModule.hpp" -#include "bar.hpp" -#include "modules/hyprland/backend.hpp" -#include "util/json.hpp" -#include -#include -#include - -namespace waybar::modules::hyprland { - -class Workspaces : public AModule, public sigc::trackable { - public: - Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); - ~Workspaces() = default; - auto update() -> void; - - private: - void onEvent(const std::string&); - bool handleScroll(GdkEventScroll*); - void configOnLaunch(const Json::Value&); - void updateButtons(); - void parseInitHyprlandWorkspaces(); - Gtk::Button& addButton(const std::string&); - std::string getIcon(const std::string&); - std::deque getAllSortedWS(); - - bool isNumber(const std::string&); - - bool needReorder = false; - - Gtk::Box box_; - const Bar& bar_; - std::deque workspaces; - std::deque persistentWorkspaces; - std::unordered_map buttons_; - std::string focusedWorkspace; - - std::mutex mutex_; -}; - -} // namespace waybar::modules::sway diff --git a/meson.build b/meson.build index 39e9cdc..3c32006 100644 --- a/meson.build +++ b/meson.build @@ -205,7 +205,6 @@ if true add_project_arguments('-DHAVE_HYPRLAND', language: 'cpp') src_files += 'src/modules/hyprland/backend.cpp' src_files += 'src/modules/hyprland/window.cpp' - src_files += 'src/modules/hyprland/workspaces.cpp' endif if libnl.found() and libnlgen.found() diff --git a/src/factory.cpp b/src/factory.cpp index 2c6fe63..6df69d5 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -61,9 +61,6 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "hyprland/window") { return new waybar::modules::hyprland::Window(id, bar_, config_[name]); } - if (ref == "hyprland/workspaces") { - return new waybar::modules::hyprland::Workspaces(id, bar_, config_[name]); - } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp deleted file mode 100644 index 92dabc9..0000000 --- a/src/modules/hyprland/workspaces.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include "modules/hyprland/workspaces.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace waybar::modules::hyprland { - -Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) - : AModule(config, "workspaces", id, false, !config["disable-scroll"].asBool()), - bar_(bar), - box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { - box_.set_name("workspaces"); - if (!id.empty()) { - box_.get_style_context()->add_class(id); - } - - event_box_.add(box_); - - if (config["enable-bar-scroll"].asBool()) { - auto &window = const_cast(bar_).window; - window.add_events(Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); - window.signal_scroll_event().connect(sigc::mem_fun(*this, &Workspaces::handleScroll)); - } - - modulesReady = true; - - if (!gIPC.get()) { - gIPC = std::make_unique(); - } - - // register for hyprland ipc - gIPC->registerForIPC("createworkspace", [&](const std::string &ev) { this->onEvent(ev); }); - gIPC->registerForIPC("destroyworkspace", [&](const std::string &ev) { this->onEvent(ev); }); - gIPC->registerForIPC("activemon", [&](const std::string &ev) { this->onEvent(ev); }); - gIPC->registerForIPC("workspace", [&](const std::string &ev) { this->onEvent(ev); }); - - // parse cfg stuff - configOnLaunch(config); - - // parse workspaces already existing - parseInitHyprlandWorkspaces(); -} - -void Workspaces::parseInitHyprlandWorkspaces() { - std::istringstream WORKSPACES; - WORKSPACES.str(gIPC->getSocket1Reply("workspaces")); - - std::string line; - while (std::getline(WORKSPACES, line)) { - if (line.find("workspace ID") == 0) { - auto workspaceName = line.substr(line.find_first_of('(') + 1).substr(0, line.find_first_of(')') - line.find_first_of('(') - 1); - workspaces.emplace_back(workspaceName); - } - } -} - -void Workspaces::configOnLaunch(const Json::Value& cfg) { - if (cfg["persistent_workspaces"].isObject()) { - spdlog::info("persistent"); - const Json::Value &persistentWorkspacesJSON = cfg["persistent_workspaces"]; - - for (auto &wn : persistentWorkspacesJSON.getMemberNames()) { - persistentWorkspaces.emplace_back(wn); - spdlog::info("persistent ws {}", wn); - } - } -} - -std::deque Workspaces::getAllSortedWS() { - std::deque result; - for (auto& ws : workspaces) - result.emplace_back(ws); - for (auto& ws : persistentWorkspaces) - result.emplace_back(ws); - - std::sort(result.begin(), result.end(), [&](const std::string& ws1, const std::string& ws2) { - if (isNumber(ws1) && isNumber(ws2)) { - return std::stoi(ws1) < std::stoi(ws2); - } else if (isNumber(ws1)) { - return true; - } else { - return false; - } - }); - - return result; -} - -std::string Workspaces::getIcon(const std::string &name) { - return config_["format-icons"][name].asString(); -} - -auto Workspaces::update() -> void { - updateButtons(); -} - -void Workspaces::updateButtons() { - mutex_.lock(); - - auto ws = getAllSortedWS(); - - for (auto it = ws.begin(); it != ws.end(); ++it) { - auto bit = buttons_.find(*it); - - if (bit == buttons_.end()) - needReorder = true; - - auto &button = bit == buttons_.end() ? addButton(*it) : bit->second; - - if (focusedWorkspace == *it) { - button.get_style_context()->add_class("focused"); - } else { - button.get_style_context()->remove_class("focused"); - } - - std::string label = *it; - if (config_["format"].isString()) { - auto format = config_["format"].asString(); - label = fmt::format(format, fmt::arg("icon", getIcon(*it)), fmt::arg("name", *it)); - } - - if (needReorder) { - box_.reorder_child(button, it - workspaces.begin()); - } - - button.set_label(label); - - button.show(); - } - - AModule::update(); - - needReorder = false; - - mutex_.unlock(); -} - -bool Workspaces::isNumber(const std::string& str) { - for (auto &c : str) { - if (!(isdigit(c) != 0 || c == '-')) return false; - } - return true; -} - -Gtk::Button &Workspaces::addButton(const std::string& name) { - auto pair = buttons_.emplace(name, name); - auto &&button = pair.first->second; - box_.pack_start(button, false, false, 0); - button.set_name(name); - button.set_relief(Gtk::RELIEF_NONE); - if (!config_["disable-click"].asBool()) { - button.signal_pressed().connect([&, name]{ - if (isNumber(name)) { - gIPC->getSocket1Reply("dispatch workspace " + name); - spdlog::info("executing {}", "dispatch workspace " + name); - } - else { - gIPC->getSocket1Reply("dispatch workspace name:" + name); - spdlog::info("executing {}", "dispatch workspace name:" + name); - } - }); - } - - return button; -} - -void Workspaces::onEvent(const std::string& ev) { - const auto EVENT = ev.substr(0, ev.find_first_of('>')); - const auto WORKSPACE = ev.substr(ev.find_last_of('>') + 1); - - mutex_.lock(); - - if (EVENT == "activemon") { - focusedWorkspace = WORKSPACE.substr(WORKSPACE.find_first_of(',') + 1); - } else if (EVENT == "workspace") { - focusedWorkspace = WORKSPACE; - } else if (EVENT == "createworkspace") { - workspaces.emplace_back(WORKSPACE); - } else { - for (auto it = workspaces.begin(); it != workspaces.end(); it++) { - if (*it == WORKSPACE) { - workspaces.erase(it); - break; - } - } - - // also remove the button - for (auto it = buttons_.begin(); it != buttons_.end(); it++) { - if (it->second.get_name() == WORKSPACE) - it = buttons_.erase(it); - - if (it == buttons_.end()) - break; - } - - } - - mutex_.unlock(); - - dp.emit(); -} - -bool Workspaces::handleScroll(GdkEventScroll *e) { - if (gdk_event_get_pointer_emulated((GdkEvent *)e)) { - /** - * Ignore emulated scroll events on window - */ - return false; - } - auto dir = AModule::getScrollDir(e); - if (dir == SCROLL_DIR::NONE) { - return true; - } - - mutex_.lock(); - - if (dir == SCROLL_DIR::UP) { - gIPC->getSocket1Reply("dispatch workspace +1"); - } else if (dir == SCROLL_DIR::DOWN) { - gIPC->getSocket1Reply("dispatch workspace -1"); - } - - mutex_.unlock(); - - return true; -} -}; From e2e59a52df9b86f8a12d7729207c44ebc66a3d41 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Wed, 17 Aug 2022 22:03:49 +0200 Subject: [PATCH 11/17] make the linter happy --- src/modules/hyprland/backend.cpp | 5 ++--- src/modules/hyprland/window.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 88705d1..ae73a25 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -100,8 +100,7 @@ void IPC::parseIPC(const std::string& ev) { } } -void IPC::registerForIPC(const std::string& ev, - std::function fn) { +void IPC::registerForIPC(const std::string& ev, std::function fn) { callbackMutex.lock(); callbacks.emplace_back(std::make_pair(ev, fn)); @@ -169,4 +168,4 @@ std::string IPC::getSocket1Reply(const std::string& rq) { return std::string(buffer); } -} \ No newline at end of file +} // namespace waybar::modules::hyprland \ No newline at end of file diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index f0462a0..c292333 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -1,8 +1,9 @@ -#include "modules/hyprland/backend.hpp" #include "modules/hyprland/window.hpp" #include +#include "modules/hyprland/backend.hpp" + namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) @@ -58,4 +59,5 @@ void Window::onEvent(const std::string& ev) { dp.emit(); } -} \ No newline at end of file + +} // namespace waybar::modules::hyprland \ No newline at end of file From e0451816e24ad18e65dbceeaf37dc6be30fd6928 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 18 Aug 2022 15:29:59 +0300 Subject: [PATCH 12/17] init man documentation --- man/waybar-hyprland-window.5.scd | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 man/waybar-hyprland-window.5.scd diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd new file mode 100644 index 0000000..4be137d --- /dev/null +++ b/man/waybar-hyprland-window.5.scd @@ -0,0 +1,31 @@ +waybar-hyprland-window(5) + +# NAME + +waybar - hyprland window module + +# DESCRIPTION + +The *window* module displays the title of the currently focused window in Hyprland. + +# CONFIGURATION + +Addressed by *hyprland/window* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} the current window title is displayed. + + +# EXAMPLES + +``` +"hyprland/window": { + "format": "{}" +} +``` + +# STYLE + +- *#window* From 8dc78e4e40aad0a00583dcb6aad8b97c7f5248f9 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:54:20 +0200 Subject: [PATCH 13/17] Revert "init man documentation" --- include/factory.hpp | 5 -- include/modules/hypr/ipc.hpp | 22 -------- include/modules/hypr/window.hpp | 23 --------- man/waybar-hyprland-window.5.scd | 31 ------------ meson.build | 6 --- src/factory.cpp | 5 -- src/modules/hypr/ipc.cpp | 86 -------------------------------- src/modules/hypr/window.cpp | 27 ---------- 8 files changed, 205 deletions(-) delete mode 100644 include/modules/hypr/ipc.hpp delete mode 100644 include/modules/hypr/window.hpp delete mode 100644 man/waybar-hyprland-window.5.scd delete mode 100644 src/modules/hypr/ipc.cpp delete mode 100644 src/modules/hypr/window.cpp diff --git a/include/factory.hpp b/include/factory.hpp index dc93cfc..47ef530 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -1,7 +1,5 @@ #pragma once -#define HAVE_HYPR - #include #ifdef HAVE_LIBDATE #include "modules/clock.hpp" @@ -27,9 +25,6 @@ #include "modules/hyprland/backend.hpp" #include "modules/hyprland/window.hpp" #endif -#ifdef HAVE_HYPR -#include "modules/hypr/window.hpp" -#endif #if defined(__linux__) && !defined(NO_FILESYSTEM) #include "modules/battery.hpp" #endif diff --git a/include/modules/hypr/ipc.hpp b/include/modules/hypr/ipc.hpp deleted file mode 100644 index 0cf061a..0000000 --- a/include/modules/hypr/ipc.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "bar.hpp" -#include "client.hpp" - -namespace waybar::modules::hypr { - - std::string makeRequest(std::string); - -} // namespace waybar::modules::hypr diff --git a/include/modules/hypr/window.hpp b/include/modules/hypr/window.hpp deleted file mode 100644 index 83f8650..0000000 --- a/include/modules/hypr/window.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -#include -#include "ALabel.hpp" -#include "util/sleeper_thread.hpp" -#include "util/format.hpp" -#include "ipc.hpp" - -namespace waybar::modules::hypr { - -class Window : public ALabel { - public: - Window(const std::string&, const Json::Value&); - ~Window() = default; - auto update() -> void; - - private: - util::SleeperThread thread_; -}; - -} // namespace waybar::modules::hypr diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd deleted file mode 100644 index 4be137d..0000000 --- a/man/waybar-hyprland-window.5.scd +++ /dev/null @@ -1,31 +0,0 @@ -waybar-hyprland-window(5) - -# NAME - -waybar - hyprland window module - -# DESCRIPTION - -The *window* module displays the title of the currently focused window in Hyprland. - -# CONFIGURATION - -Addressed by *hyprland/window* - -*format*: ++ - typeof: string ++ - default: {} ++ - The format, how information should be displayed. On {} the current window title is displayed. - - -# EXAMPLES - -``` -"hyprland/window": { - "format": "{}" -} -``` - -# STYLE - -- *#window* diff --git a/meson.build b/meson.build index 47ee977..3c32006 100644 --- a/meson.build +++ b/meson.build @@ -187,12 +187,6 @@ src_files += [ 'src/modules/sway/workspaces.cpp' ] -add_project_arguments('-DHAVE_HYPR', language: 'cpp') -src_files += [ - 'src/modules/hypr/ipc.cpp', - 'src/modules/hypr/window.cpp', -] - if true add_project_arguments('-DHAVE_WLR', language: 'cpp') src_files += 'src/modules/wlr/taskbar.cpp' diff --git a/src/factory.cpp b/src/factory.cpp index 907f09a..6df69d5 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -61,11 +61,6 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref == "hyprland/window") { return new waybar::modules::hyprland::Window(id, bar_, config_[name]); } -#endif -#ifdef HAVE_HYPR - if (ref == "hypr/window") { - return new waybar::modules::hypr::Window(id, config_[name]); - } #endif if (ref == "idle_inhibitor") { return new waybar::modules::IdleInhibitor(id, bar_, config_[name]); diff --git a/src/modules/hypr/ipc.cpp b/src/modules/hypr/ipc.cpp deleted file mode 100644 index 40488ec..0000000 --- a/src/modules/hypr/ipc.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "modules/hypr/ipc.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util/string.hpp" - -std::string waybar::modules::hypr::makeRequest(std::string req) { - const auto SERVERSOCKET = socket(AF_INET, SOCK_STREAM, 0); - - if (SERVERSOCKET < 0) { - spdlog::error("[Hypr IPC] Couldn't open a socket."); - return ""; - } - - const auto SERVER = gethostbyname("localhost"); - - if (!SERVER) { - spdlog::error("[Hypr IPC] Couldn't get localhost."); - return ""; - } - - sockaddr_in serverAddress = {0}; - serverAddress.sin_family = AF_INET; - bcopy((char*)SERVER->h_addr, (char*)&serverAddress.sin_addr.s_addr, SERVER->h_length); - - std::ifstream socketPortStream; - socketPortStream.open("/tmp/hypr/.socket"); - - if (!socketPortStream.good()) { - spdlog::error("[Hypr IPC] No socket file. Is Hyprland running?"); - return ""; - } - - std::string port = ""; - std::getline(socketPortStream, port); - socketPortStream.close(); - - int portInt = 0; - try { - portInt = std::stoi(port.c_str()); - } catch (...) { - spdlog::error("[Hypr IPC] Port not an int?!"); - return ""; - } - - if (portInt == 0) { - spdlog::error("[Hypr IPC] Port 0. Aborting."); - return ""; - } - - serverAddress.sin_port = portInt; - - if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { - spdlog::error("[Hypr IPC] Couldn't connect to port {} , is Hyprland running?", port); - return ""; - } - - auto sizeWritten = write(SERVERSOCKET, req.c_str(), req.length()); - - if (sizeWritten < 0) { - spdlog::error("[Hypr IPC] Couldn't write to the socket."); - return ""; - } - - char buffer[8192] = {0}; - - sizeWritten = read(SERVERSOCKET, buffer, 8192); - - if (sizeWritten < 0) { - spdlog::error("[Hypr IPC] Couldn't cread from the socket."); - return ""; - } - - close(SERVERSOCKET); - - return std::string(buffer); -} \ No newline at end of file diff --git a/src/modules/hypr/window.cpp b/src/modules/hypr/window.cpp deleted file mode 100644 index ca224fc..0000000 --- a/src/modules/hypr/window.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "modules/hypr/window.hpp" -#include "modules/hypr/ipc.hpp" - -using namespace waybar::util; - -waybar::modules::hypr::Window::Window(const std::string& id, const Json::Value& config) : ALabel(config, "window", id, "{window}", 0.5f) { - thread_ = [this] { - dp.emit(); - thread_.sleep_for(interval_); - }; -} - -auto waybar::modules::hypr::Window::update() -> void { - auto format = format_; - - std::string windowName = waybar::modules::hypr::makeRequest("activewindow"); - - if (windowName != "") - windowName = windowName.substr(windowName.find_first_of('>') + 2, windowName.find_first_of('\n') - windowName.find_first_of('>') - 3); - - event_box_.show(); - label_.set_markup(fmt::format(format, - fmt::arg("window", windowName))); - - // Call parent update - ALabel::update(); -} From 112d481ae7cdf828ec82fade4bc3c2456ecba649 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 18 Aug 2022 15:59:00 +0300 Subject: [PATCH 14/17] Init man documentation --- man/waybar-hyprland-window.5.scd | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 man/waybar-hyprland-window.5.scd diff --git a/man/waybar-hyprland-window.5.scd b/man/waybar-hyprland-window.5.scd new file mode 100644 index 0000000..4be137d --- /dev/null +++ b/man/waybar-hyprland-window.5.scd @@ -0,0 +1,31 @@ +waybar-hyprland-window(5) + +# NAME + +waybar - hyprland window module + +# DESCRIPTION + +The *window* module displays the title of the currently focused window in Hyprland. + +# CONFIGURATION + +Addressed by *hyprland/window* + +*format*: ++ + typeof: string ++ + default: {} ++ + The format, how information should be displayed. On {} the current window title is displayed. + + +# EXAMPLES + +``` +"hyprland/window": { + "format": "{}" +} +``` + +# STYLE + +- *#window* From ee504b826d88f570fc59ae7c8d211ca668b19a41 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Aug 2022 15:16:28 +0200 Subject: [PATCH 15/17] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fefde2c..addee57 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ #### Current features - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) +- Hyprland (Focused window name) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time - Battery From b7bd06ad8fdc0ad1ce00aa1a44b24bc94b8e4530 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Aug 2022 15:21:50 +0200 Subject: [PATCH 16/17] Update window.cpp --- src/modules/sway/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index ebbfa4b..1d7fbe9 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -15,7 +15,7 @@ namespace waybar::modules::sway { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) { + : AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), windowId_(-1) { // Icon size if (config_["icon-size"].isUInt()) { app_icon_size_ = config["icon-size"].asUInt(); From bcee4e15d3e4d970d477ca3ad1cc7b1b4c691345 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Aug 2022 15:22:25 +0200 Subject: [PATCH 17/17] fix: lint files --- src/modules/mpd/mpd.cpp | 23 +++++++++++------------ src/modules/sni/item.cpp | 5 ++--- src/modules/sway/window.cpp | 4 ++-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/modules/mpd/mpd.cpp b/src/modules/mpd/mpd.cpp index 9b878f9..0f58343 100644 --- a/src/modules/mpd/mpd.cpp +++ b/src/modules/mpd/mpd.cpp @@ -166,18 +166,17 @@ void waybar::modules::MPD::setLabel() { if (config_["title-len"].isInt()) title = title.substr(0, config_["title-len"].asInt()); try { - label_.set_markup( - fmt::format(format, fmt::arg("artist", Glib::Markup::escape_text(artist).raw()), - fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist).raw()), - fmt::arg("album", Glib::Markup::escape_text(album).raw()), - fmt::arg("title", Glib::Markup::escape_text(title).raw()), - fmt::arg("date", Glib::Markup::escape_text(date).raw()), - fmt::arg("volume", volume), fmt::arg("elapsedTime", elapsedTime), - fmt::arg("totalTime", totalTime), fmt::arg("songPosition", song_pos), - fmt::arg("queueLength", queue_length), fmt::arg("stateIcon", stateIcon), - fmt::arg("consumeIcon", consumeIcon), fmt::arg("randomIcon", randomIcon), - fmt::arg("repeatIcon", repeatIcon), fmt::arg("singleIcon", singleIcon), - fmt::arg("filename", filename))); + label_.set_markup(fmt::format( + format, fmt::arg("artist", Glib::Markup::escape_text(artist).raw()), + fmt::arg("albumArtist", Glib::Markup::escape_text(album_artist).raw()), + fmt::arg("album", Glib::Markup::escape_text(album).raw()), + fmt::arg("title", Glib::Markup::escape_text(title).raw()), + fmt::arg("date", Glib::Markup::escape_text(date).raw()), fmt::arg("volume", volume), + fmt::arg("elapsedTime", elapsedTime), fmt::arg("totalTime", totalTime), + fmt::arg("songPosition", song_pos), fmt::arg("queueLength", queue_length), + fmt::arg("stateIcon", stateIcon), fmt::arg("consumeIcon", consumeIcon), + fmt::arg("randomIcon", randomIcon), fmt::arg("repeatIcon", repeatIcon), + fmt::arg("singleIcon", singleIcon), fmt::arg("filename", filename))); } catch (fmt::format_error const& e) { spdlog::warn("mpd: format error: {}", e.what()); } diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 773cfda..7cd0045 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -314,9 +314,7 @@ void Item::updateImage() { } Glib::RefPtr Item::getIconPixbuf() { - if (!icon_name.empty()) { - try { std::ifstream temp(icon_name); if (temp.is_open()) { @@ -347,7 +345,8 @@ Glib::RefPtr Item::getIconPixbuf() { if (icon_name.empty()) { spdlog::error("Item '{}': No icon name or pixmap given.", id); } else { - spdlog::error("Item '{}': Could not find an icon named '{}' and no pixmap given.", id, icon_name); + spdlog::error("Item '{}': Could not find an icon named '{}' and no pixmap given.", id, + icon_name); } return getIconByName("image-missing", getScaledIconSize()); diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 1d7fbe9..3d63743 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -227,8 +227,8 @@ std::tuple int nb = node.size(); if (parentWorkspace != 0) nb = leafNodesInWorkspace(parentWorkspace); - return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), - app_id, app_class, shell}; + return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()), + app_id, app_class, shell}; } } // iterate