Skip to content

feat(window-transition): implement window transition protocol - #1403

Closed
deepin-wm wants to merge 1 commit into
linuxdeepin:masterfrom
deepin-wm:feat/animation-new
Closed

deepin-wm wants to merge 1 commit into
linuxdeepin:masterfrom
deepin-wm:feat/animation-new

Conversation

@deepin-wm

@deepin-wm deepin-wm commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

feat(window-transition): implement window transition protocol

Add the server-side implementation of treeland-window-transition-unstable-v1,
which plays a window open/close transition relative to a source rectangle
attached to an xdg-activation token.

A client attaches a persistent transition rectangle (geometry plus an
optional source image) to an xdg_activation_token_v1 before committing the
token. At activation the compositor associates the rectangle with the target
window, animating from the rectangle's global position on open and back to it
on close. The rectangle stays alive, so set_geometry and set_source_buffer
update it immediately.

新增 treeland-window-transition-unstable-v1 的服务端实现,基于关联到
xdg-activation token 的源矩形播放窗口打开/关闭转场。

客户端在提交 token 前挂载一个持久的转场矩形(几何信息及可选源图像)。
激活时合成器将矩形关联到目标窗口,打开时从矩形的全局位置播放动画,
关闭时过渡回该矩形。矩形持续存活,set_geometry / set_source_buffer
可立即更新。

Code Review Fixes

All 4 review issues resolved:

  1. wl_buffer.release — reverted to original wlr_buffer_unlock (confirmed correct via wlroots source)
  2. Removed #include <QtCore> from surfacewrapper.cpp (already has QTimer and QVariant)
  3. QML WindowTransition.qml — replaced opacity: 0 + Component.onCompleted with direct binding opacity: root.direction === 1 ? 1 : 0
  4. QtQuick.Effects import — no change needed (gated by enableBlur property)

Note

This PR supersedes #1394 due to push permission limitations on the original fork (glyvut/treeland). The branch is pushed to deepin-wm/treeland fork instead.

WM-480

Summary by Sourcery

Implement server-side window transitions driven by persistent activation-token source rectangles and optional source images.

New Features:

  • Implement the server-side treeland-window-transition-unstable-v1 protocol for associating persistent source rectangles and optional images with activated windows.
  • Animate window opening and closing between the target window and its activation source rectangle, including support for delayed activation and live rectangle updates.
  • Add a Qt client example demonstrating activation-token-based window transitions, source images, modal windows, and cross-process launches.

Bug Fixes:

  • Preserve activation requests for unmapped windows so they can be applied when the window becomes ready.
  • Fall back safely to existing animations when transition sources, targets, or activation timing are invalid.

Enhancements:

  • Integrate window transitions with surface lifecycle, activation handling, buffer ownership, and QML animation components.
  • Add dedicated logging for the window-transition module.

Build:

  • Register the window-transition module, QML component, protocol generation, and example application in the build.

Tests:

  • Add an interactive window-transition example application for validating activation, geometry updates, optional source buffers, and animation behavior.

Add the server-side implementation of treeland-window-transition-unstable-v1,
which plays a window open/close transition relative to a source rectangle
attached to an xdg-activation token.

A client attaches a persistent transition rectangle (geometry plus an
optional source image) to an xdg_activation_token_v1 before committing the
token. At activation the compositor associates the rectangle with the target
window, animating from the rectangle's global position on open and back to it
on close. The rectangle stays alive, so set_geometry and set_source_buffer
update it immediately.

新增 treeland-window-transition-unstable-v1 的服务端实现,基于关联到
xdg-activation token 的源矩形播放窗口打开/关闭转场。

客户端在提交 token 前挂载一个持久的转场矩形(几何信息及可选源图像)。
激活时合成器将矩形关联到目标窗口,打开时从矩形的全局位置播放动画,
关闭时过渡回该矩形。矩形持续存活,set_geometry / set_source_buffer
可立即更新。

Log: 实现窗口转场协议,基于激活 token 的源矩形播放开/关转场
Influence: 新增窗口转场模块、QML 动画组件及 test-window-transition 样例;
激活流程支持矩形关联并播放开/关动画。
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: deepin-wm

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

@deepin-wm
deepin-wm marked this pull request as draft September 14, 2026 10:06
@sourcery-ai

sourcery-ai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements treeland-window-transition-unstable-v1 end to end: clients attach persistent geometry and optional source buffers to xdg-activation tokens, the compositor carries that metadata through activation, associates it with the target surface, and runs QML-based open/close animations with lifecycle, expiration, fallback, and resource cleanup handling; a Qt test client demonstrates the protocol.

Sequence diagram for window transition token activation

sequenceDiagram
    participant Client
    participant Activation as ActivationManagerInterfaceV1
    participant Transition as WindowTransitionManagerInterfaceV1
    participant Helper
    participant Target as SurfaceWrapper
    participant QML as WindowTransition

    Client->>Transition: get_window_transition_rect(token)
    Client->>Transition: set_geometry(x, y, width, height)
    Client->>Transition: set_source_buffer(buffer)
    Client->>Activation: commit()
    Activation->>Transition: takeCommittedRect(token, tokenResource)
    Client->>Activation: activate(token, targetSurface)
    Activation->>Helper: activateRequested(token, disposition, target, seat, origin)
    Helper->>Transition: associatePendingRect(token, target, origin)
    Transition->>Target: setWindowTransitionRect(rect, origin)
    Transition->>Target: setWindowTransitionSourceImage(image)
    Target->>QML: createWindowTransition(fromGeometry, toGeometry, sourceBuffer, OPEN_ANIMATION)
    QML-->>Target: finished
    Target-->>Client: window opens from source rectangle
Loading

Sequence diagram for window transition close animation

sequenceDiagram
    participant Target as SurfaceWrapper
    participant QML as WindowTransition
    participant Origin as SourceSurface
    participant Client

    Target->>Target: computeGlobalWindowTransitionRect()
    Target->>QML: createWindowTransition(fromGeometry, toGeometry, sourceBuffer, CLOSE_ANIMATION)
    QML->>QML: start()
    QML-->>Target: finished
    Target->>Target: onHideAnimationFinished()
    Target->>Target: dropWindowTransitionSourceBuffer()
    Target-->>Client: window closes toward source rectangle
Loading

State diagram for transition rectangle lifecycle

stateDiagram-v2
    [*] --> Created: get_window_transition_rect
    Created --> Committed: set_geometry
    Committed --> Pending: commit token
    Pending --> Associated: activate token
    Pending --> Discarded: token expires or invalid target
    Associated --> Associated: set_geometry / set_source_buffer
    Associated --> Opening: target maps
    Opening --> Active: animation finished
    Active --> Closing: target unmaps
    Closing --> Closed: animation finished
    Discarded --> [*]
    Closed --> [*]
Loading

File-Level Changes

Change Details Files
Adds the server-side window-transition protocol and tracks transition rectangles across activation-token lifecycle events.
  • Creates the protocol global and validates rectangle/token resources.
  • Stores committed rectangles by activation token, expires unconsumed entries, and associates consumed rectangles with target and originating surfaces.
  • Propagates activation token, seat, and originating-surface context through activation handling while preserving one-shot token semantics.
  • Registers the module during seat initialization and discards or associates pending rectangles based on target mapping state.
src/modules/window-transition/CMakeLists.txt
src/modules/window-transition/windowtransitionmanagerinterfacev1.h
src/modules/window-transition/windowtransitionmanagerinterfacev1.cpp
src/modules/activation/activationmanagerinterfacev1.h
src/modules/activation/activationmanagerinterfacev1.cpp
src/seat/helper.h
src/seat/helper.cpp
src/common/treelandlogging.h
src/common/treelandlogging.cpp
Integrates transition rectangles into surface lifecycle and compositor animations.
  • Stores source geometry and image data on SurfaceWrapper and updates them while the rectangle remains associated.
  • Converts the source buffer into a CPU-readable QImage, manages wlroots buffer access and lifetime, and clears resources on surface or animation teardown.
  • Computes global source geometry and starts dedicated open/close transitions with fallback behavior for invalid or unavailable origins.
  • Defers activation for unmapped targets until initialization/mapping, then flushes the pending activation.
src/surface/surfacewrapper.h
src/surface/surfacewrapper.cpp
Introduces the QML animation component and exposes it through the QML engine.
  • Animates position and size between source and target geometries with configurable duration and easing.
  • Fades the optional source image according to open/close direction and optionally enables blur/shadow effects.
  • Adds component creation and registration in the QML module.
src/core/qml/Animations/WindowTransition.qml
src/core/qmlengine.h
src/core/qmlengine.cpp
src/CMakeLists.txt
Adds a Qt Wayland demonstration client covering activation and persistent rectangle updates.
  • Generates the activation and transition protocol client bindings and installs a test executable.
  • Demonstrates cross-process and modal launches, source-buffer toggling, disabled transitions, geometry updates, and cleanup of persistent rectangles.
examples/CMakeLists.txt
examples/test-window-transition/CMakeLists.txt
examples/test-window-transition/main.cpp

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/modules/activation/activationmanagerinterfacev1.cpp" line_range="231-240" />
<code_context>
         WSeat *tokenSeat = nullptr;
+        WSurface *originatingSurface = nullptr;
+
         auto it = std::find_if(m_tokens.begin(), m_tokens.end(),
                                [&token](const TokenInfo &t) { return t.token == token; });
         if (it != m_tokens.end()) {
             tokenSeat = it->seat.data();
-            m_tokens.erase(it);
+            originatingSurface = it->originatingSurface;
+            disposition = it->fromTrustedSurface
+                ? (it->serial.has_value()
+                       ? ActivationManagerInterfaceV1::TokenDisposition::Active
+                       : ActivationManagerInterfaceV1::TokenDisposition::Attention)
+                : ActivationManagerInterfaceV1::TokenDisposition::Attention;
         }

</code_context>
<issue_to_address>
**issue (bug_risk):** Expired activation tokens are accepted because the lookup computes disposition without checking `it->expiry.hasExpired()`. A token that has remained in `m_tokens` past its 60-second lifetime is consumed and activation proceeds as Active or Attention instead of being rejected as Invalid.

**Triggers:** When a client activates a committed token after its expiry deadline but before the periodic sweep removes it.

**Suggested fix:** Treat an expired iterator as invalid before reading its fields, or call the existing expiry validation logic during the single lookup.
</issue_to_address>

### Comment 2
<location path="examples/test-window-transition/CMakeLists.txt" line_range="1" />
<code_context>
+        Qt6::Gui
+        Qt6::Widgets
+        Qt6::WaylandClient
+        Qt6::GuiPrivate
+        Qt6::WaylandClientPrivate
+)
+
</code_context>
<issue_to_address>
**issue (bug_risk):** The example links `Qt6::GuiPrivate` and `Qt6::WaylandClientPrivate` unconditionally, although those components are found only when Qt6 is at least 6.10. On older supported Qt6 versions the configure step references unavailable imported targets and the example cannot be configured or built.

**Triggers:** When configuring with Qt6 older than 6.10.

**Suggested fix:** Make the private-module links conditional as well, or require Qt6 6.10+ unconditionally before configuring this example.

```suggestion
find_package(Qt6 6.10 REQUIRED COMPONENTS Gui WaylandClient Widgets)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines 231 to +240
auto it = std::find_if(m_tokens.begin(), m_tokens.end(),
[&token](const TokenInfo &t) { return t.token == token; });
if (it != m_tokens.end()) {
tokenSeat = it->seat.data();
m_tokens.erase(it);
originatingSurface = it->originatingSurface;
disposition = it->fromTrustedSurface
? (it->serial.has_value()
? ActivationManagerInterfaceV1::TokenDisposition::Active
: ActivationManagerInterfaceV1::TokenDisposition::Attention)
: ActivationManagerInterfaceV1::TokenDisposition::Attention;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Expired activation tokens are accepted because the lookup computes disposition without checking it->expiry.hasExpired(). A token that has remained in m_tokens past its 60-second lifetime is consumed and activation proceeds as Active or Attention instead of being rejected as Invalid.

Triggers: When a client activates a committed token after its expiry deadline but before the periodic sweep removes it.

Suggested fix: Treat an expired iterator as invalid before reading its fields, or call the existing expiry validation logic during the single lookup.

@@ -0,0 +1,30 @@
find_package(Qt6 REQUIRED COMPONENTS Gui WaylandClient Widgets)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The example links Qt6::GuiPrivate and Qt6::WaylandClientPrivate unconditionally, although those components are found only when Qt6 is at least 6.10. On older supported Qt6 versions the configure step references unavailable imported targets and the example cannot be configured or built.

Triggers: When configuring with Qt6 older than 6.10.

Suggested fix: Make the private-module links conditional as well, or require Qt6 6.10+ unconditionally before configuring this example.

Suggested change
find_package(Qt6 REQUIRED COMPONENTS Gui WaylandClient Widgets)
find_package(Qt6 6.10 REQUIRED COMPONENTS Gui WaylandClient Widgets)

@deepin-wm

Copy link
Copy Markdown
Contributor Author

此 PR 不应被创建,已关闭。修复内容已通过 glyvut#8 交付。

@deepin-wm deepin-wm closed this Sep 15, 2026
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