refactor(workspaces): listen ipc event

This commit is contained in:
Alexis 2018-08-13 21:23:43 +02:00
parent 68f9ea3065
commit ea9a08d473
5 changed files with 146 additions and 116 deletions

View File

@ -4,6 +4,7 @@
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "util/chrono.hpp" #include "util/chrono.hpp"
#include "util/json.hpp"
#include "IModule.hpp" #include "IModule.hpp"
namespace waybar::modules { namespace waybar::modules {
@ -15,13 +16,16 @@ namespace waybar::modules {
operator Gtk::Widget &(); operator Gtk::Widget &();
private: private:
void _addWorkspace(Json::Value node); void _addWorkspace(Json::Value node);
Json::Value _getWorkspaces(); Json::Value _getWorkspaces(const std::string data);
Bar &_bar; Bar &_bar;
waybar::util::SleeperThread _thread; waybar::util::SleeperThread _thread;
Gtk::Box _box; Gtk::Box _box;
util::JsonParser _parser;
std::mutex _mutex;
std::unordered_map<int, Gtk::Button> _buttons; std::unordered_map<int, Gtk::Button> _buttons;
int _ipcSocketfd; Json::Value _workspaces;
int _ipcEventSocketfd; int _ipcfd;
int _ipcEventfd;
}; };
} }

34
include/util/json.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <json/json.h>
namespace waybar::util {
struct JsonParser {
JsonParser()
: _reader(_builder.newCharReader())
{}
Json::Value parse(const std::string data)
{
Json::Value root;
std::string err;
bool res =
_reader->parse(data.c_str(), data.c_str() + data.size(), &root, &err);
if (!res)
throw std::runtime_error(err);
return root;
}
~JsonParser()
{
delete _reader;
}
private:
Json::CharReaderBuilder _builder;
Json::CharReader *_reader;
};
}

View File

@ -1,6 +1,7 @@
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "factory.hpp" #include "factory.hpp"
#include "util/json.hpp"
waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output) waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output)
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL}, : client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
@ -125,19 +126,13 @@ auto waybar::Bar::toggle() -> void
auto waybar::Bar::_setupConfig() -> void auto waybar::Bar::_setupConfig() -> void
{ {
Json::Value root; util::JsonParser parser;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
std::string err;
std::ifstream file(client.configFile); std::ifstream file(client.configFile);
if (!file.is_open()) if (!file.is_open())
throw std::runtime_error("Can't open config file"); throw std::runtime_error("Can't open config file");
std::string str((std::istreambuf_iterator<char>(file)), std::string str((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
bool res = reader->parse(str.c_str(), str.c_str() + str.size(), &_config, &err); _config = parser.parse(str);
delete reader;
if (!res)
throw std::runtime_error(err);
} }
auto waybar::Bar::_setupCss() -> void auto waybar::Bar::_setupCss() -> void

View File

@ -11,90 +11,85 @@ static const size_t ipc_header_size = sizeof(ipc_magic)+8;
std::string get_socketpath(void) { std::string get_socketpath(void) {
const char *env = getenv("SWAYSOCK"); const char *env = getenv("SWAYSOCK");
if (env) return std::string(env); if (env) return std::string(env);
std::string str; std::string str;
{ {
std::string str_buf; std::string str_buf;
FILE* in; FILE* in;
char buf[512] = { 0 }; char buf[512] = { 0 };
if (!(in = popen("sway --get-socketpath 2>/dev/null", "r"))) { if (!(in = popen("sway --get-socketpath 2>/dev/null", "r"))) {
throw std::runtime_error("Failed to get socket path"); throw std::runtime_error("Failed to get socket path");
} }
while (fgets(buf, sizeof(buf), in) != nullptr) { while (fgets(buf, sizeof(buf), in) != nullptr) {
str_buf.append(buf, sizeof(buf)); str_buf.append(buf, sizeof(buf));
} }
pclose(in); pclose(in);
str = str_buf; str = str_buf;
} }
if (str.back() == '\n') { if (str.back() == '\n') {
str.pop_back(); str.pop_back();
} }
return str; return str;
} }
int ipc_open_socket(std::string socket_path) { int ipc_open_socket(std::string socket_path) {
struct sockaddr_un addr; struct sockaddr_un addr;
int socketfd; int socketfd;
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket"); throw std::runtime_error("Unable to open Unix socket");
} }
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1); strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1] = 0; addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
int l = sizeof(struct sockaddr_un); int l = sizeof(struct sockaddr_un);
if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) { if (connect(socketfd, (struct sockaddr *)&addr, l) == -1) {
throw std::runtime_error("Unable to connect to " + socket_path); throw std::runtime_error("Unable to connect to " + socket_path);
} }
return socketfd; return socketfd;
} }
struct ipc_response ipc_recv_response(int socketfd) { struct ipc_response ipc_recv_response(int socketfd) {
char data[ipc_header_size]; struct ipc_response response;
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
size_t total = 0;
size_t total = 0; while (total < ipc_header_size) {
while (total < ipc_header_size) { ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0); if (received <= 0) {
if (received <= 0) { throw std::runtime_error("Unable to receive IPC response");
throw std::runtime_error("Unable to receive IPC response"); }
} total += received;
total += received; }
}
struct ipc_response response; total = 0;
response.size = data32[0];
total = 0; response.type = data32[1];
response.size = data32[0];
response.type = data32[1];
char payload[response.size + 1]; char payload[response.size + 1];
while (total < response.size) { while (total < response.size) {
ssize_t received = recv(socketfd, payload + total, response.size - total, 0); ssize_t received = recv(socketfd, payload + total, response.size - total, 0);
if (received < 0) { if (received < 0) {
throw std::runtime_error("Unable to receive IPC response"); throw std::runtime_error("Unable to receive IPC response");
} }
total += received; total += received;
} }
payload[response.size] = '\0'; payload[response.size] = '\0';
response.payload = std::string(payload); response.payload = std::string(payload);
return response; return response;
} }
std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) { std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) {
char data[ipc_header_size]; char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
memcpy(data, ipc_magic, sizeof(ipc_magic)); memcpy(data, ipc_magic, sizeof(ipc_magic));
data32[0] = *len; data32[0] = *len;
data32[1] = type; data32[1] = type;
if (send(socketfd, data, ipc_header_size, 0) == -1) { if (send(socketfd, data, ipc_header_size, 0) == -1)
throw std::runtime_error("Unable to send IPC header"); throw std::runtime_error("Unable to send IPC header");
} if (send(socketfd, payload, *len, 0) == -1)
throw std::runtime_error("Unable to send IPC payload");
if (send(socketfd, payload, *len, 0) == -1) { struct ipc_response resp = ipc_recv_response(socketfd);
throw std::runtime_error("Unable to send IPC payload"); *len = resp.size;
} return resp.payload;
struct ipc_response resp = ipc_recv_response(socketfd);
*len = resp.size;
return resp.payload;
} }

View File

@ -6,47 +6,60 @@ waybar::modules::Workspaces::Workspaces(Bar &bar)
{ {
_box.get_style_context()->add_class("workspaces"); _box.get_style_context()->add_class("workspaces");
std::string socketPath = get_socketpath(); std::string socketPath = get_socketpath();
_ipcSocketfd = ipc_open_socket(socketPath); _ipcfd = ipc_open_socket(socketPath);
_ipcEventSocketfd = ipc_open_socket(socketPath); _ipcEventfd = ipc_open_socket(socketPath);
const char *subscribe = "[ \"workspace\", \"mode\" ]"; const char *subscribe = "[ \"workspace\" ]";
uint32_t len = strlen(subscribe); uint32_t len = strlen(subscribe);
ipc_single_command(_ipcEventSocketfd, IPC_SUBSCRIBE, subscribe, &len); ipc_single_command(_ipcEventfd, IPC_SUBSCRIBE, subscribe, &len);
_thread = [this] { _thread = [this] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Workspaces::update)); try {
_thread.sleep_for(chrono::milliseconds(250)); if (_bar.outputName.empty()) {
// Wait for the name of the output
while (_bar.outputName.empty())
_thread.sleep_for(chrono::milliseconds(150));
} else
ipc_recv_response(_ipcEventfd);
uint32_t len = 0;
auto str = ipc_single_command(_ipcfd, IPC_GET_WORKSPACES, nullptr, &len);
std::lock_guard<std::mutex> lock(_mutex);
_workspaces = _getWorkspaces(str);
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Workspaces::update));
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}; };
} }
auto waybar::modules::Workspaces::update() -> void auto waybar::modules::Workspaces::update() -> void
{ {
if (_bar.outputName.empty()) return; std::lock_guard<std::mutex> lock(_mutex);
Json::Value workspaces = _getWorkspaces();
bool needReorder = false; bool needReorder = false;
for (auto it = _buttons.begin(); it != _buttons.end(); ++it) { for (auto it = _buttons.begin(); it != _buttons.end(); ++it) {
auto ws = std::find_if(workspaces.begin(), workspaces.end(), auto ws = std::find_if(_workspaces.begin(), _workspaces.end(),
[it](auto node) -> bool { return node["num"].asInt() == it->first; }); [it](auto node) -> bool { return node["num"].asInt() == it->first; });
if (ws == workspaces.end()) { if (ws == _workspaces.end()) {
it = _buttons.erase(it); it = _buttons.erase(it);
needReorder = true; needReorder = true;
} }
} }
for (auto node : workspaces) { for (auto node : _workspaces) {
if (_bar.outputName != node["output"].asString())
continue;
auto it = _buttons.find(node["num"].asInt()); auto it = _buttons.find(node["num"].asInt());
if (it == _buttons.end() && _bar.outputName == node["output"].asString()) { if (it == _buttons.end()) {
_addWorkspace(node); _addWorkspace(node);
needReorder = true; needReorder = true;
} else { } else {
auto styleContext = it->second.get_style_context(); auto &button = it->second;
bool isCurrent = node["focused"].asBool(); bool isCurrent = node["focused"].asBool();
if (!isCurrent) { if (!isCurrent) {
styleContext->remove_class("current"); button.get_style_context()->remove_class("current");
} else if (isCurrent) { } else if (isCurrent) {
styleContext->add_class("current"); button.get_style_context()->add_class("current");
} }
if (needReorder) if (needReorder)
_box.reorder_child(it->second, node["num"].asInt() - 1); _box.reorder_child(button, node["num"].asInt() - 1);
it->second.show(); button.show();
} }
} }
} }
@ -61,7 +74,7 @@ void waybar::modules::Workspaces::_addWorkspace(Json::Value node)
try { try {
auto value = fmt::format("workspace \"{}\"", pair.first->first); auto value = fmt::format("workspace \"{}\"", pair.first->first);
uint32_t size = value.size(); uint32_t size = value.size();
ipc_single_command(_ipcSocketfd, IPC_COMMAND, value.c_str(), &size); ipc_single_command(_ipcfd, IPC_COMMAND, value.c_str(), &size);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
@ -73,27 +86,16 @@ void waybar::modules::Workspaces::_addWorkspace(Json::Value node)
button.show(); button.show();
} }
Json::Value waybar::modules::Workspaces::_getWorkspaces() Json::Value waybar::modules::Workspaces::_getWorkspaces(const std::string data)
{ {
uint32_t len = 0; Json::Value res;
Json::Value root;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
try { try {
std::string str = ipc_single_command(_ipcSocketfd, IPC_GET_WORKSPACES,
nullptr, &len);
std::string err; std::string err;
bool res = res = _parser.parse(data);
reader->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
delete reader;
if (!res) {
std::cerr << err << std::endl;
return root;
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }
return root; return res;
} }
waybar::modules::Workspaces::operator Gtk::Widget &() { waybar::modules::Workspaces::operator Gtk::Widget &() {