Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ qt_add_qml_module(libtreeland
seat/helper.h
seat/seatsmanager.cpp
seat/seatsmanager.h
seat/systemdconfigmanager.cpp
seat/systemdconfigmanager.h
session/session.cpp
session/session.h
surface/surfacecontainer.cpp
Expand Down
64 changes: 20 additions & 44 deletions src/seat/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

#include "helper.h"
#include "seatsmanager.h"
#include "systemdconfigmanager.h"
#include <QScopeGuard>

Check warning on line 7 in src/seat/helper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QScopeGuard> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFile>

Check warning on line 8 in src/seat/helper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFile> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QRandomGenerator>

Check warning on line 9 in src/seat/helper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QRandomGenerator> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
Expand Down Expand Up @@ -276,12 +277,10 @@
Q_ASSERT(!m_instance);
m_instance = this;

Q_ASSERT(!m_config);
m_config.reset(TreelandUserConfig::createByName("org.deepin.dde.treeland.user",
"org.deepin.dde.treeland",
"/dde")); // will update user path in Helper::init
m_globalConfig.reset(TreelandConfig::create("org.deepin.dde.treeland",
QString()));
m_systemConfigManager = new SystemDConfigManager(this);
// Block on the global config after the pointer is stored, so any nested-loop
// callback reaching Helper::config()/globalConfig() sees a valid pointer.
m_systemConfigManager->initialize();

m_renderWindow->setColor(Qt::black);
m_rootSurfaceContainer->setFlag(QQuickItem::ItemIsFocusScope, true);
Expand Down Expand Up @@ -404,12 +403,12 @@

TreelandUserConfig *Helper::config()
{
return m_config.get();
return m_systemConfigManager->userConfig();
}

TreelandConfig *Helper::globalConfig()
{
return m_globalConfig.get();
return m_systemConfigManager->globalConfig();
}

void Helper::syncPaletteTypeWithWindowThemeType(int32_t themeType)
Expand Down Expand Up @@ -442,7 +441,7 @@
{
if (m_treelandRemoteSource)
return;
if (m_globalConfig->debugSource()) {
if (m_systemConfigManager->globalConfig()->debugSource()) {
m_treelandRemoteSource = new TreelandRemoteSource(this);
}
}
Expand Down Expand Up @@ -574,7 +573,7 @@

auto *config = outputObject->config();
const QString outputId = WallpaperManager::getOutputId(outputObject);
const QString primaryOutputId = m_globalConfig->primaryOutputId();
const QString primaryOutputId = m_systemConfigManager->globalConfig()->primaryOutputId();
if (config->enabled() && primaryOutputId == outputId) {
m_rootSurfaceContainer->setPrimaryOutput(outputObject);
} else if (config->enabled()
Expand Down Expand Up @@ -670,7 +669,7 @@
[this,
restoreOutputConfig = std::move(restoreOutputConfig),
outputObject = QPointer<Output>(o)]() mutable {
runWhenTreelandConfigInitialized(m_globalConfig.get(),
runWhenTreelandConfigInitialized(m_systemConfigManager->globalConfig(),
outputObject,
std::move(restoreOutputConfig));
});
Expand Down Expand Up @@ -1559,17 +1558,15 @@
m_personalizationInterfaceV1 = m_server->attach<PersonalizationManagerInterfaceV1>();

auto updateCurrentUser = [this] {
m_config.reset(TreelandUserConfig::createByName("org.deepin.dde.treeland.user",
"org.deepin.dde.treeland",
"/" + m_userModel->currentUserName()));
m_systemConfigManager->resetUserConfig(m_userModel->currentUserName());
// Notify QML that the config pointer has changed so bindings (e.g. sourceSize
// on WQuickCursor) reconnect their notifiers to the new TreelandUserConfig object.
Q_EMIT configChanged();
connect(m_config.get(),
connect(m_systemConfigManager->userConfig(),
&TreelandUserConfig::cursorThemeNameChanged,
m_sessionManager,
&SessionManager::syncActiveSessionCursorSettings);
connect(m_config.get(),
connect(m_systemConfigManager->userConfig(),
&TreelandUserConfig::cursorSizeChanged,
m_sessionManager,
&SessionManager::syncActiveSessionCursorSettings);
Expand All @@ -1581,25 +1578,12 @@
}

m_inputManager->setupSeatUserConfig(m_userModel->currentUserName());
auto onConfigInitialized = [this] {
m_sessionManager->syncActiveSessionCursorSettings();
syncPaletteTypeWithWindowThemeType(m_config->windowThemeType());
m_wallpaperManager->updateWallpaperConfig();
tryInitRemoteSource();
};
// TODO(YaoBing Xiao): pre-initialize dconfig, remove isInitializeSucceeded
#if TREELANDCONFIG_DCONFIG_FILE_VERSION_MINOR > 0
if (m_config->isInitializeSucceeded()) {
#else
if (m_config->isInitializeSucceed()) {
#endif
onConfigInitialized();
} else {
connect(m_config.get(),
&TreelandUserConfig::configInitializeSucceed,
this,
onConfigInitialized);
}
// resetUserConfig() blocks until the user config is ready, so the
// follow-up sync can run directly without a deferred signal connection.
m_sessionManager->syncActiveSessionCursorSettings();
syncPaletteTypeWithWindowThemeType(m_systemConfigManager->userConfig()->windowThemeType());
m_wallpaperManager->updateWallpaperConfig();
tryInitRemoteSource();
};
connect(m_userModel, &UserModel::currentUserNameChanged, this, updateCurrentUser);

Expand Down Expand Up @@ -1900,18 +1884,10 @@

m_keyboardStateNotifyManagerInterfaceV1 = m_server->attach<TreelandKeyboardStateNotifyManagerInterfaceV1>();

#if TREELANDCONFIG_DCONFIG_FILE_VERSION_MINOR > 0
if (m_globalConfig->isInitializeSucceeded()) {
#else
if (m_globalConfig->isInitializeSucceed()) {
#endif
} else {
}

m_backend->handle()->start();

// If the stored primary output is no longer present, select a random one as primary
const QString primaryOutputId = m_globalConfig->primaryOutputId();
const QString primaryOutputId = m_systemConfigManager->globalConfig()->primaryOutputId();
if (!currentPrimaryMatchesId(m_rootSurfaceContainer, primaryOutputId)) {
const auto &outputs = m_rootSurfaceContainer->outputs();
if (!outputs.isEmpty()) {
Expand Down
4 changes: 2 additions & 2 deletions src/seat/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ShortcutManagerV2;
class ShortcutRunner;
class SurfaceContainer;
class SurfaceWrapper;
class SystemDConfigManager;
class TreelandConfig;
class TreelandUserConfig;
class TreelandRemoteSource;
Expand Down Expand Up @@ -363,8 +364,7 @@ private Q_SLOTS:
WSeat *m_currentEventSeat = nullptr;

static Helper *m_instance;
std::unique_ptr<TreelandUserConfig> m_config;
std::unique_ptr<TreelandConfig> m_globalConfig;
SystemDConfigManager *m_systemConfigManager = nullptr;
Treeland::Treeland *m_treeland = nullptr;
FpsDisplayManager *m_fpsManager = nullptr;
SessionManager *m_sessionManager = nullptr;
Expand Down
154 changes: 154 additions & 0 deletions src/seat/systemdconfigmanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright (C) 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "systemdconfigmanager.h"

#include "common/treelandlogging.h"

Check warning on line 6 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "common/treelandlogging.h" not found.
#include "treelandconfig.hpp"

Check warning on line 7 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "treelandconfig.hpp" not found.
#include "treelanduserconfig.hpp"

Check warning on line 8 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "treelanduserconfig.hpp" not found.

#include <QEventLoop>

Check warning on line 10 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QEventLoop> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTimer>

Check warning on line 11 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace {
// The generated config classes renamed isInitializeSucceed() to
// isInitializeSucceeded() once the dconfig file minor version became > 0, so
// guard the call site the same way the rest of the codebase does.
bool globalConfigReady(TreelandConfig *config)
{
#if TREELANDCONFIG_DCONFIG_FILE_VERSION_MINOR > 0
return config->isInitializeSucceeded();
#else
return config->isInitializeSucceed();
#endif
}

bool userConfigReady(TreelandUserConfig *config)
{
#if TREELANDCONFIG_DCONFIG_FILE_VERSION_MINOR > 0
return config->isInitializeSucceeded();
#else
return config->isInitializeSucceed();
#endif
}
} // namespace

SystemDConfigManager::SystemDConfigManager(QObject *parent)
: QObject(parent)
{
m_globalConfig.reset(TreelandConfig::create("org.deepin.dde.treeland", QString()));
// Stand-in config used before a real session path is known; the real path
// is applied in resetUserConfig() and only that one is awaited.
m_userConfig.reset(TreelandUserConfig::createByName("org.deepin.dde.treeland.user",
"org.deepin.dde.treeland",
"/dde"));
// The blocking wait lives in initialize() so Helper can store the pointer
// first; see initialize() and the class doc on isInitializeSucceeded().
}

SystemDConfigManager::~SystemDConfigManager() = default;

void SystemDConfigManager::initialize()
{
m_globalConfigInitialized = waitForGlobalConfigInitialized();
checkInitialized();
}

bool SystemDConfigManager::isInitializeSucceeded() const

Check warning on line 57 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'isInitializeSucceeded' is never used.
{
return m_globalConfigInitialized && m_userConfigInitialized;
}

TreelandConfig *SystemDConfigManager::globalConfig() const

Check warning on line 62 in src/seat/systemdconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'globalConfig' is never used.
{
return m_globalConfig.get();
}

TreelandUserConfig *SystemDConfigManager::userConfig() const
{
return m_userConfig.get();
}

void SystemDConfigManager::resetUserConfig(const QString &userName)
{
m_userConfig.reset(TreelandUserConfig::createByName("org.deepin.dde.treeland.user",
"org.deepin.dde.treeland",
"/" + userName));
m_userConfigInitialized = false;

// The "dde" path is only a placeholder for the greeter, so do not block on
// it; only a real session config is awaited.
if (userName != "dde") {
m_userConfigInitialized = waitForUserConfigInitialized();
}

checkInitialized();
}

bool SystemDConfigManager::waitForGlobalConfigInitialized()
{
// Already loaded successfully or already reported failure: either way the
// wait is done, and only success counts as initialized.
if (globalConfigReady(m_globalConfig.get())) {
return true;
}
if (m_globalConfig->isInitializeFailed()) {
return false;
}

bool success = false;
QEventLoop loop;
connect(m_globalConfig.get(), &TreelandConfig::configInitializeSucceed, &loop, [&success, &loop] {
success = true;
loop.quit();
});
connect(m_globalConfig.get(), &TreelandConfig::configInitializeFailed, &loop, [&success, &loop] {
success = false;
loop.quit();
});
QTimer::singleShot(5000, &loop, [&success, &loop] {
qCWarning(lcTlConfig) << "Global dconfig initialization timed out after 5s, using defaults";
success = false;
loop.quit();
});
loop.exec();
return success;
}

bool SystemDConfigManager::waitForUserConfigInitialized()
{
// Mirror the global path: succeed, fail, or timeout all terminate the
// wait instead of forcing a full 5s timeout on failure.
if (userConfigReady(m_userConfig.get())) {
return true;
}
if (m_userConfig->isInitializeFailed()) {
return false;
}

bool success = false;
QEventLoop loop;
connect(m_userConfig.get(), &TreelandUserConfig::configInitializeSucceed, &loop, [&success, &loop] {
success = true;
loop.quit();
});
connect(m_userConfig.get(), &TreelandUserConfig::configInitializeFailed, &loop, [&success, &loop] {
success = false;
loop.quit();
});
QTimer::singleShot(5000, &loop, [&success, &loop] {
qCWarning(lcTlConfig) << "User dconfig initialization timed out after 5s, using defaults";
success = false;
loop.quit();
});
loop.exec();
return success;
}

void SystemDConfigManager::checkInitialized()
{
if (m_globalConfigInitialized && m_userConfigInitialized && !m_initializeSucceedEmitted) {
m_initializeSucceedEmitted = true;
Q_EMIT initializeSucceed();
}
}
57 changes: 57 additions & 0 deletions src/seat/systemdconfigmanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (C) 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#pragma once

#include <QObject>
#include <QString>
#include <memory>

class TreelandConfig;
class TreelandUserConfig;

class SystemDConfigManager : public QObject
{
Q_OBJECT
public:
explicit SystemDConfigManager(QObject *parent = nullptr);
~SystemDConfigManager() override;

// Block until the global config finishes initializing (success, failure,
// or timeout). Call once after construction, before the global config is
// consumed. Splitting the wait out of the constructor lets Helper assign the
// manager pointer first, so any nested-loop callback reaching
// Helper::config()/globalConfig() sees a valid pointer instead of null.
void initialize();

// True only when both configs finished initializing *successfully*. A
// failure or timeout is intentionally not counted as success, so the
// initializeSucceed signal is not emitted for degraded configs. In the
// greeter ("dde" placeholder) scenario the user config is deliberately not
// awaited, so this stays false by design and the signal does not fire.
bool isInitializeSucceeded() const;

TreelandConfig *globalConfig() const;
TreelandUserConfig *userConfig() const;

// Rebuild the user-scoped config for the given user and block until it
// finishes initializing (real user only; the "dde" placeholder is not
// awaited). See isInitializeSucceeded() for the greeter contract.
void resetUserConfig(const QString &userName);

Q_SIGNALS:
void initializeSucceed();

private:
// Returns true when the config initialized successfully, false on failure
// or timeout (defaults are then in effect, which is still safe to read).
bool waitForGlobalConfigInitialized();
bool waitForUserConfigInitialized();
void checkInitialized();

std::unique_ptr<TreelandConfig> m_globalConfig;
std::unique_ptr<TreelandUserConfig> m_userConfig;
bool m_globalConfigInitialized = false;
bool m_userConfigInitialized = false;
bool m_initializeSucceedEmitted = false;
};
Loading
Loading