diff --git a/misc/dconfig/org.deepin.dde.treeland.user.json b/misc/dconfig/org.deepin.dde.treeland.user.json index c28c7083e..2dc0a7254 100644 --- a/misc/dconfig/org.deepin.dde.treeland.user.json +++ b/misc/dconfig/org.deepin.dde.treeland.user.json @@ -442,6 +442,17 @@ "description[zh_CN]": "多任务视图加载因子", "permissions": "readonly", "visibility": "private" + }, + "wallpaperConfig": { + "value": "", + "serial": 0, + "flags": ["global"], + "name": "Wallpaper Config", + "name[zh_CN]": "壁纸配置", + "description": "A configuration to manage wallpapers used by the system, including desktop and lockscreen wallpapers for each screen and workspace.", + "description[zh_CN]": "用于管理系统当前使用的壁纸配置,包括每个屏幕和工作区的桌面壁纸和锁屏壁纸。", + "permissions": "readwrite", + "visibility": "public" } } } diff --git a/misc/systemd/treeland.service.in b/misc/systemd/treeland.service.in index 597cec1f2..ef54ab320 100644 --- a/misc/systemd/treeland.service.in +++ b/misc/systemd/treeland.service.in @@ -17,6 +17,7 @@ Environment=DSG_APP_ID=org.deepin.dde.treeland Environment=ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer # For Debug: if enable asan, you can't get it's log from journalctl, so ensure the log to a file Environment=ASAN_OPTIONS=log_path=/tmp/treeland-asan:detect_leaks=0:abort_on_error=1:symbolize=1 +Environment=DDM_DISPLAY_MANAGER=1 ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/treeland.sh --lockscreen Restart=on-failure RestartSec=1s diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d1ea86d7..845389dea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,12 +186,16 @@ qt_add_qml_module(libtreeland utils/loginddbustypes.cpp utils/fpsdisplaymanager.cpp utils/fpsdisplaymanager.h - wallpaper/wallpapercontroller.cpp - wallpaper/wallpapercontroller.h - wallpaper/wallpaperimage.cpp - wallpaper/wallpaperimage.h + wallpaper/wallpapersurface.h + wallpaper/wallpapersurface.cpp + wallpaper/wallpaperitem.cpp + wallpaper/wallpaperitem.h wallpaper/wallpapermanager.cpp wallpaper/wallpapermanager.h + wallpaper/wallpaperconfig.h + wallpaper/wallpaperconfig.cpp + wallpaper/wallpaperlauncher.h + wallpaper/wallpaperlauncher.cpp workspace/workspace.cpp workspace/workspace.h workspace/workspaceanimationcontroller.cpp diff --git a/src/core/qml/Effects/LaunchpadCover.qml b/src/core/qml/Effects/LaunchpadCover.qml index b50dcba1d..d7cc7fe34 100644 --- a/src/core/qml/Effects/LaunchpadCover.qml +++ b/src/core/qml/Effects/LaunchpadCover.qml @@ -18,16 +18,15 @@ Item { z: (wrapper.z ?? 0) - 1 anchors.fill: wrapper - WallpaperController { - id: wallpaperController + Wallpaper { + id: wallpaper output: root.output - lock: true - type: mapped ? WallpaperController.Scale : WallpaperController.Normal + workspace: Helper.workspace.current } ShaderEffectSource { - id: wallpaper - sourceItem: wallpaperController.proxy + id: wallpaperSource + sourceItem: wallpaper recursive: true live: true smooth: true @@ -38,14 +37,14 @@ Item { State { name: "Normal" PropertyChanges { - target: wallpaper + target: wallpaperSource scale: 1 } }, State { name: "Scale" PropertyChanges { - target: wallpaper + target: wallpaperSource scale: 1.4 } } @@ -75,7 +74,7 @@ Item { Blur { anchors.fill: parent - z: wallpaper.z + 1 + z: wallpaperSource.z + 1 } Rectangle { @@ -92,4 +91,8 @@ Item { easing.type: Easing.OutExpo } } + + onMappedChanged: { + Helper.setLaunchpadMapped(root.output, root.mapped) + } } diff --git a/src/core/qml/PrimaryOutput.qml b/src/core/qml/PrimaryOutput.qml index 1b89a7a7c..3839b9200 100644 --- a/src/core/qml/PrimaryOutput.qml +++ b/src/core/qml/PrimaryOutput.qml @@ -9,7 +9,6 @@ import Treeland OutputItem { id: rootOutputItem readonly property OutputViewport screenViewport: outputViewport - property alias wallpaperVisible: wallpaper.visible property bool forceSoftwareCursor: false devicePixelRatio: output?.scale ?? devicePixelRatio @@ -102,8 +101,6 @@ OutputItem { output: rootOutputItem.output workspace: Helper.workspace.current anchors.fill: parent - fillMode: Image.PreserveAspectCrop - retainWhileLoading: true clip: true states: [ @@ -121,6 +118,13 @@ OutputItem { scale: 1.4 } }, + State { + name: "ScaleTo1.2" + PropertyChanges { + target: wallpaper + scale: 1.2 + } + }, State { name: "ScaleWithoutAnimation" PropertyChanges { @@ -149,6 +153,15 @@ OutputItem { easing.type: Easing.OutExpo } }, + Transition { + from: "*" + to: "ScaleTo1.2" + PropertyAnimation { + property: "scale" + duration: 1000 + easing.type: Easing.OutExpo + } + }, Transition { from: "*" to: "ScaleWithoutAnimation" @@ -158,6 +171,36 @@ OutputItem { } } ] + + Connections { + target: Helper + function onLaunchpadMappedChanged(output, mapped) { + if (output !== rootOutputItem.output) { + return; + } + + wallpaper.state = mapped ? "Scale" : "Normal" + } + + function onShowDesktopRequested(output) { + if (output !== rootOutputItem.output) { + return; + } + + wallpaper.state = "Normal" + wallpaper.play = true + wallpaper.slowDown() + } + + function onStartLockscreened(output, showAnimation) { + if (output !== rootOutputItem.output) { + return; + } + + wallpaper.play = false + wallpaper.state = showAnimation ? "ScaleTo1.2" : "ScaleWithoutAnimation" + } + } } } diff --git a/src/core/qml/WorkspaceSwitcher.qml b/src/core/qml/WorkspaceSwitcher.qml index c32d6e5a5..53d89948a 100644 --- a/src/core/qml/WorkspaceSwitcher.qml +++ b/src/core/qml/WorkspaceSwitcher.qml @@ -27,13 +27,6 @@ Item { width: output.outputItem.width height: output.outputItem.height - WallpaperController { - id: wpCtrl - output: animationDelegate.output.outputItem.output - type: WallpaperController.Normal - lock: true - } - Row { x: - Helper.workspace.animationController.viewportPos * animationDelegate.localAnimationScaleFactor spacing: Helper.workspace.animationController.refGap * animationDelegate.localAnimationScaleFactor @@ -44,12 +37,11 @@ Item { height: animationDelegate.output.outputItem.height id: workspaceDelegate required property WorkspaceModel workspace - ShaderEffectSource { - id: wallpaperShot - sourceItem: wpCtrl.proxy - hideSource: false - anchors.fill: parent + Wallpaper { + workspace: workspaceDelegate.workspace + output: animationDelegate.output.outputItem.output } + WorkspaceProxy { workspace: workspaceDelegate.workspace output: animationDelegate.output diff --git a/src/core/shellhandler.cpp b/src/core/shellhandler.cpp index 23921d6ae..fe3fe75c8 100644 --- a/src/core/shellhandler.cpp +++ b/src/core/shellhandler.cpp @@ -15,6 +15,8 @@ #include "treelandconfig.hpp" #include "treelanduserconfig.hpp" #include "workspace/workspace.h" +#include "session/session.h" +#include "wallpapershellinterfacev1.h" #include @@ -248,6 +250,15 @@ void ShellHandler::initLayerShell(WServer *server) connect(m_layerShell, &WLayerShell::surfaceRemoved, this, &ShellHandler::onLayerSurfaceRemoved); } +void ShellHandler::initWallpaperShell(Waylib::Server::WServer *server) +{ + Q_ASSERT_X(!m_wallpaperShell, Q_FUNC_INFO, "Only init once!"); + m_wallpaperShell = server->attach(m_wallpaperShell); + if (qEnvironmentVariableIsSet("DDM_DISPLAY_MANAGER")) { + m_wallpaperShell->setFilter([this](WClient *client) { return Helper::instance()->sessionManager()->isDDEUserClient(client); }); + } +} + WXWayland *ShellHandler::createXWayland(WServer *server, WSeat *seat, qw_compositor *compositor, diff --git a/src/core/shellhandler.h b/src/core/shellhandler.h index bfc88c2fd..8a71666b9 100644 --- a/src/core/shellhandler.h +++ b/src/core/shellhandler.h @@ -55,6 +55,8 @@ QT_END_NAMESPACE class AppIdResolverManager; // forward declare new protocol manager class WindowConfigStore; // forward declare config store +class TreelandWallpaperShellInterfaceV1; +class TreelandWallpaperSurfaceInterfaceV1; class ShellHandler : public QObject { @@ -68,6 +70,7 @@ class ShellHandler : public QObject void createComponent(QmlEngine *engine); void initXdgShell(WAYLIB_SERVER_NAMESPACE::WServer *server); void initLayerShell(WAYLIB_SERVER_NAMESPACE::WServer *server); + void initWallpaperShell(WAYLIB_SERVER_NAMESPACE::WServer *server); [[nodiscard]] WAYLIB_SERVER_NAMESPACE::WXWayland *createXWayland( WAYLIB_SERVER_NAMESPACE::WServer *server, WAYLIB_SERVER_NAMESPACE::WSeat *seat, @@ -79,6 +82,9 @@ class ShellHandler : public QObject WAYLIB_SERVER_NAMESPACE::WSeat *seat); WAYLIB_SERVER_NAMESPACE::WXWayland *defaultXWaylandSocket() const; + TreelandWallpaperShellInterfaceV1 *wallpaperShell() const { + return m_wallpaperShell; + } Q_SIGNALS: void surfaceWrapperAdded(SurfaceWrapper *wrapper); void surfaceWrapperAboutToRemove(SurfaceWrapper *wrapper); @@ -129,6 +135,7 @@ private Q_SLOTS: WAYLIB_SERVER_NAMESPACE::WXdgShell *m_xdgShell = nullptr; WAYLIB_SERVER_NAMESPACE::WLayerShell *m_layerShell = nullptr; + TreelandWallpaperShellInterfaceV1 *m_wallpaperShell = nullptr; WAYLIB_SERVER_NAMESPACE::WInputMethodHelper *m_inputMethodHelper = nullptr; QList m_xwaylands; diff --git a/src/modules/wallpaper/wallpapershellinterfacev1.cpp b/src/modules/wallpaper/wallpapershellinterfacev1.cpp index 52ad6ec2f..53c7cdd30 100644 --- a/src/modules/wallpaper/wallpapershellinterfacev1.cpp +++ b/src/modules/wallpaper/wallpapershellinterfacev1.cpp @@ -1,6 +1,7 @@ // 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 "wallpapersurface.h" #include "wallpapershellinterfacev1.h" #include "qwayland-server-treeland-wallpaper-shell-unstable-v1.h" @@ -18,6 +19,7 @@ class TreelandWallpaperShellInterfaceV1Private : public QtWaylandServer::treelan wl_global *global() const; TreelandWallpaperShellInterfaceV1 *q = nullptr; + QList producedWallpapers; protected: void treeland_wallpaper_shell_v1_destroy_global() override; @@ -58,6 +60,7 @@ void TreelandWallpaperShellInterfaceV1Private::treeland_wallpaper_shell_v1_get_t return; } + producedWallpapers.append(file_source); wl_resource *surfaceResource = wl_resource_create(resource->client(), &treeland_wallpaper_surface_v1_interface, resource->version(), @@ -70,8 +73,9 @@ void TreelandWallpaperShellInterfaceV1Private::treeland_wallpaper_shell_v1_get_t auto wallpaperSurface = new TreelandWallpaperSurfaceInterfaceV1(surface, file_source, surfaceResource); s_wallpaperSurfaces.append(wallpaperSurface); - QObject::connect(wallpaperSurface, &QObject::destroyed, [wallpaperSurface]() { + QObject::connect(wallpaperSurface, &QObject::destroyed, [wallpaperSurface, this]() { s_wallpaperSurfaces.removeOne(wallpaperSurface); + producedWallpapers.removeOne(wallpaperSurface->source()); }); Q_EMIT q->wallpaperSurfaceAdded(wallpaperSurface); @@ -83,6 +87,11 @@ TreelandWallpaperShellInterfaceV1::TreelandWallpaperShellInterfaceV1(QObject *pa { } +QList TreelandWallpaperShellInterfaceV1::producedWallpapers() const +{ + return d->producedWallpapers; +} + TreelandWallpaperShellInterfaceV1::~TreelandWallpaperShellInterfaceV1() = default; void TreelandWallpaperShellInterfaceV1::create(WServer *server) @@ -116,6 +125,7 @@ class TreelandWallpaperSurfaceInterfaceV1Private : public QtWaylandServer::treel TreelandWallpaperSurfaceInterfaceV1 *q; wl_resource *surfaceResource = nullptr; wl_resource *resource = nullptr; + WallpaperSurface *surface = nullptr; QString wallpaperSource; protected: @@ -198,9 +208,24 @@ TreelandWallpaperSurfaceInterfaceV1 *TreelandWallpaperSurfaceInterfaceV1::get(co return nullptr; } +void TreelandWallpaperSurfaceInterfaceV1::setPlay(bool value) +{ + if (value) { + d->send_play(); + } else { + d->send_pause(); + } +} + +void TreelandWallpaperSurfaceInterfaceV1::slowDown() +{ + d->send_slow_down(3000); +} + TreelandWallpaperSurfaceInterfaceV1::TreelandWallpaperSurfaceInterfaceV1(wl_resource *surface, const QString &source, wl_resource *resource) : d(new TreelandWallpaperSurfaceInterfaceV1Private(this, source, surface, resource)) { + d->surface = new WallpaperSurface(this, this); } diff --git a/src/modules/wallpaper/wallpapershellinterfacev1.h b/src/modules/wallpaper/wallpapershellinterfacev1.h index 83b293bde..11ef35322 100644 --- a/src/modules/wallpaper/wallpapershellinterfacev1.h +++ b/src/modules/wallpaper/wallpapershellinterfacev1.h @@ -23,6 +23,7 @@ class TreelandWallpaperShellInterfaceV1 : public QObject , public WServerInterfa ~TreelandWallpaperShellInterfaceV1() override; static constexpr int InterfaceVersion = 1; + QList producedWallpapers() const; Q_SIGNALS: void wallpaperSurfaceAdded(TreelandWallpaperSurfaceInterfaceV1 *interface); @@ -50,6 +51,8 @@ class TreelandWallpaperSurfaceInterfaceV1 : public QObject static TreelandWallpaperSurfaceInterfaceV1 *get(WSurface *surface); static TreelandWallpaperSurfaceInterfaceV1 *get(const QString &source); + void setPlay(bool value); + void slowDown(); Q_SIGNALS: void failed(uint32_t error); diff --git a/src/plugins/lockscreen/qml/Greeter.qml b/src/plugins/lockscreen/qml/Greeter.qml index 9b1bb917c..8e29073e2 100644 --- a/src/plugins/lockscreen/qml/Greeter.qml +++ b/src/plugins/lockscreen/qml/Greeter.qml @@ -32,10 +32,11 @@ FocusScope { lockView.showAnimation = showAnimation lockView.forceActiveFocus() if (showAnimation) { - wallpaperController.type = WallpaperController.Scale - } else { - wallpaperController.type = WallpaperController.ScaleWithoutAnimation + Helper.startLockscreen(root.output, showAnimation); + background.state = "Show" + wallpaper.play = true; } + background.opacity = 1.0 switch (root.currentMode) { case Greeter.CurrentMode.Lock: lockView.start() @@ -53,30 +54,95 @@ FocusScope { palette.windowText: Qt.rgba(1.0, 1.0, 1.0, 1.0) - WallpaperController { - id: wallpaperController - output: root.output - lock: true - type: WallpaperController.Normal - } - - // prevent event passing through greeter - MouseArea { - anchors.fill: parent - enabled: true - } - Rectangle { - id: cover + id: background + + readonly property int duration: 1000 anchors.fill: parent color: 'black' - opacity: wallpaperController.type === WallpaperController.Normal ? 0 : 0.6 + opacity: 0.0 + transformOrigin: Item.Center Behavior on opacity { PropertyAnimation { - duration: wallpaperController.type === WallpaperController.ScaleWithoutAnimation ? 0 : 1000 + duration: lockView.showAnimation ? 2000 : 0 + easing.type: Easing.OutExpo + } + } + Behavior on scale { + PropertyAnimation { + duration: lockView.showAnimation ? 2000 : 0 easing.type: Easing.OutExpo } } + states: [ + State { + name: "Show" + PropertyChanges { + target: background + scale: 1.2 + } + PropertyChanges { + target: background + opacity: 1 + } + }, + State { + name: "Hide" + PropertyChanges { + target: background + scale: 1 + } + PropertyChanges { + target: background + opacity: 0 + } + } + ] + + transitions: [ + Transition { + from: "Hide" + to: "Show" + PropertyAnimation { + property: "scale" + duration: background.duration + easing.type: Easing.OutExpo + } + PropertyAnimation { + property: "opacity" + duration: background.duration + easing.type: Easing.OutExpo + } + }, + Transition { + from: "Show" + to: "Hide" + PropertyAnimation { + property: "scale" + duration: background.duration + easing.type: Easing.OutExpo + } + PropertyAnimation { + property: "opacity" + duration: background.duration + easing.type: Easing.OutExpo + } + } + ] + Wallpaper { + id: wallpaper + + anchors.fill: parent + wallpaperRole: Wallpaper.Lockscreen + output: root.output + wallpaperState: Wallpaper.Normal + } + } + + // prevent event passing through greeter + MouseArea { + anchors.fill: parent + enabled: true } LockView { @@ -85,8 +151,10 @@ FocusScope { root.currentMode === Greeter.CurrentMode.SwitchUser anchors.fill: parent onQuit: function () { - wallpaperController.type = WallpaperController.Normal + wallpaper.play = false; + background.state = "Hide" root.animationPlayed() + Helper.showDesktop(root.output) } onAnimationPlayFinished: function () { if (lockView.state === LoginAnimation.Hide) { @@ -101,7 +169,6 @@ FocusScope { anchors.fill: parent onClicked: function () { - wallpaperController.type = WallpaperController.Normal root.animationPlayed() root.animationPlayFinished() } @@ -114,8 +181,4 @@ FocusScope { lockView.start() } } - - Component.onDestruction: { - wallpaperController.lock = false - } } diff --git a/src/plugins/multitaskview/qml/MultitaskviewProxy.qml b/src/plugins/multitaskview/qml/MultitaskviewProxy.qml index 72aa70e91..f08ec494d 100644 --- a/src/plugins/multitaskview/qml/MultitaskviewProxy.qml +++ b/src/plugins/multitaskview/qml/MultitaskviewProxy.qml @@ -130,16 +130,15 @@ Multitaskview { width: output.outputItem.width height: output.outputItem.height - WallpaperController { - id: wallpaperController + Wallpaper { + id: wallpaper output: outputPlacementItem.output.outputItem.output - lock: false - type: WallpaperController.Normal + workspace: Helper.workspace.current } ShaderEffectSource { z: Multitaskview.Background - sourceItem: wallpaperController.proxy + sourceItem: wallpaper recursive: true live: true smooth: true @@ -223,16 +222,15 @@ Multitaskview { required property int index width: outputPlacementItem.output.outputItem.width height: outputPlacementItem.output.outputItem.height - WallpaperController { - id: wpCtrl + Wallpaper { + id: wallpaper2 output: outputPlacementItem.output.outputItem.output - lock: true - type: WallpaperController.Normal + workspace: Helper.workspace.current } ShaderEffectSource { z: Multitaskview.Background - sourceItem: wpCtrl.proxy + sourceItem: wallpaper2 recursive: true live: true smooth: true diff --git a/src/plugins/multitaskview/qml/WorkspaceSelectionList.qml b/src/plugins/multitaskview/qml/WorkspaceSelectionList.qml index ce2e6819f..df04b9a64 100644 --- a/src/plugins/multitaskview/qml/WorkspaceSelectionList.qml +++ b/src/plugins/multitaskview/qml/WorkspaceSelectionList.qml @@ -144,16 +144,15 @@ Item { } clip: true - WallpaperController { - id: wpCtrl + Wallpaper { + id: wallpaper output: root.output.outputItem.output - lock: true - type: WallpaperController.Normal + workspace: workspaceThumbDelegate.workspace } ShaderEffectSource { - id: wallpaper - sourceItem: wpCtrl.proxy + id: wallpaperSource + sourceItem: wallpaper anchors.fill: parent recursive: false hideSource: visible @@ -166,8 +165,8 @@ Item { preferredRendererType: Shape.CurveRenderer ShapePath { strokeWidth: 0 - fillItem: wallpaper - fillTransform: PlanarTransform.fromScale(content.width / wpCtrl.proxy.width, content.height / wpCtrl.proxy.height, 0, 0) + fillItem: wallpaperSource + fillTransform: PlanarTransform.fromScale(content.width / wallpaper.width, content.height / wallpaper.height, 0, 0) PathRectangle { width: content.width height: content.height diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index 17e7652f4..57965c470 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -46,6 +46,8 @@ #include "utils/cmdline.h" #include "utils/fpsdisplaymanager.h" #include "workspace/workspace.h" +#include "wallpaper/wallpapermanager.h" +#include "wallpapershellinterfacev1.h" #include #include @@ -167,6 +169,7 @@ Helper *Helper::m_instance = nullptr; Helper::Helper(QObject *parent) : WSeatEventFilter(parent) , m_sessionManager(new SessionManager(this)) + , m_wallpaperManager(new WallpaperManager(this)) , m_renderWindow(new WOutputRenderWindow(this)) , m_server(new WServer(this)) , m_rootSurfaceContainer(new RootSurfaceContainer(m_renderWindow->contentItem())) @@ -416,6 +419,7 @@ void Helper::onOutputAdded(WOutput *output) } } settings.endGroup(); + m_wallpaperManager->ensureWallpaperConfigForOutput(o); } void Helper::onOutputRemoved(WOutput *output) @@ -484,6 +488,8 @@ void Helper::onOutputRemoved(WOutput *output) } m_outputManager->removeOutput(output); + m_wallpaperManager->removeOutputWallpaper(output->handle()->handle()); + delete o; } @@ -1249,6 +1255,11 @@ void Helper::init(Treeland::Treeland *treeland) m_shellHandler->createComponent(engine); m_shellHandler->initXdgShell(m_server); m_shellHandler->initLayerShell(m_server); + m_shellHandler->initWallpaperShell(m_server); + connect(m_shellHandler->wallpaperShell(), + &TreelandWallpaperShellInterfaceV1::wallpaperSurfaceAdded, + m_wallpaperManager, + &WallpaperManager::handleWallpaperSurfaceAdded); m_shellHandler->initInputMethodHelper(m_server, m_seat); m_foreignToplevel = m_server->attach(); @@ -1307,6 +1318,17 @@ void Helper::init(Treeland::Treeland *treeland) "/" + m_userModel->currentUserName())); auto user = m_userModel->currentUser(); m_personalization->setUserId(user ? user->UID() : getuid()); + if (m_userModel->currentUserName() == "dde") { + return; + } + if (m_config->isInitializeSucceeded()) { + m_wallpaperManager->updateWallpaperConfig(); + } else { + connect(m_config.get(), + &TreelandUserConfig::configInitializeSucceed, + m_wallpaperManager, + &WallpaperManager::updateWallpaperConfig); + } }; connect(m_userModel, &UserModel::currentUserNameChanged, this, updateCurrentUser); @@ -1475,6 +1497,21 @@ void Helper::init(Treeland::Treeland *treeland) &Helper::onExtSessionLock); #endif + m_wallpaperNotifierInterfaceV1 = m_server->attach(); + if (qEnvironmentVariableIsSet("DDM_DISPLAY_MANAGER")) { + m_wallpaperNotifierInterfaceV1->setFilter([this](WClient *client) { return m_sessionManager->isDDEUserClient(client); }); + } + connect(m_wallpaperNotifierInterfaceV1, + &TreelandWallpaperNotifierInterfaceV1::binded, + m_wallpaperManager, + &WallpaperManager::onWallpaperNotifierbinded); + + m_wallpaperManagerInterfaceV1 = m_server->attach(); + connect(m_wallpaperManagerInterfaceV1, + &TreelandWallpaperManagerInterfaceV1::wallpaperCreated, + m_wallpaperManager, + &WallpaperManager::onWallpaperAdded); + m_shortcutManager = m_server->attach(); connect(m_treeland, &Treeland::Treeland::SessionChanged, @@ -2182,6 +2219,26 @@ bool Helper::isLaunchpad(WLayerSurface *surface) const return scope == "dde-shell/launchpad"; } +void Helper::setLaunchpadMapped(WOutput *output, bool mapped) +{ + Q_EMIT launchpadMappedChanged(output, mapped); +} + +void Helper::showDesktop(WOutput *output) +{ + Q_EMIT showDesktopRequested(output); +} + +void Helper::startLockscreen(WOutput *output, bool showAnimation) +{ + Q_EMIT startLockscreened(output, showAnimation); +} + +QString Helper::currentWorkspaceWallpaper(WOutput *output) +{ + return m_wallpaperManager->currentWorkspaceWallpaper(output); +} + void Helper::handleWindowPicker(WindowPickerInterface *picker) { connect(picker, &WindowPickerInterface::pick, this, [this, picker](const QString &hint) { diff --git a/src/seat/helper.h b/src/seat/helper.h index c644896d1..c1ac88b05 100644 --- a/src/seat/helper.h +++ b/src/seat/helper.h @@ -9,6 +9,9 @@ #include "modules/virtual-output/virtualoutputmanager.h" #include "modules/window-management/windowmanagement.h" #include "utils/fpsdisplaymanager.h" +#include "modules/wallpaper/wallpapermanagerinterfacev1.h" +#include "modules/wallpaper/wallpapernotifierinterfacev1.h" +#include "wallpaper/wallpaperconfig.h" #include #include @@ -113,6 +116,8 @@ class VirtualOutputV1; class WallpaperColorV1; class WindowManagementV1; class WindowPickerInterface; +class WallpaperManager; +class WallpaperItem; struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request; struct wlr_idle_inhibitor_v1; @@ -193,6 +198,10 @@ class Helper : public WSeatEventFilter WindowManagementV1::DesktopState showDesktopState() const; Q_INVOKABLE bool isLaunchpad(WLayerSurface *surface) const; + Q_INVOKABLE void setLaunchpadMapped(WOutput *output, bool mapped); + Q_INVOKABLE void showDesktop(WOutput *output); + Q_INVOKABLE void startLockscreen(WOutput *output, bool showAnimation); + Q_INVOKABLE QString currentWorkspaceWallpaper(WOutput *output); void handleWindowPicker(WindowPickerInterface *picker); @@ -246,6 +255,11 @@ public Q_SLOTS: void blockActivateSurfaceChanged(); void requestQuit(); + void updateWallpaper(); + + void launchpadMappedChanged(WOutput *output, bool mapped); + void showDesktopRequested(WOutput *output); + void startLockscreened(WOutput *output, bool showAnimation); private Q_SLOTS: void onShowDesktop(); @@ -279,8 +293,11 @@ private Q_SLOTS: void handleLockScreen(LockScreenInterface *lockScreen); void handleNewForeignToplevelCaptureRequest(wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request); void onExtSessionLock(WSessionLock *lock); - private: + friend class SessionManager; + friend class WallpaperManager; + friend class WallpaperItem; + void allowNonDrmOutputAutoChangeMode(WOutput *output); int indexOfOutput(WOutput *output) const; @@ -324,6 +341,7 @@ private Q_SLOTS: Treeland::Treeland *m_treeland = nullptr; FpsDisplayManager *m_fpsManager = nullptr; SessionManager *m_sessionManager = nullptr; + WallpaperManager *m_wallpaperManager = nullptr; CurrentMode m_currentMode{ CurrentMode::Normal }; @@ -365,6 +383,8 @@ private Q_SLOTS: OutputManagerV1 *m_outputManagerV1 = nullptr; DDMInterfaceV1 *m_ddmInterfaceV1 = nullptr; ScreensaverInterfaceV1 *m_screensaverInterfaceV1 = nullptr; + TreelandWallpaperManagerInterfaceV1 *m_wallpaperManagerInterfaceV1 = nullptr; + TreelandWallpaperNotifierInterfaceV1 *m_wallpaperNotifierInterfaceV1 = nullptr; #ifdef EXT_SESSION_LOCK_V1 WSessionLockManager *m_sessionLockManager = nullptr; QTimer *m_lockScreenGraceTimer = nullptr; diff --git a/src/session/session.cpp b/src/session/session.cpp index e743c9bf6..8bd5c5c60 100644 --- a/src/session/session.cpp +++ b/src/session/session.cpp @@ -9,6 +9,7 @@ #include "seat/helper.h" #include "workspace/workspace.h" #include "xsettings/settingmanager.h" +#include "wallpaper/wallpaperlauncher.h" #include #include @@ -99,6 +100,10 @@ SessionManager::SessionManager(QObject *parent) SessionManager::~SessionManager() { m_sessions.clear(); + if (m_wallpaperLauncher) { + delete m_wallpaperLauncher; + m_wallpaperLauncher = nullptr; + } } const QList> &SessionManager::sessions() const @@ -298,6 +303,11 @@ std::shared_ptr SessionManager::ensureSession(int id, QString username) if (!session->m_socket) return nullptr; + if (!m_wallpaperLauncher) { + m_wallpaperLauncher = new WallpaperLauncher(session->socket()->rootSocket()); + m_wallpaperLauncher->start(); + } + session->m_xwayland = createXWayland(session->m_socket); if (!session->m_xwayland) return nullptr; @@ -383,6 +393,25 @@ std::shared_ptr SessionManager::sessionForSocket(WSocket *socket) const return nullptr; } +bool SessionManager::isDDEUserClient(WClient *client) +{ + for (auto session : m_sessions) { + if (session->m_socket == client->socket()->rootSocket()) { + struct passwd *pw = getpwuid(session->m_uid); + if (!pw) { + return false; + } + + QString user{ pw->pw_name }; + if (user == "dde") { + return true; + } + } + } + + return false; +} + /** * Update the active session to the given uid, creating it if necessary. * This will update XWayland visibility and emit socketFileChanged if the @@ -407,7 +436,7 @@ void SessionManager::updateActiveUserSession(const QString &username, int id) // TODO: Each Wayland socket's active surface needs to be cleaned up individually. Helper::instance()->activateSurface(nullptr); // Emit signal and update socket enabled state - if (previous && previous->m_socket) + if (previous && previous->m_socket && previous->username() != "dde") previous->m_socket->setEnabled(false); session->m_socket->setEnabled(true); Q_EMIT socketFileChanged(); diff --git a/src/session/session.h b/src/session/session.h index a4b1991db..25331aae2 100644 --- a/src/session/session.h +++ b/src/session/session.h @@ -15,6 +15,7 @@ WAYLIB_SERVER_END_NAMESPACE WAYLIB_SERVER_USE_NAMESPACE class SettingManager; +class WallpaperLauncher; class Session : public QObject { Q_OBJECT @@ -68,6 +69,7 @@ class SessionManager : public QObject { std::shared_ptr sessionForUser(const QString &username) const; std::shared_ptr sessionForXWayland(WXWayland *xwayland) const; std::shared_ptr sessionForSocket(WSocket *socket) const; + bool isDDEUserClient(WClient *client); Q_SIGNALS: void socketFileChanged(); @@ -78,4 +80,5 @@ class SessionManager : public QObject { std::weak_ptr m_activeSession; QList> m_sessions; + WallpaperLauncher *m_wallpaperLauncher { nullptr }; }; diff --git a/src/wallpaper/wallpaperconfig.cpp b/src/wallpaper/wallpaperconfig.cpp new file mode 100644 index 000000000..52f2f01fb --- /dev/null +++ b/src/wallpaper/wallpaperconfig.cpp @@ -0,0 +1,75 @@ +// 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 "wallpaperconfig.h" + +#define OUTPUT_NAME "outputName" +#define LOCK_SCREEN_WALLAPER_TYPE "lockScreenWallpaperType" +#define DESKTOP_WALLAPER_TYPE "desktopWallpaperType" +#define WORKSPACE_INDEX "workspaceIndex" +#define DESKTOP_WALLPAPER "desktopWallpaper" +#define LOCK_SCREEN_WALLPAPER "lockScreenWallpaper" +#define WORKSPACES "workspaces" +#define ENABLE "enable" + +QJsonObject WallpaperWorkspaceConfig::toJson() const +{ + QJsonObject obj; + obj[WORKSPACE_INDEX] = workspaceId; + obj[DESKTOP_WALLPAPER] = desktopWallpaper; + obj[DESKTOP_WALLAPER_TYPE] = desktopWallpapertype; + obj[ENABLE] = enable; + return obj; +} + +WallpaperWorkspaceConfig WallpaperWorkspaceConfig::fromJson(const QJsonObject &obj) +{ + WallpaperWorkspaceConfig ws; + ws.workspaceId = obj[WORKSPACE_INDEX].toInt(); + ws.desktopWallpaper = obj[DESKTOP_WALLPAPER].toString(); + ws.desktopWallpapertype = static_cast(obj[DESKTOP_WALLAPER_TYPE].toInt()); + ws.enable = obj[ENABLE].toBool(); + return ws; +} + +QJsonObject WallpaperOutputConfig::toJson() const +{ + QJsonObject obj; + obj[OUTPUT_NAME] = outputName; + obj[LOCK_SCREEN_WALLPAPER] = lockscreenWallpaper; + obj[LOCK_SCREEN_WALLAPER_TYPE] = lockScreenWallpapertype; + obj[ENABLE] = enable; + QJsonArray workspacesArray; + for (const WallpaperWorkspaceConfig& ws : workspaces) { + workspacesArray.append(ws.toJson()); + } + obj[WORKSPACES] = workspacesArray; + + return obj; +} + +bool WallpaperOutputConfig::containsWorkspace(int id) +{ + for (const WallpaperWorkspaceConfig& ws : workspaces) { + if (ws.workspaceId == id) { + return true; + } + } + + return false; +} + +WallpaperOutputConfig WallpaperOutputConfig::fromJson(const QJsonObject &obj) +{ + WallpaperOutputConfig out; + out.outputName = obj[OUTPUT_NAME].toString(); + out.lockscreenWallpaper = obj[LOCK_SCREEN_WALLPAPER].toString(); + out.lockScreenWallpapertype = static_cast(obj[LOCK_SCREEN_WALLAPER_TYPE].toInt()); + out.enable = obj[ENABLE].toBool(); + QJsonArray workspacesArray = obj[WORKSPACES].toArray(); + for (const QJsonValue& value : workspacesArray) { + out.workspaces.append(WallpaperWorkspaceConfig::fromJson(value.toObject())); + } + + return out; +} diff --git a/src/wallpaper/wallpaperconfig.h b/src/wallpaper/wallpaperconfig.h new file mode 100644 index 000000000..3af7d5353 --- /dev/null +++ b/src/wallpaper/wallpaperconfig.h @@ -0,0 +1,36 @@ +// 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 "modules/wallpaper/wallpapermanagerinterfacev1.h" + +#include +#include +#include + +class WallpaperWorkspaceConfig { +public: + int workspaceId; + QString desktopWallpaper; + TreelandWallpaperInterfaceV1::WallpaperType desktopWallpapertype; + bool enable; + + QJsonObject toJson() const; + + static WallpaperWorkspaceConfig fromJson(const QJsonObject& obj); +}; + +class WallpaperOutputConfig { +public: + QString outputName; + QString lockscreenWallpaper; + QList workspaces; + TreelandWallpaperInterfaceV1::WallpaperType lockScreenWallpapertype; + bool enable; + + QJsonObject toJson() const; + bool containsWorkspace(int id); + + static WallpaperOutputConfig fromJson(const QJsonObject& obj); +}; diff --git a/src/wallpaper/wallpapercontroller.cpp b/src/wallpaper/wallpapercontroller.cpp deleted file mode 100644 index d36cf5fc6..000000000 --- a/src/wallpaper/wallpapercontroller.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2024 Dingyuan Zhang . -// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include "wallpapercontroller.h" - -#include "wallpaperimage.h" -#include "wallpapermanager.h" - -WallpaperController::WallpaperController(QObject *parent) - : QObject(parent) - , m_output(nullptr) -{ -} - -WallpaperController::~WallpaperController() -{ - setLock(false); -} - -void WallpaperController::setType(WallpaperType type) -{ - auto *manager = WallpaperManager::instance(); - if (manager->isLocked(this) && !manager->isSelfLocked(this)) { - return; - } - - m_type = type; - - updateState(); -} - -void WallpaperController::setOutput(WAYLIB_SERVER_NAMESPACE::WOutput *output) -{ - if (!output) { - return; - } - - if (m_output == output) { - return; - } - - m_output = output; - - Q_EMIT outputChanged(); - Q_EMIT proxyChanged(); -} - -void WallpaperController::updateState() -{ - if (!m_output) { - return; - } - - auto *manager = WallpaperManager::instance(); - auto *proxy = manager->get(m_output); - - Q_ASSERT(proxy); - - QString state = "Normal"; - switch (m_type) { - case Normal: - break; - case Scale: - state = "Scale"; - break; - case ScaleWithoutAnimation: - state = "ScaleWithoutAnimation"; - break; - } - proxy->setState(state); - - Q_EMIT proxyChanged(); - Q_EMIT typeChanged(); -} - -WallpaperImage *WallpaperController::proxy() const -{ - if (!m_output) { - return nullptr; - } - - auto *manager = WallpaperManager::instance(); - return manager->get(m_output); -} - -void WallpaperController::setLock(bool lock) -{ - auto *manager = WallpaperManager::instance(); - manager->setLock(this, lock); -} - -bool WallpaperController::lock() const -{ - auto *manager = WallpaperManager::instance(); - return manager->isSelfLocked(this); -} diff --git a/src/wallpaper/wallpapercontroller.h b/src/wallpaper/wallpapercontroller.h deleted file mode 100644 index 816f92545..000000000 --- a/src/wallpaper/wallpapercontroller.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2024 Dingyuan Zhang . -// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#pragma once - -#include - -#include -#include - -Q_MOC_INCLUDE("wallpaper/wallpaperimage.h") - -class WallpaperImage; - -class WallpaperController : public QObject -{ - Q_OBJECT - Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WOutput* output READ output WRITE setOutput NOTIFY outputChanged REQUIRED) - Q_PROPERTY(WallpaperType type READ type WRITE setType NOTIFY typeChanged) - Q_PROPERTY(WallpaperImage* proxy READ proxy NOTIFY proxyChanged) - Q_PROPERTY(bool lock READ lock WRITE setLock NOTIFY lockChanged) - - QML_ELEMENT - -public: - explicit WallpaperController(QObject *parent = nullptr); - ~WallpaperController() override; - - enum WallpaperType - { - Normal, - Scale, - ScaleWithoutAnimation, - }; - Q_ENUM(WallpaperType) - -Q_SIGNALS: - void typeChanged(); - void outputChanged(); - void proxyChanged(); - void lockChanged(); - -public: - void setType(WallpaperType type); - - WallpaperType type() const - { - return m_type; - } - - void setOutput(WAYLIB_SERVER_NAMESPACE::WOutput *output); - - inline WAYLIB_SERVER_NAMESPACE::WOutput *output() const - { - return m_output; - } - - WallpaperImage *proxy() const; - - void setLock(bool state); - bool lock() const; - -private: - void updateState(); - -private: - WAYLIB_SERVER_NAMESPACE::WOutput *m_output; - WallpaperType m_type{ WallpaperType::Normal }; -}; diff --git a/src/wallpaper/wallpaperimage.cpp b/src/wallpaper/wallpaperimage.cpp deleted file mode 100644 index a5eef386a..000000000 --- a/src/wallpaper/wallpaperimage.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2024 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 "wallpaperimage.h" - -#include "core/qmlengine.h" -#include "seat/helper.h" -#include "modules/personalization/personalizationmanager.h" -#include "greeter/usermodel.h" -#include "wallpapermanager.h" -#include "workspace/workspacemodel.h" -#include - -#include - -#include - - -WAYLIB_SERVER_USE_NAMESPACE - -WallpaperImage::WallpaperImage(QQuickItem *parent) - : QQuickAnimatedImage(parent) -{ - connect(Helper::instance()->qmlEngine()->singletonInstance("Treeland", - "UserModel"), - &UserModel::currentUserNameChanged, - this, - &WallpaperImage::updateSource); - - connect(Helper::instance()->personalization(), - &PersonalizationV1::backgroundChanged, - this, - &WallpaperImage::updateSource); - - setFillMode(Tile); - setCache(false); - setAsynchronous(true); - - updateSource(); -} - -WallpaperImage::~WallpaperImage() { } - -WorkspaceModel *WallpaperImage::workspace() -{ - return m_workspace; -} - -void WallpaperImage::setWorkspace(WorkspaceModel *workspace) -{ - if (m_workspace != workspace) { - m_workspace = workspace; - Q_EMIT workspaceChanged(); - updateSource(); - } -} - -WOutput *WallpaperImage::output() -{ - return m_output; -} - -void WallpaperImage::setOutput(WOutput *output) -{ - if (m_output != output) { - if (m_output) - QObject::disconnect(m_output, nullptr, this, nullptr); - - m_output = output; - Q_EMIT outputChanged(); - - if (output) { - setSourceSize(output->transformedSize()); - connect(output, &WOutput::transformedSizeChanged, this, [this] { - setSourceSize(m_output->transformedSize()); - }); - - WallpaperManager::instance()->add(this, WOutputItem::getOutputItem(output)); - } else { - WallpaperManager::instance()->remove(this); - } - updateSource(); - } -} - -void WallpaperImage::updateSource() -{ - if (!m_output) { - return; - } - - auto *personalization = Helper::instance()->personalization(); - auto path = personalization->background(m_output->name()); - QUrl url; - if (path.startsWith("qrc:")) { - url = QUrl(path); - } else if (path.startsWith("/")) { - url = QUrl::fromLocalFile(path); - } - setSource(url.toString()); - update(); -} diff --git a/src/wallpaper/wallpaperimage.h b/src/wallpaper/wallpaperimage.h deleted file mode 100644 index ec9cd1010..000000000 --- a/src/wallpaper/wallpaperimage.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2024 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 "wglobal.h" - -#include - -Q_MOC_INCLUDE("workspace/workspace.h") - -WAYLIB_SERVER_BEGIN_NAMESPACE -class WOutput; -WAYLIB_SERVER_END_NAMESPACE - -WAYLIB_SERVER_USE_NAMESPACE - -class WorkspaceModel; - -class WallpaperImage : public QQuickAnimatedImage -{ - Q_OBJECT - Q_PROPERTY(WorkspaceModel* workspace READ workspace WRITE setWorkspace NOTIFY workspaceChanged FINAL) - Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WOutput* output READ output WRITE setOutput NOTIFY outputChanged FINAL) - - QML_NAMED_ELEMENT(Wallpaper) - QML_ADDED_IN_VERSION(1, 0) - -public: - WallpaperImage(QQuickItem *parent = nullptr); - ~WallpaperImage(); - - WorkspaceModel *workspace(); - void setWorkspace(WorkspaceModel *workspace); - - WOutput *output(); - void setOutput(WOutput *output); - -Q_SIGNALS: - void outputChanged(); - void workspaceChanged(); - -protected: - void updateSource(); - -private: - int m_userId = -1; - QPointer m_workspace; - QPointer m_output; -}; diff --git a/src/wallpaper/wallpaperitem.cpp b/src/wallpaper/wallpaperitem.cpp new file mode 100644 index 000000000..32a02fb38 --- /dev/null +++ b/src/wallpaper/wallpaperitem.cpp @@ -0,0 +1,233 @@ +// 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 "wallpaperitem.h" + +#include "core/qmlengine.h" +#include "seat/helper.h" +#include "modules/personalization/personalizationmanager.h" +#include "workspace/workspacemodel.h" +#include "wallpapershellinterfacev1.h" +#include "wallpaper/wallpapermanager.h" +#include "workspace.h" +#include "shellhandler.h" + +#include +#include + +#include +#include + +WAYLIB_SERVER_USE_NAMESPACE + +WallpaperItem::WallpaperItem(QQuickItem *parent) + : WSurfaceItemContent(parent) +{ + m_model = Helper::instance()->qmlEngine()->singletonInstance("Treeland", + "UserModel"); + connect(m_model, + &UserModel::currentUserNameChanged, + this, + &WallpaperItem::handleCurrentuserChanged); + connect(Helper::instance()->shellHandler()->wallpaperShell(), + &TreelandWallpaperShellInterfaceV1::wallpaperSurfaceAdded, + this, + &WallpaperItem::handleWallpaperSurfaceAdded); + connect(Helper::instance(), + &Helper::updateWallpaper, + this, + &WallpaperItem::updateSurface); + connect(Helper::instance()->workspace(), + &Workspace::workspaceAdded, + this, + &WallpaperItem::handleWorkspaceAdded); +} + +WallpaperItem::~WallpaperItem() = default; + +WorkspaceModel *WallpaperItem::workspace() +{ + return m_workspace; +} + +void WallpaperItem::setWorkspace(WorkspaceModel *workspace) +{ + if (m_workspace == workspace) { + return; + } + + m_workspace = workspace; + Q_EMIT workspaceChanged(); + if (wallpaperRole() == Lockscreen) + return; + + updateSurface(); +} + +WOutput *WallpaperItem::output() +{ + return m_output; +} + +void WallpaperItem::setOutput(WOutput *output) +{ + if (m_output == output) { + return; + } + + m_output = output; + Q_EMIT outputChanged(); + updateSurface(); +} + +void WallpaperItem::setWallpaperRole(WallpaperRole role) +{ + if (m_wallpaperRole == role) { + return; + } + + m_wallpaperRole = role; + Q_EMIT wallpaperRoleChanged(); + updateSurface(); +} + +void WallpaperItem::setWallpaperState(WallpaperState state) +{ + if (m_state == state) { + return; + } + + m_state = state; + Q_EMIT wallpaperStateChanged(); +} + +WallpaperItem::WallpaperState WallpaperItem::wallpaperState() +{ + return m_state; +} + +QString WallpaperItem::source() const +{ + return m_source; +} + +bool WallpaperItem::play() const +{ + return m_play; +} + +void WallpaperItem::setPlay(bool value) +{ + if (m_play == value) { + return; + } + + TreelandWallpaperSurfaceInterfaceV1 *interface = + TreelandWallpaperSurfaceInterfaceV1::get(source()); + if (!interface) { + return; + } + interface->setPlay(value); + m_play = value; + Q_EMIT playChanged(); +} + +void WallpaperItem::slowDown() +{ + TreelandWallpaperSurfaceInterfaceV1 *interface = + TreelandWallpaperSurfaceInterfaceV1::get(source()); + if (!interface) { + return; + } + + interface->slowDown(); +} + +void WallpaperItem::handleCurrentuserChanged() +{ + updateSurface(); +} + +WallpaperItem::WallpaperRole WallpaperItem::wallpaperRole() +{ + return m_wallpaperRole; +} + +void WallpaperItem::updateSurface() +{ + if (!output()) { + return; + } + + if (wallpaperRole() == Desktop && !workspace()) { + return; + } + + WallpaperOutputConfig config = + Helper::instance()->m_wallpaperManager->getOutputConfig(output()->nativeHandle()); + if (wallpaperRole() == Lockscreen) { + if (config.lockscreenWallpaper != m_source) { + TreelandWallpaperSurfaceInterfaceV1 *interface = + TreelandWallpaperSurfaceInterfaceV1::get(config.lockscreenWallpaper); + if (!interface) { + return; + } + m_source = config.lockscreenWallpaper; + setSurface(interface->wSurface()); + interface->wSurface()->enterOutput(output()); + Q_EMIT sourceChanged(); + } + return; + } + + if (wallpaperRole() == Desktop) { + if (!workspace()) { + return; + } + for (WallpaperWorkspaceConfig workspaceConfig : std::as_const(config.workspaces)) { + if (workspaceConfig.workspaceId == workspace()->id() && + workspaceConfig.desktopWallpaper != m_source) { + TreelandWallpaperSurfaceInterfaceV1 *interface = + TreelandWallpaperSurfaceInterfaceV1::get(workspaceConfig.desktopWallpaper); + if (!interface) { + return; + } + m_source = workspaceConfig.desktopWallpaper; + setSurface(interface->wSurface()); + interface->wSurface()->enterOutput(output()); + Q_EMIT sourceChanged(); + break; + } + } + return; + } +} + +void WallpaperItem::handleWallpaperSurfaceAdded(TreelandWallpaperSurfaceInterfaceV1 *interface) +{ + if (wallpaperRole() != Lockscreen && + Helper::instance()->m_wallpaperManager->getWallpaperType(interface->source()) == TreelandWallpaperInterfaceV1::Video) { + QTimer::singleShot(1000, + this, + [this]{ + updateSurface(); + setPlay(false); + }); + } else { + updateSurface(); + } +} + +void WallpaperItem::handleWorkspaceAdded() +{ + if (wallpaperRole() == Lockscreen) { + return; + } + + if (!output() || !workspace()) { + return; + } + + Helper::instance()->m_wallpaperManager->syncAddWorkspace(); + updateSurface(); +} diff --git a/src/wallpaper/wallpaperitem.h b/src/wallpaper/wallpaperitem.h new file mode 100644 index 000000000..b01b2f645 --- /dev/null +++ b/src/wallpaper/wallpaperitem.h @@ -0,0 +1,95 @@ +// 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 "wglobal.h" +#include "wsurfaceitem.h" +#include "greeter/usermodel.h" + +Q_MOC_INCLUDE("workspace/workspace.h") + +class TreelandWallpaperSurfaceInterfaceV1; + +WAYLIB_SERVER_BEGIN_NAMESPACE +class WOutput; +WAYLIB_SERVER_END_NAMESPACE + +WAYLIB_SERVER_USE_NAMESPACE + +class WorkspaceModel; + +class WallpaperItem : public WSurfaceItemContent +{ + Q_OBJECT + Q_PROPERTY(WorkspaceModel* workspace READ workspace WRITE setWorkspace NOTIFY workspaceChanged FINAL) + Q_PROPERTY(WAYLIB_SERVER_NAMESPACE::WOutput* output READ output WRITE setOutput NOTIFY outputChanged FINAL) + Q_PROPERTY(WallpaperRole wallpaperRole READ wallpaperRole WRITE setWallpaperRole NOTIFY wallpaperRoleChanged FINAL) + Q_PROPERTY(QString source READ source NOTIFY sourceChanged FINAL) + Q_PROPERTY(WallpaperState wallpaperState READ wallpaperState WRITE setWallpaperState NOTIFY wallpaperStateChanged FINAL) + Q_PROPERTY(bool play READ play WRITE setPlay NOTIFY playChanged FINAL) + + QML_NAMED_ELEMENT(Wallpaper) + QML_ADDED_IN_VERSION(1, 0) + +public: + enum WallpaperRole { + Desktop = 0x1, + Lockscreen = 0x2 + }; + Q_ENUM(WallpaperRole) + + enum WallpaperState + { + Normal, + Scale, + ScaleWithoutAnimation, + }; + Q_ENUM(WallpaperState) + + WallpaperItem(QQuickItem *parent = nullptr); + ~WallpaperItem(); + + WorkspaceModel *workspace(); + void setWorkspace(WorkspaceModel *workspace); + + WOutput *output(); + void setOutput(WOutput *output); + + enum WallpaperRole wallpaperRole(); + void setWallpaperRole(enum WallpaperRole role); + + enum WallpaperState wallpaperState(); + void setWallpaperState(enum WallpaperState state); + + QString source() const; + + bool play() const; + void setPlay(bool value); + + Q_INVOKABLE void slowDown(); + +Q_SIGNALS: + void outputChanged(); + void workspaceChanged(); + void wallpaperRoleChanged(); + void sourceChanged(); + void wallpaperStateChanged(); + void playChanged(); + +private Q_SLOTS: + void handleCurrentuserChanged(); + void updateSurface(); + void handleWallpaperSurfaceAdded(TreelandWallpaperSurfaceInterfaceV1 *interface); + void handleWorkspaceAdded(); + +private: + int m_userId = -1; + QPointer m_workspace = nullptr; + QPointer m_output = nullptr; + enum WallpaperRole m_wallpaperRole = Desktop; + enum WallpaperState m_state = Normal; + QString m_source; + UserModel *m_model; + bool m_play = false; +}; diff --git a/src/wallpaper/wallpaperlauncher.cpp b/src/wallpaper/wallpaperlauncher.cpp new file mode 100644 index 000000000..fa254c973 --- /dev/null +++ b/src/wallpaper/wallpaperlauncher.cpp @@ -0,0 +1,132 @@ +// 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 "wallpaperlauncher.h" +#include "common/treelandlogging.h" +#include "helper.h" + +#include + +WallpaperLauncher::WallpaperLauncher(QPointer socket) + : QObject(nullptr) + , m_socket(socket) +{ + m_launcherThread = new QThread(); + this->moveToThread(m_launcherThread); + m_launcherThread->start(); +} + +WallpaperLauncher::~WallpaperLauncher() +{ + if (m_launcherThread) { + QMetaObject::invokeMethod(this, + &WallpaperLauncher::onStopRequested, + Qt::BlockingQueuedConnection); + m_launcherThread->quit(); + m_launcherThread->wait(); + m_launcherThread->deleteLater(); + } +} + +void WallpaperLauncher::setDisplayName(const QString &displayName) +{ + QMetaObject::invokeMethod(this, + &WallpaperLauncher::onSetDisplayNameRequested, + Qt::QueuedConnection, + displayName); +} + +void WallpaperLauncher::start() +{ + QMetaObject::invokeMethod(this, + &WallpaperLauncher::onStartRequested, + Qt::QueuedConnection); +} + +void WallpaperLauncher::stop() +{ + QMetaObject::invokeMethod(this, + &WallpaperLauncher::onStopRequested, + Qt::QueuedConnection); +} + +void WallpaperLauncher::onSetDisplayNameRequested(const QString &displayName) +{ + if (m_displayName == displayName) { + return; + } + + m_displayName = displayName; +} + +void WallpaperLauncher::onStartRequested() +{ + if (m_wallpaperProcess) { + qCDebug(treelandWallpaper) << "Wallpaper already running or start requested."; + return; + } + + m_wallpaperProcess = new QProcess(this); + m_wallpaperProcess->setProgram(QStringLiteral("treeland-wallpaper-factory")); + m_wallpaperProcess->setProcessChannelMode(QProcess::MergedChannels); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("WAYLAND_DISPLAY", m_socket->fullServerName()); + env.insert("QT_QPA_PLATFORM", "wayland"); + m_wallpaperProcess->setProcessEnvironment(env); + connect(m_wallpaperProcess, + &QProcess::errorOccurred, + this, + &WallpaperLauncher::handleWallpaperError); + connect(m_wallpaperProcess, + QOverload::of(&QProcess::finished), + this, + &WallpaperLauncher::handleWallpaperFinished); + m_wallpaperProcess->start(); +} + +void WallpaperLauncher::onStopRequested() +{ + if (!m_wallpaperProcess) { + qCDebug(treelandWallpaper) << "Wallpaper not running or stop already requested."; + return; + } + + Q_EMIT finished(); + + if (m_wallpaperProcess->state() != QProcess::NotRunning) { + disconnect(m_wallpaperProcess, nullptr, this, nullptr); + m_wallpaperProcess->terminate(); + m_wallpaperProcess->waitForFinished(5000); + } + delete m_wallpaperProcess; + m_wallpaperProcess = nullptr; +} + +void WallpaperLauncher::handleWallpaperFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + stop(); +} + +void WallpaperLauncher::handleWallpaperError(QProcess::ProcessError error) +{ + switch (error) { + case QProcess::FailedToStart: + qCCritical(treelandWallpaper) << "Wallpaper process failed to start"; + break; + case QProcess::Crashed: + qCCritical(treelandWallpaper) << "Wallpaper process crashed"; + break; + case QProcess::Timedout: + qCCritical(treelandWallpaper) << "Wallpaper operation timed out"; + break; + case QProcess::WriteError: + case QProcess::ReadError: + qCCritical(treelandWallpaper) << "An error occurred while communicating with Wallpaper"; + break; + case QProcess::UnknownError: + qCCritical(treelandWallpaper) << "An unknown error has occurred in Wallpaper"; + break; + } + + Q_EMIT errorOccurred(); +} diff --git a/src/wallpaper/wallpaperlauncher.h b/src/wallpaper/wallpaperlauncher.h new file mode 100644 index 000000000..8952f3807 --- /dev/null +++ b/src/wallpaper/wallpaperlauncher.h @@ -0,0 +1,43 @@ +// 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 "wsocket.h" + +#include +#include +#include + +WAYLIB_SERVER_USE_NAMESPACE + +class WallpaperLauncher : public QObject +{ + Q_OBJECT +public: + explicit WallpaperLauncher(QPointer socket); + ~WallpaperLauncher() override; + + void setDisplayName(const QString &displayName); + void start(); + void stop(); + +Q_SIGNALS: + void started(const QString &displayName); + void finished(); + void errorOccurred(); + +private Q_SLOTS: + void onSetDisplayNameRequested(const QString &displayName); + void onStartRequested(); + void onStopRequested(); + + void handleWallpaperFinished(int exitCode, QProcess::ExitStatus exitStatus); + void handleWallpaperError(QProcess::ProcessError error); + +private: + QPointer m_socket = nullptr; + QThread *m_launcherThread = nullptr; + QProcess *m_wallpaperProcess = nullptr; + QString m_displayName; +}; diff --git a/src/wallpaper/wallpapermanager.cpp b/src/wallpaper/wallpapermanager.cpp index c716c7291..56b2348b1 100644 --- a/src/wallpaper/wallpapermanager.cpp +++ b/src/wallpaper/wallpapermanager.cpp @@ -1,56 +1,193 @@ -// Copyright (C) 2024 Dingyuan Zhang . +// 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 "wallpapermanager.h" - -#include "wallpapercontroller.h" -#include "wallpaperimage.h" +#include "workspace.h" +#include "helper.h" +#include "treelanduserconfig.hpp" #include "common/treelandlogging.h" +#include "modules/wallpaper/wallpapershellinterfacev1.h" +#include "shellhandler.h" + +#include +#include + +#define DEFAULT_WALLPAPER "/usr/share/wallpapers/deepin/deepin-default.jpg" + +enum class WallpaperType { + Image, + Video, + Unknown +}; + +static WallpaperType detectWallpaperType(const QString &path) +{ + if (path.isEmpty()) + return WallpaperType::Unknown; -#include -#include + static QMimeDatabase db; + const QMimeType mime = db.mimeTypeForFile(path, QMimeDatabase::MatchContent); + if (mime.isValid() && mime.name().startsWith("image/")) { + return WallpaperType::Image; + } + + if (mime.isValid() && mime.name().startsWith("video/")) { + return WallpaperType::Video; + } -WallpaperManager::WallpaperManager(QQuickItem *parent) + return WallpaperType::Unknown; +} + +WallpaperManager::WallpaperManager(QObject *parent) : QObject(parent) { } -WallpaperManager::~WallpaperManager() { } +WallpaperManager::~WallpaperManager() +{ + +} + +QString WallpaperManager::getOutputId(wlr_output *output) +{ + QString model = output->model; + QString outputId = model.append(output->serial); + + return outputId; +} + +QString WallpaperManager::getOutputId(Output *output) +{ + return getOutputId(output->output()->nativeHandle()); +} + +WallpaperOutputConfig WallpaperManager::getOutputConfig(Output *output) +{ + return getOutputConfig(getOutputId(output)); +} -WallpaperManager *WallpaperManager::instance() +WallpaperOutputConfig WallpaperManager::getOutputConfig(wlr_output *output) { - static WallpaperManager *instance = new WallpaperManager(); - return instance; + return getOutputConfig(getOutputId(output)); } -void WallpaperManager::add(WallpaperImage *proxy, WAYLIB_SERVER_NAMESPACE::WOutputItem *outputItem) +WallpaperOutputConfig WallpaperManager::getOutputConfig(const QString &id) { - Q_ASSERT(m_proxys.find(outputItem) == m_proxys.end()); - m_proxys[outputItem] = proxy; + for (WallpaperOutputConfig output : m_wallpaperConfig) { + if (output.outputName == id) { + return output; + } + } + + return WallpaperOutputConfig{}; +} - connect(proxy, &WallpaperImage::destroyed, [this, proxy] { - remove(proxy); - }); +void WallpaperManager::updateWallpaperConfig() +{ + m_wallpaperConfig.clear(); + if (!Helper::instance()->m_config->wallpaperConfig().isEmpty()) { + QJsonDocument doc = QJsonDocument::fromJson(Helper::instance()->m_config->wallpaperConfig().toUtf8()); + if (doc.isArray()) { + QJsonArray jsonArray = doc.array(); + for (const QJsonValue& value : jsonArray) { + QJsonObject obj = value.toObject(); + m_wallpaperConfig.append(WallpaperOutputConfig::fromJson(obj)); + } + } else { + qCCritical(treelandWallpaper) << "wallpapers config value error: Expected a JSON array, but got something else."; + } + } else { + defaultWallpaperConfig(); + } + m_wallpaperConfigUpdated = true; } -void WallpaperManager::remove(WallpaperImage *proxy) +void WallpaperManager::defaultWallpaperConfig() { - m_proxys.remove(m_proxys.key(proxy)); + Workspace *workspace = Helper::instance()->workspace(); + Q_ASSERT(workspace); + for (Output *output : Helper::instance()->m_outputList) { + WallpaperOutputConfig outputConfig; + outputConfig.lockscreenWallpaper = Helper::instance()->m_config->defaultBackground(); + outputConfig.outputName = WallpaperManager::getOutputId(output); + outputConfig.enable = output->output()->nativeHandle()->enabled; + WallpaperType type = detectWallpaperType(outputConfig.lockscreenWallpaper); + if (type == WallpaperType::Unknown) { + outputConfig.lockscreenWallpaper = DEFAULT_WALLPAPER; + outputConfig.lockScreenWallpapertype = TreelandWallpaperInterfaceV1::Image; + } else { + outputConfig.lockScreenWallpapertype = static_cast(type); + } + + for (int i = 0; i < workspace->count(); i++) { + WallpaperWorkspaceConfig workspaceConfig; + workspaceConfig.desktopWallpaper = Helper::instance()->m_config->defaultBackground(); + workspaceConfig.workspaceId = i; + workspaceConfig.enable = true; + WallpaperType type = detectWallpaperType(workspaceConfig.desktopWallpaper); + if (type == WallpaperType::Unknown) { + workspaceConfig.desktopWallpaper = DEFAULT_WALLPAPER; + workspaceConfig.desktopWallpapertype = TreelandWallpaperInterfaceV1::Image; + } else { + workspaceConfig.desktopWallpapertype = static_cast(type); + } + outputConfig.workspaces.append(workspaceConfig); + } + m_wallpaperConfig.append(outputConfig); + } + + QString json = wallpaperConfigToJsonString(); + Helper::instance()->m_config->setWallpaperConfig(json); } -void WallpaperManager::remove(WAYLIB_SERVER_NAMESPACE::WOutputItem *outputItem) +void WallpaperManager::ensureWallpaperConfigForOutput(Output *output) { - m_proxys.remove(outputItem); + if (!m_wallpaperConfigUpdated) { + return; + } + + bool update = false; + if (!configContainsOutput(output)) { + WallpaperOutputConfig refConfig = m_wallpaperConfig.last(); + WallpaperWorkspaceConfig refWorkspaceConfig = refConfig.workspaces.first(); + Workspace *workspace = Helper::instance()->workspace(); + Q_ASSERT(workspace); + + WallpaperOutputConfig outputConfig; + outputConfig.lockscreenWallpaper = refConfig.lockscreenWallpaper; + outputConfig.lockScreenWallpapertype = refConfig.lockScreenWallpapertype; + outputConfig.outputName = getOutputId(output); + outputConfig.enable = output->output()->nativeHandle()->enabled; + for (int i = 0; i < workspace->count(); i++) { + WallpaperWorkspaceConfig workspaceConfig; + workspaceConfig.desktopWallpaper = refWorkspaceConfig.desktopWallpaper; + workspaceConfig.desktopWallpapertype = refWorkspaceConfig.desktopWallpapertype; + workspaceConfig.workspaceId = i; + workspaceConfig.enable = true; + outputConfig.workspaces.append(workspaceConfig); + } + m_wallpaperConfig.append(outputConfig); + update = true; + } else { + return; + } + + if (update) { + QString json = wallpaperConfigToJsonString(); + Helper::instance()->m_config->setWallpaperConfig(json); + Q_EMIT updateWallpaper(); + } } -bool WallpaperManager::isLocked(const WallpaperController *controller) const +bool WallpaperManager::configContainsOutput(Output *output) { - if (!controller) { + if (m_wallpaperConfig.isEmpty()) { return false; } - for (const auto *c : m_proxyLockList) { - if (c->output() == controller->output()) { + QString outputId = getOutputId(output); + for (const WallpaperOutputConfig& output : m_wallpaperConfig) { + if (output.outputName == outputId) { return true; } } @@ -58,45 +195,241 @@ bool WallpaperManager::isLocked(const WallpaperController *controller) const return false; } -bool WallpaperManager::isSelfLocked(const WallpaperController *controller) const +QString WallpaperManager::wallpaperConfigToJsonString() { - if (!controller) { - return false; + QJsonArray array; + for (const auto &cfg : m_wallpaperConfig) { + array.append(cfg.toJson()); } - return m_proxyLockList.contains(controller); + QJsonDocument doc(array); + return QString::fromUtf8( + doc.toJson(QJsonDocument::Indented)); } -WallpaperImage *WallpaperManager::get(WAYLIB_SERVER_NAMESPACE::WOutputItem *outputItem) const +void WallpaperManager::setOutputWallpaper(wlr_output *output, [[maybe_unused]] int workspaceIndex, const QString &fileSource, TreelandWallpaperInterfaceV1::WallpaperRoles roles, TreelandWallpaperInterfaceV1::WallpaperType type) { - if (!outputItem) { - return nullptr; + bool update = false; + for (WallpaperOutputConfig &outputConfig : m_wallpaperConfig) { + if (outputConfig.outputName == getOutputId(output)) { + if (roles.testFlag(TreelandWallpaperInterfaceV1::Lockscreen)) { + if (outputConfig.lockscreenWallpaper != fileSource) { + update = true; + outputConfig.lockscreenWallpaper = fileSource; + outputConfig.lockScreenWallpapertype = type; + } + } + + if (roles.testFlag(TreelandWallpaperInterfaceV1::Desktop)) { + for (WallpaperWorkspaceConfig &workspaceConfig : outputConfig.workspaces) { + if (workspaceConfig.desktopWallpaper != fileSource) { + update = true; + workspaceConfig.desktopWallpaper = fileSource; + workspaceConfig.desktopWallpapertype = type; + } + } + } + + break; + } } - return get(outputItem->output()); + if (update) { + Helper::instance()->m_config->setWallpaperConfig(wallpaperConfigToJsonString()); + } } -WallpaperImage *WallpaperManager::get(WAYLIB_SERVER_NAMESPACE::WOutput *output) const +QMap WallpaperManager::globalValidWallpaper(wlr_output *exclusiveOutput, int exclusiveworkspaceId) { - for (auto *proxy : m_proxys.keys()) { - if (proxy->output() == output) { - return m_proxys[proxy]; + QMap wallpapers; + for (const WallpaperOutputConfig& output : m_wallpaperConfig) { + if (!wallpapers.contains(output.lockscreenWallpaper)) { + if (!(exclusiveOutput && + output.outputName == getOutputId(exclusiveOutput)) && output.enable) { + wallpapers.insert(output.lockscreenWallpaper, output.lockScreenWallpapertype); + } + } + for (const WallpaperWorkspaceConfig& workspace : output.workspaces) { + if (exclusiveOutput && + output.outputName == getOutputId(exclusiveOutput) && + workspace.workspaceId == exclusiveworkspaceId) { + continue; + } + + if (!wallpapers.contains(workspace.desktopWallpaper) && workspace.enable) { + wallpapers.insert(workspace.desktopWallpaper, workspace.desktopWallpapertype); + } } } - qCWarning(treelandWallpaper) << "No wallpaper proxy found for output" << output; - return nullptr; + return wallpapers; } -void WallpaperManager::setLock(WallpaperController *controller, bool lock) +void WallpaperManager::syncAddWorkspace() { - if (!controller) { + if (!m_wallpaperConfigUpdated) { return; } - if (lock && !m_proxyLockList.contains(controller)) { - m_proxyLockList.append(controller); - } else if (!lock) { - m_proxyLockList.removeOne(controller); + bool update = false; + for (WallpaperOutputConfig &outputConfig : m_wallpaperConfig) { + WallpaperWorkspaceConfig refConfig = outputConfig.workspaces[0]; + Workspace *workspace = Helper::instance()->workspace(); + Q_ASSERT(workspace); + for (int i = 0; i < workspace->count(); i++) { + if (i >= outputConfig.workspaces.count()) { + WallpaperWorkspaceConfig workspaceConfig; + workspaceConfig.desktopWallpaper = refConfig.desktopWallpaper; + workspaceConfig.desktopWallpapertype = refConfig.desktopWallpapertype; + workspaceConfig.workspaceId = i; + workspaceConfig.enable = true; + outputConfig.workspaces.append(workspaceConfig); + update = true; + } + } + } + + if (update) { + Helper::instance()->m_config->setWallpaperConfig(wallpaperConfigToJsonString()); + } +} + +void WallpaperManager::removeOutputWallpaper(wlr_output *output) +{ + QMap globalWallpapers = globalValidWallpaper(output, -1); + for (int i = 0; i < m_wallpaperConfig.size(); ++i) { + if (m_wallpaperConfig[i].outputName == getOutputId(output)) { + WallpaperOutputConfig &outputConfig = m_wallpaperConfig[i]; + outputConfig.enable = false; + if (!globalWallpapers.contains(outputConfig.lockscreenWallpaper)) { + Helper::instance()->m_wallpaperNotifierInterfaceV1->sendRemove(outputConfig.lockscreenWallpaper); + } + for (WallpaperWorkspaceConfig &workspace : outputConfig.workspaces) { + workspace.enable = false; + if (!globalWallpapers.contains(workspace.desktopWallpaper)) { + Helper::instance()->m_wallpaperNotifierInterfaceV1->sendRemove(workspace.desktopWallpaper); + } + } + break; + } + } +} + +QString WallpaperManager::currentWorkspaceWallpaper(WOutput *output) +{ + Workspace *workspace = Helper::instance()->workspace(); + Q_ASSERT(workspace); + for (int i = 0; i < m_wallpaperConfig.size(); ++i) { + if (m_wallpaperConfig[i].outputName == getOutputId(output->nativeHandle())) { + return m_wallpaperConfig[i].workspaces[workspace->currentIndex()].desktopWallpaper; + } } + + return QStringLiteral("unknown"); } + +TreelandWallpaperInterfaceV1::WallpaperType WallpaperManager::getWallpaperType(const QString &wallpaper) +{ + for (WallpaperOutputConfig outputConfig : std::as_const(m_wallpaperConfig)) { + if (outputConfig.lockscreenWallpaper == wallpaper) { + return outputConfig.lockScreenWallpapertype; + } + + for (WallpaperWorkspaceConfig workspaceConfig : std::as_const(outputConfig.workspaces)) { + if (workspaceConfig.desktopWallpaper == wallpaper) { + return workspaceConfig.desktopWallpapertype; + } + } + } + + qCCritical(treelandWallpaper) << "Failed find wallpaper type for" << wallpaper; + return TreelandWallpaperInterfaceV1::Image; +} + +void WallpaperManager::onWallpaperAdded(TreelandWallpaperInterfaceV1 *interface) +{ + connect(interface, + &TreelandWallpaperInterfaceV1::binded, + this, [this, interface]{ + Workspace *workspace = Helper::instance()->workspace(); + Q_ASSERT(workspace); + for (int i = 0; i < m_wallpaperConfig.size(); ++i) { + if (m_wallpaperConfig[i].outputName == getOutputId(interface->wOutput()->nativeHandle())) { + WallpaperOutputConfig outputConfig = m_wallpaperConfig[i]; + interface->sendChanged(TreelandWallpaperInterfaceV1::Lockscreen, outputConfig.lockScreenWallpapertype, outputConfig.lockscreenWallpaper); + for (WallpaperWorkspaceConfig workspaceConfig : outputConfig.workspaces) { + if (workspaceConfig.workspaceId == workspace->currentIndex()) { + interface->sendChanged(TreelandWallpaperInterfaceV1::Desktop, workspaceConfig.desktopWallpapertype, workspaceConfig.desktopWallpaper); + } + } + break; + } + } + }, Qt::SingleShotConnection); + connect(interface, + &TreelandWallpaperInterfaceV1::imageSourceChanged, + this, + &WallpaperManager::onImageChanged); + connect(interface, + &TreelandWallpaperInterfaceV1::videoSourceChanged, + this, + &WallpaperManager::onVideoChanged); +} + +void WallpaperManager::onImageChanged(int workspaceIndex, const QString &fileSource, TreelandWallpaperInterfaceV1::WallpaperRoles roles) +{ + TreelandWallpaperInterfaceV1 *interface = + static_cast(sender()); + QMap globalWallpapers = globalValidWallpaper(nullptr, -1); + setOutputWallpaper(interface->wOutput()->nativeHandle(), + workspaceIndex, + fileSource, + roles, + TreelandWallpaperInterfaceV1::Image); + if (globalWallpapers.contains(fileSource)) { + interface->sendError(fileSource, TreelandWallpaperInterfaceV1::AlreadyUsed); + Q_EMIT updateWallpaper(); + } else { + Helper::instance()->m_wallpaperNotifierInterfaceV1->sendAdd(TreelandWallpaperInterfaceV1::Image, fileSource); + } +} + +void WallpaperManager::onVideoChanged(int workspaceIndex, const QString &fileSource, TreelandWallpaperInterfaceV1::WallpaperRoles roles) +{ + TreelandWallpaperInterfaceV1 *interface = + static_cast(sender()); + QMap globalWallpapers = globalValidWallpaper(nullptr, -1); + setOutputWallpaper(interface->wOutput()->nativeHandle(), + workspaceIndex, + fileSource, + roles, + TreelandWallpaperInterfaceV1::Video); + if (globalWallpapers.contains(fileSource)) { + interface->sendError(fileSource, TreelandWallpaperInterfaceV1::AlreadyUsed); + Q_EMIT updateWallpaper(); + } else { + Helper::instance()->m_wallpaperNotifierInterfaceV1->sendAdd(TreelandWallpaperInterfaceV1::Video, fileSource); + } +} + +void WallpaperManager::onWallpaperNotifierbinded() +{ + QMap globalWallpapers = globalValidWallpaper(nullptr, -1); + QMapIterator i(globalWallpapers); + while (i.hasNext()) { + i.next(); + Helper::instance()->m_wallpaperNotifierInterfaceV1->sendAdd(static_cast(i.value()), i.key()); + } +} + +void WallpaperManager::handleWallpaperSurfaceAdded([[maybe_unused]] TreelandWallpaperSurfaceInterfaceV1 *interface) +{ + QList wallpapers = Helper::instance()->shellHandler()->wallpaperShell()->producedWallpapers(); + QMap globalWallpapers = globalValidWallpaper(nullptr, -1); + foreach (auto wallpaper, std::as_const(wallpapers)) { + if (!globalWallpapers.contains(wallpaper)) { + Helper::instance()->m_wallpaperNotifierInterfaceV1->sendRemove(wallpaper); + } + } +} + diff --git a/src/wallpaper/wallpapermanager.h b/src/wallpaper/wallpapermanager.h index 94faa683b..73362b60c 100644 --- a/src/wallpaper/wallpapermanager.h +++ b/src/wallpaper/wallpapermanager.h @@ -1,49 +1,63 @@ -// Copyright (C) 2024 Dingyuan Zhang . +// 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 +#include "output/output.h" +#include "wallpaper/wallpaperconfig.h" -#include -#include +#include -namespace WAYLIB_SERVER_NAMESPACE { -class WOutput; -class WOutputItem; -} // namespace WAYLIB_SERVER_NAMESPACE - -class WallpaperImage; -class WallpaperController; +class TreelandWallpaperSurfaceInterfaceV1; class WallpaperManager : public QObject { Q_OBJECT - explicit WallpaperManager(QQuickItem *parent = nullptr); - ~WallpaperManager(); - public: - static WallpaperManager *instance(); - -private: - friend class WallpaperImage; - void add(WallpaperImage *proxy, WAYLIB_SERVER_NAMESPACE::WOutputItem *outputItem); - void remove(WallpaperImage *proxy); - void remove(WAYLIB_SERVER_NAMESPACE::WOutputItem *outputItem); - -private: - friend class WallpaperImage; - friend class WallpaperController; - bool isLocked(const WallpaperController *controller) const; - bool isSelfLocked(const WallpaperController *controller) const; - -private: - friend class WallpaperController; - WallpaperImage *get(WAYLIB_SERVER_NAMESPACE::WOutputItem *outputItem) const; - WallpaperImage *get(WAYLIB_SERVER_NAMESPACE::WOutput *output) const; - void setLock(WallpaperController *controller, bool lock); + enum UpdateReason + { + Normal, + Scale, + }; + + explicit WallpaperManager(QObject *parent = nullptr); + ~WallpaperManager() override; + static QString getOutputId(wlr_output *output); + static QString getOutputId(Output *output); + WallpaperOutputConfig getOutputConfig(Output *output); + WallpaperOutputConfig getOutputConfig(wlr_output *output); + WallpaperOutputConfig getOutputConfig(const QString &id); + void defaultWallpaperConfig(); + void ensureWallpaperConfigForOutput(Output *output); + bool configContainsOutput(Output *output); + QString wallpaperConfigToJsonString(); + void setOutputWallpaper(wlr_output *output, + int workspaceIndex, + const QString &fileSource, + TreelandWallpaperInterfaceV1::WallpaperRoles roles, + TreelandWallpaperInterfaceV1::WallpaperType type); + QMap globalValidWallpaper(wlr_output *exclusiveOutput, int exclusiveworkspaceId); + void syncAddWorkspace(); + void removeOutputWallpaper(wlr_output *output); + QString currentWorkspaceWallpaper(WOutput *output); + TreelandWallpaperInterfaceV1::WallpaperType getWallpaperType(const QString &wallpaper); + +Q_SIGNALS: + void updateWallpaper(); + +public Q_SLOTS: + void updateWallpaperConfig(); + void onWallpaperAdded(TreelandWallpaperInterfaceV1 *interface); + void onImageChanged(int workspaceIndex, + const QString &fileSource, + TreelandWallpaperInterfaceV1::WallpaperRoles roles); + void onVideoChanged(int workspaceIndex, + const QString &fileSource, + TreelandWallpaperInterfaceV1::WallpaperRoles roles); + void onWallpaperNotifierbinded(); + void handleWallpaperSurfaceAdded(TreelandWallpaperSurfaceInterfaceV1 *interface); private: - QMap m_proxys; - QList m_proxyLockList; + bool m_wallpaperConfigUpdated { false }; + QList m_wallpaperConfig; }; diff --git a/src/wallpaper/wallpapersurface.cpp b/src/wallpaper/wallpapersurface.cpp new file mode 100644 index 000000000..1a2c9e925 --- /dev/null +++ b/src/wallpaper/wallpapersurface.cpp @@ -0,0 +1,78 @@ +// 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 "wallpapersurface.h" +#include "wallpapershellinterfacev1.h" +#include "private/wtoplevelsurface_p.h" +#include "private/wglobal_p.h" + +#include + +class Q_DECL_HIDDEN WallpaperSurfacePrivate : public WToplevelSurfacePrivate { +public: + WallpaperSurfacePrivate(WallpaperSurface *qq, TreelandWallpaperSurfaceInterfaceV1 *surface); + ~WallpaperSurfacePrivate(); + + wl_client *waylandClient() const override { + return interface->client(); + } + void instantRelease() override { + } + + WSurface *surface = nullptr; + TreelandWallpaperSurfaceInterfaceV1 *interface = nullptr; +}; + +WallpaperSurfacePrivate::WallpaperSurfacePrivate(WallpaperSurface *qq, TreelandWallpaperSurfaceInterfaceV1 *surface) + : WToplevelSurfacePrivate(qq) + , interface(surface) +{ +} + +WallpaperSurfacePrivate::~WallpaperSurfacePrivate() +{ +} + +WallpaperSurface::WallpaperSurface(TreelandWallpaperSurfaceInterfaceV1 *handle, QObject *parent) + : WToplevelSurface(*new WallpaperSurfacePrivate(this, handle), parent) +{ + init(); +} + +WallpaperSurface::~WallpaperSurface() = default; + +WSurface *WallpaperSurface::surface() const +{ + W_DC(WallpaperSurface); + return d->surface; +} + +QRect WallpaperSurface::getContentGeometry() const +{ + W_DC(WallpaperSurface); + wlr_surface *surface = wlr_surface_from_resource(d->interface->surfaceResource()); + if (!surface) { + return QRect(); + } + + return QRect(0, 0, surface->current.width, surface->current.height); +} + +bool WallpaperSurface::checkNewSize(const QSize &size, QSize *clipedSize) +{ + if (size.width() < 0 || size.height() < 0) { + if (clipedSize) + *clipedSize = QSize(0, 0); + return false; + } + + return true; +} + +void WallpaperSurface::init() +{ + W_D(WallpaperSurface); + + d->surface = new WSurface(qw_surface::from(wlr_surface_from_resource(d->interface->surfaceResource())), this); + d->surface->setAttachedData(this); +} diff --git a/src/wallpaper/wallpapersurface.h b/src/wallpaper/wallpapersurface.h new file mode 100644 index 000000000..b35db775d --- /dev/null +++ b/src/wallpaper/wallpapersurface.h @@ -0,0 +1,39 @@ +// 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 +#include + +WAYLIB_SERVER_USE_NAMESPACE +QW_USE_NAMESPACE + +class TreelandWallpaperSurfaceInterfaceV1; +class WallpaperSurfacePrivate; +class WAYLIB_SERVER_EXPORT WallpaperSurface : public WToplevelSurface +{ + Q_OBJECT + W_DECLARE_PRIVATE(WallpaperSurface) + Q_PROPERTY(WSurface *surface READ surface NOTIFY surfaceChanged) + + QML_NAMED_ELEMENT(WallpaperSurface) + QML_UNCREATABLE("Only create in C++") + +public: + explicit WallpaperSurface(TreelandWallpaperSurfaceInterfaceV1 *handle, + QObject *parent = nullptr); + ~WallpaperSurface(); + + WSurface *surface() const override; + QRect getContentGeometry() const override; + +Q_SIGNALS: + void surfaceChanged(); + +public Q_SLOTS: + bool checkNewSize(const QSize &size, QSize *clipedSize = nullptr) override; + +private: + void init(); +}; diff --git a/src/workspace/workspace.cpp b/src/workspace/workspace.cpp index 2742df91d..bf6c821f0 100644 --- a/src/workspace/workspace.cpp +++ b/src/workspace/workspace.cpp @@ -159,6 +159,7 @@ int Workspace::createModel(const QString &name, bool visible) auto id = doCreateModel(name, visible); Helper::instance()->config()->setNumWorkspace(count()); Q_EMIT countChanged(); + Q_EMIT workspaceAdded(); return id; } diff --git a/src/workspace/workspace.h b/src/workspace/workspace.h index 548dc41f5..0681207f3 100644 --- a/src/workspace/workspace.h +++ b/src/workspace/workspace.h @@ -91,6 +91,7 @@ class Workspace : public SurfaceContainer void currentFilterChanged(); void currentIndexChanged(); void countChanged(); + void workspaceAdded(); private: int nextWorkspaceId() const; diff --git a/waylib/src/server/kernel/private/wglobal_p.h b/waylib/src/server/kernel/private/wglobal_p.h index e8ab7b8a3..a610206c0 100644 --- a/waylib/src/server/kernel/private/wglobal_p.h +++ b/waylib/src/server/kernel/private/wglobal_p.h @@ -8,7 +8,7 @@ WAYLIB_SERVER_BEGIN_NAMESPACE -class Q_DECL_HIDDEN WObjectPrivate +class Q_DECL_EXPORT WObjectPrivate { public: static WObjectPrivate *get(WObject *qq); @@ -34,7 +34,7 @@ class Q_DECL_HIDDEN WObjectPrivate W_DECLARE_PUBLIC(WObject) }; -class Q_DECL_HIDDEN WWrapObjectPrivate : public WObjectPrivate +class Q_DECL_EXPORT WWrapObjectPrivate : public WObjectPrivate { public: WWrapObjectPrivate(WWrapObject *q); diff --git a/waylib/src/server/kernel/private/wtoplevelsurface_p.h b/waylib/src/server/kernel/private/wtoplevelsurface_p.h index f4026c5bb..0abe34bd4 100644 --- a/waylib/src/server/kernel/private/wtoplevelsurface_p.h +++ b/waylib/src/server/kernel/private/wtoplevelsurface_p.h @@ -7,7 +7,7 @@ WAYLIB_SERVER_BEGIN_NAMESPACE -class Q_DECL_HIDDEN WToplevelSurfacePrivate : public WWrapObjectPrivate +class Q_DECL_EXPORT WToplevelSurfacePrivate : public WWrapObjectPrivate { public: inline WToplevelSurfacePrivate(WToplevelSurface *q) diff --git a/waylib/src/server/qtquick/wsurfaceitem.cpp b/waylib/src/server/qtquick/wsurfaceitem.cpp index 15e268d93..33b0d5095 100644 --- a/waylib/src/server/qtquick/wsurfaceitem.cpp +++ b/waylib/src/server/qtquick/wsurfaceitem.cpp @@ -260,7 +260,8 @@ class Q_DECL_HIDDEN WSurfaceItemContentPrivate: public QQuickItemPrivate q, [this, q] (const QList> committedOutputs) { lastRendered = rendered; if (Q_LIKELY((rendered || q->isVisible()) && live) - && committedOutputs.contains(surface->framePacingOutput())) { + && surface && + committedOutputs.contains(surface->framePacingOutput())) { surface->notifyFrameDone(); rendered = false; }