From d04bcf060bb71021cb9ea19d7a201274fca92108 Mon Sep 17 00:00:00 2001 From: Elysia Date: Sun, 4 Aug 2024 23:50:34 +0800 Subject: [PATCH 01/24] Start to add wayland --- startlingmo/CMakeLists.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 startlingmo/CMakeLists.txt diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt new file mode 100644 index 0000000..e69de29 From a15f08c0eb680294dacf43f71c0ff7c892f1b984 Mon Sep 17 00:00:00 2001 From: Elysia Date: Wed, 7 Aug 2024 00:31:11 +0800 Subject: [PATCH 02/24] Added initial supports. --- CMakeLists.txt | 2 + debian/control | 4 +- startlingmo/CMakeLists.txt | 52 +++ startlingmo/cmake/FindKWinPath.cmake | 29 ++ startlingmo/config-startlingmo.h.cmake | 9 + startlingmo/lingmo-session/CMakeLists.txt | 18 + startlingmo/lingmo-session/README | 3 + startlingmo/lingmo-session/main.cpp | 16 + .../lingmo-session/org.lingmo.Startup.xml | 9 + startlingmo/lingmo-session/singleton.h | 32 ++ startlingmo/lingmo-session/startup.cpp | 92 ++++ startlingmo/lingmo-session/startup.hpp | 51 ++ startlingmo/lingmo-sourceenv.sh | 7 + startlingmo/property.cpp | 44 ++ startlingmo/property.hpp | 28 ++ startlingmo/startlingmo-wayland.cpp | 90 ++++ startlingmo/startlingmo.cpp | 439 ++++++++++++++++++ startlingmo/startlingmo.hpp | 59 +++ 18 files changed, 983 insertions(+), 1 deletion(-) create mode 100644 startlingmo/cmake/FindKWinPath.cmake create mode 100644 startlingmo/config-startlingmo.h.cmake create mode 100644 startlingmo/lingmo-session/CMakeLists.txt create mode 100644 startlingmo/lingmo-session/README create mode 100644 startlingmo/lingmo-session/main.cpp create mode 100644 startlingmo/lingmo-session/org.lingmo.Startup.xml create mode 100644 startlingmo/lingmo-session/singleton.h create mode 100644 startlingmo/lingmo-session/startup.cpp create mode 100644 startlingmo/lingmo-session/startup.hpp create mode 100644 startlingmo/lingmo-sourceenv.sh create mode 100644 startlingmo/property.cpp create mode 100644 startlingmo/property.hpp create mode 100644 startlingmo/startlingmo-wayland.cpp create mode 100644 startlingmo/startlingmo.cpp create mode 100644 startlingmo/startlingmo.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bf006d3..efdf2ef 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ add_subdirectory(sddm-helper) add_subdirectory(clipboard) add_subdirectory(chotkeys) +add_subdirectory(startlingmo) + install(FILES lingmo DESTINATION /etc/ COMPONENT Runtime) install(FILES Desktop/Lingmo DESTINATION /etc/Desktop/ COMPONENT Runtime) install(FILES wallpaper-color-pick/lingmo-wallpaper-color-pick DESTINATION "/usr/bin/") diff --git a/debian/control b/debian/control index 6d0a539..57e8e53 100755 --- a/debian/control +++ b/debian/control @@ -40,7 +40,9 @@ Build-Depends: cmake, qtdeclarative5-dev, qtquickcontrols2-5-dev, qttools5-dev, - qttools5-dev-tools + qttools5-dev-tools, + libkf5wayland-dev, + plasma-workspace-dev Standards-Version: 4.5.0 Homepage: https://github.com/LingmoOS/core diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index e69de29..7d37e3a 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -0,0 +1,52 @@ +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(KF6_MIN_VERSION "6.1.0") + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +find_package(Qt5 COMPONENTS Core DBus REQUIRED) + +find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE) +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +find_package(KF5 REQUIRED COMPONENTS DBusAddons CoreAddons) + +find_package(LibKWorkspace REQUIRED) + +find_package(KF5Wayland REQUIRED) + + +add_library(startlingmo OBJECT startlingmo.cpp property.cpp) +target_link_libraries(startlingmo + PUBLIC + Qt5::Core + Qt5::DBus + KF5::DBusAddons + KF5::CoreAddons + PW::KWorkspace +) + +add_executable(startlingmo-wayland startlingmo-wayland.cpp) +target_link_libraries(startlingmo-wayland + PRIVATE + startlingmo + PUBLIC + Qt5::Core + Qt5::DBus +) + +include(KDEInstallDirs) +include(FindKWinPath) +include(ECMQtDeclareLoggingCategory) + +find_kwin_wayland_bin_path() +find_kwin_bin_path() + +configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startlingmo.h) + +add_subdirectory(lingmo-session) + diff --git a/startlingmo/cmake/FindKWinPath.cmake b/startlingmo/cmake/FindKWinPath.cmake new file mode 100644 index 0000000..c959fbd --- /dev/null +++ b/startlingmo/cmake/FindKWinPath.cmake @@ -0,0 +1,29 @@ +function(find_kwin_wayland_bin_path) + # 使用 whereis 命令来查找 kwin 或 kwin_x11 + execute_process( + COMMAND whereis kwin_wayland + OUTPUT_VARIABLE kwin_wayland_output + ERROR_QUIET + ) + + # 解析输出以获取二进制文件的路径 + string(REGEX MATCH "/[^\n\r ]*/kwin_wayland" kwin_wayland_bin_path "${kwin_wayland_output}") + + # 设置 KWIN_WAYLAND_BIN_PATH 变量 + set(KWIN_WAYLAND_BIN_PATH "${kwin_wayland_bin_path}" CACHE PATH "Path to the KWin Wayland binary" FORCE) +endfunction() + +function(find_kwin_bin_path) + # 使用 whereis 命令来查找 kwin 或 kwin_x11 + execute_process( + COMMAND whereis kwin + OUTPUT_VARIABLE kwin_output + ERROR_QUIET + ) + + # 解析输出以获取二进制文件的路径 + string(REGEX MATCH "/[^\n\r ]*/kwin" kwin_bin_path "${kwin_output}") + + # 设置 KWIN_WAYLAND_BIN_PATH 变量 + set(KWIN_BIN "${kwin_bin_path}" CACHE PATH "Path to the KWin binary" FORCE) +endfunction() \ No newline at end of file diff --git a/startlingmo/config-startlingmo.h.cmake b/startlingmo/config-startlingmo.h.cmake new file mode 100644 index 0000000..ccad21b --- /dev/null +++ b/startlingmo/config-startlingmo.h.cmake @@ -0,0 +1,9 @@ +#pragma once + +#define CMAKE_INSTALL_FULL_BINDIR "@CMAKE_INSTALL_FULL_BINDIR@" +#define KDE_INSTALL_FULL_DATAROOTDIR "@KDE_INSTALL_FULL_DATAROOTDIR@" +#define CMAKE_INSTALL_FULL_LIBEXECDIR "@CMAKE_INSTALL_FULL_LIBEXECDIR@" +#define CMAKE_INSTALL_FULL_LIBEXECDIR_KF6 "@CMAKE_INSTALL_FULL_LIBEXECDIR_KF6@" +#define KWIN_WAYLAND_BIN_PATH "@KWIN_WAYLAND_BIN_PATH@" + +#define KWIN_BIN "${KWIN_BIN}" diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt new file mode 100644 index 0000000..a9d54d2 --- /dev/null +++ b/startlingmo/lingmo-session/CMakeLists.txt @@ -0,0 +1,18 @@ +set(lingmo_session_SRCS + main.cpp + startup.cpp +) + +ecm_qt_declare_logging_category(lingmo_session_SRCS HEADER debug.h IDENTIFIER LINGMO_SESSION CATEGORY_NAME org.lingmo.session) + +qt_add_dbus_adaptor( lingmo_session_SRCS org.lingmo.Startup.xml startup.hpp Startup) + +add_executable(lingmo_session ${lingmo_session_SRCS}) + +target_include_directories(lingmo_session PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo ${CMAKE_BINARY_DIR}/startkde) +target_include_directories(lingmo_session PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(lingmo_session + startlingmo +) + +install(TARGETS lingmo_session ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) \ No newline at end of file diff --git a/startlingmo/lingmo-session/README b/startlingmo/lingmo-session/README new file mode 100644 index 0000000..2cd414d --- /dev/null +++ b/startlingmo/lingmo-session/README @@ -0,0 +1,3 @@ +This application runs all init/autostart scripts needed for a lingmo session. + +This application should only contain tasks relating to starting executables and setting environment variables so that an implementation of systemd units would not cause duplication. diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp new file mode 100644 index 0000000..bef236b --- /dev/null +++ b/startlingmo/lingmo-session/main.cpp @@ -0,0 +1,16 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "startup.hpp" + +#include + +int main(int argc, char **argv) { + QCoreApplication app(argc, argv); + + Startup::getInstance()->init(&app); + + app.exec(); +} diff --git a/startlingmo/lingmo-session/org.lingmo.Startup.xml b/startlingmo/lingmo-session/org.lingmo.Startup.xml new file mode 100644 index 0000000..68904ac --- /dev/null +++ b/startlingmo/lingmo-session/org.lingmo.Startup.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/startlingmo/lingmo-session/singleton.h b/startlingmo/lingmo-session/singleton.h new file mode 100644 index 0000000..f2d9556 --- /dev/null +++ b/startlingmo/lingmo-session/singleton.h @@ -0,0 +1,32 @@ +/* + SPDX-FileCopyrightText: 2024 zhuzichu520 + + SPDX-License-Identifier: MIT +*/ +#ifndef SINGLETON_H +#define SINGLETON_H + +/** + * @brief The Singleton class + */ +template +class Singleton { +public: + static T* getInstance(); +}; + +template +T* Singleton::getInstance() { + static T* instance = new T(); + return instance; +} + +#define SINGLETON(Class) \ +private: \ + friend class Singleton; \ + public: \ + static Class* getInstance() { \ + return Singleton::getInstance(); \ +} + +#endif // SINGLETON_H diff --git a/startlingmo/lingmo-session/startup.cpp b/startlingmo/lingmo-session/startup.cpp new file mode 100644 index 0000000..ce78783 --- /dev/null +++ b/startlingmo/lingmo-session/startup.cpp @@ -0,0 +1,92 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "startup.hpp" +#include + +#include "debug.h" +#include "startupadaptor.h" + +#include + +void Startup::updateLaunchEnv(const QString &key, const QString &value) { + qputenv(key.toLatin1(), value.toLatin1()); +} + +void Startup::init(QObject *parent) { + Startup::getInstance()->setParent(parent); +} + +Startup::Startup() : QObject(nullptr) { + + new StartupAdaptor(this); + QDBusConnection::sessionBus().registerObject( + QStringLiteral("/Startup"), QStringLiteral("org.lingmo.Startup"), this); + QDBusConnection::sessionBus().registerService( + QStringLiteral("org.lingmo.Startup")); + + if (qEnvironmentVariable("XDG_SESSION_TYPE") != QLatin1String("wayland")) { + // Currently don't do anything if we're not running under Wayland + } else { + // This must block until started as it sets the WAYLAND_DISPLAY/DISPLAY env + // variables needed for the rest of the boot fortunately it's very fast as + // it's just starting a wrapper + StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}, + QStringLiteral("org.kde.KWinWrapper")); + kwinWaylandJob.exec(); + } +} + +bool Startup::startDetached(QProcess *process) { + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->start(); + const bool ret = process->waitForStarted(); + if (ret) { + m_processes << process; + } + return ret; +} + +StartServiceJob::StartServiceJob(const QString &process, + const QStringList &args, + const QString &serviceId, + const QProcessEnvironment &additionalEnv) + : KJob(), m_process(new QProcess), m_serviceId(serviceId), + m_additionalEnv(additionalEnv) { + m_process->setProgram(process); + m_process->setArguments(args); + + auto watcher = + new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, this); + connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, + &StartServiceJob::emitResult); +} + +void StartServiceJob::start() { + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(m_additionalEnv); + m_process->setProcessEnvironment(env); + + if (!m_serviceId.isEmpty() && + QDBusConnection::sessionBus().interface()->isServiceRegistered( + m_serviceId)) { + qCDebug(LINGMO_SESSION) << m_process << "already running"; + emitResult(); + return; + } + qCDebug(LINGMO_SESSION) << "Starting " << m_process->program() + << m_process->arguments(); + if (!Startup::getInstance()->startDetached(m_process)) { + qCWarning(LINGMO_SESSION) << "error starting process" + << m_process->program() << m_process->arguments(); + emitResult(); + } + + if (m_serviceId.isEmpty()) { + emitResult(); + } +} diff --git a/startlingmo/lingmo-session/startup.hpp b/startlingmo/lingmo-session/startup.hpp new file mode 100644 index 0000000..7e9a8e3 --- /dev/null +++ b/startlingmo/lingmo-session/startup.hpp @@ -0,0 +1,51 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#ifndef STARTUP_HPP +#define STARTUP_HPP + +#include +#include + +#include + +#include "singleton.h" + +class Startup : public QObject { + Q_OBJECT + Startup(); + +public: + SINGLETON(Startup); + void init(QObject *parent); + + bool startDetached(QProcess *process); + +public Q_SLOTS: + // alternatively we could drop this? + void updateLaunchEnv(const QString &key, const QString &value); + +private: + QVector m_processes; +}; + +/** + * Launches a process, and waits for the service to appear on the session bus + */ +class StartServiceJob : public KJob { + Q_OBJECT +public: + StartServiceJob( + const QString &process, const QStringList &args, const QString &serviceId, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +private: + QProcess *m_process; + const QString m_serviceId; + const QProcessEnvironment m_additionalEnv; +}; + +#endif // STARTUP_HPP \ No newline at end of file diff --git a/startlingmo/lingmo-sourceenv.sh b/startlingmo/lingmo-sourceenv.sh new file mode 100644 index 0000000..4a00f15 --- /dev/null +++ b/startlingmo/lingmo-sourceenv.sh @@ -0,0 +1,7 @@ +for i in $@ +do + . $i >/dev/null +done + +# env may not support -0, fall back to GNU env +env -0 2>/dev/null || genv -0 diff --git a/startlingmo/property.cpp b/startlingmo/property.cpp new file mode 100644 index 0000000..13a2da3 --- /dev/null +++ b/startlingmo/property.cpp @@ -0,0 +1,44 @@ +#include "property.hpp" + +template +LProperty::LProperty() : + m_cObject(nullptr), + Set(nullptr), + Get(nullptr) +{} + +template +void LProperty::setContainer(Container *cObject) { + m_cObject = cObject; +} + +template +void LProperty::setter(void (Container::*pSet)(ValueType value)) { + if ((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE)) + Set = pSet; + else + Set = nullptr; +} + +template +void LProperty::getter(ValueType (Container::*pGet)()) { + if ((nPropType == READ_ONLY) || (nPropType == READ_WRITE)) + Get = pGet; + else + Get = nullptr; +} + +template +ValueType LProperty::operator=(const ValueType &value) { + assert(m_cObject != nullptr); + assert(Set != nullptr); + (m_cObject->*Set)(value); + return value; +} + +template +LProperty::operator ValueType() { + assert(m_cObject != nullptr); + assert(Get != nullptr); + return (m_cObject->*Get)(); +} \ No newline at end of file diff --git a/startlingmo/property.hpp b/startlingmo/property.hpp new file mode 100644 index 0000000..b32d11b --- /dev/null +++ b/startlingmo/property.hpp @@ -0,0 +1,28 @@ +#ifndef PROPERTY_HPP +#define PROPERTY_HPP + +#include + +#define READ_ONLY 1 +#define WRITE_ONLY 2 +#define READ_WRITE 3 + +template +class LProperty { + public: + LProperty(); + + void setContainer(Container *cObject); + void setter(void (Container::*pSet)(ValueType value)); + void getter(ValueType (Container::*pGet)()); + + ValueType operator=(const ValueType &value); + operator ValueType(); + + private: + Container *m_cObject; + void (Container::*Set)(ValueType value); + ValueType (Container::*Get)(); +}; + +#endif // PROPERTY_HPP \ No newline at end of file diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp new file mode 100644 index 0000000..dd71354 --- /dev/null +++ b/startlingmo/startlingmo-wayland.cpp @@ -0,0 +1,90 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include +#include +#include +#include + +#include "startlingmo.hpp" + +extern "C" { +#include "signal.h" +} + +extern QTextStream out; + +int main(int argc, char **argv) { + + QCoreApplication app(argc, argv); + + createConfigDirectory(); + setupCursor(true); + signal(SIGTERM, sigtermHandler); + + // Let clients try to reconnect to kwin after a restart + qputenv("QT_WAYLAND_RECONNECT", "1"); + + // Query whether org.freedesktop.locale1 is available. If it is, try to + // set XKB_DEFAULT_{MODEL,LAYOUT,VARIANT,OPTIONS} accordingly. + { + const QString locale1Service = QStringLiteral("org.freedesktop.locale1"); + const QString locale1Path = QStringLiteral("/org/freedesktop/locale1"); + QDBusMessage message = QDBusMessage::createMethodCall( + locale1Service, locale1Path, + QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("GetAll")); + message << locale1Service; + QDBusMessage resultMessage = QDBusConnection::systemBus().call(message); + if (resultMessage.type() == QDBusMessage::ReplyMessage) { + QVariantMap result; + QDBusArgument dbusArgument = + resultMessage.arguments().at(0).value(); + while (!dbusArgument.atEnd()) { + dbusArgument >> result; + } + + auto queryAndSet = [&result](const char *var, const QString &value) { + const auto r = result.value(value).toString(); + if (!r.isEmpty()) + qputenv(var, r.toUtf8()); + }; + + queryAndSet("XKB_DEFAULT_MODEL", QStringLiteral("X11Model")); + queryAndSet("XKB_DEFAULT_LAYOUT", QStringLiteral("X11Layout")); + queryAndSet("XKB_DEFAULT_VARIANT", QStringLiteral("X11Variant")); + queryAndSet("XKB_DEFAULT_OPTIONS", QStringLiteral("X11Options")); + } else { + qCWarning(LINGMO_STARTUP) + << "No valid reply from org.freedesktop.locale1" << resultMessage; + } + } + + runEnvironmentScripts(); + + if (!qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) { + out << "startplasmacompositor: Could not start D-Bus. Can you call " + "qdbus?\n"; + return 1; + } + + setupLingmoEnvironment(); + initLanguage(); + + qputenv("XDG_SESSION_TYPE", "wayland"); + + auto oldSystemdEnvironment = getSystemdEnvironment(); + if (!syncDBusEnvironment()) { + out << "Could not sync environment to dbus.\n"; + return 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_*) + importSystemdEnvrionment(); + + return 0; +} \ No newline at end of file diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp new file mode 100644 index 0000000..dee8b4d --- /dev/null +++ b/startlingmo/startlingmo.cpp @@ -0,0 +1,439 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "startlingmo.hpp" +#include "config-startlingmo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +extern QTextStream out; +QTextStream out(stderr); + +Q_LOGGING_CATEGORY(LINGMO_STARTUP, "org.lingmo.startup") + +void sigtermHandler(int signalNumber) { + Q_UNUSED(signalNumber) + if (QCoreApplication::instance()) { + QCoreApplication::instance()->exit(-1); + } +} + +QStringList allServices(const QLatin1String &prefix) { + const QStringList services = + QDBusConnection::sessionBus().interface()->registeredServiceNames(); + QStringList names; + + std::copy_if(services.cbegin(), services.cend(), std::back_inserter(names), + [&prefix](const QString &serviceName) { + return serviceName.startsWith(prefix); + }); + + return names; +} + +void gentleTermination(QProcess *p) { + if (p->state() != QProcess::Running) { + return; + } + + p->terminate(); + + // Wait longer for a session than a greeter + if (!p->waitForFinished(5000)) { + p->kill(); + if (!p->waitForFinished(5000)) { + qCWarning(LINGMO_STARTUP) + << "Could not fully finish the process" << p->program(); + } + } +} + +int 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); + + QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + &p, [&p] { gentleTermination(&p); }); + // qCDebug(LINGMO_STARTUP) << "started..." << program << args; + p.waitForFinished(-1); + if (p.exitCode()) { + qCWarning(LINGMO_STARTUP) + << program << args << "exited with code" << p.exitCode(); + } + return p.exitCode(); +} + +void createConfigDirectory() { + const QString configDir = + QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + if (!QDir().mkpath(configDir)) + out << "Could not create config directory XDG_CONFIG_HOME: " << configDir + << '\n'; +} + +void setupCursor(bool wayland) { +#ifdef XCURSOR_PATH + QByteArray path(XCURSOR_PATH); + path.replace("$XCURSOR_PATH", qgetenv("XCURSOR_PATH")); + qputenv("XCURSOR_PATH", path); +#endif + + // TODO: consider linking directly + if (!wayland) { + QSettings settings(QSettings::UserScope, "lingmoos", "theme"); + qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); + QString cursorTheme = settings.value("CursorTheme", "default").toString(); + int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; + + runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); + } +} + +// Source scripts found in /lingmo-workspace/env/*.sh +// (where correspond to the system and user's configuration +// directory. +// +// Scripts are sourced in reverse order of priority of their directory, as +// defined by `QStandardPaths::standardLocations`. This ensures that +// high-priority scripts (such as those in the user's home directory) are +// sourced last and take precedence over lower-priority scripts (such as system +// defaults). Scripts in the same directory are sourced in lexical order of +// their filename. +// +// This is where you can define environment variables that will be available to +// all KDE programs, so this is where you can run agents using e.g. eval +// `ssh-agent` or eval `gpg-agent --daemon`. Note: if you do that, you should +// also put "ssh-agent -k" as a shutdown script +// +// (see end of this file). +// For anything else (that doesn't set env vars, or that needs a window +// manager), better use the Autostart folder. +void runEnvironmentScripts() { + QStringList scripts; + auto locations = + QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); + + //`standardLocations()` returns locations sorted by "order of priority". We + // iterate in reverse + // order so that high-priority scripts are sourced last and their + // modifications take precedence. + for (auto loc = locations.crbegin(); loc != locations.crend(); loc++) { + QDir dir(*loc); + if (!dir.cd(QStringLiteral("./lingmo-workspace/env"))) { + // Skip location if lingmo-workspace/env subdirectory does not exist + continue; + } + const auto dirScripts = + dir.entryInfoList({QStringLiteral("*.sh")}, QDir::Files, QDir::Name); + for (const auto &script : dirScripts) { + scripts << script.absoluteFilePath(); + } + } + sourceFiles(scripts); +} + +void sourceFiles(const QStringList &files) { + QStringList filteredFiles; + std::copy_if(files.begin(), files.end(), std::back_inserter(filteredFiles), + [](const QString &i) { return QFileInfo(i).isReadable(); }); + + if (filteredFiles.isEmpty()) + return; + + filteredFiles.prepend( + QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/lingmo-sourceenv.sh")); + + QProcess p; + p.start(QStringLiteral("/bin/sh"), filteredFiles); + p.waitForFinished(-1); + + const auto fullEnv = p.readAllStandardOutput(); + auto envs = fullEnv.split('\0'); + + for (auto &env : envs) { + const int idx = env.indexOf('='); + if (Q_UNLIKELY(idx <= 0)) { + continue; + } + + const auto name = env.left(idx); + if (isShellVariable(name)) { + continue; + } + setEnvironmentVariable(name, env.mid(idx + 1)); + } +} + +bool isShellVariable(const QByteArray &name) { + return name == "_" || name == "SHELL" || name.startsWith("SHLVL"); +} + +void setEnvironmentVariable(const QByteArray &name, const QByteArray &value) { + if (qgetenv(name) != value) { + qputenv(name, value); + } +} + +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 setupLingmoEnvironment() { + // Manually disable auto scaling because we are scaling above + // otherwise apps that manually opt in for high DPI get auto scaled by the + // developer AND manually scaled by us + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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", "Lingmo"); + qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); + qputenv("XDG_SESSION_DESKTOP", "Lingmo"); + + // Qt + // qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); + // qputenv("QT_PLATFORM_PLUGIN", "lingmo"); + + // ref: + // https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping + qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); + + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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"); +} + +std::optional getSystemdEnvironment() { + 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()) { + return std::nullopt; + } + auto variant = qdbus_cast(arguments[0]); + if (variant.type() != QVariant::StringList) { + return std::nullopt; + } + + const auto assignmentList = variant.toStringList(); + QProcessEnvironment ret; + for (auto &env : assignmentList) { + const int idx = env.indexOf(QLatin1Char('=')); + if (Q_LIKELY(idx > 0)) { + ret.insert(env.left(idx), env.mid(idx + 1)); + } + } + + return ret; +} + +// Drop session-specific variables from the systemd environment. +// Those can be leftovers from previous sessions, which can interfere with the +// session we want to start now, e.g. $DISPLAY might break kwin_wayland. +static void dropSessionVarsFromSystemdEnvironment() { + const auto environment = getSystemdEnvironment(); + if (!environment) { + return; + } + + QStringList varsToDrop; + for (auto &nameStr : environment.value().keys()) { + // If it's set in this process, it'll be overwritten by the following + // UpdateLaunchEnvJob + const auto name = nameStr.toLocal8Bit(); + if (!qEnvironmentVariableIsSet(name) && isSessionVariable(name)) { + varsToDrop.append(nameStr); + } + } + + auto msg = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("UnsetEnvironment")); + msg << varsToDrop; + auto reply = QDBusConnection::sessionBus().call(msg); + if (reply.type() == QDBusMessage::ErrorMessage) { + qCWarning(LINGMO_STARTUP) + << "Failed to unset systemd environment variables:" << reply.errorName() + << reply.errorMessage(); + } +} + +// kwin_wayland can possibly also start dbus-activated services which need env +// variables. In that case, the update in startplasma might be too late. +bool syncDBusEnvironment() { + dropSessionVarsFromSystemdEnvironment(); + + // Shell variables are filtered out of things we explicitly load, but they + // still might have been inherited from the parent process + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + for (auto &name : environment.keys()) { + if (isShellVariable(name.toLocal8Bit())) { + environment.remove(name); + } + } + + // At this point all environment variables are set, let's send it to the DBus + // session server to update the activation environment + auto job = new UpdateLaunchEnvironmentJob(environment); + QEventLoop e; + QObject::connect(job, &UpdateLaunchEnvironmentJob::finished, &e, + &QEventLoop::quit); + e.exec(); + return true; +} + +void initLanguage() { + QSettings settings(QSettings::UserScope, "lingmoos", "language"); + QString value = settings.value("language", "").toString(); + + // Init Language + if (value.isEmpty()) { + QFile file("/etc/locale.gen"); + if (file.open(QIODevice::ReadOnly)) { + QStringList lines = QString(file.readAll()).split('\n'); + + for (const QString &line : lines) { + if (line.startsWith('#')) + continue; + + if (line.trimmed().isEmpty()) + continue; + + value = line.split(' ').first().split('.').first(); + } + } + } + + if (value.isEmpty()) + value = "en_US"; + + settings.setValue("language", value); + + 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()); + } +} + +// Import systemd user environment. +// +// Systemd read ~/.config/environment.d which applies to all systemd user unit. +// But it won't work if plasma is not started by systemd. +void importSystemdEnvrionment() { + const auto environment = getSystemdEnvironment(); + if (!environment) { + return; + } + + for (auto &nameStr : environment.value().keys()) { + const auto name = nameStr.toLocal8Bit(); + if (!isShellVariable(name) && !isSessionVariable(name)) { + setEnvironmentVariable(name, + environment.value().value(nameStr).toLocal8Bit()); + } + } +} + +// If something went on an endless restart crash loop it will get blacklisted, +// as this is a clean login we will want to reset those counters This is +// independent of whether we use the Plasma systemd boot +void resetSystemdFailedUnits() { + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("ResetFailed")); + QDBusConnection::sessionBus().call(message); +} + +// Reload systemd to make sure the current configuration is active, which also +// reruns generators. Needed for e.g. XDG autostart changes to become effective. +void reloadSystemd() { + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("Reload")); + QDBusConnection::sessionBus().call(message); +} + +bool startLingmoSession(bool wayland) { + resetSystemdFailedUnits(); + reloadSystemd(); + + std::unique_ptr startPlasmaSession; + + { + startPlasmaSession.reset(new QProcess); + qCDebug(LINGMO_STARTUP) << "Using classic boot"; + } +} \ No newline at end of file diff --git a/startlingmo/startlingmo.hpp b/startlingmo/startlingmo.hpp new file mode 100644 index 0000000..48b0270 --- /dev/null +++ b/startlingmo/startlingmo.hpp @@ -0,0 +1,59 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#ifndef STARTLINGMO_HPP +#define STARTLINGMO_HPP + +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(LINGMO_STARTUP) + +void sigtermHandler(int signalNumber); + +QStringList allServices(const QLatin1String &prefix); + +void gentleTermination(QProcess *process); + +int runSync(const QString &program, const QStringList &args, + const QStringList &env = {}); + +void createConfigDirectory(); + +void setupCursor(bool wayland); + +void runEnvironmentScripts(); + +void sourceFiles(const QStringList &files); + +bool isShellVariable(const QByteArray &name); + +void setEnvironmentVariable(const QByteArray &name, const QByteArray &value); + +void setupLingmoEnvironment(); + +std::optional getSystemdEnvironment(); + +bool syncDBusEnvironment(); + +void initLanguage(); + +void importSystemdEnvrionment(); + +bool startLingmoSession(bool wayland); + +struct KillBeforeDeleter { + void operator()(QProcess *pointer) { + if (pointer) { + gentleTermination(pointer); + } + delete pointer; + } +}; + +#endif // STARTLINGMO_HPP \ No newline at end of file From 43908be765ea52a2117ff38eb56c88c6ff08df04 Mon Sep 17 00:00:00 2001 From: Elysia Date: Wed, 7 Aug 2024 13:19:48 +0800 Subject: [PATCH 03/24] Add some helper --- startlingmo/lingmo-session/CMakeLists.txt | 6 ++ startlingmo/lingmo-session/sessiontrack.cpp | 54 +++++++++++++++++ startlingmo/lingmo-session/sessiontrack.h | 23 ++++++++ startlingmo/lingmo-session/signalhandler.cpp | 62 ++++++++++++++++++++ startlingmo/lingmo-session/signalhandler.h | 38 ++++++++++++ startlingmo/lingmo-session/startup.cpp | 56 +++++++++++++++++- startlingmo/lingmo-session/startup.hpp | 20 +++++++ 7 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 startlingmo/lingmo-session/sessiontrack.cpp create mode 100644 startlingmo/lingmo-session/sessiontrack.h create mode 100644 startlingmo/lingmo-session/signalhandler.cpp create mode 100644 startlingmo/lingmo-session/signalhandler.h diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt index a9d54d2..8629cf5 100644 --- a/startlingmo/lingmo-session/CMakeLists.txt +++ b/startlingmo/lingmo-session/CMakeLists.txt @@ -1,6 +1,12 @@ + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED 17) + set(lingmo_session_SRCS main.cpp startup.cpp + sessiontrack.cpp + signalhandler.cpp ) ecm_qt_declare_logging_category(lingmo_session_SRCS HEADER debug.h IDENTIFIER LINGMO_SESSION CATEGORY_NAME org.lingmo.session) diff --git a/startlingmo/lingmo-session/sessiontrack.cpp b/startlingmo/lingmo-session/sessiontrack.cpp new file mode 100644 index 0000000..c8363f7 --- /dev/null +++ b/startlingmo/lingmo-session/sessiontrack.cpp @@ -0,0 +1,54 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "sessiontrack.h" +#include "signalhandler.h" +#include +#include +#include +#include +#include + +SessionTrack::SessionTrack(const QVector &processes) + : m_processes(processes) { + SignalHandler::self()->addSignal(SIGTERM); + connect(SignalHandler::self(), &SignalHandler::signalReceived, + QCoreApplication::instance(), [](int signal) { + if (signal == SIGTERM) { + QCoreApplication::instance()->exit(0); + } + }); + + for (auto process : std::as_const(m_processes)) { + connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(removeProcess(int, QProcess::ExitStatus))); + } + + QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + this, &SessionTrack::deleteLater); +} + +SessionTrack::~SessionTrack() { + disconnect(this, nullptr, QCoreApplication::instance(), nullptr); + + for (auto process : std::as_const(m_processes)) { + process->terminate(); + } + + // copy before the loop as we remove finished processes from the vector + const QVector processesCopy = m_processes; + for (auto process : processesCopy) { + if (process->state() == QProcess::Running && + !process->waitForFinished(500)) { + process->kill(); + } else { + delete process; + } + } +} + +void SessionTrack::removeProcess(int exitCode, QProcess::ExitStatus exitStatus) { + m_processes.removeAll(static_cast(sender())); +} \ No newline at end of file diff --git a/startlingmo/lingmo-session/sessiontrack.h b/startlingmo/lingmo-session/sessiontrack.h new file mode 100644 index 0000000..7cbc8b0 --- /dev/null +++ b/startlingmo/lingmo-session/sessiontrack.h @@ -0,0 +1,23 @@ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#pragma once + +#include +#include + +class SessionTrack : public QObject { + Q_OBJECT +public: + SessionTrack(const QVector &processes); + ~SessionTrack() override; + + void removeProcess(int exitCode, QProcess::ExitStatus exitStatus); + +private: + QVector m_processes; + QEventLoopLocker m_lock; +}; diff --git a/startlingmo/lingmo-session/signalhandler.cpp b/startlingmo/lingmo-session/signalhandler.cpp new file mode 100644 index 0000000..b32c3ee --- /dev/null +++ b/startlingmo/lingmo-session/signalhandler.cpp @@ -0,0 +1,62 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "signalhandler.h" +#include "debug.h" + +#include +#include +#include +#include + +int SignalHandler::signalFd[2]; + +SignalHandler::SignalHandler() +{ + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { + qCWarning(LINGMO_SESSION) << "Couldn't create a socketpair"; + return; + } + + m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); + connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); +} + +SignalHandler::~SignalHandler() +{ + for (int sig : std::as_const(m_signalsRegistered)) { + signal(sig, nullptr); + } + close(signalFd[0]); + close(signalFd[1]); +} + +void SignalHandler::addSignal(int signalToTrack) +{ + m_signalsRegistered.insert(signalToTrack); + signal(signalToTrack, signalHandler); +} + +void SignalHandler::signalHandler(int signal) +{ + ::write(signalFd[0], &signal, sizeof(signal)); +} + +void SignalHandler::handleSignal() +{ + m_handler->setEnabled(false); + int signal; + ::read(signalFd[1], &signal, sizeof(signal)); + m_handler->setEnabled(true); + + Q_EMIT signalReceived(signal); +} + +SignalHandler *SignalHandler::self() +{ + static SignalHandler s_self; + return &s_self; +} diff --git a/startlingmo/lingmo-session/signalhandler.h b/startlingmo/lingmo-session/signalhandler.h new file mode 100644 index 0000000..c0f8428 --- /dev/null +++ b/startlingmo/lingmo-session/signalhandler.h @@ -0,0 +1,38 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#pragma once + +#include +#include +#include + +/** + * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop + * + * It's a singleton as it relies on static data getting defined. + */ +class SignalHandler : public QObject +{ + Q_OBJECT +public: + ~SignalHandler() override; + void addSignal(int signal); + + static SignalHandler *self(); + +Q_SIGNALS: + void signalReceived(int signal); + +private: + SignalHandler(); + void handleSignal(); + static void signalHandler(int signal); + + QSet m_signalsRegistered; + static int signalFd[2]; + QSocketNotifier *m_handler = nullptr; +}; diff --git a/startlingmo/lingmo-session/startup.cpp b/startlingmo/lingmo-session/startup.cpp index ce78783..074fe5b 100644 --- a/startlingmo/lingmo-session/startup.cpp +++ b/startlingmo/lingmo-session/startup.cpp @@ -8,6 +8,7 @@ #include "debug.h" #include "startupadaptor.h" +#include "sessiontrack.h" #include @@ -16,7 +17,7 @@ void Startup::updateLaunchEnv(const QString &key, const QString &value) { } void Startup::init(QObject *parent) { - Startup::getInstance()->setParent(parent); + Startup::getInstance()->setParent(parent); } Startup::Startup() : QObject(nullptr) { @@ -38,6 +39,32 @@ Startup::Startup() : QObject(nullptr) { QStringLiteral("org.kde.KWinWrapper")); kwinWaylandJob.exec(); } + + const QVector sequence = { + + }; + KJob *last = nullptr; + for (KJob *job : sequence) { + if (!job) { + continue; + } + if (last) { + connect(last, &KJob::finished, job, &KJob::start); + } + last = job; + } + + connect(sequence.last(), &KJob::finished, this, &Startup::finishStartup); + sequence.first()->start(); +} + +void Startup::finishStartup() { + qCDebug(LINGMO_SESSION) << "Finished"; + // upAndRunning(QStringLiteral("ready")); + + // playStartupSound(); + new SessionTrack(m_processes); + deleteLater(); } bool Startup::startDetached(QProcess *process) { @@ -90,3 +117,30 @@ void StartServiceJob::start() { emitResult(); } } + +StartProcessJob::StartProcessJob(const QString &process, + const QStringList &args, + const QProcessEnvironment &additionalEnv) + : KJob(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); +} + +void StartProcessJob::start() { + qCDebug(LINGMO_SESSION) << "Starting " << m_process->program() + << m_process->arguments(); + + m_process->start(); +} + +void StartProcessJob::finised(int exitCode, QProcess::ExitStatus) { + qCInfo(LINGMO_SESSION) << "process job " << m_process->program() + << "finished with exit code " << exitCode; + emitResult(); +} diff --git a/startlingmo/lingmo-session/startup.hpp b/startlingmo/lingmo-session/startup.hpp index 7e9a8e3..58a8519 100644 --- a/startlingmo/lingmo-session/startup.hpp +++ b/startlingmo/lingmo-session/startup.hpp @@ -27,6 +27,8 @@ public Q_SLOTS: // alternatively we could drop this? void updateLaunchEnv(const QString &key, const QString &value); + void finishStartup(); + private: QVector m_processes; }; @@ -48,4 +50,22 @@ class StartServiceJob : public KJob { const QProcessEnvironment m_additionalEnv; }; +/** + * Launches a process, and waits for the process to start + */ +class StartProcessJob : public KJob { + Q_OBJECT +public: + StartProcessJob( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +public Q_SLOTS: + void finised(int exitCode, QProcess::ExitStatus); + +private: + QProcess *m_process; +}; + #endif // STARTUP_HPP \ No newline at end of file From 59710b4b50dee23de99ca6eea7c27e18089d6c68 Mon Sep 17 00:00:00 2001 From: Elysia Date: Wed, 7 Aug 2024 15:02:24 +0800 Subject: [PATCH 04/24] Able to display cursors, --- startlingmo/CMakeLists.txt | 2 ++ startlingmo/lingmo-session/main.cpp | 7 +++++-- startlingmo/lingmo-session/startup.cpp | 15 +++++++-------- startlingmo/lingmo-session/startup.hpp | 9 +++++---- startlingmo/startlingmo-wayland.cpp | 2 ++ startlingmo/startlingmo.cpp | 20 ++++++++++++++++++-- 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index 7d37e3a..157cdd0 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -50,3 +50,5 @@ configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-sta add_subdirectory(lingmo-session) +install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(PROGRAMS lingmo-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp index bef236b..070a8ea 100644 --- a/startlingmo/lingmo-session/main.cpp +++ b/startlingmo/lingmo-session/main.cpp @@ -6,11 +6,14 @@ #include "startup.hpp" #include +#include + +std::shared_ptr startup_ptr; int main(int argc, char **argv) { QCoreApplication app(argc, argv); - Startup::getInstance()->init(&app); - + startup_ptr = std::make_shared(&app); + app.exec(); } diff --git a/startlingmo/lingmo-session/startup.cpp b/startlingmo/lingmo-session/startup.cpp index 074fe5b..511ff73 100644 --- a/startlingmo/lingmo-session/startup.cpp +++ b/startlingmo/lingmo-session/startup.cpp @@ -11,16 +11,13 @@ #include "sessiontrack.h" #include +#include void Startup::updateLaunchEnv(const QString &key, const QString &value) { qputenv(key.toLatin1(), value.toLatin1()); } -void Startup::init(QObject *parent) { - Startup::getInstance()->setParent(parent); -} - -Startup::Startup() : QObject(nullptr) { +Startup::Startup(QObject *parent) : QObject(parent) { new StartupAdaptor(this); QDBusConnection::sessionBus().registerObject( @@ -40,8 +37,10 @@ Startup::Startup() : QObject(nullptr) { kwinWaylandJob.exec(); } + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("MOZ_ENABLE_WAYLAND", "1"); const QVector sequence = { - + new StartProcessJob(QStringLiteral("foot"), {}, env) }; KJob *last = nullptr; for (KJob *job : sequence) { @@ -107,7 +106,7 @@ void StartServiceJob::start() { } qCDebug(LINGMO_SESSION) << "Starting " << m_process->program() << m_process->arguments(); - if (!Startup::getInstance()->startDetached(m_process)) { + if (!startup_ptr->startDetached(m_process)) { qCWarning(LINGMO_SESSION) << "error starting process" << m_process->program() << m_process->arguments(); emitResult(); @@ -139,7 +138,7 @@ void StartProcessJob::start() { m_process->start(); } -void StartProcessJob::finised(int exitCode, QProcess::ExitStatus) { +void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { qCInfo(LINGMO_SESSION) << "process job " << m_process->program() << "finished with exit code " << exitCode; emitResult(); diff --git a/startlingmo/lingmo-session/startup.hpp b/startlingmo/lingmo-session/startup.hpp index 58a8519..1d33736 100644 --- a/startlingmo/lingmo-session/startup.hpp +++ b/startlingmo/lingmo-session/startup.hpp @@ -11,14 +11,13 @@ #include -#include "singleton.h" +#include class Startup : public QObject { Q_OBJECT - Startup(); public: - SINGLETON(Startup); + Startup(QObject *parent); void init(QObject *parent); bool startDetached(QProcess *process); @@ -62,10 +61,12 @@ class StartProcessJob : public KJob { void start() override; public Q_SLOTS: - void finised(int exitCode, QProcess::ExitStatus); + void finished(int, QProcess::ExitStatus); private: QProcess *m_process; }; +extern std::shared_ptr startup_ptr; + #endif // STARTUP_HPP \ No newline at end of file diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp index dd71354..22ac79a 100644 --- a/startlingmo/startlingmo-wayland.cpp +++ b/startlingmo/startlingmo-wayland.cpp @@ -86,5 +86,7 @@ int main(int argc, char **argv) { // variables (e.g. LANG and LC_*) importSystemdEnvrionment(); + startLingmoSession(true); + return 0; } \ No newline at end of file diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp index dee8b4d..d0c13db 100644 --- a/startlingmo/startlingmo.cpp +++ b/startlingmo/startlingmo.cpp @@ -430,10 +430,26 @@ bool startLingmoSession(bool wayland) { resetSystemdFailedUnits(); reloadSystemd(); - std::unique_ptr startPlasmaSession; + bool rc = true; + QEventLoop e; + + std::unique_ptr startLingmoSession; { - startPlasmaSession.reset(new QProcess); + startLingmoSession.reset(new QProcess); qCDebug(LINGMO_STARTUP) << "Using classic boot"; + + startLingmoSession->setProcessChannelMode(QProcess::ForwardedChannels); + + startLingmoSession->start( + QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/lingmo_session"), + QStringList{}); + } + + if (rc) { + QObject::connect(QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, &e, &QEventLoop::quit); + e.exec(); } + return rc; } \ No newline at end of file From bdcfa4307ea4ea3fbb64fa4f06ba30b2900104f7 Mon Sep 17 00:00:00 2001 From: Elysia Date: Wed, 7 Aug 2024 19:52:22 +0800 Subject: [PATCH 05/24] Added Job Capability --- startlingmo/lingmo-session-old/CMakeLists.txt | 24 + .../README | 0 startlingmo/lingmo-session-old/main.cpp | 19 + .../org.lingmo.Startup.xml | 0 .../sessiontrack.cpp | 0 .../sessiontrack.h | 0 .../signalhandler.cpp | 0 .../signalhandler.h | 0 .../singleton.h | 0 .../startup.cpp | 0 .../startup.hpp | 0 startlingmo/lingmo-session/CMakeLists.txt | 52 +- startlingmo/lingmo-session/application.cpp | 358 ++++++++++++++ startlingmo/lingmo-session/application.h | 91 ++++ .../lingmo-session/com.lingmo.Session.xml | 32 ++ startlingmo/lingmo-session/daemon-helper.cpp | 138 ++++++ startlingmo/lingmo-session/daemon-helper.h | 203 ++++++++ startlingmo/lingmo-session/job_private.hpp | 41 ++ .../lingmo-session/lingmo-xsession.desktop | 7 + startlingmo/lingmo-session/main.cpp | 38 +- .../lingmo-session/networkproxymanager.cpp | 97 ++++ .../lingmo-session/networkproxymanager.h | 51 ++ .../lingmo-session/powermanager/power.cpp | 80 ++++ .../lingmo-session/powermanager/power.h | 118 +++++ .../powermanager/powerproviders.cpp | 448 ++++++++++++++++++ .../powermanager/powerproviders.h | 109 +++++ startlingmo/lingmo-session/process.cpp | 30 ++ startlingmo/lingmo-session/process.h | 34 ++ startlingmo/lingmo-session/processmanager.cpp | 275 +++++++++++ startlingmo/lingmo-session/processmanager.h | 113 +++++ startlingmo/startlingmo.cpp | 10 +- startlingmo/startlingmo.hpp | 1 + 32 files changed, 2341 insertions(+), 28 deletions(-) create mode 100644 startlingmo/lingmo-session-old/CMakeLists.txt rename startlingmo/{lingmo-session => lingmo-session-old}/README (100%) create mode 100644 startlingmo/lingmo-session-old/main.cpp rename startlingmo/{lingmo-session => lingmo-session-old}/org.lingmo.Startup.xml (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/sessiontrack.cpp (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/sessiontrack.h (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/signalhandler.cpp (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/signalhandler.h (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/singleton.h (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/startup.cpp (100%) rename startlingmo/{lingmo-session => lingmo-session-old}/startup.hpp (100%) mode change 100644 => 100755 startlingmo/lingmo-session/CMakeLists.txt create mode 100755 startlingmo/lingmo-session/application.cpp create mode 100755 startlingmo/lingmo-session/application.h create mode 100755 startlingmo/lingmo-session/com.lingmo.Session.xml create mode 100644 startlingmo/lingmo-session/daemon-helper.cpp create mode 100644 startlingmo/lingmo-session/daemon-helper.h create mode 100644 startlingmo/lingmo-session/job_private.hpp create mode 100755 startlingmo/lingmo-session/lingmo-xsession.desktop mode change 100644 => 100755 startlingmo/lingmo-session/main.cpp create mode 100755 startlingmo/lingmo-session/networkproxymanager.cpp create mode 100755 startlingmo/lingmo-session/networkproxymanager.h create mode 100755 startlingmo/lingmo-session/powermanager/power.cpp create mode 100755 startlingmo/lingmo-session/powermanager/power.h create mode 100755 startlingmo/lingmo-session/powermanager/powerproviders.cpp create mode 100755 startlingmo/lingmo-session/powermanager/powerproviders.h create mode 100755 startlingmo/lingmo-session/process.cpp create mode 100755 startlingmo/lingmo-session/process.h create mode 100755 startlingmo/lingmo-session/processmanager.cpp create mode 100755 startlingmo/lingmo-session/processmanager.h diff --git a/startlingmo/lingmo-session-old/CMakeLists.txt b/startlingmo/lingmo-session-old/CMakeLists.txt new file mode 100644 index 0000000..8629cf5 --- /dev/null +++ b/startlingmo/lingmo-session-old/CMakeLists.txt @@ -0,0 +1,24 @@ + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED 17) + +set(lingmo_session_SRCS + main.cpp + startup.cpp + sessiontrack.cpp + signalhandler.cpp +) + +ecm_qt_declare_logging_category(lingmo_session_SRCS HEADER debug.h IDENTIFIER LINGMO_SESSION CATEGORY_NAME org.lingmo.session) + +qt_add_dbus_adaptor( lingmo_session_SRCS org.lingmo.Startup.xml startup.hpp Startup) + +add_executable(lingmo_session ${lingmo_session_SRCS}) + +target_include_directories(lingmo_session PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo ${CMAKE_BINARY_DIR}/startkde) +target_include_directories(lingmo_session PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(lingmo_session + startlingmo +) + +install(TARGETS lingmo_session ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) \ No newline at end of file diff --git a/startlingmo/lingmo-session/README b/startlingmo/lingmo-session-old/README similarity index 100% rename from startlingmo/lingmo-session/README rename to startlingmo/lingmo-session-old/README diff --git a/startlingmo/lingmo-session-old/main.cpp b/startlingmo/lingmo-session-old/main.cpp new file mode 100644 index 0000000..070a8ea --- /dev/null +++ b/startlingmo/lingmo-session-old/main.cpp @@ -0,0 +1,19 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "startup.hpp" + +#include +#include + +std::shared_ptr startup_ptr; + +int main(int argc, char **argv) { + QCoreApplication app(argc, argv); + + startup_ptr = std::make_shared(&app); + + app.exec(); +} diff --git a/startlingmo/lingmo-session/org.lingmo.Startup.xml b/startlingmo/lingmo-session-old/org.lingmo.Startup.xml similarity index 100% rename from startlingmo/lingmo-session/org.lingmo.Startup.xml rename to startlingmo/lingmo-session-old/org.lingmo.Startup.xml diff --git a/startlingmo/lingmo-session/sessiontrack.cpp b/startlingmo/lingmo-session-old/sessiontrack.cpp similarity index 100% rename from startlingmo/lingmo-session/sessiontrack.cpp rename to startlingmo/lingmo-session-old/sessiontrack.cpp diff --git a/startlingmo/lingmo-session/sessiontrack.h b/startlingmo/lingmo-session-old/sessiontrack.h similarity index 100% rename from startlingmo/lingmo-session/sessiontrack.h rename to startlingmo/lingmo-session-old/sessiontrack.h diff --git a/startlingmo/lingmo-session/signalhandler.cpp b/startlingmo/lingmo-session-old/signalhandler.cpp similarity index 100% rename from startlingmo/lingmo-session/signalhandler.cpp rename to startlingmo/lingmo-session-old/signalhandler.cpp diff --git a/startlingmo/lingmo-session/signalhandler.h b/startlingmo/lingmo-session-old/signalhandler.h similarity index 100% rename from startlingmo/lingmo-session/signalhandler.h rename to startlingmo/lingmo-session-old/signalhandler.h diff --git a/startlingmo/lingmo-session/singleton.h b/startlingmo/lingmo-session-old/singleton.h similarity index 100% rename from startlingmo/lingmo-session/singleton.h rename to startlingmo/lingmo-session-old/singleton.h diff --git a/startlingmo/lingmo-session/startup.cpp b/startlingmo/lingmo-session-old/startup.cpp similarity index 100% rename from startlingmo/lingmo-session/startup.cpp rename to startlingmo/lingmo-session-old/startup.cpp diff --git a/startlingmo/lingmo-session/startup.hpp b/startlingmo/lingmo-session-old/startup.hpp similarity index 100% rename from startlingmo/lingmo-session/startup.hpp rename to startlingmo/lingmo-session-old/startup.hpp diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt old mode 100644 new mode 100755 index 8629cf5..bb8aa1d --- a/startlingmo/lingmo-session/CMakeLists.txt +++ b/startlingmo/lingmo-session/CMakeLists.txt @@ -1,24 +1,48 @@ +project(lingmo_session) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED 17) -set(lingmo_session_SRCS - main.cpp - startup.cpp - sessiontrack.cpp - signalhandler.cpp -) +set(TARGET lingmo_session) + +set(SOURCES + application.cpp + main.cpp + process.cpp + processmanager.cpp + networkproxymanager.cpp -ecm_qt_declare_logging_category(lingmo_session_SRCS HEADER debug.h IDENTIFIER LINGMO_SESSION CATEGORY_NAME org.lingmo.session) + powermanager/power.cpp + powermanager/powerproviders.cpp + + daemon-helper.cpp + daemon-helper.h +) -qt_add_dbus_adaptor( lingmo_session_SRCS org.lingmo.Startup.xml startup.hpp Startup) +qt5_add_dbus_adaptor(DBUS_SOURCES + com.lingmo.Session.xml + application.h Application + sessionadaptor SessionAdaptor) +set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) -add_executable(lingmo_session ${lingmo_session_SRCS}) +find_package(KF5WindowSystem) +find_package(Threads) -target_include_directories(lingmo_session PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo ${CMAKE_BINARY_DIR}/startkde) -target_include_directories(lingmo_session PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(lingmo_session - startlingmo +add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES}) +target_link_libraries(${TARGET} + PRIVATE + startlingmo + PUBLIC + Qt5::Core + Qt5::Gui + Qt5::Widgets + Qt5::Quick + Qt5::DBus + Qt5::X11Extras + KF5::WindowSystem + ${CMAKE_THREAD_LIBS_INIT} ) +target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) -install(TARGETS lingmo_session ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) \ No newline at end of file +install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(FILES lingmo-xsession.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) diff --git a/startlingmo/lingmo-session/application.cpp b/startlingmo/lingmo-session/application.cpp new file mode 100755 index 0000000..d740f37 --- /dev/null +++ b/startlingmo/lingmo-session/application.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#include "application.h" +#include "sessionadaptor.h" + +// Qt +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// STL +#include + +#include "startlingmo.hpp" + + +Application::Application(int &argc, char **argv) + : QApplication(argc, argv) + , m_processManager(new ProcessManager(this)) + , m_networkProxyManager(new NetworkProxyManager) + , m_wayland(false) +{ + new SessionAdaptor(this); + + // connect to D-Bus and register as an object: + QDBusConnection::sessionBus().registerService(QStringLiteral("com.lingmo.Session")); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Session"), this); + + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("Lingmo Session")); + parser.addHelpOption(); + + QCommandLineOption waylandOption(QStringList() << "w" << "wayland" << "Wayland Mode"); + parser.addOption(waylandOption); + parser.process(*this); + + m_wayland = parser.isSet(waylandOption); + + // createConfigDirectory(); + // initKWinConfig(); + // initLanguage(); + initScreenScaleFactors(); + initXResource(); + + 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(); + + qunsetenv("XCURSOR_THEME"); + qunsetenv("XCURSOR_SIZE"); + qunsetenv("SESSION_MANAGER"); + + m_networkProxyManager->update(); + + // QTimer::singleShot(50, this, &Application::updateUserDirs); + + // Launch Lingmo and user defined processes ! + QTimer::singleShot(100, m_processManager, &ProcessManager::start); +} + +bool Application::wayland() const +{ + return m_wayland; +} + +void Application::launch(const QString &exec, const QStringList &args) +{ + QProcess process; + process.setProgram(exec); + process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); + process.setArguments(args); + process.startDetached(); +} + +void Application::launch(const QString &exec, const QString &workingDir, const QStringList &args) +{ + QProcess process; + process.setProgram(exec); + process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); + process.setWorkingDirectory(workingDir); + process.setArguments(args); + process.startDetached(); +} + +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", "Lingmo"); + qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); + qputenv("XDG_SESSION_DESKTOP", "Lingmo"); + + // Qt + qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); + qputenv("QT_PLATFORM_PLUGIN", "lingmo"); + + // ref: https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping + qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); + + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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::initLanguage() +{ + QSettings settings(QSettings::UserScope, "lingmoos", "language"); + QString value = settings.value("language", "").toString(); + + // Init Language + if (value.isEmpty()) { + QFile file("/etc/locale.gen"); + if (file.open(QIODevice::ReadOnly)) { + QStringList lines = QString(file.readAll()).split('\n'); + + for (const QString &line : lines) { + if (line.startsWith('#')) + continue; + + if (line.trimmed().isEmpty()) + continue; + + value = line.split(' ').first().split('.').first(); + } + } + } + + if (value.isEmpty()) + value = "en_US"; + + settings.setValue("language", value); + + 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, "lingmoos", "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)); + } +} + +void Application::initXResource() +{ + QSettings settings(QSettings::UserScope, "lingmoos", "theme"); + qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); + int fontDpi = 96 * scaleFactor; + QString cursorTheme = settings.value("CursorTheme", "default").toString(); + int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; + int xftAntialias = settings.value("XftAntialias", 1).toBool(); + QString xftHintStyle = settings.value("XftHintStyle", "hintslight").toString(); + + const QString datas = QString("Xft.dpi: %1\n" + "Xcursor.theme: %2\n" + "Xcursor.size: %3\n" + "Xft.antialias: %4\n" + "Xft.hintstyle: %5\n" + "Xft.rgba: rgb") + .arg(fontDpi) + .arg(cursorTheme) + .arg(cursorSize) + .arg(xftAntialias) + .arg(xftHintStyle); + + QProcess p; + p.start(QStringLiteral("xrdb"), {QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp")}); + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.write(datas.toLatin1()); + p.closeWriteChannel(); + p.waitForFinished(-1); + + // For lingmo-wine + qputenv("LINGMO_FONT_DPI", QByteArray::number(fontDpi)); + + // Init cursor + runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); + // qputenv("XCURSOR_THEME", cursorTheme.toLatin1()); + // qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor)); +} + +void Application::initKWinConfig() +{ + QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/kwinrc", + QSettings::IniFormat); + + settings.beginGroup("Effect-Blur"); + settings.setValue("BlurStrength", 10); + settings.setValue("NoiseStrength", 0); + settings.endGroup(); + + settings.beginGroup("Windows"); + settings.setValue("FocusStealingPreventionLevel", 0); + settings.setValue("HideUtilityWindowsForInactive", false); + settings.setValue("BorderlessMaximizedWindows", false); + settings.setValue("Placement", "Centered"); + settings.endGroup(); + + settings.beginGroup("org.kde.kdecoration2"); + settings.setValue("BorderSize", "Normal"); + settings.setValue("ButtonsOnLeft", ""); + settings.setValue("ButtonsOnRight", "HIAX"); + settings.setValue("library", "org.lingmo.decoration"); + settings.setValue("theme", ""); + settings.endGroup(); +} + +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 lingmoDE is not started by systemd. +void Application::importSystemdEnvrionment() +{ + auto environment = getSystemdEnvironment(); + if (!environment) { + return; + } + + for (auto &envString : environment->toStringList()) { + 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; +} + +void Application::updateUserDirs() +{ + // bool isLingmoOS = QFile::exists("/etc/lingmoos"); + + // if (!isLingmoOS) + // return; + + // QProcess p; + // p.setEnvironment(QStringList() << "LC_ALL=C"); + // p.start("xdg-user-dirs-update", QStringList() << "--force"); + // p.waitForFinished(-1); +} + +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(); +} diff --git a/startlingmo/lingmo-session/application.h b/startlingmo/lingmo-session/application.h new file mode 100755 index 0000000..0920420 --- /dev/null +++ b/startlingmo/lingmo-session/application.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include +#include +#include + +#include "processmanager.h" +#include "networkproxymanager.h" +#include "powermanager/power.h" + +class Application : public QApplication +{ + Q_OBJECT + +public: + explicit Application(int &argc, char **argv); + + bool wayland() const; + +public slots: + void logout() { + m_processManager->logout(); + } + + void reboot() { + m_power.reboot(); + QCoreApplication::exit(0); + } + + void powerOff() { + m_power.shutdown(); + QCoreApplication::exit(0); + } + + void suspend() { + m_power.suspend(); + } + + [[maybe_unused]] void startDesktopProcess() { + // Start Lingmo Desktop Environment + m_processManager->startDesktopProcess(); + } + + [[maybe_unused]] void updateNetworkProxy() { + m_networkProxyManager->update(); + } + + void launch(const QString &exec, const QStringList &args); + void launch(const QString &exec, const QString &workingDir, const QStringList &args); + +private: + void initEnvironments(); + void initLanguage(); + void initScreenScaleFactors(); + void initXResource(); + void initKWinConfig(); + bool syncDBusEnvironment(); + void importSystemdEnvrionment(); + void createConfigDirectory(); + void updateUserDirs(); + int runSync(const QString &program, const QStringList &args, const QStringList &env = {}); + +private: + ProcessManager *m_processManager; + NetworkProxyManager *m_networkProxyManager; + Power m_power; + + bool m_wayland; +}; + +#endif // APPLICATION_H diff --git a/startlingmo/lingmo-session/com.lingmo.Session.xml b/startlingmo/lingmo-session/com.lingmo.Session.xml new file mode 100755 index 0000000..199b7ee --- /dev/null +++ b/startlingmo/lingmo-session/com.lingmo.Session.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/startlingmo/lingmo-session/daemon-helper.cpp b/startlingmo/lingmo-session/daemon-helper.cpp new file mode 100644 index 0000000..8516b27 --- /dev/null +++ b/startlingmo/lingmo-session/daemon-helper.cpp @@ -0,0 +1,138 @@ +/** + * @name daemon-helper.cpp + * @author Elysia + **/ +#include "daemon-helper.h" +#include "job_private.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace LINGMO_SESSION { +Daemon::Daemon(const QList> &processList, + bool _enableAutoStart, QObject *parent) + : QObject(parent), m_processList(processList), + m_enableAutoRestart(_enableAutoStart) { + for (const auto &processInfo : m_processList) { + startProcess(processInfo); + } +} + +void Daemon::onProcessError(QProcess::ProcessError error) { + const QPointer process = qobject_cast(sender()); + + if (!process) + return; + + QString program = process->program(); + qDebug() << "Process error:" << program << "Error:" << error; + + for (const auto &processInfo : m_processList) { + if (processInfo.first == program) { + qDebug() << "Restarting process due to error:" << program; + QTimer::singleShot(1, this, [this, processInfo]() { + startProcess(processInfo); + }); // Restart after 1 second + return; + } + } +} + +void Daemon::startProcess(const QPair &processInfo) { + const QPointer process = new QProcess(this); + + if (this->m_enableAutoRestart) + connect(process, &QProcess::errorOccurred, this, &Daemon::onProcessError); + + process->start(processInfo.first, processInfo.second); + if (process->waitForStarted()) { + qDebug() << "Process started:" << processInfo.first + << "PID:" << process->processId(); + } else { + qDebug() << "Failed to start process:" << processInfo.first + << process->errorString(); + } +} + +JobPrivate::JobPrivate() {} + +JobPrivate::~JobPrivate() {} + +Job::Job(QObject *parent) : QObject(parent), d_ptr(new JobPrivate) {} + +Job::~Job() { + if (!d_ptr->isFinished) { + d_ptr->isFinished = true; + Q_EMIT finished(this); + } +} + +void Job::emitResult() { + if (d_func()->isFinished) { + finishJob(true); + } +} +void Job::finishJob(bool emitResult) { + Q_D(Job); + Q_ASSERT(!d->isFinished); + d->isFinished = true; + + if (d->eventLoop) { + d->eventLoop->quit(); + } + + Q_EMIT finished(this); + + if (emitResult) { + Q_EMIT result(this); + } + + if (isAutoDelete()) { + deleteLater(); + } +} + +bool Job::isAutoDelete() const { + Q_D(const Job); + return d->isAutoDelete; +} + +void Job::setAutoDelete(bool autodelete) { + Q_D(Job); + d->isAutoDelete = autodelete; +} + +bool Job::exec() { + Q_D(Job); + // Usually this job would delete itself, via deleteLater() just after + // emitting result() (unless configured otherwise). Since we use an event + // loop below, that event loop will process the deletion event and we'll + // have been deleted when exec() returns. This crashes, so temporarily + // suspend autodeletion and manually do it afterwards. + const bool wasAutoDelete = isAutoDelete(); + setAutoDelete(false); + + Q_ASSERT(!d->eventLoop); + + QEventLoop loop(this); + d->eventLoop = &loop; + + start(); + + if (!d->isFinished) { + d->m_startedWithExec = true; + d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); + } + d->eventLoop = nullptr; + + if (wasAutoDelete) { + deleteLater(); + } + return (d->error == NoError); +} +} // namespace LINGMO_SESSION diff --git a/startlingmo/lingmo-session/daemon-helper.h b/startlingmo/lingmo-session/daemon-helper.h new file mode 100644 index 0000000..0098b73 --- /dev/null +++ b/startlingmo/lingmo-session/daemon-helper.h @@ -0,0 +1,203 @@ +#ifndef __DAEMON_HELPER_ +#define __DAEMON_HELPER_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace LINGMO_SESSION { +class JobPrivate; + +class Daemon : public QObject { + Q_OBJECT + +public: + /** + * Start all the passed process using daemon. + * @param processList Process list to start + * @param parent + */ + explicit Daemon(const QList> &processList, + bool _enableAutoStart = true, QObject *parent = nullptr); + +public slots: + + /** + * Handle the case when the progarm has some errors (i.e. crashed) + * @param error + */ + void onProcessError(QProcess::ProcessError error); + +private: + /** + * Start a given process using daemon helper + * @brief startProcess + * @param processInfo + */ + void startProcess(const QPair &processInfo); + + QList> m_processList; + + /** + * @brief Whether to enable auto reload when process exited. + */ + bool m_enableAutoRestart; +}; + +class Job : public QObject { + Q_OBJECT +public: + Job(QObject *parent = nullptr); + + ~Job(); + + /** + * Returns whether this job automatically deletes itself once + * the job is finished. + * + * @return whether the job is deleted automatically after + * finishing. + */ + bool isAutoDelete() const; + + /** + * Sets the auto-delete property of the job. If @p autodelete is + * set to @c false the job will not delete itself once it is finished. + * + * The default for any Job is to automatically delete itself, which + * implies that the job was created on the heap (using new). + * If the job is created on the stack (which isn't the typical use-case + * for a job) then you must set auto-delete to @c false, otherwise you + * could get a crash when the job finishes and tries to delete itself. + * + * @note If you set auto-delete to @c false then you need to kill the + * job manually, ideally by calling kill(). + * + * @param autodelete set to @c false to disable automatic deletion + * of the job. + */ + void setAutoDelete(bool autodelete); + + /** + * Executes the job synchronously. + * + * This will start a nested QEventLoop internally. Nested event loop can be + * dangerous and can have unintended side effects, you should avoid calling + * exec() whenever you can and use the asynchronous interface of Job instead. + * + * Should you indeed call this method, you need to make sure that all callers + * are reentrant, so that events delivered by the inner event loop don't cause + * non-reentrant functions to be called, which usually wreaks havoc. + * + * Note that the event loop started by this method does not process user input + * events, which means your user interface will effectively be blocked. Other + * events like paint or network events are still being processed. The + * advantage of not processing user input events is that the chance of + * accidental reentrance is greatly reduced. Still you should avoid calling + * this function. + * + * @return true if the job has been executed without error, false otherwise + */ + bool exec(); + + /** + * Starts the job asynchronously. + * + * When the job is finished, result() is emitted. + * + * Warning: Never implement any synchronous workload in this method. This + * method should just trigger the job startup, not do any work itself. It is + * expected to be non-blocking. + * + * This is the method all subclasses need to implement. + * It should setup and trigger the workload of the job. It should not do any + * work itself. This includes all signals and terminating the job, e.g. by + * emitResult(). The workload, which could be another method of the + * subclass, is to be triggered using the event loop, e.g. by code like: + * \code + * void ExampleJob::start() + * { + * QTimer::singleShot(0, this, &ExampleJob::doWork); + * } + * \endcode + */ + Q_SCRIPTABLE virtual void start() = 0; + + enum { + /*** Indicates there is no error */ + NoError = 0, + /*** Indicates the job was killed */ + KilledJobError = 1, + /*** Subclasses should define error codes starting at this value */ + UserDefinedError = 100, + }; +Q_SIGNALS: + /** + * Emitted when the job is finished, in any case. It is used to notify + * observers that the job is terminated and that progress can be hidden. + * + * This signal is guaranteed to be emitted exactly once. + * + * This is a private signal, it can't be emitted directly by subclass, use + * emitResult() instead. + * + * In general, to be notified of a job's completion, client code should + * connect to result() rather than finished(), so that kill(Quietly) is indeed + * quiet. However if you store a list of jobs and they might get killed + * silently, then you must connect to this instead of result(), to avoid + * dangling pointers in your list. + * + * @param job the job that emitted this signal + * @internal + * + * @see result + */ + void finished(Job *job); + + /** + * Emitted when the job is finished (except when killed with KJob::Quietly). + * + * This signal is guaranteed to be emitted at most once. + * + * Use error to know if the job was finished with error. + * + * This is a private signal, it can't be emitted directly by subclasses of + * KJob, use emitResult() instead. + * + * Please connect to this signal instead of finished. + * + * @param job the job that emitted this signal + * + * @see kill + */ + void result(Job *job); + +protected: + /** + * Utility function to emit the result signal, and end this job. + * It first notifies the observers to hide the progress for this job using + * the finished() signal. + * + * @note Deletes this job using deleteLater(). + * + * @see result() + * @see finished() + */ + void emitResult(); + + std::unique_ptr const d_ptr; + +private: + void finishJob(bool emitResult); + + Q_DECLARE_PRIVATE(Job) +}; +} // namespace LINGMO_SESSION +#endif diff --git a/startlingmo/lingmo-session/job_private.hpp b/startlingmo/lingmo-session/job_private.hpp new file mode 100644 index 0000000..adb0351 --- /dev/null +++ b/startlingmo/lingmo-session/job_private.hpp @@ -0,0 +1,41 @@ +#ifndef JOB_PRIVATE_HPP +#define JOB_PRIVATE_HPP + +#include "daemon-helper.h" +#include +#include + +#include + +using namespace LINGMO_SESSION; + +class QTimer; +class QEventLoop; + +// This is a private class, but it's exported for +// KIO::Job's usage. Other Job classes in kdelibs may +// use it too. +namespace LINGMO_SESSION { +class JobPrivate { +public: + JobPrivate(); + virtual ~JobPrivate(); + + Job *q_ptr = nullptr; + + QEventLoop *eventLoop = nullptr; + // eventLoopLocker prevents QCoreApplication from exiting when the last + // window is closed until the job has finished running + QEventLoopLocker eventLoopLocker; + bool suspended = false; + bool isAutoDelete = true; + + bool isFinished = false; + bool m_startedWithExec = false; + + int error = Job::NoError; + + Q_DECLARE_PUBLIC(Job) +}; +} // namespace LINGMO_SESSION +#endif \ No newline at end of file diff --git a/startlingmo/lingmo-session/lingmo-xsession.desktop b/startlingmo/lingmo-session/lingmo-xsession.desktop new file mode 100755 index 0000000..4fb9f6d --- /dev/null +++ b/startlingmo/lingmo-session/lingmo-xsession.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Exec=lingmo-session +TryExec=lingmo-session +Name=Lingmo Desktop +Keywords=session +Comment=session diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp old mode 100644 new mode 100755 index 070a8ea..8ebbc96 --- a/startlingmo/lingmo-session/main.cpp +++ b/startlingmo/lingmo-session/main.cpp @@ -1,19 +1,33 @@ /* - SPDX-FileCopyrightText: 2024 Elysia + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#include "startup.hpp" +#include "application.h" +#include -#include -#include +int main(int argc, char *argv[]) +{ -std::shared_ptr startup_ptr; + QQuickWindow::setDefaultAlphaBuffer(true); + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); -int main(int argc, char **argv) { - QCoreApplication app(argc, argv); + Application a(argc, argv); + a.setQuitOnLastWindowClosed(false); - startup_ptr = std::make_shared(&app); - - app.exec(); + return a.exec(); } diff --git a/startlingmo/lingmo-session/networkproxymanager.cpp b/startlingmo/lingmo-session/networkproxymanager.cpp new file mode 100755 index 0000000..aba6301 --- /dev/null +++ b/startlingmo/lingmo-session/networkproxymanager.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: Reion Wong + * + * 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 . + */ + + +#include "networkproxymanager.h" +#include +#include +#include +#include + +NetworkProxyManager::NetworkProxyManager(QObject *parent) + : QObject(parent) + , m_settings(QSettings::UserScope, "lingmoos", "network") +{ +} + +void NetworkProxyManager::update() +{ + qunsetenv("HTTP_PROXY"); + qunsetenv("HTTPS_PROXY"); + qunsetenv("FTP_PROXY"); + qunsetenv("ALL_PROXY"); + qunsetenv("NO_PROXY"); + + qunsetenv("http_proxy"); + qunsetenv("https_proxy"); + qunsetenv("ftp_proxy"); + qunsetenv("all_proxy"); + qunsetenv("no_proxy"); + + m_settings.sync(); + + m_flag = m_settings.value("ProxyFlag", 0).toInt(); + m_useSameProxy = m_settings.value("UseSameProxy", false).toBool(); + m_scriptProxy = m_settings.value("ProxyScriptProxy", "").toString(); + m_httpProxy = m_settings.value("HttpProxy", "").toString(); + m_ftpProxy = m_settings.value("FtpProxy", "").toString(); + m_socksProxy = m_settings.value("SocksProxy", "").toString(); + m_httpProxyPort = m_settings.value("HttpProxyPort", "").toString(); + m_ftpProxyPort = m_settings.value("FtpProxyPort", "").toString(); + m_socksProxyPort = m_settings.value("SocksProxyPort", "").toString(); + + QMap dbusActivationEnv; + QStringList systemdUpdates; + + if (m_flag == 0) { + // No proxy + } else if (m_flag == 1) { + // Use proxy auto configuration URL + } else if (m_flag == 2) { + // Use manually specified proxy configuration + + QString httpProxy = QString("http://%1:%2/").arg(m_httpProxy).arg(m_httpProxyPort); + QString ftpProxy = QString("http://%1:%2/").arg(m_ftpProxy).arg(m_ftpProxyPort); + + if (m_useSameProxy) { + ftpProxy = httpProxy; + } + + if (!m_httpProxy.isEmpty() && !m_httpProxyPort.isEmpty()) { + qputenv("HTTP_PROXY", httpProxy.toLatin1()); + qputenv("HTTPS_PROXY", httpProxy.toLatin1()); + + qputenv("http_proxy", httpProxy.toLatin1()); + qputenv("https_proxy", httpProxy.toLatin1()); + } + + if (!m_ftpProxy.isEmpty() && !m_ftpProxyPort.isEmpty()) { + qputenv("FTP_PROXY", ftpProxy.toLatin1()); + qputenv("ftp_proxy", ftpProxy.toLatin1()); + } + + qputenv("NO_PROXY", "localhost,127.0.0.0/8,::1"); + qputenv("no_proxy", "localhost,127.0.0.0/8,::1"); + + if (!m_socksProxy.isEmpty() && !m_socksProxyPort.isEmpty()) { + // qputenv("ALL_PROXY", QString("socks://%1:%2/").arg(m_socksProxy).arg(m_socksProxyPort).toLatin1()); + // qputenv("all_proxy", QString("socks://%1:%2/").arg(m_socksProxy).arg(m_socksProxyPort).toLatin1()); + } + } +} diff --git a/startlingmo/lingmo-session/networkproxymanager.h b/startlingmo/lingmo-session/networkproxymanager.h new file mode 100755 index 0000000..dbbd14c --- /dev/null +++ b/startlingmo/lingmo-session/networkproxymanager.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: Reion Wong + * + * 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 . + */ + +#ifndef NETWORKPROXYMANAGER_H +#define NETWORKPROXYMANAGER_H + +#include +#include + +class NetworkProxyManager : public QObject +{ + Q_OBJECT + +public: + explicit NetworkProxyManager(QObject *parent = nullptr); + + void update(); + +private: + QSettings m_settings; + + int m_flag; + bool m_useSameProxy; + + QString m_scriptProxy; + QString m_httpProxy; + QString m_ftpProxy; + QString m_socksProxy; + + QString m_httpProxyPort; + QString m_ftpProxyPort; + QString m_socksProxyPort; +}; + +#endif // NETWORKPROXYMANAGER_H diff --git a/startlingmo/lingmo-session/powermanager/power.cpp b/startlingmo/lingmo-session/powermanager/power.cpp new file mode 100755 index 0000000..534b6be --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/power.cpp @@ -0,0 +1,80 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#include "power.h" +#include "powerproviders.h" + +#include +#include + +Power::Power(bool useSessionProvider, QObject * parent /*= nullptr*/) : + QObject(parent) +{ + m_providers.append(new SystemdProvider(this)); + m_providers.append(new UPowerProvider(this)); + m_providers.append(new ConsoleKitProvider(this)); +} + +Power::Power(QObject * parent /*= nullptr*/) + : Power(true, parent) +{ +} + +Power::~Power() +{ +} + +bool Power::canAction(Power::Action action) const +{ + for(const PowerProvider* provider : qAsConst(m_providers)) + if (provider->canAction(action)) + return true; + + return false; +} + +bool Power::doAction(Power::Action action) +{ + for(PowerProvider* provider : qAsConst(m_providers)) { + if (provider->canAction(action) && + provider->doAction(action)) { + return true; + } + } + return false; +} + +bool Power::canLogout() const { return canAction(PowerLogout); } +bool Power::canHibernate() const { return canAction(PowerHibernate); } +bool Power::canReboot() const { return canAction(PowerReboot); } +bool Power::canShutdown() const { return canAction(PowerShutdown); } +bool Power::canSuspend() const { return canAction(PowerSuspend); } +bool Power::canMonitorOff() const { return canAction(PowerMonitorOff); } +bool Power::canShowLeaveDialog() const { return canAction(PowerShowLeaveDialog); } + +bool Power::logout() { return doAction(PowerLogout); } +bool Power::hibernate() { return doAction(PowerHibernate); } +bool Power::reboot() { return doAction(PowerReboot); } +bool Power::shutdown() { return doAction(PowerShutdown); } +bool Power::suspend() { return doAction(PowerSuspend); } +bool Power::monitorOff() { return doAction(PowerMonitorOff); } +bool Power::showLeaveDialog() { return doAction(PowerShowLeaveDialog); } diff --git a/startlingmo/lingmo-session/powermanager/power.h b/startlingmo/lingmo-session/powermanager/power.h new file mode 100755 index 0000000..8583943 --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/power.h @@ -0,0 +1,118 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#ifndef POWER_H +#define POWER_H + +#include +#include + +class PowerProvider; + +/*! Power class provides an interface to control system-wide power and session management. + It allows logout from the user session, hibernate, reboot, shutdown and suspend computer. + This is a wrapper class. All the real work is done in the PowerWorker classes. +*/ +class Power : public QObject +{ + Q_OBJECT + +public: + /// Power can perform next actions: + enum Action{ + PowerLogout, /// Close the current user session. + PowerHibernate, /// Hibernate the comupter + PowerReboot, /// Reboot the computer + PowerShutdown, /// Shutdown the computer + PowerSuspend, /// Suspend the computer + PowerMonitorOff, /// Turn off the monitor(s) + PowerShowLeaveDialog /// Show the lxqt-leave dialog + }; + + /*! + * Constructs the Power object. + * \param useLxqtSessionProvider indicates if the DBus methods + * provided by lxqt-session should be considered. This is useful to + * avoid recursion if the lxqt-session wants to provide some of the + * methods by itself with internal use of this object. + */ + explicit Power(bool useSessionProvider, QObject *parent = nullptr); + /// Constructs a Power with using the lxqt-session provider. + explicit Power(QObject *parent = nullptr); + + /// Destroys the object. + ~Power() override; + + /// Returns true if the Power can perform action. + bool canAction(Action action) const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerLogout). + bool canLogout() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerHibernate). + bool canHibernate() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerReboot). + bool canReboot() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerShutdown). + bool canShutdown() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerSuspend). + bool canSuspend() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerMonitorOff). + bool canMonitorOff() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerShowLeaveDialog). + bool canShowLeaveDialog() const; + +public Q_SLOTS: + /// Performs the requested action. + bool doAction(Action action); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerLogout). + bool logout(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerHibernate). + bool hibernate(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerReboot). + bool reboot(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerShutdown). + bool shutdown(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerSuspend). + bool suspend(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerMonitorOff). + bool monitorOff(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerShowLeaveDialog). + bool showLeaveDialog(); + +private: + QList m_providers; +}; + +#endif diff --git a/startlingmo/lingmo-session/powermanager/powerproviders.cpp b/startlingmo/lingmo-session/powermanager/powerproviders.cpp new file mode 100755 index 0000000..63a702b --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/powerproviders.cpp @@ -0,0 +1,448 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * Petr Vanek + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#include "powerproviders.h" +#include +#include +#include +#include // for kill() + +#define UPOWER_SERVICE "org.freedesktop.UPower" +#define UPOWER_PATH "/org/freedesktop/UPower" +#define UPOWER_INTERFACE UPOWER_SERVICE + +#define CONSOLEKIT_SERVICE "org.freedesktop.ConsoleKit" +#define CONSOLEKIT_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CONSOLEKIT_INTERFACE "org.freedesktop.ConsoleKit.Manager" + +#define SYSTEMD_SERVICE "org.freedesktop.login1" +#define SYSTEMD_PATH "/org/freedesktop/login1" +#define SYSTEMD_INTERFACE "org.freedesktop.login1.Manager" + +#define PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" + +/************************************************ + Helper func + ************************************************/ +void printDBusMsg(const QDBusMessage &msg) +{ + qWarning() << "** Dbus error **************************"; + qWarning() << "Error name " << msg.errorName(); + qWarning() << "Error msg " << msg.errorMessage(); + qWarning() << "****************************************"; +} + +/************************************************ + Helper func + ************************************************/ +static bool dbusCall(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection, + const QString & method, + PowerProvider::DbusErrorCheck errorCheck = PowerProvider::CheckDBUS + ) +{ + QDBusInterface dbus(service, path, interface, connection); + + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service << path << interface << method; + if (errorCheck == PowerProvider::CheckDBUS) + { + // Notification::notify( + // QObject::tr("Power Manager Error"), + // QObject::tr("QDBusInterface is invalid") + QStringLiteral("\n\n") + service + QStringLiteral(' ') + path + QStringLiteral(' ') + interface + QStringLiteral(' ') + method, + // QStringLiteral("logo.png")); + } + return false; + } + + QDBusMessage msg = dbus.call(method); + if (!msg.errorName().isEmpty()) { + printDBusMsg(msg); + if (errorCheck == PowerProvider::CheckDBUS) + { + // Notification::notify( + // QObject::tr("Power Manager Error (D-BUS call)"), + // msg.errorName() + QStringLiteral("\n\n") + msg.errorMessage(), + // QStringLiteral("logo.png")); + } + } + + // If the method no returns value, we believe that it was successful. + return msg.arguments().isEmpty() || + msg.arguments().constFirst().isNull() || + msg.arguments().constFirst().toBool(); +} + +/************************************************ + Helper func + + Just like dbusCall(), except that systemd + returns a string instead of a bool, and it takes + an "interactivity boolean" as an argument. + ************************************************/ +static bool dbusCallSystemd(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection, + const QString &method, + bool needBoolArg, + PowerProvider::DbusErrorCheck errorCheck = PowerProvider::CheckDBUS + ) +{ + QDBusInterface dbus(service, path, interface, connection); + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service << path << interface << method; + if (errorCheck == PowerProvider::CheckDBUS) { + // Notification::notify( + // QObject::tr("Power Manager Error"), + // QObject::tr("QDBusInterface is invalid") + QStringLiteral("\n\n") + service + QStringLiteral(' ') + path + QStringLiteral(' ')+ interface + QStringLiteral(' ') + method, + // QStringLiteral("logo.png")); + } + + return false; + } + + QDBusMessage msg = dbus.call(method, needBoolArg ? QVariant(true) : QVariant()); + + if (!msg.errorName().isEmpty()) { + printDBusMsg(msg); + if (errorCheck == PowerProvider::CheckDBUS) { + // Notification::notify( + // QObject::tr("Power Manager Error (D-BUS call)"), + // msg.errorName() + QStringLiteral("\n\n") + msg.errorMessage(), + // QStringLiteral("logo.png")); + } + } + + // If the method no returns value, we believe that it was successful. + if (msg.arguments().isEmpty() || msg.arguments().constFirst().isNull()) + return true; + + QString response = msg.arguments().constFirst().toString(); + qDebug() << "systemd:" << method << "=" << response; + return response == QStringLiteral("yes") || response == QStringLiteral("challenge"); +} + +/************************************************ + Helper func + ************************************************/ +bool dbusGetProperty(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection, + const QString & property + ) +{ + QDBusInterface dbus(service, path, interface, connection); + if (!dbus.isValid()) + { + qWarning() << "dbusGetProperty: QDBusInterface is invalid" << service << path << interface << property; +// Notification::notify(QObject::tr("Power Manager"), +// "logo.png", +// QObject::tr("Power Manager Error"), +// QObject::tr("QDBusInterface is invalid")+ "\n\n" + service +" " + path +" " + interface +" " + property); + + return false; + } + + QDBusMessage msg = dbus.call(QStringLiteral("Get"), dbus.interface(), property); + + if (!msg.errorName().isEmpty()) + { + printDBusMsg(msg); +// Notification::notify(QObject::tr("Power Manager"), +// "logo.png", +// QObject::tr("Power Manager Error (Get Property)"), +// msg.errorName() + "\n\n" + msg.errorMessage()); + } + + return !msg.arguments().isEmpty() && + msg.arguments().constFirst().value().variant().toBool(); +} + +/************************************************ + PowerProvider + ************************************************/ +PowerProvider::PowerProvider(QObject *parent): + QObject(parent) +{ +} + +PowerProvider::~PowerProvider() +{ +} + +/************************************************ + UPowerProvider + ************************************************/ +UPowerProvider::UPowerProvider(QObject *parent): + PowerProvider(parent) +{ +} + +UPowerProvider::~UPowerProvider() +{ +} + +bool UPowerProvider::canAction(Power::Action action) const +{ + QString command; + QString property; + switch (action) { + case Power::PowerHibernate: + property = QStringLiteral("CanHibernate"); + command = QStringLiteral("HibernateAllowed"); + break; + case Power::PowerSuspend: + property = QStringLiteral("CanSuspend"); + command = QStringLiteral("SuspendAllowed"); + break; + default: + return false; + } + + return dbusGetProperty( // Whether the system is able to hibernate. + QStringLiteral(UPOWER_SERVICE), + QStringLiteral(UPOWER_PATH), + QStringLiteral(PROPERTIES_INTERFACE), + QDBusConnection::systemBus(), + property + ) + && + dbusCall( // Check if the caller has (or can get) the PolicyKit privilege to call command. + QStringLiteral(UPOWER_SERVICE), + QStringLiteral(UPOWER_PATH), + QStringLiteral(UPOWER_INTERFACE), + QDBusConnection::systemBus(), + command, + // canAction should be always silent because it can freeze + // g_main_context_iteration Qt event loop in QMessageBox + // on panel startup if there is no DBUS running. + PowerProvider::DontCheckDBUS + ); +} + +bool UPowerProvider::doAction(Power::Action action) +{ + QString command; + + switch (action) { + case Power::PowerHibernate: + command = QStringLiteral("Hibernate"); + break; + case Power::PowerSuspend: + command = QStringLiteral("Suspend"); + break; + default: + return false; + } + + + return dbusCall(QStringLiteral(UPOWER_SERVICE), + QStringLiteral(UPOWER_PATH), + QStringLiteral(UPOWER_INTERFACE), + QDBusConnection::systemBus(), + command ); +} + +/************************************************ + ConsoleKitProvider + ************************************************/ +ConsoleKitProvider::ConsoleKitProvider(QObject *parent): + PowerProvider(parent) +{ +} + +ConsoleKitProvider::~ConsoleKitProvider() +{ +} + +bool ConsoleKitProvider::canAction(Power::Action action) const +{ + QString command; + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("CanReboot"); + break; + + case Power::PowerShutdown: + command = QStringLiteral("CanPowerOff"); + break; + + case Power::PowerHibernate: + command = QStringLiteral("CanHibernate"); + break; + + case Power::PowerSuspend: + command = QStringLiteral("CanSuspend"); + break; + + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(CONSOLEKIT_SERVICE), + QStringLiteral(CONSOLEKIT_PATH), + QStringLiteral(CONSOLEKIT_INTERFACE), + QDBusConnection::systemBus(), + command, + false, + // canAction should be always silent because it can freeze + // g_main_context_iteration Qt event loop in QMessageBox + // on panel startup if there is no DBUS running. + PowerProvider::DontCheckDBUS + ); +} + +bool ConsoleKitProvider::doAction(Power::Action action) +{ + QString command; + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("Reboot"); + break; + case Power::PowerShutdown: + command = QStringLiteral("PowerOff"); + break; + case Power::PowerHibernate: + command = QStringLiteral("Hibernate"); + break; + case Power::PowerSuspend: + command = QStringLiteral("Suspend"); + break; + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(CONSOLEKIT_SERVICE), + QStringLiteral(CONSOLEKIT_PATH), + QStringLiteral(CONSOLEKIT_INTERFACE), + QDBusConnection::systemBus(), + command, + true); +} + +/************************************************ + SystemdProvider + + http://www.freedesktop.org/wiki/Software/systemd/logind + ************************************************/ + +SystemdProvider::SystemdProvider(QObject *parent): + PowerProvider(parent) +{ +} + +SystemdProvider::~SystemdProvider() +{ +} + +bool SystemdProvider::canAction(Power::Action action) const +{ + QString command; + + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("CanReboot"); + break; + case Power::PowerShutdown: + command = QStringLiteral("CanPowerOff"); + break; + case Power::PowerSuspend: + command = QStringLiteral("CanSuspend"); + break; + case Power::PowerHibernate: + command = QStringLiteral("CanHibernate"); + break; + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(SYSTEMD_SERVICE), + QStringLiteral(SYSTEMD_PATH), + QStringLiteral(SYSTEMD_INTERFACE), + QDBusConnection::systemBus(), + command, + false, + // canAction should be always silent because it can freeze + // g_main_context_iteration Qt event loop in QMessageBox + // on panel startup if there is no DBUS running. + PowerProvider::DontCheckDBUS + ); +} + +bool SystemdProvider::doAction(Power::Action action) +{ + QString command; + + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("Reboot"); + break; + case Power::PowerShutdown: + command = QStringLiteral("PowerOff"); + break; + case Power::PowerSuspend: + command = QStringLiteral("Suspend"); + break; + case Power::PowerHibernate: + command = QStringLiteral("Hibernate"); + break; + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(SYSTEMD_SERVICE), + QStringLiteral(SYSTEMD_PATH), + QStringLiteral(SYSTEMD_INTERFACE), + QDBusConnection::systemBus(), + command, + true + ); +} + +/************************************************ + HalProvider + ************************************************/ +HalProvider::HalProvider(QObject *parent): + PowerProvider(parent) +{ +} + +HalProvider::~HalProvider() +{ +} + +bool HalProvider::canAction(Power::Action action) const +{ + Q_UNUSED(action) + return false; +} + +bool HalProvider::doAction(Power::Action action) +{ + Q_UNUSED(action) + return false; +} diff --git a/startlingmo/lingmo-session/powermanager/powerproviders.h b/startlingmo/lingmo-session/powermanager/powerproviders.h new file mode 100755 index 0000000..ec77cb8 --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/powerproviders.h @@ -0,0 +1,109 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#ifndef POWERPROVIDERS_H +#define POWERPROVIDERS_H + +#include "power.h" + +#include +#include // for PID_T + +class PowerProvider: public QObject +{ + Q_OBJECT + +public: + enum DbusErrorCheck { + CheckDBUS, + DontCheckDBUS + }; + + explicit PowerProvider(QObject *parent = nullptr); + ~PowerProvider() override; + + /*! Returns true if the Power can perform action. + This is a pure virtual function, and must be reimplemented in subclasses. */ + virtual bool canAction(Power::Action action) const = 0 ; + +public Q_SLOTS: + /*! Performs the requested action. + This is a pure virtual function, and must be reimplemented in subclasses. */ + virtual bool doAction(Power::Action action) = 0; +}; + + +class UPowerProvider: public PowerProvider +{ + Q_OBJECT + +public: + UPowerProvider(QObject *parent = nullptr); + ~UPowerProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + + +class ConsoleKitProvider: public PowerProvider +{ + Q_OBJECT + +public: + ConsoleKitProvider(QObject *parent = nullptr); + ~ConsoleKitProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + + +class SystemdProvider: public PowerProvider +{ + Q_OBJECT + +public: + SystemdProvider(QObject *parent = nullptr); + ~SystemdProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + +class HalProvider: public PowerProvider +{ + Q_OBJECT + +public: + HalProvider(QObject *parent = nullptr); + ~HalProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + +#endif diff --git a/startlingmo/lingmo-session/process.cpp b/startlingmo/lingmo-session/process.cpp new file mode 100755 index 0000000..06d33a5 --- /dev/null +++ b/startlingmo/lingmo-session/process.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#include "process.h" + +Process::Process(QObject *parent) + : QProcess(parent) +{ + QProcess::setProcessChannelMode(QProcess::ForwardedChannels); +} + +Process::~Process() +{ +} diff --git a/startlingmo/lingmo-session/process.h b/startlingmo/lingmo-session/process.h new file mode 100755 index 0000000..3ab3c57 --- /dev/null +++ b/startlingmo/lingmo-session/process.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#ifndef PROCESS_H +#define PROCESS_H + +#include + +class Process : public QProcess +{ + Q_OBJECT + +public: + Process(QObject *parent = nullptr); + ~Process(); +}; + +#endif diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp new file mode 100755 index 0000000..15e4bf1 --- /dev/null +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2023-2024 Lingmo OS Team. + */ + +#include "processmanager.h" +#include "application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "daemon-helper.h" + +ProcessManager::ProcessManager(Application *app, QObject *parent) + : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { + qApp->installNativeEventFilter(this); +} + +ProcessManager::~ProcessManager() { + qApp->removeNativeEventFilter(this); + + QMapIterator i(m_systemProcess); + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + delete p; + m_systemProcess[i.key()] = nullptr; + } +} + +void ProcessManager::start() { + startWindowManager(); + startDaemonProcess(); +} + +void ProcessManager::logout() { + QMapIterator i(m_systemProcess); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + p->terminate(); + } + i.toFront(); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { + p->kill(); + } + } + + QCoreApplication::exit(0); +} + +void ProcessManager::startWindowManager() { + auto detcted_wayland = + qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); + + if (detcted_wayland || m_app->wayland()) { + } else { + auto *wmProcess = new QProcess; + + wmProcess->start("kwin_x11", QStringList()); + } + + if (!m_app->wayland()) { + QEventLoop waitLoop; + m_waitLoop = &waitLoop; + // add a timeout to avoid infinite blocking if a WM fail to execute. + QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); + waitLoop.exec(); + m_waitLoop = nullptr; + } +} + +void ProcessManager::startDesktopProcess() { + // When the lingmo-settings-daemon theme module is loaded, start the desktop. + // In the way, there will be no problem that desktop and launcher can't get + // wallpaper. + + QList> list; + // Desktop components + list << qMakePair(QString("lingmo-notificationd"), QStringList()); + list << qMakePair(QString("lingmo-statusbar"), QStringList()); + list << qMakePair(QString("lingmo-dock"), QStringList()); + list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); + list << qMakePair(QString("lingmo-launcher"), QStringList()); + list << qMakePair(QString("lingmo-powerman"), QStringList()); + list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); + + m_desktopAutoStartD = std::make_shared(list); + + // Auto start + QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); +} + +void ProcessManager::startDaemonProcess() { + QList> list; + list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); + list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); + list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); + list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); + // list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-chotkeys"), QStringList()); + + m_daemonAutoStartD = std::make_shared(list); +} + +void ProcessManager::loadAutoStartProcess() { + QList> list; + + const QStringList dirs = QStandardPaths::locateAll( + QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), + QStandardPaths::LocateDirectory); + for (const QString &dir : dirs) { + const QDir d(dir); + const QStringList fileNames = + d.entryList(QStringList() << QStringLiteral("*.desktop")); + for (const QString &file : fileNames) { + QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); + desktop.setIniCodec("UTF-8"); + desktop.beginGroup("Desktop Entry"); + + // Ignore files the require a specific desktop environment + if (desktop.contains("NotShowIn")) { + const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); + if (notShowIn.contains("Lingmo")) + continue; + } + if (desktop.contains("OnlyShowIn")) { + const QStringList onlyShowIn = + desktop.value("OnlyShowIn").toStringList(); + if (!onlyShowIn.contains("Lingmo")) + continue; + } + + const QString execValue = desktop.value("Exec").toString(); + + // 避免冲突 + if (execValue.contains("gmenudbusmenuproxy")) + continue; + + // 使用 QProcess::splitCommand 来解析命令和参数 + QStringList args = QProcess::splitCommand(execValue); + + // 检查是否至少有一个元素(即程序路径) + if (!args.isEmpty()) { + auto program = args.first(); + args.removeFirst(); // 移除程序路径,剩下的都是参数 + + list << qMakePair(program, args); + } else { + qWarning() << "Invalid 'Exec' found in file!"; + } + } + } + + m_userAutoStartD = std::make_shared(list, false); +} + +bool ProcessManager::nativeEventFilter(const QByteArray &eventType, + void *message, long *result) { + if (eventType != "xcb_generic_event_t") // We only want to handle XCB events + return false; + + // ref: lxqt session + if (!m_wmStarted && m_waitLoop) { + // all window managers must set their name according to the spec + if (!QString::fromUtf8( + NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) + .wmName()) + .isEmpty()) { + qDebug() << "Window manager started"; + m_wmStarted = true; + if (m_waitLoop && m_waitLoop->isRunning()) + m_waitLoop->exit(); + + qApp->removeNativeEventFilter(this); + } + } + + return false; +} + +StartProcessJob::StartProcessJob(const QString &process, + const QStringList &args, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(finished(int, QProcess::ExitStatus))); +} + +void StartProcessJob::start() { + qDebug() << "Starting " << m_process->program() << m_process->arguments(); + + m_process->start(); +} + +void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { + qDebug() << "process job " << m_process->program() + << "finished with exit code " << exitCode; + emitResult(); +} + +StartServiceJob::StartServiceJob(const QString &process, + const QStringList &args, + const QString &serviceId, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess), m_serviceId(serviceId), + m_additionalEnv(additionalEnv) { + m_process->setProgram(process); + m_process->setArguments(args); + + auto watcher = + new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, this); + connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, + &StartServiceJob::emitResult); +} + +void StartServiceJob::start() { + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(m_additionalEnv); + m_process->setProcessEnvironment(env); + + if (!m_serviceId.isEmpty() && + QDBusConnection::sessionBus().interface()->isServiceRegistered( + m_serviceId)) { + qDebug() << m_process << "already running"; + emitResult(); + return; + } + qDebug() << "Starting " << m_process->program() << m_process->arguments(); + if (!startDetached(m_process)) { + qWarning() << "error starting process" << m_process->program() + << m_process->arguments(); + emitResult(); + } + + if (m_serviceId.isEmpty()) { + emitResult(); + } +} + +bool StartServiceJob::startDetached(QProcess *process) { + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->start(); + const bool ret = process->waitForStarted(); + return ret; +} \ No newline at end of file diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h new file mode 100755 index 0000000..a313008 --- /dev/null +++ b/startlingmo/lingmo-session/processmanager.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#ifndef PROCESSMANAGER_H +#define PROCESSMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "daemon-helper.h" + +class Application; +class ProcessManager : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT + +public: + explicit ProcessManager(Application *app, QObject *parent = nullptr); + ~ProcessManager(); + + void start(); + void logout(); + + void startWindowManager(); + void startDesktopProcess(); + void startDaemonProcess(); + + /** + * @brief Start the user defined autostart process. + * Typically, they are in /.config/autostart/xxx.desktop + */ + void loadAutoStartProcess(); + + bool nativeEventFilter(const QByteArray &eventType, void *message, + long *result) override; + +private: + Application *m_app; + QMap m_systemProcess; + QMap m_autoStartProcess; + + // Daemon helper for desktop components + std::shared_ptr m_desktopAutoStartD; + + // Daemon helper for other daemon components + std::shared_ptr m_daemonAutoStartD; + + // Daemon helper for User Auto Start Process + std::shared_ptr m_userAutoStartD; + + bool m_wmStarted; + QEventLoop *m_waitLoop; +}; + +using namespace LINGMO_SESSION; +/** + * Launches a process, and waits for the process to start + */ +class StartProcessJob : public Job { + Q_OBJECT +public: + StartProcessJob( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + + void finished(int exitCode, QProcess::ExitStatus e); + +private: + QProcess *m_process; +}; + +/** + * Launches a process, and waits for the service to appear on the session bus + */ +class StartServiceJob : public Job { + Q_OBJECT +public: + StartServiceJob( + const QString &process, const QStringList &args, const QString &serviceId, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + + void start() override; + + bool startDetached(QProcess *process); + +private: + QProcess *m_process; + const QString m_serviceId; + const QProcessEnvironment m_additionalEnv; +}; +#endif // PROCESSMANAGER_H diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp index d0c13db..89314bd 100644 --- a/startlingmo/startlingmo.cpp +++ b/startlingmo/startlingmo.cpp @@ -435,6 +435,12 @@ bool startLingmoSession(bool wayland) { std::unique_ptr startLingmoSession; + QStringList lingmoSessionOptions; + + if (wayland) { + lingmoSessionOptions << QStringLiteral("--wayland"); + } + { startLingmoSession.reset(new QProcess); qCDebug(LINGMO_STARTUP) << "Using classic boot"; @@ -443,7 +449,7 @@ bool startLingmoSession(bool wayland) { startLingmoSession->start( QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/lingmo_session"), - QStringList{}); + lingmoSessionOptions); } if (rc) { @@ -452,4 +458,4 @@ bool startLingmoSession(bool wayland) { e.exec(); } return rc; -} \ No newline at end of file +} diff --git a/startlingmo/startlingmo.hpp b/startlingmo/startlingmo.hpp index 48b0270..7e83bd4 100644 --- a/startlingmo/startlingmo.hpp +++ b/startlingmo/startlingmo.hpp @@ -56,4 +56,5 @@ struct KillBeforeDeleter { } }; +bool isSessionVariable(const QByteArray &name); #endif // STARTLINGMO_HPP \ No newline at end of file From 5befd7faa1c077816c1f94a657ebc8b658071120 Mon Sep 17 00:00:00 2001 From: Elysia Date: Wed, 7 Aug 2024 21:34:46 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8D=B8=E8=BD=BD?= =?UTF-8?q?=E8=84=9A=E6=9C=AC&=E8=BF=98=E6=98=AF=E5=8F=AA=E8=83=BD?= =?UTF-8?q?=E7=9C=8B=E9=BC=A0=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 11 + cmake_uninstall.cmake.in | 21 + startlingmo/lingmo-session/application.cpp | 513 +++++++++--------- startlingmo/lingmo-session/application.h | 87 +-- startlingmo/lingmo-session/daemon-helper.cpp | 2 + startlingmo/lingmo-session/main.cpp | 21 +- startlingmo/lingmo-session/processmanager.cpp | 24 +- startlingmo/lingmo-session/processmanager.h | 3 +- startlingmo/startlingmo.cpp | 1 - 9 files changed, 367 insertions(+), 316 deletions(-) create mode 100644 cmake_uninstall.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index efdf2ef..fabed3d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,3 +39,14 @@ add_subdirectory(startlingmo) install(FILES lingmo DESTINATION /etc/ COMPONENT Runtime) install(FILES Desktop/Lingmo DESTINATION /etc/Desktop/ COMPONENT Runtime) install(FILES wallpaper-color-pick/lingmo-wallpaper-color-pick DESTINATION "/usr/bin/") + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000..c2d34d4 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/startlingmo/lingmo-session/application.cpp b/startlingmo/lingmo-session/application.cpp index d740f37..52f7a0d 100755 --- a/startlingmo/lingmo-session/application.cpp +++ b/startlingmo/lingmo-session/application.cpp @@ -21,13 +21,12 @@ #include "sessionadaptor.h" // Qt -#include #include -#include -#include -#include #include #include +#include +#include +#include #include #include @@ -37,322 +36,320 @@ #include "startlingmo.hpp" +Application::Application(const QCommandLineParser &parser, QObject *parent) + : QObject(parent), m_processManager(new ProcessManager(this)), + m_networkProxyManager(new NetworkProxyManager), m_wayland(false) { + new SessionAdaptor(this); -Application::Application(int &argc, char **argv) - : QApplication(argc, argv) - , m_processManager(new ProcessManager(this)) - , m_networkProxyManager(new NetworkProxyManager) - , m_wayland(false) -{ - new SessionAdaptor(this); - - // connect to D-Bus and register as an object: - QDBusConnection::sessionBus().registerService(QStringLiteral("com.lingmo.Session")); - QDBusConnection::sessionBus().registerObject(QStringLiteral("/Session"), this); + // connect to D-Bus and register as an object: + QDBusConnection::sessionBus().registerService( + QStringLiteral("com.lingmo.Session")); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Session"), + this); - QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("Lingmo Session")); - parser.addHelpOption(); + m_wayland = parser.isSet("wayland"); - QCommandLineOption waylandOption(QStringList() << "w" << "wayland" << "Wayland Mode"); - parser.addOption(waylandOption); - parser.process(*this); + // createConfigDirectory(); + // initKWinConfig(); + // initLanguage(); + // initScreenScaleFactors(); + // initXResource(); - m_wayland = parser.isSet(waylandOption); + // initEnvironments(); - // createConfigDirectory(); - // initKWinConfig(); - // initLanguage(); - initScreenScaleFactors(); - initXResource(); + if (!syncDBusEnvironment()) { + // Startup error + qDebug() << "Could not sync environment to dbus."; + qApp->exit(1); + } - 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(); - // 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(); + // qunsetenv("XCURSOR_THEME"); + // qunsetenv("XCURSOR_SIZE"); + // qunsetenv("SESSION_MANAGER"); - qunsetenv("XCURSOR_THEME"); - qunsetenv("XCURSOR_SIZE"); - qunsetenv("SESSION_MANAGER"); + m_networkProxyManager->update(); - m_networkProxyManager->update(); + // QTimer::singleShot(50, this, &Application::updateUserDirs); - // QTimer::singleShot(50, this, &Application::updateUserDirs); - - // Launch Lingmo and user defined processes ! - QTimer::singleShot(100, m_processManager, &ProcessManager::start); + // Launch Lingmo and user defined processes ! + QTimer::singleShot(100, m_processManager, &ProcessManager::start); } -bool Application::wayland() const -{ - return m_wayland; -} +bool Application::wayland() const { return m_wayland; } -void Application::launch(const QString &exec, const QStringList &args) -{ - QProcess process; - process.setProgram(exec); - process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); - process.setArguments(args); - process.startDetached(); +void Application::launch(const QString &exec, const QStringList &args) { + QProcess process; + process.setProgram(exec); + process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); + process.setArguments(args); + process.startDetached(); } -void Application::launch(const QString &exec, const QString &workingDir, const QStringList &args) -{ - QProcess process; - process.setProgram(exec); - process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); - process.setWorkingDirectory(workingDir); - process.setArguments(args); - process.startDetached(); +void Application::launch(const QString &exec, const QString &workingDir, + const QStringList &args) { + QProcess process; + process.setProgram(exec); + process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); + process.setWorkingDirectory(workingDir); + process.setArguments(args); + process.startDetached(); } -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", "Lingmo"); - qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); - qputenv("XDG_SESSION_DESKTOP", "Lingmo"); - - // Qt - qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); - qputenv("QT_PLATFORM_PLUGIN", "lingmo"); - - // ref: https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping - qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); - - qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); - - // 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::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", "Lingmo"); + qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); + qputenv("XDG_SESSION_DESKTOP", "Lingmo"); + + // Qt + qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); + qputenv("QT_PLATFORM_PLUGIN", "lingmo"); + + // ref: + // https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping + qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); + + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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::initLanguage() -{ - QSettings settings(QSettings::UserScope, "lingmoos", "language"); - QString value = settings.value("language", "").toString(); +void Application::initLanguage() { + QSettings settings(QSettings::UserScope, "lingmoos", "language"); + QString value = settings.value("language", "").toString(); - // Init Language - if (value.isEmpty()) { - QFile file("/etc/locale.gen"); - if (file.open(QIODevice::ReadOnly)) { - QStringList lines = QString(file.readAll()).split('\n'); + // Init Language + if (value.isEmpty()) { + QFile file("/etc/locale.gen"); + if (file.open(QIODevice::ReadOnly)) { + QStringList lines = QString(file.readAll()).split('\n'); - for (const QString &line : lines) { - if (line.startsWith('#')) - continue; + for (const QString &line : lines) { + if (line.startsWith('#')) + continue; - if (line.trimmed().isEmpty()) - continue; + if (line.trimmed().isEmpty()) + continue; - value = line.split(' ').first().split('.').first(); - } - } + value = line.split(' ').first().split('.').first(); + } } + } - if (value.isEmpty()) - value = "en_US"; + if (value.isEmpty()) + value = "en_US"; - settings.setValue("language", value); + settings.setValue("language", value); - QString str = QString("%1.UTF-8").arg(value); + 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()); - } - } + 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("LANGUAGE", value.toUtf8()); + qputenv(lc, value.toUtf8()); } + } + + if (!value.isEmpty()) { + qputenv("LANGUAGE", value.toUtf8()); + } } -void Application::initScreenScaleFactors() -{ - QSettings settings(QSettings::UserScope, "lingmoos", "theme"); - qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); +void Application::initScreenScaleFactors() { + QSettings settings(QSettings::UserScope, "lingmoos", "theme"); + qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); - qputenv("QT_SCREEN_SCALE_FACTORS", QByteArray::number(scaleFactor)); + 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)); - } + // 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)); + } } -void Application::initXResource() -{ - QSettings settings(QSettings::UserScope, "lingmoos", "theme"); - qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); - int fontDpi = 96 * scaleFactor; - QString cursorTheme = settings.value("CursorTheme", "default").toString(); - int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; - int xftAntialias = settings.value("XftAntialias", 1).toBool(); - QString xftHintStyle = settings.value("XftHintStyle", "hintslight").toString(); - - const QString datas = QString("Xft.dpi: %1\n" - "Xcursor.theme: %2\n" - "Xcursor.size: %3\n" - "Xft.antialias: %4\n" - "Xft.hintstyle: %5\n" - "Xft.rgba: rgb") - .arg(fontDpi) - .arg(cursorTheme) - .arg(cursorSize) - .arg(xftAntialias) - .arg(xftHintStyle); - - QProcess p; - p.start(QStringLiteral("xrdb"), {QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp")}); - p.setProcessChannelMode(QProcess::ForwardedChannels); - p.write(datas.toLatin1()); - p.closeWriteChannel(); - p.waitForFinished(-1); - - // For lingmo-wine - qputenv("LINGMO_FONT_DPI", QByteArray::number(fontDpi)); - - // Init cursor - runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); - // qputenv("XCURSOR_THEME", cursorTheme.toLatin1()); - // qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor)); +void Application::initXResource() { + QSettings settings(QSettings::UserScope, "lingmoos", "theme"); + qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); + int fontDpi = 96 * scaleFactor; + QString cursorTheme = settings.value("CursorTheme", "default").toString(); + int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; + int xftAntialias = settings.value("XftAntialias", 1).toBool(); + QString xftHintStyle = + settings.value("XftHintStyle", "hintslight").toString(); + + const QString datas = QString("Xft.dpi: %1\n" + "Xcursor.theme: %2\n" + "Xcursor.size: %3\n" + "Xft.antialias: %4\n" + "Xft.hintstyle: %5\n" + "Xft.rgba: rgb") + .arg(fontDpi) + .arg(cursorTheme) + .arg(cursorSize) + .arg(xftAntialias) + .arg(xftHintStyle); + + QProcess p; + p.start(QStringLiteral("xrdb"), + {QStringLiteral("-quiet"), QStringLiteral("-merge"), + QStringLiteral("-nocpp")}); + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.write(datas.toLatin1()); + p.closeWriteChannel(); + p.waitForFinished(-1); + + // For lingmo-wine + qputenv("LINGMO_FONT_DPI", QByteArray::number(fontDpi)); + + // Init cursor + runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); + // qputenv("XCURSOR_THEME", cursorTheme.toLatin1()); + // qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor)); } -void Application::initKWinConfig() -{ - QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/kwinrc", - QSettings::IniFormat); - - settings.beginGroup("Effect-Blur"); - settings.setValue("BlurStrength", 10); - settings.setValue("NoiseStrength", 0); - settings.endGroup(); - - settings.beginGroup("Windows"); - settings.setValue("FocusStealingPreventionLevel", 0); - settings.setValue("HideUtilityWindowsForInactive", false); - settings.setValue("BorderlessMaximizedWindows", false); - settings.setValue("Placement", "Centered"); - settings.endGroup(); - - settings.beginGroup("org.kde.kdecoration2"); - settings.setValue("BorderSize", "Normal"); - settings.setValue("ButtonsOnLeft", ""); - settings.setValue("ButtonsOnRight", "HIAX"); - settings.setValue("library", "org.lingmo.decoration"); - settings.setValue("theme", ""); - settings.endGroup(); +void Application::initKWinConfig() { + QSettings settings( + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + "/kwinrc", + QSettings::IniFormat); + + settings.beginGroup("Effect-Blur"); + settings.setValue("BlurStrength", 10); + settings.setValue("NoiseStrength", 0); + settings.endGroup(); + + settings.beginGroup("Windows"); + settings.setValue("FocusStealingPreventionLevel", 0); + settings.setValue("HideUtilityWindowsForInactive", false); + settings.setValue("BorderlessMaximizedWindows", false); + settings.setValue("Placement", "Centered"); + settings.endGroup(); + + settings.beginGroup("org.kde.kdecoration2"); + settings.setValue("BorderSize", "Normal"); + settings.setValue("ButtonsOnLeft", ""); + settings.setValue("ButtonsOnRight", "HIAX"); + settings.setValue("library", "org.lingmo.decoration"); + settings.setValue("theme", ""); + settings.endGroup(); } -bool Application::syncDBusEnvironment() -{ - int exitCode = 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") }); - } + // 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; + 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 lingmoDE is not started by systemd. -void Application::importSystemdEnvrionment() -{ - auto environment = getSystemdEnvironment(); - if (!environment) { - return; +void Application::importSystemdEnvrionment() { + auto environment = getSystemdEnvironment(); + if (!environment) { + return; + } + + for (auto &envString : environment->toStringList()) { + const auto env = envString.toLocal8Bit(); + const int idx = env.indexOf('='); + if (Q_UNLIKELY(idx <= 0)) { + continue; } - for (auto &envString : environment->toStringList()) { - 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)); + 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); +void Application::createConfigDirectory() { + const QString configDir = + QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); - if (!QDir().mkpath(configDir)) - qDebug() << "Could not create config directory XDG_CONFIG_HOME: " << configDir; + if (!QDir().mkpath(configDir)) + qDebug() << "Could not create config directory XDG_CONFIG_HOME: " + << configDir; } -void Application::updateUserDirs() -{ - // bool isLingmoOS = QFile::exists("/etc/lingmoos"); +void Application::updateUserDirs() { + // bool isLingmoOS = QFile::exists("/etc/lingmoos"); - // if (!isLingmoOS) - // return; + // if (!isLingmoOS) + // return; - // QProcess p; - // p.setEnvironment(QStringList() << "LC_ALL=C"); - // p.start("xdg-user-dirs-update", QStringList() << "--force"); - // p.waitForFinished(-1); + // QProcess p; + // p.setEnvironment(QStringList() << "LC_ALL=C"); + // p.start("xdg-user-dirs-update", QStringList() << "--force"); + // p.waitForFinished(-1); } -int Application::runSync(const QString &program, const QStringList &args, const QStringList &env) -{ - QProcess p; +int Application::runSync(const QString &program, const QStringList &args, + const QStringList &env) { + QProcess p; - if (!env.isEmpty()) - p.setEnvironment(QProcess::systemEnvironment() << env); + if (!env.isEmpty()) + p.setEnvironment(QProcess::systemEnvironment() << env); - p.setProcessChannelMode(QProcess::ForwardedChannels); - p.start(program, args); - p.waitForFinished(-1); + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.start(program, args); + p.waitForFinished(-1); - if (p.exitCode()) { - qWarning() << program << args << "exited with code" << p.exitCode(); - } + if (p.exitCode()) { + qWarning() << program << args << "exited with code" << p.exitCode(); + } - return p.exitCode(); + return p.exitCode(); } diff --git a/startlingmo/lingmo-session/application.h b/startlingmo/lingmo-session/application.h index 0920420..3ea7640 100755 --- a/startlingmo/lingmo-session/application.h +++ b/startlingmo/lingmo-session/application.h @@ -21,71 +21,72 @@ #define APPLICATION_H #include +#include #include +#include +#include #include -#include "processmanager.h" #include "networkproxymanager.h" #include "powermanager/power.h" +#include "processmanager.h" -class Application : public QApplication -{ - Q_OBJECT +class Application : public QObject { + Q_OBJECT public: - explicit Application(int &argc, char **argv); + explicit Application(const QCommandLineParser &parser, + QObject *parent = nullptr); - bool wayland() const; + bool wayland() const; public slots: - void logout() { - m_processManager->logout(); - } + void logout() { m_processManager->logout(); } - void reboot() { - m_power.reboot(); - QCoreApplication::exit(0); - } + void reboot() { + m_power.reboot(); + QCoreApplication::exit(0); + } - void powerOff() { - m_power.shutdown(); - QCoreApplication::exit(0); - } + void powerOff() { + m_power.shutdown(); + QCoreApplication::exit(0); + } - void suspend() { - m_power.suspend(); - } + void suspend() { m_power.suspend(); } - [[maybe_unused]] void startDesktopProcess() { - // Start Lingmo Desktop Environment - m_processManager->startDesktopProcess(); - } + [[maybe_unused]] void startDesktopProcess() { + // Start Lingmo Desktop Environment + m_processManager->startDesktopProcess(); + } - [[maybe_unused]] void updateNetworkProxy() { - m_networkProxyManager->update(); - } + [[maybe_unused]] void updateNetworkProxy() { + m_networkProxyManager->update(); + } - void launch(const QString &exec, const QStringList &args); - void launch(const QString &exec, const QString &workingDir, const QStringList &args); + void launch(const QString &exec, const QStringList &args); + void launch(const QString &exec, const QString &workingDir, + const QStringList &args); private: - void initEnvironments(); - void initLanguage(); - void initScreenScaleFactors(); - void initXResource(); - void initKWinConfig(); - bool syncDBusEnvironment(); - void importSystemdEnvrionment(); - void createConfigDirectory(); - void updateUserDirs(); - int runSync(const QString &program, const QStringList &args, const QStringList &env = {}); + void initEnvironments(); + void initLanguage(); + void initScreenScaleFactors(); + void initXResource(); + void initKWinConfig(); + bool syncDBusEnvironment(); + void importSystemdEnvrionment(); + void createConfigDirectory(); + void updateUserDirs(); + int runSync(const QString &program, const QStringList &args, + const QStringList &env = {}); private: - ProcessManager *m_processManager; - NetworkProxyManager *m_networkProxyManager; - Power m_power; + ProcessManager *m_processManager; + NetworkProxyManager *m_networkProxyManager; + Power m_power; - bool m_wayland; + bool m_wayland; }; #endif // APPLICATION_H diff --git a/startlingmo/lingmo-session/daemon-helper.cpp b/startlingmo/lingmo-session/daemon-helper.cpp index 8516b27..10d0d42 100644 --- a/startlingmo/lingmo-session/daemon-helper.cpp +++ b/startlingmo/lingmo-session/daemon-helper.cpp @@ -44,6 +44,8 @@ void Daemon::onProcessError(QProcess::ProcessError error) { } void Daemon::startProcess(const QPair &processInfo) { + qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); + const QPointer process = new QProcess(this); if (this->m_enableAutoRestart) diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp index 8ebbc96..1faae3e 100755 --- a/startlingmo/lingmo-session/main.cpp +++ b/startlingmo/lingmo-session/main.cpp @@ -18,16 +18,29 @@ */ #include "application.h" +#include #include +#include +#include + int main(int argc, char *argv[]) { - + qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland")); QQuickWindow::setDefaultAlphaBuffer(true); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); - Application a(argc, argv); - a.setQuitOnLastWindowClosed(false); + QCoreApplication app(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("Lingmo Session")); + parser.addHelpOption(); + + QCommandLineOption waylandOption(QStringList() << "w" << "wayland" << "Wayland Mode"); + parser.addOption(waylandOption); + parser.process(app); + + new Application(parser, &app); - return a.exec(); + return app.exec(); } diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp index 15e4bf1..2eecd02 100755 --- a/startlingmo/lingmo-session/processmanager.cpp +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -26,6 +26,13 @@ #include "daemon-helper.h" +void Delay_MSec(unsigned int msec) { + QEventLoop loop; // 定义一个新的事件循环 + QTimer::singleShot( + msec, &loop, SLOT(quit())); // 创建单次定时器,槽函数为事件循环的退出函数 + loop.exec(); // 事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出 +} + ProcessManager::ProcessManager(Application *app, QObject *parent) : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { qApp->installNativeEventFilter(this); @@ -74,20 +81,19 @@ void ProcessManager::startWindowManager() { qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); if (detcted_wayland || m_app->wayland()) { + // StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), + // {QStringLiteral("--xwayland")}, + // QStringLiteral("org.kde.KWinWrapper")); + StartProcessJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}); + kwinWaylandJob.start(); + + Delay_MSec(10000); } else { auto *wmProcess = new QProcess; wmProcess->start("kwin_x11", QStringList()); } - - if (!m_app->wayland()) { - QEventLoop waitLoop; - m_waitLoop = &waitLoop; - // add a timeout to avoid infinite blocking if a WM fail to execute. - QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); - waitLoop.exec(); - m_waitLoop = nullptr; - } } void ProcessManager::startDesktopProcess() { diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h index a313008..83f04f9 100755 --- a/startlingmo/lingmo-session/processmanager.h +++ b/startlingmo/lingmo-session/processmanager.h @@ -84,7 +84,8 @@ class StartProcessJob : public Job { const QString &process, const QStringList &args, const QProcessEnvironment &additionalEnv = QProcessEnvironment()); void start() override; - + +public Q_SLOTS: void finished(int exitCode, QProcess::ExitStatus e); private: diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp index 89314bd..193e018 100644 --- a/startlingmo/startlingmo.cpp +++ b/startlingmo/startlingmo.cpp @@ -98,7 +98,6 @@ void setupCursor(bool wayland) { qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); QString cursorTheme = settings.value("CursorTheme", "default").toString(); int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; - runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); } } From 7af658e9eda192cd06948b53bd05122fab569f83 Mon Sep 17 00:00:00 2001 From: Elysia Date: Wed, 7 Aug 2024 22:00:09 +0800 Subject: [PATCH 07/24] Test to start foot --- startlingmo/lingmo-session/daemon-helper.cpp | 2 -- startlingmo/lingmo-session/processmanager.cpp | 13 +++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/startlingmo/lingmo-session/daemon-helper.cpp b/startlingmo/lingmo-session/daemon-helper.cpp index 10d0d42..8516b27 100644 --- a/startlingmo/lingmo-session/daemon-helper.cpp +++ b/startlingmo/lingmo-session/daemon-helper.cpp @@ -44,8 +44,6 @@ void Daemon::onProcessError(QProcess::ProcessError error) { } void Daemon::startProcess(const QPair &processInfo) { - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); - const QPointer process = new QProcess(this); if (this->m_enableAutoRestart) diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp index 2eecd02..b9ebb71 100755 --- a/startlingmo/lingmo-session/processmanager.cpp +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -120,12 +120,13 @@ void ProcessManager::startDesktopProcess() { void ProcessManager::startDaemonProcess() { QList> list; - list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); - list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); - list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); - list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); - // list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-chotkeys"), QStringList()); + // list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); + // list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); + // list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); + // list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); + // // list << qMakePair(QString("lingmo-clipboard"), QStringList()); + // list << qMakePair(QString("lingmo-chotkeys"), QStringList()); + list << qMakePair(QString("foot"), QStringList()); m_daemonAutoStartD = std::make_shared(list); } From cf2fa3f37ddfbd7c2416c8835af734b957f77996 Mon Sep 17 00:00:00 2001 From: Elysia Date: Thu, 8 Aug 2024 10:09:52 +0800 Subject: [PATCH 08/24] Added KWin-Wayaldn Wrapper --- startlingmo/CMakeLists.txt | 1 + startlingmo/lingmo-session/daemon-helper.h | 4 +- startlingmo/lingmo-session/main.cpp | 1 + startlingmo/lingmo-session/processmanager.cpp | 16 +- startlingmo/wayland_wrapper/CMakeLists.txt | 22 ++ startlingmo/wayland_wrapper/kwin_wrapper.cpp | 176 ++++++++++++ .../wayland_wrapper/lib/CMakeLists.txt | 20 ++ .../wayland_wrapper/lib/xauthority.cpp | 77 ++++++ startlingmo/wayland_wrapper/lib/xauthority.h | 14 + .../wayland_wrapper/lib/xwaylandsocket.cpp | 250 ++++++++++++++++++ .../wayland_wrapper/lib/xwaylandsocket.h | 40 +++ startlingmo/wayland_wrapper/wl-socket.c | 172 ++++++++++++ startlingmo/wayland_wrapper/wl-socket.h | 39 +++ 13 files changed, 822 insertions(+), 10 deletions(-) create mode 100644 startlingmo/wayland_wrapper/CMakeLists.txt create mode 100644 startlingmo/wayland_wrapper/kwin_wrapper.cpp create mode 100644 startlingmo/wayland_wrapper/lib/CMakeLists.txt create mode 100644 startlingmo/wayland_wrapper/lib/xauthority.cpp create mode 100644 startlingmo/wayland_wrapper/lib/xauthority.h create mode 100644 startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp create mode 100644 startlingmo/wayland_wrapper/lib/xwaylandsocket.h create mode 100644 startlingmo/wayland_wrapper/wl-socket.c create mode 100644 startlingmo/wayland_wrapper/wl-socket.h diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index 157cdd0..196b498 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -49,6 +49,7 @@ find_kwin_bin_path() configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startlingmo.h) add_subdirectory(lingmo-session) +add_subdirectory(wayland_wrapper) install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS lingmo-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) diff --git a/startlingmo/lingmo-session/daemon-helper.h b/startlingmo/lingmo-session/daemon-helper.h index 0098b73..e911942 100644 --- a/startlingmo/lingmo-session/daemon-helper.h +++ b/startlingmo/lingmo-session/daemon-helper.h @@ -179,7 +179,7 @@ class Job : public QObject { */ void result(Job *job); -protected: +protected : /** * Utility function to emit the result signal, and end this job. * It first notifies the observers to hide the progress for this job using @@ -190,7 +190,7 @@ class Job : public QObject { * @see result() * @see finished() */ - void emitResult(); + Q_SLOT void emitResult(); std::unique_ptr const d_ptr; diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp index 1faae3e..667647e 100755 --- a/startlingmo/lingmo-session/main.cpp +++ b/startlingmo/lingmo-session/main.cpp @@ -27,6 +27,7 @@ int main(int argc, char *argv[]) { qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland")); + qputenv("XDG_SESSION_TYPE", QByteArrayLiteral("wayland")); QQuickWindow::setDefaultAlphaBuffer(true); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp index b9ebb71..af7abd5 100755 --- a/startlingmo/lingmo-session/processmanager.cpp +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -81,14 +81,14 @@ void ProcessManager::startWindowManager() { qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); if (detcted_wayland || m_app->wayland()) { - // StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), - // {QStringLiteral("--xwayland")}, - // QStringLiteral("org.kde.KWinWrapper")); - StartProcessJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), - {QStringLiteral("--xwayland")}); - kwinWaylandJob.start(); - - Delay_MSec(10000); + StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}, + QStringLiteral("org.kde.KWinWrapper")); + // StartProcessJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), + // {QStringLiteral("--xwayland")}); + kwinWaylandJob.exec(); + + // Delay_MSec(10000); } else { auto *wmProcess = new QProcess; diff --git a/startlingmo/wayland_wrapper/CMakeLists.txt b/startlingmo/wayland_wrapper/CMakeLists.txt new file mode 100644 index 0000000..ce03f1a --- /dev/null +++ b/startlingmo/wayland_wrapper/CMakeLists.txt @@ -0,0 +1,22 @@ +add_subdirectory(lib) + +add_executable(lingmo_kwin_wayland_wrapper) +target_sources(lingmo_kwin_wayland_wrapper PRIVATE + kwin_wrapper.cpp + wl-socket.c +) + +ecm_qt_declare_logging_category(lingmo_kwin_wayland_wrapper + HEADER + wrapper_logging.h + IDENTIFIER + KWIN_WRAPPER + CATEGORY_NAME + lingmo_kwin_wayland_wrapper + DEFAULT_SEVERITY + Warning +) + +target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KF5::DBusAddons KF5::CoreAddons KWinXwaylandCommon) +set_property(TARGET lingmo_kwin_wayland_wrapper PROPERTY C_STANDARD 11) +install(TARGETS lingmo_kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/startlingmo/wayland_wrapper/kwin_wrapper.cpp b/startlingmo/wayland_wrapper/kwin_wrapper.cpp new file mode 100644 index 0000000..c1ffd77 --- /dev/null +++ b/startlingmo/wayland_wrapper/kwin_wrapper.cpp @@ -0,0 +1,176 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +/** + * This tiny executable creates a socket, then starts kwin passing it the FD to the wayland socket + * along with the name of the socket to use + * On any non-zero kwin exit kwin gets restarted. + * + * After restart kwin is relaunched but now with the KWIN_RESTART_COUNT env set to an incrementing counter + * + * It's somewhat akin to systemd socket activation, but we also need the lock file, finding the next free socket + * and so on, hence our own binary. + * + * Usage kwin_wayland_wrapper [argForKwin] [argForKwin] ... + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "wl-socket.h" +#include "wrapper_logging.h" +#include "xauthority.h" +#include "xwaylandsocket.h" + +class KWinWrapper : public QObject +{ + Q_OBJECT +public: + KWinWrapper(QObject *parent); + ~KWinWrapper(); + void run(); + +private: + wl_socket *m_socket; + + int m_crashCount = 0; + QProcess *m_kwinProcess = nullptr; + + std::unique_ptr m_xwlSocket; + QTemporaryFile m_xauthorityFile; +}; + +KWinWrapper::KWinWrapper(QObject *parent) + : QObject(parent) + , m_kwinProcess(new QProcess(this)) +{ + m_socket = wl_socket_create(); + if (!m_socket) { + qFatal("Could not create wayland socket"); + } + + if (qApp->arguments().contains(QLatin1String("--xwayland"))) { + m_xwlSocket.reset(new KWin::XwaylandSocket(KWin::XwaylandSocket::OperationMode::TransferFdsOnExec)); + if (!m_xwlSocket->isValid()) { + qCWarning(KWIN_WRAPPER) << "Failed to create Xwayland connection sockets"; + m_xwlSocket.reset(); + } + if (m_xwlSocket) { + if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) { + if (!generateXauthorityFile(m_xwlSocket->display(), &m_xauthorityFile)) { + qCWarning(KWIN_WRAPPER) << "Failed to create an Xauthority file"; + } + } + } + } +} + +KWinWrapper::~KWinWrapper() +{ + wl_socket_destroy(m_socket); + if (m_kwinProcess) { + disconnect(m_kwinProcess, nullptr, this, nullptr); + m_kwinProcess->terminate(); + m_kwinProcess->waitForFinished(); + m_kwinProcess->kill(); + m_kwinProcess->waitForFinished(); + } +} + +void KWinWrapper::run() +{ + m_kwinProcess->setProgram("kwin_wayland"); + + QStringList args; + + args << "--wayland-fd" << QString::number(wl_socket_get_fd(m_socket)); + args << "--socket" << QString::fromUtf8(wl_socket_get_display_name(m_socket)); + + if (m_xwlSocket) { + const auto xwaylandFileDescriptors = m_xwlSocket->fileDescriptors(); + for (const int &fileDescriptor : xwaylandFileDescriptors) { + args << "--xwayland-fd" << QString::number(fileDescriptor); + } + args << "--xwayland-display" << m_xwlSocket->name(); + if (m_xauthorityFile.open()) { + args << "--xwayland-xauthority" << m_xauthorityFile.fileName(); + } + } + + // attach our main process arguments + // the first entry is dropped as it will be our program name + args << qApp->arguments().mid(1); + + m_kwinProcess->setProcessChannelMode(QProcess::ForwardedChannels); + m_kwinProcess->setArguments(args); + + connect(m_kwinProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { + if (exitCode == 0) { + qApp->quit(); + return; + } else if (exitCode == 133) { + m_crashCount = 0; + } else { + m_crashCount++; + } + + if (m_crashCount > 10) { + qApp->quit(); + return; + } + qputenv("KWIN_RESTART_COUNT", QByteArray::number(m_crashCount)); + // restart + m_kwinProcess->start(); + }); + + m_kwinProcess->start(); + + QProcessEnvironment env; + env.insert("WAYLAND_DISPLAY", QString::fromUtf8(wl_socket_get_display_name(m_socket))); + if (m_xwlSocket) { + env.insert("DISPLAY", m_xwlSocket->name()); + if (m_xauthorityFile.open()) { + env.insert("XAUTHORITY", m_xauthorityFile.fileName()); + } + } + + auto envSyncJob = new UpdateLaunchEnvironmentJob(env); + connect(envSyncJob, &UpdateLaunchEnvironmentJob::finished, this, []() { + // The service name is merely there to indicate to the world that we're up and ready with all envs exported + QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KWinWrapper")); + }); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + app.setQuitLockEnabled(false); // don't exit when the first KJob finishes + + KSignalHandler::self()->watchSignal(SIGTERM); + QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, [&app](int signal) { + if (signal == SIGTERM) { + app.quit(); + } + }); + + KWinWrapper wrapper(&app); + wrapper.run(); + + return app.exec(); +} + +#include "kwin_wrapper.moc" diff --git a/startlingmo/wayland_wrapper/lib/CMakeLists.txt b/startlingmo/wayland_wrapper/lib/CMakeLists.txt new file mode 100644 index 0000000..dff9a60 --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(KWinXwaylandCommon STATIC + xwaylandsocket.cpp + xauthority.cpp +) + +ecm_qt_declare_logging_category(KWinXwaylandCommon + HEADER + xwayland_logging.h + IDENTIFIER + KWIN_XWL + CATEGORY_NAME + kwin_xwl + DEFAULT_SEVERITY + Warning +) + +set_property(TARGET KWinXwaylandCommon PROPERTY POSITION_INDEPENDENT_CODE ON) + +target_include_directories(KWinXwaylandCommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(KWinXwaylandCommon Qt::Core Qt::Network) diff --git a/startlingmo/wayland_wrapper/lib/xauthority.cpp b/startlingmo/wayland_wrapper/lib/xauthority.cpp new file mode 100644 index 0000000..bef5560 --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xauthority.cpp @@ -0,0 +1,77 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + SPDX-FileCopyrightText: 2021 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "xauthority.h" + +#include +#include +#include +#include +#include + +static void writeXauthorityEntry(QDataStream &stream, quint16 family, + const QByteArray &address, const QByteArray &display, + const QByteArray &name, const QByteArray &cookie) +{ + stream << quint16(family); + + auto writeArray = [&stream](const QByteArray &str) { + stream << quint16(str.size()); + stream.writeRawData(str.constData(), str.size()); + }; + + writeArray(address); + writeArray(display); + writeArray(name); + writeArray(cookie); +} + +static QByteArray generateXauthorityCookie() +{ + QByteArray cookie; + cookie.resize(16); // Cookie must be 128bits + + QRandomGenerator *generator = QRandomGenerator::system(); + for (int i = 0; i < cookie.size(); ++i) { + cookie[i] = uint8_t(generator->bounded(256)); + } + return cookie; +} + +bool generateXauthorityFile(int display, QTemporaryFile *authorityFile) +{ + const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + + authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX")); + if (!authorityFile->open()) { + return false; + } + + const QByteArray hostname = QHostInfo::localHostName().toUtf8(); + const QByteArray displayName = QByteArray::number(display); + const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1"); + const QByteArray cookie = generateXauthorityCookie(); + + QDataStream stream(authorityFile); + stream.setByteOrder(QDataStream::BigEndian); + + // Write entry with FamilyLocal and the host name as address + writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie); + + // Write entry with FamilyWild, no address + writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie); + + if (stream.status() != QDataStream::Ok || !authorityFile->flush()) { + authorityFile->remove(); + return false; + } + + return true; +} diff --git a/startlingmo/wayland_wrapper/lib/xauthority.h b/startlingmo/wayland_wrapper/lib/xauthority.h new file mode 100644 index 0000000..7ba232b --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xauthority.h @@ -0,0 +1,14 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +class QTemporaryFile; + +bool generateXauthorityFile(int display, QTemporaryFile *authorityFile); diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp b/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp new file mode 100644 index 0000000..e31899c --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp @@ -0,0 +1,250 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "xwaylandsocket.h" +#include "xwayland_logging.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace KWin +{ + +class UnixSocketAddress +{ +public: + enum class Type { + Unix, + Abstract, + }; + + UnixSocketAddress(const QString &socketPath, Type type); + + const sockaddr *data() const; + int size() const; + +private: + QByteArray m_buffer; +}; + +UnixSocketAddress::UnixSocketAddress(const QString &socketPath, Type type) +{ + const QByteArray encodedSocketPath = QFile::encodeName(socketPath); + + int byteCount = offsetof(sockaddr_un, sun_path) + encodedSocketPath.size() + 1; + m_buffer.resize(byteCount); + + sockaddr_un *address = reinterpret_cast(m_buffer.data()); + address->sun_family = AF_UNIX; + + if (type == Type::Unix) { + memcpy(address->sun_path, encodedSocketPath.data(), encodedSocketPath.size()); + address->sun_path[encodedSocketPath.size()] = '\0'; + } else { + // Abstract domain socket does not need the NUL-termination byte. + *address->sun_path = '\0'; + memcpy(address->sun_path + 1, encodedSocketPath.data(), encodedSocketPath.size()); + } +} + +const sockaddr *UnixSocketAddress::data() const +{ + return reinterpret_cast(m_buffer.data()); +} + +int UnixSocketAddress::size() const +{ + return m_buffer.size(); +} + +static QString lockFileNameForDisplay(int display) +{ + return QStringLiteral("/tmp/.X%1-lock").arg(display); +} + +static QString socketFileNameForDisplay(int display) +{ + return QStringLiteral("/tmp/.X11-unix/X%1").arg(display); +} + +static bool tryLockFile(const QString &lockFileName) +{ + for (int attempt = 0; attempt < 3; ++attempt) { + QFile lockFile(lockFileName); + if (lockFile.open(QFile::WriteOnly | QFile::NewOnly)) { + char buffer[12]; + snprintf(buffer, sizeof(buffer), "%10lld\n", QCoreApplication::applicationPid()); + if (lockFile.write(buffer, sizeof(buffer) - 1) != sizeof(buffer) - 1) { + qCWarning(KWIN_XWL) << "Failed to write pid to lock file:" << lockFile.errorString(); + lockFile.remove(); + return false; + } + return true; + } else if (lockFile.open(QFile::ReadOnly)) { + const int lockPid = lockFile.readLine().trimmed().toInt(); + if (!lockPid) { + return false; + } + if (kill(lockPid, 0) < 0 && errno == ESRCH) { + lockFile.remove(); // Try to grab the lock file in the next loop iteration. + } else { + return false; + } + } + } + return false; +} + +static int listen_helper(const QString &filePath, UnixSocketAddress::Type type, XwaylandSocket::OperationMode mode) +{ + const UnixSocketAddress socketAddress(filePath, type); + + int socketFlags = SOCK_STREAM; + if (mode == XwaylandSocket::OperationMode::CloseFdsOnExec) { + socketFlags |= SOCK_CLOEXEC; + } + int fileDescriptor = socket(AF_UNIX, socketFlags, 0); + if (fileDescriptor == -1) { + return -1; + } + + if (bind(fileDescriptor, socketAddress.data(), socketAddress.size()) == -1) { + close(fileDescriptor); + return -1; + } + + if (listen(fileDescriptor, 1) == -1) { + close(fileDescriptor); + return -1; + } + + return fileDescriptor; +} + +static bool checkSocketsDirectory() +{ + struct stat info; + const char *path = "/tmp/.X11-unix"; + + if (lstat(path, &info) != 0) { + if (errno == ENOENT) { + qCWarning(KWIN_XWL) << path << "does not exist. Please check your installation"; + return false; + } + + qCWarning(KWIN_XWL, "Failed to stat %s: %s", path, strerror(errno)); + return false; + } + + if (!S_ISDIR(info.st_mode)) { + qCWarning(KWIN_XWL) << path << "is not a directory. Broken system?"; + return false; + } + if (info.st_uid != 0 && info.st_uid != getuid()) { + qCWarning(KWIN_XWL) << path << "is not owned by root or us"; + return false; + } + if (!(info.st_mode & S_ISVTX)) { + qCWarning(KWIN_XWL) << path << "has no sticky bit on. Your system might be compromised!"; + return false; + } + + return true; +} + +XwaylandSocket::XwaylandSocket(OperationMode mode) +{ + if (!checkSocketsDirectory()) { + return; + } + + for (int display = 0; display < 100; ++display) { + const QString socketFilePath = socketFileNameForDisplay(display); + const QString lockFilePath = lockFileNameForDisplay(display); + + if (!tryLockFile(lockFilePath)) { + continue; + } + + QVector fileDescriptors; + auto socketCleanup = qScopeGuard([&fileDescriptors]() { + for (const int &fileDescriptor : std::as_const(fileDescriptors)) { + close(fileDescriptor); + } + }); + + QFile::remove(socketFilePath); + const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix, mode); + if (unixFileDescriptor == -1) { + QFile::remove(lockFilePath); + continue; + } + fileDescriptors << unixFileDescriptor; + +#if defined(Q_OS_LINUX) + const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract, mode); + if (abstractFileDescriptor == -1) { + QFile::remove(lockFilePath); + QFile::remove(socketFilePath); + continue; + } + fileDescriptors << abstractFileDescriptor; +#endif + + m_fileDescriptors = fileDescriptors; + socketCleanup.dismiss(); + + m_socketFilePath = socketFilePath; + m_lockFilePath = lockFilePath; + m_display = display; + return; + } + + qCWarning(KWIN_XWL) << "Failed to find free X11 connection socket"; +} + +XwaylandSocket::~XwaylandSocket() +{ + for (const int &fileDescriptor : std::as_const(m_fileDescriptors)) { + close(fileDescriptor); + } + if (!m_socketFilePath.isEmpty()) { + QFile::remove(m_socketFilePath); + } + if (!m_lockFilePath.isEmpty()) { + QFile::remove(m_lockFilePath); + } +} + +bool XwaylandSocket::isValid() const +{ + return m_display != -1; +} + +QVector XwaylandSocket::fileDescriptors() const +{ + return m_fileDescriptors; +} + +int XwaylandSocket::display() const +{ + return m_display; +} + +QString XwaylandSocket::name() const +{ + return ":" + QString::number(m_display); +} + +} // namespace KWin diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.h b/startlingmo/wayland_wrapper/lib/xwaylandsocket.h new file mode 100644 index 0000000..80bad35 --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xwaylandsocket.h @@ -0,0 +1,40 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include +#include +#include + +namespace KWin +{ + +class XwaylandSocket +{ +public: + enum class OperationMode { + CloseFdsOnExec, + TransferFdsOnExec + }; + + XwaylandSocket(OperationMode operationMode); + ~XwaylandSocket(); + + bool isValid() const; + int display() const; + QString name() const; + + QVector fileDescriptors() const; + +private: + QVector m_fileDescriptors; + int m_display = -1; + QString m_socketFilePath; + QString m_lockFilePath; +}; + +} // namespace KWin diff --git a/startlingmo/wayland_wrapper/wl-socket.c b/startlingmo/wayland_wrapper/wl-socket.c new file mode 100644 index 0000000..1b02839 --- /dev/null +++ b/startlingmo/wayland_wrapper/wl-socket.c @@ -0,0 +1,172 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + SPDX-FileCopyrightText: 2008 Kristian Høgsberg + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This is the size of the char array in struct sock_addr_un. + * No Wayland socket can be created with a path longer than this, + * including the null terminator. + */ +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#define LOCK_SUFFIX ".lock" +#define LOCK_SUFFIXLEN 5 + +struct wl_socket { + int fd; + int fd_lock; + struct sockaddr_un addr; + char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN]; + char display_name[16]; +}; + +static struct wl_socket *wl_socket_alloc(void) +{ + struct wl_socket *s; + + s = malloc(sizeof *s); + if (!s) + return NULL; + + s->fd = -1; + s->fd_lock = -1; + + return s; +} + +static int wl_socket_lock(struct wl_socket *socket) +{ + struct stat socket_stat; + + snprintf(socket->lock_addr, sizeof socket->lock_addr, "%s%s", socket->addr.sun_path, LOCK_SUFFIX); + + socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC | O_RDWR, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); + + if (socket->fd_lock < 0) { + printf("unable to open lockfile %s check permissions\n", socket->lock_addr); + goto err; + } + + if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) { + printf("unable to lock lockfile %s, maybe another compositor is running\n", socket->lock_addr); + goto err_fd; + } + + if (lstat(socket->addr.sun_path, &socket_stat) < 0) { + if (errno != ENOENT) { + printf("did not manage to stat file %s\n", socket->addr.sun_path); + goto err_fd; + } + } else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) { + unlink(socket->addr.sun_path); + } + + return 0; +err_fd: + close(socket->fd_lock); + socket->fd_lock = -1; +err: + *socket->lock_addr = 0; + /* we did not set this value here, but without lock the + * socket won't be created anyway. This prevents the + * wl_socket_destroy from unlinking already existing socket + * created by other compositor */ + *socket->addr.sun_path = 0; + + return -1; +} + +void wl_socket_destroy(struct wl_socket *s) +{ + if (s->addr.sun_path[0]) + unlink(s->addr.sun_path); + if (s->fd >= 0) + close(s->fd); + if (s->lock_addr[0]) + unlink(s->lock_addr); + if (s->fd_lock >= 0) + close(s->fd_lock); + + free(s); +} + +const char *wl_socket_get_display_name(struct wl_socket *s) +{ + return s->display_name; +} + +int wl_socket_get_fd(struct wl_socket *s) +{ + return s->fd; +} + +struct wl_socket *wl_socket_create() +{ + struct wl_socket *s; + int displayno = 0; + int name_size; + + /* A reasonable number of maximum default sockets. If + * you need more than this, use the explicit add_socket API. */ + const int MAX_DISPLAYNO = 32; + const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (!runtime_dir) { + printf("XDG_RUNTIME_DIR not set"); + return NULL; + } + + s = wl_socket_alloc(); + if (s == NULL) + return NULL; + + do { + snprintf(s->display_name, sizeof s->display_name, "wayland-%d", displayno); + s->addr.sun_family = AF_LOCAL; + name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s/%s", runtime_dir, s->display_name) + 1; + assert(name_size > 0); + + if (name_size > (int)sizeof s->addr.sun_path) { + goto fail; + } + + if (wl_socket_lock(s) < 0) + continue; + + s->fd = socket(PF_LOCAL, SOCK_STREAM, 0); + + int size = SUN_LEN(&s->addr); + int ret = bind(s->fd, (struct sockaddr*)&s->addr, size); + if (ret < 0) { + goto fail; + } + ret = listen(s->fd, 128); + if (ret < 0) { + goto fail; + } + return s; + } while (displayno++ < MAX_DISPLAYNO); + +fail: + wl_socket_destroy(s); + return NULL; +} diff --git a/startlingmo/wayland_wrapper/wl-socket.h b/startlingmo/wayland_wrapper/wl-socket.h new file mode 100644 index 0000000..ac69ca9 --- /dev/null +++ b/startlingmo/wayland_wrapper/wl-socket.h @@ -0,0 +1,39 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Allocate and create a socket + * It is bound and accepted + */ +struct wl_socket *wl_socket_create(); + +/** + * Returns the file descriptor for the socket + */ +int wl_socket_get_fd(struct wl_socket *); + +/** + * Returns the name of the socket, i.e "wayland-0" + */ +char *wl_socket_get_display_name(struct wl_socket *); + +/** + * Cleanup resources and close the FD + */ +void wl_socket_destroy(struct wl_socket *socket); + +#ifdef __cplusplus +} +#endif From 2d8d227f4210540ffb01376518025ce8e8f1287b Mon Sep 17 00:00:00 2001 From: Elysia Date: Thu, 8 Aug 2024 12:20:49 +0800 Subject: [PATCH 09/24] Added EnvironmentUpdator --- startlingmo/CMakeLists.txt | 28 ++-- startlingmo/UpdateLaunchEnvironment.cpp | 150 ++++++++++++++++++ startlingmo/UpdateLaunchEnvironment.hpp | 52 ++++++ .../{lingmo-session => }/daemon-helper.cpp | 0 .../{lingmo-session => }/daemon-helper.h | 0 .../{lingmo-session => }/job_private.hpp | 0 startlingmo/lingmo-session/CMakeLists.txt | 3 - startlingmo/lingmo-session/application.cpp | 25 --- startlingmo/property.cpp | 44 ----- startlingmo/property.hpp | 28 ---- startlingmo/signalhandler.cpp | 63 ++++++++ startlingmo/signalhandler.h | 38 +++++ startlingmo/startlingmo-wayland.cpp | 3 + startlingmo/startlingmo.cpp | 24 ++- startlingmo/startlingmo.hpp | 4 +- startlingmo/wayland_wrapper/CMakeLists.txt | 3 +- startlingmo/wayland_wrapper/kwin_wrapper.cpp | 11 +- 17 files changed, 355 insertions(+), 121 deletions(-) create mode 100644 startlingmo/UpdateLaunchEnvironment.cpp create mode 100644 startlingmo/UpdateLaunchEnvironment.hpp rename startlingmo/{lingmo-session => }/daemon-helper.cpp (100%) rename startlingmo/{lingmo-session => }/daemon-helper.h (100%) rename startlingmo/{lingmo-session => }/job_private.hpp (100%) delete mode 100644 startlingmo/property.cpp delete mode 100644 startlingmo/property.hpp create mode 100644 startlingmo/signalhandler.cpp create mode 100644 startlingmo/signalhandler.h diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index 196b498..2ce630e 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -13,14 +13,31 @@ find_package(Qt5 COMPONENTS Core DBus REQUIRED) find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE) list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) +include(KDEInstallDirs) +include(FindKWinPath) +include(ECMQtDeclareLoggingCategory) + +find_kwin_wayland_bin_path() +find_kwin_bin_path() + +configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startlingmo.h) + find_package(KF5 REQUIRED COMPONENTS DBusAddons CoreAddons) find_package(LibKWorkspace REQUIRED) find_package(KF5Wayland REQUIRED) +SET(startlingmo_SRCS + startlingmo.cpp + daemon-helper.cpp + UpdateLaunchEnvironment.cpp + signalhandler.cpp +) + +ecm_qt_declare_logging_category(startlingmo_SRCS HEADER debug.h IDENTIFIER LINGMO_STARTUP CATEGORY_NAME org.lingmo.startup) -add_library(startlingmo OBJECT startlingmo.cpp property.cpp) +add_library(startlingmo OBJECT ${startlingmo_SRCS}) target_link_libraries(startlingmo PUBLIC Qt5::Core @@ -39,15 +56,6 @@ target_link_libraries(startlingmo-wayland Qt5::DBus ) -include(KDEInstallDirs) -include(FindKWinPath) -include(ECMQtDeclareLoggingCategory) - -find_kwin_wayland_bin_path() -find_kwin_bin_path() - -configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startlingmo.h) - add_subdirectory(lingmo-session) add_subdirectory(wayland_wrapper) diff --git a/startlingmo/UpdateLaunchEnvironment.cpp b/startlingmo/UpdateLaunchEnvironment.cpp new file mode 100644 index 0000000..4ebf517 --- /dev/null +++ b/startlingmo/UpdateLaunchEnvironment.cpp @@ -0,0 +1,150 @@ +#include "UpdateLaunchEnvironment.hpp" + +#include +#include +#include +#include + +#include + +#include "debug.h" +#include "startlingmo.hpp" + +class UpdateLaunchEnvironmentJobPrivate +{ +public: + explicit UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q); + void monitorReply(const QDBusPendingReply<> &reply); + + static bool isPosixName(const QString &name); + static bool isSystemdApprovedValue(const QString &value); + + UpdateLaunchEnvironmentJob *q; + QProcessEnvironment environment; + int pendingReplies = 0; +}; + +UpdateLaunchEnvironmentJobPrivate::UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q) + : q(q) +{ +} + +void UpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply) +{ + ++pendingReplies; + + auto *watcher = new QDBusPendingCallWatcher(reply, q); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + --pendingReplies; + + if (pendingReplies == 0) { + Q_EMIT q->finished(); + q->deleteLater(); + } + }); +} + +UpdateLaunchEnvironmentJob::UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment) + : d(new UpdateLaunchEnvironmentJobPrivate(this)) +{ + d->environment = environment; + QTimer::singleShot(0, this, &UpdateLaunchEnvironmentJob::start); +} + +UpdateLaunchEnvironmentJob::~UpdateLaunchEnvironmentJob() = default; + +void UpdateLaunchEnvironmentJob::start() +{ + qDBusRegisterMetaType>(); + QMap dbusActivationEnv; + QStringList systemdUpdates; + + for (const auto &varName : d->environment.keys()) { + if (!UpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) { + qCWarning(LINGMO_STARTUP) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters"; + continue; + } + const QString value = d->environment.value(varName); + + // plasma-session + QDBusMessage plasmaSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.Startup"), + QStringLiteral("/Startup"), + QStringLiteral("org.kde.Startup"), + QStringLiteral("updateLaunchEnv")); + plasmaSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); + auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(plasmaSessionMsg); + d->monitorReply(plasmaSessionReply); + + // DBus-activation environment + dbusActivationEnv.insert(varName, value); + + // _user_ systemd env + // Systemd has stricter parsing of valid environment variables + // https://github.com/systemd/systemd/issues/16704 + // validate here + if (!UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) { + qCWarning(LINGMO_STARTUP) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters"; + continue; + } + const QString updateString = varName + QStringLiteral("=") + value; + systemdUpdates.append(updateString); + } + + // DBus-activation environment + QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("/org/freedesktop/DBus"), + QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("UpdateActivationEnvironment")); + dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)}); + + auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg); + d->monitorReply(dbusActivationReply); + + // _user_ systemd env + QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("SetEnvironment")); + systemdActivationMsg.setArguments({systemdUpdates}); + + auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg); + d->monitorReply(systemdActivationReply); +} + +bool UpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name) +{ + // Posix says characters like % should be 'tolerated', but it gives issues in practice. + // https://bugzilla.redhat.com/show_bug.cgi?id=1754395 + // https://bugzilla.redhat.com/show_bug.cgi?id=1879216 + // Ensure systemd compat by only allowing alphanumerics and _ in names. + bool first = true; + for (const QChar c : name) { + if (first && !c.isLetter() && c != QLatin1Char('_')) { + return false; + } else if (first) { + first = false; + } else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { + return false; + } + } + return !first; +} + +bool UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value) +{ + // systemd code checks that a value contains no control characters except \n \t + // effectively copied from systemd's string_has_cc + for (const char &it : value.toLatin1()) { + if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) { + continue; + } + if (it > 0 && it < ' ') { + return false; + } + if (it == 127) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/startlingmo/UpdateLaunchEnvironment.hpp b/startlingmo/UpdateLaunchEnvironment.hpp new file mode 100644 index 0000000..1ad55ca --- /dev/null +++ b/startlingmo/UpdateLaunchEnvironment.hpp @@ -0,0 +1,52 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ +#ifndef UPDATELAUNCHENVIRONMENT_HPP +#define UPDATELAUNCHENVIRONMENT_HPP + +#include + +#include + +class QString; +class UpdateLaunchEnvironmentJobPrivate; + +/** + * @class UpdateLaunchEnvironmentJob updatelaunchenvironmentjob.h + * + * Job for updating the launch environment. + * + * This job adds or updates an environment variable in process environment that will be used + * when a process is launched: + * This includes: + * - DBus activation + * - Systemd units + * - lingmo-session + * + * Environment variables are sanitized before uploading. + * + * This object deletes itself after completion, similar to KJobs + */ +class UpdateLaunchEnvironmentJob : public QObject +{ + Q_OBJECT + +public: + explicit UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment); + ~UpdateLaunchEnvironmentJob() override; + +Q_SIGNALS: + void finished(); + +private: + void start(); + +private: + std::unique_ptr const d; +}; + +#endif // UPDATELAUNCHENVIRONMENT_HPP \ No newline at end of file diff --git a/startlingmo/lingmo-session/daemon-helper.cpp b/startlingmo/daemon-helper.cpp similarity index 100% rename from startlingmo/lingmo-session/daemon-helper.cpp rename to startlingmo/daemon-helper.cpp diff --git a/startlingmo/lingmo-session/daemon-helper.h b/startlingmo/daemon-helper.h similarity index 100% rename from startlingmo/lingmo-session/daemon-helper.h rename to startlingmo/daemon-helper.h diff --git a/startlingmo/lingmo-session/job_private.hpp b/startlingmo/job_private.hpp similarity index 100% rename from startlingmo/lingmo-session/job_private.hpp rename to startlingmo/job_private.hpp diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt index bb8aa1d..edeab81 100755 --- a/startlingmo/lingmo-session/CMakeLists.txt +++ b/startlingmo/lingmo-session/CMakeLists.txt @@ -14,9 +14,6 @@ set(SOURCES powermanager/power.cpp powermanager/powerproviders.cpp - - daemon-helper.cpp - daemon-helper.h ) qt5_add_dbus_adaptor(DBUS_SOURCES diff --git a/startlingmo/lingmo-session/application.cpp b/startlingmo/lingmo-session/application.cpp index 52f7a0d..e1d0d19 100755 --- a/startlingmo/lingmo-session/application.cpp +++ b/startlingmo/lingmo-session/application.cpp @@ -49,34 +49,9 @@ Application::Application(const QCommandLineParser &parser, QObject *parent) m_wayland = parser.isSet("wayland"); - // createConfigDirectory(); - // initKWinConfig(); - // initLanguage(); - // initScreenScaleFactors(); - // initXResource(); - - // 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(); - - // qunsetenv("XCURSOR_THEME"); - // qunsetenv("XCURSOR_SIZE"); - // qunsetenv("SESSION_MANAGER"); m_networkProxyManager->update(); - // QTimer::singleShot(50, this, &Application::updateUserDirs); - // Launch Lingmo and user defined processes ! QTimer::singleShot(100, m_processManager, &ProcessManager::start); } diff --git a/startlingmo/property.cpp b/startlingmo/property.cpp deleted file mode 100644 index 13a2da3..0000000 --- a/startlingmo/property.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "property.hpp" - -template -LProperty::LProperty() : - m_cObject(nullptr), - Set(nullptr), - Get(nullptr) -{} - -template -void LProperty::setContainer(Container *cObject) { - m_cObject = cObject; -} - -template -void LProperty::setter(void (Container::*pSet)(ValueType value)) { - if ((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE)) - Set = pSet; - else - Set = nullptr; -} - -template -void LProperty::getter(ValueType (Container::*pGet)()) { - if ((nPropType == READ_ONLY) || (nPropType == READ_WRITE)) - Get = pGet; - else - Get = nullptr; -} - -template -ValueType LProperty::operator=(const ValueType &value) { - assert(m_cObject != nullptr); - assert(Set != nullptr); - (m_cObject->*Set)(value); - return value; -} - -template -LProperty::operator ValueType() { - assert(m_cObject != nullptr); - assert(Get != nullptr); - return (m_cObject->*Get)(); -} \ No newline at end of file diff --git a/startlingmo/property.hpp b/startlingmo/property.hpp deleted file mode 100644 index b32d11b..0000000 --- a/startlingmo/property.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef PROPERTY_HPP -#define PROPERTY_HPP - -#include - -#define READ_ONLY 1 -#define WRITE_ONLY 2 -#define READ_WRITE 3 - -template -class LProperty { - public: - LProperty(); - - void setContainer(Container *cObject); - void setter(void (Container::*pSet)(ValueType value)); - void getter(ValueType (Container::*pGet)()); - - ValueType operator=(const ValueType &value); - operator ValueType(); - - private: - Container *m_cObject; - void (Container::*Set)(ValueType value); - ValueType (Container::*Get)(); -}; - -#endif // PROPERTY_HPP \ No newline at end of file diff --git a/startlingmo/signalhandler.cpp b/startlingmo/signalhandler.cpp new file mode 100644 index 0000000..9e96c58 --- /dev/null +++ b/startlingmo/signalhandler.cpp @@ -0,0 +1,63 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "signalhandler.h" +#include "debug.h" +#include "startlingmo.hpp" + +#include +#include +#include +#include + +int SignalHandler::signalFd[2]; + +SignalHandler::SignalHandler() +{ + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { + qCWarning(LINGMO_STARTUP) << "Couldn't create a socketpair"; + return; + } + + m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); + connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); +} + +SignalHandler::~SignalHandler() +{ + for (int sig : std::as_const(m_signalsRegistered)) { + signal(sig, nullptr); + } + close(signalFd[0]); + close(signalFd[1]); +} + +void SignalHandler::addSignal(int signalToTrack) +{ + m_signalsRegistered.insert(signalToTrack); + signal(signalToTrack, signalHandler); +} + +void SignalHandler::signalHandler(int signal) +{ + ::write(signalFd[0], &signal, sizeof(signal)); +} + +void SignalHandler::handleSignal() +{ + m_handler->setEnabled(false); + int signal; + ::read(signalFd[1], &signal, sizeof(signal)); + m_handler->setEnabled(true); + + Q_EMIT signalReceived(signal); +} + +SignalHandler *SignalHandler::self() +{ + static SignalHandler s_self; + return &s_self; +} diff --git a/startlingmo/signalhandler.h b/startlingmo/signalhandler.h new file mode 100644 index 0000000..c0f8428 --- /dev/null +++ b/startlingmo/signalhandler.h @@ -0,0 +1,38 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#pragma once + +#include +#include +#include + +/** + * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop + * + * It's a singleton as it relies on static data getting defined. + */ +class SignalHandler : public QObject +{ + Q_OBJECT +public: + ~SignalHandler() override; + void addSignal(int signal); + + static SignalHandler *self(); + +Q_SIGNALS: + void signalReceived(int signal); + +private: + SignalHandler(); + void handleSignal(); + static void signalHandler(int signal); + + QSet m_signalsRegistered; + static int signalFd[2]; + QSocketNotifier *m_handler = nullptr; +}; diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp index 22ac79a..e6122e1 100644 --- a/startlingmo/startlingmo-wayland.cpp +++ b/startlingmo/startlingmo-wayland.cpp @@ -10,6 +10,8 @@ #include "startlingmo.hpp" +#include "debug.h" + extern "C" { #include "signal.h" } @@ -72,6 +74,7 @@ int main(int argc, char **argv) { setupLingmoEnvironment(); initLanguage(); + initScreenScaleFactors(); qputenv("XDG_SESSION_TYPE", "wayland"); diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp index 193e018..127d614 100644 --- a/startlingmo/startlingmo.cpp +++ b/startlingmo/startlingmo.cpp @@ -14,13 +14,15 @@ #include #include #include -#include +#include + +#include "debug.h" + +#include "UpdateLaunchEnvironment.hpp" extern QTextStream out; QTextStream out(stderr); -Q_LOGGING_CATEGORY(LINGMO_STARTUP, "org.lingmo.startup") - void sigtermHandler(int signalNumber) { Q_UNUSED(signalNumber) if (QCoreApplication::instance()) { @@ -458,3 +460,19 @@ bool startLingmoSession(bool wayland) { } return rc; } + +void initScreenScaleFactors() { + QSettings settings(QSettings::UserScope, "lingmoos", "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)); + } +} diff --git a/startlingmo/startlingmo.hpp b/startlingmo/startlingmo.hpp index 7e83bd4..013e71e 100644 --- a/startlingmo/startlingmo.hpp +++ b/startlingmo/startlingmo.hpp @@ -12,8 +12,6 @@ #include #include -Q_DECLARE_LOGGING_CATEGORY(LINGMO_STARTUP) - void sigtermHandler(int signalNumber); QStringList allServices(const QLatin1String &prefix); @@ -57,4 +55,6 @@ struct KillBeforeDeleter { }; bool isSessionVariable(const QByteArray &name); + +void initScreenScaleFactors(); #endif // STARTLINGMO_HPP \ No newline at end of file diff --git a/startlingmo/wayland_wrapper/CMakeLists.txt b/startlingmo/wayland_wrapper/CMakeLists.txt index ce03f1a..aa0f511 100644 --- a/startlingmo/wayland_wrapper/CMakeLists.txt +++ b/startlingmo/wayland_wrapper/CMakeLists.txt @@ -17,6 +17,7 @@ ecm_qt_declare_logging_category(lingmo_kwin_wayland_wrapper Warning ) -target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KF5::DBusAddons KF5::CoreAddons KWinXwaylandCommon) +target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KWinXwaylandCommon startlingmo) +target_include_directories(lingmo_kwin_wayland_wrapper PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) set_property(TARGET lingmo_kwin_wayland_wrapper PROPERTY C_STANDARD 11) install(TARGETS lingmo_kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/startlingmo/wayland_wrapper/kwin_wrapper.cpp b/startlingmo/wayland_wrapper/kwin_wrapper.cpp index c1ffd77..0a16b54 100644 --- a/startlingmo/wayland_wrapper/kwin_wrapper.cpp +++ b/startlingmo/wayland_wrapper/kwin_wrapper.cpp @@ -26,9 +26,10 @@ #include #include -#include -#include +#include "signalhandler.h" +#include "UpdateLaunchEnvironment.hpp" +#include #include #include "wl-socket.h" @@ -151,7 +152,7 @@ void KWinWrapper::run() auto envSyncJob = new UpdateLaunchEnvironmentJob(env); connect(envSyncJob, &UpdateLaunchEnvironmentJob::finished, this, []() { // The service name is merely there to indicate to the world that we're up and ready with all envs exported - QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KWinWrapper")); + QDBusConnection::sessionBus().registerService(QStringLiteral("org.lingmo.KWinWrapper")); }); } @@ -160,8 +161,8 @@ int main(int argc, char **argv) QCoreApplication app(argc, argv); app.setQuitLockEnabled(false); // don't exit when the first KJob finishes - KSignalHandler::self()->watchSignal(SIGTERM); - QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, [&app](int signal) { + SignalHandler::self()->addSignal(SIGTERM); + QObject::connect(SignalHandler::self(), &SignalHandler::signalReceived, &app, [&app](int signal) { if (signal == SIGTERM) { app.quit(); } From e9d69d0cdebda3ac06c756cd8bc5923707b42d3c Mon Sep 17 00:00:00 2001 From: Elysia Date: Thu, 8 Aug 2024 14:50:33 +0800 Subject: [PATCH 10/24] Able to call dbus. Have some errors on monitoring dbus service --- startlingmo/UpdateLaunchEnvironment.cpp | 11 +-- startlingmo/lingmo-session/CMakeLists.txt | 7 +- startlingmo/lingmo-session/application.cpp | 69 +++---------------- startlingmo/lingmo-session/application.h | 2 + .../lingmo-session/com.lingmo.Session.xml | 4 ++ .../lingmo-session/lingmo-xsession.desktop | 7 -- startlingmo/lingmo-session/processmanager.cpp | 27 +++----- startlingmo/lingmo-session/processmanager.h | 1 + startlingmo/wayland_wrapper/kwin_wrapper.cpp | 2 + 9 files changed, 38 insertions(+), 92 deletions(-) delete mode 100755 startlingmo/lingmo-session/lingmo-xsession.desktop diff --git a/startlingmo/UpdateLaunchEnvironment.cpp b/startlingmo/UpdateLaunchEnvironment.cpp index 4ebf517..9020633 100644 --- a/startlingmo/UpdateLaunchEnvironment.cpp +++ b/startlingmo/UpdateLaunchEnvironment.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "debug.h" #include "startlingmo.hpp" @@ -68,12 +69,12 @@ void UpdateLaunchEnvironmentJob::start() const QString value = d->environment.value(varName); // plasma-session - QDBusMessage plasmaSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.Startup"), - QStringLiteral("/Startup"), - QStringLiteral("org.kde.Startup"), + QDBusMessage lingmoSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("com.lingmo.Session"), + QStringLiteral("/Session"), + QStringLiteral("com.lingmo.Session"), QStringLiteral("updateLaunchEnv")); - plasmaSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); - auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(plasmaSessionMsg); + lingmoSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); + auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(lingmoSessionMsg); d->monitorReply(plasmaSessionReply); // DBus-activation environment diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt index edeab81..1a9f4fd 100755 --- a/startlingmo/lingmo-session/CMakeLists.txt +++ b/startlingmo/lingmo-session/CMakeLists.txt @@ -16,15 +16,17 @@ set(SOURCES powermanager/powerproviders.cpp ) -qt5_add_dbus_adaptor(DBUS_SOURCES +qt_add_dbus_adaptor(DBUS_SOURCES com.lingmo.Session.xml application.h Application sessionadaptor SessionAdaptor) -set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) +# set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) find_package(KF5WindowSystem) find_package(Threads) +ecm_qt_declare_logging_category(SOURCES HEADER debug.h IDENTIFIER LINGMO_SESSION_D CATEGORY_NAME org.lingmo.session) + add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES}) target_link_libraries(${TARGET} PRIVATE @@ -42,4 +44,3 @@ target_link_libraries(${TARGET} target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(FILES lingmo-xsession.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) diff --git a/startlingmo/lingmo-session/application.cpp b/startlingmo/lingmo-session/application.cpp index e1d0d19..8d6f0ea 100755 --- a/startlingmo/lingmo-session/application.cpp +++ b/startlingmo/lingmo-session/application.cpp @@ -33,8 +33,10 @@ // STL #include +#include #include "startlingmo.hpp" +#include "debug.h" Application::Application(const QCommandLineParser &parser, QObject *parent) : QObject(parent), m_processManager(new ProcessManager(this)), @@ -49,7 +51,6 @@ Application::Application(const QCommandLineParser &parser, QObject *parent) m_wayland = parser.isSet("wayland"); - m_networkProxyManager->update(); // Launch Lingmo and user defined processes ! @@ -58,6 +59,12 @@ Application::Application(const QCommandLineParser &parser, QObject *parent) bool Application::wayland() const { return m_wayland; } +void Application::updateLaunchEnv(const QString &key, const QString &value) { + qCDebug(LINGMO_SESSION_D) << "Update launch env: " << key << value; + qputenv(key.toLatin1(), value.toLatin1()); + m_processManager->updateLaunchEnv(key, value); +} + void Application::launch(const QString &exec, const QStringList &args) { QProcess process; process.setProgram(exec); @@ -251,66 +258,6 @@ void Application::initKWinConfig() { settings.endGroup(); } -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 lingmoDE is not started by systemd. -void Application::importSystemdEnvrionment() { - auto environment = getSystemdEnvironment(); - if (!environment) { - return; - } - - for (auto &envString : environment->toStringList()) { - 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; -} - -void Application::updateUserDirs() { - // bool isLingmoOS = QFile::exists("/etc/lingmoos"); - - // if (!isLingmoOS) - // return; - - // QProcess p; - // p.setEnvironment(QStringList() << "LC_ALL=C"); - // p.start("xdg-user-dirs-update", QStringList() << "--force"); - // p.waitForFinished(-1); -} - int Application::runSync(const QString &program, const QStringList &args, const QStringList &env) { QProcess p; diff --git a/startlingmo/lingmo-session/application.h b/startlingmo/lingmo-session/application.h index 3ea7640..e681d2c 100755 --- a/startlingmo/lingmo-session/application.h +++ b/startlingmo/lingmo-session/application.h @@ -41,6 +41,8 @@ class Application : public QObject { bool wayland() const; public slots: + void updateLaunchEnv(const QString &key, const QString &value); + void logout() { m_processManager->logout(); } void reboot() { diff --git a/startlingmo/lingmo-session/com.lingmo.Session.xml b/startlingmo/lingmo-session/com.lingmo.Session.xml index 199b7ee..eb52db4 100755 --- a/startlingmo/lingmo-session/com.lingmo.Session.xml +++ b/startlingmo/lingmo-session/com.lingmo.Session.xml @@ -28,5 +28,9 @@ + + + + diff --git a/startlingmo/lingmo-session/lingmo-xsession.desktop b/startlingmo/lingmo-session/lingmo-xsession.desktop deleted file mode 100755 index 4fb9f6d..0000000 --- a/startlingmo/lingmo-session/lingmo-xsession.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Type=Application -Exec=lingmo-session -TryExec=lingmo-session -Name=Lingmo Desktop -Keywords=session -Comment=session diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp index af7abd5..9ee2612 100755 --- a/startlingmo/lingmo-session/processmanager.cpp +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -26,13 +26,6 @@ #include "daemon-helper.h" -void Delay_MSec(unsigned int msec) { - QEventLoop loop; // 定义一个新的事件循环 - QTimer::singleShot( - msec, &loop, SLOT(quit())); // 创建单次定时器,槽函数为事件循环的退出函数 - loop.exec(); // 事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出 -} - ProcessManager::ProcessManager(Application *app, QObject *parent) : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { qApp->installNativeEventFilter(this); @@ -50,6 +43,10 @@ ProcessManager::~ProcessManager() { } } +void ProcessManager::updateLaunchEnv(const QString &key, const QString &value) { + qputenv(key.toLatin1(), value.toLatin1()); +} + void ProcessManager::start() { startWindowManager(); startDaemonProcess(); @@ -81,14 +78,11 @@ void ProcessManager::startWindowManager() { qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); if (detcted_wayland || m_app->wayland()) { - StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), - {QStringLiteral("--xwayland")}, - QStringLiteral("org.kde.KWinWrapper")); - // StartProcessJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), - // {QStringLiteral("--xwayland")}); - kwinWaylandJob.exec(); - - // Delay_MSec(10000); + StartServiceJob kwinWaylandJob( + QStringLiteral("lingmo_kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}, + QStringLiteral("org.lingmo.KWinWrapper")); + kwinWaylandJob.exec(); // Wait untill kwin_wayland_wrapper started } else { auto *wmProcess = new QProcess; @@ -123,7 +117,8 @@ void ProcessManager::startDaemonProcess() { // list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); // list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); // list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); - // list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); + // list << qMakePair(QString("lingmo-permission-surveillance"), + // QStringList()); // // list << qMakePair(QString("lingmo-clipboard"), QStringList()); // list << qMakePair(QString("lingmo-chotkeys"), QStringList()); list << qMakePair(QString("foot"), QStringList()); diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h index 83f04f9..49257ce 100755 --- a/startlingmo/lingmo-session/processmanager.h +++ b/startlingmo/lingmo-session/processmanager.h @@ -55,6 +55,7 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter { bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; + void updateLaunchEnv(const QString &key, const QString &value); private: Application *m_app; QMap m_systemProcess; diff --git a/startlingmo/wayland_wrapper/kwin_wrapper.cpp b/startlingmo/wayland_wrapper/kwin_wrapper.cpp index 0a16b54..bf52c64 100644 --- a/startlingmo/wayland_wrapper/kwin_wrapper.cpp +++ b/startlingmo/wayland_wrapper/kwin_wrapper.cpp @@ -30,6 +30,7 @@ #include "UpdateLaunchEnvironment.hpp" #include +#include #include #include "wl-socket.h" @@ -152,6 +153,7 @@ void KWinWrapper::run() auto envSyncJob = new UpdateLaunchEnvironmentJob(env); connect(envSyncJob, &UpdateLaunchEnvironmentJob::finished, this, []() { // The service name is merely there to indicate to the world that we're up and ready with all envs exported + qCDebug(KWIN_WRAPPER) << "KWinWrapper is ready"; QDBusConnection::sessionBus().registerService(QStringLiteral("org.lingmo.KWinWrapper")); }); } From 51167b65b6d677e678cb05b8a25e9694bc173276 Mon Sep 17 00:00:00 2001 From: Elysia Date: Thu, 8 Aug 2024 16:09:01 +0800 Subject: [PATCH 11/24] Able to start foot --- startlingmo/daemon-helper.cpp | 2 +- startlingmo/lingmo-session/processmanager.cpp | 53 +++++++++++++------ startlingmo/lingmo-session/processmanager.h | 18 +++++-- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/startlingmo/daemon-helper.cpp b/startlingmo/daemon-helper.cpp index 8516b27..486a0e7 100644 --- a/startlingmo/daemon-helper.cpp +++ b/startlingmo/daemon-helper.cpp @@ -73,7 +73,7 @@ Job::~Job() { } void Job::emitResult() { - if (d_func()->isFinished) { + if (!d_func()->isFinished) { finishJob(true); } } diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp index 9ee2612..556c7df 100755 --- a/startlingmo/lingmo-session/processmanager.cpp +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -26,8 +26,13 @@ #include "daemon-helper.h" +ProcessManager *s_self; + ProcessManager::ProcessManager(Application *app, QObject *parent) : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { + Q_ASSERT(!s_self); + s_self = this; + qApp->installNativeEventFilter(this); } @@ -78,11 +83,12 @@ void ProcessManager::startWindowManager() { qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); if (detcted_wayland || m_app->wayland()) { - StartServiceJob kwinWaylandJob( - QStringLiteral("lingmo_kwin_wayland_wrapper"), - {QStringLiteral("--xwayland")}, - QStringLiteral("org.lingmo.KWinWrapper")); - kwinWaylandJob.exec(); // Wait untill kwin_wayland_wrapper started + auto kwinWaylandJob = + new StartServiceJob(QStringLiteral("lingmo_kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}, + QStringLiteral("org.lingmo.KWinWrapper")); + kwinWaylandJob->setParent(this); + kwinWaylandJob->exec(); // Wait untill kwin_wayland_wrapper started } else { auto *wmProcess = new QProcess; @@ -121,9 +127,23 @@ void ProcessManager::startDaemonProcess() { // QStringList()); // // list << qMakePair(QString("lingmo-clipboard"), QStringList()); // list << qMakePair(QString("lingmo-chotkeys"), QStringList()); - list << qMakePair(QString("foot"), QStringList()); + const QVector sequence = { + new StartProcessJob(QStringLiteral("foot"), {}), + }; + Job *last = nullptr; + for (Job *job : sequence) { + if (!job) { + continue; + } + if (last) { + connect(last, &Job::finished, job, &Job::start); + } + last = job; + } - m_daemonAutoStartD = std::make_shared(list); + // connect(sequence.last(), &Job::finished, this, &Startup::finishStartup); + sequence.first()->start(); + // m_daemonAutoStartD = std::make_shared(list); } void ProcessManager::loadAutoStartProcess() { @@ -202,6 +222,16 @@ bool ProcessManager::nativeEventFilter(const QByteArray &eventType, return false; } +bool ProcessManager::startDetached(QProcess *process) { + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->start(); + const bool ret = process->waitForStarted(); + if (ret) { + m_processes << process; + } + return ret; +} + StartProcessJob::StartProcessJob(const QString &process, const QStringList &args, const QProcessEnvironment &additionalEnv) @@ -258,7 +288,7 @@ void StartServiceJob::start() { return; } qDebug() << "Starting " << m_process->program() << m_process->arguments(); - if (!startDetached(m_process)) { + if (!ProcessManager::self()->startDetached(m_process)) { qWarning() << "error starting process" << m_process->program() << m_process->arguments(); emitResult(); @@ -268,10 +298,3 @@ void StartServiceJob::start() { emitResult(); } } - -bool StartServiceJob::startDetached(QProcess *process) { - process->setProcessChannelMode(QProcess::ForwardedChannels); - process->start(); - const bool ret = process->waitForStarted(); - return ret; -} \ No newline at end of file diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h index 49257ce..1eba859 100755 --- a/startlingmo/lingmo-session/processmanager.h +++ b/startlingmo/lingmo-session/processmanager.h @@ -32,13 +32,21 @@ #include "daemon-helper.h" class Application; +class ProcessManager; + +extern ProcessManager *s_self; + class ProcessManager : public QObject, public QAbstractNativeEventFilter { Q_OBJECT - public: explicit ProcessManager(Application *app, QObject *parent = nullptr); ~ProcessManager(); + static ProcessManager *self() { + Q_ASSERT(s_self); + return s_self; + } + void start(); void logout(); @@ -56,10 +64,14 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter { long *result) override; void updateLaunchEnv(const QString &key, const QString &value); + + bool startDetached(QProcess *process); + private: Application *m_app; QMap m_systemProcess; QMap m_autoStartProcess; + QVector m_processes; // Daemon helper for desktop components std::shared_ptr m_desktopAutoStartD; @@ -85,7 +97,7 @@ class StartProcessJob : public Job { const QString &process, const QStringList &args, const QProcessEnvironment &additionalEnv = QProcessEnvironment()); void start() override; - + public Q_SLOTS: void finished(int exitCode, QProcess::ExitStatus e); @@ -105,8 +117,6 @@ class StartServiceJob : public Job { void start() override; - bool startDetached(QProcess *process); - private: QProcess *m_process; const QString m_serviceId; From f20a5ff024225bd740cb9b53a850d9b8c8b63767 Mon Sep 17 00:00:00 2001 From: Elysia Date: Thu, 8 Aug 2024 19:46:00 +0800 Subject: [PATCH 12/24] Able to start DE --- startlingmo/CMakeLists.txt | 2 + startlingmo/lingmo-session-old/CMakeLists.txt | 24 --- startlingmo/lingmo-session-old/README | 3 - startlingmo/lingmo-session-old/main.cpp | 19 --- .../lingmo-session-old/org.lingmo.Startup.xml | 9 -- .../lingmo-session-old/sessiontrack.cpp | 54 ------- startlingmo/lingmo-session-old/sessiontrack.h | 23 --- .../lingmo-session-old/signalhandler.cpp | 62 -------- .../lingmo-session-old/signalhandler.h | 38 ----- startlingmo/lingmo-session-old/singleton.h | 32 ---- startlingmo/lingmo-session-old/startup.cpp | 145 ------------------ startlingmo/lingmo-session-old/startup.hpp | 72 --------- startlingmo/lingmo-session/main.cpp | 32 ++-- startlingmo/lingmo-session/processmanager.cpp | 101 ++++++++---- startlingmo/lingmo-session/processmanager.h | 15 ++ startlingmo/lingmo-wayland-session.desktop | 6 + startlingmo/startlingmo-wayland.cpp | 2 + 17 files changed, 108 insertions(+), 531 deletions(-) delete mode 100644 startlingmo/lingmo-session-old/CMakeLists.txt delete mode 100644 startlingmo/lingmo-session-old/README delete mode 100644 startlingmo/lingmo-session-old/main.cpp delete mode 100644 startlingmo/lingmo-session-old/org.lingmo.Startup.xml delete mode 100644 startlingmo/lingmo-session-old/sessiontrack.cpp delete mode 100644 startlingmo/lingmo-session-old/sessiontrack.h delete mode 100644 startlingmo/lingmo-session-old/signalhandler.cpp delete mode 100644 startlingmo/lingmo-session-old/signalhandler.h delete mode 100644 startlingmo/lingmo-session-old/singleton.h delete mode 100644 startlingmo/lingmo-session-old/startup.cpp delete mode 100644 startlingmo/lingmo-session-old/startup.hpp create mode 100755 startlingmo/lingmo-wayland-session.desktop diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index 2ce630e..4689afa 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -61,3 +61,5 @@ add_subdirectory(wayland_wrapper) install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS lingmo-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) +install(FILES lingmo-wayland-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions/) + diff --git a/startlingmo/lingmo-session-old/CMakeLists.txt b/startlingmo/lingmo-session-old/CMakeLists.txt deleted file mode 100644 index 8629cf5..0000000 --- a/startlingmo/lingmo-session-old/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED 17) - -set(lingmo_session_SRCS - main.cpp - startup.cpp - sessiontrack.cpp - signalhandler.cpp -) - -ecm_qt_declare_logging_category(lingmo_session_SRCS HEADER debug.h IDENTIFIER LINGMO_SESSION CATEGORY_NAME org.lingmo.session) - -qt_add_dbus_adaptor( lingmo_session_SRCS org.lingmo.Startup.xml startup.hpp Startup) - -add_executable(lingmo_session ${lingmo_session_SRCS}) - -target_include_directories(lingmo_session PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo ${CMAKE_BINARY_DIR}/startkde) -target_include_directories(lingmo_session PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(lingmo_session - startlingmo -) - -install(TARGETS lingmo_session ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) \ No newline at end of file diff --git a/startlingmo/lingmo-session-old/README b/startlingmo/lingmo-session-old/README deleted file mode 100644 index 2cd414d..0000000 --- a/startlingmo/lingmo-session-old/README +++ /dev/null @@ -1,3 +0,0 @@ -This application runs all init/autostart scripts needed for a lingmo session. - -This application should only contain tasks relating to starting executables and setting environment variables so that an implementation of systemd units would not cause duplication. diff --git a/startlingmo/lingmo-session-old/main.cpp b/startlingmo/lingmo-session-old/main.cpp deleted file mode 100644 index 070a8ea..0000000 --- a/startlingmo/lingmo-session-old/main.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#include "startup.hpp" - -#include -#include - -std::shared_ptr startup_ptr; - -int main(int argc, char **argv) { - QCoreApplication app(argc, argv); - - startup_ptr = std::make_shared(&app); - - app.exec(); -} diff --git a/startlingmo/lingmo-session-old/org.lingmo.Startup.xml b/startlingmo/lingmo-session-old/org.lingmo.Startup.xml deleted file mode 100644 index 68904ac..0000000 --- a/startlingmo/lingmo-session-old/org.lingmo.Startup.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/startlingmo/lingmo-session-old/sessiontrack.cpp b/startlingmo/lingmo-session-old/sessiontrack.cpp deleted file mode 100644 index c8363f7..0000000 --- a/startlingmo/lingmo-session-old/sessiontrack.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#include "sessiontrack.h" -#include "signalhandler.h" -#include -#include -#include -#include -#include - -SessionTrack::SessionTrack(const QVector &processes) - : m_processes(processes) { - SignalHandler::self()->addSignal(SIGTERM); - connect(SignalHandler::self(), &SignalHandler::signalReceived, - QCoreApplication::instance(), [](int signal) { - if (signal == SIGTERM) { - QCoreApplication::instance()->exit(0); - } - }); - - for (auto process : std::as_const(m_processes)) { - connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(removeProcess(int, QProcess::ExitStatus))); - } - - QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, - this, &SessionTrack::deleteLater); -} - -SessionTrack::~SessionTrack() { - disconnect(this, nullptr, QCoreApplication::instance(), nullptr); - - for (auto process : std::as_const(m_processes)) { - process->terminate(); - } - - // copy before the loop as we remove finished processes from the vector - const QVector processesCopy = m_processes; - for (auto process : processesCopy) { - if (process->state() == QProcess::Running && - !process->waitForFinished(500)) { - process->kill(); - } else { - delete process; - } - } -} - -void SessionTrack::removeProcess(int exitCode, QProcess::ExitStatus exitStatus) { - m_processes.removeAll(static_cast(sender())); -} \ No newline at end of file diff --git a/startlingmo/lingmo-session-old/sessiontrack.h b/startlingmo/lingmo-session-old/sessiontrack.h deleted file mode 100644 index 7cbc8b0..0000000 --- a/startlingmo/lingmo-session-old/sessiontrack.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - SPDX-FileCopyrightText: 2018 David Edmundson - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#pragma once - -#include -#include - -class SessionTrack : public QObject { - Q_OBJECT -public: - SessionTrack(const QVector &processes); - ~SessionTrack() override; - - void removeProcess(int exitCode, QProcess::ExitStatus exitStatus); - -private: - QVector m_processes; - QEventLoopLocker m_lock; -}; diff --git a/startlingmo/lingmo-session-old/signalhandler.cpp b/startlingmo/lingmo-session-old/signalhandler.cpp deleted file mode 100644 index b32c3ee..0000000 --- a/startlingmo/lingmo-session-old/signalhandler.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#include "signalhandler.h" -#include "debug.h" - -#include -#include -#include -#include - -int SignalHandler::signalFd[2]; - -SignalHandler::SignalHandler() -{ - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { - qCWarning(LINGMO_SESSION) << "Couldn't create a socketpair"; - return; - } - - m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); - connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); -} - -SignalHandler::~SignalHandler() -{ - for (int sig : std::as_const(m_signalsRegistered)) { - signal(sig, nullptr); - } - close(signalFd[0]); - close(signalFd[1]); -} - -void SignalHandler::addSignal(int signalToTrack) -{ - m_signalsRegistered.insert(signalToTrack); - signal(signalToTrack, signalHandler); -} - -void SignalHandler::signalHandler(int signal) -{ - ::write(signalFd[0], &signal, sizeof(signal)); -} - -void SignalHandler::handleSignal() -{ - m_handler->setEnabled(false); - int signal; - ::read(signalFd[1], &signal, sizeof(signal)); - m_handler->setEnabled(true); - - Q_EMIT signalReceived(signal); -} - -SignalHandler *SignalHandler::self() -{ - static SignalHandler s_self; - return &s_self; -} diff --git a/startlingmo/lingmo-session-old/signalhandler.h b/startlingmo/lingmo-session-old/signalhandler.h deleted file mode 100644 index c0f8428..0000000 --- a/startlingmo/lingmo-session-old/signalhandler.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#pragma once - -#include -#include -#include - -/** - * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop - * - * It's a singleton as it relies on static data getting defined. - */ -class SignalHandler : public QObject -{ - Q_OBJECT -public: - ~SignalHandler() override; - void addSignal(int signal); - - static SignalHandler *self(); - -Q_SIGNALS: - void signalReceived(int signal); - -private: - SignalHandler(); - void handleSignal(); - static void signalHandler(int signal); - - QSet m_signalsRegistered; - static int signalFd[2]; - QSocketNotifier *m_handler = nullptr; -}; diff --git a/startlingmo/lingmo-session-old/singleton.h b/startlingmo/lingmo-session-old/singleton.h deleted file mode 100644 index f2d9556..0000000 --- a/startlingmo/lingmo-session-old/singleton.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 zhuzichu520 - - SPDX-License-Identifier: MIT -*/ -#ifndef SINGLETON_H -#define SINGLETON_H - -/** - * @brief The Singleton class - */ -template -class Singleton { -public: - static T* getInstance(); -}; - -template -T* Singleton::getInstance() { - static T* instance = new T(); - return instance; -} - -#define SINGLETON(Class) \ -private: \ - friend class Singleton; \ - public: \ - static Class* getInstance() { \ - return Singleton::getInstance(); \ -} - -#endif // SINGLETON_H diff --git a/startlingmo/lingmo-session-old/startup.cpp b/startlingmo/lingmo-session-old/startup.cpp deleted file mode 100644 index 511ff73..0000000 --- a/startlingmo/lingmo-session-old/startup.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#include "startup.hpp" -#include - -#include "debug.h" -#include "startupadaptor.h" -#include "sessiontrack.h" - -#include -#include - -void Startup::updateLaunchEnv(const QString &key, const QString &value) { - qputenv(key.toLatin1(), value.toLatin1()); -} - -Startup::Startup(QObject *parent) : QObject(parent) { - - new StartupAdaptor(this); - QDBusConnection::sessionBus().registerObject( - QStringLiteral("/Startup"), QStringLiteral("org.lingmo.Startup"), this); - QDBusConnection::sessionBus().registerService( - QStringLiteral("org.lingmo.Startup")); - - if (qEnvironmentVariable("XDG_SESSION_TYPE") != QLatin1String("wayland")) { - // Currently don't do anything if we're not running under Wayland - } else { - // This must block until started as it sets the WAYLAND_DISPLAY/DISPLAY env - // variables needed for the rest of the boot fortunately it's very fast as - // it's just starting a wrapper - StartServiceJob kwinWaylandJob(QStringLiteral("kwin_wayland_wrapper"), - {QStringLiteral("--xwayland")}, - QStringLiteral("org.kde.KWinWrapper")); - kwinWaylandJob.exec(); - } - - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("MOZ_ENABLE_WAYLAND", "1"); - const QVector sequence = { - new StartProcessJob(QStringLiteral("foot"), {}, env) - }; - KJob *last = nullptr; - for (KJob *job : sequence) { - if (!job) { - continue; - } - if (last) { - connect(last, &KJob::finished, job, &KJob::start); - } - last = job; - } - - connect(sequence.last(), &KJob::finished, this, &Startup::finishStartup); - sequence.first()->start(); -} - -void Startup::finishStartup() { - qCDebug(LINGMO_SESSION) << "Finished"; - // upAndRunning(QStringLiteral("ready")); - - // playStartupSound(); - new SessionTrack(m_processes); - deleteLater(); -} - -bool Startup::startDetached(QProcess *process) { - process->setProcessChannelMode(QProcess::ForwardedChannels); - process->start(); - const bool ret = process->waitForStarted(); - if (ret) { - m_processes << process; - } - return ret; -} - -StartServiceJob::StartServiceJob(const QString &process, - const QStringList &args, - const QString &serviceId, - const QProcessEnvironment &additionalEnv) - : KJob(), m_process(new QProcess), m_serviceId(serviceId), - m_additionalEnv(additionalEnv) { - m_process->setProgram(process); - m_process->setArguments(args); - - auto watcher = - new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), - QDBusServiceWatcher::WatchForRegistration, this); - connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, - &StartServiceJob::emitResult); -} - -void StartServiceJob::start() { - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(m_additionalEnv); - m_process->setProcessEnvironment(env); - - if (!m_serviceId.isEmpty() && - QDBusConnection::sessionBus().interface()->isServiceRegistered( - m_serviceId)) { - qCDebug(LINGMO_SESSION) << m_process << "already running"; - emitResult(); - return; - } - qCDebug(LINGMO_SESSION) << "Starting " << m_process->program() - << m_process->arguments(); - if (!startup_ptr->startDetached(m_process)) { - qCWarning(LINGMO_SESSION) << "error starting process" - << m_process->program() << m_process->arguments(); - emitResult(); - } - - if (m_serviceId.isEmpty()) { - emitResult(); - } -} - -StartProcessJob::StartProcessJob(const QString &process, - const QStringList &args, - const QProcessEnvironment &additionalEnv) - : KJob(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); - - connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); -} - -void StartProcessJob::start() { - qCDebug(LINGMO_SESSION) << "Starting " << m_process->program() - << m_process->arguments(); - - m_process->start(); -} - -void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { - qCInfo(LINGMO_SESSION) << "process job " << m_process->program() - << "finished with exit code " << exitCode; - emitResult(); -} diff --git a/startlingmo/lingmo-session-old/startup.hpp b/startlingmo/lingmo-session-old/startup.hpp deleted file mode 100644 index 1d33736..0000000 --- a/startlingmo/lingmo-session-old/startup.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#ifndef STARTUP_HPP -#define STARTUP_HPP - -#include -#include - -#include - -#include - -class Startup : public QObject { - Q_OBJECT - -public: - Startup(QObject *parent); - void init(QObject *parent); - - bool startDetached(QProcess *process); - -public Q_SLOTS: - // alternatively we could drop this? - void updateLaunchEnv(const QString &key, const QString &value); - - void finishStartup(); - -private: - QVector m_processes; -}; - -/** - * Launches a process, and waits for the service to appear on the session bus - */ -class StartServiceJob : public KJob { - Q_OBJECT -public: - StartServiceJob( - const QString &process, const QStringList &args, const QString &serviceId, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - void start() override; - -private: - QProcess *m_process; - const QString m_serviceId; - const QProcessEnvironment m_additionalEnv; -}; - -/** - * Launches a process, and waits for the process to start - */ -class StartProcessJob : public KJob { - Q_OBJECT -public: - StartProcessJob( - const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - void start() override; - -public Q_SLOTS: - void finished(int, QProcess::ExitStatus); - -private: - QProcess *m_process; -}; - -extern std::shared_ptr startup_ptr; - -#endif // STARTUP_HPP \ No newline at end of file diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp index 667647e..654d7b4 100755 --- a/startlingmo/lingmo-session/main.cpp +++ b/startlingmo/lingmo-session/main.cpp @@ -18,30 +18,28 @@ */ #include "application.h" +#include #include #include -#include #include +int main(int argc, char *argv[]) { + QQuickWindow::setDefaultAlphaBuffer(true); + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); -int main(int argc, char *argv[]) -{ - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland")); - qputenv("XDG_SESSION_TYPE", QByteArrayLiteral("wayland")); - QQuickWindow::setDefaultAlphaBuffer(true); - QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); - - QCoreApplication app(argc, argv); + QCoreApplication app(argc, argv); - QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("Lingmo Session")); - parser.addHelpOption(); + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("Lingmo Session")); + parser.addHelpOption(); - QCommandLineOption waylandOption(QStringList() << "w" << "wayland" << "Wayland Mode"); - parser.addOption(waylandOption); - parser.process(app); + QCommandLineOption waylandOption(QStringList() << "w" + << "wayland" + << "Wayland Mode"); + parser.addOption(waylandOption); + parser.process(app); - new Application(parser, &app); + new Application(parser, &app); - return app.exec(); + return app.exec(); } diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp index 556c7df..e38d70d 100755 --- a/startlingmo/lingmo-session/processmanager.cpp +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -23,8 +23,10 @@ #include #include #include +#include #include "daemon-helper.h" +#include "debug.h" ProcessManager *s_self; @@ -101,34 +103,46 @@ void ProcessManager::startDesktopProcess() { // In the way, there will be no problem that desktop and launcher can't get // wallpaper. - QList> list; - // Desktop components - list << qMakePair(QString("lingmo-notificationd"), QStringList()); - list << qMakePair(QString("lingmo-statusbar"), QStringList()); - list << qMakePair(QString("lingmo-dock"), QStringList()); - list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); - list << qMakePair(QString("lingmo-launcher"), QStringList()); - list << qMakePair(QString("lingmo-powerman"), QStringList()); - list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); - - m_desktopAutoStartD = std::make_shared(list); - - // Auto start - QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); + auto xcb_extra = QProcessEnvironment(); + xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); + const QVector sequence = { + new LaunchProcess(QStringLiteral("lingmo-notificationd"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-statusbar"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-dock"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-filemanager"), + QStringList("--desktop"), xcb_extra), + + new LaunchProcess(QStringLiteral("lingmo-launcher"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-powerman"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-clipboard"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-wallpaper-color-pick"), {}, + xcb_extra), + }; + Job *last = nullptr; + for (Job *job : sequence) { + if (!job) { + continue; + } + if (last) { + connect(last, &Job::finished, job, &Job::start); + } + last = job; + } + + // connect(sequence.last(), &Job::finished, this, &Startup::finishStartup); + sequence.first()->start(); } void ProcessManager::startDaemonProcess() { - QList> list; - // list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); - // list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); - // list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); - // list << qMakePair(QString("lingmo-permission-surveillance"), - // QStringList()); - // // list << qMakePair(QString("lingmo-clipboard"), QStringList()); - // list << qMakePair(QString("lingmo-chotkeys"), QStringList()); + auto xcb_extra = QProcessEnvironment(); + xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); const QVector sequence = { - new StartProcessJob(QStringLiteral("foot"), {}), + new LaunchProcess(QStringLiteral("lingmo-settings-daemon"), {}, + xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-xembedsniproxy"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-gmenuproxy"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-permission-surveillance"), {}, + xcb_extra), }; Job *last = nullptr; for (Job *job : sequence) { @@ -141,9 +155,8 @@ void ProcessManager::startDaemonProcess() { last = job; } - // connect(sequence.last(), &Job::finished, this, &Startup::finishStartup); + connect(sequence.last(), &Job::finished, this, &ProcessManager::startDesktopProcess); sequence.first()->start(); - // m_daemonAutoStartD = std::make_shared(list); } void ProcessManager::loadAutoStartProcess() { @@ -190,7 +203,7 @@ void ProcessManager::loadAutoStartProcess() { list << qMakePair(program, args); } else { - qWarning() << "Invalid 'Exec' found in file!"; + qCWarning(LINGMO_SESSION_D) << "Invalid 'Exec' found in file!"; } } } @@ -248,14 +261,15 @@ StartProcessJob::StartProcessJob(const QString &process, } void StartProcessJob::start() { - qDebug() << "Starting " << m_process->program() << m_process->arguments(); + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); m_process->start(); } void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { - qDebug() << "process job " << m_process->program() - << "finished with exit code " << exitCode; + qCDebug(LINGMO_SESSION_D) << "process job " << m_process->program() + << "finished with exit code " << exitCode; emitResult(); } @@ -283,14 +297,15 @@ void StartServiceJob::start() { if (!m_serviceId.isEmpty() && QDBusConnection::sessionBus().interface()->isServiceRegistered( m_serviceId)) { - qDebug() << m_process << "already running"; + qCDebug(LINGMO_SESSION_D) << m_process << "already running"; emitResult(); return; } qDebug() << "Starting " << m_process->program() << m_process->arguments(); if (!ProcessManager::self()->startDetached(m_process)) { - qWarning() << "error starting process" << m_process->program() - << m_process->arguments(); + qCWarning(LINGMO_SESSION_D) + << "error starting process" << m_process->program() + << m_process->arguments(); emitResult(); } @@ -298,3 +313,23 @@ void StartServiceJob::start() { emitResult(); } } + +LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); +} + +void LaunchProcess::start() { + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); + + m_process->startDetached(); + + emitResult(); +} \ No newline at end of file diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h index 1eba859..fbb4d74 100755 --- a/startlingmo/lingmo-session/processmanager.h +++ b/startlingmo/lingmo-session/processmanager.h @@ -90,6 +90,21 @@ using namespace LINGMO_SESSION; /** * Launches a process, and waits for the process to start */ +class LaunchProcess : public Job { + Q_OBJECT +public: + LaunchProcess( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +private: + QProcess *m_process; +}; + +/** + * Launches a process, and waits for the process to finish + */ class StartProcessJob : public Job { Q_OBJECT public: diff --git a/startlingmo/lingmo-wayland-session.desktop b/startlingmo/lingmo-wayland-session.desktop new file mode 100755 index 0000000..4c1c412 --- /dev/null +++ b/startlingmo/lingmo-wayland-session.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Exec=startlingmo-wayland +Name=Lingmo Desktop (Wayland Experimental) +Keywords=session +Comment=session diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp index e6122e1..ad6f748 100644 --- a/startlingmo/startlingmo-wayland.cpp +++ b/startlingmo/startlingmo-wayland.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "startlingmo.hpp" @@ -77,6 +78,7 @@ int main(int argc, char **argv) { initScreenScaleFactors(); qputenv("XDG_SESSION_TYPE", "wayland"); + qputenv("QT_QPA_PLATFORM", "wayland"); auto oldSystemdEnvironment = getSystemdEnvironment(); if (!syncDBusEnvironment()) { From 6f72fa7c9f57e33ddfb70064b78b454317d5d94e Mon Sep 17 00:00:00 2001 From: Elysia Date: Mon, 2 Sep 2024 22:56:08 +0800 Subject: [PATCH 13/24] Added X11, having problems with existing lingmo_session --- startlingmo/CMakeLists.txt | 11 +++++ startlingmo/lingmo-xorg-session.desktop | 7 +++ startlingmo/startlingmo-wayland.cpp | 2 - startlingmo/startlingmo-x11.cpp | 62 +++++++++++++++++++++++++ startlingmo/startlingmo.cpp | 35 ++++++++++++++ startlingmo/startlingmo.hpp | 6 +++ 6 files changed, 121 insertions(+), 2 deletions(-) create mode 100755 startlingmo/lingmo-xorg-session.desktop create mode 100644 startlingmo/startlingmo-x11.cpp diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index 4689afa..7f94336 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -56,10 +56,21 @@ target_link_libraries(startlingmo-wayland Qt5::DBus ) +add_executable(startlingmo-x11 startlingmo-x11.cpp) +target_link_libraries(startlingmo-x11 + PRIVATE + startlingmo + PUBLIC + Qt5::Core + Qt5::DBus +) + add_subdirectory(lingmo-session) add_subdirectory(wayland_wrapper) install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS startlingmo-x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS lingmo-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) install(FILES lingmo-wayland-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions/) +install(FILES lingmo-xorg-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) diff --git a/startlingmo/lingmo-xorg-session.desktop b/startlingmo/lingmo-xorg-session.desktop new file mode 100755 index 0000000..7df13bc --- /dev/null +++ b/startlingmo/lingmo-xorg-session.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Exec=startlingmo-x11 +TryExec=startlingmo-x11 +Name=Lingmo Desktop (Xorg Session) +Keywords=session +Comment=session diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp index ad6f748..095e249 100644 --- a/startlingmo/startlingmo-wayland.cpp +++ b/startlingmo/startlingmo-wayland.cpp @@ -13,9 +13,7 @@ #include "debug.h" -extern "C" { #include "signal.h" -} extern QTextStream out; diff --git a/startlingmo/startlingmo-x11.cpp b/startlingmo/startlingmo-x11.cpp new file mode 100644 index 0000000..4084f0f --- /dev/null +++ b/startlingmo/startlingmo-x11.cpp @@ -0,0 +1,62 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "signal.h" +#include "startlingmo.hpp" + +#include + +#include + +void sighupHandler(int) { std::cout << "GOT SIGHUP\n"; } + +int main(int argc, char *argv[]) { + + QCoreApplication app(argc, argv); + + // When the X server dies we get a HUP signal from xinit. We must ignore it + // because we still need to do some cleanup. + signal(SIGHUP, sighupHandler); + + qputenv("QT_NO_XDG_DESKTOP_PORTAL", QByteArrayLiteral("1")); + + // ToDo: Maybe we can check wether lingmo de is already running? + + createConfigDirectory(); + initLanguage(); + initScreenScaleFactors(); + + // NOTE: Be very mindful of what you start this early in the process. The + // environment is not yet complete. + setupCursor(false); + + runEnvironmentScripts(); + + std::cout << "Starting lingmo de ...\n"; + + setupLingmoEnvironment(); + + qunsetenv("QT_NO_XDG_DESKTOP_PORTAL"); + auto oldSystemdEnvironment = getSystemdEnvironment(); + if (!syncDBusEnvironment()) { + // Startup error + messageBox(QStringLiteral("Could not sync environment to dbus.\n")); + return 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_*) + importSystemdEnvrionment(); + + if (!startLingmoSession(false)) + return 1; + + std::cout << "Shutting down lingmo de ...\n"; + + cleanupPlasmaEnvironment(oldSystemdEnvironment); + + return 0; +} \ No newline at end of file diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp index 127d614..bf0253b 100644 --- a/startlingmo/startlingmo.cpp +++ b/startlingmo/startlingmo.cpp @@ -476,3 +476,38 @@ void initScreenScaleFactors() { qputenv("GDK_DPI_SCALE", QByteArray::number(qFloor(scaleFactor), 'g', 0)); } } + +void messageBox(const QString &text) { + out << text; + runSync(QStringLiteral("xmessage"), + {QStringLiteral("-geometry"), QStringLiteral("500x100"), text}); +} + +void cleanupPlasmaEnvironment( + const std::optional &oldSystemdEnvironment) { + + if (!oldSystemdEnvironment) { + return; + } + + auto currentEnv = getSystemdEnvironment(); + if (!currentEnv) { + return; + } + + // According to systemd documentation: + // If a variable is listed in both, the variable is set after this method + // returns, i.e. the set list overrides the unset list. So this will + // effectively restore the state to the values in oldSystemdEnvironment. + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("UnsetAndSetEnvironment")); + message.setArguments({currentEnv.value().keys(), + oldSystemdEnvironment.value().toStringList()}); + + // The session program gonna quit soon, ensure the message is flushed. + auto reply = QDBusConnection::sessionBus().asyncCall(message); + reply.waitForFinished(); +} diff --git a/startlingmo/startlingmo.hpp b/startlingmo/startlingmo.hpp index 013e71e..ce89353 100644 --- a/startlingmo/startlingmo.hpp +++ b/startlingmo/startlingmo.hpp @@ -57,4 +57,10 @@ struct KillBeforeDeleter { bool isSessionVariable(const QByteArray &name); void initScreenScaleFactors(); + +void messageBox(const QString &text); + +void cleanupPlasmaEnvironment( + const std::optional &oldSystemdEnvironment); + #endif // STARTLINGMO_HPP \ No newline at end of file From 13e5095907e4c0c2c1f3d271b484f83c37d5805c Mon Sep 17 00:00:00 2001 From: Elysia Date: Mon, 2 Sep 2024 22:57:09 +0800 Subject: [PATCH 14/24] Update CI --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 46c88d1..4632b79 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,14 +10,19 @@ jobs: ubuntu: name: Ubuntu runs-on: ubuntu-latest + container: docker.io/library/debian:trixie steps: - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v4 + - name: Update repository run: sudo apt-get update -y + - name: Install the basic dev packages run: sudo apt-get install -y equivs curl git devscripts lintian build-essential automake autotools-dev cmake g++ + - name: Install build dependencies run: sudo mk-build-deps -i -t "apt-get --yes" -r + - name: Build Package run: sudo dpkg-buildpackage -b -uc -us -j$(nproc) From 50304b31be1fead5f89d26d2b6697beeb7698f70 Mon Sep 17 00:00:00 2001 From: Elysia Date: Mon, 2 Sep 2024 22:59:27 +0800 Subject: [PATCH 15/24] Remove sudo --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4632b79..b8cfe81 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,13 +16,13 @@ jobs: uses: actions/checkout@v4 - name: Update repository - run: sudo apt-get update -y + run: apt-get update -y - name: Install the basic dev packages - run: sudo apt-get install -y equivs curl git devscripts lintian build-essential automake autotools-dev cmake g++ + run: apt-get install -y equivs curl git devscripts lintian build-essential automake autotools-dev cmake g++ - name: Install build dependencies - run: sudo mk-build-deps -i -t "apt-get --yes" -r + run: mk-build-deps -i -t "apt-get --yes" -r - name: Build Package - run: sudo dpkg-buildpackage -b -uc -us -j$(nproc) + run: dpkg-buildpackage -b -uc -us -j$(nproc) From 456017ea38e582adebdc30f18e0daf222e8fd6f0 Mon Sep 17 00:00:00 2001 From: Elysia Date: Mon, 2 Sep 2024 23:01:25 +0800 Subject: [PATCH 16/24] Update depends --- debian/control | 4 ++++ notificationd/translations/en_US.ts | 4 ++-- notificationd/translations/zh_CN.ts | 4 ++-- polkit-agent/translations/ar_AA.ts | 6 +++--- polkit-agent/translations/be_BY.ts | 6 +++--- polkit-agent/translations/be_Latn.ts | 6 +++--- polkit-agent/translations/bg_BG.ts | 6 +++--- polkit-agent/translations/bs_BA.ts | 6 +++--- polkit-agent/translations/cs_CZ.ts | 6 +++--- polkit-agent/translations/da_DK.ts | 6 +++--- polkit-agent/translations/de_DE.ts | 6 +++--- polkit-agent/translations/en_US.ts | 6 +++--- polkit-agent/translations/eo_XX.ts | 6 +++--- polkit-agent/translations/es_ES.ts | 6 +++--- polkit-agent/translations/es_MX.ts | 6 +++--- polkit-agent/translations/fa_IR.ts | 6 +++--- polkit-agent/translations/fi_FI.ts | 6 +++--- polkit-agent/translations/fr_FR.ts | 6 +++--- polkit-agent/translations/he_IL.ts | 6 +++--- polkit-agent/translations/hi_IN.ts | 6 +++--- polkit-agent/translations/hu_HU.ts | 6 +++--- polkit-agent/translations/id_ID.ts | 6 +++--- polkit-agent/translations/ie.ts | 6 +++--- polkit-agent/translations/it_IT.ts | 6 +++--- polkit-agent/translations/ja_JP.ts | 6 +++--- polkit-agent/translations/lt_LT.ts | 6 +++--- polkit-agent/translations/lv_LV.ts | 6 +++--- polkit-agent/translations/mg.ts | 6 +++--- polkit-agent/translations/ml_IN.ts | 6 +++--- polkit-agent/translations/nb_NO.ts | 6 +++--- polkit-agent/translations/ne_NP.ts | 6 +++--- polkit-agent/translations/pl_PL.ts | 6 +++--- polkit-agent/translations/pt_BR.ts | 6 +++--- polkit-agent/translations/pt_PT.ts | 6 +++--- polkit-agent/translations/ro_RO.ts | 6 +++--- polkit-agent/translations/ru_RU.ts | 6 +++--- polkit-agent/translations/si_LK.ts | 6 +++--- polkit-agent/translations/sk_SK.ts | 6 +++--- polkit-agent/translations/so.ts | 6 +++--- polkit-agent/translations/sr_RS.ts | 6 +++--- polkit-agent/translations/sv_SE.ts | 6 +++--- polkit-agent/translations/sw.ts | 6 +++--- polkit-agent/translations/ta_IN.ts | 6 +++--- polkit-agent/translations/tr_TR.ts | 6 +++--- polkit-agent/translations/uk_UA.ts | 6 +++--- polkit-agent/translations/uz_UZ.ts | 6 +++--- polkit-agent/translations/vi_VN.ts | 6 +++--- polkit-agent/translations/zh_CN.ts | 6 +++--- polkit-agent/translations/zh_HK.ts | 6 +++--- polkit-agent/translations/zh_TW.ts | 6 +++--- settings-daemon/translations/ar_AA.ts | 16 ++++++++++++++++ settings-daemon/translations/be_BY.ts | 16 ++++++++++++++++ settings-daemon/translations/be_Latn.ts | 16 ++++++++++++++++ settings-daemon/translations/bg_BG.ts | 16 ++++++++++++++++ settings-daemon/translations/bs_BA.ts | 16 ++++++++++++++++ settings-daemon/translations/cs_CZ.ts | 16 ++++++++++++++++ settings-daemon/translations/da_DK.ts | 16 ++++++++++++++++ settings-daemon/translations/de_DE.ts | 16 ++++++++++++++++ settings-daemon/translations/en_US.ts | 4 ++-- settings-daemon/translations/eo_XX.ts | 16 ++++++++++++++++ settings-daemon/translations/es_ES.ts | 16 ++++++++++++++++ settings-daemon/translations/es_MX.ts | 16 ++++++++++++++++ settings-daemon/translations/fa_IR.ts | 16 ++++++++++++++++ settings-daemon/translations/fi_FI.ts | 16 ++++++++++++++++ settings-daemon/translations/fr_FR.ts | 16 ++++++++++++++++ settings-daemon/translations/he_IL.ts | 16 ++++++++++++++++ settings-daemon/translations/hi_IN.ts | 16 ++++++++++++++++ settings-daemon/translations/hu_HU.ts | 16 ++++++++++++++++ settings-daemon/translations/id_ID.ts | 16 ++++++++++++++++ settings-daemon/translations/ie.ts | 16 ++++++++++++++++ settings-daemon/translations/it_IT.ts | 16 ++++++++++++++++ settings-daemon/translations/ja_JP.ts | 16 ++++++++++++++++ settings-daemon/translations/lt_LT.ts | 16 ++++++++++++++++ settings-daemon/translations/lv_LV.ts | 16 ++++++++++++++++ settings-daemon/translations/mg.ts | 16 ++++++++++++++++ settings-daemon/translations/ml_IN.ts | 16 ++++++++++++++++ settings-daemon/translations/nb_NO.ts | 16 ++++++++++++++++ settings-daemon/translations/ne_NP.ts | 16 ++++++++++++++++ settings-daemon/translations/pl_PL.ts | 16 ++++++++++++++++ settings-daemon/translations/pt_BR.ts | 16 ++++++++++++++++ settings-daemon/translations/pt_PT.ts | 16 ++++++++++++++++ settings-daemon/translations/ro_RO.ts | 16 ++++++++++++++++ settings-daemon/translations/ru_RU.ts | 16 ++++++++++++++++ settings-daemon/translations/si_LK.ts | 16 ++++++++++++++++ settings-daemon/translations/sk_SK.ts | 16 ++++++++++++++++ settings-daemon/translations/so.ts | 16 ++++++++++++++++ settings-daemon/translations/sr_RS.ts | 16 ++++++++++++++++ settings-daemon/translations/sv_SE.ts | 16 ++++++++++++++++ settings-daemon/translations/sw.ts | 16 ++++++++++++++++ settings-daemon/translations/ta_IN.ts | 16 ++++++++++++++++ settings-daemon/translations/tr_TR.ts | 16 ++++++++++++++++ settings-daemon/translations/uk_UA.ts | 16 ++++++++++++++++ settings-daemon/translations/uz_UZ.ts | 16 ++++++++++++++++ settings-daemon/translations/vi_VN.ts | 16 ++++++++++++++++ settings-daemon/translations/zh_CN.ts | 4 ++-- settings-daemon/translations/zh_TW.ts | 16 ++++++++++++++++ shutdown-ui/translations/ar_AA.ts | 10 +++++----- shutdown-ui/translations/be_BY.ts | 16 +++++++++------- shutdown-ui/translations/be_Latn.ts | 13 +++++++++---- shutdown-ui/translations/bg_BG.ts | 10 +++++----- shutdown-ui/translations/bs_BA.ts | 13 +++++++++---- shutdown-ui/translations/cs_CZ.ts | 16 +++++++++------- shutdown-ui/translations/da_DK.ts | 16 +++++++++------- shutdown-ui/translations/de_DE.ts | 16 +++++++++------- shutdown-ui/translations/en_US.ts | 10 +++++----- shutdown-ui/translations/eo_XX.ts | 13 +++++++++---- shutdown-ui/translations/es_ES.ts | 16 +++++++++------- shutdown-ui/translations/es_MX.ts | 13 +++++++++---- shutdown-ui/translations/fa_IR.ts | 16 +++++++++------- shutdown-ui/translations/fi_FI.ts | 13 +++++++++---- shutdown-ui/translations/fr_FR.ts | 16 +++++++++------- shutdown-ui/translations/he_IL.ts | 10 +++++----- shutdown-ui/translations/hi_IN.ts | 13 +++++++++---- shutdown-ui/translations/hu_HU.ts | 16 +++++++++------- shutdown-ui/translations/id_ID.ts | 16 +++++++++------- shutdown-ui/translations/ie.ts | 13 +++++++++---- shutdown-ui/translations/it_IT.ts | 16 +++++++++------- shutdown-ui/translations/ja_JP.ts | 10 +++++----- shutdown-ui/translations/lt_LT.ts | 10 +++++----- shutdown-ui/translations/lv_LV.ts | 10 +++++----- shutdown-ui/translations/mg.ts | 10 +++++----- shutdown-ui/translations/ml_IN.ts | 10 +++++----- shutdown-ui/translations/nb_NO.ts | 13 +++++++++---- shutdown-ui/translations/ne_NP.ts | 13 +++++++++---- shutdown-ui/translations/pl_PL.ts | 16 +++++++++------- shutdown-ui/translations/pt_BR.ts | 16 +++++++++------- shutdown-ui/translations/pt_PT.ts | 16 +++++++++------- shutdown-ui/translations/ro_RO.ts | 10 +++++----- shutdown-ui/translations/ru_RU.ts | 16 +++++++++------- shutdown-ui/translations/si_LK.ts | 13 +++++++++---- shutdown-ui/translations/sk_SK.ts | 16 +++++++++------- shutdown-ui/translations/so.ts | 13 +++++++++---- shutdown-ui/translations/sr_RS.ts | 10 +++++----- shutdown-ui/translations/sv_SE.ts | 16 +++++++++------- shutdown-ui/translations/sw.ts | 10 +++++----- shutdown-ui/translations/ta_IN.ts | 13 +++++++++---- shutdown-ui/translations/tr_TR.ts | 16 +++++++++------- shutdown-ui/translations/uk_UA.ts | 16 +++++++++------- shutdown-ui/translations/uz_UZ.ts | 13 +++++++++---- shutdown-ui/translations/vi_VN.ts | 10 +++++----- shutdown-ui/translations/zh_CN.ts | 10 +++++----- shutdown-ui/translations/zh_TW.ts | 16 +++++++++------- 142 files changed, 1215 insertions(+), 404 deletions(-) diff --git a/debian/control b/debian/control index 57e8e53..6a74a13 100755 --- a/debian/control +++ b/debian/control @@ -6,6 +6,8 @@ Build-Depends: cmake, debhelper (>= 9), extra-cmake-modules, pkg-config, + kwin-x11, + kwin-wayland, xserver-xorg-input-libinput-dev, libx11-xcb-dev, libxcb1-dev, @@ -62,6 +64,8 @@ Depends: accountsservice, qml-module-qtquick-shapes, appmenu-gtk2-module, appmenu-gtk3-module, + kwin-x11, + kwin-wayland, ${misc:Depends}, ${shlibs:Depends} Description: LingmoOS System Components diff --git a/notificationd/translations/en_US.ts b/notificationd/translations/en_US.ts index caba99f..d3135a2 100755 --- a/notificationd/translations/en_US.ts +++ b/notificationd/translations/en_US.ts @@ -42,12 +42,12 @@ NotificationWindow - + Notification Center - + No notifications diff --git a/notificationd/translations/zh_CN.ts b/notificationd/translations/zh_CN.ts index 1780ce0..200920c 100755 --- a/notificationd/translations/zh_CN.ts +++ b/notificationd/translations/zh_CN.ts @@ -42,12 +42,12 @@ NotificationWindow - + Notification Center 通知中心 - + No notifications 无通知 diff --git a/polkit-agent/translations/ar_AA.ts b/polkit-agent/translations/ar_AA.ts index 15b234b..93e3ab2 100755 --- a/polkit-agent/translations/ar_AA.ts +++ b/polkit-agent/translations/ar_AA.ts @@ -4,17 +4,17 @@ main - + Password كلمة المرور - + Done تم - + Cancel إلغاﺀ diff --git a/polkit-agent/translations/be_BY.ts b/polkit-agent/translations/be_BY.ts index 4e8e3ed..88f72ba 100755 --- a/polkit-agent/translations/be_BY.ts +++ b/polkit-agent/translations/be_BY.ts @@ -4,17 +4,17 @@ main - + Password Пароль - + Done Гатова - + Cancel Скасаваць diff --git a/polkit-agent/translations/be_Latn.ts b/polkit-agent/translations/be_Latn.ts index 987ee17..e7e32de 100755 --- a/polkit-agent/translations/be_Latn.ts +++ b/polkit-agent/translations/be_Latn.ts @@ -4,17 +4,17 @@ main - + Password - + Done - + Cancel diff --git a/polkit-agent/translations/bg_BG.ts b/polkit-agent/translations/bg_BG.ts index 1c73ca5..bb3ba0b 100755 --- a/polkit-agent/translations/bg_BG.ts +++ b/polkit-agent/translations/bg_BG.ts @@ -4,17 +4,17 @@ main - + Password Парола - + Done Готово - + Cancel Отказ diff --git a/polkit-agent/translations/bs_BA.ts b/polkit-agent/translations/bs_BA.ts index e72f11e..dd04c77 100755 --- a/polkit-agent/translations/bs_BA.ts +++ b/polkit-agent/translations/bs_BA.ts @@ -4,17 +4,17 @@ main - + Password Lozinka - + Done U redu - + Cancel Otkaži diff --git a/polkit-agent/translations/cs_CZ.ts b/polkit-agent/translations/cs_CZ.ts index 4d374f5..99a67c9 100755 --- a/polkit-agent/translations/cs_CZ.ts +++ b/polkit-agent/translations/cs_CZ.ts @@ -4,17 +4,17 @@ main - + Password Heslo - + Done Hotovo - + Cancel Storno diff --git a/polkit-agent/translations/da_DK.ts b/polkit-agent/translations/da_DK.ts index 9544277..ccca8be 100755 --- a/polkit-agent/translations/da_DK.ts +++ b/polkit-agent/translations/da_DK.ts @@ -4,17 +4,17 @@ main - + Password Kodeord - + Done Færdig - + Cancel Annullér diff --git a/polkit-agent/translations/de_DE.ts b/polkit-agent/translations/de_DE.ts index da2ec39..ef28304 100755 --- a/polkit-agent/translations/de_DE.ts +++ b/polkit-agent/translations/de_DE.ts @@ -4,17 +4,17 @@ main - + Password Passwort - + Done Fertig - + Cancel Abbrechen diff --git a/polkit-agent/translations/en_US.ts b/polkit-agent/translations/en_US.ts index 619e52b..4ed3039 100755 --- a/polkit-agent/translations/en_US.ts +++ b/polkit-agent/translations/en_US.ts @@ -4,17 +4,17 @@ main - + Password Password - + Done Done - + Cancel Cancel diff --git a/polkit-agent/translations/eo_XX.ts b/polkit-agent/translations/eo_XX.ts index 8c0e7fa..df92883 100755 --- a/polkit-agent/translations/eo_XX.ts +++ b/polkit-agent/translations/eo_XX.ts @@ -4,17 +4,17 @@ main - + Password Pasvorto - + Done Finita - + Cancel Nuligi diff --git a/polkit-agent/translations/es_ES.ts b/polkit-agent/translations/es_ES.ts index fa80ef6..b1c49f3 100755 --- a/polkit-agent/translations/es_ES.ts +++ b/polkit-agent/translations/es_ES.ts @@ -4,17 +4,17 @@ main - + Password Contraseña - + Done Terminado - + Cancel Cancelar diff --git a/polkit-agent/translations/es_MX.ts b/polkit-agent/translations/es_MX.ts index 5ff6c3c..9fa8ceb 100755 --- a/polkit-agent/translations/es_MX.ts +++ b/polkit-agent/translations/es_MX.ts @@ -4,17 +4,17 @@ main - + Password Contraseña - + Done Listo - + Cancel Cancelar diff --git a/polkit-agent/translations/fa_IR.ts b/polkit-agent/translations/fa_IR.ts index 031c09f..acabea4 100755 --- a/polkit-agent/translations/fa_IR.ts +++ b/polkit-agent/translations/fa_IR.ts @@ -4,17 +4,17 @@ main - + Password گذرواژه - + Done انجام شد - + Cancel لغو کردن diff --git a/polkit-agent/translations/fi_FI.ts b/polkit-agent/translations/fi_FI.ts index 91836bc..94637d2 100755 --- a/polkit-agent/translations/fi_FI.ts +++ b/polkit-agent/translations/fi_FI.ts @@ -4,17 +4,17 @@ main - + Password Salasana - + Done Valmis - + Cancel Peruuta diff --git a/polkit-agent/translations/fr_FR.ts b/polkit-agent/translations/fr_FR.ts index 2a990a6..1f44424 100755 --- a/polkit-agent/translations/fr_FR.ts +++ b/polkit-agent/translations/fr_FR.ts @@ -4,17 +4,17 @@ main - + Password Mot de passe - + Done Terminé - + Cancel Annuler diff --git a/polkit-agent/translations/he_IL.ts b/polkit-agent/translations/he_IL.ts index ce4f114..6fc3c13 100755 --- a/polkit-agent/translations/he_IL.ts +++ b/polkit-agent/translations/he_IL.ts @@ -4,17 +4,17 @@ main - + Password סיסמא - + Done בוצע - + Cancel ביטול diff --git a/polkit-agent/translations/hi_IN.ts b/polkit-agent/translations/hi_IN.ts index 829e546..ba6c799 100755 --- a/polkit-agent/translations/hi_IN.ts +++ b/polkit-agent/translations/hi_IN.ts @@ -4,17 +4,17 @@ main - + Password कुंजिका - + Done ठीक है - + Cancel रद्द करें diff --git a/polkit-agent/translations/hu_HU.ts b/polkit-agent/translations/hu_HU.ts index 029d37b..f8af3fe 100755 --- a/polkit-agent/translations/hu_HU.ts +++ b/polkit-agent/translations/hu_HU.ts @@ -4,17 +4,17 @@ main - + Password Jelszó - + Done Kész - + Cancel Mégsem diff --git a/polkit-agent/translations/id_ID.ts b/polkit-agent/translations/id_ID.ts index 18ba162..1f3ca8b 100755 --- a/polkit-agent/translations/id_ID.ts +++ b/polkit-agent/translations/id_ID.ts @@ -4,17 +4,17 @@ main - + Password Sandi - + Done Selesai - + Cancel Batal diff --git a/polkit-agent/translations/ie.ts b/polkit-agent/translations/ie.ts index 159b39d..a05e229 100755 --- a/polkit-agent/translations/ie.ts +++ b/polkit-agent/translations/ie.ts @@ -4,17 +4,17 @@ main - + Password Contrasigne - + Done Finit - + Cancel Anullar diff --git a/polkit-agent/translations/it_IT.ts b/polkit-agent/translations/it_IT.ts index 7283fb0..1657325 100755 --- a/polkit-agent/translations/it_IT.ts +++ b/polkit-agent/translations/it_IT.ts @@ -4,17 +4,17 @@ main - + Password Password - + Done Finito - + Cancel Annulla diff --git a/polkit-agent/translations/ja_JP.ts b/polkit-agent/translations/ja_JP.ts index e0ca745..f0bad7e 100755 --- a/polkit-agent/translations/ja_JP.ts +++ b/polkit-agent/translations/ja_JP.ts @@ -4,17 +4,17 @@ main - + Password パスワード - + Done 終了 - + Cancel キャンセル diff --git a/polkit-agent/translations/lt_LT.ts b/polkit-agent/translations/lt_LT.ts index 71cd3e6..029ba39 100755 --- a/polkit-agent/translations/lt_LT.ts +++ b/polkit-agent/translations/lt_LT.ts @@ -4,17 +4,17 @@ main - + Password Slaptažodis - + Done Atlikta - + Cancel Atsisakyti diff --git a/polkit-agent/translations/lv_LV.ts b/polkit-agent/translations/lv_LV.ts index b00dd25..bf889ba 100755 --- a/polkit-agent/translations/lv_LV.ts +++ b/polkit-agent/translations/lv_LV.ts @@ -4,17 +4,17 @@ main - + Password Parole - + Done Gatavs - + Cancel Atcelt diff --git a/polkit-agent/translations/mg.ts b/polkit-agent/translations/mg.ts index d6e4001..01270ef 100755 --- a/polkit-agent/translations/mg.ts +++ b/polkit-agent/translations/mg.ts @@ -4,17 +4,17 @@ main - + Password Teny miafina - + Done Vita - + Cancel Ajanona diff --git a/polkit-agent/translations/ml_IN.ts b/polkit-agent/translations/ml_IN.ts index a768c01..d276d73 100755 --- a/polkit-agent/translations/ml_IN.ts +++ b/polkit-agent/translations/ml_IN.ts @@ -4,17 +4,17 @@ main - + Password - + Done - + Cancel diff --git a/polkit-agent/translations/nb_NO.ts b/polkit-agent/translations/nb_NO.ts index 7b943cc..35b8cef 100755 --- a/polkit-agent/translations/nb_NO.ts +++ b/polkit-agent/translations/nb_NO.ts @@ -4,17 +4,17 @@ main - + Password Passord - + Done Ferdig - + Cancel Avbryt diff --git a/polkit-agent/translations/ne_NP.ts b/polkit-agent/translations/ne_NP.ts index 20061c4..b578a5b 100755 --- a/polkit-agent/translations/ne_NP.ts +++ b/polkit-agent/translations/ne_NP.ts @@ -4,17 +4,17 @@ main - + Password गोप्य शब्द - + Done भयो - + Cancel रद्द diff --git a/polkit-agent/translations/pl_PL.ts b/polkit-agent/translations/pl_PL.ts index 84d6f33..e479980 100755 --- a/polkit-agent/translations/pl_PL.ts +++ b/polkit-agent/translations/pl_PL.ts @@ -4,17 +4,17 @@ main - + Password Hasło - + Done Gotowe - + Cancel Anuluj diff --git a/polkit-agent/translations/pt_BR.ts b/polkit-agent/translations/pt_BR.ts index 2021184..6cc713b 100755 --- a/polkit-agent/translations/pt_BR.ts +++ b/polkit-agent/translations/pt_BR.ts @@ -4,17 +4,17 @@ main - + Password Senha - + Done Feito - + Cancel Cancelar diff --git a/polkit-agent/translations/pt_PT.ts b/polkit-agent/translations/pt_PT.ts index fa68e65..8234457 100755 --- a/polkit-agent/translations/pt_PT.ts +++ b/polkit-agent/translations/pt_PT.ts @@ -4,17 +4,17 @@ main - + Password Palavra-passe - + Done Feito - + Cancel Cancelar diff --git a/polkit-agent/translations/ro_RO.ts b/polkit-agent/translations/ro_RO.ts index fef6d31..44dbef6 100755 --- a/polkit-agent/translations/ro_RO.ts +++ b/polkit-agent/translations/ro_RO.ts @@ -4,17 +4,17 @@ main - + Password - + Done - + Cancel diff --git a/polkit-agent/translations/ru_RU.ts b/polkit-agent/translations/ru_RU.ts index 3605a9a..bccf776 100755 --- a/polkit-agent/translations/ru_RU.ts +++ b/polkit-agent/translations/ru_RU.ts @@ -4,17 +4,17 @@ main - + Password Пароль - + Done Готово - + Cancel Отменить diff --git a/polkit-agent/translations/si_LK.ts b/polkit-agent/translations/si_LK.ts index e747232..5b726c1 100755 --- a/polkit-agent/translations/si_LK.ts +++ b/polkit-agent/translations/si_LK.ts @@ -4,17 +4,17 @@ main - + Password මුරපදය - + Done - + Cancel අවලංගු කරන්න diff --git a/polkit-agent/translations/sk_SK.ts b/polkit-agent/translations/sk_SK.ts index ebff851..b988885 100755 --- a/polkit-agent/translations/sk_SK.ts +++ b/polkit-agent/translations/sk_SK.ts @@ -4,17 +4,17 @@ main - + Password Heslo - + Done Hotovo - + Cancel Zrušiť diff --git a/polkit-agent/translations/so.ts b/polkit-agent/translations/so.ts index eaf4da9..ea9d555 100755 --- a/polkit-agent/translations/so.ts +++ b/polkit-agent/translations/so.ts @@ -4,17 +4,17 @@ main - + Password Erey sireed - + Done Dhammee - + Cancel Xir diff --git a/polkit-agent/translations/sr_RS.ts b/polkit-agent/translations/sr_RS.ts index 3b7dda5..e4502f1 100755 --- a/polkit-agent/translations/sr_RS.ts +++ b/polkit-agent/translations/sr_RS.ts @@ -4,17 +4,17 @@ main - + Password Šifra - + Done Završeno - + Cancel Otkaži diff --git a/polkit-agent/translations/sv_SE.ts b/polkit-agent/translations/sv_SE.ts index 4663dbb..297adbf 100755 --- a/polkit-agent/translations/sv_SE.ts +++ b/polkit-agent/translations/sv_SE.ts @@ -4,17 +4,17 @@ main - + Password Lösenord - + Done Klar - + Cancel Avbryt diff --git a/polkit-agent/translations/sw.ts b/polkit-agent/translations/sw.ts index 83f9200..28fdc04 100755 --- a/polkit-agent/translations/sw.ts +++ b/polkit-agent/translations/sw.ts @@ -4,17 +4,17 @@ main - + Password Neno la siri - + Done Maliza - + Cancel Ghairi diff --git a/polkit-agent/translations/ta_IN.ts b/polkit-agent/translations/ta_IN.ts index 3cac0e6..ad1ad9b 100755 --- a/polkit-agent/translations/ta_IN.ts +++ b/polkit-agent/translations/ta_IN.ts @@ -4,17 +4,17 @@ main - + Password - + Done - + Cancel diff --git a/polkit-agent/translations/tr_TR.ts b/polkit-agent/translations/tr_TR.ts index aff4576..13d343b 100755 --- a/polkit-agent/translations/tr_TR.ts +++ b/polkit-agent/translations/tr_TR.ts @@ -4,17 +4,17 @@ main - + Password Şifre - + Done Hazır - + Cancel İptal diff --git a/polkit-agent/translations/uk_UA.ts b/polkit-agent/translations/uk_UA.ts index 3905e4a..da6d900 100755 --- a/polkit-agent/translations/uk_UA.ts +++ b/polkit-agent/translations/uk_UA.ts @@ -4,17 +4,17 @@ main - + Password Пароль - + Done Готово - + Cancel Скасувати diff --git a/polkit-agent/translations/uz_UZ.ts b/polkit-agent/translations/uz_UZ.ts index 58663f9..cdf5f69 100755 --- a/polkit-agent/translations/uz_UZ.ts +++ b/polkit-agent/translations/uz_UZ.ts @@ -4,17 +4,17 @@ main - + Password Hahfiy so'z - + Done Bajarildi - + Cancel Bekor diff --git a/polkit-agent/translations/vi_VN.ts b/polkit-agent/translations/vi_VN.ts index 0bb4f86..0229e89 100755 --- a/polkit-agent/translations/vi_VN.ts +++ b/polkit-agent/translations/vi_VN.ts @@ -4,17 +4,17 @@ main - + Password Mật khẩu - + Done Đã hoàn thành - + Cancel Đã hủy diff --git a/polkit-agent/translations/zh_CN.ts b/polkit-agent/translations/zh_CN.ts index 4a38ab3..5a9fe16 100755 --- a/polkit-agent/translations/zh_CN.ts +++ b/polkit-agent/translations/zh_CN.ts @@ -4,17 +4,17 @@ main - + Password 密码 - + Done 完成 - + Cancel 取消 diff --git a/polkit-agent/translations/zh_HK.ts b/polkit-agent/translations/zh_HK.ts index 6419785..e1a1f12 100755 --- a/polkit-agent/translations/zh_HK.ts +++ b/polkit-agent/translations/zh_HK.ts @@ -4,17 +4,17 @@ main - + Password - + Done - + Cancel diff --git a/polkit-agent/translations/zh_TW.ts b/polkit-agent/translations/zh_TW.ts index 6004332..4a38641 100755 --- a/polkit-agent/translations/zh_TW.ts +++ b/polkit-agent/translations/zh_TW.ts @@ -4,17 +4,17 @@ main - + Password 密碼 - + Done 完成 - + Cancel 取消 diff --git a/settings-daemon/translations/ar_AA.ts b/settings-daemon/translations/ar_AA.ts index a7883e8..ee1fa0e 100755 --- a/settings-daemon/translations/ar_AA.ts +++ b/settings-daemon/translations/ar_AA.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/be_BY.ts b/settings-daemon/translations/be_BY.ts index e5954da..9870cac 100755 --- a/settings-daemon/translations/be_BY.ts +++ b/settings-daemon/translations/be_BY.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/be_Latn.ts b/settings-daemon/translations/be_Latn.ts index 38af7fa..1162e55 100755 --- a/settings-daemon/translations/be_Latn.ts +++ b/settings-daemon/translations/be_Latn.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/bg_BG.ts b/settings-daemon/translations/bg_BG.ts index abd9d1a..e53cbf4 100755 --- a/settings-daemon/translations/bg_BG.ts +++ b/settings-daemon/translations/bg_BG.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/bs_BA.ts b/settings-daemon/translations/bs_BA.ts index fd1b7c4..0cfb124 100755 --- a/settings-daemon/translations/bs_BA.ts +++ b/settings-daemon/translations/bs_BA.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/cs_CZ.ts b/settings-daemon/translations/cs_CZ.ts index b0cdb15..63b0c8d 100755 --- a/settings-daemon/translations/cs_CZ.ts +++ b/settings-daemon/translations/cs_CZ.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/da_DK.ts b/settings-daemon/translations/da_DK.ts index 3760b5d..a08d009 100755 --- a/settings-daemon/translations/da_DK.ts +++ b/settings-daemon/translations/da_DK.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/de_DE.ts b/settings-daemon/translations/de_DE.ts index 97385ed..dc778b1 100755 --- a/settings-daemon/translations/de_DE.ts +++ b/settings-daemon/translations/de_DE.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/en_US.ts b/settings-daemon/translations/en_US.ts index 4711ded..5ceee02 100755 --- a/settings-daemon/translations/en_US.ts +++ b/settings-daemon/translations/en_US.ts @@ -47,7 +47,7 @@ Language - + The system language has been changed, please log out and log in @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/eo_XX.ts b/settings-daemon/translations/eo_XX.ts index 9dc12b0..e69fef0 100755 --- a/settings-daemon/translations/eo_XX.ts +++ b/settings-daemon/translations/eo_XX.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/es_ES.ts b/settings-daemon/translations/es_ES.ts index 5960286..4326eec 100755 --- a/settings-daemon/translations/es_ES.ts +++ b/settings-daemon/translations/es_ES.ts @@ -68,6 +68,22 @@ atrás + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/es_MX.ts b/settings-daemon/translations/es_MX.ts index f1de702..d7581e7 100755 --- a/settings-daemon/translations/es_MX.ts +++ b/settings-daemon/translations/es_MX.ts @@ -44,6 +44,22 @@ %1 atras + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/fa_IR.ts b/settings-daemon/translations/fa_IR.ts index 62a5b5d..28645ba 100755 --- a/settings-daemon/translations/fa_IR.ts +++ b/settings-daemon/translations/fa_IR.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/fi_FI.ts b/settings-daemon/translations/fi_FI.ts index 2326fc0..732ebf0 100755 --- a/settings-daemon/translations/fi_FI.ts +++ b/settings-daemon/translations/fi_FI.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/fr_FR.ts b/settings-daemon/translations/fr_FR.ts index 13c5563..2179777 100755 --- a/settings-daemon/translations/fr_FR.ts +++ b/settings-daemon/translations/fr_FR.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/he_IL.ts b/settings-daemon/translations/he_IL.ts index 63b02ec..2e86ae7 100755 --- a/settings-daemon/translations/he_IL.ts +++ b/settings-daemon/translations/he_IL.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/hi_IN.ts b/settings-daemon/translations/hi_IN.ts index 923f158..190465e 100755 --- a/settings-daemon/translations/hi_IN.ts +++ b/settings-daemon/translations/hi_IN.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/hu_HU.ts b/settings-daemon/translations/hu_HU.ts index ea5a531..f0e5add 100755 --- a/settings-daemon/translations/hu_HU.ts +++ b/settings-daemon/translations/hu_HU.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/id_ID.ts b/settings-daemon/translations/id_ID.ts index 0e2ee51..32d0674 100755 --- a/settings-daemon/translations/id_ID.ts +++ b/settings-daemon/translations/id_ID.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ie.ts b/settings-daemon/translations/ie.ts index f07ddfd..9634283 100755 --- a/settings-daemon/translations/ie.ts +++ b/settings-daemon/translations/ie.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/it_IT.ts b/settings-daemon/translations/it_IT.ts index 513b734..3cbd9fc 100755 --- a/settings-daemon/translations/it_IT.ts +++ b/settings-daemon/translations/it_IT.ts @@ -44,6 +44,22 @@ %1 fa + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ja_JP.ts b/settings-daemon/translations/ja_JP.ts index ba5f0a7..e37d32e 100755 --- a/settings-daemon/translations/ja_JP.ts +++ b/settings-daemon/translations/ja_JP.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/lt_LT.ts b/settings-daemon/translations/lt_LT.ts index 8578f08..606831c 100755 --- a/settings-daemon/translations/lt_LT.ts +++ b/settings-daemon/translations/lt_LT.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/lv_LV.ts b/settings-daemon/translations/lv_LV.ts index 8e26001..2015c17 100755 --- a/settings-daemon/translations/lv_LV.ts +++ b/settings-daemon/translations/lv_LV.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/mg.ts b/settings-daemon/translations/mg.ts index cdc0e69..c8a95dd 100755 --- a/settings-daemon/translations/mg.ts +++ b/settings-daemon/translations/mg.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ml_IN.ts b/settings-daemon/translations/ml_IN.ts index 515ebf3..477b13c 100755 --- a/settings-daemon/translations/ml_IN.ts +++ b/settings-daemon/translations/ml_IN.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/nb_NO.ts b/settings-daemon/translations/nb_NO.ts index 6589b27..c7e0a80 100755 --- a/settings-daemon/translations/nb_NO.ts +++ b/settings-daemon/translations/nb_NO.ts @@ -68,6 +68,22 @@ siden + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ne_NP.ts b/settings-daemon/translations/ne_NP.ts index aaa21f6..52b56da 100755 --- a/settings-daemon/translations/ne_NP.ts +++ b/settings-daemon/translations/ne_NP.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/pl_PL.ts b/settings-daemon/translations/pl_PL.ts index 1a4771a..71872ab 100755 --- a/settings-daemon/translations/pl_PL.ts +++ b/settings-daemon/translations/pl_PL.ts @@ -69,6 +69,22 @@ temu + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/pt_BR.ts b/settings-daemon/translations/pt_BR.ts index 3dd8379..ebabe98 100755 --- a/settings-daemon/translations/pt_BR.ts +++ b/settings-daemon/translations/pt_BR.ts @@ -44,6 +44,22 @@ %1 atrás + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/pt_PT.ts b/settings-daemon/translations/pt_PT.ts index b94e27a..5803e2e 100755 --- a/settings-daemon/translations/pt_PT.ts +++ b/settings-daemon/translations/pt_PT.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ro_RO.ts b/settings-daemon/translations/ro_RO.ts index 51d28ff..de7089b 100755 --- a/settings-daemon/translations/ro_RO.ts +++ b/settings-daemon/translations/ro_RO.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ru_RU.ts b/settings-daemon/translations/ru_RU.ts index 5d99c10..e8d40ed 100755 --- a/settings-daemon/translations/ru_RU.ts +++ b/settings-daemon/translations/ru_RU.ts @@ -68,6 +68,22 @@ назад + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/si_LK.ts b/settings-daemon/translations/si_LK.ts index f7c5f22..5fa9bb0 100755 --- a/settings-daemon/translations/si_LK.ts +++ b/settings-daemon/translations/si_LK.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/sk_SK.ts b/settings-daemon/translations/sk_SK.ts index 9ede441..c00eda2 100755 --- a/settings-daemon/translations/sk_SK.ts +++ b/settings-daemon/translations/sk_SK.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/so.ts b/settings-daemon/translations/so.ts index 9485c11..90260d8 100755 --- a/settings-daemon/translations/so.ts +++ b/settings-daemon/translations/so.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/sr_RS.ts b/settings-daemon/translations/sr_RS.ts index 87fe5b1..da992ba 100755 --- a/settings-daemon/translations/sr_RS.ts +++ b/settings-daemon/translations/sr_RS.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/sv_SE.ts b/settings-daemon/translations/sv_SE.ts index 514313e..a488498 100755 --- a/settings-daemon/translations/sv_SE.ts +++ b/settings-daemon/translations/sv_SE.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/sw.ts b/settings-daemon/translations/sw.ts index c464787..732caed 100755 --- a/settings-daemon/translations/sw.ts +++ b/settings-daemon/translations/sw.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/ta_IN.ts b/settings-daemon/translations/ta_IN.ts index dbdc313..fb54c85 100755 --- a/settings-daemon/translations/ta_IN.ts +++ b/settings-daemon/translations/ta_IN.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/tr_TR.ts b/settings-daemon/translations/tr_TR.ts index 2a8ef14..35153d7 100755 --- a/settings-daemon/translations/tr_TR.ts +++ b/settings-daemon/translations/tr_TR.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/uk_UA.ts b/settings-daemon/translations/uk_UA.ts index c6195f9..d9e1d47 100755 --- a/settings-daemon/translations/uk_UA.ts +++ b/settings-daemon/translations/uk_UA.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/uz_UZ.ts b/settings-daemon/translations/uz_UZ.ts index a77da4e..ee29c98 100755 --- a/settings-daemon/translations/uz_UZ.ts +++ b/settings-daemon/translations/uz_UZ.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/vi_VN.ts b/settings-daemon/translations/vi_VN.ts index e6f051c..0f0f591 100755 --- a/settings-daemon/translations/vi_VN.ts +++ b/settings-daemon/translations/vi_VN.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/settings-daemon/translations/zh_CN.ts b/settings-daemon/translations/zh_CN.ts index b51a26e..99d9f42 100755 --- a/settings-daemon/translations/zh_CN.ts +++ b/settings-daemon/translations/zh_CN.ts @@ -47,7 +47,7 @@ Language - + The system language has been changed, please log out and log in 系统语言已更改,请注销并登录 @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect 屏幕缩放需要重新登录才能生效 diff --git a/settings-daemon/translations/zh_TW.ts b/settings-daemon/translations/zh_TW.ts index deb6c65..291c803 100755 --- a/settings-daemon/translations/zh_TW.ts +++ b/settings-daemon/translations/zh_TW.ts @@ -68,6 +68,22 @@ vergangen + + Language + + + The system language has been changed, please log out and log in + + + + + ThemeManager + + + Screen scaling needs to be re-login to take effect + + + UPowerDevice diff --git a/shutdown-ui/translations/ar_AA.ts b/shutdown-ui/translations/ar_AA.ts index eedf65a..ff49016 100755 --- a/shutdown-ui/translations/ar_AA.ts +++ b/shutdown-ui/translations/ar_AA.ts @@ -4,27 +4,27 @@ main - + Shutdown إيقاف التشغيل - + Reboot إعادة التشغيل - + Logout تسجيل الخروج - + Lock screen قفل الشاشة - + Suspend سكون diff --git a/shutdown-ui/translations/be_BY.ts b/shutdown-ui/translations/be_BY.ts index 6969adb..7311893 100755 --- a/shutdown-ui/translations/be_BY.ts +++ b/shutdown-ui/translations/be_BY.ts @@ -4,27 +4,29 @@ main - + Shutdown Выключэнне - + Reboot Перазагрузка - + Logout Выйсці з сеансу - + + Lock screen + + + + Suspend Спячы рэжым - - - diff --git a/shutdown-ui/translations/be_Latn.ts b/shutdown-ui/translations/be_Latn.ts index 2893012..fd87ddb 100755 --- a/shutdown-ui/translations/be_Latn.ts +++ b/shutdown-ui/translations/be_Latn.ts @@ -4,22 +4,27 @@ main - + Shutdown Vykliučennie - + Reboot Pierazahruzka - + Logout Vyjsci z sieansu - + + Lock screen + + + + Suspend Spiačy režym diff --git a/shutdown-ui/translations/bg_BG.ts b/shutdown-ui/translations/bg_BG.ts index 0e037ef..e97ff22 100755 --- a/shutdown-ui/translations/bg_BG.ts +++ b/shutdown-ui/translations/bg_BG.ts @@ -4,27 +4,27 @@ main - + Shutdown Изкючване - + Reboot Рестартиране - + Logout Излизане - + Lock screen Заключен екран - + Suspend Приспиване diff --git a/shutdown-ui/translations/bs_BA.ts b/shutdown-ui/translations/bs_BA.ts index e272494..a41bfcd 100755 --- a/shutdown-ui/translations/bs_BA.ts +++ b/shutdown-ui/translations/bs_BA.ts @@ -4,22 +4,27 @@ main - + Shutdown Ugasiti - + Reboot Ponovno pokreni - + Logout Odjavi se - + + Lock screen + + + + Suspend Suspendiraj diff --git a/shutdown-ui/translations/cs_CZ.ts b/shutdown-ui/translations/cs_CZ.ts index ddf6a93..6205384 100755 --- a/shutdown-ui/translations/cs_CZ.ts +++ b/shutdown-ui/translations/cs_CZ.ts @@ -4,27 +4,29 @@ main - + Shutdown Vypnout - + Reboot Restartovat - + Logout Odhlásit se - + + Lock screen + + + + Suspend Uspat - - - diff --git a/shutdown-ui/translations/da_DK.ts b/shutdown-ui/translations/da_DK.ts index d476e06..93b35b6 100755 --- a/shutdown-ui/translations/da_DK.ts +++ b/shutdown-ui/translations/da_DK.ts @@ -4,27 +4,29 @@ main - + Shutdown Luk ned - + Reboot Genstart - + Logout Log ud - + + Lock screen + + + + Suspend Suspender - - - diff --git a/shutdown-ui/translations/de_DE.ts b/shutdown-ui/translations/de_DE.ts index 3022d1b..84404c2 100755 --- a/shutdown-ui/translations/de_DE.ts +++ b/shutdown-ui/translations/de_DE.ts @@ -4,27 +4,29 @@ main - + Shutdown Herunterfahren - + Reboot Neustart - + Logout Abmelden - + + Lock screen + + + + Suspend Bereitschaft - - - diff --git a/shutdown-ui/translations/en_US.ts b/shutdown-ui/translations/en_US.ts index 82f8da6..48a8652 100755 --- a/shutdown-ui/translations/en_US.ts +++ b/shutdown-ui/translations/en_US.ts @@ -4,27 +4,27 @@ main - + Shutdown Shutdown - + Reboot Reboot - + Logout Log out - + Lock screen Lock screen - + Suspend Suspend diff --git a/shutdown-ui/translations/eo_XX.ts b/shutdown-ui/translations/eo_XX.ts index fadb4af..5007592 100755 --- a/shutdown-ui/translations/eo_XX.ts +++ b/shutdown-ui/translations/eo_XX.ts @@ -4,22 +4,27 @@ main - + Shutdown - + Reboot - + Logout - + + Lock screen + + + + Suspend diff --git a/shutdown-ui/translations/es_ES.ts b/shutdown-ui/translations/es_ES.ts index 6a037fa..dff1ca1 100755 --- a/shutdown-ui/translations/es_ES.ts +++ b/shutdown-ui/translations/es_ES.ts @@ -4,27 +4,29 @@ main - + Shutdown Apagar - + Reboot Reiniciar - + Logout Cerrar sesión - + + Lock screen + + + + Suspend Suspender - - - diff --git a/shutdown-ui/translations/es_MX.ts b/shutdown-ui/translations/es_MX.ts index 81ef26f..1feb7cf 100755 --- a/shutdown-ui/translations/es_MX.ts +++ b/shutdown-ui/translations/es_MX.ts @@ -4,22 +4,27 @@ main - + Shutdown Apagar - + Reboot Reiniciar - + Logout Cerrar Sesión - + + Lock screen + + + + Suspend Suspender diff --git a/shutdown-ui/translations/fa_IR.ts b/shutdown-ui/translations/fa_IR.ts index 99a2fc1..0c25d67 100755 --- a/shutdown-ui/translations/fa_IR.ts +++ b/shutdown-ui/translations/fa_IR.ts @@ -4,27 +4,29 @@ main - + Shutdown خاموش - + Reboot راه‌اندازی مجدد - + Logout خروج - + + Lock screen + + + + Suspend تعلیق - - - diff --git a/shutdown-ui/translations/fi_FI.ts b/shutdown-ui/translations/fi_FI.ts index 8b0b4f5..e90b2f0 100755 --- a/shutdown-ui/translations/fi_FI.ts +++ b/shutdown-ui/translations/fi_FI.ts @@ -4,22 +4,27 @@ main - + Shutdown Sammuta - + Reboot Käynnistä uudelleen - + Logout Kirjaudu ulos - + + Lock screen + + + + Suspend Keskeytä diff --git a/shutdown-ui/translations/fr_FR.ts b/shutdown-ui/translations/fr_FR.ts index c953ece..f534d3c 100755 --- a/shutdown-ui/translations/fr_FR.ts +++ b/shutdown-ui/translations/fr_FR.ts @@ -4,27 +4,29 @@ main - + Shutdown Éteindre - + Reboot Redémarrer - + Logout Se déconnecter - + + Lock screen + + + + Suspend Suspendre - - - diff --git a/shutdown-ui/translations/he_IL.ts b/shutdown-ui/translations/he_IL.ts index 7f4e5a9..625c6d1 100755 --- a/shutdown-ui/translations/he_IL.ts +++ b/shutdown-ui/translations/he_IL.ts @@ -4,27 +4,27 @@ main - + Shutdown כיבוי - + Reboot הפעל מחדש - + Logout התנתק - + Lock screen מסך נעילה - + Suspend מצב שינה diff --git a/shutdown-ui/translations/hi_IN.ts b/shutdown-ui/translations/hi_IN.ts index b295392..9832a3a 100755 --- a/shutdown-ui/translations/hi_IN.ts +++ b/shutdown-ui/translations/hi_IN.ts @@ -4,22 +4,27 @@ main - + Shutdown शट डाउन - + Reboot - + Logout - + + Lock screen + + + + Suspend diff --git a/shutdown-ui/translations/hu_HU.ts b/shutdown-ui/translations/hu_HU.ts index e6d089d..1331a6a 100755 --- a/shutdown-ui/translations/hu_HU.ts +++ b/shutdown-ui/translations/hu_HU.ts @@ -4,27 +4,29 @@ main - + Shutdown Leállítás - + Reboot Újraindítás - + Logout Kijelentkezés - + + Lock screen + + + + Suspend Alvó állapot - - - diff --git a/shutdown-ui/translations/id_ID.ts b/shutdown-ui/translations/id_ID.ts index 962bedf..f557dc5 100755 --- a/shutdown-ui/translations/id_ID.ts +++ b/shutdown-ui/translations/id_ID.ts @@ -4,27 +4,29 @@ main - + Shutdown Matikan - + Reboot Boot Ulang - + Logout Keluar - + + Lock screen + + + + Suspend Tangguhkan - - - diff --git a/shutdown-ui/translations/ie.ts b/shutdown-ui/translations/ie.ts index 576f064..f454155 100755 --- a/shutdown-ui/translations/ie.ts +++ b/shutdown-ui/translations/ie.ts @@ -4,22 +4,27 @@ main - + Shutdown Extinter - + Reboot Reiniciar - + Logout Cluder li session - + + Lock screen + + + + Suspend Suspender diff --git a/shutdown-ui/translations/it_IT.ts b/shutdown-ui/translations/it_IT.ts index 8e20bd3..989a126 100755 --- a/shutdown-ui/translations/it_IT.ts +++ b/shutdown-ui/translations/it_IT.ts @@ -4,27 +4,29 @@ main - + Shutdown Spegni - + Reboot Riavvia - + Logout Disconnettiti - + + Lock screen + + + + Suspend Sospendi - - - diff --git a/shutdown-ui/translations/ja_JP.ts b/shutdown-ui/translations/ja_JP.ts index f6d54f4..eb12e89 100755 --- a/shutdown-ui/translations/ja_JP.ts +++ b/shutdown-ui/translations/ja_JP.ts @@ -4,27 +4,27 @@ main - + Shutdown シャットダウン - + Reboot 再起動 - + Logout ログアウト - + Lock screen 画面をロック - + Suspend サスペンド diff --git a/shutdown-ui/translations/lt_LT.ts b/shutdown-ui/translations/lt_LT.ts index a67bc37..addec85 100755 --- a/shutdown-ui/translations/lt_LT.ts +++ b/shutdown-ui/translations/lt_LT.ts @@ -4,27 +4,27 @@ main - + Shutdown Išjungti - + Reboot Paleisti iš naujo - + Logout Atsijungti - + Lock screen Užrakinti ekraną - + Suspend Pristabdyti diff --git a/shutdown-ui/translations/lv_LV.ts b/shutdown-ui/translations/lv_LV.ts index 23e867b..54d3d95 100755 --- a/shutdown-ui/translations/lv_LV.ts +++ b/shutdown-ui/translations/lv_LV.ts @@ -4,27 +4,27 @@ main - + Shutdown Izslēgt - + Reboot Restartēt - + Logout Izrakstīties - + Lock screen Bloķēt ekrānu - + Suspend Aizmidzināt diff --git a/shutdown-ui/translations/mg.ts b/shutdown-ui/translations/mg.ts index 2c60a3c..7a4d8b4 100755 --- a/shutdown-ui/translations/mg.ts +++ b/shutdown-ui/translations/mg.ts @@ -4,27 +4,27 @@ main - + Shutdown Vonoina - + Reboot Averina alefa - + Logout Mivoaka - + Lock screen Hidiana ny ecran - + Suspend Ajanona diff --git a/shutdown-ui/translations/ml_IN.ts b/shutdown-ui/translations/ml_IN.ts index caa4440..799fa8e 100755 --- a/shutdown-ui/translations/ml_IN.ts +++ b/shutdown-ui/translations/ml_IN.ts @@ -4,27 +4,27 @@ main - + Shutdown - + Reboot - + Logout - + Lock screen - + Suspend diff --git a/shutdown-ui/translations/nb_NO.ts b/shutdown-ui/translations/nb_NO.ts index d49ae15..1b1d8a4 100755 --- a/shutdown-ui/translations/nb_NO.ts +++ b/shutdown-ui/translations/nb_NO.ts @@ -4,22 +4,27 @@ main - + Shutdown Slå av - + Reboot Omstart - + Logout Logg ut - + + Lock screen + + + + Suspend Hvilemodus diff --git a/shutdown-ui/translations/ne_NP.ts b/shutdown-ui/translations/ne_NP.ts index 11d6cc5..264fb0e 100755 --- a/shutdown-ui/translations/ne_NP.ts +++ b/shutdown-ui/translations/ne_NP.ts @@ -4,22 +4,27 @@ main - + Shutdown पावर अफ - + Reboot पुन: सुचारु - + Logout लग आउट - + + Lock screen + + + + Suspend सस्पेन्ड diff --git a/shutdown-ui/translations/pl_PL.ts b/shutdown-ui/translations/pl_PL.ts index 7419fe1..b543b0c 100755 --- a/shutdown-ui/translations/pl_PL.ts +++ b/shutdown-ui/translations/pl_PL.ts @@ -4,27 +4,29 @@ main - + Shutdown Wyłącz - + Reboot Uruchom ponownie - + Logout Wyloguj się - + + Lock screen + + + + Suspend Uśpij - - - diff --git a/shutdown-ui/translations/pt_BR.ts b/shutdown-ui/translations/pt_BR.ts index 942fa70..63d3250 100755 --- a/shutdown-ui/translations/pt_BR.ts +++ b/shutdown-ui/translations/pt_BR.ts @@ -4,27 +4,29 @@ main - + Shutdown Desligar - + Reboot Reiniciar - + Logout Encerrar sessão - + + Lock screen + + + + Suspend Suspender - - - diff --git a/shutdown-ui/translations/pt_PT.ts b/shutdown-ui/translations/pt_PT.ts index d6412f7..c57d42f 100755 --- a/shutdown-ui/translations/pt_PT.ts +++ b/shutdown-ui/translations/pt_PT.ts @@ -4,27 +4,29 @@ main - + Shutdown Desligar - + Reboot Reiniciar - + Logout Terminar sessão - + + Lock screen + + + + Suspend Suspender - - - diff --git a/shutdown-ui/translations/ro_RO.ts b/shutdown-ui/translations/ro_RO.ts index d2705c5..c3fbf46 100755 --- a/shutdown-ui/translations/ro_RO.ts +++ b/shutdown-ui/translations/ro_RO.ts @@ -4,27 +4,27 @@ main - + Shutdown - + Reboot - + Logout - + Lock screen - + Suspend diff --git a/shutdown-ui/translations/ru_RU.ts b/shutdown-ui/translations/ru_RU.ts index e78924e..94b3cbf 100755 --- a/shutdown-ui/translations/ru_RU.ts +++ b/shutdown-ui/translations/ru_RU.ts @@ -4,27 +4,29 @@ main - + Shutdown Выключение - + Reboot Перезагрузка - + Logout Выйти - + + Lock screen + + + + Suspend Спящий режим - - - diff --git a/shutdown-ui/translations/si_LK.ts b/shutdown-ui/translations/si_LK.ts index 81a32df..5530cc0 100755 --- a/shutdown-ui/translations/si_LK.ts +++ b/shutdown-ui/translations/si_LK.ts @@ -4,22 +4,27 @@ main - + Shutdown - + Reboot - + Logout නික්මෙන්න - + + Lock screen + + + + Suspend අත්හිටුවන්න diff --git a/shutdown-ui/translations/sk_SK.ts b/shutdown-ui/translations/sk_SK.ts index c5b76b6..45f28ab 100755 --- a/shutdown-ui/translations/sk_SK.ts +++ b/shutdown-ui/translations/sk_SK.ts @@ -4,27 +4,29 @@ main - + Shutdown Vypnúť - + Reboot Reštartovať - + Logout Odhlásiť sa - + + Lock screen + + + + Suspend Uspať - - - diff --git a/shutdown-ui/translations/so.ts b/shutdown-ui/translations/so.ts index f201c01..6ac3fd7 100755 --- a/shutdown-ui/translations/so.ts +++ b/shutdown-ui/translations/so.ts @@ -4,22 +4,27 @@ main - + Shutdown Dami - + Reboot Soo daar - + Logout Ka bax - + + Lock screen + + + + Suspend Haki diff --git a/shutdown-ui/translations/sr_RS.ts b/shutdown-ui/translations/sr_RS.ts index 48f6d12..26bda62 100755 --- a/shutdown-ui/translations/sr_RS.ts +++ b/shutdown-ui/translations/sr_RS.ts @@ -4,27 +4,27 @@ main - + Shutdown Isključi - + Reboot Pokreni ponovo - + Logout Odjavi se - + Lock screen Zaključaj ekran - + Suspend Suspenduj diff --git a/shutdown-ui/translations/sv_SE.ts b/shutdown-ui/translations/sv_SE.ts index 8c8c40e..26eaad0 100755 --- a/shutdown-ui/translations/sv_SE.ts +++ b/shutdown-ui/translations/sv_SE.ts @@ -4,27 +4,29 @@ main - + Shutdown Stäng av - + Reboot Starta om - + Logout Logga ut - + + Lock screen + + + + Suspend Viloläge - - - diff --git a/shutdown-ui/translations/sw.ts b/shutdown-ui/translations/sw.ts index 5179d6a..63a924a 100755 --- a/shutdown-ui/translations/sw.ts +++ b/shutdown-ui/translations/sw.ts @@ -4,27 +4,27 @@ main - + Shutdown Zima - + Reboot Wakisha tena - + Logout Toka Nje - + Lock screen Funga skrini - + Suspend Simamisha diff --git a/shutdown-ui/translations/ta_IN.ts b/shutdown-ui/translations/ta_IN.ts index e700bb7..c25e0cd 100755 --- a/shutdown-ui/translations/ta_IN.ts +++ b/shutdown-ui/translations/ta_IN.ts @@ -4,22 +4,27 @@ main - + Shutdown நிறுத்தவும் - + Reboot மீண்டும் துவக்கவும் - + Logout வெளியேறு - + + Lock screen + + + + Suspend இடைநிறுத்து diff --git a/shutdown-ui/translations/tr_TR.ts b/shutdown-ui/translations/tr_TR.ts index bd9fdbd..bd49f65 100755 --- a/shutdown-ui/translations/tr_TR.ts +++ b/shutdown-ui/translations/tr_TR.ts @@ -4,27 +4,29 @@ main - + Shutdown Kapat - + Reboot Yeniden başlat - + Logout Çıkış yap - + + Lock screen + + + + Suspend Uyku - - - diff --git a/shutdown-ui/translations/uk_UA.ts b/shutdown-ui/translations/uk_UA.ts index 5056601..5513f9d 100755 --- a/shutdown-ui/translations/uk_UA.ts +++ b/shutdown-ui/translations/uk_UA.ts @@ -4,27 +4,29 @@ main - + Shutdown Вимкнути - + Reboot Перезавантаження - + Logout Вийти з сеансу - + + Lock screen + + + + Suspend Сон - - - diff --git a/shutdown-ui/translations/uz_UZ.ts b/shutdown-ui/translations/uz_UZ.ts index 3444735..b38b1fd 100755 --- a/shutdown-ui/translations/uz_UZ.ts +++ b/shutdown-ui/translations/uz_UZ.ts @@ -4,22 +4,27 @@ main - + Shutdown Ishni yakunlash - + Reboot Qayta yuklash - + Logout Chiqish - + + Lock screen + + + + Suspend Uhlatish diff --git a/shutdown-ui/translations/vi_VN.ts b/shutdown-ui/translations/vi_VN.ts index 0f4cf34..68ad533 100755 --- a/shutdown-ui/translations/vi_VN.ts +++ b/shutdown-ui/translations/vi_VN.ts @@ -4,27 +4,27 @@ main - + Shutdown Tắt nguồn - + Reboot Khởi động lại - + Logout Đăng xuất - + Lock screen Khóa màn hình - + Suspend Ngủ đông diff --git a/shutdown-ui/translations/zh_CN.ts b/shutdown-ui/translations/zh_CN.ts index e5e2a34..60ffc1f 100755 --- a/shutdown-ui/translations/zh_CN.ts +++ b/shutdown-ui/translations/zh_CN.ts @@ -4,27 +4,27 @@ main - + Shutdown 关机 - + Reboot 重新启动 - + Logout 注销 - + Lock screen 锁屏 - + Suspend 休眠 diff --git a/shutdown-ui/translations/zh_TW.ts b/shutdown-ui/translations/zh_TW.ts index 6c3a4b8..bd80969 100755 --- a/shutdown-ui/translations/zh_TW.ts +++ b/shutdown-ui/translations/zh_TW.ts @@ -4,27 +4,29 @@ main - + Shutdown 關閉電腦 - + Reboot 重新啓動 - + Logout 登出 - + + Lock screen + + + + Suspend 掛起 - - - From 5e8e767d56d65f5f4cc93bad82222c3d5e9de175 Mon Sep 17 00:00:00 2001 From: Elysia Date: Sun, 29 Sep 2024 21:13:27 +0800 Subject: [PATCH 17/24] Remove startlingmo --- session/application.cpp | 8 + session/daemon-helper.cpp | 107 +++- session/daemon-helper.h | 231 ++++++-- {startlingmo => session}/job_private.hpp | 0 session/main.cpp | 3 - session/processmanager.cpp | 409 ++++++++------ session/processmanager.h | 59 +- startlingmo/CMakeLists.txt | 76 --- startlingmo/UpdateLaunchEnvironment.cpp | 151 ------ startlingmo/UpdateLaunchEnvironment.hpp | 52 -- startlingmo/cmake/FindKWinPath.cmake | 29 - startlingmo/config-startlingmo.h.cmake | 9 - startlingmo/daemon-helper.cpp | 138 ----- startlingmo/daemon-helper.h | 203 ------- startlingmo/lingmo-session/CMakeLists.txt | 46 -- startlingmo/lingmo-session/application.cpp | 277 ---------- startlingmo/lingmo-session/application.h | 94 ---- .../lingmo-session/com.lingmo.Session.xml | 36 -- startlingmo/lingmo-session/main.cpp | 45 -- .../lingmo-session/networkproxymanager.cpp | 97 ---- .../lingmo-session/networkproxymanager.h | 51 -- .../lingmo-session/powermanager/power.cpp | 80 --- .../lingmo-session/powermanager/power.h | 118 ---- .../powermanager/powerproviders.cpp | 448 --------------- .../powermanager/powerproviders.h | 109 ---- startlingmo/lingmo-session/process.cpp | 30 - startlingmo/lingmo-session/process.h | 34 -- startlingmo/lingmo-session/processmanager.cpp | 335 ------------ startlingmo/lingmo-session/processmanager.h | 140 ----- startlingmo/lingmo-sourceenv.sh | 7 - startlingmo/lingmo-wayland-session.desktop | 6 - startlingmo/lingmo-xorg-session.desktop | 7 - startlingmo/signalhandler.cpp | 63 --- startlingmo/signalhandler.h | 38 -- startlingmo/startlingmo-wayland.cpp | 95 ---- startlingmo/startlingmo-x11.cpp | 62 --- startlingmo/startlingmo.cpp | 513 ------------------ startlingmo/startlingmo.hpp | 66 --- startlingmo/wayland_wrapper/CMakeLists.txt | 23 - startlingmo/wayland_wrapper/kwin_wrapper.cpp | 179 ------ .../wayland_wrapper/lib/CMakeLists.txt | 20 - .../wayland_wrapper/lib/xauthority.cpp | 77 --- startlingmo/wayland_wrapper/lib/xauthority.h | 14 - .../wayland_wrapper/lib/xwaylandsocket.cpp | 250 --------- .../wayland_wrapper/lib/xwaylandsocket.h | 40 -- startlingmo/wayland_wrapper/wl-socket.c | 172 ------ startlingmo/wayland_wrapper/wl-socket.h | 39 -- 47 files changed, 609 insertions(+), 4477 deletions(-) rename {startlingmo => session}/job_private.hpp (100%) delete mode 100644 startlingmo/CMakeLists.txt delete mode 100644 startlingmo/UpdateLaunchEnvironment.cpp delete mode 100644 startlingmo/UpdateLaunchEnvironment.hpp delete mode 100644 startlingmo/cmake/FindKWinPath.cmake delete mode 100644 startlingmo/config-startlingmo.h.cmake delete mode 100644 startlingmo/daemon-helper.cpp delete mode 100644 startlingmo/daemon-helper.h delete mode 100755 startlingmo/lingmo-session/CMakeLists.txt delete mode 100755 startlingmo/lingmo-session/application.cpp delete mode 100755 startlingmo/lingmo-session/application.h delete mode 100755 startlingmo/lingmo-session/com.lingmo.Session.xml delete mode 100755 startlingmo/lingmo-session/main.cpp delete mode 100755 startlingmo/lingmo-session/networkproxymanager.cpp delete mode 100755 startlingmo/lingmo-session/networkproxymanager.h delete mode 100755 startlingmo/lingmo-session/powermanager/power.cpp delete mode 100755 startlingmo/lingmo-session/powermanager/power.h delete mode 100755 startlingmo/lingmo-session/powermanager/powerproviders.cpp delete mode 100755 startlingmo/lingmo-session/powermanager/powerproviders.h delete mode 100755 startlingmo/lingmo-session/process.cpp delete mode 100755 startlingmo/lingmo-session/process.h delete mode 100755 startlingmo/lingmo-session/processmanager.cpp delete mode 100755 startlingmo/lingmo-session/processmanager.h delete mode 100644 startlingmo/lingmo-sourceenv.sh delete mode 100755 startlingmo/lingmo-wayland-session.desktop delete mode 100755 startlingmo/lingmo-xorg-session.desktop delete mode 100644 startlingmo/signalhandler.cpp delete mode 100644 startlingmo/signalhandler.h delete mode 100644 startlingmo/startlingmo-wayland.cpp delete mode 100644 startlingmo/startlingmo-x11.cpp delete mode 100644 startlingmo/startlingmo.cpp delete mode 100644 startlingmo/startlingmo.hpp delete mode 100644 startlingmo/wayland_wrapper/CMakeLists.txt delete mode 100644 startlingmo/wayland_wrapper/kwin_wrapper.cpp delete mode 100644 startlingmo/wayland_wrapper/lib/CMakeLists.txt delete mode 100644 startlingmo/wayland_wrapper/lib/xauthority.cpp delete mode 100644 startlingmo/wayland_wrapper/lib/xauthority.h delete mode 100644 startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp delete mode 100644 startlingmo/wayland_wrapper/lib/xwaylandsocket.h delete mode 100644 startlingmo/wayland_wrapper/wl-socket.c delete mode 100644 startlingmo/wayland_wrapper/wl-socket.h diff --git a/session/application.cpp b/session/application.cpp index 32786e9..aebdc43 100755 --- a/session/application.cpp +++ b/session/application.cpp @@ -127,6 +127,14 @@ Application::Application(int &argc, char **argv) qunsetenv("XCURSOR_SIZE"); qunsetenv("SESSION_MANAGER"); + if (m_wayland) { + qputenv("XDG_SESSION_TYPE", "wayland"); + qputenv("QT_QPA_PLATFORM", "wayland"); + } else { + // force xcb QPA plugin as session manager server is very X11 specific. + qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); + } + m_networkProxyManager->update(); QTimer::singleShot(50, this, &Application::updateUserDirs); diff --git a/session/daemon-helper.cpp b/session/daemon-helper.cpp index 901b510..be1b712 100644 --- a/session/daemon-helper.cpp +++ b/session/daemon-helper.cpp @@ -3,18 +3,21 @@ * @author Elysia **/ #include "daemon-helper.h" +#include "job_private.hpp" #include -#include -#include #include #include -#include #include +#include +#include +#include namespace LINGMO_SESSION { -Daemon::Daemon(const QList> &processList, bool _enableAutoStart, QObject *parent) - : QObject(parent), m_processList(processList), m_enableAutoRestart(_enableAutoStart) { +Daemon::Daemon(const QList> &processList, + bool _enableAutoStart, QObject *parent) + : QObject(parent), m_processList(processList), + m_enableAutoRestart(_enableAutoStart) { for (const auto &processInfo : m_processList) { startProcess(processInfo); } @@ -30,7 +33,7 @@ void Daemon::onProcessError(QProcess::ProcessError error) { qDebug() << "Process error:" << program << "Error:" << error; for (const auto &processInfo : m_processList) { - if (processInfo.first == program) { + if (std::get<0>(processInfo) == program) { qDebug() << "Restarting process due to error:" << program; QTimer::singleShot(1, this, [this, processInfo]() { startProcess(processInfo); @@ -40,18 +43,98 @@ void Daemon::onProcessError(QProcess::ProcessError error) { } } -void Daemon::startProcess(const QPair &processInfo) { +void Daemon::startProcess(const std::tuple &processInfo) { const QPointer process = new QProcess(this); if (this->m_enableAutoRestart) - connect(process, &QProcess::errorOccurred, - this, &Daemon::onProcessError); + connect(process, &QProcess::errorOccurred, this, &Daemon::onProcessError); + + process->setProcessEnvironment(std::get<2>(processInfo)); - process->start(processInfo.first, processInfo.second); + process->start(std::get<0>(processInfo), std::get<1>(processInfo)); if (process->waitForStarted()) { - qDebug() << "Process started:" << processInfo.first << "PID:" << process->processId(); + qDebug() << "Process started:" << std::get<0>(processInfo) + << "PID:" << process->processId(); } else { - qDebug() << "Failed to start process:" << processInfo.first << process->errorString(); + qDebug() << "Failed to start process:" << std::get<0>(processInfo) + << process->errorString(); + } +} + +JobPrivate::JobPrivate() {} + +JobPrivate::~JobPrivate() {} + +Job::Job(QObject *parent) : QObject(parent), d_ptr(new JobPrivate) {} + +Job::~Job() { + if (!d_ptr->isFinished) { + d_ptr->isFinished = true; + Q_EMIT finished(this); + } +} + +void Job::emitResult() { + if (!d_func()->isFinished) { + finishJob(true); + } +} +void Job::finishJob(bool emitResult) { + Q_D(Job); + Q_ASSERT(!d->isFinished); + d->isFinished = true; + + if (d->eventLoop) { + d->eventLoop->quit(); + } + + Q_EMIT finished(this); + + if (emitResult) { + Q_EMIT result(this); + } + + if (isAutoDelete()) { + deleteLater(); + } +} + +bool Job::isAutoDelete() const { + Q_D(const Job); + return d->isAutoDelete; +} + +void Job::setAutoDelete(bool autodelete) { + Q_D(Job); + d->isAutoDelete = autodelete; +} + +bool Job::exec() { + Q_D(Job); + // Usually this job would delete itself, via deleteLater() just after + // emitting result() (unless configured otherwise). Since we use an event + // loop below, that event loop will process the deletion event and we'll + // have been deleted when exec() returns. This crashes, so temporarily + // suspend autodeletion and manually do it afterwards. + const bool wasAutoDelete = isAutoDelete(); + setAutoDelete(false); + + Q_ASSERT(!d->eventLoop); + + QEventLoop loop(this); + d->eventLoop = &loop; + + start(); + + if (!d->isFinished) { + d->m_startedWithExec = true; + d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); + } + d->eventLoop = nullptr; + + if (wasAutoDelete) { + deleteLater(); } + return (d->error == NoError); } } // namespace LINGMO_SESSION diff --git a/session/daemon-helper.h b/session/daemon-helper.h index 3715569..e9e9721 100644 --- a/session/daemon-helper.h +++ b/session/daemon-helper.h @@ -1,49 +1,204 @@ #ifndef __DAEMON_HELPER_ #define __DAEMON_HELPER_ +#include +#include +#include #include #include -#include -#include -#include #include #include -#include +#include +#include + +#include namespace LINGMO_SESSION { - class Daemon : public QObject { - Q_OBJECT - - public: - /** - * Start all the passed process using daemon. - * @param processList Process list to start - * @param parent - */ - explicit Daemon(const QList>& processList, bool _enableAutoStart = true, QObject* parent = nullptr); - - public slots: - - /** - * Handle the case when the progarm has some errors (i.e. crashed) - * @param error - */ - void onProcessError(QProcess::ProcessError error); - - private: - /** - * Start a given process using daemon helper - * @brief startProcess - * @param processInfo - */ - void startProcess(const QPair& processInfo); - - QList> m_processList; - - /** - * @brief Whether to enable auto reload when process exited. - */ - bool m_enableAutoRestart; +class JobPrivate; + +class Daemon : public QObject { + Q_OBJECT + +public: + /** + * Start all the passed process using daemon. + * @param processList Process list to start + * @param parent + */ + explicit Daemon(const QList> &processList, + bool _enableAutoStart = true, QObject *parent = nullptr); + +public slots: + + /** + * Handle the case when the progarm has some errors (i.e. crashed) + * @param error + */ + void onProcessError(QProcess::ProcessError error); + +private: + /** + * Start a given process using daemon helper + * @brief startProcess + * @param processInfo + */ + void startProcess(const std::tuple &processInfo); + + QList> m_processList; + + /** + * @brief Whether to enable auto reload when process exited. + */ + bool m_enableAutoRestart; +}; + +class Job : public QObject { + Q_OBJECT +public: + Job(QObject *parent = nullptr); + + ~Job(); + + /** + * Returns whether this job automatically deletes itself once + * the job is finished. + * + * @return whether the job is deleted automatically after + * finishing. + */ + bool isAutoDelete() const; + + /** + * Sets the auto-delete property of the job. If @p autodelete is + * set to @c false the job will not delete itself once it is finished. + * + * The default for any Job is to automatically delete itself, which + * implies that the job was created on the heap (using new). + * If the job is created on the stack (which isn't the typical use-case + * for a job) then you must set auto-delete to @c false, otherwise you + * could get a crash when the job finishes and tries to delete itself. + * + * @note If you set auto-delete to @c false then you need to kill the + * job manually, ideally by calling kill(). + * + * @param autodelete set to @c false to disable automatic deletion + * of the job. + */ + void setAutoDelete(bool autodelete); + + /** + * Executes the job synchronously. + * + * This will start a nested QEventLoop internally. Nested event loop can be + * dangerous and can have unintended side effects, you should avoid calling + * exec() whenever you can and use the asynchronous interface of Job instead. + * + * Should you indeed call this method, you need to make sure that all callers + * are reentrant, so that events delivered by the inner event loop don't cause + * non-reentrant functions to be called, which usually wreaks havoc. + * + * Note that the event loop started by this method does not process user input + * events, which means your user interface will effectively be blocked. Other + * events like paint or network events are still being processed. The + * advantage of not processing user input events is that the chance of + * accidental reentrance is greatly reduced. Still you should avoid calling + * this function. + * + * @return true if the job has been executed without error, false otherwise + */ + bool exec(); + + /** + * Starts the job asynchronously. + * + * When the job is finished, result() is emitted. + * + * Warning: Never implement any synchronous workload in this method. This + * method should just trigger the job startup, not do any work itself. It is + * expected to be non-blocking. + * + * This is the method all subclasses need to implement. + * It should setup and trigger the workload of the job. It should not do any + * work itself. This includes all signals and terminating the job, e.g. by + * emitResult(). The workload, which could be another method of the + * subclass, is to be triggered using the event loop, e.g. by code like: + * \code + * void ExampleJob::start() + * { + * QTimer::singleShot(0, this, &ExampleJob::doWork); + * } + * \endcode + */ + Q_SCRIPTABLE virtual void start() = 0; + + enum { + /*** Indicates there is no error */ + NoError = 0, + /*** Indicates the job was killed */ + KilledJobError = 1, + /*** Subclasses should define error codes starting at this value */ + UserDefinedError = 100, }; -} +Q_SIGNALS: + /** + * Emitted when the job is finished, in any case. It is used to notify + * observers that the job is terminated and that progress can be hidden. + * + * This signal is guaranteed to be emitted exactly once. + * + * This is a private signal, it can't be emitted directly by subclass, use + * emitResult() instead. + * + * In general, to be notified of a job's completion, client code should + * connect to result() rather than finished(), so that kill(Quietly) is indeed + * quiet. However if you store a list of jobs and they might get killed + * silently, then you must connect to this instead of result(), to avoid + * dangling pointers in your list. + * + * @param job the job that emitted this signal + * @internal + * + * @see result + */ + void finished(Job *job); + + /** + * Emitted when the job is finished (except when killed with KJob::Quietly). + * + * This signal is guaranteed to be emitted at most once. + * + * Use error to know if the job was finished with error. + * + * This is a private signal, it can't be emitted directly by subclasses of + * KJob, use emitResult() instead. + * + * Please connect to this signal instead of finished. + * + * @param job the job that emitted this signal + * + * @see kill + */ + void result(Job *job); + +protected : + /** + * Utility function to emit the result signal, and end this job. + * It first notifies the observers to hide the progress for this job using + * the finished() signal. + * + * @note Deletes this job using deleteLater(). + * + * @see result() + * @see finished() + */ + Q_SLOT void emitResult(); + + std::unique_ptr const d_ptr; + +private: + void finishJob(bool emitResult); + + Q_DECLARE_PRIVATE(Job) +}; +} // namespace LINGMO_SESSION #endif diff --git a/startlingmo/job_private.hpp b/session/job_private.hpp similarity index 100% rename from startlingmo/job_private.hpp rename to session/job_private.hpp diff --git a/session/main.cpp b/session/main.cpp index 43b40ee..0d2fecb 100755 --- a/session/main.cpp +++ b/session/main.cpp @@ -24,9 +24,6 @@ int main(int argc, char *argv[]) { // putenv((char *)"SESSION_MANAGER="); - // force xcb QPA plugin as session manager server is very X11 specific. - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); - QQuickWindow::setDefaultAlphaBuffer(true); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); diff --git a/session/processmanager.cpp b/session/processmanager.cpp index 756395c..2a8d522 100755 --- a/session/processmanager.cpp +++ b/session/processmanager.cpp @@ -6,193 +6,298 @@ #include "application.h" #include -#include -#include +#include +#include #include +#include #include -#include -#include +#include #include -#include +#include +#include +#include #include #include -#include -#include #include +#include +#include +#include +#include +#include #include "daemon-helper.h" ProcessManager::ProcessManager(Application *app, QObject *parent) - : QObject(parent) - , m_app(app) - , m_wmStarted(false) - , m_waitLoop(nullptr) -{ - qApp->installNativeEventFilter(this); + : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { + qApp->installNativeEventFilter(this); } -ProcessManager::~ProcessManager() -{ - qApp->removeNativeEventFilter(this); +ProcessManager::~ProcessManager() { + qApp->removeNativeEventFilter(this); - QMapIterator i(m_systemProcess); - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - delete p; - m_systemProcess[i.key()] = nullptr; - } + QMapIterator i(m_systemProcess); + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + delete p; + m_systemProcess[i.key()] = nullptr; + } } -void ProcessManager::start() -{ - startWindowManager(); - startDaemonProcess(); +void ProcessManager::start() { + startWindowManager(); + startDaemonProcess(); } -void ProcessManager::logout() -{ - QMapIterator i(m_systemProcess); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - p->terminate(); - } - i.toFront(); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { - p->kill(); - } +void ProcessManager::logout() { + QMapIterator i(m_systemProcess); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + p->terminate(); + } + i.toFront(); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { + p->kill(); } + } - QCoreApplication::exit(0); + QCoreApplication::exit(0); } -void ProcessManager::startWindowManager() -{ +void ProcessManager::startWindowManager() { + auto detcted_wayland = + qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); + + if (detcted_wayland || m_app->wayland()) { + auto kwinWaylandJob = + new StartServiceJob(QStringLiteral("lingmo_kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}, + QStringLiteral("org.lingmo.KWinWrapper")); + kwinWaylandJob->setParent(this); + kwinWaylandJob->exec(); // Wait untill kwin_wayland_wrapper started + } else { auto *wmProcess = new QProcess; - wmProcess->start(m_app->wayland() ? "kwin_wayland" : "kwin_x11", QStringList()); - - if (!m_app->wayland()) { - QEventLoop waitLoop; - m_waitLoop = &waitLoop; - // add a timeout to avoid infinite blocking if a WM fail to execute. - QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); - waitLoop.exec(); - m_waitLoop = nullptr; - } + wmProcess->start("kwin_x11", QStringList()); + } } -void ProcessManager::startDesktopProcess() -{ - // When the lingmo-settings-daemon theme module is loaded, start the desktop. - // In the way, there will be no problem that desktop and launcher can't get wallpaper. - - QList> list; - // Desktop components - list << qMakePair(QString("lingmo-notificationd"), QStringList()); - list << qMakePair(QString("lingmo-statusbar"), QStringList()); - list << qMakePair(QString("lingmo-dock"), QStringList()); - list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); - list << qMakePair(QString("lingmo-launcher"), QStringList()); - list << qMakePair(QString("lingmo-powerman"), QStringList()); - list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); - - m_desktopAutoStartD = std::make_shared(list); - - // Auto start - QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); +void ProcessManager::startDesktopProcess() { + // When the lingmo-settings-daemon theme module is loaded, start the desktop. + // In the way, there will be no problem that desktop and launcher can't get + // wallpaper. + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("QT_QPA_PLATFORM", "xcb"); + QList> list; + // Desktop components + list << std::make_tuple(QString("lingmo-notificationd"), QStringList(), env); + list << std::make_tuple(QString("lingmo-statusbar"), QStringList(), env); + list << std::make_tuple(QString("lingmo-dock"), QStringList(), env); + list << std::make_tuple(QString("lingmo-filemanager"), QStringList("--desktop"), env); + list << std::make_tuple(QString("lingmo-launcher"), QStringList(), env); + list << std::make_tuple(QString("lingmo-powerman"), QStringList(), env); + list << std::make_tuple(QString("lingmo-clipboard"), QStringList(), env); + list << std::make_tuple(QString("lingmo-wallpaper-color-pick"), QStringList(), env); + + m_desktopAutoStartD = std::make_shared(list); + + // Auto start + QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); } -void ProcessManager::startDaemonProcess() -{ - QList> list; - list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); - list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); - list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); - list << qMakePair(QString("lingmo-permission-surveillance"),QStringList()); -// list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-chotkeys"), QStringList()); - - m_daemonAutoStartD = std::make_shared(list); +void ProcessManager::startDaemonProcess() { + auto env = QProcessEnvironment::systemEnvironment(); + // xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); + QList> list; + list << std::make_tuple(QString("lingmo-settings-daemon"), QStringList(), env); + list << std::make_tuple(QString("lingmo-xembedsniproxy"), QStringList(), env); + list << std::make_tuple(QString("lingmo-gmenuproxy"), QStringList(), env); + list << std::make_tuple(QString("lingmo-permission-surveillance"), QStringList(), env); + // list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << std::make_tuple(QString("lingmo-chotkeys"), QStringList(), env); + + m_daemonAutoStartD = std::make_shared(list); } -void ProcessManager::loadAutoStartProcess() -{ - QList> list; - - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, - QStringLiteral("autostart"), - QStandardPaths::LocateDirectory); - for (const QString &dir : dirs) { - const QDir d(dir); - const QStringList fileNames = d.entryList(QStringList() << QStringLiteral("*.desktop")); - for (const QString &file : fileNames) { - QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); - desktop.setIniCodec("UTF-8"); - desktop.beginGroup("Desktop Entry"); - - // Ignore files the require a specific desktop environment - if (desktop.contains("NotShowIn")) { - const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); - if (notShowIn.contains("Lingmo")) - continue; - } - if (desktop.contains("OnlyShowIn")) { - const QStringList onlyShowIn = desktop.value("OnlyShowIn").toStringList(); - if (!onlyShowIn.contains("Lingmo")) - continue; - } - - const QString execValue = desktop.value("Exec").toString(); - - // 避免冲突 - if (execValue.contains("gmenudbusmenuproxy")) - continue; - - // 使用 QProcess::splitCommand 来解析命令和参数 - QStringList args = QProcess::splitCommand(execValue); - - // 检查是否至少有一个元素(即程序路径) - if (!args.isEmpty()) { - auto program = args.first(); - args.removeFirst(); // 移除程序路径,剩下的都是参数 - - list << qMakePair(program, args); - } else { - qWarning() << "Invalid 'Exec' found in file!"; - } - } +void ProcessManager::loadAutoStartProcess() { + QList> list; + auto env = QProcessEnvironment::systemEnvironment(); + + const QStringList dirs = QStandardPaths::locateAll( + QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), + QStandardPaths::LocateDirectory); + for (const QString &dir : dirs) { + const QDir d(dir); + const QStringList fileNames = + d.entryList(QStringList() << QStringLiteral("*.desktop")); + for (const QString &file : fileNames) { + QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); + desktop.setIniCodec("UTF-8"); + desktop.beginGroup("Desktop Entry"); + + // Ignore files the require a specific desktop environment + if (desktop.contains("NotShowIn")) { + const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); + if (notShowIn.contains("Lingmo")) + continue; + } + if (desktop.contains("OnlyShowIn")) { + const QStringList onlyShowIn = + desktop.value("OnlyShowIn").toStringList(); + if (!onlyShowIn.contains("Lingmo")) + continue; + } + + const QString execValue = desktop.value("Exec").toString(); + + // 避免冲突 + if (execValue.contains("gmenudbusmenuproxy")) + continue; + + // 使用 QProcess::splitCommand 来解析命令和参数 + QStringList args = QProcess::splitCommand(execValue); + + // 检查是否至少有一个元素(即程序路径) + if (!args.isEmpty()) { + auto program = args.first(); + args.removeFirst(); // 移除程序路径,剩下的都是参数 + + list << std::make_tuple(program, args, env); + } else { + qWarning() << "Invalid 'Exec' found in file!"; + } } + } - m_userAutoStartD = std::make_shared(list, false); + m_userAutoStartD = std::make_shared(list, false); } -bool ProcessManager::nativeEventFilter(const QByteArray &eventType, void *message, long *result) -{ - if (eventType != "xcb_generic_event_t") // We only want to handle XCB events - return false; - - // ref: lxqt session - if (!m_wmStarted && m_waitLoop) { - // all window managers must set their name according to the spec - if (!QString::fromUtf8(NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck).wmName()).isEmpty()) { - qDebug() << "Window manager started"; - m_wmStarted = true; - if (m_waitLoop && m_waitLoop->isRunning()) - m_waitLoop->exit(); - - qApp->removeNativeEventFilter(this); - } +bool ProcessManager::nativeEventFilter(const QByteArray &eventType, + void *message, long *result) { + if (eventType != "xcb_generic_event_t") // We only want to handle XCB events + return false; + + // ref: lxqt session + if (!m_wmStarted && m_waitLoop) { + // all window managers must set their name according to the spec + if (!QString::fromUtf8( + NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) + .wmName()) + .isEmpty()) { + qDebug() << "Window manager started"; + m_wmStarted = true; + if (m_waitLoop && m_waitLoop->isRunning()) + m_waitLoop->exit(); + + qApp->removeNativeEventFilter(this); } + } - return false; + return false; +} + + +//////////////////////////////////////////////////////////// + +StartProcessJob::StartProcessJob(const QString &process, + const QStringList &args, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(finished(int, QProcess::ExitStatus))); +} + +void StartProcessJob::start() { + qDebug() + << "Starting " << m_process->program() << m_process->arguments(); + + m_process->start(); +} + +void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { + qDebug() << "process job " << m_process->program() + << "finished with exit code " << exitCode; + emitResult(); +} + +StartServiceJob::StartServiceJob(const QString &process, + const QStringList &args, + const QString &serviceId, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess), m_serviceId(serviceId), + m_additionalEnv(additionalEnv) { + m_process->setProgram(process); + m_process->setArguments(args); + + auto watcher = + new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, this); + connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, + &StartServiceJob::emitResult); +} + +void StartServiceJob::start() { + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(m_additionalEnv); + m_process->setProcessEnvironment(env); + + if (!m_serviceId.isEmpty() && + QDBusConnection::sessionBus().interface()->isServiceRegistered( + m_serviceId)) { + qDebug() << m_process << "already running"; + emitResult(); + return; + } + qDebug() << "Starting " << m_process->program() << m_process->arguments(); + + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + m_process->start(); + const bool ret = m_process->waitForStarted(); + + if (!ret) { + qWarning() + << "error starting process" << m_process->program() + << m_process->arguments(); + emitResult(); + } + + if (m_serviceId.isEmpty()) { + emitResult(); + } } + +LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); +} + +void LaunchProcess::start() { + qDebug() + << "Starting " << m_process->program() << m_process->arguments(); + + m_process->startDetached(); + + emitResult(); +} \ No newline at end of file diff --git a/session/processmanager.h b/session/processmanager.h index f6a5562..8058aca 100755 --- a/session/processmanager.h +++ b/session/processmanager.h @@ -20,13 +20,14 @@ #ifndef PROCESSMANAGER_H #define PROCESSMANAGER_H -#include #include -#include -#include +#include #include #include +#include +#include #include +#include #include "daemon-helper.h" @@ -72,4 +73,56 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter QEventLoop *m_waitLoop; }; +using namespace LINGMO_SESSION; +/** + * Launches a process, and waits for the process to start + */ +class LaunchProcess : public Job { + Q_OBJECT +public: + LaunchProcess( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +private: + QProcess *m_process; +}; + +/** + * Launches a process, and waits for the process to finish + */ +class StartProcessJob : public Job { + Q_OBJECT +public: + StartProcessJob( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +public Q_SLOTS: + void finished(int exitCode, QProcess::ExitStatus e); + +private: + QProcess *m_process; +}; + +/** + * Launches a process, and waits for the service to appear on the session bus + */ +class StartServiceJob : public Job { + Q_OBJECT +public: + StartServiceJob( + const QString &process, const QStringList &args, const QString &serviceId, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + + void start() override; + +private: + QProcess *m_process; + const QString m_serviceId; + const QProcessEnvironment m_additionalEnv; +}; + #endif // PROCESSMANAGER_H diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt deleted file mode 100644 index 7f94336..0000000 --- a/startlingmo/CMakeLists.txt +++ /dev/null @@ -1,76 +0,0 @@ -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(KF6_MIN_VERSION "6.1.0") - -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -find_package(Qt5 COMPONENTS Core DBus REQUIRED) - -find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE) -list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) - -include(KDEInstallDirs) -include(FindKWinPath) -include(ECMQtDeclareLoggingCategory) - -find_kwin_wayland_bin_path() -find_kwin_bin_path() - -configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startlingmo.h) - -find_package(KF5 REQUIRED COMPONENTS DBusAddons CoreAddons) - -find_package(LibKWorkspace REQUIRED) - -find_package(KF5Wayland REQUIRED) - -SET(startlingmo_SRCS - startlingmo.cpp - daemon-helper.cpp - UpdateLaunchEnvironment.cpp - signalhandler.cpp -) - -ecm_qt_declare_logging_category(startlingmo_SRCS HEADER debug.h IDENTIFIER LINGMO_STARTUP CATEGORY_NAME org.lingmo.startup) - -add_library(startlingmo OBJECT ${startlingmo_SRCS}) -target_link_libraries(startlingmo - PUBLIC - Qt5::Core - Qt5::DBus - KF5::DBusAddons - KF5::CoreAddons - PW::KWorkspace -) - -add_executable(startlingmo-wayland startlingmo-wayland.cpp) -target_link_libraries(startlingmo-wayland - PRIVATE - startlingmo - PUBLIC - Qt5::Core - Qt5::DBus -) - -add_executable(startlingmo-x11 startlingmo-x11.cpp) -target_link_libraries(startlingmo-x11 - PRIVATE - startlingmo - PUBLIC - Qt5::Core - Qt5::DBus -) - -add_subdirectory(lingmo-session) -add_subdirectory(wayland_wrapper) - -install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -install(TARGETS startlingmo-x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -install(PROGRAMS lingmo-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) -install(FILES lingmo-wayland-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions/) -install(FILES lingmo-xorg-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) - diff --git a/startlingmo/UpdateLaunchEnvironment.cpp b/startlingmo/UpdateLaunchEnvironment.cpp deleted file mode 100644 index 9020633..0000000 --- a/startlingmo/UpdateLaunchEnvironment.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "UpdateLaunchEnvironment.hpp" - -#include -#include -#include -#include - -#include -#include - -#include "debug.h" -#include "startlingmo.hpp" - -class UpdateLaunchEnvironmentJobPrivate -{ -public: - explicit UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q); - void monitorReply(const QDBusPendingReply<> &reply); - - static bool isPosixName(const QString &name); - static bool isSystemdApprovedValue(const QString &value); - - UpdateLaunchEnvironmentJob *q; - QProcessEnvironment environment; - int pendingReplies = 0; -}; - -UpdateLaunchEnvironmentJobPrivate::UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q) - : q(q) -{ -} - -void UpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply) -{ - ++pendingReplies; - - auto *watcher = new QDBusPendingCallWatcher(reply, q); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) { - watcher->deleteLater(); - --pendingReplies; - - if (pendingReplies == 0) { - Q_EMIT q->finished(); - q->deleteLater(); - } - }); -} - -UpdateLaunchEnvironmentJob::UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment) - : d(new UpdateLaunchEnvironmentJobPrivate(this)) -{ - d->environment = environment; - QTimer::singleShot(0, this, &UpdateLaunchEnvironmentJob::start); -} - -UpdateLaunchEnvironmentJob::~UpdateLaunchEnvironmentJob() = default; - -void UpdateLaunchEnvironmentJob::start() -{ - qDBusRegisterMetaType>(); - QMap dbusActivationEnv; - QStringList systemdUpdates; - - for (const auto &varName : d->environment.keys()) { - if (!UpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) { - qCWarning(LINGMO_STARTUP) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters"; - continue; - } - const QString value = d->environment.value(varName); - - // plasma-session - QDBusMessage lingmoSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("com.lingmo.Session"), - QStringLiteral("/Session"), - QStringLiteral("com.lingmo.Session"), - QStringLiteral("updateLaunchEnv")); - lingmoSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); - auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(lingmoSessionMsg); - d->monitorReply(plasmaSessionReply); - - // DBus-activation environment - dbusActivationEnv.insert(varName, value); - - // _user_ systemd env - // Systemd has stricter parsing of valid environment variables - // https://github.com/systemd/systemd/issues/16704 - // validate here - if (!UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) { - qCWarning(LINGMO_STARTUP) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters"; - continue; - } - const QString updateString = varName + QStringLiteral("=") + value; - systemdUpdates.append(updateString); - } - - // DBus-activation environment - QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), - QStringLiteral("/org/freedesktop/DBus"), - QStringLiteral("org.freedesktop.DBus"), - QStringLiteral("UpdateActivationEnvironment")); - dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)}); - - auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg); - d->monitorReply(dbusActivationReply); - - // _user_ systemd env - QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), - QStringLiteral("/org/freedesktop/systemd1"), - QStringLiteral("org.freedesktop.systemd1.Manager"), - QStringLiteral("SetEnvironment")); - systemdActivationMsg.setArguments({systemdUpdates}); - - auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg); - d->monitorReply(systemdActivationReply); -} - -bool UpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name) -{ - // Posix says characters like % should be 'tolerated', but it gives issues in practice. - // https://bugzilla.redhat.com/show_bug.cgi?id=1754395 - // https://bugzilla.redhat.com/show_bug.cgi?id=1879216 - // Ensure systemd compat by only allowing alphanumerics and _ in names. - bool first = true; - for (const QChar c : name) { - if (first && !c.isLetter() && c != QLatin1Char('_')) { - return false; - } else if (first) { - first = false; - } else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { - return false; - } - } - return !first; -} - -bool UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value) -{ - // systemd code checks that a value contains no control characters except \n \t - // effectively copied from systemd's string_has_cc - for (const char &it : value.toLatin1()) { - if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) { - continue; - } - if (it > 0 && it < ' ') { - return false; - } - if (it == 127) { - return false; - } - } - return true; -} \ No newline at end of file diff --git a/startlingmo/UpdateLaunchEnvironment.hpp b/startlingmo/UpdateLaunchEnvironment.hpp deleted file mode 100644 index 1ad55ca..0000000 --- a/startlingmo/UpdateLaunchEnvironment.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - SPDX-FileCopyrightText: 2020 Kai Uwe Broulik - SPDX-FileCopyrightText: 2021 David Edmundson - SPDX-FileCopyrightText: 2024 Elysia - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ -#ifndef UPDATELAUNCHENVIRONMENT_HPP -#define UPDATELAUNCHENVIRONMENT_HPP - -#include - -#include - -class QString; -class UpdateLaunchEnvironmentJobPrivate; - -/** - * @class UpdateLaunchEnvironmentJob updatelaunchenvironmentjob.h - * - * Job for updating the launch environment. - * - * This job adds or updates an environment variable in process environment that will be used - * when a process is launched: - * This includes: - * - DBus activation - * - Systemd units - * - lingmo-session - * - * Environment variables are sanitized before uploading. - * - * This object deletes itself after completion, similar to KJobs - */ -class UpdateLaunchEnvironmentJob : public QObject -{ - Q_OBJECT - -public: - explicit UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment); - ~UpdateLaunchEnvironmentJob() override; - -Q_SIGNALS: - void finished(); - -private: - void start(); - -private: - std::unique_ptr const d; -}; - -#endif // UPDATELAUNCHENVIRONMENT_HPP \ No newline at end of file diff --git a/startlingmo/cmake/FindKWinPath.cmake b/startlingmo/cmake/FindKWinPath.cmake deleted file mode 100644 index c959fbd..0000000 --- a/startlingmo/cmake/FindKWinPath.cmake +++ /dev/null @@ -1,29 +0,0 @@ -function(find_kwin_wayland_bin_path) - # 使用 whereis 命令来查找 kwin 或 kwin_x11 - execute_process( - COMMAND whereis kwin_wayland - OUTPUT_VARIABLE kwin_wayland_output - ERROR_QUIET - ) - - # 解析输出以获取二进制文件的路径 - string(REGEX MATCH "/[^\n\r ]*/kwin_wayland" kwin_wayland_bin_path "${kwin_wayland_output}") - - # 设置 KWIN_WAYLAND_BIN_PATH 变量 - set(KWIN_WAYLAND_BIN_PATH "${kwin_wayland_bin_path}" CACHE PATH "Path to the KWin Wayland binary" FORCE) -endfunction() - -function(find_kwin_bin_path) - # 使用 whereis 命令来查找 kwin 或 kwin_x11 - execute_process( - COMMAND whereis kwin - OUTPUT_VARIABLE kwin_output - ERROR_QUIET - ) - - # 解析输出以获取二进制文件的路径 - string(REGEX MATCH "/[^\n\r ]*/kwin" kwin_bin_path "${kwin_output}") - - # 设置 KWIN_WAYLAND_BIN_PATH 变量 - set(KWIN_BIN "${kwin_bin_path}" CACHE PATH "Path to the KWin binary" FORCE) -endfunction() \ No newline at end of file diff --git a/startlingmo/config-startlingmo.h.cmake b/startlingmo/config-startlingmo.h.cmake deleted file mode 100644 index ccad21b..0000000 --- a/startlingmo/config-startlingmo.h.cmake +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#define CMAKE_INSTALL_FULL_BINDIR "@CMAKE_INSTALL_FULL_BINDIR@" -#define KDE_INSTALL_FULL_DATAROOTDIR "@KDE_INSTALL_FULL_DATAROOTDIR@" -#define CMAKE_INSTALL_FULL_LIBEXECDIR "@CMAKE_INSTALL_FULL_LIBEXECDIR@" -#define CMAKE_INSTALL_FULL_LIBEXECDIR_KF6 "@CMAKE_INSTALL_FULL_LIBEXECDIR_KF6@" -#define KWIN_WAYLAND_BIN_PATH "@KWIN_WAYLAND_BIN_PATH@" - -#define KWIN_BIN "${KWIN_BIN}" diff --git a/startlingmo/daemon-helper.cpp b/startlingmo/daemon-helper.cpp deleted file mode 100644 index 486a0e7..0000000 --- a/startlingmo/daemon-helper.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/** - * @name daemon-helper.cpp - * @author Elysia - **/ -#include "daemon-helper.h" -#include "job_private.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace LINGMO_SESSION { -Daemon::Daemon(const QList> &processList, - bool _enableAutoStart, QObject *parent) - : QObject(parent), m_processList(processList), - m_enableAutoRestart(_enableAutoStart) { - for (const auto &processInfo : m_processList) { - startProcess(processInfo); - } -} - -void Daemon::onProcessError(QProcess::ProcessError error) { - const QPointer process = qobject_cast(sender()); - - if (!process) - return; - - QString program = process->program(); - qDebug() << "Process error:" << program << "Error:" << error; - - for (const auto &processInfo : m_processList) { - if (processInfo.first == program) { - qDebug() << "Restarting process due to error:" << program; - QTimer::singleShot(1, this, [this, processInfo]() { - startProcess(processInfo); - }); // Restart after 1 second - return; - } - } -} - -void Daemon::startProcess(const QPair &processInfo) { - const QPointer process = new QProcess(this); - - if (this->m_enableAutoRestart) - connect(process, &QProcess::errorOccurred, this, &Daemon::onProcessError); - - process->start(processInfo.first, processInfo.second); - if (process->waitForStarted()) { - qDebug() << "Process started:" << processInfo.first - << "PID:" << process->processId(); - } else { - qDebug() << "Failed to start process:" << processInfo.first - << process->errorString(); - } -} - -JobPrivate::JobPrivate() {} - -JobPrivate::~JobPrivate() {} - -Job::Job(QObject *parent) : QObject(parent), d_ptr(new JobPrivate) {} - -Job::~Job() { - if (!d_ptr->isFinished) { - d_ptr->isFinished = true; - Q_EMIT finished(this); - } -} - -void Job::emitResult() { - if (!d_func()->isFinished) { - finishJob(true); - } -} -void Job::finishJob(bool emitResult) { - Q_D(Job); - Q_ASSERT(!d->isFinished); - d->isFinished = true; - - if (d->eventLoop) { - d->eventLoop->quit(); - } - - Q_EMIT finished(this); - - if (emitResult) { - Q_EMIT result(this); - } - - if (isAutoDelete()) { - deleteLater(); - } -} - -bool Job::isAutoDelete() const { - Q_D(const Job); - return d->isAutoDelete; -} - -void Job::setAutoDelete(bool autodelete) { - Q_D(Job); - d->isAutoDelete = autodelete; -} - -bool Job::exec() { - Q_D(Job); - // Usually this job would delete itself, via deleteLater() just after - // emitting result() (unless configured otherwise). Since we use an event - // loop below, that event loop will process the deletion event and we'll - // have been deleted when exec() returns. This crashes, so temporarily - // suspend autodeletion and manually do it afterwards. - const bool wasAutoDelete = isAutoDelete(); - setAutoDelete(false); - - Q_ASSERT(!d->eventLoop); - - QEventLoop loop(this); - d->eventLoop = &loop; - - start(); - - if (!d->isFinished) { - d->m_startedWithExec = true; - d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); - } - d->eventLoop = nullptr; - - if (wasAutoDelete) { - deleteLater(); - } - return (d->error == NoError); -} -} // namespace LINGMO_SESSION diff --git a/startlingmo/daemon-helper.h b/startlingmo/daemon-helper.h deleted file mode 100644 index e911942..0000000 --- a/startlingmo/daemon-helper.h +++ /dev/null @@ -1,203 +0,0 @@ -#ifndef __DAEMON_HELPER_ -#define __DAEMON_HELPER_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LINGMO_SESSION { -class JobPrivate; - -class Daemon : public QObject { - Q_OBJECT - -public: - /** - * Start all the passed process using daemon. - * @param processList Process list to start - * @param parent - */ - explicit Daemon(const QList> &processList, - bool _enableAutoStart = true, QObject *parent = nullptr); - -public slots: - - /** - * Handle the case when the progarm has some errors (i.e. crashed) - * @param error - */ - void onProcessError(QProcess::ProcessError error); - -private: - /** - * Start a given process using daemon helper - * @brief startProcess - * @param processInfo - */ - void startProcess(const QPair &processInfo); - - QList> m_processList; - - /** - * @brief Whether to enable auto reload when process exited. - */ - bool m_enableAutoRestart; -}; - -class Job : public QObject { - Q_OBJECT -public: - Job(QObject *parent = nullptr); - - ~Job(); - - /** - * Returns whether this job automatically deletes itself once - * the job is finished. - * - * @return whether the job is deleted automatically after - * finishing. - */ - bool isAutoDelete() const; - - /** - * Sets the auto-delete property of the job. If @p autodelete is - * set to @c false the job will not delete itself once it is finished. - * - * The default for any Job is to automatically delete itself, which - * implies that the job was created on the heap (using new). - * If the job is created on the stack (which isn't the typical use-case - * for a job) then you must set auto-delete to @c false, otherwise you - * could get a crash when the job finishes and tries to delete itself. - * - * @note If you set auto-delete to @c false then you need to kill the - * job manually, ideally by calling kill(). - * - * @param autodelete set to @c false to disable automatic deletion - * of the job. - */ - void setAutoDelete(bool autodelete); - - /** - * Executes the job synchronously. - * - * This will start a nested QEventLoop internally. Nested event loop can be - * dangerous and can have unintended side effects, you should avoid calling - * exec() whenever you can and use the asynchronous interface of Job instead. - * - * Should you indeed call this method, you need to make sure that all callers - * are reentrant, so that events delivered by the inner event loop don't cause - * non-reentrant functions to be called, which usually wreaks havoc. - * - * Note that the event loop started by this method does not process user input - * events, which means your user interface will effectively be blocked. Other - * events like paint or network events are still being processed. The - * advantage of not processing user input events is that the chance of - * accidental reentrance is greatly reduced. Still you should avoid calling - * this function. - * - * @return true if the job has been executed without error, false otherwise - */ - bool exec(); - - /** - * Starts the job asynchronously. - * - * When the job is finished, result() is emitted. - * - * Warning: Never implement any synchronous workload in this method. This - * method should just trigger the job startup, not do any work itself. It is - * expected to be non-blocking. - * - * This is the method all subclasses need to implement. - * It should setup and trigger the workload of the job. It should not do any - * work itself. This includes all signals and terminating the job, e.g. by - * emitResult(). The workload, which could be another method of the - * subclass, is to be triggered using the event loop, e.g. by code like: - * \code - * void ExampleJob::start() - * { - * QTimer::singleShot(0, this, &ExampleJob::doWork); - * } - * \endcode - */ - Q_SCRIPTABLE virtual void start() = 0; - - enum { - /*** Indicates there is no error */ - NoError = 0, - /*** Indicates the job was killed */ - KilledJobError = 1, - /*** Subclasses should define error codes starting at this value */ - UserDefinedError = 100, - }; -Q_SIGNALS: - /** - * Emitted when the job is finished, in any case. It is used to notify - * observers that the job is terminated and that progress can be hidden. - * - * This signal is guaranteed to be emitted exactly once. - * - * This is a private signal, it can't be emitted directly by subclass, use - * emitResult() instead. - * - * In general, to be notified of a job's completion, client code should - * connect to result() rather than finished(), so that kill(Quietly) is indeed - * quiet. However if you store a list of jobs and they might get killed - * silently, then you must connect to this instead of result(), to avoid - * dangling pointers in your list. - * - * @param job the job that emitted this signal - * @internal - * - * @see result - */ - void finished(Job *job); - - /** - * Emitted when the job is finished (except when killed with KJob::Quietly). - * - * This signal is guaranteed to be emitted at most once. - * - * Use error to know if the job was finished with error. - * - * This is a private signal, it can't be emitted directly by subclasses of - * KJob, use emitResult() instead. - * - * Please connect to this signal instead of finished. - * - * @param job the job that emitted this signal - * - * @see kill - */ - void result(Job *job); - -protected : - /** - * Utility function to emit the result signal, and end this job. - * It first notifies the observers to hide the progress for this job using - * the finished() signal. - * - * @note Deletes this job using deleteLater(). - * - * @see result() - * @see finished() - */ - Q_SLOT void emitResult(); - - std::unique_ptr const d_ptr; - -private: - void finishJob(bool emitResult); - - Q_DECLARE_PRIVATE(Job) -}; -} // namespace LINGMO_SESSION -#endif diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt deleted file mode 100755 index 1a9f4fd..0000000 --- a/startlingmo/lingmo-session/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -project(lingmo_session) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED 17) - -set(TARGET lingmo_session) - -set(SOURCES - application.cpp - main.cpp - process.cpp - processmanager.cpp - networkproxymanager.cpp - - powermanager/power.cpp - powermanager/powerproviders.cpp -) - -qt_add_dbus_adaptor(DBUS_SOURCES - com.lingmo.Session.xml - application.h Application - sessionadaptor SessionAdaptor) -# set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) - -find_package(KF5WindowSystem) -find_package(Threads) - -ecm_qt_declare_logging_category(SOURCES HEADER debug.h IDENTIFIER LINGMO_SESSION_D CATEGORY_NAME org.lingmo.session) - -add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES}) -target_link_libraries(${TARGET} - PRIVATE - startlingmo - PUBLIC - Qt5::Core - Qt5::Gui - Qt5::Widgets - Qt5::Quick - Qt5::DBus - Qt5::X11Extras - KF5::WindowSystem - ${CMAKE_THREAD_LIBS_INIT} -) -target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) - -install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/startlingmo/lingmo-session/application.cpp b/startlingmo/lingmo-session/application.cpp deleted file mode 100755 index 8d6f0ea..0000000 --- a/startlingmo/lingmo-session/application.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: revenmartin - * - * 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 . - */ - -#include "application.h" -#include "sessionadaptor.h" - -// Qt -#include -#include -#include -#include -#include -#include - -#include -#include - -// STL -#include -#include - -#include "startlingmo.hpp" -#include "debug.h" - -Application::Application(const QCommandLineParser &parser, QObject *parent) - : QObject(parent), m_processManager(new ProcessManager(this)), - m_networkProxyManager(new NetworkProxyManager), m_wayland(false) { - new SessionAdaptor(this); - - // connect to D-Bus and register as an object: - QDBusConnection::sessionBus().registerService( - QStringLiteral("com.lingmo.Session")); - QDBusConnection::sessionBus().registerObject(QStringLiteral("/Session"), - this); - - m_wayland = parser.isSet("wayland"); - - m_networkProxyManager->update(); - - // Launch Lingmo and user defined processes ! - QTimer::singleShot(100, m_processManager, &ProcessManager::start); -} - -bool Application::wayland() const { return m_wayland; } - -void Application::updateLaunchEnv(const QString &key, const QString &value) { - qCDebug(LINGMO_SESSION_D) << "Update launch env: " << key << value; - qputenv(key.toLatin1(), value.toLatin1()); - m_processManager->updateLaunchEnv(key, value); -} - -void Application::launch(const QString &exec, const QStringList &args) { - QProcess process; - process.setProgram(exec); - process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); - process.setArguments(args); - process.startDetached(); -} - -void Application::launch(const QString &exec, const QString &workingDir, - const QStringList &args) { - QProcess process; - process.setProgram(exec); - process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); - process.setWorkingDirectory(workingDir); - process.setArguments(args); - process.startDetached(); -} - -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", "Lingmo"); - qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); - qputenv("XDG_SESSION_DESKTOP", "Lingmo"); - - // Qt - qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); - qputenv("QT_PLATFORM_PLUGIN", "lingmo"); - - // ref: - // https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping - qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); - - qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); - - // 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::initLanguage() { - QSettings settings(QSettings::UserScope, "lingmoos", "language"); - QString value = settings.value("language", "").toString(); - - // Init Language - if (value.isEmpty()) { - QFile file("/etc/locale.gen"); - if (file.open(QIODevice::ReadOnly)) { - QStringList lines = QString(file.readAll()).split('\n'); - - for (const QString &line : lines) { - if (line.startsWith('#')) - continue; - - if (line.trimmed().isEmpty()) - continue; - - value = line.split(' ').first().split('.').first(); - } - } - } - - if (value.isEmpty()) - value = "en_US"; - - settings.setValue("language", value); - - 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, "lingmoos", "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)); - } -} - -void Application::initXResource() { - QSettings settings(QSettings::UserScope, "lingmoos", "theme"); - qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); - int fontDpi = 96 * scaleFactor; - QString cursorTheme = settings.value("CursorTheme", "default").toString(); - int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; - int xftAntialias = settings.value("XftAntialias", 1).toBool(); - QString xftHintStyle = - settings.value("XftHintStyle", "hintslight").toString(); - - const QString datas = QString("Xft.dpi: %1\n" - "Xcursor.theme: %2\n" - "Xcursor.size: %3\n" - "Xft.antialias: %4\n" - "Xft.hintstyle: %5\n" - "Xft.rgba: rgb") - .arg(fontDpi) - .arg(cursorTheme) - .arg(cursorSize) - .arg(xftAntialias) - .arg(xftHintStyle); - - QProcess p; - p.start(QStringLiteral("xrdb"), - {QStringLiteral("-quiet"), QStringLiteral("-merge"), - QStringLiteral("-nocpp")}); - p.setProcessChannelMode(QProcess::ForwardedChannels); - p.write(datas.toLatin1()); - p.closeWriteChannel(); - p.waitForFinished(-1); - - // For lingmo-wine - qputenv("LINGMO_FONT_DPI", QByteArray::number(fontDpi)); - - // Init cursor - runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); - // qputenv("XCURSOR_THEME", cursorTheme.toLatin1()); - // qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor)); -} - -void Application::initKWinConfig() { - QSettings settings( - QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kwinrc", - QSettings::IniFormat); - - settings.beginGroup("Effect-Blur"); - settings.setValue("BlurStrength", 10); - settings.setValue("NoiseStrength", 0); - settings.endGroup(); - - settings.beginGroup("Windows"); - settings.setValue("FocusStealingPreventionLevel", 0); - settings.setValue("HideUtilityWindowsForInactive", false); - settings.setValue("BorderlessMaximizedWindows", false); - settings.setValue("Placement", "Centered"); - settings.endGroup(); - - settings.beginGroup("org.kde.kdecoration2"); - settings.setValue("BorderSize", "Normal"); - settings.setValue("ButtonsOnLeft", ""); - settings.setValue("ButtonsOnRight", "HIAX"); - settings.setValue("library", "org.lingmo.decoration"); - settings.setValue("theme", ""); - settings.endGroup(); -} - -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(); -} diff --git a/startlingmo/lingmo-session/application.h b/startlingmo/lingmo-session/application.h deleted file mode 100755 index e681d2c..0000000 --- a/startlingmo/lingmo-session/application.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: revenmartin - * - * 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 . - */ - -#ifndef APPLICATION_H -#define APPLICATION_H - -#include -#include -#include -#include -#include -#include - -#include "networkproxymanager.h" -#include "powermanager/power.h" -#include "processmanager.h" - -class Application : public QObject { - Q_OBJECT - -public: - explicit Application(const QCommandLineParser &parser, - QObject *parent = nullptr); - - bool wayland() const; - -public slots: - void updateLaunchEnv(const QString &key, const QString &value); - - void logout() { m_processManager->logout(); } - - void reboot() { - m_power.reboot(); - QCoreApplication::exit(0); - } - - void powerOff() { - m_power.shutdown(); - QCoreApplication::exit(0); - } - - void suspend() { m_power.suspend(); } - - [[maybe_unused]] void startDesktopProcess() { - // Start Lingmo Desktop Environment - m_processManager->startDesktopProcess(); - } - - [[maybe_unused]] void updateNetworkProxy() { - m_networkProxyManager->update(); - } - - void launch(const QString &exec, const QStringList &args); - void launch(const QString &exec, const QString &workingDir, - const QStringList &args); - -private: - void initEnvironments(); - void initLanguage(); - void initScreenScaleFactors(); - void initXResource(); - void initKWinConfig(); - bool syncDBusEnvironment(); - void importSystemdEnvrionment(); - void createConfigDirectory(); - void updateUserDirs(); - int runSync(const QString &program, const QStringList &args, - const QStringList &env = {}); - -private: - ProcessManager *m_processManager; - NetworkProxyManager *m_networkProxyManager; - Power m_power; - - bool m_wayland; -}; - -#endif // APPLICATION_H diff --git a/startlingmo/lingmo-session/com.lingmo.Session.xml b/startlingmo/lingmo-session/com.lingmo.Session.xml deleted file mode 100755 index eb52db4..0000000 --- a/startlingmo/lingmo-session/com.lingmo.Session.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp deleted file mode 100755 index 654d7b4..0000000 --- a/startlingmo/lingmo-session/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: revenmartin - * - * 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 . - */ - -#include "application.h" -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - QQuickWindow::setDefaultAlphaBuffer(true); - QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); - - QCoreApplication app(argc, argv); - - QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("Lingmo Session")); - parser.addHelpOption(); - - QCommandLineOption waylandOption(QStringList() << "w" - << "wayland" - << "Wayland Mode"); - parser.addOption(waylandOption); - parser.process(app); - - new Application(parser, &app); - - return app.exec(); -} diff --git a/startlingmo/lingmo-session/networkproxymanager.cpp b/startlingmo/lingmo-session/networkproxymanager.cpp deleted file mode 100755 index aba6301..0000000 --- a/startlingmo/lingmo-session/networkproxymanager.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: Reion Wong - * - * 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 . - */ - - -#include "networkproxymanager.h" -#include -#include -#include -#include - -NetworkProxyManager::NetworkProxyManager(QObject *parent) - : QObject(parent) - , m_settings(QSettings::UserScope, "lingmoos", "network") -{ -} - -void NetworkProxyManager::update() -{ - qunsetenv("HTTP_PROXY"); - qunsetenv("HTTPS_PROXY"); - qunsetenv("FTP_PROXY"); - qunsetenv("ALL_PROXY"); - qunsetenv("NO_PROXY"); - - qunsetenv("http_proxy"); - qunsetenv("https_proxy"); - qunsetenv("ftp_proxy"); - qunsetenv("all_proxy"); - qunsetenv("no_proxy"); - - m_settings.sync(); - - m_flag = m_settings.value("ProxyFlag", 0).toInt(); - m_useSameProxy = m_settings.value("UseSameProxy", false).toBool(); - m_scriptProxy = m_settings.value("ProxyScriptProxy", "").toString(); - m_httpProxy = m_settings.value("HttpProxy", "").toString(); - m_ftpProxy = m_settings.value("FtpProxy", "").toString(); - m_socksProxy = m_settings.value("SocksProxy", "").toString(); - m_httpProxyPort = m_settings.value("HttpProxyPort", "").toString(); - m_ftpProxyPort = m_settings.value("FtpProxyPort", "").toString(); - m_socksProxyPort = m_settings.value("SocksProxyPort", "").toString(); - - QMap dbusActivationEnv; - QStringList systemdUpdates; - - if (m_flag == 0) { - // No proxy - } else if (m_flag == 1) { - // Use proxy auto configuration URL - } else if (m_flag == 2) { - // Use manually specified proxy configuration - - QString httpProxy = QString("http://%1:%2/").arg(m_httpProxy).arg(m_httpProxyPort); - QString ftpProxy = QString("http://%1:%2/").arg(m_ftpProxy).arg(m_ftpProxyPort); - - if (m_useSameProxy) { - ftpProxy = httpProxy; - } - - if (!m_httpProxy.isEmpty() && !m_httpProxyPort.isEmpty()) { - qputenv("HTTP_PROXY", httpProxy.toLatin1()); - qputenv("HTTPS_PROXY", httpProxy.toLatin1()); - - qputenv("http_proxy", httpProxy.toLatin1()); - qputenv("https_proxy", httpProxy.toLatin1()); - } - - if (!m_ftpProxy.isEmpty() && !m_ftpProxyPort.isEmpty()) { - qputenv("FTP_PROXY", ftpProxy.toLatin1()); - qputenv("ftp_proxy", ftpProxy.toLatin1()); - } - - qputenv("NO_PROXY", "localhost,127.0.0.0/8,::1"); - qputenv("no_proxy", "localhost,127.0.0.0/8,::1"); - - if (!m_socksProxy.isEmpty() && !m_socksProxyPort.isEmpty()) { - // qputenv("ALL_PROXY", QString("socks://%1:%2/").arg(m_socksProxy).arg(m_socksProxyPort).toLatin1()); - // qputenv("all_proxy", QString("socks://%1:%2/").arg(m_socksProxy).arg(m_socksProxyPort).toLatin1()); - } - } -} diff --git a/startlingmo/lingmo-session/networkproxymanager.h b/startlingmo/lingmo-session/networkproxymanager.h deleted file mode 100755 index dbbd14c..0000000 --- a/startlingmo/lingmo-session/networkproxymanager.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: Reion Wong - * - * 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 . - */ - -#ifndef NETWORKPROXYMANAGER_H -#define NETWORKPROXYMANAGER_H - -#include -#include - -class NetworkProxyManager : public QObject -{ - Q_OBJECT - -public: - explicit NetworkProxyManager(QObject *parent = nullptr); - - void update(); - -private: - QSettings m_settings; - - int m_flag; - bool m_useSameProxy; - - QString m_scriptProxy; - QString m_httpProxy; - QString m_ftpProxy; - QString m_socksProxy; - - QString m_httpProxyPort; - QString m_ftpProxyPort; - QString m_socksProxyPort; -}; - -#endif // NETWORKPROXYMANAGER_H diff --git a/startlingmo/lingmo-session/powermanager/power.cpp b/startlingmo/lingmo-session/powermanager/power.cpp deleted file mode 100755 index 534b6be..0000000 --- a/startlingmo/lingmo-session/powermanager/power.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* BEGIN_COMMON_COPYRIGHT_HEADER - * Authors: - * Alexander Sokoloff - * - * This program or library is free software; you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - * - * END_COMMON_COPYRIGHT_HEADER */ - - -#include "power.h" -#include "powerproviders.h" - -#include -#include - -Power::Power(bool useSessionProvider, QObject * parent /*= nullptr*/) : - QObject(parent) -{ - m_providers.append(new SystemdProvider(this)); - m_providers.append(new UPowerProvider(this)); - m_providers.append(new ConsoleKitProvider(this)); -} - -Power::Power(QObject * parent /*= nullptr*/) - : Power(true, parent) -{ -} - -Power::~Power() -{ -} - -bool Power::canAction(Power::Action action) const -{ - for(const PowerProvider* provider : qAsConst(m_providers)) - if (provider->canAction(action)) - return true; - - return false; -} - -bool Power::doAction(Power::Action action) -{ - for(PowerProvider* provider : qAsConst(m_providers)) { - if (provider->canAction(action) && - provider->doAction(action)) { - return true; - } - } - return false; -} - -bool Power::canLogout() const { return canAction(PowerLogout); } -bool Power::canHibernate() const { return canAction(PowerHibernate); } -bool Power::canReboot() const { return canAction(PowerReboot); } -bool Power::canShutdown() const { return canAction(PowerShutdown); } -bool Power::canSuspend() const { return canAction(PowerSuspend); } -bool Power::canMonitorOff() const { return canAction(PowerMonitorOff); } -bool Power::canShowLeaveDialog() const { return canAction(PowerShowLeaveDialog); } - -bool Power::logout() { return doAction(PowerLogout); } -bool Power::hibernate() { return doAction(PowerHibernate); } -bool Power::reboot() { return doAction(PowerReboot); } -bool Power::shutdown() { return doAction(PowerShutdown); } -bool Power::suspend() { return doAction(PowerSuspend); } -bool Power::monitorOff() { return doAction(PowerMonitorOff); } -bool Power::showLeaveDialog() { return doAction(PowerShowLeaveDialog); } diff --git a/startlingmo/lingmo-session/powermanager/power.h b/startlingmo/lingmo-session/powermanager/power.h deleted file mode 100755 index 8583943..0000000 --- a/startlingmo/lingmo-session/powermanager/power.h +++ /dev/null @@ -1,118 +0,0 @@ -/* BEGIN_COMMON_COPYRIGHT_HEADER - * Authors: - * Alexander Sokoloff - * - * This program or library is free software; you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - * - * END_COMMON_COPYRIGHT_HEADER */ - - -#ifndef POWER_H -#define POWER_H - -#include -#include - -class PowerProvider; - -/*! Power class provides an interface to control system-wide power and session management. - It allows logout from the user session, hibernate, reboot, shutdown and suspend computer. - This is a wrapper class. All the real work is done in the PowerWorker classes. -*/ -class Power : public QObject -{ - Q_OBJECT - -public: - /// Power can perform next actions: - enum Action{ - PowerLogout, /// Close the current user session. - PowerHibernate, /// Hibernate the comupter - PowerReboot, /// Reboot the computer - PowerShutdown, /// Shutdown the computer - PowerSuspend, /// Suspend the computer - PowerMonitorOff, /// Turn off the monitor(s) - PowerShowLeaveDialog /// Show the lxqt-leave dialog - }; - - /*! - * Constructs the Power object. - * \param useLxqtSessionProvider indicates if the DBus methods - * provided by lxqt-session should be considered. This is useful to - * avoid recursion if the lxqt-session wants to provide some of the - * methods by itself with internal use of this object. - */ - explicit Power(bool useSessionProvider, QObject *parent = nullptr); - /// Constructs a Power with using the lxqt-session provider. - explicit Power(QObject *parent = nullptr); - - /// Destroys the object. - ~Power() override; - - /// Returns true if the Power can perform action. - bool canAction(Action action) const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerLogout). - bool canLogout() const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerHibernate). - bool canHibernate() const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerReboot). - bool canReboot() const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerShutdown). - bool canShutdown() const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerSuspend). - bool canSuspend() const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerMonitorOff). - bool canMonitorOff() const; - - //! This function is provided for convenience. It's equivalent to calling canAction(PowerShowLeaveDialog). - bool canShowLeaveDialog() const; - -public Q_SLOTS: - /// Performs the requested action. - bool doAction(Action action); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerLogout). - bool logout(); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerHibernate). - bool hibernate(); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerReboot). - bool reboot(); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerShutdown). - bool shutdown(); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerSuspend). - bool suspend(); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerMonitorOff). - bool monitorOff(); - - //! This function is provided for convenience. It's equivalent to calling doAction(PowerShowLeaveDialog). - bool showLeaveDialog(); - -private: - QList m_providers; -}; - -#endif diff --git a/startlingmo/lingmo-session/powermanager/powerproviders.cpp b/startlingmo/lingmo-session/powermanager/powerproviders.cpp deleted file mode 100755 index 63a702b..0000000 --- a/startlingmo/lingmo-session/powermanager/powerproviders.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/* BEGIN_COMMON_COPYRIGHT_HEADER - * Authors: - * Alexander Sokoloff - * Petr Vanek - * - * This program or library is free software; you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - * - * END_COMMON_COPYRIGHT_HEADER */ - - -#include "powerproviders.h" -#include -#include -#include -#include // for kill() - -#define UPOWER_SERVICE "org.freedesktop.UPower" -#define UPOWER_PATH "/org/freedesktop/UPower" -#define UPOWER_INTERFACE UPOWER_SERVICE - -#define CONSOLEKIT_SERVICE "org.freedesktop.ConsoleKit" -#define CONSOLEKIT_PATH "/org/freedesktop/ConsoleKit/Manager" -#define CONSOLEKIT_INTERFACE "org.freedesktop.ConsoleKit.Manager" - -#define SYSTEMD_SERVICE "org.freedesktop.login1" -#define SYSTEMD_PATH "/org/freedesktop/login1" -#define SYSTEMD_INTERFACE "org.freedesktop.login1.Manager" - -#define PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" - -/************************************************ - Helper func - ************************************************/ -void printDBusMsg(const QDBusMessage &msg) -{ - qWarning() << "** Dbus error **************************"; - qWarning() << "Error name " << msg.errorName(); - qWarning() << "Error msg " << msg.errorMessage(); - qWarning() << "****************************************"; -} - -/************************************************ - Helper func - ************************************************/ -static bool dbusCall(const QString &service, - const QString &path, - const QString &interface, - const QDBusConnection &connection, - const QString & method, - PowerProvider::DbusErrorCheck errorCheck = PowerProvider::CheckDBUS - ) -{ - QDBusInterface dbus(service, path, interface, connection); - - if (!dbus.isValid()) { - qWarning() << "dbusCall: QDBusInterface is invalid" << service << path << interface << method; - if (errorCheck == PowerProvider::CheckDBUS) - { - // Notification::notify( - // QObject::tr("Power Manager Error"), - // QObject::tr("QDBusInterface is invalid") + QStringLiteral("\n\n") + service + QStringLiteral(' ') + path + QStringLiteral(' ') + interface + QStringLiteral(' ') + method, - // QStringLiteral("logo.png")); - } - return false; - } - - QDBusMessage msg = dbus.call(method); - if (!msg.errorName().isEmpty()) { - printDBusMsg(msg); - if (errorCheck == PowerProvider::CheckDBUS) - { - // Notification::notify( - // QObject::tr("Power Manager Error (D-BUS call)"), - // msg.errorName() + QStringLiteral("\n\n") + msg.errorMessage(), - // QStringLiteral("logo.png")); - } - } - - // If the method no returns value, we believe that it was successful. - return msg.arguments().isEmpty() || - msg.arguments().constFirst().isNull() || - msg.arguments().constFirst().toBool(); -} - -/************************************************ - Helper func - - Just like dbusCall(), except that systemd - returns a string instead of a bool, and it takes - an "interactivity boolean" as an argument. - ************************************************/ -static bool dbusCallSystemd(const QString &service, - const QString &path, - const QString &interface, - const QDBusConnection &connection, - const QString &method, - bool needBoolArg, - PowerProvider::DbusErrorCheck errorCheck = PowerProvider::CheckDBUS - ) -{ - QDBusInterface dbus(service, path, interface, connection); - if (!dbus.isValid()) { - qWarning() << "dbusCall: QDBusInterface is invalid" << service << path << interface << method; - if (errorCheck == PowerProvider::CheckDBUS) { - // Notification::notify( - // QObject::tr("Power Manager Error"), - // QObject::tr("QDBusInterface is invalid") + QStringLiteral("\n\n") + service + QStringLiteral(' ') + path + QStringLiteral(' ')+ interface + QStringLiteral(' ') + method, - // QStringLiteral("logo.png")); - } - - return false; - } - - QDBusMessage msg = dbus.call(method, needBoolArg ? QVariant(true) : QVariant()); - - if (!msg.errorName().isEmpty()) { - printDBusMsg(msg); - if (errorCheck == PowerProvider::CheckDBUS) { - // Notification::notify( - // QObject::tr("Power Manager Error (D-BUS call)"), - // msg.errorName() + QStringLiteral("\n\n") + msg.errorMessage(), - // QStringLiteral("logo.png")); - } - } - - // If the method no returns value, we believe that it was successful. - if (msg.arguments().isEmpty() || msg.arguments().constFirst().isNull()) - return true; - - QString response = msg.arguments().constFirst().toString(); - qDebug() << "systemd:" << method << "=" << response; - return response == QStringLiteral("yes") || response == QStringLiteral("challenge"); -} - -/************************************************ - Helper func - ************************************************/ -bool dbusGetProperty(const QString &service, - const QString &path, - const QString &interface, - const QDBusConnection &connection, - const QString & property - ) -{ - QDBusInterface dbus(service, path, interface, connection); - if (!dbus.isValid()) - { - qWarning() << "dbusGetProperty: QDBusInterface is invalid" << service << path << interface << property; -// Notification::notify(QObject::tr("Power Manager"), -// "logo.png", -// QObject::tr("Power Manager Error"), -// QObject::tr("QDBusInterface is invalid")+ "\n\n" + service +" " + path +" " + interface +" " + property); - - return false; - } - - QDBusMessage msg = dbus.call(QStringLiteral("Get"), dbus.interface(), property); - - if (!msg.errorName().isEmpty()) - { - printDBusMsg(msg); -// Notification::notify(QObject::tr("Power Manager"), -// "logo.png", -// QObject::tr("Power Manager Error (Get Property)"), -// msg.errorName() + "\n\n" + msg.errorMessage()); - } - - return !msg.arguments().isEmpty() && - msg.arguments().constFirst().value().variant().toBool(); -} - -/************************************************ - PowerProvider - ************************************************/ -PowerProvider::PowerProvider(QObject *parent): - QObject(parent) -{ -} - -PowerProvider::~PowerProvider() -{ -} - -/************************************************ - UPowerProvider - ************************************************/ -UPowerProvider::UPowerProvider(QObject *parent): - PowerProvider(parent) -{ -} - -UPowerProvider::~UPowerProvider() -{ -} - -bool UPowerProvider::canAction(Power::Action action) const -{ - QString command; - QString property; - switch (action) { - case Power::PowerHibernate: - property = QStringLiteral("CanHibernate"); - command = QStringLiteral("HibernateAllowed"); - break; - case Power::PowerSuspend: - property = QStringLiteral("CanSuspend"); - command = QStringLiteral("SuspendAllowed"); - break; - default: - return false; - } - - return dbusGetProperty( // Whether the system is able to hibernate. - QStringLiteral(UPOWER_SERVICE), - QStringLiteral(UPOWER_PATH), - QStringLiteral(PROPERTIES_INTERFACE), - QDBusConnection::systemBus(), - property - ) - && - dbusCall( // Check if the caller has (or can get) the PolicyKit privilege to call command. - QStringLiteral(UPOWER_SERVICE), - QStringLiteral(UPOWER_PATH), - QStringLiteral(UPOWER_INTERFACE), - QDBusConnection::systemBus(), - command, - // canAction should be always silent because it can freeze - // g_main_context_iteration Qt event loop in QMessageBox - // on panel startup if there is no DBUS running. - PowerProvider::DontCheckDBUS - ); -} - -bool UPowerProvider::doAction(Power::Action action) -{ - QString command; - - switch (action) { - case Power::PowerHibernate: - command = QStringLiteral("Hibernate"); - break; - case Power::PowerSuspend: - command = QStringLiteral("Suspend"); - break; - default: - return false; - } - - - return dbusCall(QStringLiteral(UPOWER_SERVICE), - QStringLiteral(UPOWER_PATH), - QStringLiteral(UPOWER_INTERFACE), - QDBusConnection::systemBus(), - command ); -} - -/************************************************ - ConsoleKitProvider - ************************************************/ -ConsoleKitProvider::ConsoleKitProvider(QObject *parent): - PowerProvider(parent) -{ -} - -ConsoleKitProvider::~ConsoleKitProvider() -{ -} - -bool ConsoleKitProvider::canAction(Power::Action action) const -{ - QString command; - switch (action) { - case Power::PowerReboot: - command = QStringLiteral("CanReboot"); - break; - - case Power::PowerShutdown: - command = QStringLiteral("CanPowerOff"); - break; - - case Power::PowerHibernate: - command = QStringLiteral("CanHibernate"); - break; - - case Power::PowerSuspend: - command = QStringLiteral("CanSuspend"); - break; - - default: - return false; - } - - return dbusCallSystemd(QStringLiteral(CONSOLEKIT_SERVICE), - QStringLiteral(CONSOLEKIT_PATH), - QStringLiteral(CONSOLEKIT_INTERFACE), - QDBusConnection::systemBus(), - command, - false, - // canAction should be always silent because it can freeze - // g_main_context_iteration Qt event loop in QMessageBox - // on panel startup if there is no DBUS running. - PowerProvider::DontCheckDBUS - ); -} - -bool ConsoleKitProvider::doAction(Power::Action action) -{ - QString command; - switch (action) { - case Power::PowerReboot: - command = QStringLiteral("Reboot"); - break; - case Power::PowerShutdown: - command = QStringLiteral("PowerOff"); - break; - case Power::PowerHibernate: - command = QStringLiteral("Hibernate"); - break; - case Power::PowerSuspend: - command = QStringLiteral("Suspend"); - break; - default: - return false; - } - - return dbusCallSystemd(QStringLiteral(CONSOLEKIT_SERVICE), - QStringLiteral(CONSOLEKIT_PATH), - QStringLiteral(CONSOLEKIT_INTERFACE), - QDBusConnection::systemBus(), - command, - true); -} - -/************************************************ - SystemdProvider - - http://www.freedesktop.org/wiki/Software/systemd/logind - ************************************************/ - -SystemdProvider::SystemdProvider(QObject *parent): - PowerProvider(parent) -{ -} - -SystemdProvider::~SystemdProvider() -{ -} - -bool SystemdProvider::canAction(Power::Action action) const -{ - QString command; - - switch (action) { - case Power::PowerReboot: - command = QStringLiteral("CanReboot"); - break; - case Power::PowerShutdown: - command = QStringLiteral("CanPowerOff"); - break; - case Power::PowerSuspend: - command = QStringLiteral("CanSuspend"); - break; - case Power::PowerHibernate: - command = QStringLiteral("CanHibernate"); - break; - default: - return false; - } - - return dbusCallSystemd(QStringLiteral(SYSTEMD_SERVICE), - QStringLiteral(SYSTEMD_PATH), - QStringLiteral(SYSTEMD_INTERFACE), - QDBusConnection::systemBus(), - command, - false, - // canAction should be always silent because it can freeze - // g_main_context_iteration Qt event loop in QMessageBox - // on panel startup if there is no DBUS running. - PowerProvider::DontCheckDBUS - ); -} - -bool SystemdProvider::doAction(Power::Action action) -{ - QString command; - - switch (action) { - case Power::PowerReboot: - command = QStringLiteral("Reboot"); - break; - case Power::PowerShutdown: - command = QStringLiteral("PowerOff"); - break; - case Power::PowerSuspend: - command = QStringLiteral("Suspend"); - break; - case Power::PowerHibernate: - command = QStringLiteral("Hibernate"); - break; - default: - return false; - } - - return dbusCallSystemd(QStringLiteral(SYSTEMD_SERVICE), - QStringLiteral(SYSTEMD_PATH), - QStringLiteral(SYSTEMD_INTERFACE), - QDBusConnection::systemBus(), - command, - true - ); -} - -/************************************************ - HalProvider - ************************************************/ -HalProvider::HalProvider(QObject *parent): - PowerProvider(parent) -{ -} - -HalProvider::~HalProvider() -{ -} - -bool HalProvider::canAction(Power::Action action) const -{ - Q_UNUSED(action) - return false; -} - -bool HalProvider::doAction(Power::Action action) -{ - Q_UNUSED(action) - return false; -} diff --git a/startlingmo/lingmo-session/powermanager/powerproviders.h b/startlingmo/lingmo-session/powermanager/powerproviders.h deleted file mode 100755 index ec77cb8..0000000 --- a/startlingmo/lingmo-session/powermanager/powerproviders.h +++ /dev/null @@ -1,109 +0,0 @@ -/* BEGIN_COMMON_COPYRIGHT_HEADER - * Authors: - * Alexander Sokoloff - * - * This program or library is free software; you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - * - * END_COMMON_COPYRIGHT_HEADER */ - - -#ifndef POWERPROVIDERS_H -#define POWERPROVIDERS_H - -#include "power.h" - -#include -#include // for PID_T - -class PowerProvider: public QObject -{ - Q_OBJECT - -public: - enum DbusErrorCheck { - CheckDBUS, - DontCheckDBUS - }; - - explicit PowerProvider(QObject *parent = nullptr); - ~PowerProvider() override; - - /*! Returns true if the Power can perform action. - This is a pure virtual function, and must be reimplemented in subclasses. */ - virtual bool canAction(Power::Action action) const = 0 ; - -public Q_SLOTS: - /*! Performs the requested action. - This is a pure virtual function, and must be reimplemented in subclasses. */ - virtual bool doAction(Power::Action action) = 0; -}; - - -class UPowerProvider: public PowerProvider -{ - Q_OBJECT - -public: - UPowerProvider(QObject *parent = nullptr); - ~UPowerProvider() override; - bool canAction(Power::Action action) const override; - -public Q_SLOTS: - bool doAction(Power::Action action) override; -}; - - -class ConsoleKitProvider: public PowerProvider -{ - Q_OBJECT - -public: - ConsoleKitProvider(QObject *parent = nullptr); - ~ConsoleKitProvider() override; - bool canAction(Power::Action action) const override; - -public Q_SLOTS: - bool doAction(Power::Action action) override; -}; - - -class SystemdProvider: public PowerProvider -{ - Q_OBJECT - -public: - SystemdProvider(QObject *parent = nullptr); - ~SystemdProvider() override; - bool canAction(Power::Action action) const override; - -public Q_SLOTS: - bool doAction(Power::Action action) override; -}; - -class HalProvider: public PowerProvider -{ - Q_OBJECT - -public: - HalProvider(QObject *parent = nullptr); - ~HalProvider() override; - bool canAction(Power::Action action) const override; - -public Q_SLOTS: - bool doAction(Power::Action action) override; -}; - -#endif diff --git a/startlingmo/lingmo-session/process.cpp b/startlingmo/lingmo-session/process.cpp deleted file mode 100755 index 06d33a5..0000000 --- a/startlingmo/lingmo-session/process.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: revenmartin - * - * 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 . - */ - -#include "process.h" - -Process::Process(QObject *parent) - : QProcess(parent) -{ - QProcess::setProcessChannelMode(QProcess::ForwardedChannels); -} - -Process::~Process() -{ -} diff --git a/startlingmo/lingmo-session/process.h b/startlingmo/lingmo-session/process.h deleted file mode 100755 index 3ab3c57..0000000 --- a/startlingmo/lingmo-session/process.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: revenmartin - * - * 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 . - */ - -#ifndef PROCESS_H -#define PROCESS_H - -#include - -class Process : public QProcess -{ - Q_OBJECT - -public: - Process(QObject *parent = nullptr); - ~Process(); -}; - -#endif diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp deleted file mode 100755 index e38d70d..0000000 --- a/startlingmo/lingmo-session/processmanager.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2023-2024 Lingmo OS Team. - */ - -#include "processmanager.h" -#include "application.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "daemon-helper.h" -#include "debug.h" - -ProcessManager *s_self; - -ProcessManager::ProcessManager(Application *app, QObject *parent) - : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { - Q_ASSERT(!s_self); - s_self = this; - - qApp->installNativeEventFilter(this); -} - -ProcessManager::~ProcessManager() { - qApp->removeNativeEventFilter(this); - - QMapIterator i(m_systemProcess); - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - delete p; - m_systemProcess[i.key()] = nullptr; - } -} - -void ProcessManager::updateLaunchEnv(const QString &key, const QString &value) { - qputenv(key.toLatin1(), value.toLatin1()); -} - -void ProcessManager::start() { - startWindowManager(); - startDaemonProcess(); -} - -void ProcessManager::logout() { - QMapIterator i(m_systemProcess); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - p->terminate(); - } - i.toFront(); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { - p->kill(); - } - } - - QCoreApplication::exit(0); -} - -void ProcessManager::startWindowManager() { - auto detcted_wayland = - qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); - - if (detcted_wayland || m_app->wayland()) { - auto kwinWaylandJob = - new StartServiceJob(QStringLiteral("lingmo_kwin_wayland_wrapper"), - {QStringLiteral("--xwayland")}, - QStringLiteral("org.lingmo.KWinWrapper")); - kwinWaylandJob->setParent(this); - kwinWaylandJob->exec(); // Wait untill kwin_wayland_wrapper started - } else { - auto *wmProcess = new QProcess; - - wmProcess->start("kwin_x11", QStringList()); - } -} - -void ProcessManager::startDesktopProcess() { - // When the lingmo-settings-daemon theme module is loaded, start the desktop. - // In the way, there will be no problem that desktop and launcher can't get - // wallpaper. - - auto xcb_extra = QProcessEnvironment(); - xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); - const QVector sequence = { - new LaunchProcess(QStringLiteral("lingmo-notificationd"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-statusbar"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-dock"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-filemanager"), - QStringList("--desktop"), xcb_extra), - - new LaunchProcess(QStringLiteral("lingmo-launcher"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-powerman"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-clipboard"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-wallpaper-color-pick"), {}, - xcb_extra), - }; - Job *last = nullptr; - for (Job *job : sequence) { - if (!job) { - continue; - } - if (last) { - connect(last, &Job::finished, job, &Job::start); - } - last = job; - } - - // connect(sequence.last(), &Job::finished, this, &Startup::finishStartup); - sequence.first()->start(); -} - -void ProcessManager::startDaemonProcess() { - auto xcb_extra = QProcessEnvironment(); - xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); - const QVector sequence = { - new LaunchProcess(QStringLiteral("lingmo-settings-daemon"), {}, - xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-xembedsniproxy"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-gmenuproxy"), {}, xcb_extra), - new LaunchProcess(QStringLiteral("lingmo-permission-surveillance"), {}, - xcb_extra), - }; - Job *last = nullptr; - for (Job *job : sequence) { - if (!job) { - continue; - } - if (last) { - connect(last, &Job::finished, job, &Job::start); - } - last = job; - } - - connect(sequence.last(), &Job::finished, this, &ProcessManager::startDesktopProcess); - sequence.first()->start(); -} - -void ProcessManager::loadAutoStartProcess() { - QList> list; - - const QStringList dirs = QStandardPaths::locateAll( - QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), - QStandardPaths::LocateDirectory); - for (const QString &dir : dirs) { - const QDir d(dir); - const QStringList fileNames = - d.entryList(QStringList() << QStringLiteral("*.desktop")); - for (const QString &file : fileNames) { - QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); - desktop.setIniCodec("UTF-8"); - desktop.beginGroup("Desktop Entry"); - - // Ignore files the require a specific desktop environment - if (desktop.contains("NotShowIn")) { - const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); - if (notShowIn.contains("Lingmo")) - continue; - } - if (desktop.contains("OnlyShowIn")) { - const QStringList onlyShowIn = - desktop.value("OnlyShowIn").toStringList(); - if (!onlyShowIn.contains("Lingmo")) - continue; - } - - const QString execValue = desktop.value("Exec").toString(); - - // 避免冲突 - if (execValue.contains("gmenudbusmenuproxy")) - continue; - - // 使用 QProcess::splitCommand 来解析命令和参数 - QStringList args = QProcess::splitCommand(execValue); - - // 检查是否至少有一个元素(即程序路径) - if (!args.isEmpty()) { - auto program = args.first(); - args.removeFirst(); // 移除程序路径,剩下的都是参数 - - list << qMakePair(program, args); - } else { - qCWarning(LINGMO_SESSION_D) << "Invalid 'Exec' found in file!"; - } - } - } - - m_userAutoStartD = std::make_shared(list, false); -} - -bool ProcessManager::nativeEventFilter(const QByteArray &eventType, - void *message, long *result) { - if (eventType != "xcb_generic_event_t") // We only want to handle XCB events - return false; - - // ref: lxqt session - if (!m_wmStarted && m_waitLoop) { - // all window managers must set their name according to the spec - if (!QString::fromUtf8( - NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) - .wmName()) - .isEmpty()) { - qDebug() << "Window manager started"; - m_wmStarted = true; - if (m_waitLoop && m_waitLoop->isRunning()) - m_waitLoop->exit(); - - qApp->removeNativeEventFilter(this); - } - } - - return false; -} - -bool ProcessManager::startDetached(QProcess *process) { - process->setProcessChannelMode(QProcess::ForwardedChannels); - process->start(); - const bool ret = process->waitForStarted(); - if (ret) { - m_processes << process; - } - return ret; -} - -StartProcessJob::StartProcessJob(const QString &process, - const QStringList &args, - const QProcessEnvironment &additionalEnv) - : Job(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); - - connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, - SLOT(finished(int, QProcess::ExitStatus))); -} - -void StartProcessJob::start() { - qCDebug(LINGMO_SESSION_D) - << "Starting " << m_process->program() << m_process->arguments(); - - m_process->start(); -} - -void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { - qCDebug(LINGMO_SESSION_D) << "process job " << m_process->program() - << "finished with exit code " << exitCode; - emitResult(); -} - -StartServiceJob::StartServiceJob(const QString &process, - const QStringList &args, - const QString &serviceId, - const QProcessEnvironment &additionalEnv) - : Job(), m_process(new QProcess), m_serviceId(serviceId), - m_additionalEnv(additionalEnv) { - m_process->setProgram(process); - m_process->setArguments(args); - - auto watcher = - new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), - QDBusServiceWatcher::WatchForRegistration, this); - connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, - &StartServiceJob::emitResult); -} - -void StartServiceJob::start() { - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(m_additionalEnv); - m_process->setProcessEnvironment(env); - - if (!m_serviceId.isEmpty() && - QDBusConnection::sessionBus().interface()->isServiceRegistered( - m_serviceId)) { - qCDebug(LINGMO_SESSION_D) << m_process << "already running"; - emitResult(); - return; - } - qDebug() << "Starting " << m_process->program() << m_process->arguments(); - if (!ProcessManager::self()->startDetached(m_process)) { - qCWarning(LINGMO_SESSION_D) - << "error starting process" << m_process->program() - << m_process->arguments(); - emitResult(); - } - - if (m_serviceId.isEmpty()) { - emitResult(); - } -} - -LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv) - : Job(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); -} - -void LaunchProcess::start() { - qCDebug(LINGMO_SESSION_D) - << "Starting " << m_process->program() << m_process->arguments(); - - m_process->startDetached(); - - emitResult(); -} \ No newline at end of file diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h deleted file mode 100755 index fbb4d74..0000000 --- a/startlingmo/lingmo-session/processmanager.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2023-2024 LingmoOS Team. - * - * Author: revenmartin - * - * 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 . - */ - -#ifndef PROCESSMANAGER_H -#define PROCESSMANAGER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "daemon-helper.h" - -class Application; -class ProcessManager; - -extern ProcessManager *s_self; - -class ProcessManager : public QObject, public QAbstractNativeEventFilter { - Q_OBJECT -public: - explicit ProcessManager(Application *app, QObject *parent = nullptr); - ~ProcessManager(); - - static ProcessManager *self() { - Q_ASSERT(s_self); - return s_self; - } - - void start(); - void logout(); - - void startWindowManager(); - void startDesktopProcess(); - void startDaemonProcess(); - - /** - * @brief Start the user defined autostart process. - * Typically, they are in /.config/autostart/xxx.desktop - */ - void loadAutoStartProcess(); - - bool nativeEventFilter(const QByteArray &eventType, void *message, - long *result) override; - - void updateLaunchEnv(const QString &key, const QString &value); - - bool startDetached(QProcess *process); - -private: - Application *m_app; - QMap m_systemProcess; - QMap m_autoStartProcess; - QVector m_processes; - - // Daemon helper for desktop components - std::shared_ptr m_desktopAutoStartD; - - // Daemon helper for other daemon components - std::shared_ptr m_daemonAutoStartD; - - // Daemon helper for User Auto Start Process - std::shared_ptr m_userAutoStartD; - - bool m_wmStarted; - QEventLoop *m_waitLoop; -}; - -using namespace LINGMO_SESSION; -/** - * Launches a process, and waits for the process to start - */ -class LaunchProcess : public Job { - Q_OBJECT -public: - LaunchProcess( - const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - void start() override; - -private: - QProcess *m_process; -}; - -/** - * Launches a process, and waits for the process to finish - */ -class StartProcessJob : public Job { - Q_OBJECT -public: - StartProcessJob( - const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - void start() override; - -public Q_SLOTS: - void finished(int exitCode, QProcess::ExitStatus e); - -private: - QProcess *m_process; -}; - -/** - * Launches a process, and waits for the service to appear on the session bus - */ -class StartServiceJob : public Job { - Q_OBJECT -public: - StartServiceJob( - const QString &process, const QStringList &args, const QString &serviceId, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - - void start() override; - -private: - QProcess *m_process; - const QString m_serviceId; - const QProcessEnvironment m_additionalEnv; -}; -#endif // PROCESSMANAGER_H diff --git a/startlingmo/lingmo-sourceenv.sh b/startlingmo/lingmo-sourceenv.sh deleted file mode 100644 index 4a00f15..0000000 --- a/startlingmo/lingmo-sourceenv.sh +++ /dev/null @@ -1,7 +0,0 @@ -for i in $@ -do - . $i >/dev/null -done - -# env may not support -0, fall back to GNU env -env -0 2>/dev/null || genv -0 diff --git a/startlingmo/lingmo-wayland-session.desktop b/startlingmo/lingmo-wayland-session.desktop deleted file mode 100755 index 4c1c412..0000000 --- a/startlingmo/lingmo-wayland-session.desktop +++ /dev/null @@ -1,6 +0,0 @@ -[Desktop Entry] -Type=Application -Exec=startlingmo-wayland -Name=Lingmo Desktop (Wayland Experimental) -Keywords=session -Comment=session diff --git a/startlingmo/lingmo-xorg-session.desktop b/startlingmo/lingmo-xorg-session.desktop deleted file mode 100755 index 7df13bc..0000000 --- a/startlingmo/lingmo-xorg-session.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Type=Application -Exec=startlingmo-x11 -TryExec=startlingmo-x11 -Name=Lingmo Desktop (Xorg Session) -Keywords=session -Comment=session diff --git a/startlingmo/signalhandler.cpp b/startlingmo/signalhandler.cpp deleted file mode 100644 index 9e96c58..0000000 --- a/startlingmo/signalhandler.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#include "signalhandler.h" -#include "debug.h" -#include "startlingmo.hpp" - -#include -#include -#include -#include - -int SignalHandler::signalFd[2]; - -SignalHandler::SignalHandler() -{ - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { - qCWarning(LINGMO_STARTUP) << "Couldn't create a socketpair"; - return; - } - - m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); - connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); -} - -SignalHandler::~SignalHandler() -{ - for (int sig : std::as_const(m_signalsRegistered)) { - signal(sig, nullptr); - } - close(signalFd[0]); - close(signalFd[1]); -} - -void SignalHandler::addSignal(int signalToTrack) -{ - m_signalsRegistered.insert(signalToTrack); - signal(signalToTrack, signalHandler); -} - -void SignalHandler::signalHandler(int signal) -{ - ::write(signalFd[0], &signal, sizeof(signal)); -} - -void SignalHandler::handleSignal() -{ - m_handler->setEnabled(false); - int signal; - ::read(signalFd[1], &signal, sizeof(signal)); - m_handler->setEnabled(true); - - Q_EMIT signalReceived(signal); -} - -SignalHandler *SignalHandler::self() -{ - static SignalHandler s_self; - return &s_self; -} diff --git a/startlingmo/signalhandler.h b/startlingmo/signalhandler.h deleted file mode 100644 index c0f8428..0000000 --- a/startlingmo/signalhandler.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#pragma once - -#include -#include -#include - -/** - * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop - * - * It's a singleton as it relies on static data getting defined. - */ -class SignalHandler : public QObject -{ - Q_OBJECT -public: - ~SignalHandler() override; - void addSignal(int signal); - - static SignalHandler *self(); - -Q_SIGNALS: - void signalReceived(int signal); - -private: - SignalHandler(); - void handleSignal(); - static void signalHandler(int signal); - - QSet m_signalsRegistered; - static int signalFd[2]; - QSocketNotifier *m_handler = nullptr; -}; diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp deleted file mode 100644 index 095e249..0000000 --- a/startlingmo/startlingmo-wayland.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#include -#include -#include -#include -#include - -#include "startlingmo.hpp" - -#include "debug.h" - -#include "signal.h" - -extern QTextStream out; - -int main(int argc, char **argv) { - - QCoreApplication app(argc, argv); - - createConfigDirectory(); - setupCursor(true); - signal(SIGTERM, sigtermHandler); - - // Let clients try to reconnect to kwin after a restart - qputenv("QT_WAYLAND_RECONNECT", "1"); - - // Query whether org.freedesktop.locale1 is available. If it is, try to - // set XKB_DEFAULT_{MODEL,LAYOUT,VARIANT,OPTIONS} accordingly. - { - const QString locale1Service = QStringLiteral("org.freedesktop.locale1"); - const QString locale1Path = QStringLiteral("/org/freedesktop/locale1"); - QDBusMessage message = QDBusMessage::createMethodCall( - locale1Service, locale1Path, - QStringLiteral("org.freedesktop.DBus.Properties"), - QStringLiteral("GetAll")); - message << locale1Service; - QDBusMessage resultMessage = QDBusConnection::systemBus().call(message); - if (resultMessage.type() == QDBusMessage::ReplyMessage) { - QVariantMap result; - QDBusArgument dbusArgument = - resultMessage.arguments().at(0).value(); - while (!dbusArgument.atEnd()) { - dbusArgument >> result; - } - - auto queryAndSet = [&result](const char *var, const QString &value) { - const auto r = result.value(value).toString(); - if (!r.isEmpty()) - qputenv(var, r.toUtf8()); - }; - - queryAndSet("XKB_DEFAULT_MODEL", QStringLiteral("X11Model")); - queryAndSet("XKB_DEFAULT_LAYOUT", QStringLiteral("X11Layout")); - queryAndSet("XKB_DEFAULT_VARIANT", QStringLiteral("X11Variant")); - queryAndSet("XKB_DEFAULT_OPTIONS", QStringLiteral("X11Options")); - } else { - qCWarning(LINGMO_STARTUP) - << "No valid reply from org.freedesktop.locale1" << resultMessage; - } - } - - runEnvironmentScripts(); - - if (!qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) { - out << "startplasmacompositor: Could not start D-Bus. Can you call " - "qdbus?\n"; - return 1; - } - - setupLingmoEnvironment(); - initLanguage(); - initScreenScaleFactors(); - - qputenv("XDG_SESSION_TYPE", "wayland"); - qputenv("QT_QPA_PLATFORM", "wayland"); - - auto oldSystemdEnvironment = getSystemdEnvironment(); - if (!syncDBusEnvironment()) { - out << "Could not sync environment to dbus.\n"; - return 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_*) - importSystemdEnvrionment(); - - startLingmoSession(true); - - return 0; -} \ No newline at end of file diff --git a/startlingmo/startlingmo-x11.cpp b/startlingmo/startlingmo-x11.cpp deleted file mode 100644 index 4084f0f..0000000 --- a/startlingmo/startlingmo-x11.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#include "signal.h" -#include "startlingmo.hpp" - -#include - -#include - -void sighupHandler(int) { std::cout << "GOT SIGHUP\n"; } - -int main(int argc, char *argv[]) { - - QCoreApplication app(argc, argv); - - // When the X server dies we get a HUP signal from xinit. We must ignore it - // because we still need to do some cleanup. - signal(SIGHUP, sighupHandler); - - qputenv("QT_NO_XDG_DESKTOP_PORTAL", QByteArrayLiteral("1")); - - // ToDo: Maybe we can check wether lingmo de is already running? - - createConfigDirectory(); - initLanguage(); - initScreenScaleFactors(); - - // NOTE: Be very mindful of what you start this early in the process. The - // environment is not yet complete. - setupCursor(false); - - runEnvironmentScripts(); - - std::cout << "Starting lingmo de ...\n"; - - setupLingmoEnvironment(); - - qunsetenv("QT_NO_XDG_DESKTOP_PORTAL"); - auto oldSystemdEnvironment = getSystemdEnvironment(); - if (!syncDBusEnvironment()) { - // Startup error - messageBox(QStringLiteral("Could not sync environment to dbus.\n")); - return 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_*) - importSystemdEnvrionment(); - - if (!startLingmoSession(false)) - return 1; - - std::cout << "Shutting down lingmo de ...\n"; - - cleanupPlasmaEnvironment(oldSystemdEnvironment); - - return 0; -} \ No newline at end of file diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp deleted file mode 100644 index bf0253b..0000000 --- a/startlingmo/startlingmo.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#include "startlingmo.hpp" -#include "config-startlingmo.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" - -#include "UpdateLaunchEnvironment.hpp" - -extern QTextStream out; -QTextStream out(stderr); - -void sigtermHandler(int signalNumber) { - Q_UNUSED(signalNumber) - if (QCoreApplication::instance()) { - QCoreApplication::instance()->exit(-1); - } -} - -QStringList allServices(const QLatin1String &prefix) { - const QStringList services = - QDBusConnection::sessionBus().interface()->registeredServiceNames(); - QStringList names; - - std::copy_if(services.cbegin(), services.cend(), std::back_inserter(names), - [&prefix](const QString &serviceName) { - return serviceName.startsWith(prefix); - }); - - return names; -} - -void gentleTermination(QProcess *p) { - if (p->state() != QProcess::Running) { - return; - } - - p->terminate(); - - // Wait longer for a session than a greeter - if (!p->waitForFinished(5000)) { - p->kill(); - if (!p->waitForFinished(5000)) { - qCWarning(LINGMO_STARTUP) - << "Could not fully finish the process" << p->program(); - } - } -} - -int 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); - - QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, - &p, [&p] { gentleTermination(&p); }); - // qCDebug(LINGMO_STARTUP) << "started..." << program << args; - p.waitForFinished(-1); - if (p.exitCode()) { - qCWarning(LINGMO_STARTUP) - << program << args << "exited with code" << p.exitCode(); - } - return p.exitCode(); -} - -void createConfigDirectory() { - const QString configDir = - QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); - if (!QDir().mkpath(configDir)) - out << "Could not create config directory XDG_CONFIG_HOME: " << configDir - << '\n'; -} - -void setupCursor(bool wayland) { -#ifdef XCURSOR_PATH - QByteArray path(XCURSOR_PATH); - path.replace("$XCURSOR_PATH", qgetenv("XCURSOR_PATH")); - qputenv("XCURSOR_PATH", path); -#endif - - // TODO: consider linking directly - if (!wayland) { - QSettings settings(QSettings::UserScope, "lingmoos", "theme"); - qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); - QString cursorTheme = settings.value("CursorTheme", "default").toString(); - int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; - runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); - } -} - -// Source scripts found in /lingmo-workspace/env/*.sh -// (where correspond to the system and user's configuration -// directory. -// -// Scripts are sourced in reverse order of priority of their directory, as -// defined by `QStandardPaths::standardLocations`. This ensures that -// high-priority scripts (such as those in the user's home directory) are -// sourced last and take precedence over lower-priority scripts (such as system -// defaults). Scripts in the same directory are sourced in lexical order of -// their filename. -// -// This is where you can define environment variables that will be available to -// all KDE programs, so this is where you can run agents using e.g. eval -// `ssh-agent` or eval `gpg-agent --daemon`. Note: if you do that, you should -// also put "ssh-agent -k" as a shutdown script -// -// (see end of this file). -// For anything else (that doesn't set env vars, or that needs a window -// manager), better use the Autostart folder. -void runEnvironmentScripts() { - QStringList scripts; - auto locations = - QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); - - //`standardLocations()` returns locations sorted by "order of priority". We - // iterate in reverse - // order so that high-priority scripts are sourced last and their - // modifications take precedence. - for (auto loc = locations.crbegin(); loc != locations.crend(); loc++) { - QDir dir(*loc); - if (!dir.cd(QStringLiteral("./lingmo-workspace/env"))) { - // Skip location if lingmo-workspace/env subdirectory does not exist - continue; - } - const auto dirScripts = - dir.entryInfoList({QStringLiteral("*.sh")}, QDir::Files, QDir::Name); - for (const auto &script : dirScripts) { - scripts << script.absoluteFilePath(); - } - } - sourceFiles(scripts); -} - -void sourceFiles(const QStringList &files) { - QStringList filteredFiles; - std::copy_if(files.begin(), files.end(), std::back_inserter(filteredFiles), - [](const QString &i) { return QFileInfo(i).isReadable(); }); - - if (filteredFiles.isEmpty()) - return; - - filteredFiles.prepend( - QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/lingmo-sourceenv.sh")); - - QProcess p; - p.start(QStringLiteral("/bin/sh"), filteredFiles); - p.waitForFinished(-1); - - const auto fullEnv = p.readAllStandardOutput(); - auto envs = fullEnv.split('\0'); - - for (auto &env : envs) { - const int idx = env.indexOf('='); - if (Q_UNLIKELY(idx <= 0)) { - continue; - } - - const auto name = env.left(idx); - if (isShellVariable(name)) { - continue; - } - setEnvironmentVariable(name, env.mid(idx + 1)); - } -} - -bool isShellVariable(const QByteArray &name) { - return name == "_" || name == "SHELL" || name.startsWith("SHLVL"); -} - -void setEnvironmentVariable(const QByteArray &name, const QByteArray &value) { - if (qgetenv(name) != value) { - qputenv(name, value); - } -} - -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 setupLingmoEnvironment() { - // Manually disable auto scaling because we are scaling above - // otherwise apps that manually opt in for high DPI get auto scaled by the - // developer AND manually scaled by us - qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); - - // 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", "Lingmo"); - qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); - qputenv("XDG_SESSION_DESKTOP", "Lingmo"); - - // Qt - // qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); - // qputenv("QT_PLATFORM_PLUGIN", "lingmo"); - - // ref: - // https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping - qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); - - qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); - - // 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"); -} - -std::optional getSystemdEnvironment() { - 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()) { - return std::nullopt; - } - auto variant = qdbus_cast(arguments[0]); - if (variant.type() != QVariant::StringList) { - return std::nullopt; - } - - const auto assignmentList = variant.toStringList(); - QProcessEnvironment ret; - for (auto &env : assignmentList) { - const int idx = env.indexOf(QLatin1Char('=')); - if (Q_LIKELY(idx > 0)) { - ret.insert(env.left(idx), env.mid(idx + 1)); - } - } - - return ret; -} - -// Drop session-specific variables from the systemd environment. -// Those can be leftovers from previous sessions, which can interfere with the -// session we want to start now, e.g. $DISPLAY might break kwin_wayland. -static void dropSessionVarsFromSystemdEnvironment() { - const auto environment = getSystemdEnvironment(); - if (!environment) { - return; - } - - QStringList varsToDrop; - for (auto &nameStr : environment.value().keys()) { - // If it's set in this process, it'll be overwritten by the following - // UpdateLaunchEnvJob - const auto name = nameStr.toLocal8Bit(); - if (!qEnvironmentVariableIsSet(name) && isSessionVariable(name)) { - varsToDrop.append(nameStr); - } - } - - auto msg = QDBusMessage::createMethodCall( - QStringLiteral("org.freedesktop.systemd1"), - QStringLiteral("/org/freedesktop/systemd1"), - QStringLiteral("org.freedesktop.systemd1.Manager"), - QStringLiteral("UnsetEnvironment")); - msg << varsToDrop; - auto reply = QDBusConnection::sessionBus().call(msg); - if (reply.type() == QDBusMessage::ErrorMessage) { - qCWarning(LINGMO_STARTUP) - << "Failed to unset systemd environment variables:" << reply.errorName() - << reply.errorMessage(); - } -} - -// kwin_wayland can possibly also start dbus-activated services which need env -// variables. In that case, the update in startplasma might be too late. -bool syncDBusEnvironment() { - dropSessionVarsFromSystemdEnvironment(); - - // Shell variables are filtered out of things we explicitly load, but they - // still might have been inherited from the parent process - QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); - for (auto &name : environment.keys()) { - if (isShellVariable(name.toLocal8Bit())) { - environment.remove(name); - } - } - - // At this point all environment variables are set, let's send it to the DBus - // session server to update the activation environment - auto job = new UpdateLaunchEnvironmentJob(environment); - QEventLoop e; - QObject::connect(job, &UpdateLaunchEnvironmentJob::finished, &e, - &QEventLoop::quit); - e.exec(); - return true; -} - -void initLanguage() { - QSettings settings(QSettings::UserScope, "lingmoos", "language"); - QString value = settings.value("language", "").toString(); - - // Init Language - if (value.isEmpty()) { - QFile file("/etc/locale.gen"); - if (file.open(QIODevice::ReadOnly)) { - QStringList lines = QString(file.readAll()).split('\n'); - - for (const QString &line : lines) { - if (line.startsWith('#')) - continue; - - if (line.trimmed().isEmpty()) - continue; - - value = line.split(' ').first().split('.').first(); - } - } - } - - if (value.isEmpty()) - value = "en_US"; - - settings.setValue("language", value); - - 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()); - } -} - -// Import systemd user environment. -// -// Systemd read ~/.config/environment.d which applies to all systemd user unit. -// But it won't work if plasma is not started by systemd. -void importSystemdEnvrionment() { - const auto environment = getSystemdEnvironment(); - if (!environment) { - return; - } - - for (auto &nameStr : environment.value().keys()) { - const auto name = nameStr.toLocal8Bit(); - if (!isShellVariable(name) && !isSessionVariable(name)) { - setEnvironmentVariable(name, - environment.value().value(nameStr).toLocal8Bit()); - } - } -} - -// If something went on an endless restart crash loop it will get blacklisted, -// as this is a clean login we will want to reset those counters This is -// independent of whether we use the Plasma systemd boot -void resetSystemdFailedUnits() { - QDBusMessage message = QDBusMessage::createMethodCall( - QStringLiteral("org.freedesktop.systemd1"), - QStringLiteral("/org/freedesktop/systemd1"), - QStringLiteral("org.freedesktop.systemd1.Manager"), - QStringLiteral("ResetFailed")); - QDBusConnection::sessionBus().call(message); -} - -// Reload systemd to make sure the current configuration is active, which also -// reruns generators. Needed for e.g. XDG autostart changes to become effective. -void reloadSystemd() { - QDBusMessage message = QDBusMessage::createMethodCall( - QStringLiteral("org.freedesktop.systemd1"), - QStringLiteral("/org/freedesktop/systemd1"), - QStringLiteral("org.freedesktop.systemd1.Manager"), - QStringLiteral("Reload")); - QDBusConnection::sessionBus().call(message); -} - -bool startLingmoSession(bool wayland) { - resetSystemdFailedUnits(); - reloadSystemd(); - - bool rc = true; - QEventLoop e; - - std::unique_ptr startLingmoSession; - - QStringList lingmoSessionOptions; - - if (wayland) { - lingmoSessionOptions << QStringLiteral("--wayland"); - } - - { - startLingmoSession.reset(new QProcess); - qCDebug(LINGMO_STARTUP) << "Using classic boot"; - - startLingmoSession->setProcessChannelMode(QProcess::ForwardedChannels); - - startLingmoSession->start( - QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/lingmo_session"), - lingmoSessionOptions); - } - - if (rc) { - QObject::connect(QCoreApplication::instance(), - &QCoreApplication::aboutToQuit, &e, &QEventLoop::quit); - e.exec(); - } - return rc; -} - -void initScreenScaleFactors() { - QSettings settings(QSettings::UserScope, "lingmoos", "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)); - } -} - -void messageBox(const QString &text) { - out << text; - runSync(QStringLiteral("xmessage"), - {QStringLiteral("-geometry"), QStringLiteral("500x100"), text}); -} - -void cleanupPlasmaEnvironment( - const std::optional &oldSystemdEnvironment) { - - if (!oldSystemdEnvironment) { - return; - } - - auto currentEnv = getSystemdEnvironment(); - if (!currentEnv) { - return; - } - - // According to systemd documentation: - // If a variable is listed in both, the variable is set after this method - // returns, i.e. the set list overrides the unset list. So this will - // effectively restore the state to the values in oldSystemdEnvironment. - QDBusMessage message = QDBusMessage::createMethodCall( - QStringLiteral("org.freedesktop.systemd1"), - QStringLiteral("/org/freedesktop/systemd1"), - QStringLiteral("org.freedesktop.systemd1.Manager"), - QStringLiteral("UnsetAndSetEnvironment")); - message.setArguments({currentEnv.value().keys(), - oldSystemdEnvironment.value().toStringList()}); - - // The session program gonna quit soon, ensure the message is flushed. - auto reply = QDBusConnection::sessionBus().asyncCall(message); - reply.waitForFinished(); -} diff --git a/startlingmo/startlingmo.hpp b/startlingmo/startlingmo.hpp deleted file mode 100644 index ce89353..0000000 --- a/startlingmo/startlingmo.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - SPDX-FileCopyrightText: 2024 Elysia - SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez - - SPDX-License-Identifier: GPL-3.0-or-later -*/ -#ifndef STARTLINGMO_HPP -#define STARTLINGMO_HPP - -#include -#include -#include -#include - -void sigtermHandler(int signalNumber); - -QStringList allServices(const QLatin1String &prefix); - -void gentleTermination(QProcess *process); - -int runSync(const QString &program, const QStringList &args, - const QStringList &env = {}); - -void createConfigDirectory(); - -void setupCursor(bool wayland); - -void runEnvironmentScripts(); - -void sourceFiles(const QStringList &files); - -bool isShellVariable(const QByteArray &name); - -void setEnvironmentVariable(const QByteArray &name, const QByteArray &value); - -void setupLingmoEnvironment(); - -std::optional getSystemdEnvironment(); - -bool syncDBusEnvironment(); - -void initLanguage(); - -void importSystemdEnvrionment(); - -bool startLingmoSession(bool wayland); - -struct KillBeforeDeleter { - void operator()(QProcess *pointer) { - if (pointer) { - gentleTermination(pointer); - } - delete pointer; - } -}; - -bool isSessionVariable(const QByteArray &name); - -void initScreenScaleFactors(); - -void messageBox(const QString &text); - -void cleanupPlasmaEnvironment( - const std::optional &oldSystemdEnvironment); - -#endif // STARTLINGMO_HPP \ No newline at end of file diff --git a/startlingmo/wayland_wrapper/CMakeLists.txt b/startlingmo/wayland_wrapper/CMakeLists.txt deleted file mode 100644 index aa0f511..0000000 --- a/startlingmo/wayland_wrapper/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -add_subdirectory(lib) - -add_executable(lingmo_kwin_wayland_wrapper) -target_sources(lingmo_kwin_wayland_wrapper PRIVATE - kwin_wrapper.cpp - wl-socket.c -) - -ecm_qt_declare_logging_category(lingmo_kwin_wayland_wrapper - HEADER - wrapper_logging.h - IDENTIFIER - KWIN_WRAPPER - CATEGORY_NAME - lingmo_kwin_wayland_wrapper - DEFAULT_SEVERITY - Warning -) - -target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KWinXwaylandCommon startlingmo) -target_include_directories(lingmo_kwin_wayland_wrapper PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) -set_property(TARGET lingmo_kwin_wayland_wrapper PROPERTY C_STANDARD 11) -install(TARGETS lingmo_kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/startlingmo/wayland_wrapper/kwin_wrapper.cpp b/startlingmo/wayland_wrapper/kwin_wrapper.cpp deleted file mode 100644 index bf52c64..0000000 --- a/startlingmo/wayland_wrapper/kwin_wrapper.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2020 - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -/** - * This tiny executable creates a socket, then starts kwin passing it the FD to the wayland socket - * along with the name of the socket to use - * On any non-zero kwin exit kwin gets restarted. - * - * After restart kwin is relaunched but now with the KWIN_RESTART_COUNT env set to an incrementing counter - * - * It's somewhat akin to systemd socket activation, but we also need the lock file, finding the next free socket - * and so on, hence our own binary. - * - * Usage kwin_wayland_wrapper [argForKwin] [argForKwin] ... - */ - -#include -#include -#include -#include -#include - -#include "signalhandler.h" -#include "UpdateLaunchEnvironment.hpp" - -#include -#include -#include - -#include "wl-socket.h" -#include "wrapper_logging.h" -#include "xauthority.h" -#include "xwaylandsocket.h" - -class KWinWrapper : public QObject -{ - Q_OBJECT -public: - KWinWrapper(QObject *parent); - ~KWinWrapper(); - void run(); - -private: - wl_socket *m_socket; - - int m_crashCount = 0; - QProcess *m_kwinProcess = nullptr; - - std::unique_ptr m_xwlSocket; - QTemporaryFile m_xauthorityFile; -}; - -KWinWrapper::KWinWrapper(QObject *parent) - : QObject(parent) - , m_kwinProcess(new QProcess(this)) -{ - m_socket = wl_socket_create(); - if (!m_socket) { - qFatal("Could not create wayland socket"); - } - - if (qApp->arguments().contains(QLatin1String("--xwayland"))) { - m_xwlSocket.reset(new KWin::XwaylandSocket(KWin::XwaylandSocket::OperationMode::TransferFdsOnExec)); - if (!m_xwlSocket->isValid()) { - qCWarning(KWIN_WRAPPER) << "Failed to create Xwayland connection sockets"; - m_xwlSocket.reset(); - } - if (m_xwlSocket) { - if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) { - if (!generateXauthorityFile(m_xwlSocket->display(), &m_xauthorityFile)) { - qCWarning(KWIN_WRAPPER) << "Failed to create an Xauthority file"; - } - } - } - } -} - -KWinWrapper::~KWinWrapper() -{ - wl_socket_destroy(m_socket); - if (m_kwinProcess) { - disconnect(m_kwinProcess, nullptr, this, nullptr); - m_kwinProcess->terminate(); - m_kwinProcess->waitForFinished(); - m_kwinProcess->kill(); - m_kwinProcess->waitForFinished(); - } -} - -void KWinWrapper::run() -{ - m_kwinProcess->setProgram("kwin_wayland"); - - QStringList args; - - args << "--wayland-fd" << QString::number(wl_socket_get_fd(m_socket)); - args << "--socket" << QString::fromUtf8(wl_socket_get_display_name(m_socket)); - - if (m_xwlSocket) { - const auto xwaylandFileDescriptors = m_xwlSocket->fileDescriptors(); - for (const int &fileDescriptor : xwaylandFileDescriptors) { - args << "--xwayland-fd" << QString::number(fileDescriptor); - } - args << "--xwayland-display" << m_xwlSocket->name(); - if (m_xauthorityFile.open()) { - args << "--xwayland-xauthority" << m_xauthorityFile.fileName(); - } - } - - // attach our main process arguments - // the first entry is dropped as it will be our program name - args << qApp->arguments().mid(1); - - m_kwinProcess->setProcessChannelMode(QProcess::ForwardedChannels); - m_kwinProcess->setArguments(args); - - connect(m_kwinProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { - if (exitCode == 0) { - qApp->quit(); - return; - } else if (exitCode == 133) { - m_crashCount = 0; - } else { - m_crashCount++; - } - - if (m_crashCount > 10) { - qApp->quit(); - return; - } - qputenv("KWIN_RESTART_COUNT", QByteArray::number(m_crashCount)); - // restart - m_kwinProcess->start(); - }); - - m_kwinProcess->start(); - - QProcessEnvironment env; - env.insert("WAYLAND_DISPLAY", QString::fromUtf8(wl_socket_get_display_name(m_socket))); - if (m_xwlSocket) { - env.insert("DISPLAY", m_xwlSocket->name()); - if (m_xauthorityFile.open()) { - env.insert("XAUTHORITY", m_xauthorityFile.fileName()); - } - } - - auto envSyncJob = new UpdateLaunchEnvironmentJob(env); - connect(envSyncJob, &UpdateLaunchEnvironmentJob::finished, this, []() { - // The service name is merely there to indicate to the world that we're up and ready with all envs exported - qCDebug(KWIN_WRAPPER) << "KWinWrapper is ready"; - QDBusConnection::sessionBus().registerService(QStringLiteral("org.lingmo.KWinWrapper")); - }); -} - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - app.setQuitLockEnabled(false); // don't exit when the first KJob finishes - - SignalHandler::self()->addSignal(SIGTERM); - QObject::connect(SignalHandler::self(), &SignalHandler::signalReceived, &app, [&app](int signal) { - if (signal == SIGTERM) { - app.quit(); - } - }); - - KWinWrapper wrapper(&app); - wrapper.run(); - - return app.exec(); -} - -#include "kwin_wrapper.moc" diff --git a/startlingmo/wayland_wrapper/lib/CMakeLists.txt b/startlingmo/wayland_wrapper/lib/CMakeLists.txt deleted file mode 100644 index dff9a60..0000000 --- a/startlingmo/wayland_wrapper/lib/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -add_library(KWinXwaylandCommon STATIC - xwaylandsocket.cpp - xauthority.cpp -) - -ecm_qt_declare_logging_category(KWinXwaylandCommon - HEADER - xwayland_logging.h - IDENTIFIER - KWIN_XWL - CATEGORY_NAME - kwin_xwl - DEFAULT_SEVERITY - Warning -) - -set_property(TARGET KWinXwaylandCommon PROPERTY POSITION_INDEPENDENT_CODE ON) - -target_include_directories(KWinXwaylandCommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(KWinXwaylandCommon Qt::Core Qt::Network) diff --git a/startlingmo/wayland_wrapper/lib/xauthority.cpp b/startlingmo/wayland_wrapper/lib/xauthority.cpp deleted file mode 100644 index bef5560..0000000 --- a/startlingmo/wayland_wrapper/lib/xauthority.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2020 Vlad Zahorodnii - SPDX-FileCopyrightText: 2021 David Edmundson - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "xauthority.h" - -#include -#include -#include -#include -#include - -static void writeXauthorityEntry(QDataStream &stream, quint16 family, - const QByteArray &address, const QByteArray &display, - const QByteArray &name, const QByteArray &cookie) -{ - stream << quint16(family); - - auto writeArray = [&stream](const QByteArray &str) { - stream << quint16(str.size()); - stream.writeRawData(str.constData(), str.size()); - }; - - writeArray(address); - writeArray(display); - writeArray(name); - writeArray(cookie); -} - -static QByteArray generateXauthorityCookie() -{ - QByteArray cookie; - cookie.resize(16); // Cookie must be 128bits - - QRandomGenerator *generator = QRandomGenerator::system(); - for (int i = 0; i < cookie.size(); ++i) { - cookie[i] = uint8_t(generator->bounded(256)); - } - return cookie; -} - -bool generateXauthorityFile(int display, QTemporaryFile *authorityFile) -{ - const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - - authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX")); - if (!authorityFile->open()) { - return false; - } - - const QByteArray hostname = QHostInfo::localHostName().toUtf8(); - const QByteArray displayName = QByteArray::number(display); - const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1"); - const QByteArray cookie = generateXauthorityCookie(); - - QDataStream stream(authorityFile); - stream.setByteOrder(QDataStream::BigEndian); - - // Write entry with FamilyLocal and the host name as address - writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie); - - // Write entry with FamilyWild, no address - writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie); - - if (stream.status() != QDataStream::Ok || !authorityFile->flush()) { - authorityFile->remove(); - return false; - } - - return true; -} diff --git a/startlingmo/wayland_wrapper/lib/xauthority.h b/startlingmo/wayland_wrapper/lib/xauthority.h deleted file mode 100644 index 7ba232b..0000000 --- a/startlingmo/wayland_wrapper/lib/xauthority.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2021 David Edmundson - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -class QTemporaryFile; - -bool generateXauthorityFile(int display, QTemporaryFile *authorityFile); diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp b/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp deleted file mode 100644 index e31899c..0000000 --- a/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "xwaylandsocket.h" -#include "xwayland_logging.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace KWin -{ - -class UnixSocketAddress -{ -public: - enum class Type { - Unix, - Abstract, - }; - - UnixSocketAddress(const QString &socketPath, Type type); - - const sockaddr *data() const; - int size() const; - -private: - QByteArray m_buffer; -}; - -UnixSocketAddress::UnixSocketAddress(const QString &socketPath, Type type) -{ - const QByteArray encodedSocketPath = QFile::encodeName(socketPath); - - int byteCount = offsetof(sockaddr_un, sun_path) + encodedSocketPath.size() + 1; - m_buffer.resize(byteCount); - - sockaddr_un *address = reinterpret_cast(m_buffer.data()); - address->sun_family = AF_UNIX; - - if (type == Type::Unix) { - memcpy(address->sun_path, encodedSocketPath.data(), encodedSocketPath.size()); - address->sun_path[encodedSocketPath.size()] = '\0'; - } else { - // Abstract domain socket does not need the NUL-termination byte. - *address->sun_path = '\0'; - memcpy(address->sun_path + 1, encodedSocketPath.data(), encodedSocketPath.size()); - } -} - -const sockaddr *UnixSocketAddress::data() const -{ - return reinterpret_cast(m_buffer.data()); -} - -int UnixSocketAddress::size() const -{ - return m_buffer.size(); -} - -static QString lockFileNameForDisplay(int display) -{ - return QStringLiteral("/tmp/.X%1-lock").arg(display); -} - -static QString socketFileNameForDisplay(int display) -{ - return QStringLiteral("/tmp/.X11-unix/X%1").arg(display); -} - -static bool tryLockFile(const QString &lockFileName) -{ - for (int attempt = 0; attempt < 3; ++attempt) { - QFile lockFile(lockFileName); - if (lockFile.open(QFile::WriteOnly | QFile::NewOnly)) { - char buffer[12]; - snprintf(buffer, sizeof(buffer), "%10lld\n", QCoreApplication::applicationPid()); - if (lockFile.write(buffer, sizeof(buffer) - 1) != sizeof(buffer) - 1) { - qCWarning(KWIN_XWL) << "Failed to write pid to lock file:" << lockFile.errorString(); - lockFile.remove(); - return false; - } - return true; - } else if (lockFile.open(QFile::ReadOnly)) { - const int lockPid = lockFile.readLine().trimmed().toInt(); - if (!lockPid) { - return false; - } - if (kill(lockPid, 0) < 0 && errno == ESRCH) { - lockFile.remove(); // Try to grab the lock file in the next loop iteration. - } else { - return false; - } - } - } - return false; -} - -static int listen_helper(const QString &filePath, UnixSocketAddress::Type type, XwaylandSocket::OperationMode mode) -{ - const UnixSocketAddress socketAddress(filePath, type); - - int socketFlags = SOCK_STREAM; - if (mode == XwaylandSocket::OperationMode::CloseFdsOnExec) { - socketFlags |= SOCK_CLOEXEC; - } - int fileDescriptor = socket(AF_UNIX, socketFlags, 0); - if (fileDescriptor == -1) { - return -1; - } - - if (bind(fileDescriptor, socketAddress.data(), socketAddress.size()) == -1) { - close(fileDescriptor); - return -1; - } - - if (listen(fileDescriptor, 1) == -1) { - close(fileDescriptor); - return -1; - } - - return fileDescriptor; -} - -static bool checkSocketsDirectory() -{ - struct stat info; - const char *path = "/tmp/.X11-unix"; - - if (lstat(path, &info) != 0) { - if (errno == ENOENT) { - qCWarning(KWIN_XWL) << path << "does not exist. Please check your installation"; - return false; - } - - qCWarning(KWIN_XWL, "Failed to stat %s: %s", path, strerror(errno)); - return false; - } - - if (!S_ISDIR(info.st_mode)) { - qCWarning(KWIN_XWL) << path << "is not a directory. Broken system?"; - return false; - } - if (info.st_uid != 0 && info.st_uid != getuid()) { - qCWarning(KWIN_XWL) << path << "is not owned by root or us"; - return false; - } - if (!(info.st_mode & S_ISVTX)) { - qCWarning(KWIN_XWL) << path << "has no sticky bit on. Your system might be compromised!"; - return false; - } - - return true; -} - -XwaylandSocket::XwaylandSocket(OperationMode mode) -{ - if (!checkSocketsDirectory()) { - return; - } - - for (int display = 0; display < 100; ++display) { - const QString socketFilePath = socketFileNameForDisplay(display); - const QString lockFilePath = lockFileNameForDisplay(display); - - if (!tryLockFile(lockFilePath)) { - continue; - } - - QVector fileDescriptors; - auto socketCleanup = qScopeGuard([&fileDescriptors]() { - for (const int &fileDescriptor : std::as_const(fileDescriptors)) { - close(fileDescriptor); - } - }); - - QFile::remove(socketFilePath); - const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix, mode); - if (unixFileDescriptor == -1) { - QFile::remove(lockFilePath); - continue; - } - fileDescriptors << unixFileDescriptor; - -#if defined(Q_OS_LINUX) - const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract, mode); - if (abstractFileDescriptor == -1) { - QFile::remove(lockFilePath); - QFile::remove(socketFilePath); - continue; - } - fileDescriptors << abstractFileDescriptor; -#endif - - m_fileDescriptors = fileDescriptors; - socketCleanup.dismiss(); - - m_socketFilePath = socketFilePath; - m_lockFilePath = lockFilePath; - m_display = display; - return; - } - - qCWarning(KWIN_XWL) << "Failed to find free X11 connection socket"; -} - -XwaylandSocket::~XwaylandSocket() -{ - for (const int &fileDescriptor : std::as_const(m_fileDescriptors)) { - close(fileDescriptor); - } - if (!m_socketFilePath.isEmpty()) { - QFile::remove(m_socketFilePath); - } - if (!m_lockFilePath.isEmpty()) { - QFile::remove(m_lockFilePath); - } -} - -bool XwaylandSocket::isValid() const -{ - return m_display != -1; -} - -QVector XwaylandSocket::fileDescriptors() const -{ - return m_fileDescriptors; -} - -int XwaylandSocket::display() const -{ - return m_display; -} - -QString XwaylandSocket::name() const -{ - return ":" + QString::number(m_display); -} - -} // namespace KWin diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.h b/startlingmo/wayland_wrapper/lib/xwaylandsocket.h deleted file mode 100644 index 80bad35..0000000 --- a/startlingmo/wayland_wrapper/lib/xwaylandsocket.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -#include -#include -#include - -namespace KWin -{ - -class XwaylandSocket -{ -public: - enum class OperationMode { - CloseFdsOnExec, - TransferFdsOnExec - }; - - XwaylandSocket(OperationMode operationMode); - ~XwaylandSocket(); - - bool isValid() const; - int display() const; - QString name() const; - - QVector fileDescriptors() const; - -private: - QVector m_fileDescriptors; - int m_display = -1; - QString m_socketFilePath; - QString m_lockFilePath; -}; - -} // namespace KWin diff --git a/startlingmo/wayland_wrapper/wl-socket.c b/startlingmo/wayland_wrapper/wl-socket.c deleted file mode 100644 index 1b02839..0000000 --- a/startlingmo/wayland_wrapper/wl-socket.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2020 - SPDX-FileCopyrightText: 2008 Kristian Høgsberg - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#define _DEFAULT_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* This is the size of the char array in struct sock_addr_un. - * No Wayland socket can be created with a path longer than this, - * including the null terminator. - */ -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif - -#define LOCK_SUFFIX ".lock" -#define LOCK_SUFFIXLEN 5 - -struct wl_socket { - int fd; - int fd_lock; - struct sockaddr_un addr; - char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN]; - char display_name[16]; -}; - -static struct wl_socket *wl_socket_alloc(void) -{ - struct wl_socket *s; - - s = malloc(sizeof *s); - if (!s) - return NULL; - - s->fd = -1; - s->fd_lock = -1; - - return s; -} - -static int wl_socket_lock(struct wl_socket *socket) -{ - struct stat socket_stat; - - snprintf(socket->lock_addr, sizeof socket->lock_addr, "%s%s", socket->addr.sun_path, LOCK_SUFFIX); - - socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC | O_RDWR, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); - - if (socket->fd_lock < 0) { - printf("unable to open lockfile %s check permissions\n", socket->lock_addr); - goto err; - } - - if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) { - printf("unable to lock lockfile %s, maybe another compositor is running\n", socket->lock_addr); - goto err_fd; - } - - if (lstat(socket->addr.sun_path, &socket_stat) < 0) { - if (errno != ENOENT) { - printf("did not manage to stat file %s\n", socket->addr.sun_path); - goto err_fd; - } - } else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) { - unlink(socket->addr.sun_path); - } - - return 0; -err_fd: - close(socket->fd_lock); - socket->fd_lock = -1; -err: - *socket->lock_addr = 0; - /* we did not set this value here, but without lock the - * socket won't be created anyway. This prevents the - * wl_socket_destroy from unlinking already existing socket - * created by other compositor */ - *socket->addr.sun_path = 0; - - return -1; -} - -void wl_socket_destroy(struct wl_socket *s) -{ - if (s->addr.sun_path[0]) - unlink(s->addr.sun_path); - if (s->fd >= 0) - close(s->fd); - if (s->lock_addr[0]) - unlink(s->lock_addr); - if (s->fd_lock >= 0) - close(s->fd_lock); - - free(s); -} - -const char *wl_socket_get_display_name(struct wl_socket *s) -{ - return s->display_name; -} - -int wl_socket_get_fd(struct wl_socket *s) -{ - return s->fd; -} - -struct wl_socket *wl_socket_create() -{ - struct wl_socket *s; - int displayno = 0; - int name_size; - - /* A reasonable number of maximum default sockets. If - * you need more than this, use the explicit add_socket API. */ - const int MAX_DISPLAYNO = 32; - const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); - if (!runtime_dir) { - printf("XDG_RUNTIME_DIR not set"); - return NULL; - } - - s = wl_socket_alloc(); - if (s == NULL) - return NULL; - - do { - snprintf(s->display_name, sizeof s->display_name, "wayland-%d", displayno); - s->addr.sun_family = AF_LOCAL; - name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s/%s", runtime_dir, s->display_name) + 1; - assert(name_size > 0); - - if (name_size > (int)sizeof s->addr.sun_path) { - goto fail; - } - - if (wl_socket_lock(s) < 0) - continue; - - s->fd = socket(PF_LOCAL, SOCK_STREAM, 0); - - int size = SUN_LEN(&s->addr); - int ret = bind(s->fd, (struct sockaddr*)&s->addr, size); - if (ret < 0) { - goto fail; - } - ret = listen(s->fd, 128); - if (ret < 0) { - goto fail; - } - return s; - } while (displayno++ < MAX_DISPLAYNO); - -fail: - wl_socket_destroy(s); - return NULL; -} diff --git a/startlingmo/wayland_wrapper/wl-socket.h b/startlingmo/wayland_wrapper/wl-socket.h deleted file mode 100644 index ac69ca9..0000000 --- a/startlingmo/wayland_wrapper/wl-socket.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2020 - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Allocate and create a socket - * It is bound and accepted - */ -struct wl_socket *wl_socket_create(); - -/** - * Returns the file descriptor for the socket - */ -int wl_socket_get_fd(struct wl_socket *); - -/** - * Returns the name of the socket, i.e "wayland-0" - */ -char *wl_socket_get_display_name(struct wl_socket *); - -/** - * Cleanup resources and close the FD - */ -void wl_socket_destroy(struct wl_socket *socket); - -#ifdef __cplusplus -} -#endif From 07f617fae2ccf723200c2f5212d9bb464c95965c Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 15:59:11 +0800 Subject: [PATCH 18/24] Revert "Remove startlingmo" This reverts commit 5e8e767d56d65f5f4cc93bad82222c3d5e9de175. --- session/application.cpp | 8 - session/daemon-helper.cpp | 107 +--- session/daemon-helper.h | 231 ++------ session/main.cpp | 3 + session/processmanager.cpp | 409 ++++++-------- session/processmanager.h | 59 +- startlingmo/CMakeLists.txt | 76 +++ startlingmo/UpdateLaunchEnvironment.cpp | 151 ++++++ startlingmo/UpdateLaunchEnvironment.hpp | 52 ++ startlingmo/cmake/FindKWinPath.cmake | 29 + startlingmo/config-startlingmo.h.cmake | 9 + startlingmo/daemon-helper.cpp | 138 +++++ startlingmo/daemon-helper.h | 203 +++++++ {session => startlingmo}/job_private.hpp | 0 startlingmo/lingmo-session/CMakeLists.txt | 46 ++ startlingmo/lingmo-session/application.cpp | 277 ++++++++++ startlingmo/lingmo-session/application.h | 94 ++++ .../lingmo-session/com.lingmo.Session.xml | 36 ++ startlingmo/lingmo-session/main.cpp | 45 ++ .../lingmo-session/networkproxymanager.cpp | 97 ++++ .../lingmo-session/networkproxymanager.h | 51 ++ .../lingmo-session/powermanager/power.cpp | 80 +++ .../lingmo-session/powermanager/power.h | 118 ++++ .../powermanager/powerproviders.cpp | 448 +++++++++++++++ .../powermanager/powerproviders.h | 109 ++++ startlingmo/lingmo-session/process.cpp | 30 + startlingmo/lingmo-session/process.h | 34 ++ startlingmo/lingmo-session/processmanager.cpp | 335 ++++++++++++ startlingmo/lingmo-session/processmanager.h | 140 +++++ startlingmo/lingmo-sourceenv.sh | 7 + startlingmo/lingmo-wayland-session.desktop | 6 + startlingmo/lingmo-xorg-session.desktop | 7 + startlingmo/signalhandler.cpp | 63 +++ startlingmo/signalhandler.h | 38 ++ startlingmo/startlingmo-wayland.cpp | 95 ++++ startlingmo/startlingmo-x11.cpp | 62 +++ startlingmo/startlingmo.cpp | 513 ++++++++++++++++++ startlingmo/startlingmo.hpp | 66 +++ startlingmo/wayland_wrapper/CMakeLists.txt | 23 + startlingmo/wayland_wrapper/kwin_wrapper.cpp | 179 ++++++ .../wayland_wrapper/lib/CMakeLists.txt | 20 + .../wayland_wrapper/lib/xauthority.cpp | 77 +++ startlingmo/wayland_wrapper/lib/xauthority.h | 14 + .../wayland_wrapper/lib/xwaylandsocket.cpp | 250 +++++++++ .../wayland_wrapper/lib/xwaylandsocket.h | 40 ++ startlingmo/wayland_wrapper/wl-socket.c | 172 ++++++ startlingmo/wayland_wrapper/wl-socket.h | 39 ++ 47 files changed, 4477 insertions(+), 609 deletions(-) create mode 100644 startlingmo/CMakeLists.txt create mode 100644 startlingmo/UpdateLaunchEnvironment.cpp create mode 100644 startlingmo/UpdateLaunchEnvironment.hpp create mode 100644 startlingmo/cmake/FindKWinPath.cmake create mode 100644 startlingmo/config-startlingmo.h.cmake create mode 100644 startlingmo/daemon-helper.cpp create mode 100644 startlingmo/daemon-helper.h rename {session => startlingmo}/job_private.hpp (100%) create mode 100755 startlingmo/lingmo-session/CMakeLists.txt create mode 100755 startlingmo/lingmo-session/application.cpp create mode 100755 startlingmo/lingmo-session/application.h create mode 100755 startlingmo/lingmo-session/com.lingmo.Session.xml create mode 100755 startlingmo/lingmo-session/main.cpp create mode 100755 startlingmo/lingmo-session/networkproxymanager.cpp create mode 100755 startlingmo/lingmo-session/networkproxymanager.h create mode 100755 startlingmo/lingmo-session/powermanager/power.cpp create mode 100755 startlingmo/lingmo-session/powermanager/power.h create mode 100755 startlingmo/lingmo-session/powermanager/powerproviders.cpp create mode 100755 startlingmo/lingmo-session/powermanager/powerproviders.h create mode 100755 startlingmo/lingmo-session/process.cpp create mode 100755 startlingmo/lingmo-session/process.h create mode 100755 startlingmo/lingmo-session/processmanager.cpp create mode 100755 startlingmo/lingmo-session/processmanager.h create mode 100644 startlingmo/lingmo-sourceenv.sh create mode 100755 startlingmo/lingmo-wayland-session.desktop create mode 100755 startlingmo/lingmo-xorg-session.desktop create mode 100644 startlingmo/signalhandler.cpp create mode 100644 startlingmo/signalhandler.h create mode 100644 startlingmo/startlingmo-wayland.cpp create mode 100644 startlingmo/startlingmo-x11.cpp create mode 100644 startlingmo/startlingmo.cpp create mode 100644 startlingmo/startlingmo.hpp create mode 100644 startlingmo/wayland_wrapper/CMakeLists.txt create mode 100644 startlingmo/wayland_wrapper/kwin_wrapper.cpp create mode 100644 startlingmo/wayland_wrapper/lib/CMakeLists.txt create mode 100644 startlingmo/wayland_wrapper/lib/xauthority.cpp create mode 100644 startlingmo/wayland_wrapper/lib/xauthority.h create mode 100644 startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp create mode 100644 startlingmo/wayland_wrapper/lib/xwaylandsocket.h create mode 100644 startlingmo/wayland_wrapper/wl-socket.c create mode 100644 startlingmo/wayland_wrapper/wl-socket.h diff --git a/session/application.cpp b/session/application.cpp index aebdc43..32786e9 100755 --- a/session/application.cpp +++ b/session/application.cpp @@ -127,14 +127,6 @@ Application::Application(int &argc, char **argv) qunsetenv("XCURSOR_SIZE"); qunsetenv("SESSION_MANAGER"); - if (m_wayland) { - qputenv("XDG_SESSION_TYPE", "wayland"); - qputenv("QT_QPA_PLATFORM", "wayland"); - } else { - // force xcb QPA plugin as session manager server is very X11 specific. - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); - } - m_networkProxyManager->update(); QTimer::singleShot(50, this, &Application::updateUserDirs); diff --git a/session/daemon-helper.cpp b/session/daemon-helper.cpp index be1b712..901b510 100644 --- a/session/daemon-helper.cpp +++ b/session/daemon-helper.cpp @@ -3,21 +3,18 @@ * @author Elysia **/ #include "daemon-helper.h" -#include "job_private.hpp" #include +#include +#include #include #include -#include -#include #include -#include +#include namespace LINGMO_SESSION { -Daemon::Daemon(const QList> &processList, - bool _enableAutoStart, QObject *parent) - : QObject(parent), m_processList(processList), - m_enableAutoRestart(_enableAutoStart) { +Daemon::Daemon(const QList> &processList, bool _enableAutoStart, QObject *parent) + : QObject(parent), m_processList(processList), m_enableAutoRestart(_enableAutoStart) { for (const auto &processInfo : m_processList) { startProcess(processInfo); } @@ -33,7 +30,7 @@ void Daemon::onProcessError(QProcess::ProcessError error) { qDebug() << "Process error:" << program << "Error:" << error; for (const auto &processInfo : m_processList) { - if (std::get<0>(processInfo) == program) { + if (processInfo.first == program) { qDebug() << "Restarting process due to error:" << program; QTimer::singleShot(1, this, [this, processInfo]() { startProcess(processInfo); @@ -43,98 +40,18 @@ void Daemon::onProcessError(QProcess::ProcessError error) { } } -void Daemon::startProcess(const std::tuple &processInfo) { +void Daemon::startProcess(const QPair &processInfo) { const QPointer process = new QProcess(this); if (this->m_enableAutoRestart) - connect(process, &QProcess::errorOccurred, this, &Daemon::onProcessError); - - process->setProcessEnvironment(std::get<2>(processInfo)); + connect(process, &QProcess::errorOccurred, + this, &Daemon::onProcessError); - process->start(std::get<0>(processInfo), std::get<1>(processInfo)); + process->start(processInfo.first, processInfo.second); if (process->waitForStarted()) { - qDebug() << "Process started:" << std::get<0>(processInfo) - << "PID:" << process->processId(); + qDebug() << "Process started:" << processInfo.first << "PID:" << process->processId(); } else { - qDebug() << "Failed to start process:" << std::get<0>(processInfo) - << process->errorString(); - } -} - -JobPrivate::JobPrivate() {} - -JobPrivate::~JobPrivate() {} - -Job::Job(QObject *parent) : QObject(parent), d_ptr(new JobPrivate) {} - -Job::~Job() { - if (!d_ptr->isFinished) { - d_ptr->isFinished = true; - Q_EMIT finished(this); - } -} - -void Job::emitResult() { - if (!d_func()->isFinished) { - finishJob(true); - } -} -void Job::finishJob(bool emitResult) { - Q_D(Job); - Q_ASSERT(!d->isFinished); - d->isFinished = true; - - if (d->eventLoop) { - d->eventLoop->quit(); - } - - Q_EMIT finished(this); - - if (emitResult) { - Q_EMIT result(this); - } - - if (isAutoDelete()) { - deleteLater(); - } -} - -bool Job::isAutoDelete() const { - Q_D(const Job); - return d->isAutoDelete; -} - -void Job::setAutoDelete(bool autodelete) { - Q_D(Job); - d->isAutoDelete = autodelete; -} - -bool Job::exec() { - Q_D(Job); - // Usually this job would delete itself, via deleteLater() just after - // emitting result() (unless configured otherwise). Since we use an event - // loop below, that event loop will process the deletion event and we'll - // have been deleted when exec() returns. This crashes, so temporarily - // suspend autodeletion and manually do it afterwards. - const bool wasAutoDelete = isAutoDelete(); - setAutoDelete(false); - - Q_ASSERT(!d->eventLoop); - - QEventLoop loop(this); - d->eventLoop = &loop; - - start(); - - if (!d->isFinished) { - d->m_startedWithExec = true; - d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); - } - d->eventLoop = nullptr; - - if (wasAutoDelete) { - deleteLater(); + qDebug() << "Failed to start process:" << processInfo.first << process->errorString(); } - return (d->error == NoError); } } // namespace LINGMO_SESSION diff --git a/session/daemon-helper.h b/session/daemon-helper.h index e9e9721..3715569 100644 --- a/session/daemon-helper.h +++ b/session/daemon-helper.h @@ -1,204 +1,49 @@ #ifndef __DAEMON_HELPER_ #define __DAEMON_HELPER_ -#include -#include -#include #include #include +#include +#include +#include #include #include -#include -#include - -#include +#include namespace LINGMO_SESSION { -class JobPrivate; - -class Daemon : public QObject { - Q_OBJECT - -public: - /** - * Start all the passed process using daemon. - * @param processList Process list to start - * @param parent - */ - explicit Daemon(const QList> &processList, - bool _enableAutoStart = true, QObject *parent = nullptr); - -public slots: - - /** - * Handle the case when the progarm has some errors (i.e. crashed) - * @param error - */ - void onProcessError(QProcess::ProcessError error); - -private: - /** - * Start a given process using daemon helper - * @brief startProcess - * @param processInfo - */ - void startProcess(const std::tuple &processInfo); - - QList> m_processList; - - /** - * @brief Whether to enable auto reload when process exited. - */ - bool m_enableAutoRestart; -}; - -class Job : public QObject { - Q_OBJECT -public: - Job(QObject *parent = nullptr); - - ~Job(); - - /** - * Returns whether this job automatically deletes itself once - * the job is finished. - * - * @return whether the job is deleted automatically after - * finishing. - */ - bool isAutoDelete() const; - - /** - * Sets the auto-delete property of the job. If @p autodelete is - * set to @c false the job will not delete itself once it is finished. - * - * The default for any Job is to automatically delete itself, which - * implies that the job was created on the heap (using new). - * If the job is created on the stack (which isn't the typical use-case - * for a job) then you must set auto-delete to @c false, otherwise you - * could get a crash when the job finishes and tries to delete itself. - * - * @note If you set auto-delete to @c false then you need to kill the - * job manually, ideally by calling kill(). - * - * @param autodelete set to @c false to disable automatic deletion - * of the job. - */ - void setAutoDelete(bool autodelete); - - /** - * Executes the job synchronously. - * - * This will start a nested QEventLoop internally. Nested event loop can be - * dangerous and can have unintended side effects, you should avoid calling - * exec() whenever you can and use the asynchronous interface of Job instead. - * - * Should you indeed call this method, you need to make sure that all callers - * are reentrant, so that events delivered by the inner event loop don't cause - * non-reentrant functions to be called, which usually wreaks havoc. - * - * Note that the event loop started by this method does not process user input - * events, which means your user interface will effectively be blocked. Other - * events like paint or network events are still being processed. The - * advantage of not processing user input events is that the chance of - * accidental reentrance is greatly reduced. Still you should avoid calling - * this function. - * - * @return true if the job has been executed without error, false otherwise - */ - bool exec(); - - /** - * Starts the job asynchronously. - * - * When the job is finished, result() is emitted. - * - * Warning: Never implement any synchronous workload in this method. This - * method should just trigger the job startup, not do any work itself. It is - * expected to be non-blocking. - * - * This is the method all subclasses need to implement. - * It should setup and trigger the workload of the job. It should not do any - * work itself. This includes all signals and terminating the job, e.g. by - * emitResult(). The workload, which could be another method of the - * subclass, is to be triggered using the event loop, e.g. by code like: - * \code - * void ExampleJob::start() - * { - * QTimer::singleShot(0, this, &ExampleJob::doWork); - * } - * \endcode - */ - Q_SCRIPTABLE virtual void start() = 0; - - enum { - /*** Indicates there is no error */ - NoError = 0, - /*** Indicates the job was killed */ - KilledJobError = 1, - /*** Subclasses should define error codes starting at this value */ - UserDefinedError = 100, + class Daemon : public QObject { + Q_OBJECT + + public: + /** + * Start all the passed process using daemon. + * @param processList Process list to start + * @param parent + */ + explicit Daemon(const QList>& processList, bool _enableAutoStart = true, QObject* parent = nullptr); + + public slots: + + /** + * Handle the case when the progarm has some errors (i.e. crashed) + * @param error + */ + void onProcessError(QProcess::ProcessError error); + + private: + /** + * Start a given process using daemon helper + * @brief startProcess + * @param processInfo + */ + void startProcess(const QPair& processInfo); + + QList> m_processList; + + /** + * @brief Whether to enable auto reload when process exited. + */ + bool m_enableAutoRestart; }; -Q_SIGNALS: - /** - * Emitted when the job is finished, in any case. It is used to notify - * observers that the job is terminated and that progress can be hidden. - * - * This signal is guaranteed to be emitted exactly once. - * - * This is a private signal, it can't be emitted directly by subclass, use - * emitResult() instead. - * - * In general, to be notified of a job's completion, client code should - * connect to result() rather than finished(), so that kill(Quietly) is indeed - * quiet. However if you store a list of jobs and they might get killed - * silently, then you must connect to this instead of result(), to avoid - * dangling pointers in your list. - * - * @param job the job that emitted this signal - * @internal - * - * @see result - */ - void finished(Job *job); - - /** - * Emitted when the job is finished (except when killed with KJob::Quietly). - * - * This signal is guaranteed to be emitted at most once. - * - * Use error to know if the job was finished with error. - * - * This is a private signal, it can't be emitted directly by subclasses of - * KJob, use emitResult() instead. - * - * Please connect to this signal instead of finished. - * - * @param job the job that emitted this signal - * - * @see kill - */ - void result(Job *job); - -protected : - /** - * Utility function to emit the result signal, and end this job. - * It first notifies the observers to hide the progress for this job using - * the finished() signal. - * - * @note Deletes this job using deleteLater(). - * - * @see result() - * @see finished() - */ - Q_SLOT void emitResult(); - - std::unique_ptr const d_ptr; - -private: - void finishJob(bool emitResult); - - Q_DECLARE_PRIVATE(Job) -}; -} // namespace LINGMO_SESSION +} #endif diff --git a/session/main.cpp b/session/main.cpp index 0d2fecb..43b40ee 100755 --- a/session/main.cpp +++ b/session/main.cpp @@ -24,6 +24,9 @@ int main(int argc, char *argv[]) { // putenv((char *)"SESSION_MANAGER="); + // force xcb QPA plugin as session manager server is very X11 specific. + qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); + QQuickWindow::setDefaultAlphaBuffer(true); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); diff --git a/session/processmanager.cpp b/session/processmanager.cpp index 2a8d522..756395c 100755 --- a/session/processmanager.cpp +++ b/session/processmanager.cpp @@ -6,298 +6,193 @@ #include "application.h" #include -#include -#include -#include +#include #include +#include #include -#include -#include +#include #include -#include -#include +#include +#include #include #include -#include -#include #include -#include -#include -#include +#include +#include #include "daemon-helper.h" ProcessManager::ProcessManager(Application *app, QObject *parent) - : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { - qApp->installNativeEventFilter(this); -} - -ProcessManager::~ProcessManager() { - qApp->removeNativeEventFilter(this); - - QMapIterator i(m_systemProcess); - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - delete p; - m_systemProcess[i.key()] = nullptr; - } + : QObject(parent) + , m_app(app) + , m_wmStarted(false) + , m_waitLoop(nullptr) +{ + qApp->installNativeEventFilter(this); } -void ProcessManager::start() { - startWindowManager(); - startDaemonProcess(); -} +ProcessManager::~ProcessManager() +{ + qApp->removeNativeEventFilter(this); -void ProcessManager::logout() { - QMapIterator i(m_systemProcess); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - p->terminate(); - } - i.toFront(); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { - p->kill(); + QMapIterator i(m_systemProcess); + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + delete p; + m_systemProcess[i.key()] = nullptr; } - } - - QCoreApplication::exit(0); } -void ProcessManager::startWindowManager() { - auto detcted_wayland = - qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); - - if (detcted_wayland || m_app->wayland()) { - auto kwinWaylandJob = - new StartServiceJob(QStringLiteral("lingmo_kwin_wayland_wrapper"), - {QStringLiteral("--xwayland")}, - QStringLiteral("org.lingmo.KWinWrapper")); - kwinWaylandJob->setParent(this); - kwinWaylandJob->exec(); // Wait untill kwin_wayland_wrapper started - } else { - auto *wmProcess = new QProcess; - - wmProcess->start("kwin_x11", QStringList()); - } +void ProcessManager::start() +{ + startWindowManager(); + startDaemonProcess(); } -void ProcessManager::startDesktopProcess() { - // When the lingmo-settings-daemon theme module is loaded, start the desktop. - // In the way, there will be no problem that desktop and launcher can't get - // wallpaper. - - auto env = QProcessEnvironment::systemEnvironment(); - env.insert("QT_QPA_PLATFORM", "xcb"); - QList> list; - // Desktop components - list << std::make_tuple(QString("lingmo-notificationd"), QStringList(), env); - list << std::make_tuple(QString("lingmo-statusbar"), QStringList(), env); - list << std::make_tuple(QString("lingmo-dock"), QStringList(), env); - list << std::make_tuple(QString("lingmo-filemanager"), QStringList("--desktop"), env); - list << std::make_tuple(QString("lingmo-launcher"), QStringList(), env); - list << std::make_tuple(QString("lingmo-powerman"), QStringList(), env); - list << std::make_tuple(QString("lingmo-clipboard"), QStringList(), env); - list << std::make_tuple(QString("lingmo-wallpaper-color-pick"), QStringList(), env); - - m_desktopAutoStartD = std::make_shared(list); - - // Auto start - QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); -} - -void ProcessManager::startDaemonProcess() { - auto env = QProcessEnvironment::systemEnvironment(); - // xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); - QList> list; - list << std::make_tuple(QString("lingmo-settings-daemon"), QStringList(), env); - list << std::make_tuple(QString("lingmo-xembedsniproxy"), QStringList(), env); - list << std::make_tuple(QString("lingmo-gmenuproxy"), QStringList(), env); - list << std::make_tuple(QString("lingmo-permission-surveillance"), QStringList(), env); - // list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << std::make_tuple(QString("lingmo-chotkeys"), QStringList(), env); - - m_daemonAutoStartD = std::make_shared(list); -} +void ProcessManager::logout() +{ + QMapIterator i(m_systemProcess); -void ProcessManager::loadAutoStartProcess() { - QList> list; - auto env = QProcessEnvironment::systemEnvironment(); - - const QStringList dirs = QStandardPaths::locateAll( - QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), - QStandardPaths::LocateDirectory); - for (const QString &dir : dirs) { - const QDir d(dir); - const QStringList fileNames = - d.entryList(QStringList() << QStringLiteral("*.desktop")); - for (const QString &file : fileNames) { - QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); - desktop.setIniCodec("UTF-8"); - desktop.beginGroup("Desktop Entry"); - - // Ignore files the require a specific desktop environment - if (desktop.contains("NotShowIn")) { - const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); - if (notShowIn.contains("Lingmo")) - continue; - } - if (desktop.contains("OnlyShowIn")) { - const QStringList onlyShowIn = - desktop.value("OnlyShowIn").toStringList(); - if (!onlyShowIn.contains("Lingmo")) - continue; - } - - const QString execValue = desktop.value("Exec").toString(); - - // 避免冲突 - if (execValue.contains("gmenudbusmenuproxy")) - continue; - - // 使用 QProcess::splitCommand 来解析命令和参数 - QStringList args = QProcess::splitCommand(execValue); - - // 检查是否至少有一个元素(即程序路径) - if (!args.isEmpty()) { - auto program = args.first(); - args.removeFirst(); // 移除程序路径,剩下的都是参数 - - list << std::make_tuple(program, args, env); - } else { - qWarning() << "Invalid 'Exec' found in file!"; - } + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + p->terminate(); } - } - - m_userAutoStartD = std::make_shared(list, false); -} - -bool ProcessManager::nativeEventFilter(const QByteArray &eventType, - void *message, long *result) { - if (eventType != "xcb_generic_event_t") // We only want to handle XCB events - return false; - - // ref: lxqt session - if (!m_wmStarted && m_waitLoop) { - // all window managers must set their name according to the spec - if (!QString::fromUtf8( - NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) - .wmName()) - .isEmpty()) { - qDebug() << "Window manager started"; - m_wmStarted = true; - if (m_waitLoop && m_waitLoop->isRunning()) - m_waitLoop->exit(); - - qApp->removeNativeEventFilter(this); + i.toFront(); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { + p->kill(); + } } - } - return false; + QCoreApplication::exit(0); } +void ProcessManager::startWindowManager() +{ + auto *wmProcess = new QProcess; -//////////////////////////////////////////////////////////// - -StartProcessJob::StartProcessJob(const QString &process, - const QStringList &args, - const QProcessEnvironment &additionalEnv) - : Job(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); - - connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, - SLOT(finished(int, QProcess::ExitStatus))); -} - -void StartProcessJob::start() { - qDebug() - << "Starting " << m_process->program() << m_process->arguments(); + wmProcess->start(m_app->wayland() ? "kwin_wayland" : "kwin_x11", QStringList()); - m_process->start(); + if (!m_app->wayland()) { + QEventLoop waitLoop; + m_waitLoop = &waitLoop; + // add a timeout to avoid infinite blocking if a WM fail to execute. + QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); + waitLoop.exec(); + m_waitLoop = nullptr; + } } -void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { - qDebug() << "process job " << m_process->program() - << "finished with exit code " << exitCode; - emitResult(); +void ProcessManager::startDesktopProcess() +{ + // When the lingmo-settings-daemon theme module is loaded, start the desktop. + // In the way, there will be no problem that desktop and launcher can't get wallpaper. + + QList> list; + // Desktop components + list << qMakePair(QString("lingmo-notificationd"), QStringList()); + list << qMakePair(QString("lingmo-statusbar"), QStringList()); + list << qMakePair(QString("lingmo-dock"), QStringList()); + list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); + list << qMakePair(QString("lingmo-launcher"), QStringList()); + list << qMakePair(QString("lingmo-powerman"), QStringList()); + list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); + + m_desktopAutoStartD = std::make_shared(list); + + // Auto start + QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); } -StartServiceJob::StartServiceJob(const QString &process, - const QStringList &args, - const QString &serviceId, - const QProcessEnvironment &additionalEnv) - : Job(), m_process(new QProcess), m_serviceId(serviceId), - m_additionalEnv(additionalEnv) { - m_process->setProgram(process); - m_process->setArguments(args); - - auto watcher = - new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), - QDBusServiceWatcher::WatchForRegistration, this); - connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, - &StartServiceJob::emitResult); +void ProcessManager::startDaemonProcess() +{ + QList> list; + list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); + list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); + list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); + list << qMakePair(QString("lingmo-permission-surveillance"),QStringList()); +// list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-chotkeys"), QStringList()); + + m_daemonAutoStartD = std::make_shared(list); } -void StartServiceJob::start() { - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(m_additionalEnv); - m_process->setProcessEnvironment(env); - - if (!m_serviceId.isEmpty() && - QDBusConnection::sessionBus().interface()->isServiceRegistered( - m_serviceId)) { - qDebug() << m_process << "already running"; - emitResult(); - return; - } - qDebug() << "Starting " << m_process->program() << m_process->arguments(); - - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - m_process->start(); - const bool ret = m_process->waitForStarted(); - - if (!ret) { - qWarning() - << "error starting process" << m_process->program() - << m_process->arguments(); - emitResult(); - } - - if (m_serviceId.isEmpty()) { - emitResult(); - } -} +void ProcessManager::loadAutoStartProcess() +{ + QList> list; + + const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, + QStringLiteral("autostart"), + QStandardPaths::LocateDirectory); + for (const QString &dir : dirs) { + const QDir d(dir); + const QStringList fileNames = d.entryList(QStringList() << QStringLiteral("*.desktop")); + for (const QString &file : fileNames) { + QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); + desktop.setIniCodec("UTF-8"); + desktop.beginGroup("Desktop Entry"); + + // Ignore files the require a specific desktop environment + if (desktop.contains("NotShowIn")) { + const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); + if (notShowIn.contains("Lingmo")) + continue; + } + if (desktop.contains("OnlyShowIn")) { + const QStringList onlyShowIn = desktop.value("OnlyShowIn").toStringList(); + if (!onlyShowIn.contains("Lingmo")) + continue; + } + + const QString execValue = desktop.value("Exec").toString(); + + // 避免冲突 + if (execValue.contains("gmenudbusmenuproxy")) + continue; + + // 使用 QProcess::splitCommand 来解析命令和参数 + QStringList args = QProcess::splitCommand(execValue); + + // 检查是否至少有一个元素(即程序路径) + if (!args.isEmpty()) { + auto program = args.first(); + args.removeFirst(); // 移除程序路径,剩下的都是参数 + + list << qMakePair(program, args); + } else { + qWarning() << "Invalid 'Exec' found in file!"; + } + } + } -LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv) - : Job(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); + m_userAutoStartD = std::make_shared(list, false); } -void LaunchProcess::start() { - qDebug() - << "Starting " << m_process->program() << m_process->arguments(); - - m_process->startDetached(); +bool ProcessManager::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +{ + if (eventType != "xcb_generic_event_t") // We only want to handle XCB events + return false; + + // ref: lxqt session + if (!m_wmStarted && m_waitLoop) { + // all window managers must set their name according to the spec + if (!QString::fromUtf8(NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck).wmName()).isEmpty()) { + qDebug() << "Window manager started"; + m_wmStarted = true; + if (m_waitLoop && m_waitLoop->isRunning()) + m_waitLoop->exit(); + + qApp->removeNativeEventFilter(this); + } + } - emitResult(); -} \ No newline at end of file + return false; +} diff --git a/session/processmanager.h b/session/processmanager.h index 8058aca..f6a5562 100755 --- a/session/processmanager.h +++ b/session/processmanager.h @@ -20,14 +20,13 @@ #ifndef PROCESSMANAGER_H #define PROCESSMANAGER_H -#include #include -#include -#include +#include #include #include +#include +#include #include -#include #include "daemon-helper.h" @@ -73,56 +72,4 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter QEventLoop *m_waitLoop; }; -using namespace LINGMO_SESSION; -/** - * Launches a process, and waits for the process to start - */ -class LaunchProcess : public Job { - Q_OBJECT -public: - LaunchProcess( - const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - void start() override; - -private: - QProcess *m_process; -}; - -/** - * Launches a process, and waits for the process to finish - */ -class StartProcessJob : public Job { - Q_OBJECT -public: - StartProcessJob( - const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - void start() override; - -public Q_SLOTS: - void finished(int exitCode, QProcess::ExitStatus e); - -private: - QProcess *m_process; -}; - -/** - * Launches a process, and waits for the service to appear on the session bus - */ -class StartServiceJob : public Job { - Q_OBJECT -public: - StartServiceJob( - const QString &process, const QStringList &args, const QString &serviceId, - const QProcessEnvironment &additionalEnv = QProcessEnvironment()); - - void start() override; - -private: - QProcess *m_process; - const QString m_serviceId; - const QProcessEnvironment m_additionalEnv; -}; - #endif // PROCESSMANAGER_H diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt new file mode 100644 index 0000000..7f94336 --- /dev/null +++ b/startlingmo/CMakeLists.txt @@ -0,0 +1,76 @@ +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(KF6_MIN_VERSION "6.1.0") + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +find_package(Qt5 COMPONENTS Core DBus REQUIRED) + +find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE) +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(KDEInstallDirs) +include(FindKWinPath) +include(ECMQtDeclareLoggingCategory) + +find_kwin_wayland_bin_path() +find_kwin_bin_path() + +configure_file(config-startlingmo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startlingmo.h) + +find_package(KF5 REQUIRED COMPONENTS DBusAddons CoreAddons) + +find_package(LibKWorkspace REQUIRED) + +find_package(KF5Wayland REQUIRED) + +SET(startlingmo_SRCS + startlingmo.cpp + daemon-helper.cpp + UpdateLaunchEnvironment.cpp + signalhandler.cpp +) + +ecm_qt_declare_logging_category(startlingmo_SRCS HEADER debug.h IDENTIFIER LINGMO_STARTUP CATEGORY_NAME org.lingmo.startup) + +add_library(startlingmo OBJECT ${startlingmo_SRCS}) +target_link_libraries(startlingmo + PUBLIC + Qt5::Core + Qt5::DBus + KF5::DBusAddons + KF5::CoreAddons + PW::KWorkspace +) + +add_executable(startlingmo-wayland startlingmo-wayland.cpp) +target_link_libraries(startlingmo-wayland + PRIVATE + startlingmo + PUBLIC + Qt5::Core + Qt5::DBus +) + +add_executable(startlingmo-x11 startlingmo-x11.cpp) +target_link_libraries(startlingmo-x11 + PRIVATE + startlingmo + PUBLIC + Qt5::Core + Qt5::DBus +) + +add_subdirectory(lingmo-session) +add_subdirectory(wayland_wrapper) + +install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS startlingmo-x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(PROGRAMS lingmo-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) +install(FILES lingmo-wayland-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions/) +install(FILES lingmo-xorg-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) + diff --git a/startlingmo/UpdateLaunchEnvironment.cpp b/startlingmo/UpdateLaunchEnvironment.cpp new file mode 100644 index 0000000..9020633 --- /dev/null +++ b/startlingmo/UpdateLaunchEnvironment.cpp @@ -0,0 +1,151 @@ +#include "UpdateLaunchEnvironment.hpp" + +#include +#include +#include +#include + +#include +#include + +#include "debug.h" +#include "startlingmo.hpp" + +class UpdateLaunchEnvironmentJobPrivate +{ +public: + explicit UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q); + void monitorReply(const QDBusPendingReply<> &reply); + + static bool isPosixName(const QString &name); + static bool isSystemdApprovedValue(const QString &value); + + UpdateLaunchEnvironmentJob *q; + QProcessEnvironment environment; + int pendingReplies = 0; +}; + +UpdateLaunchEnvironmentJobPrivate::UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q) + : q(q) +{ +} + +void UpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply) +{ + ++pendingReplies; + + auto *watcher = new QDBusPendingCallWatcher(reply, q); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + --pendingReplies; + + if (pendingReplies == 0) { + Q_EMIT q->finished(); + q->deleteLater(); + } + }); +} + +UpdateLaunchEnvironmentJob::UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment) + : d(new UpdateLaunchEnvironmentJobPrivate(this)) +{ + d->environment = environment; + QTimer::singleShot(0, this, &UpdateLaunchEnvironmentJob::start); +} + +UpdateLaunchEnvironmentJob::~UpdateLaunchEnvironmentJob() = default; + +void UpdateLaunchEnvironmentJob::start() +{ + qDBusRegisterMetaType>(); + QMap dbusActivationEnv; + QStringList systemdUpdates; + + for (const auto &varName : d->environment.keys()) { + if (!UpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) { + qCWarning(LINGMO_STARTUP) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters"; + continue; + } + const QString value = d->environment.value(varName); + + // plasma-session + QDBusMessage lingmoSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("com.lingmo.Session"), + QStringLiteral("/Session"), + QStringLiteral("com.lingmo.Session"), + QStringLiteral("updateLaunchEnv")); + lingmoSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); + auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(lingmoSessionMsg); + d->monitorReply(plasmaSessionReply); + + // DBus-activation environment + dbusActivationEnv.insert(varName, value); + + // _user_ systemd env + // Systemd has stricter parsing of valid environment variables + // https://github.com/systemd/systemd/issues/16704 + // validate here + if (!UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) { + qCWarning(LINGMO_STARTUP) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters"; + continue; + } + const QString updateString = varName + QStringLiteral("=") + value; + systemdUpdates.append(updateString); + } + + // DBus-activation environment + QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("/org/freedesktop/DBus"), + QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("UpdateActivationEnvironment")); + dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)}); + + auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg); + d->monitorReply(dbusActivationReply); + + // _user_ systemd env + QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("SetEnvironment")); + systemdActivationMsg.setArguments({systemdUpdates}); + + auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg); + d->monitorReply(systemdActivationReply); +} + +bool UpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name) +{ + // Posix says characters like % should be 'tolerated', but it gives issues in practice. + // https://bugzilla.redhat.com/show_bug.cgi?id=1754395 + // https://bugzilla.redhat.com/show_bug.cgi?id=1879216 + // Ensure systemd compat by only allowing alphanumerics and _ in names. + bool first = true; + for (const QChar c : name) { + if (first && !c.isLetter() && c != QLatin1Char('_')) { + return false; + } else if (first) { + first = false; + } else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { + return false; + } + } + return !first; +} + +bool UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value) +{ + // systemd code checks that a value contains no control characters except \n \t + // effectively copied from systemd's string_has_cc + for (const char &it : value.toLatin1()) { + if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) { + continue; + } + if (it > 0 && it < ' ') { + return false; + } + if (it == 127) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/startlingmo/UpdateLaunchEnvironment.hpp b/startlingmo/UpdateLaunchEnvironment.hpp new file mode 100644 index 0000000..1ad55ca --- /dev/null +++ b/startlingmo/UpdateLaunchEnvironment.hpp @@ -0,0 +1,52 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ +#ifndef UPDATELAUNCHENVIRONMENT_HPP +#define UPDATELAUNCHENVIRONMENT_HPP + +#include + +#include + +class QString; +class UpdateLaunchEnvironmentJobPrivate; + +/** + * @class UpdateLaunchEnvironmentJob updatelaunchenvironmentjob.h + * + * Job for updating the launch environment. + * + * This job adds or updates an environment variable in process environment that will be used + * when a process is launched: + * This includes: + * - DBus activation + * - Systemd units + * - lingmo-session + * + * Environment variables are sanitized before uploading. + * + * This object deletes itself after completion, similar to KJobs + */ +class UpdateLaunchEnvironmentJob : public QObject +{ + Q_OBJECT + +public: + explicit UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment); + ~UpdateLaunchEnvironmentJob() override; + +Q_SIGNALS: + void finished(); + +private: + void start(); + +private: + std::unique_ptr const d; +}; + +#endif // UPDATELAUNCHENVIRONMENT_HPP \ No newline at end of file diff --git a/startlingmo/cmake/FindKWinPath.cmake b/startlingmo/cmake/FindKWinPath.cmake new file mode 100644 index 0000000..c959fbd --- /dev/null +++ b/startlingmo/cmake/FindKWinPath.cmake @@ -0,0 +1,29 @@ +function(find_kwin_wayland_bin_path) + # 使用 whereis 命令来查找 kwin 或 kwin_x11 + execute_process( + COMMAND whereis kwin_wayland + OUTPUT_VARIABLE kwin_wayland_output + ERROR_QUIET + ) + + # 解析输出以获取二进制文件的路径 + string(REGEX MATCH "/[^\n\r ]*/kwin_wayland" kwin_wayland_bin_path "${kwin_wayland_output}") + + # 设置 KWIN_WAYLAND_BIN_PATH 变量 + set(KWIN_WAYLAND_BIN_PATH "${kwin_wayland_bin_path}" CACHE PATH "Path to the KWin Wayland binary" FORCE) +endfunction() + +function(find_kwin_bin_path) + # 使用 whereis 命令来查找 kwin 或 kwin_x11 + execute_process( + COMMAND whereis kwin + OUTPUT_VARIABLE kwin_output + ERROR_QUIET + ) + + # 解析输出以获取二进制文件的路径 + string(REGEX MATCH "/[^\n\r ]*/kwin" kwin_bin_path "${kwin_output}") + + # 设置 KWIN_WAYLAND_BIN_PATH 变量 + set(KWIN_BIN "${kwin_bin_path}" CACHE PATH "Path to the KWin binary" FORCE) +endfunction() \ No newline at end of file diff --git a/startlingmo/config-startlingmo.h.cmake b/startlingmo/config-startlingmo.h.cmake new file mode 100644 index 0000000..ccad21b --- /dev/null +++ b/startlingmo/config-startlingmo.h.cmake @@ -0,0 +1,9 @@ +#pragma once + +#define CMAKE_INSTALL_FULL_BINDIR "@CMAKE_INSTALL_FULL_BINDIR@" +#define KDE_INSTALL_FULL_DATAROOTDIR "@KDE_INSTALL_FULL_DATAROOTDIR@" +#define CMAKE_INSTALL_FULL_LIBEXECDIR "@CMAKE_INSTALL_FULL_LIBEXECDIR@" +#define CMAKE_INSTALL_FULL_LIBEXECDIR_KF6 "@CMAKE_INSTALL_FULL_LIBEXECDIR_KF6@" +#define KWIN_WAYLAND_BIN_PATH "@KWIN_WAYLAND_BIN_PATH@" + +#define KWIN_BIN "${KWIN_BIN}" diff --git a/startlingmo/daemon-helper.cpp b/startlingmo/daemon-helper.cpp new file mode 100644 index 0000000..486a0e7 --- /dev/null +++ b/startlingmo/daemon-helper.cpp @@ -0,0 +1,138 @@ +/** + * @name daemon-helper.cpp + * @author Elysia + **/ +#include "daemon-helper.h" +#include "job_private.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace LINGMO_SESSION { +Daemon::Daemon(const QList> &processList, + bool _enableAutoStart, QObject *parent) + : QObject(parent), m_processList(processList), + m_enableAutoRestart(_enableAutoStart) { + for (const auto &processInfo : m_processList) { + startProcess(processInfo); + } +} + +void Daemon::onProcessError(QProcess::ProcessError error) { + const QPointer process = qobject_cast(sender()); + + if (!process) + return; + + QString program = process->program(); + qDebug() << "Process error:" << program << "Error:" << error; + + for (const auto &processInfo : m_processList) { + if (processInfo.first == program) { + qDebug() << "Restarting process due to error:" << program; + QTimer::singleShot(1, this, [this, processInfo]() { + startProcess(processInfo); + }); // Restart after 1 second + return; + } + } +} + +void Daemon::startProcess(const QPair &processInfo) { + const QPointer process = new QProcess(this); + + if (this->m_enableAutoRestart) + connect(process, &QProcess::errorOccurred, this, &Daemon::onProcessError); + + process->start(processInfo.first, processInfo.second); + if (process->waitForStarted()) { + qDebug() << "Process started:" << processInfo.first + << "PID:" << process->processId(); + } else { + qDebug() << "Failed to start process:" << processInfo.first + << process->errorString(); + } +} + +JobPrivate::JobPrivate() {} + +JobPrivate::~JobPrivate() {} + +Job::Job(QObject *parent) : QObject(parent), d_ptr(new JobPrivate) {} + +Job::~Job() { + if (!d_ptr->isFinished) { + d_ptr->isFinished = true; + Q_EMIT finished(this); + } +} + +void Job::emitResult() { + if (!d_func()->isFinished) { + finishJob(true); + } +} +void Job::finishJob(bool emitResult) { + Q_D(Job); + Q_ASSERT(!d->isFinished); + d->isFinished = true; + + if (d->eventLoop) { + d->eventLoop->quit(); + } + + Q_EMIT finished(this); + + if (emitResult) { + Q_EMIT result(this); + } + + if (isAutoDelete()) { + deleteLater(); + } +} + +bool Job::isAutoDelete() const { + Q_D(const Job); + return d->isAutoDelete; +} + +void Job::setAutoDelete(bool autodelete) { + Q_D(Job); + d->isAutoDelete = autodelete; +} + +bool Job::exec() { + Q_D(Job); + // Usually this job would delete itself, via deleteLater() just after + // emitting result() (unless configured otherwise). Since we use an event + // loop below, that event loop will process the deletion event and we'll + // have been deleted when exec() returns. This crashes, so temporarily + // suspend autodeletion and manually do it afterwards. + const bool wasAutoDelete = isAutoDelete(); + setAutoDelete(false); + + Q_ASSERT(!d->eventLoop); + + QEventLoop loop(this); + d->eventLoop = &loop; + + start(); + + if (!d->isFinished) { + d->m_startedWithExec = true; + d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); + } + d->eventLoop = nullptr; + + if (wasAutoDelete) { + deleteLater(); + } + return (d->error == NoError); +} +} // namespace LINGMO_SESSION diff --git a/startlingmo/daemon-helper.h b/startlingmo/daemon-helper.h new file mode 100644 index 0000000..e911942 --- /dev/null +++ b/startlingmo/daemon-helper.h @@ -0,0 +1,203 @@ +#ifndef __DAEMON_HELPER_ +#define __DAEMON_HELPER_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace LINGMO_SESSION { +class JobPrivate; + +class Daemon : public QObject { + Q_OBJECT + +public: + /** + * Start all the passed process using daemon. + * @param processList Process list to start + * @param parent + */ + explicit Daemon(const QList> &processList, + bool _enableAutoStart = true, QObject *parent = nullptr); + +public slots: + + /** + * Handle the case when the progarm has some errors (i.e. crashed) + * @param error + */ + void onProcessError(QProcess::ProcessError error); + +private: + /** + * Start a given process using daemon helper + * @brief startProcess + * @param processInfo + */ + void startProcess(const QPair &processInfo); + + QList> m_processList; + + /** + * @brief Whether to enable auto reload when process exited. + */ + bool m_enableAutoRestart; +}; + +class Job : public QObject { + Q_OBJECT +public: + Job(QObject *parent = nullptr); + + ~Job(); + + /** + * Returns whether this job automatically deletes itself once + * the job is finished. + * + * @return whether the job is deleted automatically after + * finishing. + */ + bool isAutoDelete() const; + + /** + * Sets the auto-delete property of the job. If @p autodelete is + * set to @c false the job will not delete itself once it is finished. + * + * The default for any Job is to automatically delete itself, which + * implies that the job was created on the heap (using new). + * If the job is created on the stack (which isn't the typical use-case + * for a job) then you must set auto-delete to @c false, otherwise you + * could get a crash when the job finishes and tries to delete itself. + * + * @note If you set auto-delete to @c false then you need to kill the + * job manually, ideally by calling kill(). + * + * @param autodelete set to @c false to disable automatic deletion + * of the job. + */ + void setAutoDelete(bool autodelete); + + /** + * Executes the job synchronously. + * + * This will start a nested QEventLoop internally. Nested event loop can be + * dangerous and can have unintended side effects, you should avoid calling + * exec() whenever you can and use the asynchronous interface of Job instead. + * + * Should you indeed call this method, you need to make sure that all callers + * are reentrant, so that events delivered by the inner event loop don't cause + * non-reentrant functions to be called, which usually wreaks havoc. + * + * Note that the event loop started by this method does not process user input + * events, which means your user interface will effectively be blocked. Other + * events like paint or network events are still being processed. The + * advantage of not processing user input events is that the chance of + * accidental reentrance is greatly reduced. Still you should avoid calling + * this function. + * + * @return true if the job has been executed without error, false otherwise + */ + bool exec(); + + /** + * Starts the job asynchronously. + * + * When the job is finished, result() is emitted. + * + * Warning: Never implement any synchronous workload in this method. This + * method should just trigger the job startup, not do any work itself. It is + * expected to be non-blocking. + * + * This is the method all subclasses need to implement. + * It should setup and trigger the workload of the job. It should not do any + * work itself. This includes all signals and terminating the job, e.g. by + * emitResult(). The workload, which could be another method of the + * subclass, is to be triggered using the event loop, e.g. by code like: + * \code + * void ExampleJob::start() + * { + * QTimer::singleShot(0, this, &ExampleJob::doWork); + * } + * \endcode + */ + Q_SCRIPTABLE virtual void start() = 0; + + enum { + /*** Indicates there is no error */ + NoError = 0, + /*** Indicates the job was killed */ + KilledJobError = 1, + /*** Subclasses should define error codes starting at this value */ + UserDefinedError = 100, + }; +Q_SIGNALS: + /** + * Emitted when the job is finished, in any case. It is used to notify + * observers that the job is terminated and that progress can be hidden. + * + * This signal is guaranteed to be emitted exactly once. + * + * This is a private signal, it can't be emitted directly by subclass, use + * emitResult() instead. + * + * In general, to be notified of a job's completion, client code should + * connect to result() rather than finished(), so that kill(Quietly) is indeed + * quiet. However if you store a list of jobs and they might get killed + * silently, then you must connect to this instead of result(), to avoid + * dangling pointers in your list. + * + * @param job the job that emitted this signal + * @internal + * + * @see result + */ + void finished(Job *job); + + /** + * Emitted when the job is finished (except when killed with KJob::Quietly). + * + * This signal is guaranteed to be emitted at most once. + * + * Use error to know if the job was finished with error. + * + * This is a private signal, it can't be emitted directly by subclasses of + * KJob, use emitResult() instead. + * + * Please connect to this signal instead of finished. + * + * @param job the job that emitted this signal + * + * @see kill + */ + void result(Job *job); + +protected : + /** + * Utility function to emit the result signal, and end this job. + * It first notifies the observers to hide the progress for this job using + * the finished() signal. + * + * @note Deletes this job using deleteLater(). + * + * @see result() + * @see finished() + */ + Q_SLOT void emitResult(); + + std::unique_ptr const d_ptr; + +private: + void finishJob(bool emitResult); + + Q_DECLARE_PRIVATE(Job) +}; +} // namespace LINGMO_SESSION +#endif diff --git a/session/job_private.hpp b/startlingmo/job_private.hpp similarity index 100% rename from session/job_private.hpp rename to startlingmo/job_private.hpp diff --git a/startlingmo/lingmo-session/CMakeLists.txt b/startlingmo/lingmo-session/CMakeLists.txt new file mode 100755 index 0000000..1a9f4fd --- /dev/null +++ b/startlingmo/lingmo-session/CMakeLists.txt @@ -0,0 +1,46 @@ +project(lingmo_session) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED 17) + +set(TARGET lingmo_session) + +set(SOURCES + application.cpp + main.cpp + process.cpp + processmanager.cpp + networkproxymanager.cpp + + powermanager/power.cpp + powermanager/powerproviders.cpp +) + +qt_add_dbus_adaptor(DBUS_SOURCES + com.lingmo.Session.xml + application.h Application + sessionadaptor SessionAdaptor) +# set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) + +find_package(KF5WindowSystem) +find_package(Threads) + +ecm_qt_declare_logging_category(SOURCES HEADER debug.h IDENTIFIER LINGMO_SESSION_D CATEGORY_NAME org.lingmo.session) + +add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES}) +target_link_libraries(${TARGET} + PRIVATE + startlingmo + PUBLIC + Qt5::Core + Qt5::Gui + Qt5::Widgets + Qt5::Quick + Qt5::DBus + Qt5::X11Extras + KF5::WindowSystem + ${CMAKE_THREAD_LIBS_INIT} +) +target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) + +install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/startlingmo/lingmo-session/application.cpp b/startlingmo/lingmo-session/application.cpp new file mode 100755 index 0000000..8d6f0ea --- /dev/null +++ b/startlingmo/lingmo-session/application.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#include "application.h" +#include "sessionadaptor.h" + +// Qt +#include +#include +#include +#include +#include +#include + +#include +#include + +// STL +#include +#include + +#include "startlingmo.hpp" +#include "debug.h" + +Application::Application(const QCommandLineParser &parser, QObject *parent) + : QObject(parent), m_processManager(new ProcessManager(this)), + m_networkProxyManager(new NetworkProxyManager), m_wayland(false) { + new SessionAdaptor(this); + + // connect to D-Bus and register as an object: + QDBusConnection::sessionBus().registerService( + QStringLiteral("com.lingmo.Session")); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Session"), + this); + + m_wayland = parser.isSet("wayland"); + + m_networkProxyManager->update(); + + // Launch Lingmo and user defined processes ! + QTimer::singleShot(100, m_processManager, &ProcessManager::start); +} + +bool Application::wayland() const { return m_wayland; } + +void Application::updateLaunchEnv(const QString &key, const QString &value) { + qCDebug(LINGMO_SESSION_D) << "Update launch env: " << key << value; + qputenv(key.toLatin1(), value.toLatin1()); + m_processManager->updateLaunchEnv(key, value); +} + +void Application::launch(const QString &exec, const QStringList &args) { + QProcess process; + process.setProgram(exec); + process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); + process.setArguments(args); + process.startDetached(); +} + +void Application::launch(const QString &exec, const QString &workingDir, + const QStringList &args) { + QProcess process; + process.setProgram(exec); + process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); + process.setWorkingDirectory(workingDir); + process.setArguments(args); + process.startDetached(); +} + +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", "Lingmo"); + qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); + qputenv("XDG_SESSION_DESKTOP", "Lingmo"); + + // Qt + qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); + qputenv("QT_PLATFORM_PLUGIN", "lingmo"); + + // ref: + // https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping + qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); + + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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::initLanguage() { + QSettings settings(QSettings::UserScope, "lingmoos", "language"); + QString value = settings.value("language", "").toString(); + + // Init Language + if (value.isEmpty()) { + QFile file("/etc/locale.gen"); + if (file.open(QIODevice::ReadOnly)) { + QStringList lines = QString(file.readAll()).split('\n'); + + for (const QString &line : lines) { + if (line.startsWith('#')) + continue; + + if (line.trimmed().isEmpty()) + continue; + + value = line.split(' ').first().split('.').first(); + } + } + } + + if (value.isEmpty()) + value = "en_US"; + + settings.setValue("language", value); + + 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, "lingmoos", "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)); + } +} + +void Application::initXResource() { + QSettings settings(QSettings::UserScope, "lingmoos", "theme"); + qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); + int fontDpi = 96 * scaleFactor; + QString cursorTheme = settings.value("CursorTheme", "default").toString(); + int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; + int xftAntialias = settings.value("XftAntialias", 1).toBool(); + QString xftHintStyle = + settings.value("XftHintStyle", "hintslight").toString(); + + const QString datas = QString("Xft.dpi: %1\n" + "Xcursor.theme: %2\n" + "Xcursor.size: %3\n" + "Xft.antialias: %4\n" + "Xft.hintstyle: %5\n" + "Xft.rgba: rgb") + .arg(fontDpi) + .arg(cursorTheme) + .arg(cursorSize) + .arg(xftAntialias) + .arg(xftHintStyle); + + QProcess p; + p.start(QStringLiteral("xrdb"), + {QStringLiteral("-quiet"), QStringLiteral("-merge"), + QStringLiteral("-nocpp")}); + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.write(datas.toLatin1()); + p.closeWriteChannel(); + p.waitForFinished(-1); + + // For lingmo-wine + qputenv("LINGMO_FONT_DPI", QByteArray::number(fontDpi)); + + // Init cursor + runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); + // qputenv("XCURSOR_THEME", cursorTheme.toLatin1()); + // qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor)); +} + +void Application::initKWinConfig() { + QSettings settings( + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + "/kwinrc", + QSettings::IniFormat); + + settings.beginGroup("Effect-Blur"); + settings.setValue("BlurStrength", 10); + settings.setValue("NoiseStrength", 0); + settings.endGroup(); + + settings.beginGroup("Windows"); + settings.setValue("FocusStealingPreventionLevel", 0); + settings.setValue("HideUtilityWindowsForInactive", false); + settings.setValue("BorderlessMaximizedWindows", false); + settings.setValue("Placement", "Centered"); + settings.endGroup(); + + settings.beginGroup("org.kde.kdecoration2"); + settings.setValue("BorderSize", "Normal"); + settings.setValue("ButtonsOnLeft", ""); + settings.setValue("ButtonsOnRight", "HIAX"); + settings.setValue("library", "org.lingmo.decoration"); + settings.setValue("theme", ""); + settings.endGroup(); +} + +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(); +} diff --git a/startlingmo/lingmo-session/application.h b/startlingmo/lingmo-session/application.h new file mode 100755 index 0000000..e681d2c --- /dev/null +++ b/startlingmo/lingmo-session/application.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include +#include +#include +#include +#include +#include + +#include "networkproxymanager.h" +#include "powermanager/power.h" +#include "processmanager.h" + +class Application : public QObject { + Q_OBJECT + +public: + explicit Application(const QCommandLineParser &parser, + QObject *parent = nullptr); + + bool wayland() const; + +public slots: + void updateLaunchEnv(const QString &key, const QString &value); + + void logout() { m_processManager->logout(); } + + void reboot() { + m_power.reboot(); + QCoreApplication::exit(0); + } + + void powerOff() { + m_power.shutdown(); + QCoreApplication::exit(0); + } + + void suspend() { m_power.suspend(); } + + [[maybe_unused]] void startDesktopProcess() { + // Start Lingmo Desktop Environment + m_processManager->startDesktopProcess(); + } + + [[maybe_unused]] void updateNetworkProxy() { + m_networkProxyManager->update(); + } + + void launch(const QString &exec, const QStringList &args); + void launch(const QString &exec, const QString &workingDir, + const QStringList &args); + +private: + void initEnvironments(); + void initLanguage(); + void initScreenScaleFactors(); + void initXResource(); + void initKWinConfig(); + bool syncDBusEnvironment(); + void importSystemdEnvrionment(); + void createConfigDirectory(); + void updateUserDirs(); + int runSync(const QString &program, const QStringList &args, + const QStringList &env = {}); + +private: + ProcessManager *m_processManager; + NetworkProxyManager *m_networkProxyManager; + Power m_power; + + bool m_wayland; +}; + +#endif // APPLICATION_H diff --git a/startlingmo/lingmo-session/com.lingmo.Session.xml b/startlingmo/lingmo-session/com.lingmo.Session.xml new file mode 100755 index 0000000..eb52db4 --- /dev/null +++ b/startlingmo/lingmo-session/com.lingmo.Session.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/startlingmo/lingmo-session/main.cpp b/startlingmo/lingmo-session/main.cpp new file mode 100755 index 0000000..654d7b4 --- /dev/null +++ b/startlingmo/lingmo-session/main.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#include "application.h" +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + QQuickWindow::setDefaultAlphaBuffer(true); + QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); + + QCoreApplication app(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("Lingmo Session")); + parser.addHelpOption(); + + QCommandLineOption waylandOption(QStringList() << "w" + << "wayland" + << "Wayland Mode"); + parser.addOption(waylandOption); + parser.process(app); + + new Application(parser, &app); + + return app.exec(); +} diff --git a/startlingmo/lingmo-session/networkproxymanager.cpp b/startlingmo/lingmo-session/networkproxymanager.cpp new file mode 100755 index 0000000..aba6301 --- /dev/null +++ b/startlingmo/lingmo-session/networkproxymanager.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: Reion Wong + * + * 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 . + */ + + +#include "networkproxymanager.h" +#include +#include +#include +#include + +NetworkProxyManager::NetworkProxyManager(QObject *parent) + : QObject(parent) + , m_settings(QSettings::UserScope, "lingmoos", "network") +{ +} + +void NetworkProxyManager::update() +{ + qunsetenv("HTTP_PROXY"); + qunsetenv("HTTPS_PROXY"); + qunsetenv("FTP_PROXY"); + qunsetenv("ALL_PROXY"); + qunsetenv("NO_PROXY"); + + qunsetenv("http_proxy"); + qunsetenv("https_proxy"); + qunsetenv("ftp_proxy"); + qunsetenv("all_proxy"); + qunsetenv("no_proxy"); + + m_settings.sync(); + + m_flag = m_settings.value("ProxyFlag", 0).toInt(); + m_useSameProxy = m_settings.value("UseSameProxy", false).toBool(); + m_scriptProxy = m_settings.value("ProxyScriptProxy", "").toString(); + m_httpProxy = m_settings.value("HttpProxy", "").toString(); + m_ftpProxy = m_settings.value("FtpProxy", "").toString(); + m_socksProxy = m_settings.value("SocksProxy", "").toString(); + m_httpProxyPort = m_settings.value("HttpProxyPort", "").toString(); + m_ftpProxyPort = m_settings.value("FtpProxyPort", "").toString(); + m_socksProxyPort = m_settings.value("SocksProxyPort", "").toString(); + + QMap dbusActivationEnv; + QStringList systemdUpdates; + + if (m_flag == 0) { + // No proxy + } else if (m_flag == 1) { + // Use proxy auto configuration URL + } else if (m_flag == 2) { + // Use manually specified proxy configuration + + QString httpProxy = QString("http://%1:%2/").arg(m_httpProxy).arg(m_httpProxyPort); + QString ftpProxy = QString("http://%1:%2/").arg(m_ftpProxy).arg(m_ftpProxyPort); + + if (m_useSameProxy) { + ftpProxy = httpProxy; + } + + if (!m_httpProxy.isEmpty() && !m_httpProxyPort.isEmpty()) { + qputenv("HTTP_PROXY", httpProxy.toLatin1()); + qputenv("HTTPS_PROXY", httpProxy.toLatin1()); + + qputenv("http_proxy", httpProxy.toLatin1()); + qputenv("https_proxy", httpProxy.toLatin1()); + } + + if (!m_ftpProxy.isEmpty() && !m_ftpProxyPort.isEmpty()) { + qputenv("FTP_PROXY", ftpProxy.toLatin1()); + qputenv("ftp_proxy", ftpProxy.toLatin1()); + } + + qputenv("NO_PROXY", "localhost,127.0.0.0/8,::1"); + qputenv("no_proxy", "localhost,127.0.0.0/8,::1"); + + if (!m_socksProxy.isEmpty() && !m_socksProxyPort.isEmpty()) { + // qputenv("ALL_PROXY", QString("socks://%1:%2/").arg(m_socksProxy).arg(m_socksProxyPort).toLatin1()); + // qputenv("all_proxy", QString("socks://%1:%2/").arg(m_socksProxy).arg(m_socksProxyPort).toLatin1()); + } + } +} diff --git a/startlingmo/lingmo-session/networkproxymanager.h b/startlingmo/lingmo-session/networkproxymanager.h new file mode 100755 index 0000000..dbbd14c --- /dev/null +++ b/startlingmo/lingmo-session/networkproxymanager.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: Reion Wong + * + * 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 . + */ + +#ifndef NETWORKPROXYMANAGER_H +#define NETWORKPROXYMANAGER_H + +#include +#include + +class NetworkProxyManager : public QObject +{ + Q_OBJECT + +public: + explicit NetworkProxyManager(QObject *parent = nullptr); + + void update(); + +private: + QSettings m_settings; + + int m_flag; + bool m_useSameProxy; + + QString m_scriptProxy; + QString m_httpProxy; + QString m_ftpProxy; + QString m_socksProxy; + + QString m_httpProxyPort; + QString m_ftpProxyPort; + QString m_socksProxyPort; +}; + +#endif // NETWORKPROXYMANAGER_H diff --git a/startlingmo/lingmo-session/powermanager/power.cpp b/startlingmo/lingmo-session/powermanager/power.cpp new file mode 100755 index 0000000..534b6be --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/power.cpp @@ -0,0 +1,80 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#include "power.h" +#include "powerproviders.h" + +#include +#include + +Power::Power(bool useSessionProvider, QObject * parent /*= nullptr*/) : + QObject(parent) +{ + m_providers.append(new SystemdProvider(this)); + m_providers.append(new UPowerProvider(this)); + m_providers.append(new ConsoleKitProvider(this)); +} + +Power::Power(QObject * parent /*= nullptr*/) + : Power(true, parent) +{ +} + +Power::~Power() +{ +} + +bool Power::canAction(Power::Action action) const +{ + for(const PowerProvider* provider : qAsConst(m_providers)) + if (provider->canAction(action)) + return true; + + return false; +} + +bool Power::doAction(Power::Action action) +{ + for(PowerProvider* provider : qAsConst(m_providers)) { + if (provider->canAction(action) && + provider->doAction(action)) { + return true; + } + } + return false; +} + +bool Power::canLogout() const { return canAction(PowerLogout); } +bool Power::canHibernate() const { return canAction(PowerHibernate); } +bool Power::canReboot() const { return canAction(PowerReboot); } +bool Power::canShutdown() const { return canAction(PowerShutdown); } +bool Power::canSuspend() const { return canAction(PowerSuspend); } +bool Power::canMonitorOff() const { return canAction(PowerMonitorOff); } +bool Power::canShowLeaveDialog() const { return canAction(PowerShowLeaveDialog); } + +bool Power::logout() { return doAction(PowerLogout); } +bool Power::hibernate() { return doAction(PowerHibernate); } +bool Power::reboot() { return doAction(PowerReboot); } +bool Power::shutdown() { return doAction(PowerShutdown); } +bool Power::suspend() { return doAction(PowerSuspend); } +bool Power::monitorOff() { return doAction(PowerMonitorOff); } +bool Power::showLeaveDialog() { return doAction(PowerShowLeaveDialog); } diff --git a/startlingmo/lingmo-session/powermanager/power.h b/startlingmo/lingmo-session/powermanager/power.h new file mode 100755 index 0000000..8583943 --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/power.h @@ -0,0 +1,118 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#ifndef POWER_H +#define POWER_H + +#include +#include + +class PowerProvider; + +/*! Power class provides an interface to control system-wide power and session management. + It allows logout from the user session, hibernate, reboot, shutdown and suspend computer. + This is a wrapper class. All the real work is done in the PowerWorker classes. +*/ +class Power : public QObject +{ + Q_OBJECT + +public: + /// Power can perform next actions: + enum Action{ + PowerLogout, /// Close the current user session. + PowerHibernate, /// Hibernate the comupter + PowerReboot, /// Reboot the computer + PowerShutdown, /// Shutdown the computer + PowerSuspend, /// Suspend the computer + PowerMonitorOff, /// Turn off the monitor(s) + PowerShowLeaveDialog /// Show the lxqt-leave dialog + }; + + /*! + * Constructs the Power object. + * \param useLxqtSessionProvider indicates if the DBus methods + * provided by lxqt-session should be considered. This is useful to + * avoid recursion if the lxqt-session wants to provide some of the + * methods by itself with internal use of this object. + */ + explicit Power(bool useSessionProvider, QObject *parent = nullptr); + /// Constructs a Power with using the lxqt-session provider. + explicit Power(QObject *parent = nullptr); + + /// Destroys the object. + ~Power() override; + + /// Returns true if the Power can perform action. + bool canAction(Action action) const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerLogout). + bool canLogout() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerHibernate). + bool canHibernate() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerReboot). + bool canReboot() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerShutdown). + bool canShutdown() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerSuspend). + bool canSuspend() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerMonitorOff). + bool canMonitorOff() const; + + //! This function is provided for convenience. It's equivalent to calling canAction(PowerShowLeaveDialog). + bool canShowLeaveDialog() const; + +public Q_SLOTS: + /// Performs the requested action. + bool doAction(Action action); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerLogout). + bool logout(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerHibernate). + bool hibernate(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerReboot). + bool reboot(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerShutdown). + bool shutdown(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerSuspend). + bool suspend(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerMonitorOff). + bool monitorOff(); + + //! This function is provided for convenience. It's equivalent to calling doAction(PowerShowLeaveDialog). + bool showLeaveDialog(); + +private: + QList m_providers; +}; + +#endif diff --git a/startlingmo/lingmo-session/powermanager/powerproviders.cpp b/startlingmo/lingmo-session/powermanager/powerproviders.cpp new file mode 100755 index 0000000..63a702b --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/powerproviders.cpp @@ -0,0 +1,448 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * Petr Vanek + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#include "powerproviders.h" +#include +#include +#include +#include // for kill() + +#define UPOWER_SERVICE "org.freedesktop.UPower" +#define UPOWER_PATH "/org/freedesktop/UPower" +#define UPOWER_INTERFACE UPOWER_SERVICE + +#define CONSOLEKIT_SERVICE "org.freedesktop.ConsoleKit" +#define CONSOLEKIT_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CONSOLEKIT_INTERFACE "org.freedesktop.ConsoleKit.Manager" + +#define SYSTEMD_SERVICE "org.freedesktop.login1" +#define SYSTEMD_PATH "/org/freedesktop/login1" +#define SYSTEMD_INTERFACE "org.freedesktop.login1.Manager" + +#define PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" + +/************************************************ + Helper func + ************************************************/ +void printDBusMsg(const QDBusMessage &msg) +{ + qWarning() << "** Dbus error **************************"; + qWarning() << "Error name " << msg.errorName(); + qWarning() << "Error msg " << msg.errorMessage(); + qWarning() << "****************************************"; +} + +/************************************************ + Helper func + ************************************************/ +static bool dbusCall(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection, + const QString & method, + PowerProvider::DbusErrorCheck errorCheck = PowerProvider::CheckDBUS + ) +{ + QDBusInterface dbus(service, path, interface, connection); + + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service << path << interface << method; + if (errorCheck == PowerProvider::CheckDBUS) + { + // Notification::notify( + // QObject::tr("Power Manager Error"), + // QObject::tr("QDBusInterface is invalid") + QStringLiteral("\n\n") + service + QStringLiteral(' ') + path + QStringLiteral(' ') + interface + QStringLiteral(' ') + method, + // QStringLiteral("logo.png")); + } + return false; + } + + QDBusMessage msg = dbus.call(method); + if (!msg.errorName().isEmpty()) { + printDBusMsg(msg); + if (errorCheck == PowerProvider::CheckDBUS) + { + // Notification::notify( + // QObject::tr("Power Manager Error (D-BUS call)"), + // msg.errorName() + QStringLiteral("\n\n") + msg.errorMessage(), + // QStringLiteral("logo.png")); + } + } + + // If the method no returns value, we believe that it was successful. + return msg.arguments().isEmpty() || + msg.arguments().constFirst().isNull() || + msg.arguments().constFirst().toBool(); +} + +/************************************************ + Helper func + + Just like dbusCall(), except that systemd + returns a string instead of a bool, and it takes + an "interactivity boolean" as an argument. + ************************************************/ +static bool dbusCallSystemd(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection, + const QString &method, + bool needBoolArg, + PowerProvider::DbusErrorCheck errorCheck = PowerProvider::CheckDBUS + ) +{ + QDBusInterface dbus(service, path, interface, connection); + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service << path << interface << method; + if (errorCheck == PowerProvider::CheckDBUS) { + // Notification::notify( + // QObject::tr("Power Manager Error"), + // QObject::tr("QDBusInterface is invalid") + QStringLiteral("\n\n") + service + QStringLiteral(' ') + path + QStringLiteral(' ')+ interface + QStringLiteral(' ') + method, + // QStringLiteral("logo.png")); + } + + return false; + } + + QDBusMessage msg = dbus.call(method, needBoolArg ? QVariant(true) : QVariant()); + + if (!msg.errorName().isEmpty()) { + printDBusMsg(msg); + if (errorCheck == PowerProvider::CheckDBUS) { + // Notification::notify( + // QObject::tr("Power Manager Error (D-BUS call)"), + // msg.errorName() + QStringLiteral("\n\n") + msg.errorMessage(), + // QStringLiteral("logo.png")); + } + } + + // If the method no returns value, we believe that it was successful. + if (msg.arguments().isEmpty() || msg.arguments().constFirst().isNull()) + return true; + + QString response = msg.arguments().constFirst().toString(); + qDebug() << "systemd:" << method << "=" << response; + return response == QStringLiteral("yes") || response == QStringLiteral("challenge"); +} + +/************************************************ + Helper func + ************************************************/ +bool dbusGetProperty(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection, + const QString & property + ) +{ + QDBusInterface dbus(service, path, interface, connection); + if (!dbus.isValid()) + { + qWarning() << "dbusGetProperty: QDBusInterface is invalid" << service << path << interface << property; +// Notification::notify(QObject::tr("Power Manager"), +// "logo.png", +// QObject::tr("Power Manager Error"), +// QObject::tr("QDBusInterface is invalid")+ "\n\n" + service +" " + path +" " + interface +" " + property); + + return false; + } + + QDBusMessage msg = dbus.call(QStringLiteral("Get"), dbus.interface(), property); + + if (!msg.errorName().isEmpty()) + { + printDBusMsg(msg); +// Notification::notify(QObject::tr("Power Manager"), +// "logo.png", +// QObject::tr("Power Manager Error (Get Property)"), +// msg.errorName() + "\n\n" + msg.errorMessage()); + } + + return !msg.arguments().isEmpty() && + msg.arguments().constFirst().value().variant().toBool(); +} + +/************************************************ + PowerProvider + ************************************************/ +PowerProvider::PowerProvider(QObject *parent): + QObject(parent) +{ +} + +PowerProvider::~PowerProvider() +{ +} + +/************************************************ + UPowerProvider + ************************************************/ +UPowerProvider::UPowerProvider(QObject *parent): + PowerProvider(parent) +{ +} + +UPowerProvider::~UPowerProvider() +{ +} + +bool UPowerProvider::canAction(Power::Action action) const +{ + QString command; + QString property; + switch (action) { + case Power::PowerHibernate: + property = QStringLiteral("CanHibernate"); + command = QStringLiteral("HibernateAllowed"); + break; + case Power::PowerSuspend: + property = QStringLiteral("CanSuspend"); + command = QStringLiteral("SuspendAllowed"); + break; + default: + return false; + } + + return dbusGetProperty( // Whether the system is able to hibernate. + QStringLiteral(UPOWER_SERVICE), + QStringLiteral(UPOWER_PATH), + QStringLiteral(PROPERTIES_INTERFACE), + QDBusConnection::systemBus(), + property + ) + && + dbusCall( // Check if the caller has (or can get) the PolicyKit privilege to call command. + QStringLiteral(UPOWER_SERVICE), + QStringLiteral(UPOWER_PATH), + QStringLiteral(UPOWER_INTERFACE), + QDBusConnection::systemBus(), + command, + // canAction should be always silent because it can freeze + // g_main_context_iteration Qt event loop in QMessageBox + // on panel startup if there is no DBUS running. + PowerProvider::DontCheckDBUS + ); +} + +bool UPowerProvider::doAction(Power::Action action) +{ + QString command; + + switch (action) { + case Power::PowerHibernate: + command = QStringLiteral("Hibernate"); + break; + case Power::PowerSuspend: + command = QStringLiteral("Suspend"); + break; + default: + return false; + } + + + return dbusCall(QStringLiteral(UPOWER_SERVICE), + QStringLiteral(UPOWER_PATH), + QStringLiteral(UPOWER_INTERFACE), + QDBusConnection::systemBus(), + command ); +} + +/************************************************ + ConsoleKitProvider + ************************************************/ +ConsoleKitProvider::ConsoleKitProvider(QObject *parent): + PowerProvider(parent) +{ +} + +ConsoleKitProvider::~ConsoleKitProvider() +{ +} + +bool ConsoleKitProvider::canAction(Power::Action action) const +{ + QString command; + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("CanReboot"); + break; + + case Power::PowerShutdown: + command = QStringLiteral("CanPowerOff"); + break; + + case Power::PowerHibernate: + command = QStringLiteral("CanHibernate"); + break; + + case Power::PowerSuspend: + command = QStringLiteral("CanSuspend"); + break; + + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(CONSOLEKIT_SERVICE), + QStringLiteral(CONSOLEKIT_PATH), + QStringLiteral(CONSOLEKIT_INTERFACE), + QDBusConnection::systemBus(), + command, + false, + // canAction should be always silent because it can freeze + // g_main_context_iteration Qt event loop in QMessageBox + // on panel startup if there is no DBUS running. + PowerProvider::DontCheckDBUS + ); +} + +bool ConsoleKitProvider::doAction(Power::Action action) +{ + QString command; + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("Reboot"); + break; + case Power::PowerShutdown: + command = QStringLiteral("PowerOff"); + break; + case Power::PowerHibernate: + command = QStringLiteral("Hibernate"); + break; + case Power::PowerSuspend: + command = QStringLiteral("Suspend"); + break; + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(CONSOLEKIT_SERVICE), + QStringLiteral(CONSOLEKIT_PATH), + QStringLiteral(CONSOLEKIT_INTERFACE), + QDBusConnection::systemBus(), + command, + true); +} + +/************************************************ + SystemdProvider + + http://www.freedesktop.org/wiki/Software/systemd/logind + ************************************************/ + +SystemdProvider::SystemdProvider(QObject *parent): + PowerProvider(parent) +{ +} + +SystemdProvider::~SystemdProvider() +{ +} + +bool SystemdProvider::canAction(Power::Action action) const +{ + QString command; + + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("CanReboot"); + break; + case Power::PowerShutdown: + command = QStringLiteral("CanPowerOff"); + break; + case Power::PowerSuspend: + command = QStringLiteral("CanSuspend"); + break; + case Power::PowerHibernate: + command = QStringLiteral("CanHibernate"); + break; + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(SYSTEMD_SERVICE), + QStringLiteral(SYSTEMD_PATH), + QStringLiteral(SYSTEMD_INTERFACE), + QDBusConnection::systemBus(), + command, + false, + // canAction should be always silent because it can freeze + // g_main_context_iteration Qt event loop in QMessageBox + // on panel startup if there is no DBUS running. + PowerProvider::DontCheckDBUS + ); +} + +bool SystemdProvider::doAction(Power::Action action) +{ + QString command; + + switch (action) { + case Power::PowerReboot: + command = QStringLiteral("Reboot"); + break; + case Power::PowerShutdown: + command = QStringLiteral("PowerOff"); + break; + case Power::PowerSuspend: + command = QStringLiteral("Suspend"); + break; + case Power::PowerHibernate: + command = QStringLiteral("Hibernate"); + break; + default: + return false; + } + + return dbusCallSystemd(QStringLiteral(SYSTEMD_SERVICE), + QStringLiteral(SYSTEMD_PATH), + QStringLiteral(SYSTEMD_INTERFACE), + QDBusConnection::systemBus(), + command, + true + ); +} + +/************************************************ + HalProvider + ************************************************/ +HalProvider::HalProvider(QObject *parent): + PowerProvider(parent) +{ +} + +HalProvider::~HalProvider() +{ +} + +bool HalProvider::canAction(Power::Action action) const +{ + Q_UNUSED(action) + return false; +} + +bool HalProvider::doAction(Power::Action action) +{ + Q_UNUSED(action) + return false; +} diff --git a/startlingmo/lingmo-session/powermanager/powerproviders.h b/startlingmo/lingmo-session/powermanager/powerproviders.h new file mode 100755 index 0000000..ec77cb8 --- /dev/null +++ b/startlingmo/lingmo-session/powermanager/powerproviders.h @@ -0,0 +1,109 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * Authors: + * Alexander Sokoloff + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#ifndef POWERPROVIDERS_H +#define POWERPROVIDERS_H + +#include "power.h" + +#include +#include // for PID_T + +class PowerProvider: public QObject +{ + Q_OBJECT + +public: + enum DbusErrorCheck { + CheckDBUS, + DontCheckDBUS + }; + + explicit PowerProvider(QObject *parent = nullptr); + ~PowerProvider() override; + + /*! Returns true if the Power can perform action. + This is a pure virtual function, and must be reimplemented in subclasses. */ + virtual bool canAction(Power::Action action) const = 0 ; + +public Q_SLOTS: + /*! Performs the requested action. + This is a pure virtual function, and must be reimplemented in subclasses. */ + virtual bool doAction(Power::Action action) = 0; +}; + + +class UPowerProvider: public PowerProvider +{ + Q_OBJECT + +public: + UPowerProvider(QObject *parent = nullptr); + ~UPowerProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + + +class ConsoleKitProvider: public PowerProvider +{ + Q_OBJECT + +public: + ConsoleKitProvider(QObject *parent = nullptr); + ~ConsoleKitProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + + +class SystemdProvider: public PowerProvider +{ + Q_OBJECT + +public: + SystemdProvider(QObject *parent = nullptr); + ~SystemdProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + +class HalProvider: public PowerProvider +{ + Q_OBJECT + +public: + HalProvider(QObject *parent = nullptr); + ~HalProvider() override; + bool canAction(Power::Action action) const override; + +public Q_SLOTS: + bool doAction(Power::Action action) override; +}; + +#endif diff --git a/startlingmo/lingmo-session/process.cpp b/startlingmo/lingmo-session/process.cpp new file mode 100755 index 0000000..06d33a5 --- /dev/null +++ b/startlingmo/lingmo-session/process.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#include "process.h" + +Process::Process(QObject *parent) + : QProcess(parent) +{ + QProcess::setProcessChannelMode(QProcess::ForwardedChannels); +} + +Process::~Process() +{ +} diff --git a/startlingmo/lingmo-session/process.h b/startlingmo/lingmo-session/process.h new file mode 100755 index 0000000..3ab3c57 --- /dev/null +++ b/startlingmo/lingmo-session/process.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#ifndef PROCESS_H +#define PROCESS_H + +#include + +class Process : public QProcess +{ + Q_OBJECT + +public: + Process(QObject *parent = nullptr); + ~Process(); +}; + +#endif diff --git a/startlingmo/lingmo-session/processmanager.cpp b/startlingmo/lingmo-session/processmanager.cpp new file mode 100755 index 0000000..e38d70d --- /dev/null +++ b/startlingmo/lingmo-session/processmanager.cpp @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2023-2024 Lingmo OS Team. + */ + +#include "processmanager.h" +#include "application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "daemon-helper.h" +#include "debug.h" + +ProcessManager *s_self; + +ProcessManager::ProcessManager(Application *app, QObject *parent) + : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { + Q_ASSERT(!s_self); + s_self = this; + + qApp->installNativeEventFilter(this); +} + +ProcessManager::~ProcessManager() { + qApp->removeNativeEventFilter(this); + + QMapIterator i(m_systemProcess); + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + delete p; + m_systemProcess[i.key()] = nullptr; + } +} + +void ProcessManager::updateLaunchEnv(const QString &key, const QString &value) { + qputenv(key.toLatin1(), value.toLatin1()); +} + +void ProcessManager::start() { + startWindowManager(); + startDaemonProcess(); +} + +void ProcessManager::logout() { + QMapIterator i(m_systemProcess); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + p->terminate(); + } + i.toFront(); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { + p->kill(); + } + } + + QCoreApplication::exit(0); +} + +void ProcessManager::startWindowManager() { + auto detcted_wayland = + qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland"); + + if (detcted_wayland || m_app->wayland()) { + auto kwinWaylandJob = + new StartServiceJob(QStringLiteral("lingmo_kwin_wayland_wrapper"), + {QStringLiteral("--xwayland")}, + QStringLiteral("org.lingmo.KWinWrapper")); + kwinWaylandJob->setParent(this); + kwinWaylandJob->exec(); // Wait untill kwin_wayland_wrapper started + } else { + auto *wmProcess = new QProcess; + + wmProcess->start("kwin_x11", QStringList()); + } +} + +void ProcessManager::startDesktopProcess() { + // When the lingmo-settings-daemon theme module is loaded, start the desktop. + // In the way, there will be no problem that desktop and launcher can't get + // wallpaper. + + auto xcb_extra = QProcessEnvironment(); + xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); + const QVector sequence = { + new LaunchProcess(QStringLiteral("lingmo-notificationd"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-statusbar"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-dock"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-filemanager"), + QStringList("--desktop"), xcb_extra), + + new LaunchProcess(QStringLiteral("lingmo-launcher"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-powerman"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-clipboard"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-wallpaper-color-pick"), {}, + xcb_extra), + }; + Job *last = nullptr; + for (Job *job : sequence) { + if (!job) { + continue; + } + if (last) { + connect(last, &Job::finished, job, &Job::start); + } + last = job; + } + + // connect(sequence.last(), &Job::finished, this, &Startup::finishStartup); + sequence.first()->start(); +} + +void ProcessManager::startDaemonProcess() { + auto xcb_extra = QProcessEnvironment(); + xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); + const QVector sequence = { + new LaunchProcess(QStringLiteral("lingmo-settings-daemon"), {}, + xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-xembedsniproxy"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-gmenuproxy"), {}, xcb_extra), + new LaunchProcess(QStringLiteral("lingmo-permission-surveillance"), {}, + xcb_extra), + }; + Job *last = nullptr; + for (Job *job : sequence) { + if (!job) { + continue; + } + if (last) { + connect(last, &Job::finished, job, &Job::start); + } + last = job; + } + + connect(sequence.last(), &Job::finished, this, &ProcessManager::startDesktopProcess); + sequence.first()->start(); +} + +void ProcessManager::loadAutoStartProcess() { + QList> list; + + const QStringList dirs = QStandardPaths::locateAll( + QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), + QStandardPaths::LocateDirectory); + for (const QString &dir : dirs) { + const QDir d(dir); + const QStringList fileNames = + d.entryList(QStringList() << QStringLiteral("*.desktop")); + for (const QString &file : fileNames) { + QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); + desktop.setIniCodec("UTF-8"); + desktop.beginGroup("Desktop Entry"); + + // Ignore files the require a specific desktop environment + if (desktop.contains("NotShowIn")) { + const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); + if (notShowIn.contains("Lingmo")) + continue; + } + if (desktop.contains("OnlyShowIn")) { + const QStringList onlyShowIn = + desktop.value("OnlyShowIn").toStringList(); + if (!onlyShowIn.contains("Lingmo")) + continue; + } + + const QString execValue = desktop.value("Exec").toString(); + + // 避免冲突 + if (execValue.contains("gmenudbusmenuproxy")) + continue; + + // 使用 QProcess::splitCommand 来解析命令和参数 + QStringList args = QProcess::splitCommand(execValue); + + // 检查是否至少有一个元素(即程序路径) + if (!args.isEmpty()) { + auto program = args.first(); + args.removeFirst(); // 移除程序路径,剩下的都是参数 + + list << qMakePair(program, args); + } else { + qCWarning(LINGMO_SESSION_D) << "Invalid 'Exec' found in file!"; + } + } + } + + m_userAutoStartD = std::make_shared(list, false); +} + +bool ProcessManager::nativeEventFilter(const QByteArray &eventType, + void *message, long *result) { + if (eventType != "xcb_generic_event_t") // We only want to handle XCB events + return false; + + // ref: lxqt session + if (!m_wmStarted && m_waitLoop) { + // all window managers must set their name according to the spec + if (!QString::fromUtf8( + NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) + .wmName()) + .isEmpty()) { + qDebug() << "Window manager started"; + m_wmStarted = true; + if (m_waitLoop && m_waitLoop->isRunning()) + m_waitLoop->exit(); + + qApp->removeNativeEventFilter(this); + } + } + + return false; +} + +bool ProcessManager::startDetached(QProcess *process) { + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->start(); + const bool ret = process->waitForStarted(); + if (ret) { + m_processes << process; + } + return ret; +} + +StartProcessJob::StartProcessJob(const QString &process, + const QStringList &args, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(finished(int, QProcess::ExitStatus))); +} + +void StartProcessJob::start() { + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); + + m_process->start(); +} + +void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { + qCDebug(LINGMO_SESSION_D) << "process job " << m_process->program() + << "finished with exit code " << exitCode; + emitResult(); +} + +StartServiceJob::StartServiceJob(const QString &process, + const QStringList &args, + const QString &serviceId, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess), m_serviceId(serviceId), + m_additionalEnv(additionalEnv) { + m_process->setProgram(process); + m_process->setArguments(args); + + auto watcher = + new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, this); + connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, + &StartServiceJob::emitResult); +} + +void StartServiceJob::start() { + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(m_additionalEnv); + m_process->setProcessEnvironment(env); + + if (!m_serviceId.isEmpty() && + QDBusConnection::sessionBus().interface()->isServiceRegistered( + m_serviceId)) { + qCDebug(LINGMO_SESSION_D) << m_process << "already running"; + emitResult(); + return; + } + qDebug() << "Starting " << m_process->program() << m_process->arguments(); + if (!ProcessManager::self()->startDetached(m_process)) { + qCWarning(LINGMO_SESSION_D) + << "error starting process" << m_process->program() + << m_process->arguments(); + emitResult(); + } + + if (m_serviceId.isEmpty()) { + emitResult(); + } +} + +LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv) + : Job(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); +} + +void LaunchProcess::start() { + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); + + m_process->startDetached(); + + emitResult(); +} \ No newline at end of file diff --git a/startlingmo/lingmo-session/processmanager.h b/startlingmo/lingmo-session/processmanager.h new file mode 100755 index 0000000..fbb4d74 --- /dev/null +++ b/startlingmo/lingmo-session/processmanager.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023-2024 LingmoOS Team. + * + * Author: revenmartin + * + * 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 . + */ + +#ifndef PROCESSMANAGER_H +#define PROCESSMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "daemon-helper.h" + +class Application; +class ProcessManager; + +extern ProcessManager *s_self; + +class ProcessManager : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT +public: + explicit ProcessManager(Application *app, QObject *parent = nullptr); + ~ProcessManager(); + + static ProcessManager *self() { + Q_ASSERT(s_self); + return s_self; + } + + void start(); + void logout(); + + void startWindowManager(); + void startDesktopProcess(); + void startDaemonProcess(); + + /** + * @brief Start the user defined autostart process. + * Typically, they are in /.config/autostart/xxx.desktop + */ + void loadAutoStartProcess(); + + bool nativeEventFilter(const QByteArray &eventType, void *message, + long *result) override; + + void updateLaunchEnv(const QString &key, const QString &value); + + bool startDetached(QProcess *process); + +private: + Application *m_app; + QMap m_systemProcess; + QMap m_autoStartProcess; + QVector m_processes; + + // Daemon helper for desktop components + std::shared_ptr m_desktopAutoStartD; + + // Daemon helper for other daemon components + std::shared_ptr m_daemonAutoStartD; + + // Daemon helper for User Auto Start Process + std::shared_ptr m_userAutoStartD; + + bool m_wmStarted; + QEventLoop *m_waitLoop; +}; + +using namespace LINGMO_SESSION; +/** + * Launches a process, and waits for the process to start + */ +class LaunchProcess : public Job { + Q_OBJECT +public: + LaunchProcess( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +private: + QProcess *m_process; +}; + +/** + * Launches a process, and waits for the process to finish + */ +class StartProcessJob : public Job { + Q_OBJECT +public: + StartProcessJob( + const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + void start() override; + +public Q_SLOTS: + void finished(int exitCode, QProcess::ExitStatus e); + +private: + QProcess *m_process; +}; + +/** + * Launches a process, and waits for the service to appear on the session bus + */ +class StartServiceJob : public Job { + Q_OBJECT +public: + StartServiceJob( + const QString &process, const QStringList &args, const QString &serviceId, + const QProcessEnvironment &additionalEnv = QProcessEnvironment()); + + void start() override; + +private: + QProcess *m_process; + const QString m_serviceId; + const QProcessEnvironment m_additionalEnv; +}; +#endif // PROCESSMANAGER_H diff --git a/startlingmo/lingmo-sourceenv.sh b/startlingmo/lingmo-sourceenv.sh new file mode 100644 index 0000000..4a00f15 --- /dev/null +++ b/startlingmo/lingmo-sourceenv.sh @@ -0,0 +1,7 @@ +for i in $@ +do + . $i >/dev/null +done + +# env may not support -0, fall back to GNU env +env -0 2>/dev/null || genv -0 diff --git a/startlingmo/lingmo-wayland-session.desktop b/startlingmo/lingmo-wayland-session.desktop new file mode 100755 index 0000000..4c1c412 --- /dev/null +++ b/startlingmo/lingmo-wayland-session.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Exec=startlingmo-wayland +Name=Lingmo Desktop (Wayland Experimental) +Keywords=session +Comment=session diff --git a/startlingmo/lingmo-xorg-session.desktop b/startlingmo/lingmo-xorg-session.desktop new file mode 100755 index 0000000..7df13bc --- /dev/null +++ b/startlingmo/lingmo-xorg-session.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Exec=startlingmo-x11 +TryExec=startlingmo-x11 +Name=Lingmo Desktop (Xorg Session) +Keywords=session +Comment=session diff --git a/startlingmo/signalhandler.cpp b/startlingmo/signalhandler.cpp new file mode 100644 index 0000000..9e96c58 --- /dev/null +++ b/startlingmo/signalhandler.cpp @@ -0,0 +1,63 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "signalhandler.h" +#include "debug.h" +#include "startlingmo.hpp" + +#include +#include +#include +#include + +int SignalHandler::signalFd[2]; + +SignalHandler::SignalHandler() +{ + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { + qCWarning(LINGMO_STARTUP) << "Couldn't create a socketpair"; + return; + } + + m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); + connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); +} + +SignalHandler::~SignalHandler() +{ + for (int sig : std::as_const(m_signalsRegistered)) { + signal(sig, nullptr); + } + close(signalFd[0]); + close(signalFd[1]); +} + +void SignalHandler::addSignal(int signalToTrack) +{ + m_signalsRegistered.insert(signalToTrack); + signal(signalToTrack, signalHandler); +} + +void SignalHandler::signalHandler(int signal) +{ + ::write(signalFd[0], &signal, sizeof(signal)); +} + +void SignalHandler::handleSignal() +{ + m_handler->setEnabled(false); + int signal; + ::read(signalFd[1], &signal, sizeof(signal)); + m_handler->setEnabled(true); + + Q_EMIT signalReceived(signal); +} + +SignalHandler *SignalHandler::self() +{ + static SignalHandler s_self; + return &s_self; +} diff --git a/startlingmo/signalhandler.h b/startlingmo/signalhandler.h new file mode 100644 index 0000000..c0f8428 --- /dev/null +++ b/startlingmo/signalhandler.h @@ -0,0 +1,38 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#pragma once + +#include +#include +#include + +/** + * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop + * + * It's a singleton as it relies on static data getting defined. + */ +class SignalHandler : public QObject +{ + Q_OBJECT +public: + ~SignalHandler() override; + void addSignal(int signal); + + static SignalHandler *self(); + +Q_SIGNALS: + void signalReceived(int signal); + +private: + SignalHandler(); + void handleSignal(); + static void signalHandler(int signal); + + QSet m_signalsRegistered; + static int signalFd[2]; + QSocketNotifier *m_handler = nullptr; +}; diff --git a/startlingmo/startlingmo-wayland.cpp b/startlingmo/startlingmo-wayland.cpp new file mode 100644 index 0000000..095e249 --- /dev/null +++ b/startlingmo/startlingmo-wayland.cpp @@ -0,0 +1,95 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include +#include +#include +#include +#include + +#include "startlingmo.hpp" + +#include "debug.h" + +#include "signal.h" + +extern QTextStream out; + +int main(int argc, char **argv) { + + QCoreApplication app(argc, argv); + + createConfigDirectory(); + setupCursor(true); + signal(SIGTERM, sigtermHandler); + + // Let clients try to reconnect to kwin after a restart + qputenv("QT_WAYLAND_RECONNECT", "1"); + + // Query whether org.freedesktop.locale1 is available. If it is, try to + // set XKB_DEFAULT_{MODEL,LAYOUT,VARIANT,OPTIONS} accordingly. + { + const QString locale1Service = QStringLiteral("org.freedesktop.locale1"); + const QString locale1Path = QStringLiteral("/org/freedesktop/locale1"); + QDBusMessage message = QDBusMessage::createMethodCall( + locale1Service, locale1Path, + QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("GetAll")); + message << locale1Service; + QDBusMessage resultMessage = QDBusConnection::systemBus().call(message); + if (resultMessage.type() == QDBusMessage::ReplyMessage) { + QVariantMap result; + QDBusArgument dbusArgument = + resultMessage.arguments().at(0).value(); + while (!dbusArgument.atEnd()) { + dbusArgument >> result; + } + + auto queryAndSet = [&result](const char *var, const QString &value) { + const auto r = result.value(value).toString(); + if (!r.isEmpty()) + qputenv(var, r.toUtf8()); + }; + + queryAndSet("XKB_DEFAULT_MODEL", QStringLiteral("X11Model")); + queryAndSet("XKB_DEFAULT_LAYOUT", QStringLiteral("X11Layout")); + queryAndSet("XKB_DEFAULT_VARIANT", QStringLiteral("X11Variant")); + queryAndSet("XKB_DEFAULT_OPTIONS", QStringLiteral("X11Options")); + } else { + qCWarning(LINGMO_STARTUP) + << "No valid reply from org.freedesktop.locale1" << resultMessage; + } + } + + runEnvironmentScripts(); + + if (!qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) { + out << "startplasmacompositor: Could not start D-Bus. Can you call " + "qdbus?\n"; + return 1; + } + + setupLingmoEnvironment(); + initLanguage(); + initScreenScaleFactors(); + + qputenv("XDG_SESSION_TYPE", "wayland"); + qputenv("QT_QPA_PLATFORM", "wayland"); + + auto oldSystemdEnvironment = getSystemdEnvironment(); + if (!syncDBusEnvironment()) { + out << "Could not sync environment to dbus.\n"; + return 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_*) + importSystemdEnvrionment(); + + startLingmoSession(true); + + return 0; +} \ No newline at end of file diff --git a/startlingmo/startlingmo-x11.cpp b/startlingmo/startlingmo-x11.cpp new file mode 100644 index 0000000..4084f0f --- /dev/null +++ b/startlingmo/startlingmo-x11.cpp @@ -0,0 +1,62 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "signal.h" +#include "startlingmo.hpp" + +#include + +#include + +void sighupHandler(int) { std::cout << "GOT SIGHUP\n"; } + +int main(int argc, char *argv[]) { + + QCoreApplication app(argc, argv); + + // When the X server dies we get a HUP signal from xinit. We must ignore it + // because we still need to do some cleanup. + signal(SIGHUP, sighupHandler); + + qputenv("QT_NO_XDG_DESKTOP_PORTAL", QByteArrayLiteral("1")); + + // ToDo: Maybe we can check wether lingmo de is already running? + + createConfigDirectory(); + initLanguage(); + initScreenScaleFactors(); + + // NOTE: Be very mindful of what you start this early in the process. The + // environment is not yet complete. + setupCursor(false); + + runEnvironmentScripts(); + + std::cout << "Starting lingmo de ...\n"; + + setupLingmoEnvironment(); + + qunsetenv("QT_NO_XDG_DESKTOP_PORTAL"); + auto oldSystemdEnvironment = getSystemdEnvironment(); + if (!syncDBusEnvironment()) { + // Startup error + messageBox(QStringLiteral("Could not sync environment to dbus.\n")); + return 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_*) + importSystemdEnvrionment(); + + if (!startLingmoSession(false)) + return 1; + + std::cout << "Shutting down lingmo de ...\n"; + + cleanupPlasmaEnvironment(oldSystemdEnvironment); + + return 0; +} \ No newline at end of file diff --git a/startlingmo/startlingmo.cpp b/startlingmo/startlingmo.cpp new file mode 100644 index 0000000..bf0253b --- /dev/null +++ b/startlingmo/startlingmo.cpp @@ -0,0 +1,513 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "startlingmo.hpp" +#include "config-startlingmo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +#include "UpdateLaunchEnvironment.hpp" + +extern QTextStream out; +QTextStream out(stderr); + +void sigtermHandler(int signalNumber) { + Q_UNUSED(signalNumber) + if (QCoreApplication::instance()) { + QCoreApplication::instance()->exit(-1); + } +} + +QStringList allServices(const QLatin1String &prefix) { + const QStringList services = + QDBusConnection::sessionBus().interface()->registeredServiceNames(); + QStringList names; + + std::copy_if(services.cbegin(), services.cend(), std::back_inserter(names), + [&prefix](const QString &serviceName) { + return serviceName.startsWith(prefix); + }); + + return names; +} + +void gentleTermination(QProcess *p) { + if (p->state() != QProcess::Running) { + return; + } + + p->terminate(); + + // Wait longer for a session than a greeter + if (!p->waitForFinished(5000)) { + p->kill(); + if (!p->waitForFinished(5000)) { + qCWarning(LINGMO_STARTUP) + << "Could not fully finish the process" << p->program(); + } + } +} + +int 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); + + QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + &p, [&p] { gentleTermination(&p); }); + // qCDebug(LINGMO_STARTUP) << "started..." << program << args; + p.waitForFinished(-1); + if (p.exitCode()) { + qCWarning(LINGMO_STARTUP) + << program << args << "exited with code" << p.exitCode(); + } + return p.exitCode(); +} + +void createConfigDirectory() { + const QString configDir = + QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + if (!QDir().mkpath(configDir)) + out << "Could not create config directory XDG_CONFIG_HOME: " << configDir + << '\n'; +} + +void setupCursor(bool wayland) { +#ifdef XCURSOR_PATH + QByteArray path(XCURSOR_PATH); + path.replace("$XCURSOR_PATH", qgetenv("XCURSOR_PATH")); + qputenv("XCURSOR_PATH", path); +#endif + + // TODO: consider linking directly + if (!wayland) { + QSettings settings(QSettings::UserScope, "lingmoos", "theme"); + qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal(); + QString cursorTheme = settings.value("CursorTheme", "default").toString(); + int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor; + runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); + } +} + +// Source scripts found in /lingmo-workspace/env/*.sh +// (where correspond to the system and user's configuration +// directory. +// +// Scripts are sourced in reverse order of priority of their directory, as +// defined by `QStandardPaths::standardLocations`. This ensures that +// high-priority scripts (such as those in the user's home directory) are +// sourced last and take precedence over lower-priority scripts (such as system +// defaults). Scripts in the same directory are sourced in lexical order of +// their filename. +// +// This is where you can define environment variables that will be available to +// all KDE programs, so this is where you can run agents using e.g. eval +// `ssh-agent` or eval `gpg-agent --daemon`. Note: if you do that, you should +// also put "ssh-agent -k" as a shutdown script +// +// (see end of this file). +// For anything else (that doesn't set env vars, or that needs a window +// manager), better use the Autostart folder. +void runEnvironmentScripts() { + QStringList scripts; + auto locations = + QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); + + //`standardLocations()` returns locations sorted by "order of priority". We + // iterate in reverse + // order so that high-priority scripts are sourced last and their + // modifications take precedence. + for (auto loc = locations.crbegin(); loc != locations.crend(); loc++) { + QDir dir(*loc); + if (!dir.cd(QStringLiteral("./lingmo-workspace/env"))) { + // Skip location if lingmo-workspace/env subdirectory does not exist + continue; + } + const auto dirScripts = + dir.entryInfoList({QStringLiteral("*.sh")}, QDir::Files, QDir::Name); + for (const auto &script : dirScripts) { + scripts << script.absoluteFilePath(); + } + } + sourceFiles(scripts); +} + +void sourceFiles(const QStringList &files) { + QStringList filteredFiles; + std::copy_if(files.begin(), files.end(), std::back_inserter(filteredFiles), + [](const QString &i) { return QFileInfo(i).isReadable(); }); + + if (filteredFiles.isEmpty()) + return; + + filteredFiles.prepend( + QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/lingmo-sourceenv.sh")); + + QProcess p; + p.start(QStringLiteral("/bin/sh"), filteredFiles); + p.waitForFinished(-1); + + const auto fullEnv = p.readAllStandardOutput(); + auto envs = fullEnv.split('\0'); + + for (auto &env : envs) { + const int idx = env.indexOf('='); + if (Q_UNLIKELY(idx <= 0)) { + continue; + } + + const auto name = env.left(idx); + if (isShellVariable(name)) { + continue; + } + setEnvironmentVariable(name, env.mid(idx + 1)); + } +} + +bool isShellVariable(const QByteArray &name) { + return name == "_" || name == "SHELL" || name.startsWith("SHLVL"); +} + +void setEnvironmentVariable(const QByteArray &name, const QByteArray &value) { + if (qgetenv(name) != value) { + qputenv(name, value); + } +} + +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 setupLingmoEnvironment() { + // Manually disable auto scaling because we are scaling above + // otherwise apps that manually opt in for high DPI get auto scaled by the + // developer AND manually scaled by us + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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", "Lingmo"); + qputenv("XDG_CURRENT_DESKTOP", "Lingmo"); + qputenv("XDG_SESSION_DESKTOP", "Lingmo"); + + // Qt + // qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); + // qputenv("QT_PLATFORM_PLUGIN", "lingmo"); + + // ref: + // https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping + qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); + + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + + // 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"); +} + +std::optional getSystemdEnvironment() { + 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()) { + return std::nullopt; + } + auto variant = qdbus_cast(arguments[0]); + if (variant.type() != QVariant::StringList) { + return std::nullopt; + } + + const auto assignmentList = variant.toStringList(); + QProcessEnvironment ret; + for (auto &env : assignmentList) { + const int idx = env.indexOf(QLatin1Char('=')); + if (Q_LIKELY(idx > 0)) { + ret.insert(env.left(idx), env.mid(idx + 1)); + } + } + + return ret; +} + +// Drop session-specific variables from the systemd environment. +// Those can be leftovers from previous sessions, which can interfere with the +// session we want to start now, e.g. $DISPLAY might break kwin_wayland. +static void dropSessionVarsFromSystemdEnvironment() { + const auto environment = getSystemdEnvironment(); + if (!environment) { + return; + } + + QStringList varsToDrop; + for (auto &nameStr : environment.value().keys()) { + // If it's set in this process, it'll be overwritten by the following + // UpdateLaunchEnvJob + const auto name = nameStr.toLocal8Bit(); + if (!qEnvironmentVariableIsSet(name) && isSessionVariable(name)) { + varsToDrop.append(nameStr); + } + } + + auto msg = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("UnsetEnvironment")); + msg << varsToDrop; + auto reply = QDBusConnection::sessionBus().call(msg); + if (reply.type() == QDBusMessage::ErrorMessage) { + qCWarning(LINGMO_STARTUP) + << "Failed to unset systemd environment variables:" << reply.errorName() + << reply.errorMessage(); + } +} + +// kwin_wayland can possibly also start dbus-activated services which need env +// variables. In that case, the update in startplasma might be too late. +bool syncDBusEnvironment() { + dropSessionVarsFromSystemdEnvironment(); + + // Shell variables are filtered out of things we explicitly load, but they + // still might have been inherited from the parent process + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + for (auto &name : environment.keys()) { + if (isShellVariable(name.toLocal8Bit())) { + environment.remove(name); + } + } + + // At this point all environment variables are set, let's send it to the DBus + // session server to update the activation environment + auto job = new UpdateLaunchEnvironmentJob(environment); + QEventLoop e; + QObject::connect(job, &UpdateLaunchEnvironmentJob::finished, &e, + &QEventLoop::quit); + e.exec(); + return true; +} + +void initLanguage() { + QSettings settings(QSettings::UserScope, "lingmoos", "language"); + QString value = settings.value("language", "").toString(); + + // Init Language + if (value.isEmpty()) { + QFile file("/etc/locale.gen"); + if (file.open(QIODevice::ReadOnly)) { + QStringList lines = QString(file.readAll()).split('\n'); + + for (const QString &line : lines) { + if (line.startsWith('#')) + continue; + + if (line.trimmed().isEmpty()) + continue; + + value = line.split(' ').first().split('.').first(); + } + } + } + + if (value.isEmpty()) + value = "en_US"; + + settings.setValue("language", value); + + 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()); + } +} + +// Import systemd user environment. +// +// Systemd read ~/.config/environment.d which applies to all systemd user unit. +// But it won't work if plasma is not started by systemd. +void importSystemdEnvrionment() { + const auto environment = getSystemdEnvironment(); + if (!environment) { + return; + } + + for (auto &nameStr : environment.value().keys()) { + const auto name = nameStr.toLocal8Bit(); + if (!isShellVariable(name) && !isSessionVariable(name)) { + setEnvironmentVariable(name, + environment.value().value(nameStr).toLocal8Bit()); + } + } +} + +// If something went on an endless restart crash loop it will get blacklisted, +// as this is a clean login we will want to reset those counters This is +// independent of whether we use the Plasma systemd boot +void resetSystemdFailedUnits() { + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("ResetFailed")); + QDBusConnection::sessionBus().call(message); +} + +// Reload systemd to make sure the current configuration is active, which also +// reruns generators. Needed for e.g. XDG autostart changes to become effective. +void reloadSystemd() { + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("Reload")); + QDBusConnection::sessionBus().call(message); +} + +bool startLingmoSession(bool wayland) { + resetSystemdFailedUnits(); + reloadSystemd(); + + bool rc = true; + QEventLoop e; + + std::unique_ptr startLingmoSession; + + QStringList lingmoSessionOptions; + + if (wayland) { + lingmoSessionOptions << QStringLiteral("--wayland"); + } + + { + startLingmoSession.reset(new QProcess); + qCDebug(LINGMO_STARTUP) << "Using classic boot"; + + startLingmoSession->setProcessChannelMode(QProcess::ForwardedChannels); + + startLingmoSession->start( + QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/lingmo_session"), + lingmoSessionOptions); + } + + if (rc) { + QObject::connect(QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, &e, &QEventLoop::quit); + e.exec(); + } + return rc; +} + +void initScreenScaleFactors() { + QSettings settings(QSettings::UserScope, "lingmoos", "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)); + } +} + +void messageBox(const QString &text) { + out << text; + runSync(QStringLiteral("xmessage"), + {QStringLiteral("-geometry"), QStringLiteral("500x100"), text}); +} + +void cleanupPlasmaEnvironment( + const std::optional &oldSystemdEnvironment) { + + if (!oldSystemdEnvironment) { + return; + } + + auto currentEnv = getSystemdEnvironment(); + if (!currentEnv) { + return; + } + + // According to systemd documentation: + // If a variable is listed in both, the variable is set after this method + // returns, i.e. the set list overrides the unset list. So this will + // effectively restore the state to the values in oldSystemdEnvironment. + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("UnsetAndSetEnvironment")); + message.setArguments({currentEnv.value().keys(), + oldSystemdEnvironment.value().toStringList()}); + + // The session program gonna quit soon, ensure the message is flushed. + auto reply = QDBusConnection::sessionBus().asyncCall(message); + reply.waitForFinished(); +} diff --git a/startlingmo/startlingmo.hpp b/startlingmo/startlingmo.hpp new file mode 100644 index 0000000..ce89353 --- /dev/null +++ b/startlingmo/startlingmo.hpp @@ -0,0 +1,66 @@ +/* + SPDX-FileCopyrightText: 2024 Elysia + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#ifndef STARTLINGMO_HPP +#define STARTLINGMO_HPP + +#include +#include +#include +#include + +void sigtermHandler(int signalNumber); + +QStringList allServices(const QLatin1String &prefix); + +void gentleTermination(QProcess *process); + +int runSync(const QString &program, const QStringList &args, + const QStringList &env = {}); + +void createConfigDirectory(); + +void setupCursor(bool wayland); + +void runEnvironmentScripts(); + +void sourceFiles(const QStringList &files); + +bool isShellVariable(const QByteArray &name); + +void setEnvironmentVariable(const QByteArray &name, const QByteArray &value); + +void setupLingmoEnvironment(); + +std::optional getSystemdEnvironment(); + +bool syncDBusEnvironment(); + +void initLanguage(); + +void importSystemdEnvrionment(); + +bool startLingmoSession(bool wayland); + +struct KillBeforeDeleter { + void operator()(QProcess *pointer) { + if (pointer) { + gentleTermination(pointer); + } + delete pointer; + } +}; + +bool isSessionVariable(const QByteArray &name); + +void initScreenScaleFactors(); + +void messageBox(const QString &text); + +void cleanupPlasmaEnvironment( + const std::optional &oldSystemdEnvironment); + +#endif // STARTLINGMO_HPP \ No newline at end of file diff --git a/startlingmo/wayland_wrapper/CMakeLists.txt b/startlingmo/wayland_wrapper/CMakeLists.txt new file mode 100644 index 0000000..aa0f511 --- /dev/null +++ b/startlingmo/wayland_wrapper/CMakeLists.txt @@ -0,0 +1,23 @@ +add_subdirectory(lib) + +add_executable(lingmo_kwin_wayland_wrapper) +target_sources(lingmo_kwin_wayland_wrapper PRIVATE + kwin_wrapper.cpp + wl-socket.c +) + +ecm_qt_declare_logging_category(lingmo_kwin_wayland_wrapper + HEADER + wrapper_logging.h + IDENTIFIER + KWIN_WRAPPER + CATEGORY_NAME + lingmo_kwin_wayland_wrapper + DEFAULT_SEVERITY + Warning +) + +target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KWinXwaylandCommon startlingmo) +target_include_directories(lingmo_kwin_wayland_wrapper PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) +set_property(TARGET lingmo_kwin_wayland_wrapper PROPERTY C_STANDARD 11) +install(TARGETS lingmo_kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/startlingmo/wayland_wrapper/kwin_wrapper.cpp b/startlingmo/wayland_wrapper/kwin_wrapper.cpp new file mode 100644 index 0000000..bf52c64 --- /dev/null +++ b/startlingmo/wayland_wrapper/kwin_wrapper.cpp @@ -0,0 +1,179 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +/** + * This tiny executable creates a socket, then starts kwin passing it the FD to the wayland socket + * along with the name of the socket to use + * On any non-zero kwin exit kwin gets restarted. + * + * After restart kwin is relaunched but now with the KWIN_RESTART_COUNT env set to an incrementing counter + * + * It's somewhat akin to systemd socket activation, but we also need the lock file, finding the next free socket + * and so on, hence our own binary. + * + * Usage kwin_wayland_wrapper [argForKwin] [argForKwin] ... + */ + +#include +#include +#include +#include +#include + +#include "signalhandler.h" +#include "UpdateLaunchEnvironment.hpp" + +#include +#include +#include + +#include "wl-socket.h" +#include "wrapper_logging.h" +#include "xauthority.h" +#include "xwaylandsocket.h" + +class KWinWrapper : public QObject +{ + Q_OBJECT +public: + KWinWrapper(QObject *parent); + ~KWinWrapper(); + void run(); + +private: + wl_socket *m_socket; + + int m_crashCount = 0; + QProcess *m_kwinProcess = nullptr; + + std::unique_ptr m_xwlSocket; + QTemporaryFile m_xauthorityFile; +}; + +KWinWrapper::KWinWrapper(QObject *parent) + : QObject(parent) + , m_kwinProcess(new QProcess(this)) +{ + m_socket = wl_socket_create(); + if (!m_socket) { + qFatal("Could not create wayland socket"); + } + + if (qApp->arguments().contains(QLatin1String("--xwayland"))) { + m_xwlSocket.reset(new KWin::XwaylandSocket(KWin::XwaylandSocket::OperationMode::TransferFdsOnExec)); + if (!m_xwlSocket->isValid()) { + qCWarning(KWIN_WRAPPER) << "Failed to create Xwayland connection sockets"; + m_xwlSocket.reset(); + } + if (m_xwlSocket) { + if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) { + if (!generateXauthorityFile(m_xwlSocket->display(), &m_xauthorityFile)) { + qCWarning(KWIN_WRAPPER) << "Failed to create an Xauthority file"; + } + } + } + } +} + +KWinWrapper::~KWinWrapper() +{ + wl_socket_destroy(m_socket); + if (m_kwinProcess) { + disconnect(m_kwinProcess, nullptr, this, nullptr); + m_kwinProcess->terminate(); + m_kwinProcess->waitForFinished(); + m_kwinProcess->kill(); + m_kwinProcess->waitForFinished(); + } +} + +void KWinWrapper::run() +{ + m_kwinProcess->setProgram("kwin_wayland"); + + QStringList args; + + args << "--wayland-fd" << QString::number(wl_socket_get_fd(m_socket)); + args << "--socket" << QString::fromUtf8(wl_socket_get_display_name(m_socket)); + + if (m_xwlSocket) { + const auto xwaylandFileDescriptors = m_xwlSocket->fileDescriptors(); + for (const int &fileDescriptor : xwaylandFileDescriptors) { + args << "--xwayland-fd" << QString::number(fileDescriptor); + } + args << "--xwayland-display" << m_xwlSocket->name(); + if (m_xauthorityFile.open()) { + args << "--xwayland-xauthority" << m_xauthorityFile.fileName(); + } + } + + // attach our main process arguments + // the first entry is dropped as it will be our program name + args << qApp->arguments().mid(1); + + m_kwinProcess->setProcessChannelMode(QProcess::ForwardedChannels); + m_kwinProcess->setArguments(args); + + connect(m_kwinProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { + if (exitCode == 0) { + qApp->quit(); + return; + } else if (exitCode == 133) { + m_crashCount = 0; + } else { + m_crashCount++; + } + + if (m_crashCount > 10) { + qApp->quit(); + return; + } + qputenv("KWIN_RESTART_COUNT", QByteArray::number(m_crashCount)); + // restart + m_kwinProcess->start(); + }); + + m_kwinProcess->start(); + + QProcessEnvironment env; + env.insert("WAYLAND_DISPLAY", QString::fromUtf8(wl_socket_get_display_name(m_socket))); + if (m_xwlSocket) { + env.insert("DISPLAY", m_xwlSocket->name()); + if (m_xauthorityFile.open()) { + env.insert("XAUTHORITY", m_xauthorityFile.fileName()); + } + } + + auto envSyncJob = new UpdateLaunchEnvironmentJob(env); + connect(envSyncJob, &UpdateLaunchEnvironmentJob::finished, this, []() { + // The service name is merely there to indicate to the world that we're up and ready with all envs exported + qCDebug(KWIN_WRAPPER) << "KWinWrapper is ready"; + QDBusConnection::sessionBus().registerService(QStringLiteral("org.lingmo.KWinWrapper")); + }); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + app.setQuitLockEnabled(false); // don't exit when the first KJob finishes + + SignalHandler::self()->addSignal(SIGTERM); + QObject::connect(SignalHandler::self(), &SignalHandler::signalReceived, &app, [&app](int signal) { + if (signal == SIGTERM) { + app.quit(); + } + }); + + KWinWrapper wrapper(&app); + wrapper.run(); + + return app.exec(); +} + +#include "kwin_wrapper.moc" diff --git a/startlingmo/wayland_wrapper/lib/CMakeLists.txt b/startlingmo/wayland_wrapper/lib/CMakeLists.txt new file mode 100644 index 0000000..dff9a60 --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(KWinXwaylandCommon STATIC + xwaylandsocket.cpp + xauthority.cpp +) + +ecm_qt_declare_logging_category(KWinXwaylandCommon + HEADER + xwayland_logging.h + IDENTIFIER + KWIN_XWL + CATEGORY_NAME + kwin_xwl + DEFAULT_SEVERITY + Warning +) + +set_property(TARGET KWinXwaylandCommon PROPERTY POSITION_INDEPENDENT_CODE ON) + +target_include_directories(KWinXwaylandCommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(KWinXwaylandCommon Qt::Core Qt::Network) diff --git a/startlingmo/wayland_wrapper/lib/xauthority.cpp b/startlingmo/wayland_wrapper/lib/xauthority.cpp new file mode 100644 index 0000000..bef5560 --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xauthority.cpp @@ -0,0 +1,77 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + SPDX-FileCopyrightText: 2021 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "xauthority.h" + +#include +#include +#include +#include +#include + +static void writeXauthorityEntry(QDataStream &stream, quint16 family, + const QByteArray &address, const QByteArray &display, + const QByteArray &name, const QByteArray &cookie) +{ + stream << quint16(family); + + auto writeArray = [&stream](const QByteArray &str) { + stream << quint16(str.size()); + stream.writeRawData(str.constData(), str.size()); + }; + + writeArray(address); + writeArray(display); + writeArray(name); + writeArray(cookie); +} + +static QByteArray generateXauthorityCookie() +{ + QByteArray cookie; + cookie.resize(16); // Cookie must be 128bits + + QRandomGenerator *generator = QRandomGenerator::system(); + for (int i = 0; i < cookie.size(); ++i) { + cookie[i] = uint8_t(generator->bounded(256)); + } + return cookie; +} + +bool generateXauthorityFile(int display, QTemporaryFile *authorityFile) +{ + const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + + authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX")); + if (!authorityFile->open()) { + return false; + } + + const QByteArray hostname = QHostInfo::localHostName().toUtf8(); + const QByteArray displayName = QByteArray::number(display); + const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1"); + const QByteArray cookie = generateXauthorityCookie(); + + QDataStream stream(authorityFile); + stream.setByteOrder(QDataStream::BigEndian); + + // Write entry with FamilyLocal and the host name as address + writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie); + + // Write entry with FamilyWild, no address + writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie); + + if (stream.status() != QDataStream::Ok || !authorityFile->flush()) { + authorityFile->remove(); + return false; + } + + return true; +} diff --git a/startlingmo/wayland_wrapper/lib/xauthority.h b/startlingmo/wayland_wrapper/lib/xauthority.h new file mode 100644 index 0000000..7ba232b --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xauthority.h @@ -0,0 +1,14 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 David Edmundson + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +class QTemporaryFile; + +bool generateXauthorityFile(int display, QTemporaryFile *authorityFile); diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp b/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp new file mode 100644 index 0000000..e31899c --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp @@ -0,0 +1,250 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "xwaylandsocket.h" +#include "xwayland_logging.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace KWin +{ + +class UnixSocketAddress +{ +public: + enum class Type { + Unix, + Abstract, + }; + + UnixSocketAddress(const QString &socketPath, Type type); + + const sockaddr *data() const; + int size() const; + +private: + QByteArray m_buffer; +}; + +UnixSocketAddress::UnixSocketAddress(const QString &socketPath, Type type) +{ + const QByteArray encodedSocketPath = QFile::encodeName(socketPath); + + int byteCount = offsetof(sockaddr_un, sun_path) + encodedSocketPath.size() + 1; + m_buffer.resize(byteCount); + + sockaddr_un *address = reinterpret_cast(m_buffer.data()); + address->sun_family = AF_UNIX; + + if (type == Type::Unix) { + memcpy(address->sun_path, encodedSocketPath.data(), encodedSocketPath.size()); + address->sun_path[encodedSocketPath.size()] = '\0'; + } else { + // Abstract domain socket does not need the NUL-termination byte. + *address->sun_path = '\0'; + memcpy(address->sun_path + 1, encodedSocketPath.data(), encodedSocketPath.size()); + } +} + +const sockaddr *UnixSocketAddress::data() const +{ + return reinterpret_cast(m_buffer.data()); +} + +int UnixSocketAddress::size() const +{ + return m_buffer.size(); +} + +static QString lockFileNameForDisplay(int display) +{ + return QStringLiteral("/tmp/.X%1-lock").arg(display); +} + +static QString socketFileNameForDisplay(int display) +{ + return QStringLiteral("/tmp/.X11-unix/X%1").arg(display); +} + +static bool tryLockFile(const QString &lockFileName) +{ + for (int attempt = 0; attempt < 3; ++attempt) { + QFile lockFile(lockFileName); + if (lockFile.open(QFile::WriteOnly | QFile::NewOnly)) { + char buffer[12]; + snprintf(buffer, sizeof(buffer), "%10lld\n", QCoreApplication::applicationPid()); + if (lockFile.write(buffer, sizeof(buffer) - 1) != sizeof(buffer) - 1) { + qCWarning(KWIN_XWL) << "Failed to write pid to lock file:" << lockFile.errorString(); + lockFile.remove(); + return false; + } + return true; + } else if (lockFile.open(QFile::ReadOnly)) { + const int lockPid = lockFile.readLine().trimmed().toInt(); + if (!lockPid) { + return false; + } + if (kill(lockPid, 0) < 0 && errno == ESRCH) { + lockFile.remove(); // Try to grab the lock file in the next loop iteration. + } else { + return false; + } + } + } + return false; +} + +static int listen_helper(const QString &filePath, UnixSocketAddress::Type type, XwaylandSocket::OperationMode mode) +{ + const UnixSocketAddress socketAddress(filePath, type); + + int socketFlags = SOCK_STREAM; + if (mode == XwaylandSocket::OperationMode::CloseFdsOnExec) { + socketFlags |= SOCK_CLOEXEC; + } + int fileDescriptor = socket(AF_UNIX, socketFlags, 0); + if (fileDescriptor == -1) { + return -1; + } + + if (bind(fileDescriptor, socketAddress.data(), socketAddress.size()) == -1) { + close(fileDescriptor); + return -1; + } + + if (listen(fileDescriptor, 1) == -1) { + close(fileDescriptor); + return -1; + } + + return fileDescriptor; +} + +static bool checkSocketsDirectory() +{ + struct stat info; + const char *path = "/tmp/.X11-unix"; + + if (lstat(path, &info) != 0) { + if (errno == ENOENT) { + qCWarning(KWIN_XWL) << path << "does not exist. Please check your installation"; + return false; + } + + qCWarning(KWIN_XWL, "Failed to stat %s: %s", path, strerror(errno)); + return false; + } + + if (!S_ISDIR(info.st_mode)) { + qCWarning(KWIN_XWL) << path << "is not a directory. Broken system?"; + return false; + } + if (info.st_uid != 0 && info.st_uid != getuid()) { + qCWarning(KWIN_XWL) << path << "is not owned by root or us"; + return false; + } + if (!(info.st_mode & S_ISVTX)) { + qCWarning(KWIN_XWL) << path << "has no sticky bit on. Your system might be compromised!"; + return false; + } + + return true; +} + +XwaylandSocket::XwaylandSocket(OperationMode mode) +{ + if (!checkSocketsDirectory()) { + return; + } + + for (int display = 0; display < 100; ++display) { + const QString socketFilePath = socketFileNameForDisplay(display); + const QString lockFilePath = lockFileNameForDisplay(display); + + if (!tryLockFile(lockFilePath)) { + continue; + } + + QVector fileDescriptors; + auto socketCleanup = qScopeGuard([&fileDescriptors]() { + for (const int &fileDescriptor : std::as_const(fileDescriptors)) { + close(fileDescriptor); + } + }); + + QFile::remove(socketFilePath); + const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix, mode); + if (unixFileDescriptor == -1) { + QFile::remove(lockFilePath); + continue; + } + fileDescriptors << unixFileDescriptor; + +#if defined(Q_OS_LINUX) + const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract, mode); + if (abstractFileDescriptor == -1) { + QFile::remove(lockFilePath); + QFile::remove(socketFilePath); + continue; + } + fileDescriptors << abstractFileDescriptor; +#endif + + m_fileDescriptors = fileDescriptors; + socketCleanup.dismiss(); + + m_socketFilePath = socketFilePath; + m_lockFilePath = lockFilePath; + m_display = display; + return; + } + + qCWarning(KWIN_XWL) << "Failed to find free X11 connection socket"; +} + +XwaylandSocket::~XwaylandSocket() +{ + for (const int &fileDescriptor : std::as_const(m_fileDescriptors)) { + close(fileDescriptor); + } + if (!m_socketFilePath.isEmpty()) { + QFile::remove(m_socketFilePath); + } + if (!m_lockFilePath.isEmpty()) { + QFile::remove(m_lockFilePath); + } +} + +bool XwaylandSocket::isValid() const +{ + return m_display != -1; +} + +QVector XwaylandSocket::fileDescriptors() const +{ + return m_fileDescriptors; +} + +int XwaylandSocket::display() const +{ + return m_display; +} + +QString XwaylandSocket::name() const +{ + return ":" + QString::number(m_display); +} + +} // namespace KWin diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.h b/startlingmo/wayland_wrapper/lib/xwaylandsocket.h new file mode 100644 index 0000000..80bad35 --- /dev/null +++ b/startlingmo/wayland_wrapper/lib/xwaylandsocket.h @@ -0,0 +1,40 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include +#include +#include + +namespace KWin +{ + +class XwaylandSocket +{ +public: + enum class OperationMode { + CloseFdsOnExec, + TransferFdsOnExec + }; + + XwaylandSocket(OperationMode operationMode); + ~XwaylandSocket(); + + bool isValid() const; + int display() const; + QString name() const; + + QVector fileDescriptors() const; + +private: + QVector m_fileDescriptors; + int m_display = -1; + QString m_socketFilePath; + QString m_lockFilePath; +}; + +} // namespace KWin diff --git a/startlingmo/wayland_wrapper/wl-socket.c b/startlingmo/wayland_wrapper/wl-socket.c new file mode 100644 index 0000000..1b02839 --- /dev/null +++ b/startlingmo/wayland_wrapper/wl-socket.c @@ -0,0 +1,172 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + SPDX-FileCopyrightText: 2008 Kristian Høgsberg + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This is the size of the char array in struct sock_addr_un. + * No Wayland socket can be created with a path longer than this, + * including the null terminator. + */ +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#define LOCK_SUFFIX ".lock" +#define LOCK_SUFFIXLEN 5 + +struct wl_socket { + int fd; + int fd_lock; + struct sockaddr_un addr; + char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN]; + char display_name[16]; +}; + +static struct wl_socket *wl_socket_alloc(void) +{ + struct wl_socket *s; + + s = malloc(sizeof *s); + if (!s) + return NULL; + + s->fd = -1; + s->fd_lock = -1; + + return s; +} + +static int wl_socket_lock(struct wl_socket *socket) +{ + struct stat socket_stat; + + snprintf(socket->lock_addr, sizeof socket->lock_addr, "%s%s", socket->addr.sun_path, LOCK_SUFFIX); + + socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC | O_RDWR, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); + + if (socket->fd_lock < 0) { + printf("unable to open lockfile %s check permissions\n", socket->lock_addr); + goto err; + } + + if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) { + printf("unable to lock lockfile %s, maybe another compositor is running\n", socket->lock_addr); + goto err_fd; + } + + if (lstat(socket->addr.sun_path, &socket_stat) < 0) { + if (errno != ENOENT) { + printf("did not manage to stat file %s\n", socket->addr.sun_path); + goto err_fd; + } + } else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) { + unlink(socket->addr.sun_path); + } + + return 0; +err_fd: + close(socket->fd_lock); + socket->fd_lock = -1; +err: + *socket->lock_addr = 0; + /* we did not set this value here, but without lock the + * socket won't be created anyway. This prevents the + * wl_socket_destroy from unlinking already existing socket + * created by other compositor */ + *socket->addr.sun_path = 0; + + return -1; +} + +void wl_socket_destroy(struct wl_socket *s) +{ + if (s->addr.sun_path[0]) + unlink(s->addr.sun_path); + if (s->fd >= 0) + close(s->fd); + if (s->lock_addr[0]) + unlink(s->lock_addr); + if (s->fd_lock >= 0) + close(s->fd_lock); + + free(s); +} + +const char *wl_socket_get_display_name(struct wl_socket *s) +{ + return s->display_name; +} + +int wl_socket_get_fd(struct wl_socket *s) +{ + return s->fd; +} + +struct wl_socket *wl_socket_create() +{ + struct wl_socket *s; + int displayno = 0; + int name_size; + + /* A reasonable number of maximum default sockets. If + * you need more than this, use the explicit add_socket API. */ + const int MAX_DISPLAYNO = 32; + const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (!runtime_dir) { + printf("XDG_RUNTIME_DIR not set"); + return NULL; + } + + s = wl_socket_alloc(); + if (s == NULL) + return NULL; + + do { + snprintf(s->display_name, sizeof s->display_name, "wayland-%d", displayno); + s->addr.sun_family = AF_LOCAL; + name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s/%s", runtime_dir, s->display_name) + 1; + assert(name_size > 0); + + if (name_size > (int)sizeof s->addr.sun_path) { + goto fail; + } + + if (wl_socket_lock(s) < 0) + continue; + + s->fd = socket(PF_LOCAL, SOCK_STREAM, 0); + + int size = SUN_LEN(&s->addr); + int ret = bind(s->fd, (struct sockaddr*)&s->addr, size); + if (ret < 0) { + goto fail; + } + ret = listen(s->fd, 128); + if (ret < 0) { + goto fail; + } + return s; + } while (displayno++ < MAX_DISPLAYNO); + +fail: + wl_socket_destroy(s); + return NULL; +} diff --git a/startlingmo/wayland_wrapper/wl-socket.h b/startlingmo/wayland_wrapper/wl-socket.h new file mode 100644 index 0000000..ac69ca9 --- /dev/null +++ b/startlingmo/wayland_wrapper/wl-socket.h @@ -0,0 +1,39 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Allocate and create a socket + * It is bound and accepted + */ +struct wl_socket *wl_socket_create(); + +/** + * Returns the file descriptor for the socket + */ +int wl_socket_get_fd(struct wl_socket *); + +/** + * Returns the name of the socket, i.e "wayland-0" + */ +char *wl_socket_get_display_name(struct wl_socket *); + +/** + * Cleanup resources and close the FD + */ +void wl_socket_destroy(struct wl_socket *socket); + +#ifdef __cplusplus +} +#endif From 9c2242b943765133a11fcc709400492dd9d0c399 Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 16:26:39 +0800 Subject: [PATCH 19/24] Move wayland to session folder --- session/CMakeLists.txt | 2 ++ session/main.cpp | 3 --- {startlingmo => session}/wayland_wrapper/CMakeLists.txt | 5 +++++ {startlingmo => session}/wayland_wrapper/kwin_wrapper.cpp | 0 {startlingmo => session}/wayland_wrapper/lib/CMakeLists.txt | 0 {startlingmo => session}/wayland_wrapper/lib/xauthority.cpp | 0 {startlingmo => session}/wayland_wrapper/lib/xauthority.h | 0 .../wayland_wrapper/lib/xwaylandsocket.cpp | 0 .../wayland_wrapper/lib/xwaylandsocket.h | 0 {startlingmo => session}/wayland_wrapper/wl-socket.c | 0 {startlingmo => session}/wayland_wrapper/wl-socket.h | 0 startlingmo/CMakeLists.txt | 4 ++-- 12 files changed, 9 insertions(+), 5 deletions(-) rename {startlingmo => session}/wayland_wrapper/CMakeLists.txt (85%) rename {startlingmo => session}/wayland_wrapper/kwin_wrapper.cpp (100%) rename {startlingmo => session}/wayland_wrapper/lib/CMakeLists.txt (100%) rename {startlingmo => session}/wayland_wrapper/lib/xauthority.cpp (100%) rename {startlingmo => session}/wayland_wrapper/lib/xauthority.h (100%) rename {startlingmo => session}/wayland_wrapper/lib/xwaylandsocket.cpp (100%) rename {startlingmo => session}/wayland_wrapper/lib/xwaylandsocket.h (100%) rename {startlingmo => session}/wayland_wrapper/wl-socket.c (100%) rename {startlingmo => session}/wayland_wrapper/wl-socket.h (100%) diff --git a/session/CMakeLists.txt b/session/CMakeLists.txt index 8917368..d75fcf9 100644 --- a/session/CMakeLists.txt +++ b/session/CMakeLists.txt @@ -42,3 +42,5 @@ target_link_libraries(${TARGET} install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES lingmo-xsession.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) + +add_subdirectory(wayland_wrapper) diff --git a/session/main.cpp b/session/main.cpp index 43b40ee..0d2fecb 100644 --- a/session/main.cpp +++ b/session/main.cpp @@ -24,9 +24,6 @@ int main(int argc, char *argv[]) { // putenv((char *)"SESSION_MANAGER="); - // force xcb QPA plugin as session manager server is very X11 specific. - qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); - QQuickWindow::setDefaultAlphaBuffer(true); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); diff --git a/startlingmo/wayland_wrapper/CMakeLists.txt b/session/wayland_wrapper/CMakeLists.txt similarity index 85% rename from startlingmo/wayland_wrapper/CMakeLists.txt rename to session/wayland_wrapper/CMakeLists.txt index aa0f511..b6fb8c8 100644 --- a/startlingmo/wayland_wrapper/CMakeLists.txt +++ b/session/wayland_wrapper/CMakeLists.txt @@ -1,3 +1,8 @@ +find_package(ECM REQUIRED NO_MODULE) +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(ECMQtDeclareLoggingCategory) + add_subdirectory(lib) add_executable(lingmo_kwin_wayland_wrapper) diff --git a/startlingmo/wayland_wrapper/kwin_wrapper.cpp b/session/wayland_wrapper/kwin_wrapper.cpp similarity index 100% rename from startlingmo/wayland_wrapper/kwin_wrapper.cpp rename to session/wayland_wrapper/kwin_wrapper.cpp diff --git a/startlingmo/wayland_wrapper/lib/CMakeLists.txt b/session/wayland_wrapper/lib/CMakeLists.txt similarity index 100% rename from startlingmo/wayland_wrapper/lib/CMakeLists.txt rename to session/wayland_wrapper/lib/CMakeLists.txt diff --git a/startlingmo/wayland_wrapper/lib/xauthority.cpp b/session/wayland_wrapper/lib/xauthority.cpp similarity index 100% rename from startlingmo/wayland_wrapper/lib/xauthority.cpp rename to session/wayland_wrapper/lib/xauthority.cpp diff --git a/startlingmo/wayland_wrapper/lib/xauthority.h b/session/wayland_wrapper/lib/xauthority.h similarity index 100% rename from startlingmo/wayland_wrapper/lib/xauthority.h rename to session/wayland_wrapper/lib/xauthority.h diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp b/session/wayland_wrapper/lib/xwaylandsocket.cpp similarity index 100% rename from startlingmo/wayland_wrapper/lib/xwaylandsocket.cpp rename to session/wayland_wrapper/lib/xwaylandsocket.cpp diff --git a/startlingmo/wayland_wrapper/lib/xwaylandsocket.h b/session/wayland_wrapper/lib/xwaylandsocket.h similarity index 100% rename from startlingmo/wayland_wrapper/lib/xwaylandsocket.h rename to session/wayland_wrapper/lib/xwaylandsocket.h diff --git a/startlingmo/wayland_wrapper/wl-socket.c b/session/wayland_wrapper/wl-socket.c similarity index 100% rename from startlingmo/wayland_wrapper/wl-socket.c rename to session/wayland_wrapper/wl-socket.c diff --git a/startlingmo/wayland_wrapper/wl-socket.h b/session/wayland_wrapper/wl-socket.h similarity index 100% rename from startlingmo/wayland_wrapper/wl-socket.h rename to session/wayland_wrapper/wl-socket.h diff --git a/startlingmo/CMakeLists.txt b/startlingmo/CMakeLists.txt index 7f94336..2c4dfab 100644 --- a/startlingmo/CMakeLists.txt +++ b/startlingmo/CMakeLists.txt @@ -65,8 +65,8 @@ target_link_libraries(startlingmo-x11 Qt5::DBus ) -add_subdirectory(lingmo-session) -add_subdirectory(wayland_wrapper) +# add_subdirectory(lingmo-session) +#add_subdirectory(wayland_wrapper) install(TARGETS startlingmo-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS startlingmo-x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) From 186df540e335a2b80bcfee17b692af0da5a74a92 Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 17:34:12 +0800 Subject: [PATCH 20/24] fix wrapper --- session/wayland_wrapper/CMakeLists.txt | 10 +- .../UpdateLaunchEnvironment.cpp | 150 ++++++++++++++++++ .../UpdateLaunchEnvironment.hpp | 52 ++++++ session/wayland_wrapper/signalhandler.cpp | 62 ++++++++ session/wayland_wrapper/signalhandler.h | 38 +++++ 5 files changed, 305 insertions(+), 7 deletions(-) create mode 100644 session/wayland_wrapper/UpdateLaunchEnvironment.cpp create mode 100644 session/wayland_wrapper/UpdateLaunchEnvironment.hpp create mode 100644 session/wayland_wrapper/signalhandler.cpp create mode 100644 session/wayland_wrapper/signalhandler.h diff --git a/session/wayland_wrapper/CMakeLists.txt b/session/wayland_wrapper/CMakeLists.txt index b6fb8c8..bc896c6 100644 --- a/session/wayland_wrapper/CMakeLists.txt +++ b/session/wayland_wrapper/CMakeLists.txt @@ -1,14 +1,11 @@ -find_package(ECM REQUIRED NO_MODULE) -list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) - -include(ECMQtDeclareLoggingCategory) - add_subdirectory(lib) add_executable(lingmo_kwin_wayland_wrapper) target_sources(lingmo_kwin_wayland_wrapper PRIVATE kwin_wrapper.cpp wl-socket.c + signalhandler.cpp + UpdateLaunchEnvironment.cpp ) ecm_qt_declare_logging_category(lingmo_kwin_wayland_wrapper @@ -22,7 +19,6 @@ ecm_qt_declare_logging_category(lingmo_kwin_wayland_wrapper Warning ) -target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KWinXwaylandCommon startlingmo) -target_include_directories(lingmo_kwin_wayland_wrapper PRIVATE ${CMAKE_SOURCE_DIR}/startlingmo) +target_link_libraries(lingmo_kwin_wayland_wrapper Qt::Core Qt::DBus KWinXwaylandCommon) set_property(TARGET lingmo_kwin_wayland_wrapper PROPERTY C_STANDARD 11) install(TARGETS lingmo_kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/session/wayland_wrapper/UpdateLaunchEnvironment.cpp b/session/wayland_wrapper/UpdateLaunchEnvironment.cpp new file mode 100644 index 0000000..14bbb3e --- /dev/null +++ b/session/wayland_wrapper/UpdateLaunchEnvironment.cpp @@ -0,0 +1,150 @@ +#include "UpdateLaunchEnvironment.hpp" + +#include +#include +#include +#include + +#include +#include + +#include "wrapper_logging.h" + +class UpdateLaunchEnvironmentJobPrivate +{ +public: + explicit UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q); + void monitorReply(const QDBusPendingReply<> &reply); + + static bool isPosixName(const QString &name); + static bool isSystemdApprovedValue(const QString &value); + + UpdateLaunchEnvironmentJob *q; + QProcessEnvironment environment; + int pendingReplies = 0; +}; + +UpdateLaunchEnvironmentJobPrivate::UpdateLaunchEnvironmentJobPrivate(UpdateLaunchEnvironmentJob *q) + : q(q) +{ +} + +void UpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply) +{ + ++pendingReplies; + + auto *watcher = new QDBusPendingCallWatcher(reply, q); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + --pendingReplies; + + if (pendingReplies == 0) { + Q_EMIT q->finished(); + q->deleteLater(); + } + }); +} + +UpdateLaunchEnvironmentJob::UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment) + : d(new UpdateLaunchEnvironmentJobPrivate(this)) +{ + d->environment = environment; + QTimer::singleShot(0, this, &UpdateLaunchEnvironmentJob::start); +} + +UpdateLaunchEnvironmentJob::~UpdateLaunchEnvironmentJob() = default; + +void UpdateLaunchEnvironmentJob::start() +{ + qDBusRegisterMetaType>(); + QMap dbusActivationEnv; + QStringList systemdUpdates; + + for (const auto &varName : d->environment.keys()) { + if (!UpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) { + qCWarning(KWIN_WRAPPER) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters"; + continue; + } + const QString value = d->environment.value(varName); + + // plasma-session + QDBusMessage lingmoSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("com.lingmo.Session"), + QStringLiteral("/Session"), + QStringLiteral("com.lingmo.Session"), + QStringLiteral("updateLaunchEnv")); + lingmoSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); + auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(lingmoSessionMsg); + d->monitorReply(plasmaSessionReply); + + // DBus-activation environment + dbusActivationEnv.insert(varName, value); + + // _user_ systemd env + // Systemd has stricter parsing of valid environment variables + // https://github.com/systemd/systemd/issues/16704 + // validate here + if (!UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) { + qCWarning(KWIN_WRAPPER) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters"; + continue; + } + const QString updateString = varName + QStringLiteral("=") + value; + systemdUpdates.append(updateString); + } + + // DBus-activation environment + QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("/org/freedesktop/DBus"), + QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("UpdateActivationEnvironment")); + dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)}); + + auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg); + d->monitorReply(dbusActivationReply); + + // _user_ systemd env + QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("SetEnvironment")); + systemdActivationMsg.setArguments({systemdUpdates}); + + auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg); + d->monitorReply(systemdActivationReply); +} + +bool UpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name) +{ + // Posix says characters like % should be 'tolerated', but it gives issues in practice. + // https://bugzilla.redhat.com/show_bug.cgi?id=1754395 + // https://bugzilla.redhat.com/show_bug.cgi?id=1879216 + // Ensure systemd compat by only allowing alphanumerics and _ in names. + bool first = true; + for (const QChar c : name) { + if (first && !c.isLetter() && c != QLatin1Char('_')) { + return false; + } else if (first) { + first = false; + } else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { + return false; + } + } + return !first; +} + +bool UpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value) +{ + // systemd code checks that a value contains no control characters except \n \t + // effectively copied from systemd's string_has_cc + for (const char &it : value.toLatin1()) { + if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) { + continue; + } + if (it > 0 && it < ' ') { + return false; + } + if (it == 127) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/session/wayland_wrapper/UpdateLaunchEnvironment.hpp b/session/wayland_wrapper/UpdateLaunchEnvironment.hpp new file mode 100644 index 0000000..1ad55ca --- /dev/null +++ b/session/wayland_wrapper/UpdateLaunchEnvironment.hpp @@ -0,0 +1,52 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2024 Elysia + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ +#ifndef UPDATELAUNCHENVIRONMENT_HPP +#define UPDATELAUNCHENVIRONMENT_HPP + +#include + +#include + +class QString; +class UpdateLaunchEnvironmentJobPrivate; + +/** + * @class UpdateLaunchEnvironmentJob updatelaunchenvironmentjob.h + * + * Job for updating the launch environment. + * + * This job adds or updates an environment variable in process environment that will be used + * when a process is launched: + * This includes: + * - DBus activation + * - Systemd units + * - lingmo-session + * + * Environment variables are sanitized before uploading. + * + * This object deletes itself after completion, similar to KJobs + */ +class UpdateLaunchEnvironmentJob : public QObject +{ + Q_OBJECT + +public: + explicit UpdateLaunchEnvironmentJob(const QProcessEnvironment &environment); + ~UpdateLaunchEnvironmentJob() override; + +Q_SIGNALS: + void finished(); + +private: + void start(); + +private: + std::unique_ptr const d; +}; + +#endif // UPDATELAUNCHENVIRONMENT_HPP \ No newline at end of file diff --git a/session/wayland_wrapper/signalhandler.cpp b/session/wayland_wrapper/signalhandler.cpp new file mode 100644 index 0000000..ee3088c --- /dev/null +++ b/session/wayland_wrapper/signalhandler.cpp @@ -0,0 +1,62 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "signalhandler.h" +#include "wrapper_logging.h" + +#include +#include +#include +#include + +int SignalHandler::signalFd[2]; + +SignalHandler::SignalHandler() +{ + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { + qCWarning(KWIN_WRAPPER) << "Couldn't create a socketpair"; + return; + } + + m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); + connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); +} + +SignalHandler::~SignalHandler() +{ + for (int sig : std::as_const(m_signalsRegistered)) { + signal(sig, nullptr); + } + close(signalFd[0]); + close(signalFd[1]); +} + +void SignalHandler::addSignal(int signalToTrack) +{ + m_signalsRegistered.insert(signalToTrack); + signal(signalToTrack, signalHandler); +} + +void SignalHandler::signalHandler(int signal) +{ + ::write(signalFd[0], &signal, sizeof(signal)); +} + +void SignalHandler::handleSignal() +{ + m_handler->setEnabled(false); + int signal; + ::read(signalFd[1], &signal, sizeof(signal)); + m_handler->setEnabled(true); + + Q_EMIT signalReceived(signal); +} + +SignalHandler *SignalHandler::self() +{ + static SignalHandler s_self; + return &s_self; +} diff --git a/session/wayland_wrapper/signalhandler.h b/session/wayland_wrapper/signalhandler.h new file mode 100644 index 0000000..c0f8428 --- /dev/null +++ b/session/wayland_wrapper/signalhandler.h @@ -0,0 +1,38 @@ +/* + SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#pragma once + +#include +#include +#include + +/** + * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop + * + * It's a singleton as it relies on static data getting defined. + */ +class SignalHandler : public QObject +{ + Q_OBJECT +public: + ~SignalHandler() override; + void addSignal(int signal); + + static SignalHandler *self(); + +Q_SIGNALS: + void signalReceived(int signal); + +private: + SignalHandler(); + void handleSignal(); + static void signalHandler(int signal); + + QSet m_signalsRegistered; + static int signalFd[2]; + QSocketNotifier *m_handler = nullptr; +}; From 2d8f67c18a99748ca12cacd1356375379a675bca Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 17:34:13 +0800 Subject: [PATCH 21/24] fix kwin_wayland build --- .clang-format | 225 +++++++++++++++++++ CMakeLists.txt | 2 +- session/CMakeLists.txt | 9 + session/daemon-helper.cpp | 4 + session/processmanager.cpp | 412 +++++++++++++++++++++------------- session/processmanager.h | 86 ++++++- startlingmo/signalhandler.cpp | 63 ------ startlingmo/signalhandler.h | 38 ---- 8 files changed, 570 insertions(+), 269 deletions(-) create mode 100644 .clang-format delete mode 100644 startlingmo/signalhandler.cpp delete mode 100644 startlingmo/signalhandler.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8eda42f --- /dev/null +++ b/.clang-format @@ -0,0 +1,225 @@ +--- +Language: Cpp +# BasedOnStyle: WebKit +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: DontAlign +AlignTrailingComments: + Kind: Never + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: All +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: WebKit +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/CMakeLists.txt b/CMakeLists.txt index fabed3d..2d8bf52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ add_subdirectory(sddm-helper) add_subdirectory(clipboard) add_subdirectory(chotkeys) -add_subdirectory(startlingmo) +# add_subdirectory(startlingmo) install(FILES lingmo DESTINATION /etc/ COMPONENT Runtime) install(FILES Desktop/Lingmo DESTINATION /etc/Desktop/ COMPONENT Runtime) diff --git a/session/CMakeLists.txt b/session/CMakeLists.txt index d75fcf9..6d9e49f 100644 --- a/session/CMakeLists.txt +++ b/session/CMakeLists.txt @@ -26,8 +26,16 @@ qt5_add_dbus_adaptor(DBUS_SOURCES set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON) find_package(KF5WindowSystem) +find_package(KF5CoreAddons) find_package(Threads) +find_package(ECM REQUIRED NO_MODULE) +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(ECMQtDeclareLoggingCategory) + +ecm_qt_declare_logging_category(SOURCES HEADER debug.h IDENTIFIER LINGMO_SESSION_D CATEGORY_NAME org.lingmo.session) + add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES}) target_link_libraries(${TARGET} Qt5::Core @@ -37,6 +45,7 @@ target_link_libraries(${TARGET} Qt5::DBus Qt5::X11Extras KF5::WindowSystem + KF5::CoreAddons ${CMAKE_THREAD_LIBS_INIT} ) diff --git a/session/daemon-helper.cpp b/session/daemon-helper.cpp index 901b510..35aa41a 100644 --- a/session/daemon-helper.cpp +++ b/session/daemon-helper.cpp @@ -47,6 +47,10 @@ void Daemon::startProcess(const QPair &processInfo) { connect(process, &QProcess::errorOccurred, this, &Daemon::onProcessError); + auto xcb_extra = QProcessEnvironment(); + xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); + + process->setProcessEnvironment(xcb_extra); process->start(processInfo.first, processInfo.second); if (process->waitForStarted()) { qDebug() << "Process started:" << processInfo.first << "PID:" << process->processId(); diff --git a/session/processmanager.cpp b/session/processmanager.cpp index 756395c..58b482b 100644 --- a/session/processmanager.cpp +++ b/session/processmanager.cpp @@ -6,193 +6,293 @@ #include "application.h" #include -#include -#include +#include +#include #include +#include #include -#include -#include +#include #include -#include +#include #include #include +#include +#include -#include -#include #include +#include +#include #include "daemon-helper.h" +#include "debug.h" + ProcessManager::ProcessManager(Application *app, QObject *parent) - : QObject(parent) - , m_app(app) - , m_wmStarted(false) - , m_waitLoop(nullptr) -{ - qApp->installNativeEventFilter(this); -} - -ProcessManager::~ProcessManager() -{ - qApp->removeNativeEventFilter(this); - - QMapIterator i(m_systemProcess); - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - delete p; - m_systemProcess[i.key()] = nullptr; - } + : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { + qApp->installNativeEventFilter(this); + s_self = this; } -void ProcessManager::start() -{ - startWindowManager(); - startDaemonProcess(); +ProcessManager::~ProcessManager() { + qApp->removeNativeEventFilter(this); + + QMapIterator i(m_systemProcess); + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + delete p; + m_systemProcess[i.key()] = nullptr; + } } -void ProcessManager::logout() -{ - QMapIterator i(m_systemProcess); +void ProcessManager::start() { + startWindowManager(); + startDaemonProcess(); +} - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - p->terminate(); - } - i.toFront(); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { - p->kill(); - } +void ProcessManager::logout() { + QMapIterator i(m_systemProcess); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + p->terminate(); + } + i.toFront(); + + while (i.hasNext()) { + i.next(); + QProcess *p = i.value(); + if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { + p->kill(); } + } + + QCoreApplication::exit(0); +} + +void ProcessManager::startWindowManager() { + auto *wmProcess = new QProcess; - QCoreApplication::exit(0); + wmProcess->start(m_app->wayland() ? "kwin_wayland" : "kwin_x11", + QStringList()); + + if (!m_app->wayland()) { + QEventLoop waitLoop; + m_waitLoop = &waitLoop; + // add a timeout to avoid infinite blocking if a WM fail to execute. + QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); + waitLoop.exec(); + m_waitLoop = nullptr; + } +} + +void ProcessManager::startDesktopProcess() { + // When the lingmo-settings-daemon theme module is loaded, start the desktop. + // In the way, there will be no problem that desktop and launcher can't get + // wallpaper. + + QList> list; + // Desktop components + list << qMakePair(QString("lingmo-notificationd"), QStringList()); + list << qMakePair(QString("lingmo-statusbar"), QStringList()); + list << qMakePair(QString("lingmo-dock"), QStringList()); + list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); + list << qMakePair(QString("lingmo-launcher"), QStringList()); + list << qMakePair(QString("lingmo-powerman"), QStringList()); + list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); + + m_desktopAutoStartD = std::make_shared(list); + + // Auto start + QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); } -void ProcessManager::startWindowManager() -{ - auto *wmProcess = new QProcess; +void ProcessManager::startDaemonProcess() { + QList> list; + list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); + list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); + list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); + list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); + // list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-chotkeys"), QStringList()); - wmProcess->start(m_app->wayland() ? "kwin_wayland" : "kwin_x11", QStringList()); + m_daemonAutoStartD = std::make_shared(list); +} - if (!m_app->wayland()) { - QEventLoop waitLoop; - m_waitLoop = &waitLoop; - // add a timeout to avoid infinite blocking if a WM fail to execute. - QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); - waitLoop.exec(); - m_waitLoop = nullptr; +void ProcessManager::loadAutoStartProcess() { + QList> list; + + const QStringList dirs = QStandardPaths::locateAll( + QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), + QStandardPaths::LocateDirectory); + for (const QString &dir : dirs) { + const QDir d(dir); + const QStringList fileNames = + d.entryList(QStringList() << QStringLiteral("*.desktop")); + for (const QString &file : fileNames) { + QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); + desktop.setIniCodec("UTF-8"); + desktop.beginGroup("Desktop Entry"); + + // Ignore files the require a specific desktop environment + if (desktop.contains("NotShowIn")) { + const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); + if (notShowIn.contains("Lingmo")) + continue; + } + if (desktop.contains("OnlyShowIn")) { + const QStringList onlyShowIn = + desktop.value("OnlyShowIn").toStringList(); + if (!onlyShowIn.contains("Lingmo")) + continue; + } + + const QString execValue = desktop.value("Exec").toString(); + + // 避免冲突 + if (execValue.contains("gmenudbusmenuproxy")) + continue; + + // 使用 QProcess::splitCommand 来解析命令和参数 + QStringList args = QProcess::splitCommand(execValue); + + // 检查是否至少有一个元素(即程序路径) + if (!args.isEmpty()) { + auto program = args.first(); + args.removeFirst(); // 移除程序路径,剩下的都是参数 + + list << qMakePair(program, args); + } else { + qWarning() << "Invalid 'Exec' found in file!"; + } } + } + + m_userAutoStartD = std::make_shared(list, false); } -void ProcessManager::startDesktopProcess() -{ - // When the lingmo-settings-daemon theme module is loaded, start the desktop. - // In the way, there will be no problem that desktop and launcher can't get wallpaper. - - QList> list; - // Desktop components - list << qMakePair(QString("lingmo-notificationd"), QStringList()); - list << qMakePair(QString("lingmo-statusbar"), QStringList()); - list << qMakePair(QString("lingmo-dock"), QStringList()); - list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); - list << qMakePair(QString("lingmo-launcher"), QStringList()); - list << qMakePair(QString("lingmo-powerman"), QStringList()); - list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); - - m_desktopAutoStartD = std::make_shared(list); - - // Auto start - QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); -} - -void ProcessManager::startDaemonProcess() -{ - QList> list; - list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); - list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); - list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); - list << qMakePair(QString("lingmo-permission-surveillance"),QStringList()); -// list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-chotkeys"), QStringList()); - - m_daemonAutoStartD = std::make_shared(list); -} - -void ProcessManager::loadAutoStartProcess() -{ - QList> list; - - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, - QStringLiteral("autostart"), - QStandardPaths::LocateDirectory); - for (const QString &dir : dirs) { - const QDir d(dir); - const QStringList fileNames = d.entryList(QStringList() << QStringLiteral("*.desktop")); - for (const QString &file : fileNames) { - QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); - desktop.setIniCodec("UTF-8"); - desktop.beginGroup("Desktop Entry"); - - // Ignore files the require a specific desktop environment - if (desktop.contains("NotShowIn")) { - const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); - if (notShowIn.contains("Lingmo")) - continue; - } - if (desktop.contains("OnlyShowIn")) { - const QStringList onlyShowIn = desktop.value("OnlyShowIn").toStringList(); - if (!onlyShowIn.contains("Lingmo")) - continue; - } - - const QString execValue = desktop.value("Exec").toString(); - - // 避免冲突 - if (execValue.contains("gmenudbusmenuproxy")) - continue; - - // 使用 QProcess::splitCommand 来解析命令和参数 - QStringList args = QProcess::splitCommand(execValue); - - // 检查是否至少有一个元素(即程序路径) - if (!args.isEmpty()) { - auto program = args.first(); - args.removeFirst(); // 移除程序路径,剩下的都是参数 - - list << qMakePair(program, args); - } else { - qWarning() << "Invalid 'Exec' found in file!"; - } - } +bool ProcessManager::nativeEventFilter(const QByteArray &eventType, + void *message, long *result) { + if (eventType != "xcb_generic_event_t") // We only want to handle XCB events + return false; + + // ref: lxqt session + if (!m_wmStarted && m_waitLoop) { + // all window managers must set their name according to the spec + if (!QString::fromUtf8( + NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) + .wmName()) + .isEmpty()) { + qDebug() << "Window manager started"; + m_wmStarted = true; + if (m_waitLoop && m_waitLoop->isRunning()) + m_waitLoop->exit(); + + qApp->removeNativeEventFilter(this); } + } - m_userAutoStartD = std::make_shared(list, false); + return false; } -bool ProcessManager::nativeEventFilter(const QByteArray &eventType, void *message, long *result) -{ - if (eventType != "xcb_generic_event_t") // We only want to handle XCB events - return false; +bool ProcessManager::startDetached(QProcess *process) { + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->start(); + const bool ret = process->waitForStarted(); + if (ret) { + m_processes << process; + } + return ret; +} - // ref: lxqt session - if (!m_wmStarted && m_waitLoop) { - // all window managers must set their name according to the spec - if (!QString::fromUtf8(NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck).wmName()).isEmpty()) { - qDebug() << "Window manager started"; - m_wmStarted = true; - if (m_waitLoop && m_waitLoop->isRunning()) - m_waitLoop->exit(); +StartProcessJob::StartProcessJob(const QString &process, + const QStringList &args, + const QProcessEnvironment &additionalEnv) + : KJob(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(finished(int, QProcess::ExitStatus))); +} - qApp->removeNativeEventFilter(this); - } - } +void StartProcessJob::start() { + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); - return false; + m_process->start(); } + +void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { + qCDebug(LINGMO_SESSION_D) << "process job " << m_process->program() + << "finished with exit code " << exitCode; + emitResult(); +} + +StartServiceJob::StartServiceJob(const QString &process, + const QStringList &args, + const QString &serviceId, + const QProcessEnvironment &additionalEnv) + : KJob(), m_process(new QProcess), m_serviceId(serviceId), + m_additionalEnv(additionalEnv) { + m_process->setProgram(process); + m_process->setArguments(args); + + auto watcher = + new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, this); + connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, + &StartServiceJob::emitResult); +} + +void StartServiceJob::start() { + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(m_additionalEnv); + m_process->setProcessEnvironment(env); + + if (!m_serviceId.isEmpty() && + QDBusConnection::sessionBus().interface()->isServiceRegistered( + m_serviceId)) { + qCDebug(LINGMO_SESSION_D) << m_process << "already running"; + emitResult(); + return; + } + qDebug() << "Starting " << m_process->program() << m_process->arguments(); + if (!ProcessManager::self()->startDetached(m_process)) { + qCWarning(LINGMO_SESSION_D) + << "error starting process" << m_process->program() + << m_process->arguments(); + emitResult(); + } + + if (m_serviceId.isEmpty()) { + emitResult(); + } +} + +LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, + const QProcessEnvironment &additionalEnv) + : KJob(), m_process(new QProcess(this)) { + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); +} + +void LaunchProcess::start() { + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); + + m_process->startDetached(); + + emitResult(); +} \ No newline at end of file diff --git a/session/processmanager.h b/session/processmanager.h index f6a5562..8e36ca0 100644 --- a/session/processmanager.h +++ b/session/processmanager.h @@ -20,25 +20,32 @@ #ifndef PROCESSMANAGER_H #define PROCESSMANAGER_H -#include #include -#include -#include +#include #include #include +#include +#include #include #include "daemon-helper.h" +#include + class Application; -class ProcessManager : public QObject, public QAbstractNativeEventFilter -{ +class ProcessManager : public QObject, public QAbstractNativeEventFilter { Q_OBJECT public: - explicit ProcessManager(Application *app, QObject *parent = nullptr); + explicit ProcessManager(Application* app, QObject* parent = nullptr); ~ProcessManager(); + static ProcessManager* self() + { + Q_ASSERT(s_self); + return s_self; + } + void start(); void logout(); @@ -52,12 +59,17 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter */ void loadAutoStartProcess(); - bool nativeEventFilter(const QByteArray & eventType, void * message, long * result) override; + bool nativeEventFilter(const QByteArray& eventType, void* message, + long* result) override; + + // Start a custom process + bool startDetached(QProcess *process); private: - Application *m_app; - QMap m_systemProcess; - QMap m_autoStartProcess; + Application* m_app; + QMap m_systemProcess; + QMap m_autoStartProcess; + QVector m_processes; // Daemon helper for desktop components std::shared_ptr m_desktopAutoStartD; @@ -69,7 +81,59 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter std::shared_ptr m_userAutoStartD; bool m_wmStarted; - QEventLoop *m_waitLoop; + QEventLoop* m_waitLoop; + + static ProcessManager* s_self; +}; + +/** + * Launches a process, and waits for the process to start + */ +class LaunchProcess : public KJob { + Q_OBJECT +public: + LaunchProcess( + const QString& process, const QStringList& args, + const QProcessEnvironment& additionalEnv = QProcessEnvironment()); + void start() override; + +private: + QProcess* m_process; +}; + +/** + * Launches a process, and waits for the process to finish + */ +class StartProcessJob : public KJob { + Q_OBJECT +public: + StartProcessJob( + const QString& process, const QStringList& args, + const QProcessEnvironment& additionalEnv = QProcessEnvironment()); + void start() override; + +public Q_SLOTS: + void finished(int exitCode, QProcess::ExitStatus e); + +private: + QProcess* m_process; }; +/** + * Launches a process, and waits for the service to appear on the session bus + */ +class StartServiceJob : public KJob { + Q_OBJECT +public: + StartServiceJob( + const QString& process, const QStringList& args, const QString& serviceId, + const QProcessEnvironment& additionalEnv = QProcessEnvironment()); + + void start() override; + +private: + QProcess* m_process; + const QString m_serviceId; + const QProcessEnvironment m_additionalEnv; +}; #endif // PROCESSMANAGER_H diff --git a/startlingmo/signalhandler.cpp b/startlingmo/signalhandler.cpp deleted file mode 100644 index 9e96c58..0000000 --- a/startlingmo/signalhandler.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#include "signalhandler.h" -#include "debug.h" -#include "startlingmo.hpp" - -#include -#include -#include -#include - -int SignalHandler::signalFd[2]; - -SignalHandler::SignalHandler() -{ - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) { - qCWarning(LINGMO_STARTUP) << "Couldn't create a socketpair"; - return; - } - - m_handler = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); - connect(m_handler, &QSocketNotifier::activated, this, &SignalHandler::handleSignal); -} - -SignalHandler::~SignalHandler() -{ - for (int sig : std::as_const(m_signalsRegistered)) { - signal(sig, nullptr); - } - close(signalFd[0]); - close(signalFd[1]); -} - -void SignalHandler::addSignal(int signalToTrack) -{ - m_signalsRegistered.insert(signalToTrack); - signal(signalToTrack, signalHandler); -} - -void SignalHandler::signalHandler(int signal) -{ - ::write(signalFd[0], &signal, sizeof(signal)); -} - -void SignalHandler::handleSignal() -{ - m_handler->setEnabled(false); - int signal; - ::read(signalFd[1], &signal, sizeof(signal)); - m_handler->setEnabled(true); - - Q_EMIT signalReceived(signal); -} - -SignalHandler *SignalHandler::self() -{ - static SignalHandler s_self; - return &s_self; -} diff --git a/startlingmo/signalhandler.h b/startlingmo/signalhandler.h deleted file mode 100644 index c0f8428..0000000 --- a/startlingmo/signalhandler.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.0-only -*/ - -#pragma once - -#include -#include -#include - -/** - * Class to be able to receive ANSI C signals and forward them onto the Qt eventloop - * - * It's a singleton as it relies on static data getting defined. - */ -class SignalHandler : public QObject -{ - Q_OBJECT -public: - ~SignalHandler() override; - void addSignal(int signal); - - static SignalHandler *self(); - -Q_SIGNALS: - void signalReceived(int signal); - -private: - SignalHandler(); - void handleSignal(); - static void signalHandler(int signal); - - QSet m_signalsRegistered; - static int signalFd[2]; - QSocketNotifier *m_handler = nullptr; -}; From 7d6f0bf30771da6264a317ebe60d7eb838d93589 Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 20:02:19 +0800 Subject: [PATCH 22/24] Add env updator --- session/application.cpp | 60 +++-- session/application.h | 37 +-- session/com.lingmo.Session.xml | 4 + session/processmanager.cpp | 468 +++++++++++++++++---------------- session/processmanager.h | 9 +- 5 files changed, 315 insertions(+), 263 deletions(-) diff --git a/session/application.cpp b/session/application.cpp index 32786e9..47b9f88 100644 --- a/session/application.cpp +++ b/session/application.cpp @@ -18,16 +18,17 @@ */ #include "application.h" +#include "debug.h" #include "sessionadaptor.h" // Qt #include #include -#include -#include -#include #include #include +#include +#include +#include #include #include @@ -39,9 +40,9 @@ std::optional getSystemdEnvironment() { QStringList list; auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), - QStringLiteral("/org/freedesktop/systemd1"), - QStringLiteral("org.freedesktop.DBus.Properties"), - QStringLiteral("Get")); + 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) { @@ -61,12 +62,12 @@ std::optional getSystemdEnvironment() return variant.toStringList(); } -bool isShellVariable(const QByteArray &name) +bool isShellVariable(const QByteArray& name) { return name == "_" || name.startsWith("SHLVL"); } -bool isSessionVariable(const QByteArray &name) +bool isSessionVariable(const QByteArray& name) { // Check is variable is specific to session. return name == "DISPLAY" || name == "XAUTHORITY" || // @@ -74,14 +75,14 @@ bool isSessionVariable(const QByteArray &name) name.startsWith("XDG_"); } -void setEnvironmentVariable(const QByteArray &name, const QByteArray &value) +void setEnvironmentVariable(const QByteArray& name, const QByteArray& value) { if (qgetenv(name) != value) { qputenv(name, value); } } -Application::Application(int &argc, char **argv) +Application::Application(int& argc, char** argv) : QApplication(argc, argv) , m_processManager(new ProcessManager(this)) , m_networkProxyManager(new NetworkProxyManager) @@ -97,7 +98,9 @@ Application::Application(int &argc, char **argv) parser.setApplicationDescription(QStringLiteral("Lingmo Session")); parser.addHelpOption(); - QCommandLineOption waylandOption(QStringList() << "w" << "wayland" << "Wayland Mode"); + QCommandLineOption waylandOption(QStringList() << "w" + << "wayland" + << "Wayland Mode"); parser.addOption(waylandOption); parser.process(*this); @@ -140,7 +143,7 @@ bool Application::wayland() const return m_wayland; } -void Application::launch(const QString &exec, const QStringList &args) +void Application::launch(const QString& exec, const QStringList& args) { QProcess process; process.setProgram(exec); @@ -149,7 +152,7 @@ void Application::launch(const QString &exec, const QStringList &args) process.startDetached(); } -void Application::launch(const QString &exec, const QString &workingDir, const QStringList &args) +void Application::launch(const QString& exec, const QString& workingDir, const QStringList& args) { QProcess process; process.setProgram(exec); @@ -183,7 +186,7 @@ void Application::initEnvironments() // Qt qputenv("QT_QPA_PLATFORMTHEME", "lingmo"); qputenv("QT_PLATFORM_PLUGIN", "lingmo"); - + // ref: https://stackoverflow.com/questions/34399993/qml-performance-issue-when-updating-an-item-in-presence-of-many-non-overlapping qputenv("QT_QPA_UPDATE_IDLE_TIME", "10"); @@ -208,7 +211,7 @@ void Application::initLanguage() if (file.open(QIODevice::ReadOnly)) { QStringList lines = QString(file.readAll()).split('\n'); - for (const QString &line : lines) { + for (const QString& line : lines) { if (line.startsWith('#')) continue; @@ -276,14 +279,14 @@ void Application::initXResource() "Xft.antialias: %4\n" "Xft.hintstyle: %5\n" "Xft.rgba: rgb") - .arg(fontDpi) - .arg(cursorTheme) - .arg(cursorSize) - .arg(xftAntialias) - .arg(xftHintStyle); + .arg(fontDpi) + .arg(cursorTheme) + .arg(cursorSize) + .arg(xftAntialias) + .arg(xftHintStyle); QProcess p; - p.start(QStringLiteral("xrdb"), {QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp")}); + p.start(QStringLiteral("xrdb"), { QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp") }); p.setProcessChannelMode(QProcess::ForwardedChannels); p.write(datas.toLatin1()); p.closeWriteChannel(); @@ -293,7 +296,7 @@ void Application::initXResource() qputenv("LINGMO_FONT_DPI", QByteArray::number(fontDpi)); // Init cursor - runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)}); + runSync("cupdatecursor", { cursorTheme, QString::number(cursorSize) }); // qputenv("XCURSOR_THEME", cursorTheme.toLatin1()); // qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor)); } @@ -301,7 +304,7 @@ void Application::initXResource() void Application::initKWinConfig() { QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/kwinrc", - QSettings::IniFormat); + QSettings::IniFormat); settings.beginGroup("Effect-Blur"); settings.setValue("BlurStrength", 10); @@ -346,7 +349,7 @@ void Application::importSystemdEnvrionment() return; } - for (auto &envString : environment.value()) { + for (auto& envString : environment.value()) { const auto env = envString.toLocal8Bit(); const int idx = env.indexOf('='); if (Q_UNLIKELY(idx <= 0)) { @@ -382,7 +385,7 @@ void Application::updateUserDirs() // p.waitForFinished(-1); } -int Application::runSync(const QString &program, const QStringList &args, const QStringList &env) +int Application::runSync(const QString& program, const QStringList& args, const QStringList& env) { QProcess p; @@ -399,3 +402,10 @@ int Application::runSync(const QString &program, const QStringList &args, const return p.exitCode(); } + +void Application::updateLaunchEnv(const QString& key, const QString& value) +{ + qCDebug(LINGMO_SESSION_D) << "Update launch env: " << key << value; + qputenv(key.toLatin1(), value.toLatin1()); + m_processManager->updateLaunchEnv(key, value); +} \ No newline at end of file diff --git a/session/application.h b/session/application.h index 0920420..48bc36e 100644 --- a/session/application.h +++ b/session/application.h @@ -24,49 +24,56 @@ #include #include -#include "processmanager.h" #include "networkproxymanager.h" #include "powermanager/power.h" +#include "processmanager.h" -class Application : public QApplication -{ +class Application : public QApplication { Q_OBJECT public: - explicit Application(int &argc, char **argv); + explicit Application(int& argc, char** argv); bool wayland() const; public slots: - void logout() { + void logout() + { m_processManager->logout(); } - void reboot() { + void reboot() + { m_power.reboot(); QCoreApplication::exit(0); } - void powerOff() { + void powerOff() + { m_power.shutdown(); QCoreApplication::exit(0); } - void suspend() { + void suspend() + { m_power.suspend(); } - [[maybe_unused]] void startDesktopProcess() { + [[maybe_unused]] void startDesktopProcess() + { // Start Lingmo Desktop Environment m_processManager->startDesktopProcess(); } - [[maybe_unused]] void updateNetworkProxy() { + [[maybe_unused]] void updateNetworkProxy() + { m_networkProxyManager->update(); } - void launch(const QString &exec, const QStringList &args); - void launch(const QString &exec, const QString &workingDir, const QStringList &args); + [[maybe_unused]] void updateLaunchEnv(const QString &key, const QString &value); + + void launch(const QString& exec, const QStringList& args); + void launch(const QString& exec, const QString& workingDir, const QStringList& args); private: void initEnvironments(); @@ -78,11 +85,11 @@ public slots: void importSystemdEnvrionment(); void createConfigDirectory(); void updateUserDirs(); - int runSync(const QString &program, const QStringList &args, const QStringList &env = {}); + int runSync(const QString& program, const QStringList& args, const QStringList& env = {}); private: - ProcessManager *m_processManager; - NetworkProxyManager *m_networkProxyManager; + ProcessManager* m_processManager; + NetworkProxyManager* m_networkProxyManager; Power m_power; bool m_wayland; diff --git a/session/com.lingmo.Session.xml b/session/com.lingmo.Session.xml index 199b7ee..eb52db4 100644 --- a/session/com.lingmo.Session.xml +++ b/session/com.lingmo.Session.xml @@ -28,5 +28,9 @@ + + + + diff --git a/session/processmanager.cpp b/session/processmanager.cpp index 58b482b..aead7c1 100644 --- a/session/processmanager.cpp +++ b/session/processmanager.cpp @@ -15,10 +15,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -28,271 +28,297 @@ #include "debug.h" -ProcessManager::ProcessManager(Application *app, QObject *parent) - : QObject(parent), m_app(app), m_wmStarted(false), m_waitLoop(nullptr) { - qApp->installNativeEventFilter(this); - s_self = this; +ProcessManager* s_self = nullptr; + +ProcessManager::ProcessManager(Application* app, QObject* parent) + : QObject(parent) + , m_app(app) + , m_wmStarted(false) + , m_waitLoop(nullptr) +{ + qApp->installNativeEventFilter(this); + s_self = this; } -ProcessManager::~ProcessManager() { - qApp->removeNativeEventFilter(this); +ProcessManager::~ProcessManager() +{ + qApp->removeNativeEventFilter(this); - QMapIterator i(m_systemProcess); - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - delete p; - m_systemProcess[i.key()] = nullptr; - } + QMapIterator i(m_systemProcess); + while (i.hasNext()) { + i.next(); + QProcess* p = i.value(); + delete p; + m_systemProcess[i.key()] = nullptr; + } } -void ProcessManager::start() { - startWindowManager(); - startDaemonProcess(); +void ProcessManager::start() +{ + startWindowManager(); + startDaemonProcess(); } -void ProcessManager::logout() { - QMapIterator i(m_systemProcess); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - p->terminate(); - } - i.toFront(); - - while (i.hasNext()) { - i.next(); - QProcess *p = i.value(); - if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { - p->kill(); +void ProcessManager::logout() +{ + QMapIterator i(m_systemProcess); + + while (i.hasNext()) { + i.next(); + QProcess* p = i.value(); + p->terminate(); + } + i.toFront(); + + while (i.hasNext()) { + i.next(); + QProcess* p = i.value(); + if (p->state() != QProcess::NotRunning && !p->waitForFinished(2000)) { + p->kill(); + } } - } - QCoreApplication::exit(0); + QCoreApplication::exit(0); } -void ProcessManager::startWindowManager() { - auto *wmProcess = new QProcess; +void ProcessManager::startWindowManager() +{ + auto* wmProcess = new QProcess; - wmProcess->start(m_app->wayland() ? "kwin_wayland" : "kwin_x11", - QStringList()); + wmProcess->start(m_app->wayland() ? "lingmo_kwin_wayland_wrapper" : "kwin_x11", + QStringList()); - if (!m_app->wayland()) { - QEventLoop waitLoop; - m_waitLoop = &waitLoop; - // add a timeout to avoid infinite blocking if a WM fail to execute. - QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); - waitLoop.exec(); - m_waitLoop = nullptr; - } + if (!m_app->wayland()) { + QEventLoop waitLoop; + m_waitLoop = &waitLoop; + // add a timeout to avoid infinite blocking if a WM fail to execute. + QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit())); + waitLoop.exec(); + m_waitLoop = nullptr; + } } -void ProcessManager::startDesktopProcess() { - // When the lingmo-settings-daemon theme module is loaded, start the desktop. - // In the way, there will be no problem that desktop and launcher can't get - // wallpaper. - - QList> list; - // Desktop components - list << qMakePair(QString("lingmo-notificationd"), QStringList()); - list << qMakePair(QString("lingmo-statusbar"), QStringList()); - list << qMakePair(QString("lingmo-dock"), QStringList()); - list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); - list << qMakePair(QString("lingmo-launcher"), QStringList()); - list << qMakePair(QString("lingmo-powerman"), QStringList()); - list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); - - m_desktopAutoStartD = std::make_shared(list); - - // Auto start - QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); +void ProcessManager::startDesktopProcess() +{ + // When the lingmo-settings-daemon theme module is loaded, start the desktop. + // In the way, there will be no problem that desktop and launcher can't get + // wallpaper. + + QList> list; + // Desktop components + list << qMakePair(QString("lingmo-notificationd"), QStringList()); + list << qMakePair(QString("lingmo-statusbar"), QStringList()); + list << qMakePair(QString("lingmo-dock"), QStringList()); + list << qMakePair(QString("lingmo-filemanager"), QStringList("--desktop")); + list << qMakePair(QString("lingmo-launcher"), QStringList()); + list << qMakePair(QString("lingmo-powerman"), QStringList()); + list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-wallpaper-color-pick"), QStringList()); + + m_desktopAutoStartD = std::make_shared(list); + + // Auto start + QTimer::singleShot(100, this, &ProcessManager::loadAutoStartProcess); } -void ProcessManager::startDaemonProcess() { - QList> list; - list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); - list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); - list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); - list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); - // list << qMakePair(QString("lingmo-clipboard"), QStringList()); - list << qMakePair(QString("lingmo-chotkeys"), QStringList()); - - m_daemonAutoStartD = std::make_shared(list); +void ProcessManager::startDaemonProcess() +{ + QList> list; + list << qMakePair(QString("lingmo-settings-daemon"), QStringList()); + list << qMakePair(QString("lingmo-xembedsniproxy"), QStringList()); + list << qMakePair(QString("lingmo-gmenuproxy"), QStringList()); + list << qMakePair(QString("lingmo-permission-surveillance"), QStringList()); + // list << qMakePair(QString("lingmo-clipboard"), QStringList()); + list << qMakePair(QString("lingmo-chotkeys"), QStringList()); + + m_daemonAutoStartD = std::make_shared(list); } -void ProcessManager::loadAutoStartProcess() { - QList> list; - - const QStringList dirs = QStandardPaths::locateAll( - QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), - QStandardPaths::LocateDirectory); - for (const QString &dir : dirs) { - const QDir d(dir); - const QStringList fileNames = - d.entryList(QStringList() << QStringLiteral("*.desktop")); - for (const QString &file : fileNames) { - QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); - desktop.setIniCodec("UTF-8"); - desktop.beginGroup("Desktop Entry"); - - // Ignore files the require a specific desktop environment - if (desktop.contains("NotShowIn")) { - const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); - if (notShowIn.contains("Lingmo")) - continue; - } - if (desktop.contains("OnlyShowIn")) { - const QStringList onlyShowIn = - desktop.value("OnlyShowIn").toStringList(); - if (!onlyShowIn.contains("Lingmo")) - continue; - } - - const QString execValue = desktop.value("Exec").toString(); - - // 避免冲突 - if (execValue.contains("gmenudbusmenuproxy")) - continue; - - // 使用 QProcess::splitCommand 来解析命令和参数 - QStringList args = QProcess::splitCommand(execValue); - - // 检查是否至少有一个元素(即程序路径) - if (!args.isEmpty()) { - auto program = args.first(); - args.removeFirst(); // 移除程序路径,剩下的都是参数 - - list << qMakePair(program, args); - } else { - qWarning() << "Invalid 'Exec' found in file!"; - } +void ProcessManager::loadAutoStartProcess() +{ + QList> list; + + const QStringList dirs = QStandardPaths::locateAll( + QStandardPaths::GenericConfigLocation, QStringLiteral("autostart"), + QStandardPaths::LocateDirectory); + for (const QString& dir : dirs) { + const QDir d(dir); + const QStringList fileNames = d.entryList(QStringList() << QStringLiteral("*.desktop")); + for (const QString& file : fileNames) { + QSettings desktop(d.absoluteFilePath(file), QSettings::IniFormat); + desktop.setIniCodec("UTF-8"); + desktop.beginGroup("Desktop Entry"); + + // Ignore files the require a specific desktop environment + if (desktop.contains("NotShowIn")) { + const QStringList notShowIn = desktop.value("NotShowIn").toStringList(); + if (notShowIn.contains("Lingmo")) + continue; + } + if (desktop.contains("OnlyShowIn")) { + const QStringList onlyShowIn = desktop.value("OnlyShowIn").toStringList(); + if (!onlyShowIn.contains("Lingmo")) + continue; + } + + const QString execValue = desktop.value("Exec").toString(); + + // 避免冲突 + if (execValue.contains("gmenudbusmenuproxy")) + continue; + + // 使用 QProcess::splitCommand 来解析命令和参数 + QStringList args = QProcess::splitCommand(execValue); + + // 检查是否至少有一个元素(即程序路径) + if (!args.isEmpty()) { + auto program = args.first(); + args.removeFirst(); // 移除程序路径,剩下的都是参数 + + list << qMakePair(program, args); + } else { + qWarning() << "Invalid 'Exec' found in file!"; + } + } } - } - m_userAutoStartD = std::make_shared(list, false); + m_userAutoStartD = std::make_shared(list, false); } -bool ProcessManager::nativeEventFilter(const QByteArray &eventType, - void *message, long *result) { - if (eventType != "xcb_generic_event_t") // We only want to handle XCB events +bool ProcessManager::nativeEventFilter(const QByteArray& eventType, + void* message, long* result) +{ + if (eventType != "xcb_generic_event_t") // We only want to handle XCB events + return false; + + // ref: lxqt session + if (!m_wmStarted && m_waitLoop) { + // all window managers must set their name according to the spec + if (!QString::fromUtf8( + NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) + .wmName()) + .isEmpty()) { + qDebug() << "Window manager started"; + m_wmStarted = true; + if (m_waitLoop && m_waitLoop->isRunning()) + m_waitLoop->exit(); + + qApp->removeNativeEventFilter(this); + } + } + return false; +} - // ref: lxqt session - if (!m_wmStarted && m_waitLoop) { - // all window managers must set their name according to the spec - if (!QString::fromUtf8( - NETRootInfo(QX11Info::connection(), NET::SupportingWMCheck) - .wmName()) - .isEmpty()) { - qDebug() << "Window manager started"; - m_wmStarted = true; - if (m_waitLoop && m_waitLoop->isRunning()) - m_waitLoop->exit(); - - qApp->removeNativeEventFilter(this); +bool ProcessManager::startDetached(QProcess* process) +{ + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->start(); + const bool ret = process->waitForStarted(); + if (ret) { + m_processes << process; } - } - - return false; + return ret; } -bool ProcessManager::startDetached(QProcess *process) { - process->setProcessChannelMode(QProcess::ForwardedChannels); - process->start(); - const bool ret = process->waitForStarted(); - if (ret) { - m_processes << process; - } - return ret; +void ProcessManager::updateLaunchEnv(const QString& key, const QString& value) +{ + qputenv(key.toLatin1(), value.toLatin1()); } -StartProcessJob::StartProcessJob(const QString &process, - const QStringList &args, - const QProcessEnvironment &additionalEnv) - : KJob(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); - - connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, - SLOT(finished(int, QProcess::ExitStatus))); +StartProcessJob::StartProcessJob(const QString& process, + const QStringList& args, + const QProcessEnvironment& additionalEnv) + : KJob() + , m_process(new QProcess(this)) +{ + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(finished(int, QProcess::ExitStatus))); } -void StartProcessJob::start() { - qCDebug(LINGMO_SESSION_D) - << "Starting " << m_process->program() << m_process->arguments(); +void StartProcessJob::start() +{ + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); - m_process->start(); + m_process->start(); } -void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) { - qCDebug(LINGMO_SESSION_D) << "process job " << m_process->program() - << "finished with exit code " << exitCode; - emitResult(); +void StartProcessJob::finished(int exitCode, QProcess::ExitStatus e) +{ + qCDebug(LINGMO_SESSION_D) << "process job " << m_process->program() + << "finished with exit code " << exitCode; + emitResult(); } -StartServiceJob::StartServiceJob(const QString &process, - const QStringList &args, - const QString &serviceId, - const QProcessEnvironment &additionalEnv) - : KJob(), m_process(new QProcess), m_serviceId(serviceId), - m_additionalEnv(additionalEnv) { - m_process->setProgram(process); - m_process->setArguments(args); - - auto watcher = - new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), - QDBusServiceWatcher::WatchForRegistration, this); - connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, - &StartServiceJob::emitResult); +StartServiceJob::StartServiceJob(const QString& process, + const QStringList& args, + const QString& serviceId, + const QProcessEnvironment& additionalEnv) + : KJob() + , m_process(new QProcess) + , m_serviceId(serviceId) + , m_additionalEnv(additionalEnv) +{ + m_process->setProgram(process); + m_process->setArguments(args); + + auto watcher = new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, this); + connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, + &StartServiceJob::emitResult); } -void StartServiceJob::start() { - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(m_additionalEnv); - m_process->setProcessEnvironment(env); +void StartServiceJob::start() +{ + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(m_additionalEnv); + m_process->setProcessEnvironment(env); - if (!m_serviceId.isEmpty() && - QDBusConnection::sessionBus().interface()->isServiceRegistered( - m_serviceId)) { - qCDebug(LINGMO_SESSION_D) << m_process << "already running"; - emitResult(); - return; - } - qDebug() << "Starting " << m_process->program() << m_process->arguments(); - if (!ProcessManager::self()->startDetached(m_process)) { - qCWarning(LINGMO_SESSION_D) - << "error starting process" << m_process->program() - << m_process->arguments(); - emitResult(); - } + if (!m_serviceId.isEmpty() && QDBusConnection::sessionBus().interface()->isServiceRegistered(m_serviceId)) { + qCDebug(LINGMO_SESSION_D) << m_process << "already running"; + emitResult(); + return; + } + qDebug() << "Starting " << m_process->program() << m_process->arguments(); + if (!ProcessManager::self()->startDetached(m_process)) { + qCWarning(LINGMO_SESSION_D) + << "error starting process" << m_process->program() + << m_process->arguments(); + emitResult(); + } - if (m_serviceId.isEmpty()) { - emitResult(); - } + if (m_serviceId.isEmpty()) { + emitResult(); + } } -LaunchProcess::LaunchProcess(const QString &process, const QStringList &args, - const QProcessEnvironment &additionalEnv) - : KJob(), m_process(new QProcess(this)) { - m_process->setProgram(process); - m_process->setArguments(args); - m_process->setProcessChannelMode(QProcess::ForwardedChannels); - auto env = QProcessEnvironment::systemEnvironment(); - env.insert(additionalEnv); - m_process->setProcessEnvironment(env); +LaunchProcess::LaunchProcess(const QString& process, const QStringList& args, + const QProcessEnvironment& additionalEnv) + : KJob() + , m_process(new QProcess(this)) +{ + m_process->setProgram(process); + m_process->setArguments(args); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(additionalEnv); + m_process->setProcessEnvironment(env); } -void LaunchProcess::start() { - qCDebug(LINGMO_SESSION_D) - << "Starting " << m_process->program() << m_process->arguments(); +void LaunchProcess::start() +{ + qCDebug(LINGMO_SESSION_D) + << "Starting " << m_process->program() << m_process->arguments(); - m_process->startDetached(); + m_process->startDetached(); - emitResult(); + emitResult(); } \ No newline at end of file diff --git a/session/processmanager.h b/session/processmanager.h index 8e36ca0..32e6ccc 100644 --- a/session/processmanager.h +++ b/session/processmanager.h @@ -32,6 +32,10 @@ #include +class ProcessManager; + +extern ProcessManager* s_self; + class Application; class ProcessManager : public QObject, public QAbstractNativeEventFilter { Q_OBJECT @@ -65,6 +69,9 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter { // Start a custom process bool startDetached(QProcess *process); + // Update the launch environment for a process + void updateLaunchEnv(const QString& key, const QString& value); + private: Application* m_app; QMap m_systemProcess; @@ -82,8 +89,6 @@ class ProcessManager : public QObject, public QAbstractNativeEventFilter { bool m_wmStarted; QEventLoop* m_waitLoop; - - static ProcessManager* s_self; }; /** From 7247a1da165da57ccbc81bf51a05d4fe2901ab2b Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 20:12:52 +0800 Subject: [PATCH 23/24] add sddm entry for wayland --- session/CMakeLists.txt | 1 + session/lingmo-wayland-session.desktop | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100755 session/lingmo-wayland-session.desktop diff --git a/session/CMakeLists.txt b/session/CMakeLists.txt index 6d9e49f..538a907 100644 --- a/session/CMakeLists.txt +++ b/session/CMakeLists.txt @@ -51,5 +51,6 @@ target_link_libraries(${TARGET} install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES lingmo-xsession.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/) +install(FILES lingmo-wayland-session.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions/) add_subdirectory(wayland_wrapper) diff --git a/session/lingmo-wayland-session.desktop b/session/lingmo-wayland-session.desktop new file mode 100755 index 0000000..db0c954 --- /dev/null +++ b/session/lingmo-wayland-session.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Exec=lingmo-session --wayland +Name=Lingmo Desktop (Wayland Experimental) +Keywords=session +Comment=session From 4f2ee85cb74803f46ed03c3ad4f82821424de2b0 Mon Sep 17 00:00:00 2001 From: Elysia Date: Sat, 16 Nov 2024 20:39:21 +0800 Subject: [PATCH 24/24] Fix an error in daemon-helper --- session/application.cpp | 7 +++++++ session/daemon-helper.cpp | 5 +++-- settings-daemon/translations/ar_AA.ts | 2 +- settings-daemon/translations/be_BY.ts | 2 +- settings-daemon/translations/be_Latn.ts | 2 +- settings-daemon/translations/bg_BG.ts | 2 +- settings-daemon/translations/bs_BA.ts | 2 +- settings-daemon/translations/cs_CZ.ts | 2 +- settings-daemon/translations/da_DK.ts | 2 +- settings-daemon/translations/de_DE.ts | 2 +- settings-daemon/translations/en_US.ts | 2 +- settings-daemon/translations/eo_XX.ts | 2 +- settings-daemon/translations/es_ES.ts | 2 +- settings-daemon/translations/es_MX.ts | 2 +- settings-daemon/translations/fa_IR.ts | 2 +- settings-daemon/translations/fi_FI.ts | 2 +- settings-daemon/translations/fr_FR.ts | 2 +- settings-daemon/translations/he_IL.ts | 2 +- settings-daemon/translations/hi_IN.ts | 2 +- settings-daemon/translations/hu_HU.ts | 2 +- settings-daemon/translations/id_ID.ts | 2 +- settings-daemon/translations/ie.ts | 2 +- settings-daemon/translations/it_IT.ts | 2 +- settings-daemon/translations/ja_JP.ts | 2 +- settings-daemon/translations/lt_LT.ts | 2 +- settings-daemon/translations/lv_LV.ts | 2 +- settings-daemon/translations/mg.ts | 2 +- settings-daemon/translations/ml_IN.ts | 2 +- settings-daemon/translations/nb_NO.ts | 2 +- settings-daemon/translations/ne_NP.ts | 2 +- settings-daemon/translations/pl_PL.ts | 2 +- settings-daemon/translations/pt_BR.ts | 2 +- settings-daemon/translations/pt_PT.ts | 2 +- settings-daemon/translations/ro_RO.ts | 2 +- settings-daemon/translations/ru_RU.ts | 2 +- settings-daemon/translations/si_LK.ts | 2 +- settings-daemon/translations/sk_SK.ts | 2 +- settings-daemon/translations/so.ts | 2 +- settings-daemon/translations/sr_RS.ts | 2 +- settings-daemon/translations/sv_SE.ts | 2 +- settings-daemon/translations/sw.ts | 2 +- settings-daemon/translations/ta_IN.ts | 2 +- settings-daemon/translations/tr_TR.ts | 2 +- settings-daemon/translations/uk_UA.ts | 2 +- settings-daemon/translations/uz_UZ.ts | 2 +- settings-daemon/translations/vi_VN.ts | 2 +- settings-daemon/translations/zh_CN.ts | 2 +- settings-daemon/translations/zh_TW.ts | 2 +- 48 files changed, 56 insertions(+), 48 deletions(-) diff --git a/session/application.cpp b/session/application.cpp index 47b9f88..30e5fa6 100644 --- a/session/application.cpp +++ b/session/application.cpp @@ -106,6 +106,13 @@ Application::Application(int& argc, char** argv) m_wayland = parser.isSet(waylandOption); + if (m_wayland) { + qputenv("XDG_SESSION_TYPE", "wayland"); + qputenv("QT_QPA_PLATFORM", "wayland"); + } else { + qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); + } + createConfigDirectory(); initKWinConfig(); initLanguage(); diff --git a/session/daemon-helper.cpp b/session/daemon-helper.cpp index 35aa41a..3bf2e3f 100644 --- a/session/daemon-helper.cpp +++ b/session/daemon-helper.cpp @@ -49,8 +49,9 @@ void Daemon::startProcess(const QPair &processInfo) { auto xcb_extra = QProcessEnvironment(); xcb_extra.insert("QT_QPA_PLATFORM", "xcb"); - - process->setProcessEnvironment(xcb_extra); + auto sys_env = QProcessEnvironment::systemEnvironment(); + sys_env.insert(xcb_extra); + process->setProcessEnvironment(sys_env); process->start(processInfo.first, processInfo.second); if (process->waitForStarted()) { qDebug() << "Process started:" << processInfo.first << "PID:" << process->processId(); diff --git a/settings-daemon/translations/ar_AA.ts b/settings-daemon/translations/ar_AA.ts index ee1fa0e..5b29118 100644 --- a/settings-daemon/translations/ar_AA.ts +++ b/settings-daemon/translations/ar_AA.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/be_BY.ts b/settings-daemon/translations/be_BY.ts index 9870cac..396c3e6 100644 --- a/settings-daemon/translations/be_BY.ts +++ b/settings-daemon/translations/be_BY.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/be_Latn.ts b/settings-daemon/translations/be_Latn.ts index 1162e55..9f366de 100644 --- a/settings-daemon/translations/be_Latn.ts +++ b/settings-daemon/translations/be_Latn.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/bg_BG.ts b/settings-daemon/translations/bg_BG.ts index e53cbf4..10c7914 100644 --- a/settings-daemon/translations/bg_BG.ts +++ b/settings-daemon/translations/bg_BG.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/bs_BA.ts b/settings-daemon/translations/bs_BA.ts index 0cfb124..1b44410 100644 --- a/settings-daemon/translations/bs_BA.ts +++ b/settings-daemon/translations/bs_BA.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/cs_CZ.ts b/settings-daemon/translations/cs_CZ.ts index 63b0c8d..e21097d 100644 --- a/settings-daemon/translations/cs_CZ.ts +++ b/settings-daemon/translations/cs_CZ.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/da_DK.ts b/settings-daemon/translations/da_DK.ts index a08d009..7fa2a1c 100644 --- a/settings-daemon/translations/da_DK.ts +++ b/settings-daemon/translations/da_DK.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/de_DE.ts b/settings-daemon/translations/de_DE.ts index dc778b1..b4dffcf 100644 --- a/settings-daemon/translations/de_DE.ts +++ b/settings-daemon/translations/de_DE.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/en_US.ts b/settings-daemon/translations/en_US.ts index 5ceee02..d5463e7 100644 --- a/settings-daemon/translations/en_US.ts +++ b/settings-daemon/translations/en_US.ts @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/eo_XX.ts b/settings-daemon/translations/eo_XX.ts index e69fef0..7677db0 100644 --- a/settings-daemon/translations/eo_XX.ts +++ b/settings-daemon/translations/eo_XX.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/es_ES.ts b/settings-daemon/translations/es_ES.ts index 4326eec..cc59952 100644 --- a/settings-daemon/translations/es_ES.ts +++ b/settings-daemon/translations/es_ES.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/es_MX.ts b/settings-daemon/translations/es_MX.ts index d7581e7..80a9ecd 100644 --- a/settings-daemon/translations/es_MX.ts +++ b/settings-daemon/translations/es_MX.ts @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/fa_IR.ts b/settings-daemon/translations/fa_IR.ts index 28645ba..22bed9d 100644 --- a/settings-daemon/translations/fa_IR.ts +++ b/settings-daemon/translations/fa_IR.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/fi_FI.ts b/settings-daemon/translations/fi_FI.ts index 732ebf0..5e2c3c8 100644 --- a/settings-daemon/translations/fi_FI.ts +++ b/settings-daemon/translations/fi_FI.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/fr_FR.ts b/settings-daemon/translations/fr_FR.ts index 2179777..d2c5f80 100644 --- a/settings-daemon/translations/fr_FR.ts +++ b/settings-daemon/translations/fr_FR.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/he_IL.ts b/settings-daemon/translations/he_IL.ts index 2e86ae7..a31c772 100644 --- a/settings-daemon/translations/he_IL.ts +++ b/settings-daemon/translations/he_IL.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/hi_IN.ts b/settings-daemon/translations/hi_IN.ts index 190465e..6908094 100644 --- a/settings-daemon/translations/hi_IN.ts +++ b/settings-daemon/translations/hi_IN.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/hu_HU.ts b/settings-daemon/translations/hu_HU.ts index f0e5add..f2fda45 100644 --- a/settings-daemon/translations/hu_HU.ts +++ b/settings-daemon/translations/hu_HU.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/id_ID.ts b/settings-daemon/translations/id_ID.ts index 32d0674..03f026d 100644 --- a/settings-daemon/translations/id_ID.ts +++ b/settings-daemon/translations/id_ID.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ie.ts b/settings-daemon/translations/ie.ts index 9634283..603e121 100644 --- a/settings-daemon/translations/ie.ts +++ b/settings-daemon/translations/ie.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/it_IT.ts b/settings-daemon/translations/it_IT.ts index 3cbd9fc..80f31fa 100644 --- a/settings-daemon/translations/it_IT.ts +++ b/settings-daemon/translations/it_IT.ts @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ja_JP.ts b/settings-daemon/translations/ja_JP.ts index e37d32e..c5ac06b 100644 --- a/settings-daemon/translations/ja_JP.ts +++ b/settings-daemon/translations/ja_JP.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/lt_LT.ts b/settings-daemon/translations/lt_LT.ts index 606831c..e0884e4 100644 --- a/settings-daemon/translations/lt_LT.ts +++ b/settings-daemon/translations/lt_LT.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/lv_LV.ts b/settings-daemon/translations/lv_LV.ts index 2015c17..b25e7b3 100644 --- a/settings-daemon/translations/lv_LV.ts +++ b/settings-daemon/translations/lv_LV.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/mg.ts b/settings-daemon/translations/mg.ts index c8a95dd..5df72bd 100644 --- a/settings-daemon/translations/mg.ts +++ b/settings-daemon/translations/mg.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ml_IN.ts b/settings-daemon/translations/ml_IN.ts index 477b13c..9405bdd 100644 --- a/settings-daemon/translations/ml_IN.ts +++ b/settings-daemon/translations/ml_IN.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/nb_NO.ts b/settings-daemon/translations/nb_NO.ts index c7e0a80..133d860 100644 --- a/settings-daemon/translations/nb_NO.ts +++ b/settings-daemon/translations/nb_NO.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ne_NP.ts b/settings-daemon/translations/ne_NP.ts index 52b56da..fbc43a7 100644 --- a/settings-daemon/translations/ne_NP.ts +++ b/settings-daemon/translations/ne_NP.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/pl_PL.ts b/settings-daemon/translations/pl_PL.ts index 71872ab..1b25c9f 100644 --- a/settings-daemon/translations/pl_PL.ts +++ b/settings-daemon/translations/pl_PL.ts @@ -80,7 +80,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/pt_BR.ts b/settings-daemon/translations/pt_BR.ts index ebabe98..38bfae9 100644 --- a/settings-daemon/translations/pt_BR.ts +++ b/settings-daemon/translations/pt_BR.ts @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/pt_PT.ts b/settings-daemon/translations/pt_PT.ts index 5803e2e..741dbf0 100644 --- a/settings-daemon/translations/pt_PT.ts +++ b/settings-daemon/translations/pt_PT.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ro_RO.ts b/settings-daemon/translations/ro_RO.ts index de7089b..767e288 100644 --- a/settings-daemon/translations/ro_RO.ts +++ b/settings-daemon/translations/ro_RO.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ru_RU.ts b/settings-daemon/translations/ru_RU.ts index e8d40ed..94f5cef 100644 --- a/settings-daemon/translations/ru_RU.ts +++ b/settings-daemon/translations/ru_RU.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/si_LK.ts b/settings-daemon/translations/si_LK.ts index 5fa9bb0..6684231 100644 --- a/settings-daemon/translations/si_LK.ts +++ b/settings-daemon/translations/si_LK.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/sk_SK.ts b/settings-daemon/translations/sk_SK.ts index c00eda2..3d085ae 100644 --- a/settings-daemon/translations/sk_SK.ts +++ b/settings-daemon/translations/sk_SK.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/so.ts b/settings-daemon/translations/so.ts index 90260d8..1c44ed9 100644 --- a/settings-daemon/translations/so.ts +++ b/settings-daemon/translations/so.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/sr_RS.ts b/settings-daemon/translations/sr_RS.ts index da992ba..d71594c 100644 --- a/settings-daemon/translations/sr_RS.ts +++ b/settings-daemon/translations/sr_RS.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/sv_SE.ts b/settings-daemon/translations/sv_SE.ts index a488498..b3bae44 100644 --- a/settings-daemon/translations/sv_SE.ts +++ b/settings-daemon/translations/sv_SE.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/sw.ts b/settings-daemon/translations/sw.ts index 732caed..891fad5 100644 --- a/settings-daemon/translations/sw.ts +++ b/settings-daemon/translations/sw.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/ta_IN.ts b/settings-daemon/translations/ta_IN.ts index fb54c85..d1aee0b 100644 --- a/settings-daemon/translations/ta_IN.ts +++ b/settings-daemon/translations/ta_IN.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/tr_TR.ts b/settings-daemon/translations/tr_TR.ts index 35153d7..01d5cfe 100644 --- a/settings-daemon/translations/tr_TR.ts +++ b/settings-daemon/translations/tr_TR.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/uk_UA.ts b/settings-daemon/translations/uk_UA.ts index d9e1d47..f4a0891 100644 --- a/settings-daemon/translations/uk_UA.ts +++ b/settings-daemon/translations/uk_UA.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/uz_UZ.ts b/settings-daemon/translations/uz_UZ.ts index ee29c98..18d078a 100644 --- a/settings-daemon/translations/uz_UZ.ts +++ b/settings-daemon/translations/uz_UZ.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/vi_VN.ts b/settings-daemon/translations/vi_VN.ts index 0f0f591..28e3591 100644 --- a/settings-daemon/translations/vi_VN.ts +++ b/settings-daemon/translations/vi_VN.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect diff --git a/settings-daemon/translations/zh_CN.ts b/settings-daemon/translations/zh_CN.ts index 99d9f42..bb831bd 100644 --- a/settings-daemon/translations/zh_CN.ts +++ b/settings-daemon/translations/zh_CN.ts @@ -55,7 +55,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect 屏幕缩放需要重新登录才能生效 diff --git a/settings-daemon/translations/zh_TW.ts b/settings-daemon/translations/zh_TW.ts index 291c803..c422d36 100644 --- a/settings-daemon/translations/zh_TW.ts +++ b/settings-daemon/translations/zh_TW.ts @@ -79,7 +79,7 @@ ThemeManager - + Screen scaling needs to be re-login to take effect