From 4a6989f5eaee08f19f605d0929908db0d6643f28 Mon Sep 17 00:00:00 2001 From: LordGrimmauld Date: Mon, 4 Mar 2024 00:27:05 +0100 Subject: [PATCH] sway tree --- CMakeLists.txt | 8 ++ main.cpp | 16 +++- mainwindow.cpp | 3 +- mainwindow.h | 3 +- mainwindow.ui | 2 +- sway_bindings/Formatter.h | 41 ++++++++++ sway_bindings/Sway.cpp | 37 +++++++++ sway_bindings/Sway.h | 23 ++++++ sway_bindings/swaymsg.h | 103 +++++++++++++++++++++++++ tree/AbstractTreeModel.h | 91 ++++++++++++++++++++++ tree/AbstractTreeNode.h | 1 - tree/PsTreeModel.cpp | 71 ++--------------- tree/PsTreeModel.h | 26 ++----- tree/SwayTreeModel.cpp | 5 ++ tree/SwayTreeModel.h | 49 ++++++++++++ tree/pstree.cpp | 10 ++- tree/pstree.h | 7 +- tree/swaytree.cpp | 14 ++++ tree/swaytree.h | 155 ++++++++++++++++++++++++++++++++++++++ 19 files changed, 570 insertions(+), 95 deletions(-) create mode 100644 sway_bindings/Formatter.h create mode 100644 sway_bindings/Sway.cpp create mode 100644 sway_bindings/Sway.h create mode 100644 sway_bindings/swaymsg.h create mode 100644 tree/AbstractTreeModel.h create mode 100644 tree/SwayTreeModel.cpp create mode 100644 tree/SwayTreeModel.h create mode 100644 tree/swaytree.cpp create mode 100644 tree/swaytree.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b90d39..b8539af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,14 @@ set(PROJECT_SOURCES ${TS_FILES} tree/pstree.cpp tree/PsTreeModel.cpp + sway_bindings/Formatter.h + sway_bindings/Sway.cpp + tree/swaytree.h + tree/swaytree.cpp + tree/AbstractTreeModel.h + tree/AbstractTreeModel.h + tree/SwayTreeModel.h + tree/SwayTreeModel.cpp ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) diff --git a/main.cpp b/main.cpp index c71a897..b855c3b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,6 @@ #include "mainwindow.h" -#include "tree/pstree.h" -#include "tree/PsTreeModel.h" +#include "sway_bindings/Sway.h" +#include "tree/swaytree.h" #include #include @@ -9,11 +9,23 @@ #include #include #include +#include +#include +#include using namespace Qt::StringLiterals; +#include + +using json = nlohmann::json; + int main(int argc, char *argv[]) { + auto sway = Sway(); + auto resp = sway.sendIPC(SWAY_GET_TREE); + json rep = json::parse(resp.msg); + auto rootRecord = SwayRecord(rep); + QApplication a(argc, argv); QCoreApplication::setOrganizationName("Grimmauld"); QCoreApplication::setApplicationName("SwayMux"); diff --git a/mainwindow.cpp b/mainwindow.cpp index 5832d8c..bec4990 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include "./ui_mainwindow.h" #include "tree/PsTreeModel.h" +#include "tree/SwayTreeModel.h" #include #include @@ -8,7 +9,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - model = new PsTreeModel(ui->treeView); + model = new SwayTreeModel(ui->treeView); modelUpdateTimer = new QTimer(this); connect(modelUpdateTimer, &QTimer::timeout, this, QOverload<>::of(&MainWindow::update)); modelUpdateTimer->start(1000); diff --git a/mainwindow.h b/mainwindow.h index 6877df6..216a1e9 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -3,6 +3,7 @@ #include #include "tree/PsTreeModel.h" +#include "tree/SwayTreeModel.h" QT_BEGIN_NAMESPACE namespace Ui { @@ -22,7 +23,7 @@ public: private: Ui::MainWindow *ui; - PsTreeModel* model; + SwayTreeModel* model; QTimer* modelUpdateTimer; }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index 6991628..45bb7e8 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -37,7 +37,7 @@ - 24 + 20 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..bb195bd --- /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 = new unsigned char [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 diff --git a/tree/AbstractTreeModel.h b/tree/AbstractTreeModel.h new file mode 100644 index 0000000..2b84e8d --- /dev/null +++ b/tree/AbstractTreeModel.h @@ -0,0 +1,91 @@ +// +// Created by grimmauld on 03.03.24. +// + +#ifndef SWAYMUX_ABSTRACTTREEMODEL_H +#define SWAYMUX_ABSTRACTTREEMODEL_H + +#include +#include "AbstractTreeNode.h" +#include + +template +class AbstractTreeModel : public QAbstractItemModel { + static_assert(std::is_base_of, T>::value, + "Tree model subject class is not derived from AbstractTreeNode"); + +public: + Q_DISABLE_COPY_MOVE(AbstractTreeModel) + + explicit AbstractTreeModel(QObject *parent = nullptr): QAbstractItemModel(parent) {}; + + ~AbstractTreeModel() override = default; + + [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override { + if (!index.isValid() || role != Qt::DisplayRole) + return {}; + + const auto *item = static_cast(index.internalPointer()); + return item->data(index.column()); + } + + [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override { + return index.isValid() + ? QAbstractItemModel::flags(index) : Qt::ItemFlags(Qt::NoItemFlags); + } + + [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override { + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QVariant{}; + + return T{}.headerData(section); + } + + [[nodiscard]] QModelIndex index(int row, int column, + const QModelIndex &parent = {}) const override { + if (!hasIndex(row, column, parent)) + return {}; + + const T *parentItem = parent.isValid() + ? static_cast(parent.internalPointer()) + : getRoot(); + + if (auto *childItem = parentItem->child(row)) + return createIndex(row, column, childItem); + return {}; + } + + [[nodiscard]] QModelIndex parent(const QModelIndex &index) const override { + if (!index.isValid()) + return {}; + + auto *childItem = static_cast(index.internalPointer()); + T *parentItem = childItem->parentItem(); + + return parentItem != getRoot() + ? createIndex(parentItem->row(), 0, parentItem) : QModelIndex{}; + } + + [[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override { + if (parent.column() > 0) + return 0; + + const T *parentItem = parent.isValid() + ? static_cast(parent.internalPointer()) + : getRoot(); + + return parentItem->childCount(); + } + + [[nodiscard]] int columnCount(const QModelIndex &parent = {}) const override { + if (parent.isValid()) + return static_cast(parent.internalPointer())->columnCount(); + return T{}.columnCount(); + } + + [[nodiscard]] virtual const T* getRoot() const = 0; +}; + + +#endif //SWAYMUX_ABSTRACTTREEMODEL_H diff --git a/tree/AbstractTreeNode.h b/tree/AbstractTreeNode.h index dee6ee9..fcb3591 100644 --- a/tree/AbstractTreeNode.h +++ b/tree/AbstractTreeNode.h @@ -14,7 +14,6 @@ template class AbstractTreeNode { - // static_assert(std::is_base_of::value, "Derived not derived from BaseClass"); public: std::vector> children{}; diff --git a/tree/PsTreeModel.cpp b/tree/PsTreeModel.cpp index 7b105fe..4ed349e 100644 --- a/tree/PsTreeModel.cpp +++ b/tree/PsTreeModel.cpp @@ -4,76 +4,17 @@ #include "PsTreeModel.h" -PsTreeModel::PsTreeModel(QObject *parent) : QAbstractItemModel(parent), rootItem(get_process_records()) {} - -QModelIndex PsTreeModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) - return {}; - - const ProcessTreeNode *parentItem = parent.isValid() - ? static_cast(parent.internalPointer()) - : &rootItem; - - if (auto *childItem = parentItem->child(row)) - return createIndex(row, column, childItem); - return {}; -} - -QModelIndex PsTreeModel::parent(const QModelIndex &index) const { - if (!index.isValid()) - return {}; - - auto *childItem = static_cast(index.internalPointer()); - ProcessTreeNode *parentItem = childItem->parentItem(); - - return parentItem != &rootItem - ? createIndex(parentItem->row(), 0, parentItem) : QModelIndex{}; -} - -int PsTreeModel::rowCount(const QModelIndex &parent) const { - if (parent.column() > 0) - return 0; - - const ProcessTreeNode *parentItem = parent.isValid() - ? static_cast(parent.internalPointer()) - : &rootItem; - - return parentItem->childCount(); -} - -int PsTreeModel::columnCount(const QModelIndex &parent) const { - if (parent.isValid()) - return static_cast(parent.internalPointer())->columnCount(); - return ProcessTreeNode{}.columnCount(); -} - -QVariant PsTreeModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || role != Qt::DisplayRole) - return {}; - - const auto *item = static_cast(index.internalPointer()); - return item->data(index.column()); -} - -Qt::ItemFlags PsTreeModel::flags(const QModelIndex &index) const { - return index.isValid() - ? QAbstractItemModel::flags(index) : Qt::ItemFlags(Qt::NoItemFlags); -} - -QVariant PsTreeModel::headerData(int section, Qt::Orientation orientation, - int role) const { - if (orientation != Qt::Horizontal || role != Qt::DisplayRole) - return QVariant{}; - - return ProcessTreeNode{}.headerData(section); -} - void PsTreeModel::update() { - auto changes = update_process_records(&rootItem); + /* + auto* root = getRoot(); + auto changes = update_process_records(root); QList changedParents{}; for(auto* node: changes) { changedParents.push_back(createIndex(node->row(), 0, node)); } layoutAboutToBeChanged(changedParents); + */ } + +PsTreeModel::PsTreeModel(QObject *parent) : PsTreeModel(get_process_records(), parent) {} diff --git a/tree/PsTreeModel.h b/tree/PsTreeModel.h index 16c83ac..f46aec8 100644 --- a/tree/PsTreeModel.h +++ b/tree/PsTreeModel.h @@ -8,10 +8,10 @@ #include #include "pstree.h" +#include "AbstractTreeModel.h" -class PsTreeModel : public QAbstractItemModel { +class PsTreeModel : public AbstractTreeModel { Q_OBJECT - public: Q_DISABLE_COPY_MOVE(PsTreeModel) @@ -19,26 +19,16 @@ public: ~PsTreeModel() override = default; - [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; - - [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override; - - [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - - [[nodiscard]] QModelIndex index(int row, int column, - const QModelIndex &parent = {}) const override; - - [[nodiscard]] QModelIndex parent(const QModelIndex &index) const override; - - [[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override; - - [[nodiscard]] int columnCount(const QModelIndex &parent = {}) const override; + explicit PsTreeModel(ProcessTreeNode * rootItem, QObject *parent = nullptr) : rootItem(rootItem), AbstractTreeModel(parent) {}; void update(); + [[nodiscard]] const ProcessTreeNode *getRoot() const override { + return rootItem; + }; + private: - ProcessTreeNode rootItem; + const ProcessTreeNode* rootItem; }; diff --git a/tree/SwayTreeModel.cpp b/tree/SwayTreeModel.cpp new file mode 100644 index 0000000..3756352 --- /dev/null +++ b/tree/SwayTreeModel.cpp @@ -0,0 +1,5 @@ +// +// Created by grimmauld on 03.03.24. +// + +#include "SwayTreeModel.h" diff --git a/tree/SwayTreeModel.h b/tree/SwayTreeModel.h new file mode 100644 index 0000000..6fa5638 --- /dev/null +++ b/tree/SwayTreeModel.h @@ -0,0 +1,49 @@ +// +// Created by grimmauld on 03.03.24. +// + +#ifndef SWAYMUX_SWAYTREEMODEL_H +#define SWAYMUX_SWAYTREEMODEL_H + +#include +#include +#include "pstree.h" +#include "AbstractTreeModel.h" +#include "swaytree.h" +#include "../sway_bindings/Sway.h" + +#include +#include + +using json = nlohmann::json; + +class SwayTreeModel : public AbstractTreeModel { +Q_OBJECT + +public: + inline static Sway sway = Sway(); + + Q_DISABLE_COPY_MOVE(SwayTreeModel) + + explicit SwayTreeModel(QObject *parent = nullptr) : AbstractTreeModel(parent) { + auto res = sway.sendIPC(SWAY_GET_TREE); + auto rep = json::parse(res.msg); + rootItem = new SwayTreeNode(rep); + } + + ~SwayTreeModel() override = default; + + explicit SwayTreeModel(SwayTreeNode * rootItem, QObject *parent = nullptr) : rootItem(rootItem), + AbstractTreeModel( + parent) {}; + + [[nodiscard]] const SwayTreeNode *getRoot() const override { + return rootItem; + }; + + +private: + SwayTreeNode * rootItem; +}; + +#endif //SWAYMUX_SWAYTREEMODEL_H diff --git a/tree/pstree.cpp b/tree/pstree.cpp index 0dec592..65b6be0 100644 --- a/tree/pstree.cpp +++ b/tree/pstree.cpp @@ -48,9 +48,9 @@ void populateTree(ProcessTreeNode *node, std::map } } -ProcessTreeNode get_process_records() { - ProcessTreeNode root = ProcessTreeNode(); // rootPrc - update_process_records(&root); +ProcessTreeNode * get_process_records() { + auto* root = new ProcessTreeNode(); // rootPrc + update_process_records(root); return root; } @@ -125,7 +125,9 @@ void printTree(const ProcessTreeNode &node, const std::string &prefix) { } void printTree() { - printTree(get_process_records(), ""); + auto* tree = get_process_records(); + printTree(*tree, ""); + delete tree; } QVariant ProcessTreeNode::data(int column) const { diff --git a/tree/pstree.h b/tree/pstree.h index 73d4844..2b63909 100644 --- a/tree/pstree.h +++ b/tree/pstree.h @@ -29,7 +29,7 @@ public: ProcessRecord(std::string name, pid_t pid, pid_t ppid) : name(std::move(name)), pid(pid), ppid(ppid) {} - ProcessRecord() : name("root"), pid(0), ppid(-1) {} + ProcessRecord() : name("rootItem"), pid(0), ppid(-1) {} [[nodiscard]] bool operator<(const ProcessRecord &other) const { return this->pid < other.pid; @@ -43,6 +43,9 @@ public: class ProcessTreeNode : public AbstractTreeNode { public: + ProcessTreeNode(ProcessTreeNode& node) = delete; // default constructor to make the static asserts shut up + ProcessTreeNode(const ProcessTreeNode& node) = delete; // default constructor to make the static asserts shut up + explicit ProcessTreeNode(ProcessTreeNode *parent = nullptr) : proc(ProcessRecord()), AbstractTreeNode(parent) {} const ProcessRecord proc; @@ -67,7 +70,7 @@ public: }; }; -ProcessTreeNode get_process_records(); +ProcessTreeNode * get_process_records(); void insert_process_record(const std::filesystem::path &status_path, std::map &buff); diff --git a/tree/swaytree.cpp b/tree/swaytree.cpp new file mode 100644 index 0000000..a356aba --- /dev/null +++ b/tree/swaytree.cpp @@ -0,0 +1,14 @@ +// +// Created by grimmauld on 03.03.24. +// + +#include "swaytree.h" + +QVariant SwayTreeNode::data(int column) const { + // TODO: maybe add a more elaborate representation + return QString::fromStdString(node.name); +} + +QVariant SwayTreeNode::headerData(int column) const { + return "Title"; +} diff --git a/tree/swaytree.h b/tree/swaytree.h new file mode 100644 index 0000000..40f6ad9 --- /dev/null +++ b/tree/swaytree.h @@ -0,0 +1,155 @@ +// +// Created by grimmauld on 03.03.24. +// + +#ifndef SWAYMUX_SWAYTREE_H +#define SWAYMUX_SWAYTREE_H + +#include +#include + +using json = nlohmann::json; + +#include "AbstractTreeNode.h" + +namespace NodeType { + enum NodeType { + root, output, workspace, con, floating_con + }; + + static NodeType fromString(const std::string &str) { + if (str == "output") { + return output; + } + if (str == "workspace") { + return workspace; + } + if (str == "con") { + return con; + } + if (str == "floating_con") { + return floating_con; + } + return root; + } +} + +namespace NodeLayout { + enum NodeLayout { + splith, splitv, stacked, tabbed, output + }; + + static NodeLayout fromString(const std::string &str) { + if (str == "splith") { + return splith; + } + if (str == "splitv") { + return splitv; + } + if (str == "stacked") { + return stacked; + } + if (str == "tabbed") { + return tabbed; + } + return output; + } +} + +namespace NodeOrientation { + enum NodeOrientation { + vertical, horizontal, none + }; + + static NodeOrientation fromString(const std::string &str) { + if (str == "vertical") { + return vertical; + } + if (str == "horizontal") { + return horizontal; + } + return none; + } +} + +struct SwayRecord { + explicit SwayRecord() : + id(0), + name("rootItem"), + type(NodeType::root), + current_border_width(0), + layout(NodeLayout::output), + orientation(NodeOrientation::none), + urgent(false), + sticky(false), + focused(false) {} + + explicit SwayRecord(const json &rep) : + id(rep["id"]), + name(rep["name"]), + type(NodeType::fromString(rep["type"])), + border(rep["border"]), + current_border_width(rep["current_border_width"]), + layout(NodeLayout::fromString((rep["layout"]))), + orientation(NodeOrientation::fromString(rep["orientation"])), + urgent(rep["urgent"]), + sticky(rep["sticky"]), + focused(rep["focused"]) {}; + + const int id; + const std::string name; + const NodeType::NodeType type; + const std::string border; + const int current_border_width; + const NodeLayout::NodeLayout layout; + const NodeOrientation::NodeOrientation orientation; + const bool urgent; + const bool sticky; + const bool focused; + + [[nodiscard]] bool operator<(const SwayRecord &other) const { + return this->id < other.id; + } + + [[nodiscard]] bool operator==(const SwayRecord &other) const { + return this->id == other.id; + } +}; + +class SwayTreeNode : public AbstractTreeNode { +public: + const SwayRecord node; + + explicit SwayTreeNode(const json &rep, SwayTreeNode *parent = nullptr) : node(SwayRecord(rep)), + AbstractTreeNode(parent) { + for (const auto &child: rep["nodes"]) { + auto childNode = std::make_unique(child, this); + this->appendChild(childNode); + } + + /* + for (const auto &child: rep["floating_nodes"]) { + auto childNode = std::make_unique(child, this); + this->appendChild(childNode); + } + */ + } + + explicit SwayTreeNode(SwayTreeNode *parent = nullptr) : node(SwayRecord()), AbstractTreeNode(parent) {}; + + explicit SwayTreeNode(SwayRecord node, SwayTreeNode *parent = nullptr) : node(std::move(node)), + AbstractTreeNode(parent) {}; + + [[nodiscard]] bool operator<(const SwayTreeNode &other) const { + return this->node < other.node; + } + + [[nodiscard]] QVariant data(int column) const override; + + [[nodiscard]] QVariant headerData(int column) const override; + + [[nodiscard]] int columnCount() const override { return 1; }; +}; + + +#endif //SWAYMUX_SWAYTREE_H