summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorVitaly Wool <vitaly.wool@konsulko.com>2018-11-27 08:49:32 +0100
committerVitaly Wool <vitaly.wool@konsulko.com>2018-11-28 16:51:21 +0100
commitf52f93fa42ffc213653e4772acedfcf849707ce8 (patch)
tree409bb47397fd5fb104cc53d2df868898f0eef714 /app
parent49364e7ea0df798a48c7b671821f63401b6c7798 (diff)
Initial commit
Add Task Manager GUI. Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.com> Change-Id: I56d9a34df38fb441b598d150e88c905db95346aa
Diffstat (limited to 'app')
-rw-r--r--app/app.pri16
-rw-r--r--app/app.pro14
-rw-r--r--app/main.cpp88
-rw-r--r--app/main.qml92
-rw-r--r--app/procinfo.cpp75
-rw-r--r--app/procinfo.h35
-rw-r--r--app/qml.qrc5
-rw-r--r--app/taskmanager.cpp103
-rw-r--r--app/taskmanager.h40
9 files changed, 468 insertions, 0 deletions
diff --git a/app/app.pri b/app/app.pri
new file mode 100644
index 0000000..9b72203
--- /dev/null
+++ b/app/app.pri
@@ -0,0 +1,16 @@
+TEMPLATE = app
+QMAKE_LFLAGS += "-Wl,--hash-style=gnu -Wl,--as-needed"
+
+load(configure)
+qtCompileTest(libhomescreen)
+
+CONFIG += link_pkgconfig
+PKGCONFIG += libhomescreen qlibwindowmanager qtappfw
+
+config_libhomescreen {
+ CONFIG += link_pkgconfig
+ PKGCONFIG += homescreen
+ DEFINES += HAVE_LIBHOMESCREEN
+}
+
+DESTDIR = $${OUT_PWD}/../package/root/bin
diff --git a/app/app.pro b/app/app.pro
new file mode 100644
index 0000000..345fc5b
--- /dev/null
+++ b/app/app.pro
@@ -0,0 +1,14 @@
+TARGET = taskmanager
+
+QT = quickcontrols2 websockets
+
+SOURCES += \
+ main.cpp \
+ taskmanager.cpp \
+ procinfo.cpp
+
+HEADERS = taskmanager.h
+
+RESOURCES += qml.qrc
+
+include(app.pri)
diff --git a/app/main.cpp b/app/main.cpp
new file mode 100644
index 0000000..c4d4bba
--- /dev/null
+++ b/app/main.cpp
@@ -0,0 +1,88 @@
+#include <QtCore/QDebug>
+#include <QtCore/QCommandLineParser>
+#include <QtCore/QUrlQuery>
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlApplicationEngine>
+#include <QtQml/QQmlContext>
+#include <QtQml/qqml.h>
+#include <QQuickWindow>
+#include <QQmlApplicationEngine>
+#include <libhomescreen.hpp>
+#include <qlibwindowmanager.h>
+#include <taskmanager.h>
+
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+ QString graphic_role = QString("utility");
+ QString myname = QString("TaskManager");
+
+ QGuiApplication app(argc, argv);
+
+ QCommandLineParser parser;
+ parser.addPositionalArgument("port", app.translate("main", "port for binding"));
+ parser.addPositionalArgument("secret", app.translate("main", "secret for binding"));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.process(app);
+ QStringList positionalArguments = parser.positionalArguments();
+
+ qmlRegisterType<TaskManager>("TaskManager", 1, 0, "TaskManager");
+
+ QQmlApplicationEngine engine;
+ if (positionalArguments.length() != 2) {
+ qDebug() << "[ERROR] No port and token specified!";
+ return -1;
+ }
+
+ int port = positionalArguments.takeFirst().toInt();
+ QString secret = positionalArguments.takeFirst();
+ QUrl bindingAddress;
+ bindingAddress.setScheme(QStringLiteral("ws"));
+ bindingAddress.setHost(QStringLiteral("localhost"));
+ bindingAddress.setPort(port);
+ bindingAddress.setPath(QStringLiteral("/api"));
+ QUrlQuery query;
+ query.addQueryItem(QStringLiteral("token"), secret);
+ bindingAddress.setQuery(query);
+ QQmlContext *context = engine.rootContext();
+ context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress);
+ qDebug() << "Connect to: " << bindingAddress;
+
+ std::string token = secret.toStdString();
+ LibHomeScreen* hs = new LibHomeScreen();
+ QLibWindowmanager* qwm = new QLibWindowmanager();
+
+ // WindowManager
+ if(qwm->init(port,secret) != 0) {
+ exit(EXIT_FAILURE);
+ }
+ AGLScreenInfo screenInfo(qwm->get_scale_factor());
+ // Request a surface as described in layers.json windowmanager’s file
+ if (qwm->requestSurface(graphic_role) != 0) {
+ exit(EXIT_FAILURE);
+ }
+ // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs
+ qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, &graphic_role](json_object *object) {
+ fprintf(stderr, "Surface got syncDraw!\n");
+ qwm->endDraw(graphic_role);
+ });
+
+ // HomeScreen
+ hs->init(port, token.c_str());
+ // Set the event handler for Event_TapShortcut which will activate the surface for windowmanager
+ hs->set_event_handler(LibHomeScreen::Event_TapShortcut, [qwm, &graphic_role](json_object *object){
+ qDebug("Surface %s got tapShortcut\n", graphic_role.toStdString().c_str());
+ qwm->activateWindow(graphic_role);
+ });
+
+ context->setContextProperty(QStringLiteral("screenInfo"), &screenInfo);
+
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+ QObject *root = engine.rootObjects().first();
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(root);
+ QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateWindow()
+ ));
+ return app.exec();
+}
diff --git a/app/main.qml b/app/main.qml
new file mode 100644
index 0000000..796ec52
--- /dev/null
+++ b/app/main.qml
@@ -0,0 +1,92 @@
+import QtQuick 2.4
+import QtQuick.Window 2.2
+import QtQuick.Controls 1.4
+import TaskManager 1.0
+
+Window {
+ id: root
+ visible: true
+ width: 745
+ height: 480
+
+ TaskManager {
+ id: taskmng
+
+ onUpdateProcess: {
+ var index = findId(tid_);
+ libraryModel.set(index, {"cmd": cmd_, "tid": tid_, "user": euid_, "system_cpu": scpu_,
+ "user_cpu": ucpu_, "resident_memory": resident_memory_, "state": state_});
+ }
+
+ onAddProcess: {
+ libraryModel.append({"cmd": cmd_, "tid": tid_, "user": euid_, "system_cpu": scpu_,
+ "user_cpu": ucpu_, "resident_memory": resident_memory_, "state": state_});
+ }
+
+ onRemoveProcess: {
+ var index = findId(tid_);
+ libraryModel.remove(index);
+ }
+
+ function findId(tid) {
+ for(var i = 0; i < libraryModel.count; i++) {
+ if(tid == libraryModel.get(i).tid) {
+ return i;
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ taskmng.open(bindingAddress);
+ }
+ }
+
+ ListModel {
+ id: libraryModel
+ }
+
+
+
+ TableView {
+ width: root.width
+ height: root.height
+
+ TableViewColumn {
+ role: "cmd"
+ title: "Process"
+ width: 150
+ }
+ TableViewColumn {
+ role: "tid"
+ title: "ID"
+ width: 80
+ }
+ TableViewColumn {
+ role: "user"
+ title: "User"
+ width: 80
+ }
+ TableViewColumn {
+ role: "system_cpu"
+ title: "System %"
+ width: 100
+ }
+ TableViewColumn {
+ role: "user_cpu"
+ title: "User %"
+ width: 100
+ }
+ TableViewColumn {
+ role: "resident_memory"
+ title: "Memory"
+ width: 100
+ }
+ TableViewColumn {
+ role: "state"
+ title: "State"
+ width: 90
+ }
+ model: libraryModel
+ }
+
+}
diff --git a/app/procinfo.cpp b/app/procinfo.cpp
new file mode 100644
index 0000000..76a0df3
--- /dev/null
+++ b/app/procinfo.cpp
@@ -0,0 +1,75 @@
+#include "procinfo.h"
+#include <QJsonObject>
+#include <QString>
+
+ProcInfo::ProcInfo(const QJsonObject& obj)
+{
+ m_cmd = obj["cmd"].toString();
+ m_tid = obj["tid"].toInt();
+ m_euid = obj["euid"].toInt();
+ m_scpu = obj["scpu"].toDouble();
+ m_ucpu = obj["ucpu"].toDouble();
+ m_resident_memory = obj["resident_mem"].toDouble();
+ m_state = obj["state"].toString();
+}
+
+bool ProcInfo::operator==(const ProcInfo& o)
+{
+ // TODO: re-implement
+ if (&o == this)
+ return true;
+ return m_tid == o.m_tid;
+}
+
+QString ProcInfo::cmd() const
+{
+ return m_cmd;
+}
+
+int ProcInfo::tid() const
+{
+ return m_tid;
+}
+
+int ProcInfo::euid() const
+{
+ return m_euid;
+}
+
+double ProcInfo::scpu() const
+{
+ return m_scpu;
+}
+
+double ProcInfo::ucpu() const
+{
+ return m_ucpu;
+}
+
+double ProcInfo::resident_memory() const
+{
+ return m_resident_memory;
+}
+
+QString ProcInfo::state() const
+{
+ return m_state;
+}
+
+/*
+ * TODO: either it's a member operator that take one parameter (see above), either it's a global function which take two parameters.
+bool operator==(const ProcInfo &obj) {
+ ProcInfo obj2(jobj);
+ if(this.m_cmd == obj.m_cmd &&
+ this.m_tid == obj.m_tid &&
+ this.m_euid == obj.m_euid &&
+ this.m_scpu == obj.m_scpu &&
+ this.m_ucpu == obj.m_ucpu &&
+ this.m_resident_memory == obj.m_resident_memory &&
+ this.m_state == obj.m_state) {
+ return true;
+ } else {
+ return false;
+ }
+}
+*/
diff --git a/app/procinfo.h b/app/procinfo.h
new file mode 100644
index 0000000..fdde7d7
--- /dev/null
+++ b/app/procinfo.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <QString>
+#include <QJsonObject>
+
+class ProcInfo
+{
+public:
+ QString cmd() const;
+ int tid() const;
+ int euid() const;
+ double scpu() const;
+ double ucpu() const;
+ double resident_memory() const;
+ QString state() const;
+
+ explicit ProcInfo() = default;
+ explicit ProcInfo(const ProcInfo&) = default;
+ explicit ProcInfo(ProcInfo&&) = default;
+ ~ProcInfo() = default;
+
+ explicit ProcInfo(const QJsonObject& obj);
+
+ ProcInfo& operator=(const ProcInfo&) = default;
+ bool operator==(const ProcInfo& o);
+
+private:
+ QString m_cmd;
+ int m_tid;
+ int m_euid;
+ double m_scpu;
+ double m_ucpu;
+ double m_resident_memory;
+ QString m_state;
+};
diff --git a/app/qml.qrc b/app/qml.qrc
new file mode 100644
index 0000000..5f6483a
--- /dev/null
+++ b/app/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/app/taskmanager.cpp b/app/taskmanager.cpp
new file mode 100644
index 0000000..1b913d0
--- /dev/null
+++ b/app/taskmanager.cpp
@@ -0,0 +1,103 @@
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QtDebug>
+#include <QString>
+#include <unistd.h>
+#include <iostream>
+#include <QtCore>
+#include "taskmanager.h"
+
+TaskManager::TaskManager(QObject* parent) : QObject(parent) {
+ connect(&m_socket, SIGNAL(textMessageReceived(QString)), this, SLOT(onSocketTextReceived(QString)));
+}
+
+void TaskManager::open(const QUrl &bindingAddress) {
+ m_socket.open(bindingAddress);
+ timer = new QTimer();
+ connect(timer, SIGNAL(timeout()), this, SLOT(callService()));
+ timer->start(3000);
+}
+
+void TaskManager::callService() {
+ QJsonArray msg;
+ msg.append(2); // Call
+ msg.append(QString::number(m_nextCallId));
+ msg.append(QString("taskmanager/get_process_list"));
+ msg.append(QJsonValue());
+ m_nextCallId++;
+
+ QJsonDocument value;
+ value.setArray(msg);
+
+ m_socket.sendTextMessage(value.toJson(QJsonDocument::Compact));
+}
+
+void TaskManager::onSocketTextReceived(QString msg)
+{
+
+ QJsonDocument doc = QJsonDocument::fromJson(msg.toUtf8());
+ QJsonArray arr = doc.array();
+
+ switch(arr[0].toInt()) {
+ case 3: // RetOK
+ case 4: // RetErr
+ ProcessResponse(arr[0].toInt() == 3, arr[2]);
+ break;
+ }
+}
+
+void TaskManager::ProcessResponse(bool r, const QJsonValue& val)
+{
+ std::vector<ProcInfo> procs;
+
+ if (r) {
+ QJsonObject ret_val = val.toObject();
+ QJsonObject response = ret_val["response"].toObject();
+ QJsonArray processes = response["processes"].toArray();
+
+ for(auto it = processes.constBegin(); it != processes.constEnd(); ++it)
+ {
+ ProcInfo Proc(it->toObject());
+ procs.push_back(Proc);
+ }
+ }
+
+ int flag;
+ if(m_procinfos.empty()){
+ for(auto it = procs.begin(); it != procs.end(); ++it){ // if m_procinfos is empty then this is first call
+ emit addProcess(it->cmd(), it->tid(), it->euid(), it->scpu(), it->ucpu(), it->resident_memory(), it->state());
+ }
+ } else {
+ for(auto it = procs.begin(); it != procs.end(); ++it){ // loop through procs, it = procs element (ProcInfo obj)
+ flag = 0;
+ for(auto it2 = m_procinfos.begin(); it2 != m_procinfos.end(); ++it2){ // loop through m_procinfos, it2 m_procinfos element (ProcInfo obj)
+ // if(*it == *it2){ // if the same ID exists in both vectors
+ if(it->tid() == it2->tid()){
+ if(it->cmd() == it2->cmd()){ // if the name is still the same
+ if(!(it == it2)){ // if the same process has new data
+ emit updateProcess(it->cmd(), it->tid(), it->euid(), it->scpu(), it->ucpu(), it->resident_memory(), it->state());
+ m_procinfos.erase(it2);
+ flag = 1;
+ break;
+ }
+ } else { // process in m_procinfos has died and a new one has its ID
+ qDebug() << "The process ID has been reused for another process";
+ emit updateProcess(it->cmd(), it->tid(), it->euid(), it->scpu(), it->ucpu(), it->resident_memory(), it->state());
+ m_procinfos.erase(it2);
+ flag = 1;
+ }
+ }
+ }
+ if(flag == 0){ // if no ID was found in old vector; that means it's a new process
+ qDebug() << it->cmd() << " process has been added";
+ emit addProcess(it->cmd(), it->tid(), it->euid(), it->scpu(), it->ucpu(), it->resident_memory(), it->state());
+ }
+ }
+ for(auto it2 = m_procinfos.begin(); it2 != m_procinfos.end(); ++it2){ // remaining processes in m_procinfos are all dead
+ qDebug() << "Dead processes are removed";
+ qDebug() << it2->cmd();
+ emit removeProcess(it2->tid());
+ }
+ }
+ m_procinfos = procs;
+}
diff --git a/app/taskmanager.h b/app/taskmanager.h
new file mode 100644
index 0000000..08d0027
--- /dev/null
+++ b/app/taskmanager.h
@@ -0,0 +1,40 @@
+#include <QObject>
+#include <QString>
+#include <QSharedPointer>
+#include <QStringList>
+#include <QVector>
+#include <QtCore>
+#include <QWebSocket>
+#include "procinfo.h"
+
+#ifndef TASKMANAGER_H
+#define TASKMANAGER_H
+
+class TaskManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit TaskManager(QObject* parent = nullptr);
+
+ Q_INVOKABLE void open(const QUrl& url);
+ QTimer *timer;
+
+signals:
+ void updateProcess(const QString& cmd_, int tid_, int euid_, double scpu_, double ucpu_, double resident_memory_, const QString& state_);
+ void addProcess(const QString& cmd_, int tid_, int euid_, double scpu_, double ucpu_, double resident_memory_, const QString& state_);
+ void removeProcess(int tid_);
+
+private slots:
+ void callService();
+ void onSocketTextReceived(QString msg);
+
+private:
+ QWebSocket m_socket;
+ int m_nextCallId;
+ std::vector<ProcInfo> m_procinfos;
+
+ void ProcessResponse(bool r, const QJsonValue &val);
+};
+
+#endif // TASKMANAGER_H