Allow launching syncthing-inotify
Provide a way to have multiple instances of the launcher option page controlling separate processes.
This commit is contained in:
parent
39f8bc36cf
commit
3c2ce3e82f
|
@ -16,7 +16,7 @@ void SyncthingProcess::restartSyncthing(const QString &cmd)
|
|||
if(state() == QProcess::Running) {
|
||||
m_cmd = cmd;
|
||||
// give Syncthing 5 seconds to terminate, otherwise kill it
|
||||
QTimer::singleShot(5000, this, SLOT(killToRestart()));
|
||||
QTimer::singleShot(5000, this, &SyncthingProcess::killToRestart);
|
||||
terminate();
|
||||
} else {
|
||||
startSyncthing(cmd);
|
||||
|
@ -34,6 +34,15 @@ void SyncthingProcess::startSyncthing(const QString &cmd)
|
|||
}
|
||||
}
|
||||
|
||||
void SyncthingProcess::stopSyncthing()
|
||||
{
|
||||
if(state() == QProcess::Running) {
|
||||
// give Syncthing 5 seconds to terminate, otherwise kill it
|
||||
QTimer::singleShot(5000, this, &SyncthingProcess::kill);
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingProcess::handleFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
Q_UNUSED(exitCode)
|
||||
|
|
|
@ -16,6 +16,7 @@ public:
|
|||
public Q_SLOTS:
|
||||
void restartSyncthing(const QString &cmd);
|
||||
void startSyncthing(const QString &cmd);
|
||||
void stopSyncthing();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
|
|
@ -54,18 +54,14 @@ int initSyncthingTray(bool windowed, bool waitForTray)
|
|||
QObject::connect(&service, &SyncthingService::errorOccurred, &handleSystemdServiceError);
|
||||
#endif
|
||||
if(windowed) {
|
||||
if(v.launcher.enabled) {
|
||||
syncthingProcess().startSyncthing(v.launcher.syncthingCmd());
|
||||
}
|
||||
v.launcher.autostart();
|
||||
auto *trayWidget = new TrayWidget;
|
||||
trayWidget->setAttribute(Qt::WA_DeleteOnClose);
|
||||
trayWidget->show();
|
||||
} else {
|
||||
#ifndef QT_NO_SYSTEMTRAYICON
|
||||
if(QSystemTrayIcon::isSystemTrayAvailable() || waitForTray) {
|
||||
if(v.launcher.enabled) {
|
||||
syncthingProcess().startSyncthing(v.launcher.syncthingCmd());
|
||||
}
|
||||
v.launcher.autostart();
|
||||
auto *trayIcon = new TrayIcon;
|
||||
trayIcon->show();
|
||||
if(v.firstLaunch) {
|
||||
|
@ -151,6 +147,7 @@ int runApplication(int argc, const char *const *argv)
|
|||
res = application.exec();
|
||||
}
|
||||
|
||||
Settings::Launcher::terminate();
|
||||
Settings::save();
|
||||
return res;
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "./settings.h"
|
||||
#include "../../connector/syncthingprocess.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
|
@ -15,19 +16,81 @@
|
|||
#include <QMessageBox>
|
||||
#include <QFile>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
using namespace Data;
|
||||
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
||||
using namespace MiscUtils;
|
||||
#endif
|
||||
|
||||
namespace std {
|
||||
|
||||
template <> struct hash<QString>
|
||||
{
|
||||
std::size_t operator()(const QString &str) const
|
||||
{
|
||||
return qHash(str);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
|
||||
/*!
|
||||
* \brief Contains the processes for launching extra tools.
|
||||
* \remarks Using std::unordered_map instead of QHash because SyncthingProcess can not be copied.
|
||||
*/
|
||||
static unordered_map<QString, SyncthingProcess> toolProcesses;
|
||||
|
||||
QString Launcher::syncthingCmd() const
|
||||
{
|
||||
return syncthingPath % QChar(' ') % syncthingArgs;
|
||||
}
|
||||
|
||||
QString Launcher::toolCmd(const QString &tool) const
|
||||
{
|
||||
const ToolParameter toolParams = tools.value(tool);
|
||||
if(toolParams.path.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
return toolParams.path % QChar(' ') % toolParams.args;
|
||||
}
|
||||
|
||||
SyncthingProcess &Launcher::toolProcess(const QString &tool)
|
||||
{
|
||||
return toolProcesses[tool];
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Starts all processes (Syncthing and tools) if autostart is enabled.
|
||||
*/
|
||||
void Launcher::autostart() const
|
||||
{
|
||||
if(enabled && !syncthingPath.isEmpty()) {
|
||||
syncthingProcess().startSyncthing(syncthingCmd());
|
||||
}
|
||||
for(auto i = tools.cbegin(), end = tools.cend(); i != end; ++i) {
|
||||
const ToolParameter &toolParams = i.value();
|
||||
if(toolParams.autostart && !toolParams.path.isEmpty()) {
|
||||
toolProcesses[i.key()].startSyncthing(toolParams.path % QChar(' ') % toolParams.args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::terminate()
|
||||
{
|
||||
syncthingProcess().stopSyncthing();
|
||||
for(auto &process : toolProcesses) {
|
||||
process.second.stopSyncthing();
|
||||
}
|
||||
syncthingProcess().waitForFinished();
|
||||
for(auto &process : toolProcesses) {
|
||||
process.second.waitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
Settings &values()
|
||||
{
|
||||
static Settings settings;
|
||||
|
@ -102,9 +165,22 @@ void restore()
|
|||
|
||||
settings.beginGroup(QStringLiteral("startup"));
|
||||
auto &launcher = v.launcher;
|
||||
launcher.enabled = settings.value(QStringLiteral("launchSynchting"), launcher.enabled).toBool();
|
||||
launcher.enabled = settings.value(QStringLiteral("syncthingAutostart"), launcher.enabled).toBool();
|
||||
launcher.syncthingPath = settings.value(QStringLiteral("syncthingPath"), launcher.syncthingPath).toString();
|
||||
launcher.syncthingArgs = settings.value(QStringLiteral("syncthingArgs"), launcher.syncthingArgs).toString();
|
||||
settings.beginGroup(QStringLiteral("tools"));
|
||||
for(const QString &tool : settings.childGroups()) {
|
||||
settings.beginGroup(tool);
|
||||
ToolParameter &toolParams = launcher.tools[tool];
|
||||
toolParams.autostart = settings.value(QStringLiteral("autostart"), toolParams.autostart).toBool();
|
||||
toolParams.path = settings.value(QStringLiteral("path"), toolParams.path).toString();
|
||||
toolParams.args = settings.value(QStringLiteral("args"), toolParams.args).toString();
|
||||
settings.endGroup();
|
||||
}
|
||||
for(auto i = launcher.tools.cbegin(), end = launcher.tools.cend(); i != end; ++i) {
|
||||
|
||||
}
|
||||
settings.endGroup();
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
auto &systemd = v.systemd;
|
||||
systemd.syncthingUnit = settings.value(QStringLiteral("syncthingUnit"), systemd.syncthingUnit).toString();
|
||||
|
@ -173,9 +249,19 @@ void save()
|
|||
|
||||
settings.beginGroup(QStringLiteral("startup"));
|
||||
const auto &launcher = v.launcher;
|
||||
settings.setValue(QStringLiteral("launchSynchting"), launcher.enabled);
|
||||
settings.setValue(QStringLiteral("syncthingAutostart"), launcher.enabled);
|
||||
settings.setValue(QStringLiteral("syncthingPath"), launcher.syncthingPath);
|
||||
settings.setValue(QStringLiteral("syncthingArgs"), launcher.syncthingArgs);
|
||||
settings.beginGroup(QStringLiteral("tools"));
|
||||
for(auto i = launcher.tools.cbegin(), end = launcher.tools.cend(); i != end; ++i) {
|
||||
const ToolParameter &toolParams = i.value();
|
||||
settings.beginGroup(i.key());
|
||||
settings.setValue(QStringLiteral("autostart"), toolParams.autostart);
|
||||
settings.setValue(QStringLiteral("path"), toolParams.path);
|
||||
settings.setValue(QStringLiteral("args"), toolParams.args);
|
||||
settings.endGroup();
|
||||
}
|
||||
settings.endGroup();
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
const auto &systemd = v.systemd;
|
||||
settings.setValue(QStringLiteral("syncthingUnit"), systemd.syncthingUnit);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QSize>
|
||||
#include <QFrame>
|
||||
#include <QTabWidget>
|
||||
#include <QHash>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -24,6 +25,10 @@ namespace Dialogs {
|
|||
class QtSettings;
|
||||
}
|
||||
|
||||
namespace Data {
|
||||
class SyncthingProcess;
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
|
||||
struct Connection
|
||||
|
@ -49,6 +54,13 @@ struct Appearance
|
|||
bool brightTextColors = false;
|
||||
};
|
||||
|
||||
struct ToolParameter
|
||||
{
|
||||
QString path;
|
||||
QString args;
|
||||
bool autostart = false;
|
||||
};
|
||||
|
||||
struct Launcher
|
||||
{
|
||||
bool enabled = false;
|
||||
|
@ -59,7 +71,12 @@ struct Launcher
|
|||
QStringLiteral("syncthing");
|
||||
#endif
|
||||
QString syncthingArgs;
|
||||
QHash<QString, ToolParameter> tools;
|
||||
QString syncthingCmd() const;
|
||||
QString toolCmd(const QString &tool) const;
|
||||
static Data::SyncthingProcess &toolProcess(const QString &tool);
|
||||
void autostart() const;
|
||||
static void terminate();
|
||||
};
|
||||
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
|
|
|
@ -473,9 +473,17 @@ void AutostartOptionPage::reset()
|
|||
// LauncherOptionPage
|
||||
LauncherOptionPage::LauncherOptionPage(QWidget *parentWidget) :
|
||||
LauncherOptionPageBase(parentWidget),
|
||||
m_process(syncthingProcess()),
|
||||
m_kill(false)
|
||||
{}
|
||||
|
||||
LauncherOptionPage::LauncherOptionPage(const QString &tool, QWidget *parentWidget) :
|
||||
LauncherOptionPageBase(parentWidget),
|
||||
m_process(Launcher::toolProcess(tool)),
|
||||
m_kill(false),
|
||||
m_tool(tool)
|
||||
{}
|
||||
|
||||
LauncherOptionPage::~LauncherOptionPage()
|
||||
{
|
||||
for(const QMetaObject::Connection &connection : m_connections) {
|
||||
|
@ -486,15 +494,24 @@ LauncherOptionPage::~LauncherOptionPage()
|
|||
QWidget *LauncherOptionPage::setupWidget()
|
||||
{
|
||||
auto *widget = LauncherOptionPageBase::setupWidget();
|
||||
// adjust labels to use name of additional tool instead of "Syncthing"
|
||||
if(!m_tool.isEmpty()) {
|
||||
widget->setWindowTitle(QCoreApplication::translate("QtGui::LauncherOptionPage", "%1-launcher").arg(m_tool));
|
||||
ui()->enabledCheckBox->setText(QCoreApplication::translate("QtGui::LauncherOptionPage", "Launch %1 when starting the tray icon").arg(m_tool));
|
||||
ui()->syncthingPathLabel->setText(QCoreApplication::translate("QtGui::LauncherOptionPage", "%1 executable").arg(m_tool));
|
||||
ui()->logLabel->setText(QCoreApplication::translate("QtGui::LauncherOptionPage", "%1 log (interleaved stdout/stderr)").arg(m_tool));
|
||||
}
|
||||
// setup other widgets
|
||||
ui()->syncthingPathSelection->provideCustomFileMode(QFileDialog::ExistingFile);
|
||||
ui()->logTextEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
m_connections << QObject::connect(&syncthingProcess(), &SyncthingProcess::readyRead, bind(&LauncherOptionPage::handleSyncthingReadyRead, this));
|
||||
m_connections << QObject::connect(&syncthingProcess(), static_cast<void(SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished), bind(&LauncherOptionPage::handleSyncthingExited, this, _1, _2));
|
||||
QObject::connect(ui()->launchNowPushButton, &QPushButton::clicked, bind(&LauncherOptionPage::launch, this));
|
||||
QObject::connect(ui()->stopPushButton, &QPushButton::clicked, bind(&LauncherOptionPage::stop, this));
|
||||
const bool running = syncthingProcess().state() != QProcess::NotRunning;
|
||||
const bool running = m_process.state() != QProcess::NotRunning;
|
||||
ui()->launchNowPushButton->setHidden(running);
|
||||
ui()->stopPushButton->setHidden(!running);
|
||||
// connect signals & slots
|
||||
m_connections << QObject::connect(&m_process, &SyncthingProcess::readyRead, bind(&LauncherOptionPage::handleSyncthingReadyRead, this));
|
||||
m_connections << QObject::connect(&m_process, static_cast<void(SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished), bind(&LauncherOptionPage::handleSyncthingExited, this, _1, _2));
|
||||
QObject::connect(ui()->launchNowPushButton, &QPushButton::clicked, bind(&LauncherOptionPage::launch, this));
|
||||
QObject::connect(ui()->stopPushButton, &QPushButton::clicked, bind(&LauncherOptionPage::stop, this));
|
||||
return widget;
|
||||
}
|
||||
|
||||
|
@ -502,9 +519,16 @@ bool LauncherOptionPage::apply()
|
|||
{
|
||||
if(hasBeenShown()) {
|
||||
auto &settings = values().launcher;
|
||||
settings.enabled = ui()->enabledCheckBox->isChecked();
|
||||
settings.syncthingPath = ui()->syncthingPathSelection->lineEdit()->text();
|
||||
settings.syncthingArgs = ui()->argumentsLineEdit->text();
|
||||
if(m_tool.isEmpty()) {
|
||||
settings.enabled = ui()->enabledCheckBox->isChecked();
|
||||
settings.syncthingPath = ui()->syncthingPathSelection->lineEdit()->text();
|
||||
settings.syncthingArgs = ui()->argumentsLineEdit->text();
|
||||
} else {
|
||||
ToolParameter ¶ms = settings.tools[m_tool];
|
||||
params.autostart = ui()->enabledCheckBox->isChecked();
|
||||
params.path = ui()->syncthingPathSelection->lineEdit()->text();
|
||||
params.args = ui()->argumentsLineEdit->text();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -513,9 +537,16 @@ void LauncherOptionPage::reset()
|
|||
{
|
||||
if(hasBeenShown()) {
|
||||
const auto &settings = values().launcher;
|
||||
ui()->enabledCheckBox->setChecked(settings.enabled);
|
||||
ui()->syncthingPathSelection->lineEdit()->setText(settings.syncthingPath);
|
||||
ui()->argumentsLineEdit->setText(settings.syncthingArgs);
|
||||
if(m_tool.isEmpty()) {
|
||||
ui()->enabledCheckBox->setChecked(settings.enabled);
|
||||
ui()->syncthingPathSelection->lineEdit()->setText(settings.syncthingPath);
|
||||
ui()->argumentsLineEdit->setText(settings.syncthingArgs);
|
||||
} else {
|
||||
const ToolParameter params = settings.tools.value(m_tool);
|
||||
ui()->enabledCheckBox->setChecked(params.autostart);
|
||||
ui()->syncthingPathSelection->lineEdit()->setText(params.path);
|
||||
ui()->argumentsLineEdit->setText(params.args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -524,7 +555,7 @@ void LauncherOptionPage::handleSyncthingReadyRead()
|
|||
if(hasBeenShown()) {
|
||||
QTextCursor cursor = ui()->logTextEdit->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.insertText(QString::fromLocal8Bit(syncthingProcess().readAll()));
|
||||
cursor.insertText(QString::fromLocal8Bit(m_process.readAll()));
|
||||
if(ui()->ensureCursorVisibleCheckBox->isChecked()) {
|
||||
ui()->logTextEdit->ensureCursorVisible();
|
||||
}
|
||||
|
@ -538,10 +569,10 @@ void LauncherOptionPage::handleSyncthingExited(int exitCode, QProcess::ExitStatu
|
|||
cursor.movePosition(QTextCursor::End);
|
||||
switch(exitStatus) {
|
||||
case QProcess::NormalExit:
|
||||
cursor.insertText(QCoreApplication::translate("QtGui::LauncherOptionPage", "Syncthing exited with exit code %1\n").arg(exitCode));
|
||||
cursor.insertText(QCoreApplication::translate("QtGui::LauncherOptionPage", "%1 exited with exit code %2\n").arg(m_tool.isEmpty() ? QStringLiteral("Syncthing") : m_tool, QString::number(exitCode)));
|
||||
break;
|
||||
case QProcess::CrashExit:
|
||||
cursor.insertText(QCoreApplication::translate("QtGui::LauncherOptionPage", "Syncthing crashed with exit code %1\n").arg(exitCode));
|
||||
cursor.insertText(QCoreApplication::translate("QtGui::LauncherOptionPage", "%1 crashed with exit code %2\n").arg(m_tool.isEmpty() ? QStringLiteral("Syncthing") : m_tool, QString::number(exitCode)));
|
||||
break;
|
||||
}
|
||||
ui()->stopPushButton->hide();
|
||||
|
@ -553,11 +584,15 @@ void LauncherOptionPage::launch()
|
|||
{
|
||||
if(hasBeenShown()) {
|
||||
apply();
|
||||
if(syncthingProcess().state() == QProcess::NotRunning) {
|
||||
if(m_process.state() == QProcess::NotRunning) {
|
||||
ui()->launchNowPushButton->hide();
|
||||
ui()->stopPushButton->show();
|
||||
m_kill = false;
|
||||
syncthingProcess().startSyncthing(values().launcher.syncthingCmd());
|
||||
if(m_tool.isEmpty()) {
|
||||
m_process.startSyncthing(values().launcher.syncthingCmd());
|
||||
} else {
|
||||
m_process.startSyncthing(values().launcher.toolCmd(m_tool));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -565,12 +600,12 @@ void LauncherOptionPage::launch()
|
|||
void LauncherOptionPage::stop()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
if(syncthingProcess().state() != QProcess::NotRunning) {
|
||||
if(m_process.state() != QProcess::NotRunning) {
|
||||
if(m_kill) {
|
||||
syncthingProcess().kill();
|
||||
m_process.kill();
|
||||
} else {
|
||||
m_kill = true;
|
||||
syncthingProcess().terminate();
|
||||
m_process.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -742,7 +777,7 @@ SettingsDialog::SettingsDialog(Data::SyncthingConnection *connection, QWidget *p
|
|||
|
||||
category = new OptionCategory(this);
|
||||
category->setDisplayName(tr("Startup"));
|
||||
category->assignPages(QList<Dialogs::OptionPage *>() << new AutostartOptionPage << new LauncherOptionPage
|
||||
category->assignPages(QList<Dialogs::OptionPage *>() << new AutostartOptionPage << new LauncherOptionPage << new LauncherOptionPage(QStringLiteral("Inotify"))
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
<< new SystemdOptionPage
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@ class DateTime;
|
|||
namespace Data {
|
||||
class SyncthingConnection;
|
||||
class SyncthingService;
|
||||
class SyncthingProcess;
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
@ -46,15 +47,20 @@ DECLARE_UI_FILE_BASED_OPTION_PAGE(AppearanceOptionPage)
|
|||
|
||||
DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_SETUP(AutostartOptionPage)
|
||||
|
||||
BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(LauncherOptionPage)
|
||||
BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(LauncherOptionPage)
|
||||
public:
|
||||
LauncherOptionPage(QWidget *parentWidget = nullptr);
|
||||
LauncherOptionPage(const QString &tool, QWidget *parentWidget = nullptr);
|
||||
private:
|
||||
DECLARE_SETUP_WIDGETS
|
||||
void handleSyncthingReadyRead();
|
||||
void handleSyncthingExited(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void launch();
|
||||
void stop();
|
||||
Data::SyncthingProcess &m_process;
|
||||
QList<QMetaObject::Connection> m_connections;
|
||||
bool m_kill;
|
||||
QString m_tool;
|
||||
END_DECLARE_OPTION_PAGE
|
||||
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
|
|
Loading…
Reference in New Issue