deskflow/src/apps/deskflow-gui/deskflow-gui.cpp
sithlord48 1ace03d4b5 feat: add help and version to gui,
move no-reset to the QCommandLineProcessor
2025-04-06 06:41:25 -07:00

168 lines
5.3 KiB
C++

/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2024 Chris Rizzitello <sithord48@gmail.com>
* SPDX-FileCopyrightText: (C) 2012 - 2024 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2008 Volker Lanz <vl@fidra.de>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "common/Constants.h"
#include "common/UrlConstants.h"
#include "gui/Diagnostic.h"
#include "gui/DotEnv.h"
#include "gui/Logger.h"
#include "gui/MainWindow.h"
#include "gui/Messages.h"
#include "gui/StyleUtils.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QGuiApplication>
#include <QLocalSocket>
#include <QMessageBox>
#include <QSharedMemory>
#if defined(Q_OS_MAC)
#include <Carbon/Carbon.h>
#include <cstdlib>
#endif
#if defined(Q_OS_UNIX) && defined(QT_DEBUG)
#include <QLoggingCategory>
#endif
using namespace deskflow::gui;
#if defined(Q_OS_MAC)
bool checkMacAssistiveDevices();
#endif
int main(int argc, char *argv[])
{
#if defined(Q_OS_UNIX) && defined(QT_DEBUG)
// Fixes Fedora bug where qDebug() messages aren't printed.
QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true\nqt.*=false"));
#endif
QCoreApplication::setApplicationName(kAppName);
QCoreApplication::setOrganizationName(kAppName);
QCoreApplication::setApplicationVersion(kVersion);
QCoreApplication::setOrganizationDomain(kOrgDomain); // used in prefix, can't be a url
QGuiApplication::setDesktopFileName(QStringLiteral("org.deskflow.deskflow"));
QApplication app(argc, argv);
// Add Command Line Options
QCommandLineOption helpOption = QCommandLineOption("help", "Display Help on the command line");
QCommandLineOption versionOption = QCommandLineOption("version", "Display version information");
QCommandLineOption noResetOption =
QCommandLineOption("no-reset", "Prevent settings reset if DESKFLOW_RESET_ALL is set");
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
parser.addOption(helpOption);
parser.addOption(versionOption);
parser.addOption(noResetOption);
parser.parse(QCoreApplication::arguments());
const auto header = QStringLiteral("%1: %2\n").arg(kAppName, kDisplayVersion);
if (parser.isSet(helpOption) || !parser.unknownOptionNames().isEmpty() || !parser.errorText().isEmpty()) {
QTextStream(stdout) << header << QStringLiteral(" %1\n\n").arg(kAppDescription)
<< parser.helpText().replace(QApplication::applicationFilePath(), kAppId);
return 0;
}
if (parser.isSet(versionOption)) {
QTextStream(stdout) << header << kCopyright << Qt::endl;
return 0;
}
// Create a shared memory segment with a unique key
// This is to prevent a new instance from running if one is already running
QSharedMemory sharedMemory("deskflow-gui");
// Attempt to attach first and detach in order to clean up stale shm chunks
// This can happen if the previous instance was killed or crashed
if (sharedMemory.attach())
sharedMemory.detach();
// If we can create 1 byte of SHM we are the only instance
if (!sharedMemory.create(1)) {
// Ping the running instance to have it show itself
QLocalSocket socket;
socket.connectToServer("deskflow-gui", QLocalSocket::ReadOnly);
if (!socket.waitForConnected()) {
// If we can't connect to the other instance tell the user its running.
// This should never happen but just incase we should show something
QMessageBox::information(nullptr, QObject::tr("Deskflow"), QObject::tr("Deskflow is already running"));
}
socket.disconnectFromServer();
return 0;
}
#if !defined(Q_OS_MAC)
// causes dark mode to be used on some DE's
if (qEnvironmentVariable("XDG_CURRENT_DESKTOP") != QLatin1String("KDE")) {
QApplication::setStyle("fusion");
}
#endif
// Sets the fallback icon path
setIconFallbackPaths();
qInstallMessageHandler(deskflow::gui::messages::messageHandler);
qInfo("%s v%s", kAppName, qPrintable(kVersion));
dotenv();
Logger::instance().loadEnvVars();
#if defined(Q_OS_MAC)
if (app.applicationDirPath().startsWith("/Volumes/")) {
QString msgBody = QStringLiteral(
"Please drag %1 to the Applications folder, "
"and open it from there."
);
QMessageBox::information(NULL, kAppName, msgBody.arg(kAppName));
return 1;
}
if (!checkMacAssistiveDevices()) {
return 1;
}
#endif
// --no-reset
const auto resetEnvVar = QVariant(qEnvironmentVariable("DESKFLOW_RESET_ALL")).toBool();
if (resetEnvVar && !parser.isSet(noResetOption)) {
diagnostic::clearSettings(false);
}
MainWindow mainWindow;
mainWindow.open();
return QApplication::exec();
}
#if defined(Q_OS_MAC)
bool checkMacAssistiveDevices()
{
// new in mavericks, applications are trusted individually
// with use of the accessibility api. this call will show a
// prompt which can show the security/privacy/accessibility
// tab, with a list of allowed applications. deskflow should
// show up there automatically, but will be unchecked.
if (AXIsProcessTrusted()) {
return true;
}
const void *keys[] = {kAXTrustedCheckOptionPrompt};
const void *trueValue[] = {kCFBooleanTrue};
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, trueValue, 1, NULL, NULL);
bool result = AXIsProcessTrustedWithOptions(options);
CFRelease(options);
return result;
}
#endif