basic IPC working

This commit is contained in:
LordGrimmauld 2024-03-03 16:45:25 +01:00
commit 7bf8c5dd38
7 changed files with 232 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
cmake-build-debug
.idea

14
CMakeLists.txt Normal file
View file

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.28)
project(ipcswaytest)
set(CMAKE_CXX_STANDARD 17)
find_package(nlohmann_json 3.2.0 REQUIRED)
add_executable(ipcswaytest main.cpp
sway_bindings/swaymsg.h
sway_bindings/Sway.h
sway_bindings/Sway.cpp
sway_bindings/Formatter.h)
target_link_libraries(ipcswaytest PRIVATE nlohmann_json::nlohmann_json)

12
main.cpp Normal file
View file

@ -0,0 +1,12 @@
#include <iostream>
#include "sway_bindings/swaymsg.h"
#include "sway_bindings/Sway.h"
int main() {
auto sway = Sway();
auto resp = sway.sendIPC(SWAY_GET_TREE);
std::cout << resp.msg << "\n";
return 0;
}

41
sway_bindings/Formatter.h Normal file
View file

@ -0,0 +1,41 @@
//
// Created by grimmauld on 03.03.24.
// https://stackoverflow.com/questions/12261915/how-to-throw-stdexceptions-with-variable-messages
//
#ifndef IPCSWAYTEST_FORMATTER_H
#define IPCSWAYTEST_FORMATTER_H
#include <stdexcept>
#include <sstream>
class Formatter
{
public:
Formatter() = default;
~Formatter() = default;
template <typename Type>
Formatter & operator << (const Type & value)
{
stream_ << value;
return *this;
}
std::string str() const { return stream_.str(); }
explicit operator std::string () const { return stream_.str(); }
enum ConvertToString
{
to_str
};
std::string operator >> (ConvertToString) { return stream_.str(); }
Formatter(const Formatter &) = delete;
private:
std::stringstream stream_;
// Formatter & operator = (Formatter &);
};
#endif //IPCSWAYTEST_FORMATTER_H

37
sway_bindings/Sway.cpp Normal file
View file

@ -0,0 +1,37 @@
//
// Created by grimmauld on 03.03.24.
//
#include "Sway.h"
swaymsg Sway::sendIPC(const swaymsg &query) const {
if (send(socketfd, query.asBuffer(), query.size(), 0) == -1) {
throw std::runtime_error("Swaysock: Error on send() call");
}
return swaymsg::fromSock(socketfd);
}
Sway::Sway() : socketfd(socket(AF_UNIX, SOCK_STREAM, 0)) {
initializeSocket();
}
void Sway::initializeSocket() const {
auto *sockaddr = std::getenv("SWAYSOCK");
if (sockaddr == nullptr) {
throw std::runtime_error("Binding socket: reading SWAYSOCK env var failed");
}
if (socketfd == -1) {
throw std::runtime_error("Binding socket: could not get file descriptor");
}
struct sockaddr_un swaysock{};
swaysock.sun_family = AF_UNIX;
strcpy(swaysock.sun_path, sockaddr);
auto dlen = strlen(swaysock.sun_path) + sizeof(swaysock.sun_family);
if (connect(socketfd, (struct sockaddr *) &swaysock, dlen) == -1) {
throw std::runtime_error("Binding socket: could not connect to socket");
}
}

23
sway_bindings/Sway.h Normal file
View file

@ -0,0 +1,23 @@
//
// Created by grimmauld on 03.03.24.
//
#ifndef IPCSWAYTEST_SWAY_H
#define IPCSWAYTEST_SWAY_H
#include "swaymsg.h"
class Sway {
protected:
const int socketfd;
public:
explicit Sway();
void initializeSocket() const;
[[nodiscard]] swaymsg sendIPC(const swaymsg& query) const;
};
#endif //IPCSWAYTEST_SWAY_H

103
sway_bindings/swaymsg.h Normal file
View file

@ -0,0 +1,103 @@
//
// Created by grimmauld on 03.03.24.
//
#ifndef IPCSWAYTEST_SWAYMSG_H
#define IPCSWAYTEST_SWAYMSG_H
#include <sys/socket.h>
#include <sys/un.h>
#include <cstring>
#include <cstdlib>
#include "Formatter.h"
struct swaymsg {
explicit swaymsg(const unsigned int payloadType, const char *msg) : swaymsg(payloadType, msg,
msg == nullptr ? 0 : strlen(msg)) {};
explicit swaymsg(const unsigned int payloadType, const char *msg, const unsigned int payload_len)
: payload_len(payload_len), payload_type(payloadType) {
this->msg = static_cast<char *>(std::malloc(payload_len));
std::memcpy(this->msg, msg, payload_len);
buff = static_cast<unsigned char *>(std::malloc(size()));
std::memcpy(buff, magic, strlen(magic));
std::memcpy(buff + (unsigned int) strlen(magic), &payload_len, sizeof(payload_len));
std::memcpy(buff + (unsigned int) strlen(magic) + sizeof(payload_len), &payload_type, sizeof(payload_type));
std::memcpy(buff + (unsigned int) strlen(magic) + sizeof(payload_len) + sizeof(payload_type), msg, payload_len);
}
explicit swaymsg(const unsigned int payloadType) : swaymsg(payloadType, nullptr) {}
[[nodiscard]] static swaymsg fromSock(const int sockfd) {
char buff[6];
size_t dlen;
if ((dlen = recv(sockfd, buff, 6, 0)) != 6) {
throw std::runtime_error(
Formatter() << "Swaysock: Unexpected length on recv() call for magic: " << dlen
>> Formatter::to_str);
}
if (strncmp(buff, magic, 6) != 0) {
throw std::runtime_error("Swaysock: magic did not match");
}
unsigned int payloadType;
unsigned int payloadLen;
static_assert(sizeof(payloadType) == 4);
static_assert(sizeof(payloadLen) == 4);
if ((dlen = recv(sockfd, &payloadLen, sizeof(payloadLen), 0)) != sizeof(payloadLen)) {
throw std::runtime_error(
Formatter() << "Swaysock: Unexpected length on recv() call for len: " << dlen >> Formatter::to_str);
}
if ((dlen = recv(sockfd, &payloadType, sizeof(payloadType), 0)) != sizeof(payloadType)) {
throw std::runtime_error(
Formatter() << "Swaysock: Unexpected length on recv() call for type: " << dlen
>> Formatter::to_str);
}
char msgBuff[payloadLen];
if ((dlen = recv(sockfd, msgBuff, payloadLen, 0)) != payloadLen) {
throw std::runtime_error(
Formatter() << "Swaysock: Unexpected length on recv() call for message: " << dlen
>> Formatter::to_str);
}
return swaymsg(payloadType, msgBuff, payloadLen);
};
constexpr const static char *magic = "i3-ipc";
const unsigned int payload_len;
const unsigned int payload_type;
char *msg;
unsigned char *buff = nullptr;
static_assert(sizeof(payload_len) == 4);
static_assert(sizeof(payload_type) == 4);
[[nodiscard]] size_t size() const {
return (int) strlen(magic) + sizeof(payload_len) + sizeof(payload_type) + payload_len;
}
[[nodiscard]] void *asBuffer() const {
return buff;
}
~swaymsg() {
delete buff;
delete msg;
}
};
static const swaymsg SWAY_EXIT = swaymsg(0, "exit");
static const swaymsg SWAY_GET_WORKSPACES = swaymsg(1);
static const swaymsg SWAY_GET_OUTPUTS = swaymsg(3);
static const swaymsg SWAY_GET_TREE = swaymsg(4);
static const swaymsg SWAY_GET_VERSION = swaymsg(7);
static const swaymsg SWAY_GET_CONFIG = swaymsg(9);
static const swaymsg SWAY_GET_INPUTS = swaymsg(100);
#endif //IPCSWAYTEST_SWAYMSG_H