Add option to launch KeePassXC at system startup

Fixes #1218
This commit is contained in:
Janek Bevendorff 2020-04-29 17:47:25 +02:00
parent 26ea274259
commit 4ba8ef30f2
10 changed files with 160 additions and 17 deletions

View File

@ -27,6 +27,7 @@
#include "core/Global.h" #include "core/Global.h"
#include "core/Resources.h" #include "core/Resources.h"
#include "core/Translator.h" #include "core/Translator.h"
#include "gui/osutils/OSUtils.h"
#include "MessageBox.h" #include "MessageBox.h"
#include "touchid/TouchID.h" #include "touchid/TouchID.h"
@ -173,8 +174,10 @@ void ApplicationSettingsWidget::loadSettings()
#ifdef QT_DEBUG #ifdef QT_DEBUG
m_generalUi->singleInstanceCheckBox->setEnabled(false); m_generalUi->singleInstanceCheckBox->setEnabled(false);
m_generalUi->launchAtStartup->setEnabled(false);
#endif #endif
m_generalUi->singleInstanceCheckBox->setChecked(config()->get(Config::SingleInstance).toBool()); m_generalUi->singleInstanceCheckBox->setChecked(config()->get(Config::SingleInstance).toBool());
m_generalUi->launchAtStartup->setChecked(osUtils->isLaunchAtStartupEnabled());
m_generalUi->rememberLastDatabasesCheckBox->setChecked(config()->get(Config::RememberLastDatabases).toBool()); m_generalUi->rememberLastDatabasesCheckBox->setChecked(config()->get(Config::RememberLastDatabases).toBool());
m_generalUi->rememberLastKeyFilesCheckBox->setChecked(config()->get(Config::RememberLastKeyFiles).toBool()); m_generalUi->rememberLastKeyFilesCheckBox->setChecked(config()->get(Config::RememberLastKeyFiles).toBool());
m_generalUi->openPreviousDatabasesOnStartupCheckBox->setChecked( m_generalUi->openPreviousDatabasesOnStartupCheckBox->setChecked(
@ -299,6 +302,10 @@ void ApplicationSettingsWidget::saveSettings()
return; return;
} }
#ifndef QT_DEBUG
osUtils->setLaunchAtStartup(m_generalUi->launchAtStartup->isChecked());
#endif
config()->set(Config::SingleInstance, m_generalUi->singleInstanceCheckBox->isChecked()); config()->set(Config::SingleInstance, m_generalUi->singleInstanceCheckBox->isChecked());
config()->set(Config::RememberLastDatabases, m_generalUi->rememberLastDatabasesCheckBox->isChecked()); config()->set(Config::RememberLastDatabases, m_generalUi->rememberLastDatabasesCheckBox->isChecked());
config()->set(Config::RememberLastKeyFiles, m_generalUi->rememberLastKeyFilesCheckBox->isChecked()); config()->set(Config::RememberLastKeyFiles, m_generalUi->rememberLastKeyFilesCheckBox->isChecked());

View File

@ -6,8 +6,6 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>499</width>
<height>1174</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
@ -49,6 +47,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="launchAtStartup">
<property name="text">
<string>Automatically launch KeePassXC at system startup</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="systrayMinimizeOnStartup"> <widget class="QCheckBox" name="systrayMinimizeOnStartup">
<property name="text"> <property name="text">

View File

@ -30,7 +30,24 @@ class OSUtilsBase : public QObject
Q_OBJECT Q_OBJECT
public: public:
virtual bool isDarkMode() = 0; /**
* @return OS dark mode enabled.
*/
virtual bool isDarkMode() const = 0;
/**
* @return KeePassXC set to launch at system startup (autostart).
*/
virtual bool isLaunchAtStartupEnabled() const = 0;
/**
* @param enable Add or remove KeePassXC from system autostart.
*/
virtual void setLaunchAtStartup(bool enable) = 0;
/**
* @return OS caps lock enabled.
*/
virtual bool isCapslockEnabled() = 0; virtual bool isCapslockEnabled() = 0;
protected: protected:

View File

@ -18,6 +18,10 @@
#include "MacUtils.h" #include "MacUtils.h"
#include <QApplication> #include <QApplication>
#include <QDir>
#include <QFile>
#include <QSettings>
#include <QStandardPaths>
#include <CoreGraphics/CGEventSource.h> #include <CoreGraphics/CGEventSource.h>
@ -74,11 +78,6 @@ bool MacUtils::isHidden()
return m_appkit->isHidden(m_appkit->ownProcessId()); return m_appkit->isHidden(m_appkit->ownProcessId());
} }
bool MacUtils::isDarkMode()
{
return m_appkit->isDarkMode();
}
bool MacUtils::enableAccessibility() bool MacUtils::enableAccessibility()
{ {
return m_appkit->enableAccessibility(); return m_appkit->enableAccessibility();
@ -89,6 +88,37 @@ bool MacUtils::enableScreenRecording()
return m_appkit->enableScreenRecording(); return m_appkit->enableScreenRecording();
} }
bool MacUtils::isDarkMode() const
{
return m_appkit->isDarkMode();
}
QString MacUtils::getLaunchAgentFilename() const
{
auto launchAgentDir = QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
return QFile(launchAgentDir.absoluteFilePath(
qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist"))).fileName();
}
bool MacUtils::isLaunchAtStartupEnabled() const
{
return QFile::exists(getLaunchAgentFilename());
}
void MacUtils::setLaunchAtStartup(bool enable)
{
if (enable) {
QSettings agent(getLaunchAgentFilename(), QSettings::NativeFormat);
agent.setValue("Label", qApp->property("KPXC_QUALIFIED_APPNAME").toString());
agent.setValue("ProgramArguments", QStringList() << QApplication::applicationFilePath());
agent.setValue("RunAtLoad", true);
agent.setValue("StandardErrorPath", "/dev/null");
agent.setValue("StandardOutPath", "/dev/null");
} else if (isLaunchAtStartupEnabled()) {
QFile::remove(getLaunchAgentFilename());
}
}
bool MacUtils::isCapslockEnabled() bool MacUtils::isCapslockEnabled()
{ {
return (CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) & kCGEventFlagMaskAlphaShift) != 0; return (CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) & kCGEventFlagMaskAlphaShift) != 0;

View File

@ -33,7 +33,9 @@ class MacUtils : public OSUtilsBase
public: public:
static MacUtils* instance(); static MacUtils* instance();
bool isDarkMode() override; bool isDarkMode() const override;
bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override; bool isCapslockEnabled() override;
WId activeWindow(); WId activeWindow();
@ -53,6 +55,8 @@ protected:
~MacUtils() override; ~MacUtils() override;
private: private:
QString getLaunchAgentFilename() const;
QScopedPointer<AppKit> m_appkit; QScopedPointer<AppKit> m_appkit;
static QPointer<MacUtils> m_instance; static QPointer<MacUtils> m_instance;

View File

@ -16,11 +16,16 @@
*/ */
#include "NixUtils.h" #include "NixUtils.h"
#include <QApplication> #include <QApplication>
#include <QColor> #include <QColor>
#include <QDir>
#include <QFile>
#include <QGuiApplication> #include <QGuiApplication>
#include <QPalette> #include <QPalette>
#include <QStandardPaths>
#include <QStyle> #include <QStyle>
#include <QTextStream>
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
// namespace required to avoid name clashes with declarations in XKBlib.h // namespace required to avoid name clashes with declarations in XKBlib.h
@ -49,7 +54,7 @@ NixUtils::~NixUtils()
{ {
} }
bool NixUtils::isDarkMode() bool NixUtils::isDarkMode() const
{ {
if (!qApp || !qApp->style()) { if (!qApp || !qApp->style()) {
return false; return false;
@ -57,6 +62,58 @@ bool NixUtils::isDarkMode()
return qApp->style()->standardPalette().color(QPalette::Window).toHsl().lightness() < 110; return qApp->style()->standardPalette().color(QPalette::Window).toHsl().lightness() < 110;
} }
QString NixUtils::getAutostartDesktopFilename(bool createDirs) const
{
QDir autostartDir;
auto confHome = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
if (confHome.isEmpty()) {
return {};
}
autostartDir.setPath(confHome + QStringLiteral("/autostart"));
if (createDirs && !autostartDir.exists()) {
autostartDir.mkpath(".");
}
return QFile(autostartDir.absoluteFilePath(qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".desktop")))
.fileName();
}
bool NixUtils::isLaunchAtStartupEnabled() const
{
return QFile::exists(getAutostartDesktopFilename());
;
}
void NixUtils::setLaunchAtStartup(bool enable)
{
if (enable) {
QFile desktopFile(getAutostartDesktopFilename(true));
if (!desktopFile.open(QIODevice::WriteOnly)) {
qWarning("Failed to create autostart desktop file.");
return;
}
QTextStream stream(&desktopFile);
stream.setCodec("UTF-8");
stream << QStringLiteral("[Desktop Entry]") << '\n'
<< QStringLiteral("Name=") << QApplication::applicationDisplayName() << '\n'
<< QStringLiteral("GenericName=") << tr("Password Manager") << '\n'
<< QStringLiteral("Exec=") << QApplication::applicationFilePath() << '\n'
<< QStringLiteral("TryExec=") << QApplication::applicationFilePath() << '\n'
<< QStringLiteral("Icon=") << QApplication::applicationName().toLower() << '\n'
<< QStringLiteral("StartupWMClass=keepassxc") << '\n'
<< QStringLiteral("StartupNotify=true") << '\n'
<< QStringLiteral("Terminal=false") << '\n'
<< QStringLiteral("Type=Application") << '\n'
<< QStringLiteral("Version=1.0") << "true" << '\n'
<< QStringLiteral("Categories=Utility;Security;Qt;") << '\n'
<< QStringLiteral("MimeType=application/x-keepass2;") << '\n'
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << endl;
desktopFile.close();
} else if (isLaunchAtStartupEnabled()) {
QFile::remove(getAutostartDesktopFilename());
}
}
bool NixUtils::isCapslockEnabled() bool NixUtils::isCapslockEnabled()
{ {
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface(); QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();

View File

@ -28,7 +28,9 @@ class NixUtils : public OSUtilsBase
public: public:
static NixUtils* instance(); static NixUtils* instance();
bool isDarkMode() override; bool isDarkMode() const override;
bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override; bool isCapslockEnabled() override;
private: private:
@ -36,6 +38,8 @@ private:
~NixUtils() override; ~NixUtils() override;
private: private:
QString getAutostartDesktopFilename(bool createDirs = false) const;
static QPointer<NixUtils> m_instance; static QPointer<NixUtils> m_instance;
Q_DISABLE_COPY(NixUtils) Q_DISABLE_COPY(NixUtils)

View File

@ -77,13 +77,30 @@ bool WinUtils::DWMEventFilter::nativeEventFilter(const QByteArray& eventType, vo
return false; return false;
} }
bool WinUtils::isDarkMode() bool WinUtils::isDarkMode() const
{ {
QSettings settings(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", QSettings settings(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)",
QSettings::NativeFormat); QSettings::NativeFormat);
return settings.value("AppsUseLightTheme", 1).toInt() == 0; return settings.value("AppsUseLightTheme", 1).toInt() == 0;
} }
bool WinUtils::isLaunchAtStartupEnabled() const
{
return QSettings(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run)", QSettings::NativeFormat)
.contains(qAppName());
;
}
void WinUtils::setLaunchAtStartup(bool enable)
{
QSettings reg(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run)", QSettings::NativeFormat);
if (enable) {
reg.setValue(qAppName(), QApplication::applicationFilePath());
} else {
reg.remove(qAppName());
}
}
bool WinUtils::isCapslockEnabled() bool WinUtils::isCapslockEnabled()
{ {
return GetKeyState(VK_CAPITAL) == 1; return GetKeyState(VK_CAPITAL) == 1;

View File

@ -32,7 +32,9 @@ public:
static WinUtils* instance(); static WinUtils* instance();
static void registerEventFilters(); static void registerEventFilters();
bool isDarkMode() override; bool isDarkMode() const override;
bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override; bool isCapslockEnabled() override;
protected: protected:

View File

@ -53,13 +53,13 @@ int main(int argc, char** argv)
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif #endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
QGuiApplication::setDesktopFileName("org.keepassxc.KeePassXC.desktop");
#endif
Application app(argc, argv); Application app(argc, argv);
Application::setApplicationName("KeePassXC"); Application::setApplicationName("KeePassXC");
Application::setApplicationVersion(KEEPASSXC_VERSION); Application::setApplicationVersion(KEEPASSXC_VERSION);
app.setProperty("KPXC_QUALIFIED_APPNAME", "org.keepassxc.KeePassXC");
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
QGuiApplication::setDesktopFileName(app.property("KPXC_QUALIFIED_APPNAME").toString() + QStringLiteral(".desktop"));
#endif
// don't set organizationName as that changes the return value of // don't set organizationName as that changes the return value of
// QStandardPaths::writableLocation(QDesktopServices::DataLocation) // QStandardPaths::writableLocation(QDesktopServices::DataLocation)