refactor(workspaces): listen ipc event
parent
68f9ea3065
commit
ea9a08d473
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
11
src/bar.cpp
11
src/bar.cpp
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 &() {
|
||||||
|
|
Loading…
Reference in New Issue