2018-08-16 12:29:41 +00:00
|
|
|
#include "modules/sway/ipc/client.hpp"
|
2019-04-19 09:09:06 +00:00
|
|
|
#include <fcntl.h>
|
2018-08-08 21:54:58 +00:00
|
|
|
|
2019-04-19 09:09:06 +00:00
|
|
|
namespace waybar::modules::sway {
|
|
|
|
|
|
|
|
Ipc::Ipc() {
|
2018-12-26 10:13:36 +00:00
|
|
|
const std::string& socketPath = getSocketPath();
|
|
|
|
fd_ = open(socketPath);
|
|
|
|
fd_event_ = open(socketPath);
|
|
|
|
}
|
2018-08-08 21:54:58 +00:00
|
|
|
|
2019-04-19 09:09:06 +00:00
|
|
|
Ipc::~Ipc() {
|
2018-12-26 10:13:36 +00:00
|
|
|
// To fail the IPC header
|
|
|
|
write(fd_, "close-sway-ipc", 14);
|
|
|
|
write(fd_event_, "close-sway-ipc", 14);
|
2019-04-19 09:09:06 +00:00
|
|
|
if (fd_ > 0) {
|
|
|
|
close(fd_);
|
|
|
|
fd_ = -1;
|
|
|
|
}
|
|
|
|
if (fd_event_ > 0) {
|
|
|
|
close(fd_event_);
|
|
|
|
fd_event_ = -1;
|
|
|
|
}
|
2018-08-20 12:50:45 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 09:09:06 +00:00
|
|
|
const std::string Ipc::getSocketPath() const {
|
2019-04-18 15:52:00 +00:00
|
|
|
const char* env = getenv("SWAYSOCK");
|
2018-08-16 12:29:41 +00:00
|
|
|
if (env != nullptr) {
|
|
|
|
return std::string(env);
|
|
|
|
}
|
2018-08-13 19:23:43 +00:00
|
|
|
std::string str;
|
2018-08-08 21:54:58 +00:00
|
|
|
{
|
2018-08-13 19:23:43 +00:00
|
|
|
std::string str_buf;
|
2019-04-18 15:52:00 +00:00
|
|
|
FILE* in;
|
|
|
|
char buf[512] = {0};
|
2018-08-16 12:29:41 +00:00
|
|
|
if ((in = popen("sway --get-socketpath 2>/dev/null", "r")) == nullptr) {
|
2018-08-13 19:23:43 +00:00
|
|
|
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;
|
2019-04-19 09:09:06 +00:00
|
|
|
if (str.empty()) {
|
|
|
|
throw std::runtime_error("Socket path is empty");
|
|
|
|
}
|
2018-08-13 19:23:43 +00:00
|
|
|
}
|
|
|
|
if (str.back() == '\n') {
|
|
|
|
str.pop_back();
|
|
|
|
}
|
|
|
|
return str;
|
2018-08-08 21:54:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 09:09:06 +00:00
|
|
|
int Ipc::open(const std::string& socketPath) const {
|
|
|
|
int32_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (fd == -1) {
|
2018-08-13 19:23:43 +00:00
|
|
|
throw std::runtime_error("Unable to open Unix socket");
|
|
|
|
}
|
2019-04-19 09:09:06 +00:00
|
|
|
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
2018-08-13 19:23:43 +00:00
|
|
|
addr.sun_family = AF_UNIX;
|
2018-08-16 12:29:41 +00:00
|
|
|
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
2018-08-13 19:23:43 +00:00
|
|
|
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
|
|
|
|
int l = sizeof(struct sockaddr_un);
|
2019-04-18 15:52:00 +00:00
|
|
|
if (::connect(fd, reinterpret_cast<struct sockaddr*>(&addr), l) == -1) {
|
2018-08-29 19:07:58 +00:00
|
|
|
throw std::runtime_error("Unable to connect to Sway");
|
2018-08-13 19:23:43 +00:00
|
|
|
}
|
2018-08-20 12:50:45 +00:00
|
|
|
return fd;
|
2018-08-08 21:54:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 14:48:02 +00:00
|
|
|
struct Ipc::ipc_response Ipc::recv(int fd) {
|
2018-08-18 14:01:56 +00:00
|
|
|
std::string header;
|
2019-03-15 00:53:45 +00:00
|
|
|
header.resize(ipc_header_size_);
|
2019-04-18 15:52:00 +00:00
|
|
|
auto data32 = reinterpret_cast<uint32_t*>(header.data() + ipc_magic_.size());
|
2018-08-13 19:23:43 +00:00
|
|
|
size_t total = 0;
|
2018-08-08 21:54:58 +00:00
|
|
|
|
2018-08-20 12:50:45 +00:00
|
|
|
while (total < ipc_header_size_) {
|
2018-12-09 09:49:28 +00:00
|
|
|
auto res = ::recv(fd, header.data() + total, ipc_header_size_ - total, 0);
|
2019-04-19 10:11:55 +00:00
|
|
|
if (fd_event_ == -1 || fd_ == -1) {
|
|
|
|
// IPC is closed so just return an empty response
|
|
|
|
return {0, 0, ""};
|
|
|
|
}
|
2018-08-18 14:01:56 +00:00
|
|
|
if (res <= 0) {
|
2018-12-26 10:13:36 +00:00
|
|
|
throw std::runtime_error("Unable to receive IPC header");
|
2018-08-13 19:23:43 +00:00
|
|
|
}
|
2018-08-18 14:01:56 +00:00
|
|
|
total += res;
|
2018-08-13 19:23:43 +00:00
|
|
|
}
|
2018-12-26 10:13:36 +00:00
|
|
|
auto magic = std::string(header.data(), header.data() + ipc_magic_.size());
|
|
|
|
if (magic != ipc_magic_) {
|
|
|
|
throw std::runtime_error("Invalid IPC magic");
|
|
|
|
}
|
|
|
|
|
2018-08-13 19:23:43 +00:00
|
|
|
total = 0;
|
2018-08-18 14:01:56 +00:00
|
|
|
std::string payload;
|
2019-03-15 00:53:45 +00:00
|
|
|
payload.resize(data32[0]);
|
2018-08-18 14:01:56 +00:00
|
|
|
while (total < data32[0]) {
|
2018-12-09 09:49:28 +00:00
|
|
|
auto res = ::recv(fd, payload.data() + total, data32[0] - total, 0);
|
2018-08-18 14:01:56 +00:00
|
|
|
if (res < 0) {
|
2019-04-23 09:41:49 +00:00
|
|
|
if (errno == EINTR || errno == EAGAIN) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-12-26 10:13:36 +00:00
|
|
|
throw std::runtime_error("Unable to receive IPC payload");
|
2018-08-13 19:23:43 +00:00
|
|
|
}
|
2018-08-18 14:01:56 +00:00
|
|
|
total += res;
|
2018-08-13 19:23:43 +00:00
|
|
|
}
|
2019-04-23 09:41:49 +00:00
|
|
|
auto parsed = parser_.parse(&payload.front(), data32[0]);
|
|
|
|
return {data32[0], data32[1], parsed};
|
2018-08-08 21:54:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 14:48:02 +00:00
|
|
|
struct Ipc::ipc_response Ipc::send(int fd, uint32_t type, const std::string& payload) {
|
2018-08-18 14:01:56 +00:00
|
|
|
std::string header;
|
2019-03-15 00:53:45 +00:00
|
|
|
header.resize(ipc_header_size_);
|
2019-04-18 15:52:00 +00:00
|
|
|
auto data32 = reinterpret_cast<uint32_t*>(header.data() + ipc_magic_.size());
|
2018-08-20 12:50:45 +00:00
|
|
|
memcpy(header.data(), ipc_magic_.c_str(), ipc_magic_.size());
|
2018-08-18 14:01:56 +00:00
|
|
|
data32[0] = payload.size();
|
2018-08-13 19:23:43 +00:00
|
|
|
data32[1] = type;
|
2018-08-08 21:54:58 +00:00
|
|
|
|
2018-08-20 12:50:45 +00:00
|
|
|
if (::send(fd, header.data(), ipc_header_size_, 0) == -1) {
|
2018-08-13 19:23:43 +00:00
|
|
|
throw std::runtime_error("Unable to send IPC header");
|
2018-08-16 12:29:41 +00:00
|
|
|
}
|
2018-08-20 12:50:45 +00:00
|
|
|
if (::send(fd, payload.c_str(), payload.size(), 0) == -1) {
|
2018-08-13 19:23:43 +00:00
|
|
|
throw std::runtime_error("Unable to send IPC payload");
|
2018-08-16 12:29:41 +00:00
|
|
|
}
|
2019-04-19 09:09:06 +00:00
|
|
|
return Ipc::recv(fd);
|
2018-08-20 12:50:45 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 14:48:02 +00:00
|
|
|
void Ipc::sendCmd(uint32_t type, const std::string& payload) {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
2019-04-19 09:09:06 +00:00
|
|
|
const auto res = Ipc::send(fd_, type, payload);
|
|
|
|
signal_cmd.emit(res);
|
2018-08-20 12:50:45 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 14:48:02 +00:00
|
|
|
void Ipc::subscribe(const std::string& payload) {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_event_);
|
2019-04-19 09:09:06 +00:00
|
|
|
auto res = Ipc::send(fd_event_, IPC_SUBSCRIBE, payload);
|
2019-04-23 09:41:49 +00:00
|
|
|
if (!res.payload["success"].asBool()) {
|
2018-08-20 12:50:45 +00:00
|
|
|
throw std::runtime_error("Unable to subscribe ipc event");
|
|
|
|
}
|
2018-08-08 21:54:58 +00:00
|
|
|
}
|
2018-08-20 12:50:45 +00:00
|
|
|
|
2019-04-19 14:48:02 +00:00
|
|
|
void Ipc::handleEvent() {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_event_);
|
2019-04-19 09:09:06 +00:00
|
|
|
const auto res = Ipc::recv(fd_event_);
|
|
|
|
signal_event.emit(res);
|
2018-11-28 22:27:06 +00:00
|
|
|
}
|
2019-04-19 09:09:06 +00:00
|
|
|
|
|
|
|
} // namespace waybar::modules::sway
|