/* * Deskflow -- mouse and keyboard sharing utility * SPDX-FileCopyrightText: (C) 2024 Chris Rizzitello * SPDX-FileCopyrightText: (C) 2012 - 2024 Symless Ltd. * SPDX-FileCopyrightText: (C) 2008 Volker Lanz * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ #include "VersionInfo.h" #include "common/Constants.h" #include "common/ExitCodes.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 #include #include #include #include #if defined(Q_OS_MAC) #include #include #endif #if defined(Q_OS_UNIX) && defined(QT_DEBUG) #include #endif using namespace deskflow::gui; #if defined(Q_OS_MAC) bool checkMacAssistiveDevices(); #endif const static auto kHeader = QStringLiteral("%1: %2\n").arg(kAppName, kDisplayVersion); 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 auto helpOption = QCommandLineOption({"h", "help"}, "Display Help on the command line"); auto versionOption = QCommandLineOption({"v", "version"}, "Display version information"); auto resetOption = QCommandLineOption("reset", "Reset all settings"); QCommandLineParser parser; parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); parser.addOption(helpOption); parser.addOption(versionOption); parser.addOption(resetOption); parser.parse(QCoreApplication::arguments()); if (!parser.errorText().isEmpty()) { qCritical().noquote() << parser.errorText() << "\nUse --help for more information."; return s_exitArgs; } if (parser.isSet(helpOption)) { QTextStream(stdout) << kHeader << QStringLiteral(" %1\n\n").arg(kAppDescription) << parser.helpText().replace(QApplication::applicationFilePath(), kAppId); return s_exitSuccess; } if (parser.isSet(versionOption)) { QTextStream(stdout) << kHeader << kCopyright << Qt::endl; return s_exitSuccess; } // 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 s_exitDuplicate; } #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 and fallback theme const auto themeName = QStringLiteral("deskflow-%1").arg(iconMode()); if (QIcon::themeName().isEmpty()) QIcon::setThemeName(themeName); else QIcon::setFallbackThemeName(themeName); QIcon::setFallbackSearchPaths({QStringLiteral(":/icons/%1").arg(themeName)}); qInstallMessageHandler(deskflow::gui::messages::messageHandler); qInfo("%s v%s", kAppName, kDisplayVersion); 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(nullptr, kAppName, msgBody.arg(kAppName)); return 1; } if (!checkMacAssistiveDevices()) { return 1; } #endif // --no-reset if (parser.isSet(resetOption)) { 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(nullptr, keys, trueValue, 1, nullptr, nullptr); bool result = AXIsProcessTrustedWithOptions(options); CFRelease(options); return result; } #endif