-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
714 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,5 @@ install_manifest.txt | |
compile_commands.json | ||
CTestTestfile.cmake | ||
_deps | ||
|
||
build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
cmake_minimum_required(VERSION 3.5) | ||
|
||
project(tail-tray 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) | ||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) | ||
|
||
set(PROJECT_SOURCES | ||
main.cpp | ||
MainWindow.cpp | ||
MainWindow.h | ||
MainWindow.ui | ||
resources.qrc | ||
models.h | ||
TrayMenuManager.cpp | ||
TrayMenuManager.h | ||
) | ||
|
||
qt_add_executable(tail-tray | ||
MANUAL_FINALIZATION | ||
${PROJECT_SOURCES} | ||
TailRunner.h TailRunner.cpp | ||
) | ||
|
||
target_link_libraries(tail-tray 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.tail-tray) | ||
endif() | ||
set_target_properties(tail-tray 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 tail-tray | ||
BUNDLE DESTINATION . | ||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} | ||
) | ||
|
||
if(QT_VERSION_MAJOR EQUAL 6) | ||
qt_finalize_executable(tail-tray) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#include "MainWindow.h" | ||
#include "./ui_MainWindow.h" | ||
|
||
MainWindow::MainWindow(QWidget* parent) | ||
: QMainWindow(parent) | ||
, ui(new Ui::MainWindow) | ||
, pTrayManager(nullptr) | ||
, eCurrentState(TailState::NoAccount) | ||
, pCurrentExecution(nullptr) | ||
, pTailStatus(nullptr) | ||
, pStatusCheckTimer(nullptr) | ||
{ | ||
ui->setupUi(this); | ||
|
||
pStatusCheckTimer = new QTimer(this); | ||
connect(pStatusCheckTimer, &QTimer::timeout, this, [this]() { | ||
pCurrentExecution->checkStatus(); | ||
}); | ||
pStatusCheckTimer->setSingleShot(false); | ||
pStatusCheckTimer->start(1000 * 30); // 30sec interval | ||
|
||
pCurrentExecution = new TailRunner(this); | ||
connect(pCurrentExecution, &TailRunner::statusUpdated, this, &MainWindow::onTailStatusChanged); | ||
|
||
pTrayManager = new TrayMenuManager(pCurrentExecution, this); | ||
|
||
changeToState(TailState::NotLoggedIn); | ||
pCurrentExecution->checkStatus(); | ||
} | ||
|
||
MainWindow::~MainWindow() | ||
{ | ||
pStatusCheckTimer->stop(); | ||
delete pStatusCheckTimer; | ||
|
||
delete pCurrentExecution; | ||
delete pTailStatus; | ||
delete pTrayManager; | ||
delete ui; | ||
} | ||
|
||
TailState MainWindow::changeToState(TailState newState) | ||
{ | ||
auto retVal = eCurrentState; | ||
eCurrentState = newState; | ||
|
||
pTrayManager->stateChangedTo(newState, pTailStatus); | ||
|
||
return retVal; | ||
} | ||
|
||
void MainWindow::onTailStatusChanged(TailStatus* pNewStatus) | ||
{ | ||
delete pTailStatus; | ||
|
||
pTailStatus = pNewStatus; | ||
if (pTailStatus->user->id > 0) | ||
{ | ||
// Logged in | ||
if (pTailStatus->health.count() < 1) | ||
changeToState(TailState::Connected); | ||
else | ||
changeToState(TailState::NotConnected); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#ifndef MAINWINDOW_H | ||
#define MAINWINDOW_H | ||
|
||
#include <QMainWindow> | ||
#include <QSystemTrayIcon> | ||
#include <QMenu> | ||
#include <QTimer> | ||
|
||
#include "TailRunner.h" | ||
#include "TrayMenuManager.h" | ||
|
||
QT_BEGIN_NAMESPACE | ||
namespace Ui { | ||
class MainWindow; | ||
} | ||
QT_END_NAMESPACE | ||
|
||
class MainWindow : public QMainWindow | ||
{ | ||
Q_OBJECT | ||
|
||
public: | ||
MainWindow(QWidget* parent = nullptr); | ||
~MainWindow(); | ||
|
||
private: | ||
Ui::MainWindow* ui; | ||
|
||
TrayMenuManager* pTrayManager; | ||
|
||
TailState eCurrentState; | ||
TailRunner* pCurrentExecution; | ||
TailStatus* pTailStatus; | ||
QTimer* pStatusCheckTimer; | ||
|
||
|
||
private: | ||
// Switch to the new state and return the prev (old) state back to caller | ||
TailState changeToState(TailState newState); | ||
|
||
void onTailStatusChanged(TailStatus* pNewStatus); | ||
}; | ||
#endif // MAINWINDOW_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?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>800</width> | ||
<height>600</height> | ||
</rect> | ||
</property> | ||
<property name="windowTitle"> | ||
<string>MainWindow</string> | ||
</property> | ||
<property name="windowIcon"> | ||
<iconset resource="resources.qrc"> | ||
<normaloff>:/icons/tray-off.png</normaloff>:/icons/tray-off.png</iconset> | ||
</property> | ||
<widget class="QWidget" name="centralwidget"/> | ||
<widget class="QMenuBar" name="menubar"> | ||
<property name="geometry"> | ||
<rect> | ||
<x>0</x> | ||
<y>0</y> | ||
<width>800</width> | ||
<height>30</height> | ||
</rect> | ||
</property> | ||
</widget> | ||
<widget class="QStatusBar" name="statusbar"/> | ||
</widget> | ||
<resources> | ||
<include location="resources.qrc"/> | ||
</resources> | ||
<connections/> | ||
</ui> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
#include "TailRunner.h" | ||
|
||
#include <QProcess> | ||
#include <QDebug> | ||
#include <QByteArray> | ||
#include <QJsonDocument> | ||
#include <QJsonObject> | ||
|
||
TailRunner::TailRunner(QObject* parent) | ||
: QObject(parent) | ||
, pProcess(nullptr) | ||
, eCommand(Command::Status) { | ||
} | ||
|
||
TailRunner::~TailRunner() | ||
{ | ||
delete pProcess; | ||
} | ||
|
||
void TailRunner::checkStatus() | ||
{ | ||
eCommand = Command::Status; | ||
runCommand("status", QStringList(), true); | ||
} | ||
|
||
void TailRunner::start() | ||
{ | ||
// tailscale up --operator marcus --accept-routes --exit-node pelican | ||
eCommand = Command::Connect; | ||
QStringList args; | ||
args << "--operator" << "marcus"; | ||
args << "--accept-routes"; | ||
args << "--exit-node" << "pelican"; | ||
|
||
runCommand("up", args, true); | ||
} | ||
|
||
void TailRunner::stop() | ||
{ | ||
eCommand = Command::Disconnect; | ||
runCommand("down", QStringList()); | ||
} | ||
|
||
void TailRunner::runCommand(QString cmd, QStringList args, bool jsonResult) | ||
{ | ||
if (pProcess != nullptr) | ||
{ | ||
if (pProcess->state() == QProcess::Running) { | ||
assert(!"Process already running!"); | ||
return; | ||
} | ||
|
||
delete pProcess; | ||
} | ||
|
||
pProcess = new QProcess(this); | ||
connect(pProcess, &QProcess::errorOccurred, this, [this](QProcess::ProcessError err) { | ||
qDebug() << "Failed to run process: " << err; | ||
}); | ||
|
||
connect(pProcess, &QProcess::finished, this, [this](int exitCode, QProcess::ExitStatus status) { | ||
qDebug() << "Process exited with " << exitCode << " - " << status; | ||
|
||
// After we've invoked a command not status command we check for new status update | ||
if (eCommand != Command::Status) { | ||
checkStatus(); | ||
} | ||
}); | ||
|
||
connect(pProcess, &QProcess::readyReadStandardOutput, | ||
this, &TailRunner::onProcessCanReadStdOut); | ||
|
||
if (jsonResult) | ||
args << "--json"; | ||
|
||
args.insert(0, cmd); | ||
pProcess->start("tailscale", args); | ||
} | ||
|
||
void TailRunner::onProcessCanReadStdOut() { | ||
assert(pProcess != nullptr); | ||
|
||
auto data = pProcess->readAllStandardOutput(); | ||
|
||
// Parse the status object | ||
|
||
switch (eCommand) { | ||
case Command::Status: { | ||
QJsonParseError* parseError = nullptr; | ||
QJsonDocument doc = QJsonDocument::fromJson(data, parseError); | ||
|
||
if (parseError != nullptr) | ||
{ | ||
qDebug() << parseError->errorString(); | ||
return; | ||
} | ||
QJsonObject obj = doc.object(); | ||
parseStatusResponse(obj); | ||
break; | ||
} | ||
case Command::Connect: { | ||
break; | ||
} | ||
case Command::Disconnect: { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void TailRunner::parseStatusResponse(const QJsonObject& obj) { | ||
emit statusUpdated(TailStatus::parse(obj)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#ifndef TAILRUNNER_H | ||
#define TAILRUNNER_H | ||
|
||
#include <QObject> | ||
#include <QString> | ||
#include <QList> | ||
#include <QProcess> | ||
#include <QStringList> | ||
|
||
#include "models.h" | ||
|
||
class TailRunner : public QObject | ||
{ | ||
Q_OBJECT | ||
public: | ||
explicit TailRunner(QObject* parent = nullptr); | ||
virtual ~TailRunner(); | ||
|
||
void checkStatus(); | ||
|
||
void start(); | ||
void stop(); | ||
|
||
private: | ||
QProcess* pProcess; | ||
enum class Command { | ||
Connect, | ||
Disconnect, | ||
Status | ||
}; | ||
|
||
Command eCommand; | ||
|
||
signals: | ||
void statusUpdated(TailStatus* newStatus); | ||
|
||
private: | ||
void runCommand(QString cmd, QStringList args, bool jsonResult = false); | ||
void onProcessCanReadStdOut(); | ||
|
||
void parseStatusResponse(const QJsonObject& obj); | ||
}; | ||
|
||
#endif // TAILRUNNER_H |
Oops, something went wrong.