basic IPC working
This commit is contained in:
commit
7bf8c5dd38
7 changed files with 232 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
cmake-build-debug
|
||||
.idea
|
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal 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
12
main.cpp
Normal 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
41
sway_bindings/Formatter.h
Normal 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
37
sway_bindings/Sway.cpp
Normal 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
23
sway_bindings/Sway.h
Normal 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
103
sway_bindings/swaymsg.h
Normal 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
|
Loading…
Reference in a new issue