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 "client.hpp"
#include "util/chrono.hpp"
#include "util/json.hpp"
#include "IModule.hpp"
namespace waybar::modules {
@ -15,13 +16,16 @@ namespace waybar::modules {
operator Gtk::Widget &();
private:
void _addWorkspace(Json::Value node);
Json::Value _getWorkspaces();
Json::Value _getWorkspaces(const std::string data);
Bar &_bar;
waybar::util::SleeperThread _thread;
Gtk::Box _box;
util::JsonParser _parser;
std::mutex _mutex;
std::unordered_map<int, Gtk::Button> _buttons;
int _ipcSocketfd;
int _ipcEventSocketfd;
Json::Value _workspaces;
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 "client.hpp"
#include "factory.hpp"
#include "util/json.hpp"
waybar::Bar::Bar(Client &client, std::unique_ptr<struct wl_output *> &&p_output)
: client(client), window{Gtk::WindowType::WINDOW_TOPLEVEL},
@ -125,19 +126,13 @@ auto waybar::Bar::toggle() -> void
auto waybar::Bar::_setupConfig() -> void
{
Json::Value root;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
std::string err;
util::JsonParser parser;
std::ifstream file(client.configFile);
if (!file.is_open())
throw std::runtime_error("Can't open config file");
std::string str((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
bool res = reader->parse(str.c_str(), str.c_str() + str.size(), &_config, &err);
delete reader;
if (!res)
throw std::runtime_error(err);
_config = parser.parse(str);
}
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) {
const char *env = getenv("SWAYSOCK");
if (env) return std::string(env);
std::string str;
std::string str;
{
std::string str_buf;
FILE* in;
char buf[512] = { 0 };
if (!(in = popen("sway --get-socketpath 2>/dev/null", "r"))) {
throw std::runtime_error("Failed to get socket path");
}
while (fgets(buf, sizeof(buf), in) != nullptr) {
str_buf.append(buf, sizeof(buf));
}
pclose(in);
str = str_buf;
}
if (str.back() == '\n') {
str.pop_back();
}
return str;
std::string str_buf;
FILE* in;
char buf[512] = { 0 };
if (!(in = popen("sway --get-socketpath 2>/dev/null", "r"))) {
throw std::runtime_error("Failed to get socket path");
}
while (fgets(buf, sizeof(buf), in) != nullptr) {
str_buf.append(buf, sizeof(buf));
}
pclose(in);
str = str_buf;
}
if (str.back() == '\n') {
str.pop_back();
}
return str;
}
int ipc_open_socket(std::string socket_path) {
struct sockaddr_un addr;
int socketfd;
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket");
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path.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) {
throw std::runtime_error("Unable to connect to " + socket_path);
}
return socketfd;
struct sockaddr_un addr;
int socketfd;
if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
throw std::runtime_error("Unable to open Unix socket");
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path.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) {
throw std::runtime_error("Unable to connect to " + socket_path);
}
return socketfd;
}
struct ipc_response ipc_recv_response(int socketfd) {
char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
struct ipc_response response;
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) {
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
if (received <= 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
while (total < ipc_header_size) {
ssize_t received = recv(socketfd, data + total, ipc_header_size - total, 0);
if (received <= 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
struct ipc_response response;
total = 0;
response.size = data32[0];
response.type = data32[1];
total = 0;
response.size = data32[0];
response.type = data32[1];
char payload[response.size + 1];
while (total < response.size) {
ssize_t received = recv(socketfd, payload + total, response.size - total, 0);
if (received < 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
payload[response.size] = '\0';
response.payload = std::string(payload);
return response;
while (total < response.size) {
ssize_t received = recv(socketfd, payload + total, response.size - total, 0);
if (received < 0) {
throw std::runtime_error("Unable to receive IPC response");
}
total += received;
}
payload[response.size] = '\0';
response.payload = std::string(payload);
return response;
}
std::string ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) {
char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
memcpy(data, ipc_magic, sizeof(ipc_magic));
data32[0] = *len;
data32[1] = type;
char data[ipc_header_size];
uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic));
memcpy(data, ipc_magic, sizeof(ipc_magic));
data32[0] = *len;
data32[1] = type;
if (send(socketfd, data, ipc_header_size, 0) == -1) {
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");
}
struct ipc_response resp = ipc_recv_response(socketfd);
*len = resp.size;
return resp.payload;
if (send(socketfd, data, ipc_header_size, 0) == -1)
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");
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");
std::string socketPath = get_socketpath();
_ipcSocketfd = ipc_open_socket(socketPath);
_ipcEventSocketfd = ipc_open_socket(socketPath);
const char *subscribe = "[ \"workspace\", \"mode\" ]";
_ipcfd = ipc_open_socket(socketPath);
_ipcEventfd = ipc_open_socket(socketPath);
const char *subscribe = "[ \"workspace\" ]";
uint32_t len = strlen(subscribe);
ipc_single_command(_ipcEventSocketfd, IPC_SUBSCRIBE, subscribe, &len);
ipc_single_command(_ipcEventfd, IPC_SUBSCRIBE, subscribe, &len);
_thread = [this] {
Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Workspaces::update));
_thread.sleep_for(chrono::milliseconds(250));
try {
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
{
if (_bar.outputName.empty()) return;
Json::Value workspaces = _getWorkspaces();
std::lock_guard<std::mutex> lock(_mutex);
bool needReorder = false;
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; });
if (ws == workspaces.end()) {
if (ws == _workspaces.end()) {
it = _buttons.erase(it);
needReorder = true;
}
}
for (auto node : workspaces) {
for (auto node : _workspaces) {
if (_bar.outputName != node["output"].asString())
continue;
auto it = _buttons.find(node["num"].asInt());
if (it == _buttons.end() && _bar.outputName == node["output"].asString()) {
if (it == _buttons.end()) {
_addWorkspace(node);
needReorder = true;
} else {
auto styleContext = it->second.get_style_context();
auto &button = it->second;
bool isCurrent = node["focused"].asBool();
if (!isCurrent) {
styleContext->remove_class("current");
button.get_style_context()->remove_class("current");
} else if (isCurrent) {
styleContext->add_class("current");
button.get_style_context()->add_class("current");
}
if (needReorder)
_box.reorder_child(it->second, node["num"].asInt() - 1);
it->second.show();
_box.reorder_child(button, node["num"].asInt() - 1);
button.show();
}
}
}
@ -61,7 +74,7 @@ void waybar::modules::Workspaces::_addWorkspace(Json::Value node)
try {
auto value = fmt::format("workspace \"{}\"", pair.first->first);
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) {
std::cerr << e.what() << std::endl;
}
@ -73,27 +86,16 @@ void waybar::modules::Workspaces::_addWorkspace(Json::Value node)
button.show();
}
Json::Value waybar::modules::Workspaces::_getWorkspaces()
Json::Value waybar::modules::Workspaces::_getWorkspaces(const std::string data)
{
uint32_t len = 0;
Json::Value root;
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
Json::Value res;
try {
std::string str = ipc_single_command(_ipcSocketfd, IPC_GET_WORKSPACES,
nullptr, &len);
std::string err;
bool res =
reader->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
delete reader;
if (!res) {
std::cerr << err << std::endl;
return root;
}
res = _parser.parse(data);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return root;
return res;
}
waybar::modules::Workspaces::operator Gtk::Widget &() {