commit 7bf8c5dd387eeafa8bd338569336c983e6667b6a Author: LordGrimmauld Date: Sun Mar 3 16:45:25 2024 +0100 basic IPC working diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b72d0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cmake-build-debug +.idea \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a6a88b1 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..8dd5147 --- /dev/null +++ b/main.cpp @@ -0,0 +1,12 @@ +#include + +#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; +} diff --git a/sway_bindings/Formatter.h b/sway_bindings/Formatter.h new file mode 100644 index 0000000..14ed9d2 --- /dev/null +++ b/sway_bindings/Formatter.h @@ -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 +#include + +class Formatter +{ +public: + Formatter() = default; + ~Formatter() = default; + + template + 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 diff --git a/sway_bindings/Sway.cpp b/sway_bindings/Sway.cpp new file mode 100644 index 0000000..1cfeddd --- /dev/null +++ b/sway_bindings/Sway.cpp @@ -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"); + } +} diff --git a/sway_bindings/Sway.h b/sway_bindings/Sway.h new file mode 100644 index 0000000..bb0a886 --- /dev/null +++ b/sway_bindings/Sway.h @@ -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 diff --git a/sway_bindings/swaymsg.h b/sway_bindings/swaymsg.h new file mode 100644 index 0000000..63e9a33 --- /dev/null +++ b/sway_bindings/swaymsg.h @@ -0,0 +1,103 @@ +// +// Created by grimmauld on 03.03.24. +// + +#ifndef IPCSWAYTEST_SWAYMSG_H +#define IPCSWAYTEST_SWAYMSG_H + +#include +#include +#include +#include +#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(std::malloc(payload_len)); + std::memcpy(this->msg, msg, payload_len); + + buff = static_cast(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 \ No newline at end of file