feat: make splash screens visible and closable via foreign-toplevel#745
feat: make splash screens visible and closable via foreign-toplevel#745zccrs merged 2 commits intolinuxdeepin:masterfrom
Conversation
protocol Added foreign-toplevel protocol integration for splash screens to make them visible and manageable in the dock. Splash screens now appear in the dock with proper identification and can be closed directly from the dock. When a splash screen is closed before the actual application window appears, the matching application window will be automatically closed to maintain consistency. Key changes: 1. Register splash screens with foreign-toplevel protocol to appear in dock 2. Implement splash screen closure handling via dock requests 3. Track closed splash appIds to close matching application windows 4. Move foreign-toplevel management from Helper to ShellHandler for better integration 5. Add proper splash screen identification and state management Log: Splash screens now appear in dock and can be closed via dock interface Influence: 1. Test splash screen visibility in dock for various applications 2. Verify splash screen closure from dock works correctly 3. Test that closing splash screen prevents matching application window from opening 4. Verify normal window behavior remains unchanged after splash integration 5. Check that skipDockPreView property still works correctly for all surface types 6. Test application launch with and without splash screen closure feat: 使闪屏通过foreign-toplevel协议在dock栏可见可关闭 为闪屏添加foreign-toplevel协议集成,使其在dock栏中可见并可管理。闪屏现在 会出现在dock栏中并带有适当标识,可以直接从dock栏关闭。如果在实际应用程序 窗口出现之前关闭闪屏,匹配的应用程序窗口将自动关闭以保持一致性。 主要更改: 1. 将闪屏注册到foreign-toplevel协议以在dock栏显示 2. 实现通过dock请求处理闪屏关闭功能 3. 跟踪已关闭闪屏的appId以关闭匹配的应用程序窗口 4. 将foreign-toplevel管理从Helper移动到ShellHandler以实现更好集成 5. 添加适当的闪屏标识和状态管理 Log: 闪屏现在可在dock栏显示并可通过dock界面关闭 Influence: 1. 测试各种应用程序的闪屏在dock栏中的可见性 2. 验证从dock栏关闭闪屏功能正常工作 3. 测试关闭闪屏后是否阻止匹配应用程序窗口打开 4. 验证闪屏集成后正常窗口行为保持不变 5. 检查skipDockPreView属性对所有表面类型是否仍正常工作 6. 测试在有和没有关闭闪屏的情况下应用程序启动行为
Reviewer's GuideIntegrates splash screens with the Treeland foreign-toplevel protocol so they appear and are controllable from the dock, centralizes foreign-toplevel handling in ShellHandler, and ensures that closing a splash from the dock prevents the corresponding app window from opening while keeping dock previews in sync with surface lifecycle and skipDockPreView state. Sequence diagram for closing a splash from dock and suppressing the app windowsequenceDiagram
actor User
participant DockUI
participant ForeignToplevelV1
participant ToplevelHandle_Splash
participant SurfaceWrapper_Splash
participant ShellHandler
participant AppLauncher
participant WXdgToplevelSurface
participant SurfaceWrapper_App
User->>DockUI: Click close on splash icon
DockUI->>ForeignToplevelV1: requestClose for splash identifier
ForeignToplevelV1->>ToplevelHandle_Splash: emit requestClose
ToplevelHandle_Splash->>SurfaceWrapper_Splash: close()
activate SurfaceWrapper_Splash
SurfaceWrapper_Splash->>SurfaceWrapper_Splash: m_type == SplashScreen
SurfaceWrapper_Splash-->>ShellHandler: requestCloseSplash()
deactivate SurfaceWrapper_Splash
activate ShellHandler
ShellHandler->>ShellHandler: insert appId into m_closedSplashAppIds
ShellHandler->>ShellHandler: remove wrapper from m_prelaunchWrappers
ShellHandler->>SurfaceWrapper_Splash: destroy via RootSurfaceContainer
deactivate ShellHandler
User->>AppLauncher: Launch app (after splash closed)
AppLauncher->>WXdgToplevelSurface: create toplevel surface
WXdgToplevelSurface-->>ShellHandler: onXdgToplevelSurfaceAdded(surface)
activate ShellHandler
ShellHandler->>ShellHandler: async resolve appId
ShellHandler->>ShellHandler: ensureXdgWrapper(surface, targetAppId)
alt appId was in m_closedSplashAppIds
ShellHandler->>WXdgToplevelSurface: close()
ShellHandler->>ShellHandler: remove appId from m_closedSplashAppIds
ShellHandler-->>WXdgToplevelSurface: return (no wrapper)
else appId not in m_closedSplashAppIds
ShellHandler->>SurfaceWrapper_App: create wrapper
ShellHandler->>ShellHandler: registerSurfaceToForeignToplevel(wrapper)
ShellHandler-->>SurfaceWrapper_App: surfaceWrapperAdded
end
deactivate ShellHandler
Updated class diagram for ShellHandler, Helper, ForeignToplevelV1, and SurfaceWrapperclassDiagram
class Helper {
-RootSurfaceContainer* m_rootSurfaceContainer
-ShellHandler* m_shellHandler
-WServer* m_server
-WForeignToplevel* m_foreignToplevel
-WExtForeignToplevelListV1* m_extForeignToplevelListV1
+Helper(QObject* parent)
+void init(Treeland* treeland)
+void onSurfaceWrapperAdded(SurfaceWrapper* wrapper)
+void onSurfaceWrapperAboutToRemove(SurfaceWrapper* wrapper)
}
class ShellHandler {
-RootSurfaceContainer* m_rootSurfaceContainer
-LayerSurfaceContainer* m_backgroundContainer
-LayerSurfaceContainer* m_bottomContainer
-Workspace* m_workspace
-SurfaceContainer* m_popupContainer
-WindowConfigStore* m_windowConfigStore
-ForeignToplevelV1* m_treelandForeignToplevel
-QList~SurfaceWrapper*~ m_prelaunchWrappers
-QSet~QString~ m_pendingPrelaunchAppIds
-QSet~QString~ m_closedSplashAppIds
-QList~WToplevelSurface*~ m_pendingAppIdResolveToplevels
-QObject* m_dockPreview
+ShellHandler(RootSurfaceContainer* rootContainer, WServer* server)
+Workspace* workspace() const
+void createComponent(QmlEngine* engine, QQuickItem* parentItem)
+void initXdgShell(WServer* server)
+void initLayerShell(WServer* server)
+WXWayland* createXWayland(WServer* server, const QString& name)
+void initInputMethodHelper(WServer* server, WSeat* seat)
+WXWayland* defaultXWaylandSocket() const
+void setupDockPreview(QObject* dockPreview)
-- splash management --
-void createPrelaunchSplash(const QString& appId, uint32_t timeoutMs)
-void registerSurfaceToForeignToplevel(SurfaceWrapper* wrapper)
-void setupDockPreview()
-void onDockPreview(vector~SurfaceWrapper*~ surfaces, WSurface* target, QPoint pos, ForeignToplevelV1::PreviewDirection direction)
-void onDockPreviewTooltip(QString tooltip, WSurface* target, QPoint pos, ForeignToplevelV1::PreviewDirection direction)
-void ensureXdgWrapper(WXdgToplevelSurface* surface, const QString& targetAppId)
-void ensureXwaylandWrapper(WXWaylandSurface* surface, const QString& targetAppId)
-void setupSurfaceActiveWatcher(SurfaceWrapper* wrapper)
-void setupSurfaceWindowMenu(SurfaceWrapper* wrapper)
-void updateLayerSurfaceContainer(SurfaceWrapper* surface)
<<signal>> void surfaceWrapperAdded(SurfaceWrapper* wrapper)
<<signal>> void surfaceWrapperAboutToRemove(SurfaceWrapper* wrapper)
}
class ForeignToplevelV1 {
-treeland_foreign_toplevel_manager_v1* m_manager
-map~SurfaceWrapper*, unique_ptr~treeland_foreign_toplevel_handle_v1~~ m_surfaces
-uint32_t m_nextIdentifier
+static ForeignToplevelV1* instance()
+wl_global* global() const
+void addSurface(SurfaceWrapper* wrapper)
+void removeSurface(SurfaceWrapper* wrapper)
-void initializeToplevelHandle(SurfaceWrapper* wrapper, treeland_foreign_toplevel_handle_v1* handle)
}
class SurfaceWrapper {
<<QQuickItem>>
-Type m_type
-bool m_skipDockPreView
-QString m_appId
-WToplevelSurface* m_shellSurface
-QQuickItem* m_surfaceItem
+QString appId() const
+bool skipDockPreView() const
+void setSkipDockPreView(bool skip)
+bool resize(const QSizeF& size)
+void close()
+QRectF titlebarGeometry() const
+QRectF boundingRect() const
+void markWrapperToRemoved()
<<signal>> void requestCloseSplash()
<<signal>> void skipDockPreViewChanged()
<<signal>> void surfaceItemCreated()
}
class RootSurfaceContainer {
+void addSurface(SurfaceWrapper* wrapper)
+SurfaceWrapper* getSurface(WSurface* surface)
+void destroyForSurface(SurfaceWrapper* wrapper)
}
class QmlEngine {
+QObject* createDockPreview(QQuickItem* parentItem)
+QObject* createWindowMenu(Helper* helper)
}
class DockPreviewQml {
<<QObject>>
+void show(vector~SurfaceWrapper*~ surfaces, SurfaceWrapper* dockWrapper, QPoint pos, ForeignToplevelV1::PreviewDirection direction)
+void showTooltip(QString tooltip, SurfaceWrapper* dockWrapper, QPoint pos, ForeignToplevelV1::PreviewDirection direction)
+void close()
}
Helper --> ShellHandler : owns
Helper --> RootSurfaceContainer : owns
Helper --> QmlEngine : uses
Helper --> WForeignToplevel : uses
Helper --> WExtForeignToplevelListV1 : uses
ShellHandler --> RootSurfaceContainer : manages surfaces
ShellHandler --> ForeignToplevelV1 : registers SurfaceWrapper
ShellHandler --> QmlEngine : uses
ShellHandler --> DockPreviewQml : creates and controls
ForeignToplevelV1 --> SurfaceWrapper : manages handles
SurfaceWrapper --> RootSurfaceContainer : destroyed by
SurfaceWrapper --> WToplevelSurface : wraps
RootSurfaceContainer --> SurfaceWrapper : lookup and lifecycle
QmlEngine --> DockPreviewQml : creates
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The new
SurfaceWrapper::requestCloseSplashis declared as a slot but used as a signal (e.g.connect(wrapper, &SurfaceWrapper::requestCloseSplash, ...)andQ_EMIT requestCloseSplash()), which will not compile with the new connect syntax; move it to thesignalssection instead ofpublic Q_SLOTS. - In
ForeignToplevelV1::addSurface, theset_identifiernow uses astatic int counterinstead of something tied to the underlying surface/handle; consider whether this needs to be stable/unique across the compositor lifetime and make the identifier generation more robust (and thread‑safe) if clients rely on it. - The
m_closedSplashAppIdsset is only pruned when a matching window appears; if an app never creates a window after its splash is closed, the appId will remain in the set indefinitely—consider adding a timeout or cleanup path to avoid unbounded growth over long sessions.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `SurfaceWrapper::requestCloseSplash` is declared as a slot but used as a signal (e.g. `connect(wrapper, &SurfaceWrapper::requestCloseSplash, ...)` and `Q_EMIT requestCloseSplash()`), which will not compile with the new connect syntax; move it to the `signals` section instead of `public Q_SLOTS`.
- In `ForeignToplevelV1::addSurface`, the `set_identifier` now uses a `static int counter` instead of something tied to the underlying surface/handle; consider whether this needs to be stable/unique across the compositor lifetime and make the identifier generation more robust (and thread‑safe) if clients rely on it.
- The `m_closedSplashAppIds` set is only pruned when a matching window appears; if an app never creates a window after its splash is closed, the appId will remain in the set indefinitely—consider adding a timeout or cleanup path to avoid unbounded growth over long sessions.
## Individual Comments
### Comment 1
<location> `src/surface/surfacewrapper.h:330` </location>
<code_context>
void showOnAllWorkspaceChanged();
void requestActive();
void requestInactive();
+ void requestCloseSplash();
void skipSwitcherChanged();
void skipDockPreViewChanged();
</code_context>
<issue_to_address>
**issue (bug_risk):** requestCloseSplash should be declared as a signal, not a slot, to match its usage.
`requestCloseSplash` is currently under `public Q_SLOTS`, but in `surfacewrapper.cpp` you use `Q_EMIT requestCloseSplash();` and in `ShellHandler::createPrelaunchSplash` you connect with `&SurfaceWrapper::requestCloseSplash` as if it were a signal. Because it’s not declared under `signals:`, there will be no meta-object entry, so this pointer-to-member connection will fail at runtime (usually with `QObject::connect: No such signal ...`) and the handler won’t be invoked. Please move `requestCloseSplash` to the `signals:` section (or add one) and keep the implementation the same.
</issue_to_address>
### Comment 2
<location> `src/core/shellhandler.h:137` </location>
<code_context>
WAYLIB_SERVER_NAMESPACE::WLayerShell *m_layerShell = nullptr;
WAYLIB_SERVER_NAMESPACE::WInputMethodHelper *m_inputMethodHelper = nullptr;
QList<WAYLIB_SERVER_NAMESPACE::WXWayland *> m_xwaylands;
+ ForeignToplevelV1 *m_treelandForeignToplevel = nullptr;
QPointer<RootSurfaceContainer> m_rootSurfaceContainer;
</code_context>
<issue_to_address>
**issue (bug_risk):** m_treelandForeignToplevel is used from Helper but declared private, which will not compile.
In `Helper::init` you connect directly to `m_shellHandler->m_treelandForeignToplevel`, but this member is private in `ShellHandler` and `Helper` is not a friend, so this will not compile. Please either add a public accessor (e.g. `ForeignToplevelV1 *treelandForeignToplevel() const`) or route these connections via `ShellHandler` signals instead of accessing its internals directly.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Pull request overview
This PR integrates prelaunch splash screens into Treeland’s ForeignToplevelV1 (dock/preview) so they appear in the dock and can be closed from it, and moves Treeland foreign-toplevel registration responsibilities from Helper into ShellHandler for tighter lifecycle integration.
Changes:
- Register splash
SurfaceWrapperinstances withForeignToplevelV1, including basic metadata and close handling. - Centralize Treeland foreign-toplevel surface registration in
ShellHandler, including reacting toskipDockPreViewchanges. - Track appIds for splash screens closed from the dock and close the matching real toplevel when it appears.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/surface/surfacewrapper.h |
Adds a unified close() API and a splash-close request signal hook. |
src/surface/surfacewrapper.cpp |
Implements SurfaceWrapper::close() and adjusts skipDockPreView teardown behavior and validation. |
src/seat/helper.h |
Removes stored Treeland foreign-toplevel manager pointer from Helper. |
src/seat/helper.cpp |
Constructs ShellHandler with WServer and reroutes dock-preview connections to the manager owned by ShellHandler. |
src/modules/foreign-toplevel/foreigntoplevelmanagerv1.h |
Extracts toplevel-handle initialization into a helper method. |
src/modules/foreign-toplevel/foreigntoplevelmanagerv1.cpp |
Extends Treeland foreign-toplevel handling for splash screens and refactors handle initialization. |
src/core/shellhandler.h |
Stores Treeland foreign-toplevel manager and adds tracking for dock-closed splash appIds. |
src/core/shellhandler.cpp |
Registers wrappers (including splash) with Treeland foreign-toplevel and enforces “closed splash cancels real window” behavior. |
(中文)
本 PR 将预启动闪屏接入 Treeland 的 ForeignToplevelV1(dock/preview 协议)以便在 dock 中可见并可关闭,并将 Treeland foreign-toplevel 的管理从 Helper 迁移到 ShellHandler,以获得更一致的生命周期与状态联动。
主要改动:
- 将闪屏
SurfaceWrapper注册到ForeignToplevelV1,并支持从 dock 发起关闭。 - 在
ShellHandler中集中处理 Treeland foreign-toplevel 的注册/反注册(随skipDockPreView动态变化)。 - 记录从 dock 关闭的闪屏 appId,并在真实窗口出现时自动关闭匹配窗口。
d26e0f9 to
eaefd9e
Compare
1. Moved dock preview functionality from Helper class to ShellHandler for better code organization 2. Dock preview creation and signal connections are now handled in ShellHandler::createComponent 3. Added setupDockPreview method to connect foreign toplevel signals 4. Removed dock preview related code from Helper class including onDockPreview and onDockPreviewTooltip methods 5. Updated ShellHandler::createComponent to accept parentItem parameter for dock preview creation 6. Adjusted initialization order in Helper::init to pass correct parent item 1. 将 dock 预览功能从 Helper 类移动到 ShellHandler 以实现更好的代码组织 2. Dock 预览创建和信号连接现在在 ShellHandler::createComponent 中处理 3. 添加 setupDockPreview 方法用于连接 foreign toplevel 信号 4. 从 Helper 类中移除 dock 预览相关代码,包括 onDockPreview 和 onDockPreviewTooltip 方法 5. 更新 ShellHandler::createComponent 以接受 parentItem 参数用于 dock 预 览创建 6. 调整 Helper::init 中的初始化顺序以传递正确的父项
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: wineee, zccrs The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The new
SurfaceWrapper::requestCloseSplashis declared as a slot but used withQ_EMITand inconnectas if it were a signal; this should be declared in thesignalssection (or split into a dedicated signal and slot) to avoid Qt meta-object issues. ShellHandlerdeclaresvoid setupDockPreview(QObject *dockPreview);in the header but only defines a parameterlesssetupDockPreview()in the cpp and calls it without arguments; either remove the unused declaration or align the signature and call sites to prevent compilation or ODR problems.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `SurfaceWrapper::requestCloseSplash` is declared as a slot but used with `Q_EMIT` and in `connect` as if it were a signal; this should be declared in the `signals` section (or split into a dedicated signal and slot) to avoid Qt meta-object issues.
- `ShellHandler` declares `void setupDockPreview(QObject *dockPreview);` in the header but only defines a parameterless `setupDockPreview()` in the cpp and calls it without arguments; either remove the unused declaration or align the signature and call sites to prevent compilation or ODR problems.
## Individual Comments
### Comment 1
<location path="src/surface/surfacewrapper.h" line_range="330" />
<code_context>
void showOnAllWorkspaceChanged();
void requestActive();
void requestInactive();
+ void requestCloseSplash();
void skipSwitcherChanged();
void skipDockPreViewChanged();
</code_context>
<issue_to_address>
**issue (bug_risk):** requestCloseSplash is declared but not defined, and is used like a signal, which will currently fail to link/work correctly.
In `SurfaceWrapper`, `requestCloseSplash` is declared as a slot in the header but never defined in the `.cpp`, while `ShellHandler::createPrelaunchSplash` connects to `&SurfaceWrapper::requestCloseSplash` and `SurfaceWrapper::close()` emits it. This will cause a linker error and, since it’s in `public Q_SLOTS` instead of `signals`, moc won’t treat it as a signal. If this is meant to be a signal, move it to the `signals` section and let Qt handle it as a signal (no manual implementation needed). If it’s meant to be a slot callable from QML, add a definition and consider renaming to clearly distinguish the signal from the slot.
</issue_to_address>
### Comment 2
<location path="src/core/shellhandler.h" line_range="88-92" />
<code_context>
WAYLIB_SERVER_NAMESPACE::WSeat *seat);
WAYLIB_SERVER_NAMESPACE::WXWayland *defaultXWaylandSocket() const;
+ void setupDockPreview(QObject *dockPreview);
+
Q_SIGNALS:
</code_context>
<issue_to_address>
**issue (bug_risk):** setupDockPreview is declared with an argument in the header but implemented without one, creating an API mismatch.
In the header, `ShellHandler` declares `void setupDockPreview(QObject *dockPreview);`, but `shellhandler.cpp` only defines `void ShellHandler::setupDockPreview()` (no parameter) and uses `m_dockPreview`. This API mismatch will either produce a linker error if `setupDockPreview(QObject*)` is called, or indicates a stale declaration.
Please either remove the parameterized declaration and keep the no-arg version as a private helper/slot, or update the implementation to take the `QObject *dockPreview` parameter and pass it from callers instead of using `m_dockPreview` directly.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| void showOnAllWorkspaceChanged(); | ||
| void requestActive(); | ||
| void requestInactive(); | ||
| void requestCloseSplash(); |
There was a problem hiding this comment.
issue (bug_risk): requestCloseSplash is declared but not defined, and is used like a signal, which will currently fail to link/work correctly.
In SurfaceWrapper, requestCloseSplash is declared as a slot in the header but never defined in the .cpp, while ShellHandler::createPrelaunchSplash connects to &SurfaceWrapper::requestCloseSplash and SurfaceWrapper::close() emits it. This will cause a linker error and, since it’s in public Q_SLOTS instead of signals, moc won’t treat it as a signal. If this is meant to be a signal, move it to the signals section and let Qt handle it as a signal (no manual implementation needed). If it’s meant to be a slot callable from QML, add a definition and consider renaming to clearly distinguish the signal from the slot.
| void setupDockPreview(QObject *dockPreview); | ||
|
|
||
| Q_SIGNALS: | ||
| void surfaceWrapperAdded(SurfaceWrapper *wrapper); | ||
| void surfaceWrapperAboutToRemove(SurfaceWrapper *wrapper); |
There was a problem hiding this comment.
issue (bug_risk): setupDockPreview is declared with an argument in the header but implemented without one, creating an API mismatch.
In the header, ShellHandler declares void setupDockPreview(QObject *dockPreview);, but shellhandler.cpp only defines void ShellHandler::setupDockPreview() (no parameter) and uses m_dockPreview. This API mismatch will either produce a linker error if setupDockPreview(QObject*) is called, or indicates a stale declaration.
Please either remove the parameterized declaration and keep the no-arg version as a private helper/slot, or update the implementation to take the QObject *dockPreview parameter and pass it from callers instead of using m_dockPreview directly.
protocol
Added foreign-toplevel protocol integration for splash screens to make them visible and manageable in the dock. Splash screens now appear in the dock with proper identification and can be closed directly from the dock. When a splash screen is closed before the actual application window appears, the matching application window will be automatically closed to maintain consistency.
Key changes:
Log: Splash screens now appear in dock and can be closed via dock interface
Influence:
feat: 使闪屏通过foreign-toplevel协议在dock栏可见可关闭
为闪屏添加foreign-toplevel协议集成,使其在dock栏中可见并可管理。闪屏现在
会出现在dock栏中并带有适当标识,可以直接从dock栏关闭。如果在实际应用程序
窗口出现之前关闭闪屏,匹配的应用程序窗口将自动关闭以保持一致性。
主要更改:
Log: 闪屏现在可在dock栏显示并可通过dock界面关闭
Influence:
Summary by Sourcery
Integrate splash screens with the foreign-toplevel protocol so they appear and can be managed from the dock, and centralize foreign-toplevel handling in ShellHandler.
New Features:
Enhancements: