add basic QT tree
This commit is contained in:
commit
f4a81cfa32
78
.gitignore
vendored
Normal file
78
.gitignore
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# idea
|
||||
cmake-build-debug
|
||||
.idea
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
79
CMakeLists.txt
Normal file
79
CMakeLists.txt
Normal file
@ -0,0 +1,79 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(swaymux VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
|
||||
|
||||
set(TS_FILES swaymux_en_US.ts)
|
||||
|
||||
set(PROJECT_SOURCES
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
mainwindow.ui
|
||||
${TS_FILES}
|
||||
tree/pstree.cpp
|
||||
tree/PsTreeModel.cpp
|
||||
)
|
||||
|
||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_executable(swaymux
|
||||
MANUAL_FINALIZATION
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define target properties for Android with Qt 6 as:
|
||||
# set_property(TARGET swaymux APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
|
||||
|
||||
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
|
||||
else()
|
||||
if(ANDROID)
|
||||
add_library(swaymux SHARED
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define properties for Android with Qt 5 after find_package() calls as:
|
||||
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
||||
else()
|
||||
add_executable(swaymux
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
||||
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(swaymux PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
# If you are developing for iOS or macOS you should consider setting an
|
||||
# explicit, fixed bundle identifier manually though.
|
||||
if(${QT_VERSION} VERSION_LESS 6.1.0)
|
||||
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.swaymux)
|
||||
endif()
|
||||
set_target_properties(swaymux PROPERTIES
|
||||
${BUNDLE_ID_OPTION}
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||
MACOSX_BUNDLE TRUE
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS swaymux
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt_finalize_executable(swaymux)
|
||||
endif()
|
33
main.cpp
Normal file
33
main.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "mainwindow.h"
|
||||
#include "tree/pstree.h"
|
||||
#include "tree/PsTreeModel.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLocale>
|
||||
#include <QTranslator>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QScreen>
|
||||
#include <QTreeView>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
QCoreApplication::setOrganizationName("Grimmauld");
|
||||
QCoreApplication::setApplicationName("SwayMux");
|
||||
|
||||
QTranslator translator;
|
||||
const QStringList uiLanguages = QLocale::system().uiLanguages();
|
||||
for (const QString &locale : uiLanguages) {
|
||||
const QString baseName = "swaymux_" + QLocale(locale).name();
|
||||
if (translator.load(":/i18n/" + baseName)) {
|
||||
a.installTranslator(&translator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
MainWindow w;
|
||||
w.showMaximized();
|
||||
return a.exec();
|
||||
}
|
32
mainwindow.cpp
Normal file
32
mainwindow.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "mainwindow.h"
|
||||
#include "./ui_mainwindow.h"
|
||||
#include "tree/PsTreeModel.h"
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent), ui(new Ui::MainWindow) {
|
||||
ui->setupUi(this);
|
||||
|
||||
model = new PsTreeModel(ui->treeView);
|
||||
modelUpdateTimer = new QTimer(this);
|
||||
connect(modelUpdateTimer, &QTimer::timeout, this, QOverload<>::of(&MainWindow::update));
|
||||
modelUpdateTimer->start(1000);
|
||||
|
||||
ui->statusbar->showMessage(QString::fromStdString("test test 123"));
|
||||
ui->treeView->setModel(model);
|
||||
ui->treeView->expandAll();
|
||||
|
||||
for (int c = 0; c < model->columnCount(); ++c)
|
||||
ui->treeView->resizeColumnToContents(c);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
delete ui;
|
||||
delete model;
|
||||
delete modelUpdateTimer;
|
||||
}
|
||||
|
||||
void MainWindow::update() {
|
||||
// model->update();
|
||||
}
|
28
mainwindow.h
Normal file
28
mainwindow.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include "tree/PsTreeModel.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
void update() ;
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
PsTreeModel* model;
|
||||
QTimer* modelUpdateTimer;
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
51
mainwindow.ui
Normal file
51
mainwindow.ui
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1080</width>
|
||||
<height>740</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SwayMux</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>24</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
4
swaymux_en_US.ts
Normal file
4
swaymux_en_US.ts
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="en_US"></TS>
|
||||
|
71
tree/AbstractTreeNode.h
Normal file
71
tree/AbstractTreeNode.h
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// Created by grimmauld on 27.02.24.
|
||||
//
|
||||
|
||||
#ifndef SWAYMUX_ABSTRACTTREENODE_H
|
||||
#define SWAYMUX_ABSTRACTTREENODE_H
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <QVariant>
|
||||
|
||||
template<class T>
|
||||
class AbstractTreeNode {
|
||||
// static_assert(std::is_base_of<AbstractTreeNode, T>::value, "Derived not derived from BaseClass");
|
||||
public:
|
||||
std::vector<std::unique_ptr<T>> children{};
|
||||
|
||||
explicit AbstractTreeNode(T *parent = nullptr) : parent(parent) {}
|
||||
|
||||
[[nodiscard]] T *parentItem() { return parent; };
|
||||
|
||||
[[nodiscard]] size_t childCount() const { return children.size(); };
|
||||
|
||||
[[nodiscard]] T *child(int row) const { return row >= 0 && row < childCount() ? children.at(row).get() : nullptr; };
|
||||
|
||||
[[nodiscard]] virtual QVariant data(int column) const = 0;
|
||||
|
||||
[[nodiscard]] virtual QVariant headerData(int column) const = 0;
|
||||
|
||||
[[nodiscard]] virtual int columnCount() const = 0;
|
||||
|
||||
[[nodiscard]] int row() {
|
||||
if (parent == nullptr)
|
||||
return 0;
|
||||
const auto it = std::find_if(parent->children.cbegin(), parent->children.cend(),
|
||||
[this](const std::unique_ptr<T> &treeItem) {
|
||||
return treeItem.get() == this;
|
||||
});
|
||||
if (it != parent->children.cend())
|
||||
return std::distance(parent->children.cbegin(), it);
|
||||
Q_ASSERT(false); // should not happen
|
||||
return -1;
|
||||
}
|
||||
|
||||
void appendChild(std::unique_ptr<T> &child) { children.push_back(std::move(child)); }
|
||||
|
||||
[[nodiscard]] T* findChild(std::function<bool(const T &)> test) {
|
||||
|
||||
for (int i = 0; i < childCount(); ++i) {
|
||||
auto* c = child(i);
|
||||
if (test(*c))
|
||||
return c;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
void removeChild(int i) {
|
||||
if (i >= 0 && i < childCount()) {
|
||||
children.erase(std::next(children.begin(), i));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
T *parent;
|
||||
};
|
||||
|
||||
|
||||
#endif //SWAYMUX_ABSTRACTTREENODE_H
|
79
tree/PsTreeModel.cpp
Normal file
79
tree/PsTreeModel.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// Created by grimmauld on 25.02.24.
|
||||
//
|
||||
|
||||
#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<ProcessTreeNode *>(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<ProcessTreeNode *>(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<const ProcessTreeNode *>(parent.internalPointer())
|
||||
: &rootItem;
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
int PsTreeModel::columnCount(const QModelIndex &parent) const {
|
||||
if (parent.isValid())
|
||||
return static_cast<ProcessTreeNode *>(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<const ProcessTreeNode *>(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);
|
||||
|
||||
QList<QPersistentModelIndex> changedParents{};
|
||||
for(auto* node: changes) {
|
||||
changedParents.push_back(createIndex(node->row(), 0, node));
|
||||
}
|
||||
layoutAboutToBeChanged(changedParents);
|
||||
}
|
45
tree/PsTreeModel.h
Normal file
45
tree/PsTreeModel.h
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Created by grimmauld on 25.02.24.
|
||||
//
|
||||
|
||||
#ifndef SWAYMUX_PSTREEMODEL_H
|
||||
#define SWAYMUX_PSTREEMODEL_H
|
||||
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include "pstree.h"
|
||||
|
||||
class PsTreeModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_DISABLE_COPY_MOVE(PsTreeModel)
|
||||
|
||||
explicit PsTreeModel(QObject *parent = nullptr);
|
||||
|
||||
~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;
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
ProcessTreeNode rootItem;
|
||||
};
|
||||
|
||||
|
||||
#endif //SWAYMUX_PSTREEMODEL_H
|
151
tree/pstree.cpp
Normal file
151
tree/pstree.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
//
|
||||
// Created by grimmauld on 25.02.24.
|
||||
//
|
||||
|
||||
#include "pstree.h"
|
||||
#include "../util/StringUtil.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
const char *procFileDelim = ":";
|
||||
|
||||
void populateTree(ProcessTreeNode *node, std::map<pid_t, std::set<ProcessRecord>> &ppid_map, std::set<ProcessTreeNode*>& changedParents) {
|
||||
if (node == nullptr)
|
||||
return;
|
||||
|
||||
for (int i = (int) node->childCount() - 1; i >= 0; i--) { // backwards to avoid changing stuff that might change later
|
||||
std::set<ProcessRecord> children = ppid_map[node->proc.ppid];
|
||||
auto* c = node->child(i);
|
||||
if (c->proc.pid == 0)
|
||||
continue;
|
||||
|
||||
if (children.find(c->proc) == children.end()) {
|
||||
// node->removeChild(i); // fixme: segfaults
|
||||
changedParents.insert(node);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &child: ppid_map[node->proc.pid]) {
|
||||
// check child exists, if so no need to update
|
||||
auto* existingChildNode = node->findChild(ProcessTreeNode::matchingRecord(child));
|
||||
if (existingChildNode != nullptr) {
|
||||
populateTree(existingChildNode, ppid_map, changedParents);
|
||||
continue;
|
||||
}
|
||||
|
||||
// else add the new child node, keep track in notifier
|
||||
changedParents.insert(node);
|
||||
auto childNode = std::make_unique<ProcessTreeNode>(child, node);
|
||||
populateTree(childNode.get(), ppid_map, changedParents);
|
||||
node->appendChild(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessTreeNode get_process_records() {
|
||||
ProcessTreeNode root = ProcessTreeNode(); // rootPrc
|
||||
update_process_records(&root);
|
||||
return root;
|
||||
}
|
||||
|
||||
std::set<ProcessTreeNode*> update_process_records(ProcessTreeNode *node) {
|
||||
std::map<pid_t, ProcessRecord> pid_map;
|
||||
std::map<pid_t, std::set<ProcessRecord>> ppid_map;
|
||||
std::set<ProcessTreeNode*> changedParents{};
|
||||
|
||||
const auto proc_fs = fs::path(PROCFS_ROOT);
|
||||
auto content = fs::directory_iterator(proc_fs);
|
||||
for (const auto &proc_dir: content) {
|
||||
if (fs::is_directory(proc_dir)) {
|
||||
insert_process_record(proc_dir.path() / "status", pid_map);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t rootPid = INT32_MAX;
|
||||
for (auto &pair: pid_map) {
|
||||
auto proc = pair.second;
|
||||
if (proc.pid < rootPid) {
|
||||
rootPid = proc.pid;
|
||||
}
|
||||
ppid_map[proc.ppid].insert(proc);
|
||||
}
|
||||
|
||||
// auto rootPrc = pid_map[rootPid];
|
||||
// ProcessTreeNode root = ProcessTreeNode(); // rootPrc
|
||||
populateTree(node, ppid_map, changedParents);
|
||||
return changedParents;
|
||||
}
|
||||
|
||||
void insert_process_record(const std::filesystem::path &status_path, std::map<pid_t, ProcessRecord> &buff) {
|
||||
if (!fs::exists(status_path) || !fs::is_regular_file(status_path))
|
||||
return;
|
||||
|
||||
std::string proc_name;
|
||||
pid_t pid, ppid;
|
||||
bool foundPid, foundPPid, foundName;
|
||||
|
||||
auto reader = std::ifstream{status_path, std::ios::in};
|
||||
std::string line;
|
||||
while (std::getline(reader, line)) {
|
||||
auto data = line.data();
|
||||
auto name = strtok(data, procFileDelim);
|
||||
auto val = strtok(nullptr, procFileDelim);
|
||||
if (name == nullptr || val == nullptr)
|
||||
continue;
|
||||
|
||||
if (strcmp(name, "Name") == 0) {
|
||||
proc_name = std::string(val);
|
||||
trim(proc_name);
|
||||
foundName = true;
|
||||
} else if (strcmp(name, "Pid") == 0) {
|
||||
pid = std::stoi(val);
|
||||
foundPid = true;
|
||||
} else if (strcmp(name, "PPid") == 0) {
|
||||
ppid = std::stoi(val);
|
||||
foundPPid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPPid && foundName && foundPid) {
|
||||
buff.insert({pid, ProcessRecord(proc_name, pid, ppid)});
|
||||
}
|
||||
}
|
||||
|
||||
void printTree(const ProcessTreeNode &node, const std::string &prefix) {
|
||||
std::cout << prefix << node.proc.name << ": " << node.proc.pid << "\n";
|
||||
for (const auto &child: node.children) {
|
||||
printTree(*child, prefix + "\t");
|
||||
}
|
||||
}
|
||||
|
||||
void printTree() {
|
||||
printTree(get_process_records(), "");
|
||||
}
|
||||
|
||||
QVariant ProcessTreeNode::data(int column) const {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return QString::fromStdString(std::to_string(proc.pid));
|
||||
case 1:
|
||||
return QString::fromStdString(proc.name);
|
||||
default:
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ProcessTreeNode::headerData(int column) const {
|
||||
switch (column) {
|
||||
case 0:
|
||||
return "PID";
|
||||
case 1:
|
||||
return "Process Name";
|
||||
default:
|
||||
return QVariant{};
|
||||
}
|
||||
}
|
76
tree/pstree.h
Normal file
76
tree/pstree.h
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// Created by grimmauld on 25.02.24.
|
||||
//
|
||||
|
||||
#ifndef SWAYMUX_PSTREE_H
|
||||
#define SWAYMUX_PSTREE_H
|
||||
|
||||
#define PROCFS_ROOT "/proc"
|
||||
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <QVariant>
|
||||
#include "AbstractTreeNode.h"
|
||||
|
||||
void printTree();
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class ProcessRecord {
|
||||
public:
|
||||
const std::string name;
|
||||
const pid_t pid;
|
||||
const pid_t ppid;
|
||||
|
||||
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) {}
|
||||
|
||||
[[nodiscard]] bool operator<(const ProcessRecord &other) const {
|
||||
return this->pid < other.pid;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const ProcessRecord &other) const {
|
||||
return this->pid == other.pid && this->ppid == other.ppid && this->name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ProcessTreeNode : public AbstractTreeNode<ProcessTreeNode> {
|
||||
public:
|
||||
explicit ProcessTreeNode(ProcessTreeNode *parent = nullptr) : proc(ProcessRecord()), AbstractTreeNode(parent) {}
|
||||
|
||||
const ProcessRecord proc;
|
||||
|
||||
explicit ProcessTreeNode(ProcessRecord proc, ProcessTreeNode *parent = nullptr) : proc(std::move(proc)),
|
||||
AbstractTreeNode(parent) {}
|
||||
|
||||
[[nodiscard]] bool operator<(const ProcessTreeNode &other) const {
|
||||
return this->proc < other.proc;
|
||||
}
|
||||
|
||||
[[nodiscard]] QVariant data(int column) const override;
|
||||
|
||||
[[nodiscard]] QVariant headerData(int column) const override;
|
||||
|
||||
[[nodiscard]] int columnCount() const override { return 2; };
|
||||
|
||||
std::function<bool(const ProcessTreeNode &)> static matchingRecord(const ProcessRecord &other) {
|
||||
return [&other](const ProcessTreeNode &node) {
|
||||
return node.proc == other;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ProcessTreeNode get_process_records();
|
||||
|
||||
void insert_process_record(const std::filesystem::path &status_path, std::map<pid_t, ProcessRecord> &buff);
|
||||
|
||||
std::set<ProcessTreeNode*> update_process_records(ProcessTreeNode *node);
|
||||
|
||||
#endif //SWAYMUX_PSTREE_H
|
13
util/StringUtil.h
Normal file
13
util/StringUtil.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by grimmauld on 25.02.24.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
inline void trim(std::string & s) {
|
||||
s.erase(std::remove_if(s.begin(), s.end(), ::isspace),
|
||||
s.end());
|
||||
}
|
Loading…
Reference in New Issue
Block a user