Skip to content

Upgrade treeland-ddm protocol and unify GreeterProxy with Wayland#810

Draft
wineee wants to merge 4 commits intolinuxdeepin:masterfrom
wineee:animations
Draft

Upgrade treeland-ddm protocol and unify GreeterProxy with Wayland#810
wineee wants to merge 4 commits intolinuxdeepin:masterfrom
wineee:animations

Conversation

@wineee
Copy link
Copy Markdown
Member

@wineee wineee commented Mar 30, 2026

Summary by Sourcery

Migrate Treeland to the new treeland-ddm v2 protocol, integrating DDM over Wayland instead of a custom socket, while making session IDs string-based and adding configuration-controlled animations for window, workspace, and desktop transitions.

New Features:

  • Introduce DDMInterfaceV2 Wayland server wrapper for the treeland-ddm v2 protocol and wire it into the compositor and greeter.
  • Allow GreeterProxy to communicate with DDM via the new Wayland-based interface, including capability reporting and authentication error handling.

Bug Fixes:

  • Ensure session identification uses string-based IDs consistently, avoiding integer conversions that could break logind integration.

Enhancements:

  • Replace legacy socket-based DDM communication in the greeter with Wayland-driven DDMInterfaceV2 signals and methods, improving robustness and alignment with the compositor.
  • Gate various window, launchpad, layer shell, show-desktop, minimize, maximize, and workspace animations behind configuration flags so they can be disabled.
  • Prevent unwanted updates of a window's normal geometry during programmatic state changes by tracking and ignoring intermediate geometry updates when applying state geometry directly.

Build:

  • Update the DDM module build to generate and link against the treeland-ddm v2 protocol definitions instead of v1.

calsys456 and others added 4 commits March 30, 2026 15:51
In this upgrade we rewrite all treeland-ddm interactions, includes those
originally in SocketServer / GreeterProxy, into wayland communication.
There's only one true treeland-ddm interaction which should on behalf of
the Wayland protocol.
With the upgrade we did in previous commit, communication with ddm using
QLocalSocket is not needed now, and we shall reform GreeterProxy, the
communication method with our display manager the DDM, to use the newly
formed Wayland protocol.
Although the logind session id is exactly an integer in current
systemd-logind, but this is an UB according to systemd documentation.
Systemd advertise us to treat it as a string and we shall adapt it.
The compositor now provides separate user-level switches for
workspace, maximize, minimize, show desktop, launchpad,
layer shell, and window open/close animations.

When an animation is disabled, treeland now applies the target
state immediately instead of keeping the old window geometry or
creating the workspace switcher black background.

Log: support disabling compositor animations

Influence:
@deepin-ci-robot
Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: wineee

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 30, 2026

Reviewer's Guide

Replaces the legacy QLocalSocket-based DDM greeter protocol with a new Treeland Wayland-based treeland-ddm v2 interface, propagates session IDs to be strings instead of ints, and adds fine‑grained configuration-driven control over window/workspace animations and state transitions across the compositor.

Sequence diagram for Wayland-based DDM login via GreeterProxy

sequenceDiagram
    actor User
    participant GreeterUI
    participant GreeterProxy
    participant DDMInterfaceV2 as DDMInterfaceV2
    participant DDM as DDM_Daemon
    participant Logind as Logind_Manager
    participant Helper
    participant SessionManager
    participant UserModel
    participant SessionModel

    User->>GreeterUI: Enter credentials
    GreeterUI->>GreeterProxy: login(user, password, sessionIndex)

    alt DDMInterfaceV2 isValid
        GreeterProxy->>SessionModel: index(sessionIndex, 0)
        SessionModel-->>GreeterProxy: sessionType, sessionFile
        GreeterProxy->>DDMInterfaceV2: login(user, password, sessionType, sessionFile)
        DDMInterfaceV2-->>DDM: treeland_ddm_v2.login

        DDM-->>Logind: Create session
        Logind-->>DDM: Session created(id, username)
        DDM-->>DDMInterfaceV2: user_logged_in(username, session)
        DDMInterfaceV2-->>GreeterProxy: userLoggedIn(username, session)

        GreeterProxy->>UserModel: updateUserLoginState(username, true)
        GreeterProxy->>UserModel: userLoggedIn(username, session)
        UserModel-->>SessionManager: updateActiveUserSession(username, session)
        SessionManager-->>Helper: activeSession changed
    else DDMInterfaceV2 invalid
        GreeterProxy->>GreeterProxy: localValidation(user, password)
        alt localValidation success
            GreeterProxy->>GreeterProxy: setLock(false)
        else localValidation failed
            GreeterProxy-->>GreeterUI: failedAttemptsChanged
        end
    end
Loading

Class diagram for new DDMInterfaceV2-based greeter/session integration

classDiagram
    class DDMInterfaceV2 {
        <<QObject, WServerInterface>>
        +QByteArrayView interfaceName()
        +void setHandle(handle: wl_resource*)
        +static QString authErrorToString(error: uint32_t)
        +void login(username: QString, password: QString, sessionType: DDM_Session_Type, sessionFile: QString)
        +void logout(session: QString)
        +void lock(session: QString)
        +void unlock(session: QString, password: QString)
        +void powerOff()
        +void reboot()
        +void suspend()
        +void hibernate()
        +void hybridSleep()
        +void switchToVt(vtnr: int)
        +void connected()
        +void disconnected()
        +void capabilities(capabilities: uint32_t)
        +void userLoggedIn(username: QString, session: QString)
        +void authenticationFailed(error: uint32_t)
        -wl_global* m_global
        -wl_resource* m_handle
    }

    class GreeterProxy {
        +GreeterProxy(parent: QObject*)
        +~GreeterProxy()
        +void connectDDM(interface: DDMInterfaceV2*)
        +void powerOff()
        +void reboot()
        +void suspend()
        +void hibernate()
        +void hybridSleep()
        +void login(user: QString, password: QString, sessionIndex: int)
        +void unlock(user: QString, password: QString)
        +void logout()
        +void lock()
        +void capabilities(capabilities: uint32_t)
        +void userLoggedIn(username: QString, session: QString)
        +void authenticationFailed(error: uint32_t)
        +void onSessionNew(id: QString, path: QDBusObjectPath)
        +void onSessionRemoved(id: QString, path: QDBusObjectPath)
        +void onSessionLock()
        +void onSessionUnlock()
        -DDMInterfaceV2* m_ddmInterface
        -LockScreen* m_lockScreen
        -QString m_hostName
        -bool m_canPowerOff
        -bool m_canReboot
        -bool m_canSuspend
        -bool m_canHibernate
        -bool m_canHybridSleep
        -int m_failedAttempts
        -bool m_showShutdownView
        -bool m_showAnimation
        -bool m_hasActiveSession
    }

    class Helper {
        +static Helper* instance()
        +void init(treeland: Treeland)
        +UserModel* userModel() const
        +SessionModel* sessionModel() const
        +DDMInterfaceV2* ddmInterfaceV2() const
        +SessionManager* sessionManager() const
        +void showLockScreen(withAnimation: bool)
        +void activateSession()
        +void deactivateSession()
        +void enableRender()
        +void disableRender()
        -DDMInterfaceV2* m_ddmInterfaceV2
        -GreeterProxy* m_greeterProxy
        -SessionManager* m_sessionManager
        -UserModel* m_userModel
        -SessionModel* m_sessionModel
    }

    class Session {
        +~Session()
        +QString id() const
        +uid_t uid() const
        +const QString& username() const
        +WSocket* socket() const
        -QString m_id
        -uid_t m_uid
        -QString m_username
        -WSocket* m_socket
        -WXWayland* m_xwayland
    }

    class SessionManager {
        +bool activeSocketEnabled() const
        +void setActiveSocketEnabled(newEnabled: bool)
        +void updateActiveUserSession(username: QString, id: QString)
        +void removeSession(session: shared_ptr~Session~)
        +shared_ptr~Session~ sessionForId(id: QString) const
        +shared_ptr~Session~ sessionForUid(uid: uid_t) const
        +shared_ptr~Session~ sessionForUser(username: QString) const
        +shared_ptr~Session~ sessionForXWayland(xwayland: WXWayland*) const
        +shared_ptr~Session~ activeSession() const
        +void sessionChanged()
        -shared_ptr~Session~ ensureSession(id: QString, username: QString)
        -weak_ptr~Session~ m_activeSession
        -QList~shared_ptr~Session~~ m_sessions
    }

    class UserModel {
        +QString currentUserName() const
        +void setCurrentUserName(username: QString)
        +void updateUserLoginState(username: QString, loggedIn: bool)
        +QSharedPointer~User~ getUser(username: QString) const
        +void userLoggedIn(username: QString, session: QString)
    }

    DDMInterfaceV2 ..|> Waylib_Server_WServerInterface
    GreeterProxy --> DDMInterfaceV2 : uses
    Helper --> DDMInterfaceV2 : owns
    Helper --> GreeterProxy : owns
    Helper --> SessionManager : owns
    Helper --> UserModel : owns
    SessionManager --> Session : manages
    GreeterProxy --> Helper : uses
    GreeterProxy --> UserModel : uses
    GreeterProxy --> SessionModel : uses
    GreeterProxy --> SessionManager : uses
    Helper ..> WServer : attach
Loading

Class diagram for animation and window state control in SurfaceWrapper and workspace

classDiagram
    class SurfaceWrapper {
        +SurfaceWrapper(qmlEngine: QmlEngine*, container: SurfaceContainer*, shellSurface: QObject*, appId: QString, parent: QQuickItem*)
        +SurfaceWrapper(original: SurfaceWrapper*, parent: QQuickItem*)
        +State previousSurfaceState() const
        +State surfaceState() const
        +void setSurfaceState(newSurfaceState: State, performAnimation: bool = true)
        +bool isNormal() const
        +bool isMaximized() const
        +bool isMinimized() const
        +void requestMinimize(onAnimation: bool = true)
        +void requestCancelMinimize(onAnimation: bool = true)
        +void requestMaximize(onAnimation: bool = true)
        +void requestCancelMaximize(onAnimation: bool = true)
        +void requestToggleMaximize(onAnimation: bool = true)
        +void requestFullscreen()
        +void requestCancelFullscreen()
        +void geometryChange(newGeo: QRectF, oldGeo: QRectF)
        +void startMinimizeAnimation(iconGeometry: QRectF, direction: uint)
        +void startMaximizeAnimation(geometry: QRectF, direction: uint)
        +void startShowDesktopAnimation(show: bool)
        +void onMinimizeAnimationFinished()
        +void onShowDesktopAnimationFinished()
        +void applyStateGeometry(targetGeometry: QRectF)
        -void doSetSurfaceState(newSurfaceState: State)
        -void startStateChangeAnimation(newSurfaceState: State, targetGeometry: QRectF)
        -void updateClipRect()
        -QPointer~QAbstractAnimation~ m_geometryAnimation
        -QPointer~QAbstractAnimation~ m_windowAnimation
        -QPointer~QAbstractAnimation~ m_showDesktopAnimation
        -State m_surfaceState
        -State m_previousSurfaceState
        -QRectF m_normalGeometry
        -uint m_hideByshowDesk : 1
        -uint m_hideByLockScreen : 1
        -uint m_confirmHideByLockScreen : 1
        -uint m_ignoreNormalGeometryUpdate : 1
        -uint m_blur : 1
        -uint m_isActivated : 1
        -SurfaceRole m_surfaceRole
    }

    class WorkspaceAnimationController {
        +void slide(fromWorkspaceIndex: uint, toWorkspaceIndex: uint)
        +void bounce(currentWorkspaceIndex: uint, direction: Direction)
        +void startSlideAnimation()
        +void slideRunning(toWorkspaceIndex: uint)
        +void slideNormal(fromWorkspaceIndex: uint, toWorkspaceIndex: uint)
        -QAbstractAnimation* m_slideAnimation
        -QAbstractAnimation* m_bounceAnimation
        -QPointF m_animationDestination
        -bool m_needBounce
    }

    class Workspace {
        +void updateSurfacesOwnsOutput()
        +void createSwitcher()
        -bool m_switcherEnabled
        -QObject* m_switcher
    }

    class Helper {
        +static Helper* instance()
        +Config* config() const
        +QmlEngine* qmlEngine() const
    }

    class Config {
        +bool enableMaximizeAnimation() const
        +bool enableWindowOpenCloseAnimation() const
        +bool enableLaunchpadAnimation() const
        +bool enableLayerShellAnimation() const
        +bool enableShowDesktopAnimation() const
        +bool enableMinimizeAnimation() const
        +bool enableWorkspaceAnimation() const
    }

    class QmlEngine {
        +QAbstractAnimation* createNewAnimation(surface: SurfaceWrapper*, container: SurfaceContainer*, direction: uint)
        +QAbstractAnimation* createLaunchpadAnimation(surface: SurfaceWrapper*, direction: uint, container: SurfaceContainer*)
        +QAbstractAnimation* createLayerShellAnimation(surface: SurfaceWrapper*, container: SurfaceContainer*, direction: uint)
        +QAbstractAnimation* createShowDesktopAnimation(surface: SurfaceWrapper*, container: SurfaceContainer*, show: bool)
        +QQuickItem* createWorkspaceSwitcher(workspace: Workspace*)
    }

    SurfaceWrapper --> Helper : uses config()
    SurfaceWrapper --> QmlEngine : uses animations
    WorkspaceAnimationController --> Helper : uses config()
    Workspace --> Helper : uses config(), qmlEngine()
    Helper --> Config : owns
    Helper --> QmlEngine : owns
Loading

File-Level Changes

Change Details Files
Migrate greeter DDM communication from custom socket protocol to treeland-ddm v2 Wayland interface and integrate with Helper/Session models.
  • Remove QLocalSocket, SocketWriter, and custom DaemonMessages handling from GreeterProxy and associated auth socket management
  • Introduce DDMInterfaceV2 connection in GreeterProxy with a connectDDM method wiring capabilities/userLoggedIn/authenticationFailed signals
  • Update power/session control slots (powerOff/reboot/suspend/hibernate/hybridSleep/login/unlock/logout/lock) to call DDMInterfaceV2 methods when available and fall back to localValidation/lockscreen otherwise
  • Handle capabilities, DDM-driven userLoggedIn, and authenticationFailed in GreeterProxy, including wiring of logind Lock/Unlock DBus signals for recovered sessions
  • Wire Helper to attach DDMInterfaceV2 from the Wayland server, connect it to GreeterProxy, and use it for VT switching instead of DDMInterfaceV1
  • Remove DDMInterfaceV1 files and references, and update CMake to generate and use treeland-ddm-v2 protocol and ddminterfacev2 sources
  • Change UserModel::userLoggedIn signal and SessionManager/Session APIs to use QString session IDs consistently, adjusting logind and helper usage accordingly
src/greeter/greeterproxy.cpp
src/greeter/greeterproxy.h
src/seat/helper.cpp
src/seat/helper.h
src/session/session.cpp
src/session/session.h
src/greeter/usermodel.h
src/modules/ddm/CMakeLists.txt
src/modules/ddm/ddminterfacev2.cpp
src/modules/ddm/ddminterfacev2.h
src/modules/ddm/ddminterfacev1.cpp
src/modules/ddm/ddminterfacev1.h
Add treeland-ddm v2 server-side interface implementation with Wayland bindings and event helpers.
  • Implement DDMInterfaceV2 as a WServerInterface that advertises the treeland-ddm-v2 global, binds client resources, and tracks the current wl_resource handle
  • Provide Wayland request handlers for capabilities, user_logged_in, authentication_failed, greeter/user switching, DRM/render control, and session activation/deactivation
  • Offer public slots on DDMInterfaceV2 that wrap treeland_ddm_v2_send_* events for login/logout/lock/unlock, power actions, suspend/hibernate/hybrid sleep, and VT switching, with isValid() checks and logging
  • Emit connected/disconnected and higher-level signals (capabilities/userLoggedIn/authenticationFailed) to integrate into Qt-side greeter logic
src/modules/ddm/ddminterfacev2.cpp
src/modules/ddm/ddminterfacev2.h
Make window state changes and workspace/window/launchpad/layer-shell/show-desktop animations configurable and more robust in SurfaceWrapper and WorkspaceAnimationController/Workspace.
  • Extend SurfaceWrapper constructors to initialize an m_ignoreNormalGeometryUpdate flag and add this bitfield member
  • Change setSurfaceState to accept a performAnimation flag, skip maximize animations if disabled in config, and apply geometry directly via new applyStateGeometry when not animating
  • Use m_ignoreNormalGeometryUpdate to prevent feedback loops when applying state geometry and to avoid updating normal geometry while programmatically resizing/moving
  • Guard normal-state geometry updates in geometryChange with m_ignoreNormalGeometryUpdate
  • Add config checks (enableWindowOpenCloseAnimation, enableLaunchpadAnimation, enableLayerShellAnimation, enableShowDesktopAnimation, enableMinimizeAnimation) around createNewOrClose, startShowDesktopAnimation, and minimize/cancel-minimize paths
  • Update requestMaximize/requestCancelMaximize/requestToggleMaximize signatures to carry an onAnimation flag and propagate it into setSurfaceState
  • Declare startMaximizeAnimation in the header (implementation likely elsewhere) and add applyStateGeometry method to header
  • Gate workspace slide and bounce animations and switcher creation on enableWorkspaceAnimation; when disabled, jump viewport position directly without animation
src/surface/surfacewrapper.cpp
src/surface/surfacewrapper.h
src/workspace/workspaceanimationcontroller.cpp
src/workspace/workspace.cpp

Possibly linked issues

  • #Supports TTY switch: PR adds DDMInterfaceV2 and uses switchToVt for Ctrl+Alt+Fx, directly implementing the issue’s TTY switching support.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants