mirror of https://github.com/cutefishos/core
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.8 KiB
C++
273 lines
8.8 KiB
C++
/*
|
|
* Copyright (C) 2021 CutefishOS Team.
|
|
*
|
|
* Author: revenmartin <revenmartin@gmail.com>
|
|
*
|
|
* 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 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* 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 "application.h"
|
|
#include "sessionadaptor.h"
|
|
|
|
// Qt
|
|
#include <QDBusConnection>
|
|
#include <QStandardPaths>
|
|
#include <QSettings>
|
|
#include <QProcess>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
|
|
#include <QDBusConnectionInterface>
|
|
#include <QDBusServiceWatcher>
|
|
|
|
// STL
|
|
#include <optional>
|
|
|
|
std::optional<QStringList> getSystemdEnvironment()
|
|
{
|
|
QStringList list;
|
|
auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
|
|
QStringLiteral("/org/freedesktop/systemd1"),
|
|
QStringLiteral("org.freedesktop.DBus.Properties"),
|
|
QStringLiteral("Get"));
|
|
msg << QStringLiteral("org.freedesktop.systemd1.Manager") << QStringLiteral("Environment");
|
|
auto reply = QDBusConnection::sessionBus().call(msg);
|
|
if (reply.type() == QDBusMessage::ErrorMessage) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Make sure the returned type is correct.
|
|
auto arguments = reply.arguments();
|
|
if (arguments.isEmpty() || arguments[0].userType() != qMetaTypeId<QDBusVariant>()) {
|
|
return std::nullopt;
|
|
}
|
|
auto variant = qdbus_cast<QVariant>(arguments[0]);
|
|
if (variant.type() != QVariant::StringList) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return variant.toStringList();
|
|
}
|
|
|
|
bool isShellVariable(const QByteArray &name)
|
|
{
|
|
return name == "_" || name.startsWith("SHLVL");
|
|
}
|
|
|
|
bool isSessionVariable(const QByteArray &name)
|
|
{
|
|
// Check is variable is specific to session.
|
|
return name == "DISPLAY" || name == "XAUTHORITY" || //
|
|
name == "WAYLAND_DISPLAY" || name == "WAYLAND_SOCKET" || //
|
|
name.startsWith("XDG_");
|
|
}
|
|
|
|
void setEnvironmentVariable(const QByteArray &name, const QByteArray &value)
|
|
{
|
|
if (qgetenv(name) != value) {
|
|
qputenv(name, value);
|
|
}
|
|
}
|
|
|
|
Application::Application(int &argc, char **argv)
|
|
: QApplication(argc, argv)
|
|
, m_processManager(new ProcessManager)
|
|
{
|
|
new SessionAdaptor(this);
|
|
|
|
// connect to D-Bus and register as an object:
|
|
QDBusConnection::sessionBus().registerService(QStringLiteral("org.cutefish.Session"));
|
|
QDBusConnection::sessionBus().registerObject(QStringLiteral("/Session"), this);
|
|
|
|
createConfigDirectory();
|
|
initLanguage();
|
|
initScreenScaleFactors();
|
|
initFontDpi();
|
|
|
|
initEnvironments();
|
|
|
|
if (!syncDBusEnvironment()) {
|
|
// Startup error
|
|
qDebug() << "Could not sync environment to dbus.";
|
|
qApp->exit(1);
|
|
}
|
|
|
|
// We import systemd environment after we sync the dbus environment here.
|
|
// Otherwise it may leads to some unwanted order of applying environment
|
|
// variables (e.g. LANG and LC_*)
|
|
// ref plasma
|
|
importSystemdEnvrionment();
|
|
|
|
QTimer::singleShot(100, m_processManager, &ProcessManager::start);
|
|
}
|
|
|
|
void Application::initEnvironments()
|
|
{
|
|
// Set defaults
|
|
if (qEnvironmentVariableIsEmpty("XDG_DATA_HOME"))
|
|
qputenv("XDG_DATA_HOME", QDir::home().absoluteFilePath(QStringLiteral(".local/share")).toLocal8Bit());
|
|
if (qEnvironmentVariableIsEmpty("XDG_DESKTOP_DIR"))
|
|
qputenv("XDG_DESKTOP_DIR", QDir::home().absoluteFilePath(QStringLiteral("/Desktop")).toLocal8Bit());
|
|
if (qEnvironmentVariableIsEmpty("XDG_CONFIG_HOME"))
|
|
qputenv("XDG_CONFIG_HOME", QDir::home().absoluteFilePath(QStringLiteral(".config")).toLocal8Bit());
|
|
if (qEnvironmentVariableIsEmpty("XDG_CACHE_HOME"))
|
|
qputenv("XDG_CACHE_HOME", QDir::home().absoluteFilePath(QStringLiteral(".cache")).toLocal8Bit());
|
|
if (qEnvironmentVariableIsEmpty("XDG_DATA_DIRS"))
|
|
qputenv("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/");
|
|
if (qEnvironmentVariableIsEmpty("XDG_CONFIG_DIRS"))
|
|
qputenv("XDG_CONFIG_DIRS", "/etc/xdg");
|
|
|
|
// Environment
|
|
qputenv("DESKTOP_SESSION", "Cutefish");
|
|
qputenv("XDG_CURRENT_DESKTOP", "Cutefish");
|
|
qputenv("XDG_SESSION_DESKTOP", "Cutefish");
|
|
|
|
// Qt
|
|
qputenv("QT_QPA_PLATFORMTHEME", "cutefish");
|
|
qputenv("QT_PLATFORM_PLUGIN", "cutefish");
|
|
|
|
qunsetenv("QT_SCALE_FACTOR");
|
|
qunsetenv("QT_SCREEN_SCALE_FACTORS");
|
|
qunsetenv("QT_ENABLE_HIGHDPI_SCALING");
|
|
qunsetenv("QT_USE_PHYSICAL_DPI");
|
|
qunsetenv("QT_FONT_DPI");
|
|
qputenv("QT_SCALE_FACTOR_ROUNDING_POLICY", "PassThrough");
|
|
|
|
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
|
|
|
|
// IM Config
|
|
qputenv("GTK_IM_MODULE", "fcitx5");
|
|
qputenv("QT4_IM_MODULE", "fcitx5");
|
|
qputenv("QT_IM_MODULE", "fcitx5");
|
|
qputenv("CLUTTER_IM_MODULE", "fcitx5");
|
|
qputenv("XMODIFIERS", "@im=fcitx");
|
|
}
|
|
|
|
void Application::initFontDpi()
|
|
{
|
|
QSettings settings(QSettings::UserScope, "cutefishos", "theme");
|
|
int fontDpi = settings.value("forceFontDPI", 0).toReal();
|
|
|
|
// TODO port to c++?
|
|
const QByteArray input = "Xft.dpi: " + QByteArray::number(fontDpi);
|
|
QProcess p;
|
|
p.start(QStringLiteral("xrdb"), {QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp")});
|
|
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
|
p.write(input);
|
|
p.closeWriteChannel();
|
|
p.waitForFinished(-1);
|
|
}
|
|
|
|
void Application::initLanguage()
|
|
{
|
|
QSettings settings(QSettings::UserScope, "cutefishos", "language");
|
|
QString value = settings.value("language", "en_US").toString();
|
|
QString str = QString("%1.UTF-8").arg(value);
|
|
|
|
const auto lcValues = {
|
|
"LANG", "LC_NUMERIC", "LC_TIME", "LC_MONETARY", "LC_MEASUREMENT", "LC_COLLATE", "LC_CTYPE"
|
|
};
|
|
|
|
for (auto lc : lcValues) {
|
|
const QString value = str;
|
|
if (!value.isEmpty()) {
|
|
qputenv(lc, value.toUtf8());
|
|
}
|
|
}
|
|
|
|
if (!value.isEmpty()) {
|
|
qputenv("LANGUAGE", value.toUtf8());
|
|
}
|
|
}
|
|
|
|
void Application::initScreenScaleFactors()
|
|
{
|
|
QSettings settings(QSettings::UserScope, "cutefishos", "theme");
|
|
qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal();
|
|
|
|
qputenv("QT_SCREEN_SCALE_FACTORS", QByteArray::number(scaleFactor));
|
|
|
|
// for Gtk
|
|
if (qFloor(scaleFactor) > 1) {
|
|
qputenv("GDK_SCALE", QByteArray::number(scaleFactor, 'g', 0));
|
|
qputenv("GDK_DPI_SCALE", QByteArray::number(1.0 / scaleFactor, 'g', 3));
|
|
} else {
|
|
qputenv("GDK_SCALE", QByteArray::number(qFloor(scaleFactor), 'g', 0));
|
|
qputenv("GDK_DPI_SCALE", QByteArray::number(qFloor(scaleFactor), 'g', 0));
|
|
}
|
|
}
|
|
|
|
bool Application::syncDBusEnvironment()
|
|
{
|
|
int exitCode = 0;
|
|
|
|
// At this point all environment variables are set, let's send it to the DBus session server to update the activation environment
|
|
if (!QStandardPaths::findExecutable(QStringLiteral("dbus-update-activation-environment")).isEmpty()) {
|
|
exitCode = runSync(QStringLiteral("dbus-update-activation-environment"), { QStringLiteral("--systemd"), QStringLiteral("--all") });
|
|
}
|
|
|
|
return exitCode == 0;
|
|
}
|
|
|
|
// Import systemd user environment.
|
|
// Systemd read ~/.config/environment.d which applies to all systemd user unit.
|
|
// But it won't work if cutefishDE is not started by systemd.
|
|
void Application::importSystemdEnvrionment()
|
|
{
|
|
auto environment = getSystemdEnvironment();
|
|
if (!environment) {
|
|
return;
|
|
}
|
|
|
|
for (auto &envString : environment.value()) {
|
|
const auto env = envString.toLocal8Bit();
|
|
const int idx = env.indexOf('=');
|
|
if (Q_UNLIKELY(idx <= 0)) {
|
|
continue;
|
|
}
|
|
|
|
const auto name = env.left(idx);
|
|
if (isShellVariable(name) || isSessionVariable(name)) {
|
|
continue;
|
|
}
|
|
setEnvironmentVariable(name, env.mid(idx + 1));
|
|
}
|
|
}
|
|
|
|
void Application::createConfigDirectory()
|
|
{
|
|
const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
|
|
|
|
if (!QDir().mkpath(configDir))
|
|
qDebug() << "Could not create config directory XDG_CONFIG_HOME: " << configDir;
|
|
}
|
|
|
|
int Application::runSync(const QString &program, const QStringList &args, const QStringList &env)
|
|
{
|
|
QProcess p;
|
|
|
|
if (!env.isEmpty())
|
|
p.setEnvironment(QProcess::systemEnvironment() << env);
|
|
|
|
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
|
p.start(program, args);
|
|
p.waitForFinished(-1);
|
|
|
|
if (p.exitCode()) {
|
|
qWarning() << program << args << "exited with code" << p.exitCode();
|
|
}
|
|
|
|
return p.exitCode();
|
|
}
|