From 23b7cd2f55d3831548af7827a70344c5dd7e0980 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 23 Jan 2026 16:54:57 +0800 Subject: [PATCH] refactor: move XSettings operations to dedicated thread Previously SettingManager was moved to a separate QThread in Helper, but QThread lifecycle management was error-prone. This commit moves thread creation and management into SettingManager itself, using QMetaObject::invokeMethod for all XSettings/XResource operations to ensure thread safety. Log: Tasks: Influence: XSettings operations now run on a dedicated thread managed by SettingManager, avoiding potential crashes from improper QThread destruction. --- src/seat/helper.cpp | 39 +-- src/seat/helper.h | 1 - src/xsettings/settingmanager.cpp | 403 ++++++++++++++++++++++++++++--- src/xsettings/settingmanager.h | 58 ++++- 4 files changed, 443 insertions(+), 58 deletions(-) diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index 8915b45b2..6eb82c797 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -187,11 +187,6 @@ Session::~Session() qCDebug(treelandCore) << "Deleting session for uid:" << uid << socket; Q_EMIT aboutToBeDestroyed(); - if (settingManagerThread) { - settingManagerThread->quit(); - settingManagerThread->wait(QDeadlineTimer(25000)); - } - if (settingManager) { delete settingManager; settingManager = nullptr; @@ -2325,29 +2320,17 @@ std::shared_ptr Helper::ensureSession(int id, QString username) qCWarning(treelandInput) << "Failed to intern atom:" << _DEEPIN_NO_TITLEBAR; } session->settingManager = new SettingManager(session->xwayland->xcbConnection()); - // TODO: proper destruction of QThread. relying on the QObject tree is crashy. - // We are moving session management out of Helper anyways, will fix later. - session->settingManagerThread = new QThread(session->xwayland); - - session->settingManager->moveToThread(session->settingManagerThread); - connect(session->settingManagerThread, &QThread::started, this, [this, session]{ - const qreal scale = m_rootSurfaceContainer->window()->effectiveDevicePixelRatio(); - QMetaObject::invokeMethod(session->settingManager, [session, scale]() { - session->settingManager->setGlobalScale(scale); - session->settingManager->apply(); - }, Qt::QueuedConnection); - - QObject::connect(Helper::instance()->window(), - &WOutputRenderWindow::effectiveDevicePixelRatioChanged, - session->settingManager, - [session](qreal dpr) { - session->settingManager->setGlobalScale(dpr); - session->settingManager->apply(); - }, Qt::QueuedConnection); - }); - - connect(session->settingManagerThread, &QThread::finished, session->settingManagerThread, &QThread::deleteLater); - session->settingManagerThread->start(); + const qreal scale = m_rootSurfaceContainer->window()->effectiveDevicePixelRatio(); + session->settingManager->setGlobalScale(scale); + session->settingManager->apply(); + + QObject::connect(Helper::instance()->window(), + &WOutputRenderWindow::effectiveDevicePixelRatioChanged, + session->settingManager, + [session](qreal dpr) { + session->settingManager->setGlobalScale(dpr); + session->settingManager->apply(); + }, Qt::QueuedConnection); } }); return xwayland; diff --git a/src/seat/helper.h b/src/seat/helper.h index 87faf5f08..ea5ea5bb0 100644 --- a/src/seat/helper.h +++ b/src/seat/helper.h @@ -134,7 +134,6 @@ struct Session : QObject { WXWayland *xwayland = nullptr; quint32 noTitlebarAtom = XCB_ATOM_NONE; SettingManager *settingManager = nullptr; - QThread *settingManagerThread = nullptr; ~Session(); diff --git a/src/xsettings/settingmanager.cpp b/src/xsettings/settingmanager.cpp index 91040fa9d..0fa9ee560 100644 --- a/src/xsettings/settingmanager.cpp +++ b/src/xsettings/settingmanager.cpp @@ -2,108 +2,455 @@ // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "settingmanager.h" -#include "common/treelandlogging.h" + +#include const static qreal BASE_DPI = 96; const static qreal XSETTINGS_BASE_DPI_FIXED = BASE_DPI * 1024; -SettingManager::SettingManager(xcb_connection_t *connection, QObject *parent) +SettingManager::SettingManager(xcb_connection_t *connection, QObject *parent, bool async) : QObject(parent) + , m_async(async) , m_resource(new XResource(connection, this)) , m_settings(new XSettings(connection, this)) { + if (m_async) { + m_thread = new QThread; + this->moveToThread(m_thread); + m_thread->start(); + } } SettingManager::~SettingManager() { + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + } +} + +void SettingManager::setGTKThemeAsync(const QString &themeName) +{ + QMetaObject::invokeMethod(this, + "onSetGTKTheme", + Qt::QueuedConnection, + Q_ARG(QString, themeName)); +} + +QString SettingManager::GTKThemeAsync() const +{ + QString result; + QMetaObject::invokeMethod( + const_cast(this), + "onGetGTKTheme", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result) + ); + + return result; +} + +void SettingManager::setFontAsync(const QString &name) +{ + QMetaObject::invokeMethod(this, + "onSetFont", + Qt::QueuedConnection, + Q_ARG(QString, name)); +} + +QString SettingManager::fontAsync() const +{ + QString result; + QMetaObject::invokeMethod( + const_cast(this), + "onGetFont", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result) + ); + + return result; +} + +void SettingManager::setIconThemeAsync(const QString &theme) +{ + QMetaObject::invokeMethod(this, + "onSetIconTheme", + Qt::QueuedConnection, + Q_ARG(QString, theme)); +} + +QString SettingManager::iconThemeAsync() const +{ + QString result; + QMetaObject::invokeMethod( + const_cast(this), + "onGetIconTheme", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result) + ); + + return result; +} + +void SettingManager::setSoundThemeAsync(const QString &theme) +{ + QMetaObject::invokeMethod(this, + "onSetSoundTheme", + Qt::QueuedConnection, + Q_ARG(QString, theme)); +} + +QString SettingManager::soundThemeAsync() const +{ + QString result; + QMetaObject::invokeMethod( + const_cast(this), + "onGetSoundTheme", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result) + ); + + return result; +} + +void SettingManager::setCursorThemeAsync(const QString &theme) +{ + QMetaObject::invokeMethod(this, + "onSetCursorTheme", + Qt::QueuedConnection, + Q_ARG(QString, theme)); +} + +QString SettingManager::cursorThemeAsync() const +{ + QString result; + QMetaObject::invokeMethod( + const_cast(this), + "onGetCursorTheme", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result) + ); + + return result; +} + +void SettingManager::setCursorSizeAsync(qreal value) +{ + QMetaObject::invokeMethod(this, + "onSetCursorSize", + Qt::QueuedConnection, + Q_ARG(qreal, value)); +} + +qreal SettingManager::cursorSizeAsync() const +{ + qreal result = 0.0; + QMetaObject::invokeMethod( + const_cast(this), + "onGetcursorSize", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(qreal, result) + ); + + return result; +} + +void SettingManager::setDoubleClickIntervalAsync(int interval) +{ + QMetaObject::invokeMethod(this, + "onSetDoubleClickInterval", + Qt::QueuedConnection, + Q_ARG(int, interval)); +} + +void SettingManager::setGlobalScaleAsync(qreal scale) +{ + QMetaObject::invokeMethod(this, + "onSetGlobalScale", + Qt::QueuedConnection, + Q_ARG(int, scale)); +} + +qreal SettingManager::globalScaleAsync() const +{ + qreal result = 0.0; + QMetaObject::invokeMethod( + const_cast(this), + "onGlobalScale", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(qreal, result) + ); + + return result; +} + +void SettingManager::applyAsync() +{ + QMetaObject::invokeMethod(this, + "onApply", + Qt::QueuedConnection); +} + +void SettingManager::onSetGTKTheme(const QString &themeName) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_ThemeName), + themeName); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_ThemeName), + themeName); +} + +QString SettingManager::onGetGTKTheme() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_ThemeName)).toString(); +} + +void SettingManager::onSetFont(const QString &name) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_FontName), + name); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_FontName), + name); +} + +QString SettingManager::onGetFont() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_FontName)).toString(); +} + +void SettingManager::onSetIconTheme(const QString &theme) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_IconThemeName), + theme); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_IconThemeName), + theme); +} + +QString SettingManager::onGetIconTheme() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_IconThemeName)).toString(); +} + +void SettingManager::onSetSoundTheme(const QString &theme) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Net_SoundThemeName), + theme); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Net_SoundThemeName), + theme); +} + +QString SettingManager::onGetSoundTheme() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Net_SoundThemeName)).toString(); +} + +void SettingManager::onSetCursorTheme(const QString &theme) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_CursorThemeName), + theme); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_CursorThemeName), + theme); +} + +QString SettingManager::onGetCursorTheme() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_CursorThemeName)).toString(); +} + +void SettingManager::onSetCursorSize(qreal value) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Xcursor_Size), + value); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Xcursor_Size), + value); +} + +qreal SettingManager::onGetcursorSize() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Xcursor_Size)).toReal(); +} + +void SettingManager::onSetDoubleClickInterval(int interval) +{ + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Net_DoubleClickTime), + interval); +} + +void SettingManager::onSetGlobalScale(qreal scale) +{ + m_resource->setPropertyValue(XResource::toByteArray(XResource::Xft_DPI), + scale * BASE_DPI); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gdk_WindowScalingFactor), + qFloor(scale)); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gdk_UnscaledDPI), + scale * XSETTINGS_BASE_DPI_FIXED); + m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Xft_DPI), + scale * XSETTINGS_BASE_DPI_FIXED); +} + +qreal SettingManager::onGetGlobalScale() const +{ + return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gdk_UnscaledDPI)).toReal() / XSETTINGS_BASE_DPI_FIXED; +} + +void SettingManager::onApply() +{ + m_resource->apply(); + m_settings->apply(); } void SettingManager::setGTKTheme(const QString &themeName) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_ThemeName), themeName); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_ThemeName), themeName); + if (async()) { + setGTKThemeAsync(themeName); + } + + onSetGTKTheme(themeName); } QString SettingManager::GTKTheme() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_ThemeName)).toString(); + if (async()) { + return GTKThemeAsync(); + } + + return onGetGTKTheme(); } void SettingManager::setFont(const QString &name) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_FontName), name); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_FontName), name); + if (async()) { + setFontAsync(name); + } + + onSetFont(name); } QString SettingManager::font() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_FontName)).toString(); + if (async()) { + return fontAsync(); + } + + return onGetFont(); } void SettingManager::setIconTheme(const QString &theme) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_IconThemeName), theme); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_IconThemeName), theme); + if (async()) { + setIconThemeAsync(theme); + return; + } + + onSetIconTheme(theme); } QString SettingManager::iconTheme() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_IconThemeName)).toString(); + if (async()) { + return iconThemeAsync(); + } + + return onGetIconTheme(); } void SettingManager::setSoundTheme(const QString &theme) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Net_SoundThemeName), theme); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Net_SoundThemeName), theme); + if (async()) { + setSoundThemeAsync(theme); + return; + } + + onSetSoundTheme(theme); } QString SettingManager::soundTheme() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Net_SoundThemeName)).toString(); + if (async()) { + return soundThemeAsync(); + } + + return onGetSoundTheme(); } void SettingManager::setCursorTheme(const QString &theme) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Gtk_CursorThemeName), theme); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gtk_CursorThemeName), theme); + if (async()) { + setCursorThemeAsync(theme); + return; + } + + onSetCursorTheme(theme); } QString SettingManager::cursorTheme() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gtk_CursorThemeName)).toString(); + if (async()) { + return cursorThemeAsync(); + } + + return onGetCursorTheme(); } void SettingManager::setCursorSize(qreal value) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Xcursor_Size), value); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Xcursor_Size), value); + if (async()) { + setCursorSizeAsync(value); + return; + } + + onSetCursorSize(value); } qreal SettingManager::cursorSize() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Xcursor_Size)).toReal(); + if (async()) { + return cursorSizeAsync(); + } + + return onGetcursorSize(); } void SettingManager::setDoubleClickInterval(int interval) { - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Net_DoubleClickTime), interval); + if (async()) { + setDoubleClickIntervalAsync(interval); + return; + } + + onSetDoubleClickInterval(interval); } void SettingManager::setGlobalScale(qreal scale) { - m_resource->setPropertyValue(XResource::toByteArray(XResource::Xft_DPI), scale * BASE_DPI); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gdk_WindowScalingFactor), qFloor(scale)); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Gdk_UnscaledDPI), scale * XSETTINGS_BASE_DPI_FIXED); - m_settings->setPropertyValue(XSettings::toByteArray(XSettings::Xft_DPI), scale * XSETTINGS_BASE_DPI_FIXED); + if (async()) { + setGlobalScaleAsync(scale); + return; + } + + onSetGlobalScale(scale); } qreal SettingManager::globalScale() const { - return m_settings->getPropertyValue(XSettings::toByteArray(XSettings::Gdk_UnscaledDPI)).toReal() / XSETTINGS_BASE_DPI_FIXED; + if (async()) { + return globalScaleAsync(); + } + + return onGetGlobalScale(); } void SettingManager::apply() { - m_resource->apply(); - m_settings->apply(); + if (async()) { + applyAsync(); + return; + } + + onApply(); +} + +bool SettingManager::async() const +{ + return m_async; } diff --git a/src/xsettings/settingmanager.h b/src/xsettings/settingmanager.h index 6e74248b4..ed4198b42 100644 --- a/src/xsettings/settingmanager.h +++ b/src/xsettings/settingmanager.h @@ -7,12 +7,13 @@ #include "xsettings.h" #include +#include class SettingManager : public QObject { Q_OBJECT public: - explicit SettingManager(xcb_connection_t *connection, QObject *parent = nullptr); + explicit SettingManager(xcb_connection_t *connection, QObject *parent = nullptr, bool async = true); ~SettingManager() override; void setGTKTheme(const QString &themeName); @@ -40,7 +41,62 @@ class SettingManager : public QObject void apply(); + bool async() const; private: + void setGTKThemeAsync(const QString &themeName); + QString GTKThemeAsync() const; + + void setFontAsync(const QString &name); + QString fontAsync() const; + + void setIconThemeAsync(const QString &theme); + QString iconThemeAsync() const; + + void setSoundThemeAsync(const QString &theme); + QString soundThemeAsync() const; + + void setCursorThemeAsync(const QString &theme); + QString cursorThemeAsync() const; + + void setCursorSizeAsync(qreal value); + qreal cursorSizeAsync() const; + + void setDoubleClickIntervalAsync(int interval); + + void setGlobalScaleAsync(qreal scale); + qreal globalScaleAsync() const; + + void applyAsync(); + +private Q_SLOTS: + void onSetGTKTheme(const QString &themeName); + QString onGetGTKTheme() const; + + void onSetFont(const QString &name); + QString onGetFont() const; + + void onSetIconTheme(const QString &theme); + QString onGetIconTheme() const; + + void onSetSoundTheme(const QString &theme); + QString onGetSoundTheme() const; + + void onSetCursorTheme(const QString &theme); + QString onGetCursorTheme() const; + + void onSetCursorSize(qreal value); + qreal onGetcursorSize() const; + + void onSetDoubleClickInterval(int interval); + + void onSetGlobalScale(qreal scale); + qreal onGetGlobalScale() const; + + void onApply(); + +private: + bool m_async; + QThread *m_thread = nullptr; XResource *m_resource = nullptr; XSettings *m_settings = nullptr; };