From e00774889b9de6f7582e175572c580845bed6946 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Tue, 10 Feb 2026 11:30:15 +0800 Subject: [PATCH] feat: implement wallpaper management system 1. Add wallpaper configuration management with support for multiple screens and workspaces 2. Implement TreelandWallpaperShellInterfaceV1 protocol for wallpaper communication 3. Replace WallpaperController with new WallpaperItem component 4. Add wallpaper launcher for managing wallpaper processes 5. Support both desktop and lockscreen wallpapers with different roles 6. Add configuration persistence through DConfig 7. Implement wallpaper type detection (image/video) 8. Add workspace synchronization for wallpaper configuration Log: Added advanced wallpaper management system with multi-screen and workspace support Influence: 1. Test wallpaper loading on different outputs and workspaces 2. Verify lockscreen wallpaper functionality 3. Test wallpaper configuration changes through DConfig 4. Check wallpaper process management and lifecycle 5. Verify wallpaper protocol communication 6. Test multi-user wallpaper scenarios 7. Validate workspace switching with wallpaper changes 8. Check error handling for invalid wallpaper files --- .../dconfig/org.deepin.dde.treeland.user.json | 11 + misc/systemd/treeland.service.in | 1 + src/CMakeLists.txt | 12 +- src/core/qml/Effects/LaunchpadCover.qml | 21 +- src/core/qml/PrimaryOutput.qml | 49 +- src/core/qml/WorkspaceSwitcher.qml | 16 +- src/core/shellhandler.cpp | 11 + src/core/shellhandler.h | 7 + .../wallpaper/wallpapershellinterfacev1.cpp | 27 +- .../wallpaper/wallpapershellinterfacev1.h | 3 + src/plugins/lockscreen/qml/Greeter.qml | 113 +++-- .../multitaskview/qml/MultitaskviewProxy.qml | 18 +- .../qml/WorkspaceSelectionList.qml | 15 +- src/seat/helper.cpp | 57 +++ src/seat/helper.h | 22 +- src/session/session.cpp | 31 +- src/session/session.h | 3 + src/wallpaper/wallpaperconfig.cpp | 75 ++++ src/wallpaper/wallpaperconfig.h | 36 ++ src/wallpaper/wallpapercontroller.cpp | 96 ---- src/wallpaper/wallpapercontroller.h | 69 --- src/wallpaper/wallpaperimage.cpp | 102 ----- src/wallpaper/wallpaperimage.h | 50 --- src/wallpaper/wallpaperitem.cpp | 233 ++++++++++ src/wallpaper/wallpaperitem.h | 95 ++++ src/wallpaper/wallpaperlauncher.cpp | 132 ++++++ src/wallpaper/wallpaperlauncher.h | 43 ++ src/wallpaper/wallpapermanager.cpp | 423 ++++++++++++++++-- src/wallpaper/wallpapermanager.h | 84 ++-- src/wallpaper/wallpapersurface.cpp | 78 ++++ src/wallpaper/wallpapersurface.h | 39 ++ src/workspace/workspace.cpp | 1 + src/workspace/workspace.h | 1 + waylib/src/server/kernel/private/wglobal_p.h | 4 +- .../kernel/private/wtoplevelsurface_p.h | 2 +- waylib/src/server/qtquick/wsurfaceitem.cpp | 3 +- 36 files changed, 1508 insertions(+), 475 deletions(-) create mode 100644 src/wallpaper/wallpaperconfig.cpp create mode 100644 src/wallpaper/wallpaperconfig.h delete mode 100644 src/wallpaper/wallpapercontroller.cpp delete mode 100644 src/wallpaper/wallpapercontroller.h delete mode 100644 src/wallpaper/wallpaperimage.cpp delete mode 100644 src/wallpaper/wallpaperimage.h create mode 100644 src/wallpaper/wallpaperitem.cpp create mode 100644 src/wallpaper/wallpaperitem.h create mode 100644 src/wallpaper/wallpaperlauncher.cpp create mode 100644 src/wallpaper/wallpaperlauncher.h create mode 100644 src/wallpaper/wallpapersurface.cpp create mode 100644 src/wallpaper/wallpapersurface.h 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; }