keepassxc/src/gui/osutils/nixutils/NixUtils.cpp
Janek Bevendorff dad4ad68f3 Improve macOS platform integration (backport).
- Allow switching between themes without restart (except classic)
- Rework icon loading and recolouring logic to react to theme changes
- Automatically react to light/dark theme change
- Remove explicit selection of monochrome tray icon variant (selected
  automatically now)

The tray icon doesn't respond perfectly to theme changes yet on Big Sur,
since we need different icons for dark and light theme and cannot simply
let the OS recolour the icon for us (we do that, too, but only as an
additional fallback). At the moment, there is no signal to listen to
that would allow this.

This patch adds a few generic methods to OSUtils for detecting and
communicating theme changes, which are only stubs for Windows and Linux at
the moment and need to be implemented in future commits.

Fixes #5349
2021-01-31 12:18:58 +01:00

149 lines
4.7 KiB
C++

/*
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "NixUtils.h"
#include <QApplication>
#include <QColor>
#include <QDir>
#include <QFile>
#include <QGuiApplication>
#include <QPalette>
#include <QStandardPaths>
#include <QStyle>
#include <QTextStream>
#include <qpa/qplatformnativeinterface.h>
// namespace required to avoid name clashes with declarations in XKBlib.h
namespace X11
{
#include <X11/XKBlib.h>
}
QPointer<NixUtils> NixUtils::m_instance = nullptr;
NixUtils* NixUtils::instance()
{
if (!m_instance) {
m_instance = new NixUtils(qApp);
}
return m_instance;
}
NixUtils::NixUtils(QObject* parent)
: OSUtilsBase(parent)
{
}
NixUtils::~NixUtils()
{
}
bool NixUtils::isDarkMode() const
{
if (!qApp || !qApp->style()) {
return false;
}
return qApp->style()->standardPalette().color(QPalette::Window).toHsl().lightness() < 110;
}
bool NixUtils::isStatusBarDark() const
{
// TODO: implement
return isDarkMode();
}
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;
}
const QString appImagePath = QString::fromLocal8Bit(qgetenv("APPIMAGE"));
const bool isAppImage = !appImagePath.isNull() && QFile::exists(appImagePath);
const QString executeablePath = isAppImage ? appImagePath : QApplication::applicationFilePath();
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=") << executeablePath << '\n'
<< QStringLiteral("TryExec=") << executeablePath << '\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") << '\n'
<< QStringLiteral("Categories=Utility;Security;Qt;") << '\n'
<< QStringLiteral("MimeType=application/x-keepass2;") << '\n'
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << '\n'
<< QStringLiteral("X-GNOME-Autostart-Delay=2") << '\n'
<< QStringLiteral("X-KDE-autostart-after=panel") << endl;
desktopFile.close();
} else if (isLaunchAtStartupEnabled()) {
QFile::remove(getAutostartDesktopFilename());
}
}
bool NixUtils::isCapslockEnabled()
{
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
auto* display = native->nativeResourceForWindow("display", nullptr);
if (!display) {
return false;
}
QString platform = QGuiApplication::platformName();
if (platform == "xcb") {
unsigned state = 0;
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
return ((state & 1u) != 0);
}
}
// TODO: Wayland
return false;
}