From bb1cf7570e3c99168889eccee61e5f922d192f34 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 19 Apr 2019 11:09:06 +0200 Subject: [PATCH] refactor(IPC): use sigc signal --- include/modules/sway/ipc/client.hpp | 10 +++-- include/modules/sway/mode.hpp | 1 + include/modules/sway/window.hpp | 2 + include/modules/sway/workspaces.hpp | 1 + src/modules/network.cpp | 4 +- src/modules/sway/ipc/client.cpp | 61 ++++++++++++++++--------- src/modules/sway/mode.cpp | 32 ++++++++----- src/modules/sway/window.cpp | 69 +++++++++++++++++------------ src/modules/sway/workspaces.cpp | 51 +++++++++++---------- 9 files changed, 140 insertions(+), 91 deletions(-) diff --git a/include/modules/sway/ipc/client.hpp b/include/modules/sway/ipc/client.hpp index ba862ff7..3b456249 100644 --- a/include/modules/sway/ipc/client.hpp +++ b/include/modules/sway/ipc/client.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -20,9 +21,12 @@ class Ipc { std::string payload; }; - struct ipc_response sendCmd(uint32_t type, const std::string &payload = "") const; - void subscribe(const std::string &payload) const; - struct ipc_response handleEvent() const; + sigc::signal signal_event; + sigc::signal signal_cmd; + + void sendCmd(uint32_t type, const std::string &payload = "") const; + void subscribe(const std::string &payload) const; + void handleEvent() const; protected: static inline const std::string ipc_magic_ = "i3-ipc"; diff --git a/include/modules/sway/mode.hpp b/include/modules/sway/mode.hpp index 6c0d9233..89c9a8b3 100644 --- a/include/modules/sway/mode.hpp +++ b/include/modules/sway/mode.hpp @@ -17,6 +17,7 @@ class Mode : public ALabel { auto update() -> void; private: + void onEvent(const struct Ipc::ipc_response); void worker(); const Bar& bar_; diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index b5e2bd76..aaeb2d38 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -18,6 +18,8 @@ class Window : public ALabel { auto update() -> void; private: + void onEvent(const struct Ipc::ipc_response); + void onCmd(const struct Ipc::ipc_response); void worker(); std::tuple getFocusedNode(Json::Value nodes); void getFocusedWindow(); diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index 4e3173ce..b57b5e49 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -20,6 +20,7 @@ class Workspaces : public IModule { operator Gtk::Widget&(); private: + void onCmd(const struct Ipc::ipc_response); void worker(); void addWorkspace(const Json::Value&); void onButtonReady(const Json::Value&, Gtk::Button&); diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 713cc1c5..5ba364db 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -114,9 +114,9 @@ void waybar::modules::Network::worker() { } thread_timer_.sleep_for(interval_); }; - struct epoll_event events[EPOLL_MAX] = {{0}}; + std::array events; thread_ = [this, &events] { - int ec = epoll_wait(efd_, events, EPOLL_MAX, -1); + int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); if (ec > 0) { for (auto i = 0; i < ec; i++) { if (events[i].data.fd == nl_socket_get_fd(info_sock_)) { diff --git a/src/modules/sway/ipc/client.cpp b/src/modules/sway/ipc/client.cpp index fe794a44..de19020f 100644 --- a/src/modules/sway/ipc/client.cpp +++ b/src/modules/sway/ipc/client.cpp @@ -1,21 +1,29 @@ #include "modules/sway/ipc/client.hpp" +#include -waybar::modules::sway::Ipc::Ipc() { +namespace waybar::modules::sway { + +Ipc::Ipc() { const std::string& socketPath = getSocketPath(); fd_ = open(socketPath); fd_event_ = open(socketPath); } -waybar::modules::sway::Ipc::~Ipc() { +Ipc::~Ipc() { // To fail the IPC header write(fd_, "close-sway-ipc", 14); write(fd_event_, "close-sway-ipc", 14); - - close(fd_); - close(fd_event_); + if (fd_ > 0) { + close(fd_); + fd_ = -1; + } + if (fd_event_ > 0) { + close(fd_event_); + fd_event_ = -1; + } } -const std::string waybar::modules::sway::Ipc::getSocketPath() const { +const std::string Ipc::getSocketPath() const { const char* env = getenv("SWAYSOCK"); if (env != nullptr) { return std::string(env); @@ -33,6 +41,9 @@ const std::string waybar::modules::sway::Ipc::getSocketPath() const { } pclose(in); str = str_buf; + if (str.empty()) { + throw std::runtime_error("Socket path is empty"); + } } if (str.back() == '\n') { str.pop_back(); @@ -40,12 +51,14 @@ const std::string waybar::modules::sway::Ipc::getSocketPath() const { return str; } -int waybar::modules::sway::Ipc::open(const std::string& socketPath) const { - struct sockaddr_un addr = {0}; - int fd = -1; - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { +int Ipc::open(const std::string& socketPath) const { + int32_t fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { throw std::runtime_error("Unable to open Unix socket"); } + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + struct sockaddr_un addr; + memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; @@ -56,7 +69,7 @@ int waybar::modules::sway::Ipc::open(const std::string& socketPath) const { return fd; } -struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::recv(int fd) const { +struct Ipc::ipc_response Ipc::recv(int fd) const { std::string header; header.resize(ipc_header_size_); auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); @@ -65,6 +78,10 @@ struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::recv while (total < ipc_header_size_) { auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0); if (res <= 0) { + if (res <= 0 && (fd_event_ == -1 || fd_ == -1)) { + // IPC is closed so just return empty response + return {0, 0, ""}; + } throw std::runtime_error("Unable to receive IPC header"); } total += res; @@ -88,8 +105,7 @@ struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::recv return {data32[0], data32[1], &payload.front()}; } -struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::send( - int fd, uint32_t type, const std::string& payload) const { +struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) const { std::string header; header.resize(ipc_header_size_); auto data32 = reinterpret_cast(header.data() + ipc_magic_.size()); @@ -103,21 +119,24 @@ struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::send if (::send(fd, payload.c_str(), payload.size(), 0) == -1) { throw std::runtime_error("Unable to send IPC payload"); } - return recv(fd); + return Ipc::recv(fd); } -struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::sendCmd( - uint32_t type, const std::string& payload) const { - return send(fd_, type, payload); +void Ipc::sendCmd(uint32_t type, const std::string& payload) const { + const auto res = Ipc::send(fd_, type, payload); + signal_cmd.emit(res); } -void waybar::modules::sway::Ipc::subscribe(const std::string& payload) const { - auto res = send(fd_event_, IPC_SUBSCRIBE, payload); +void Ipc::subscribe(const std::string& payload) const { + auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload); if (res.payload != "{\"success\": true}") { throw std::runtime_error("Unable to subscribe ipc event"); } } -struct waybar::modules::sway::Ipc::ipc_response waybar::modules::sway::Ipc::handleEvent() const { - return recv(fd_event_); +void Ipc::handleEvent() const { + const auto res = Ipc::recv(fd_event_); + signal_event.emit(res); } + +} // namespace waybar::modules::sway \ No newline at end of file diff --git a/src/modules/sway/mode.cpp b/src/modules/sway/mode.cpp index b797017f..f43f9290 100644 --- a/src/modules/sway/mode.cpp +++ b/src/modules/sway/mode.cpp @@ -1,35 +1,41 @@ #include "modules/sway/mode.hpp" -waybar::modules::sway::Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config) +namespace waybar::modules::sway { + +Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "{}"), bar_(bar) { label_.set_name("mode"); if (!id.empty()) { label_.get_style_context()->add_class(id); } ipc_.subscribe("[ \"mode\" ]"); + ipc_.signal_event.connect(sigc::mem_fun(*this, &Mode::onEvent)); // Launch worker worker(); dp.emit(); } -void waybar::modules::sway::Mode::worker() { +void Mode::onEvent(const struct Ipc::ipc_response res) { + auto parsed = parser_.parse(res.payload); + if (parsed["change"] != "default") { + mode_ = parsed["change"].asString(); + } else { + mode_.clear(); + } + dp.emit(); +} + +void Mode::worker() { thread_ = [this] { try { - auto res = ipc_.handleEvent(); - auto parsed = parser_.parse(res.payload); - if (parsed["change"] != "default") { - mode_ = parsed["change"].asString(); - } else { - mode_.clear(); - } - dp.emit(); + ipc_.handleEvent(); } catch (const std::exception& e) { std::cerr << "Mode: " << e.what() << std::endl; } }; } -auto waybar::modules::sway::Mode::update() -> void { +auto Mode::update() -> void { if (mode_.empty()) { event_box_.hide(); } else { @@ -39,4 +45,6 @@ auto waybar::modules::sway::Mode::update() -> void { } event_box_.show(); } -} \ No newline at end of file +} + +} // namespace waybar::modules::sway \ No newline at end of file diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 0a3f3a9d..7173fec5 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -1,7 +1,8 @@ #include "modules/sway/window.hpp" -waybar::modules::sway::Window::Window(const std::string& id, const Bar& bar, - const Json::Value& config) +namespace waybar::modules::sway { + +Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "{}"), bar_(bar), windowId_(-1) { label_.set_name("window"); if (!id.empty()) { @@ -12,45 +13,58 @@ waybar::modules::sway::Window::Window(const std::string& id, const Bar& bar, label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); } ipc_.subscribe("[\"window\",\"workspace\"]"); + ipc_.signal_event.connect(sigc::mem_fun(*this, &Window::onEvent)); + ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Window::onCmd)); getFocusedWindow(); // Launch worker worker(); } -void waybar::modules::sway::Window::worker() { +void Window::onEvent(const struct Ipc::ipc_response res) { + auto parsed = parser_.parse(res.payload); + // Check for waybar prevents flicker when hovering window module + if ((parsed["change"] == "focus" || parsed["change"] == "title") && + parsed["container"]["focused"].asBool() && + parsed["container"]["name"].asString() != "waybar") { + window_ = Glib::Markup::escape_text(parsed["container"]["name"].asString()); + windowId_ = parsed["container"]["id"].asInt(); + dp.emit(); + } else if ((parsed["change"] == "close" && parsed["container"]["focused"].asBool() && + windowId_ == parsed["container"]["id"].asInt()) || + (parsed["change"] == "focus" && parsed["current"]["focus"].isArray() && + parsed["current"]["focus"].empty())) { + window_.clear(); + windowId_ = -1; + dp.emit(); + } +} + +void Window::onCmd(const struct Ipc::ipc_response res) { + auto parsed = parser_.parse(res.payload); + auto [id, name] = getFocusedNode(parsed["nodes"]); + windowId_ = id; + window_ = name; + dp.emit(); +} + +void Window::worker() { thread_ = [this] { try { - auto res = ipc_.handleEvent(); - auto parsed = parser_.parse(res.payload); - // Check for waybar prevents flicker when hovering window module - if ((parsed["change"] == "focus" || parsed["change"] == "title") && - parsed["container"]["focused"].asBool() && - parsed["container"]["name"].asString() != "waybar") { - window_ = Glib::Markup::escape_text(parsed["container"]["name"].asString()); - windowId_ = parsed["container"]["id"].asInt(); - dp.emit(); - } else if ((parsed["change"] == "close" && parsed["container"]["focused"].asBool() && - windowId_ == parsed["container"]["id"].asInt()) || - (parsed["change"] == "focus" && parsed["current"]["focus"].isArray() && - parsed["current"]["focus"].empty())) { - window_.clear(); - windowId_ = -1; - dp.emit(); - } + ipc_.handleEvent(); } catch (const std::exception& e) { std::cerr << "Window: " << e.what() << std::endl; } }; } -auto waybar::modules::sway::Window::update() -> void { +auto Window::update() -> void { label_.set_markup(fmt::format(format_, window_)); if (tooltipEnabled()) { label_.set_tooltip_text(window_); } } -std::tuple waybar::modules::sway::Window::getFocusedNode(Json::Value nodes) { +std::tuple Window::getFocusedNode(Json::Value nodes) { for (auto const& node : nodes) { if (node["focused"].asBool() && node["type"] == "con") { return {node["id"].asInt(), node["name"].asString()}; @@ -63,15 +77,12 @@ std::tuple waybar::modules::sway::Window::getFocusedNode(Json: return {-1, std::string()}; } -void waybar::modules::sway::Window::getFocusedWindow() { +void Window::getFocusedWindow() { try { - auto res = ipc_.sendCmd(IPC_GET_TREE); - auto parsed = parser_.parse(res.payload); - auto [id, name] = getFocusedNode(parsed["nodes"]); - windowId_ = id; - window_ = name; - Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Window::update)); + ipc_.sendCmd(IPC_GET_TREE); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } } + +} // namespace waybar::modules::sway \ No newline at end of file diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index bd2635c3..c303488d 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -1,7 +1,8 @@ #include "modules/sway/workspaces.hpp" -waybar::modules::sway::Workspaces::Workspaces(const std::string &id, const Bar &bar, - const Json::Value &config) +namespace waybar::modules::sway { + +Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : bar_(bar), config_(config), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), @@ -11,31 +12,35 @@ waybar::modules::sway::Workspaces::Workspaces(const std::string &id, const Bar & box_.get_style_context()->add_class(id); } ipc_.subscribe("[ \"workspace\" ]"); + ipc_.signal_cmd.connect(sigc::mem_fun(*this, &Workspaces::onCmd)); // Launch worker worker(); } -void waybar::modules::sway::Workspaces::worker() { +void Workspaces::onCmd(const struct Ipc::ipc_response res) { + if (thread_.isRunning()) { + std::lock_guard lock(mutex_); + workspaces_ = parser_.parse(res.payload); + dp.emit(); + } +} + +void Workspaces::worker() { thread_ = [this] { try { if (!workspaces_.empty()) { ipc_.handleEvent(); } - { - std::lock_guard lock(mutex_); - auto res = ipc_.sendCmd(IPC_GET_WORKSPACES); - if (thread_.isRunning()) { - workspaces_ = parser_.parse(res.payload); - } + if (thread_.isRunning()) { + ipc_.sendCmd(IPC_GET_WORKSPACES); } - dp.emit(); } catch (const std::exception &e) { std::cerr << "Workspaces: " << e.what() << std::endl; } }; } -auto waybar::modules::sway::Workspaces::update() -> void { +auto Workspaces::update() -> void { bool needReorder = false; std::lock_guard lock(mutex_); for (auto it = buttons_.begin(); it != buttons_.end();) { @@ -100,7 +105,7 @@ auto waybar::modules::sway::Workspaces::update() -> void { } } -void waybar::modules::sway::Workspaces::addWorkspace(const Json::Value &node) { +void Workspaces::addWorkspace(const Json::Value &node) { auto icon = getIcon(node["name"].asString(), node); auto format = config_["format"].isString() ? fmt::format(config_["format"].asString(), @@ -117,8 +122,7 @@ void waybar::modules::sway::Workspaces::addWorkspace(const Json::Value &node) { button.set_relief(Gtk::RELIEF_NONE); button.signal_clicked().connect([this, pair] { try { - std::lock_guard lock(mutex_); - auto cmd = fmt::format("workspace \"{}\"", pair.first->first); + auto cmd = fmt::format("workspace \"{}\"", pair.first->first); ipc_.sendCmd(IPC_COMMAND, cmd); } catch (const std::exception &e) { std::cerr << e.what() << std::endl; @@ -142,8 +146,7 @@ void waybar::modules::sway::Workspaces::addWorkspace(const Json::Value &node) { onButtonReady(node, button); } -std::string waybar::modules::sway::Workspaces::getIcon(const std::string &name, - const Json::Value &node) { +std::string Workspaces::getIcon(const std::string &name, const Json::Value &node) { std::vector keys = {name, "urgent", "focused", "visible", "default"}; for (auto const &key : keys) { if (key == "focused" || key == "visible" || key == "urgent") { @@ -157,7 +160,7 @@ std::string waybar::modules::sway::Workspaces::getIcon(const std::string &name, return name; } -bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e) { +bool Workspaces::handleScroll(GdkEventScroll *e) { // Avoid concurrent scroll event if (scrolling_) { return false; @@ -199,8 +202,7 @@ bool waybar::modules::sway::Workspaces::handleScroll(GdkEventScroll *e) { return true; } -const std::string waybar::modules::sway::Workspaces::getCycleWorkspace(uint8_t focused_workspace, - bool prev) const { +const std::string Workspaces::getCycleWorkspace(uint8_t focused_workspace, bool prev) const { auto inc = prev ? -1 : 1; int size = workspaces_.size(); uint8_t idx = 0; @@ -225,7 +227,7 @@ const std::string waybar::modules::sway::Workspaces::getCycleWorkspace(uint8_t f return ""; } -uint16_t waybar::modules::sway::Workspaces::getWorkspaceIndex(const std::string &name) const { +uint16_t Workspaces::getWorkspaceIndex(const std::string &name) const { uint16_t idx = 0; for (const auto &workspace : workspaces_) { if (workspace["name"].asString() == name) { @@ -239,7 +241,7 @@ uint16_t waybar::modules::sway::Workspaces::getWorkspaceIndex(const std::string return workspaces_.size(); } -std::string waybar::modules::sway::Workspaces::trimWorkspaceName(std::string name) { +std::string Workspaces::trimWorkspaceName(std::string name) { std::size_t found = name.find(":"); if (found != std::string::npos) { return name.substr(found + 1); @@ -247,8 +249,7 @@ std::string waybar::modules::sway::Workspaces::trimWorkspaceName(std::string nam return name; } -void waybar::modules::sway::Workspaces::onButtonReady(const Json::Value &node, - Gtk::Button & button) { +void Workspaces::onButtonReady(const Json::Value &node, Gtk::Button &button) { if (config_["current-only"].asBool()) { if (node["focused"].asBool()) { button.show(); @@ -260,4 +261,6 @@ void waybar::modules::sway::Workspaces::onButtonReady(const Json::Value &node, } } -waybar::modules::sway::Workspaces::operator Gtk::Widget &() { return box_; } +Workspaces::operator Gtk::Widget &() { return box_; } + +} // namespace waybar::modules::sway \ No newline at end of file