-
Notifications
You must be signed in to change notification settings - Fork 33
feat: Add wallpaper producer client for Treeland Wayland compositor #703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reviewer's GuideIntroduces a new standalone Sequence diagram for treeland-wallpaper-factory handling a new video wallpapersequenceDiagram
participant Compositor
participant Notifier as TreelandWallpaperNotifierV1
participant Factory as TreelandWallpaperFactory
participant View as QQuickView_Wallpaper
participant WW as WallpaperWindow
participant QML as QML_Engine
participant MpvItem as MpvVideoItem
participant MpvCtrl as MpvVideoController
participant MPV as libmpv
participant ShellInt as QWaylandWallpaperShellIntegration
participant ShellSurf as QWaylandWallpaperSurface
Compositor->>Notifier: treeland_wallpaper_notifier_v1_add(wallpaper_source_type_video, file_source)
Notifier-->>Factory: treeland_wallpaper_notifier_v1_add callback
Factory->>Factory: create QQuickView
Factory->>View: construct
Factory->>WW: WallpaperWindow::get(View)
WW->>WW: setSource(file_source)
Factory->>View: loadFromModule(com.treeland.wallfactory, Video)
View->>QML: instantiate root QML object
QML-->>Factory: root is MpvVideoItem
Factory->>MpvItem: setSource(file_source)
Note over MpvItem,MpvCtrl: mpv control thread and context initialization
MpvItem->>MpvItem: constructor
MpvItem->>MpvItem: check QQuickWindow graphicsApi OpenGL
MpvItem->>MpvCtrl: create MpvVideoController
MpvItem->>MpvCtrl: init (in worker thread)
MpvCtrl->>MPV: mpv_create, mpv_initialize
MpvCtrl->>MPV: set vo=libmpv, hwdec, threads
MPV-->>MpvCtrl: ready
MpvItem->>MpvItem: observe mpv properties
MpvItem-->>MpvItem: ready signal emitted
MpvItem->>MpvItem: loadFile(source)
MpvItem->>MpvCtrl: commandAsync([loadfile, file_source])
MpvCtrl->>MPV: mpv_command_node_async
Note over View,ShellInt: Wayland wallpaper shell integration
WW->>WW: eventFilter PlatformSurfaceCreated
WW->>ShellInt: initialize(display)
ShellInt->>ShellSurf: createShellSurface(window)
ShellSurf->>ShellSurf: init treeland_wallpaper_surface_v1
Note over MpvItem,MPV: OpenGL FBO rendering pipeline
View->>MpvItem: createRenderer
MpvItem-->>View: MpvRenderer
MpvRenderer->>MPV: mpv_render_context_create (with OpenGL params)
MPV-->>MpvRenderer: mpv_render_context
loop frame updates
MPV-->>MpvRenderer: update callback handleMpvRedraw
MpvRenderer->>MpvRenderer: render()
MpvRenderer->>MPV: mpv_render_context_render(FBO)
end
ShellSurf-->>Compositor: wl_buffer frames for wallpaper surface
Class diagram for treeland-wallpaper-factory mpv and wallpaper shell integrationclassDiagram
class MpvVideoItem {
Q_OBJECT
QML_ELEMENT
// QML properties
QString source
QString mediaTitle
double position
double duration
QString formattedPosition
QString formattedDuration
bool pause
int volume
bool loopFile
double speed
bool mute
VideoScaleMode scaleMode
double panScan
// enums
enum AsyncIds
enum Property
enum VideoScaleMode
// lifecycle
+MpvVideoItem(QQuickItem *parent)
+~MpvVideoItem()
+Renderer* createRenderer() const
// accessors
+static QString toString(Property p)
+QString mediaTitle()
+double position()
+void setPosition(double value)
+double duration()
+QString formattedPosition()
+QString formattedDuration()
+bool pause()
+void setPause(bool value)
+int volume()
+void setVolume(int value)
+QString source()
+void setSource(QString source)
+bool loopFile()
+void setLoopFile(bool value)
+double speed()
+void setSpeed(double value)
+void setReady(bool ready)
+bool mute()
+void setMute(bool value)
+VideoScaleMode scaleMode()
+void setScaleMode(VideoScaleMode mode)
+double panScan()
+void setPanScan(double value)
// mpv interaction
+void loadFile(QString file)
+int setPropertyBlocking(QString property, QVariant value)
+void setPropertyAsync(QString property, QVariant value, int id)
+QVariant getProperty(QString property)
+void getPropertyAsync(QString property, int id)
+QVariant commandBlocking(QVariant params)
+void commandAsync(QStringList params, int id)
+QVariant expandText(QString text)
+int unobserveProperty(uint64_t id)
// signals (simplified)
<<signal>> mediaTitleChanged()
<<signal>> positionChanged()
<<signal>> durationChanged()
<<signal>> pauseChanged()
<<signal>> volumeChanged()
<<signal>> sourceChanged()
<<signal>> loopFileChanged()
<<signal>> speedChanged()
<<signal>> muteChanged()
<<signal>> scaleModeChanged()
<<signal>> panScanChanged()
<<signal>> fileStarted()
<<signal>> fileLoaded()
<<signal>> endFile(QString reason)
<<signal>> videoReconfig()
<<signal>> ready()
<<signal>> observeProperty(QString property, mpv_format format, uint64_t id)
<<signal>> setProperty(QString property, QVariant value)
<<signal>> command(QStringList params)
// private
-void initConnections()
-QString formatTime(double time)
-QThread *m_workerThread
-MpvVideoController *m_mpvController
-mpv_handle *m_mpv
-mpv_render_context *m_mpvGL
-QUrl m_file
-QString m_source
-bool m_readyed
}
class MpvRenderer {
+MpvRenderer(MpvVideoItem *item)
+~MpvRenderer()
+QOpenGLFramebufferObject* createFramebufferObject(QSize size)
+void render()
-MpvVideoItem *m_item
}
class MpvVideoController {
Q_OBJECT
+MpvVideoController(QObject *parent)
+QString getError(int error)
+static void mpvEvents(void *ctx)
+void eventHandler()
+mpv_handle* mpv() const
// slots
<<slot>> void init()
<<slot>> void observeProperty(QString property, mpv_format format, uint64_t id)
<<slot>> int unobserveProperty(uint64_t id)
<<slot>> int setProperty(QString property, QVariant value)
<<slot>> int setPropertyAsync(QString property, QVariant value, int id)
<<slot>> QVariant getProperty(QString property)
<<slot>> int getPropertyAsync(QString property, int id)
<<slot>> QVariant command(QVariant params)
<<slot>> int commandAsync(QVariant params, int id)
// signals
<<signal>> propertyChanged(QString property, QVariant value)
<<signal>> asyncReply(QVariant data, mpv_event event)
<<signal>> fileStarted()
<<signal>> fileLoaded()
<<signal>> endFile(QString reason)
<<signal>> videoReconfig()
// helpers
-mpv_node_list* createList(mpv_node *dst, bool is_map, int num)
-void setNode(mpv_node *dst, QVariant src)
-void freeNode(mpv_node *dst)
-QVariant nodeToVariant(const mpv_node *node)
-mpv_handle *m_mpv
}
class WallpaperWindow {
Q_OBJECT
QString source
+~WallpaperWindow()
+QString source()
+void setSource(QString source)
+bool eventFilter(QObject *watched, QEvent *event)
+static WallpaperWindow* get(QWindow *window)
+static WallpaperWindow* qmlAttachedProperties(QObject *object)
<<signal>> sourceChanged()
-void initializeShellIntegration()
-WallpaperWindow(QWindow *window)
-std::unique_ptr<WallpaperWindowPrivate> d
}
class WallpaperWindowPrivate {
+WallpaperWindowPrivate(QWindow *window)
QWindow *parentWindow
QString source
}
class TreelandWallpaperNotifierClientV1 {
Q_OBJECT
+TreelandWallpaperNotifierClientV1()
+~TreelandWallpaperNotifierClientV1()
+void instantiate()
// protocol handlers
+void treeland_wallpaper_notifier_v1_add(uint32_t source_type, QString file_source)
+void treeland_wallpaper_notifier_v1_remove(QString file_source)
// slots
<<slot>> void updateAllWallpaperViewSizes()
<<slot>> void onScreenAdded(QScreen *screen)
<<slot>> void onScreenRemoved(QScreen *screen)
-QList<QQuickView*> m_windows
-QSet<QScreen*> m_connectedScreens
}
class QWaylandWallpaperShellIntegration {
+QWaylandWallpaperShellIntegration()
+~QWaylandWallpaperShellIntegration()
+QtWaylandClient::QWaylandShellSurface* createShellSurface(QtWaylandClient::QWaylandWindow *window)
}
class QWaylandWallpaperSurface {
Q_OBJECT
+QWaylandWallpaperSurface(QWaylandWallpaperShellIntegration *shell, QtWaylandClient::QWaylandWindow *window)
+~QWaylandWallpaperSurface()
+bool isExposed() const
-void treeland_wallpaper_surface_v1_position(wl_fixed_t position)
-void treeland_wallpaper_surface_v1_pause()
-void treeland_wallpaper_surface_v1_play()
-QWaylandWallpaperShellIntegration *m_shell
-WallpaperWindow *m_interface
-QtWaylandClient::QWaylandWindow *m_window
-QSize m_pendingSize
-QString m_activationToken
-bool m_configured
}
class QWaylandWallpaerIntegrationPlugin {
+QWaylandWallpaerIntegrationPlugin()
+QtWaylandClient::QWaylandShellIntegration* create(QString key, QStringList paramList)
}
MpvVideoItem --> MpvRenderer : creates
MpvRenderer --> MpvVideoItem : holds_pointer
MpvVideoItem --> MpvVideoController : uses_async_api
MpvVideoController --> MpvVideoItem : emits_signals
WallpaperWindow --> WallpaperWindowPrivate : owns
TreelandWallpaperNotifierClientV1 o-- QQuickView : manages_wallpaper_views
TreelandWallpaperNotifierClientV1 o-- WallpaperWindow : attaches_and_sets_source
QWaylandWallpaperShellIntegration --> QWaylandWallpaperSurface : createShellSurface
QWaylandWallpaperSurface --> WallpaperWindow : reads_source
QWaylandWallpaerIntegrationPlugin --> QWaylandWallpaperShellIntegration : factory
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've found 4 issues, and left some high level feedback:
- In MpvVideoController, the mpv_node structures created in setProperty/setPropertyAsync/command/commandAsync via setNode() are never freed on the success path; consider calling freeNode() or mpv_free_node_contents() after the libmpv call to avoid leaking the allocated strings/lists.
- TreelandWallpaperNotifierClientV1::treeland_wallpaper_notifier_v1_remove() compares QQuickView::source() (the QML URL) against file_source; if the intention is to match by wallpaper media path, you likely want to compare against WallpaperWindow::source() instead.
- There are a couple of small inconsistencies and leftovers worth cleaning up (e.g. the qWarning("zzxyb" ...) debug log in treeland_wallpaper_notifier_v1_add and the application name typo "treeland-wallpaer-factory" in main.cpp), which can make future debugging and packaging clearer.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In MpvVideoController, the mpv_node structures created in setProperty/setPropertyAsync/command/commandAsync via setNode() are never freed on the success path; consider calling freeNode() or mpv_free_node_contents() after the libmpv call to avoid leaking the allocated strings/lists.
- TreelandWallpaperNotifierClientV1::treeland_wallpaper_notifier_v1_remove() compares QQuickView::source() (the QML URL) against file_source; if the intention is to match by wallpaper media path, you likely want to compare against WallpaperWindow::source() instead.
- There are a couple of small inconsistencies and leftovers worth cleaning up (e.g. the qWarning("zzxyb" ...) debug log in treeland_wallpaper_notifier_v1_add and the application name typo "treeland-wallpaer-factory" in main.cpp), which can make future debugging and packaging clearer.
## Individual Comments
### Comment 1
<location> `wallpaper-factory/mpvvideocontroller.cpp:111-102` </location>
<code_context>
+ break;
+ }
+
+ case MPV_EVENT_COMMAND_REPLY: {
+ mpv_event_property *prop = static_cast<mpv_event_property *>(event->data);
+ auto data = nodeToVariant(reinterpret_cast<mpv_node *>(prop));
+ Q_EMIT asyncReply(data, {*event});
+ break;
+ }
</code_context>
<issue_to_address>
**issue (bug_risk):** Incorrect handling of MPV_EVENT_COMMAND_REPLY payload type
For `MPV_EVENT_COMMAND_REPLY`, `event->data` is an `mpv_event_command*`, not an `mpv_event_property*`. Casting it to `mpv_event_property` and then to `mpv_node*` is undefined behavior and will likely read invalid memory. Instead, use the proper type and `cmd->result`:
```cpp
case MPV_EVENT_COMMAND_REPLY: {
auto *cmd = static_cast<mpv_event_command *>(event->data);
QVariant data;
if (cmd->result)
data = nodeToVariant(cmd->result);
Q_EMIT asyncReply(data, {*event});
break;
}
```
and rely on mpv to manage the node lifetime per the API docs.
</issue_to_address>
### Comment 2
<location> `wallpaper-factory/treelandwallpapernotifierclient.cpp:80-85` </location>
<code_context>
+ return;
+ }
+
+ QQuickItem *root = wallpaperWindow->rootObject();
+ if (source_type == QtWayland::treeland_wallpaper_notifier_v1::wallpaper_source_type::wallpaper_source_type_image) {
+ QQuickAnimatedImage *image = static_cast<QQuickAnimatedImage *>(root);
+ image->setSource(QUrl::fromLocalFile(file_source));
+ } else {
+ MpvVideoItem *video = static_cast<MpvVideoItem *>(root);
+ video->setSource(file_source);
+ }
</code_context>
<issue_to_address>
**issue (bug_risk):** Unsafe static_casts from rootObject to QQuickAnimatedImage/MpvVideoItem
This relies on `root` always being the expected type and uses `static_cast`, which will cause undefined behavior if the QML changes, the wrong component is loaded, or loading fails.
Use `qobject_cast` and handle failure instead:
```cpp
QQuickItem *root = wallpaperWindow->rootObject();
if (source_type == ..._image) {
auto *image = qobject_cast<QQuickAnimatedImage *>(root);
if (!image) {
qCCritical(WALLPAPER) << "Root object is not QQuickAnimatedImage";
delete wallpaperWindow;
return;
}
image->setSource(QUrl::fromLocalFile(file_source));
} else {
auto *video = qobject_cast<MpvVideoItem *>(root);
if (!video) {
qCCritical(WALLPAPER) << "Root object is not MpvVideoItem";
delete wallpaperWindow;
return;
}
video->setSource(file_source);
}
```
</issue_to_address>
### Comment 3
<location> `wallpaper-factory/mpvvideocontroller.cpp:19-18` </location>
<code_context>
+ return QStringLiteral("MPV_ERROR_SUCCESS");
+ case MPV_ERROR_EVENT_QUEUE_FULL:
+ return QStringLiteral("MPV_ERROR_EVENT_QUEUE_FULL");
+ case MPV_ERROR_NOMEM:
+ return QStringLiteral("MPV_ERROR_EVENT_QUEUE_FULL");
+ case MPV_ERROR_UNINITIALIZED:
+ return QStringLiteral("MPV_ERROR_UNINITIALIZED");
</code_context>
<issue_to_address>
**nitpick (bug_risk):** Incorrect error string for MPV_ERROR_NOMEM
In `getError`, the `MPV_ERROR_NOMEM` branch currently returns the `"MPV_ERROR_EVENT_QUEUE_FULL"` string, which makes it impossible to distinguish these errors. Please map `MPV_ERROR_NOMEM` to its own string (e.g. `"MPV_ERROR_NOMEM"`) for consistency and easier debugging.
</issue_to_address>
### Comment 4
<location> `wallpaper-factory/main.cpp:15` </location>
<code_context>
+ QGuiApplication app(argc, argv);
+
+ app.setOrganizationName("deepin");
+ app.setApplicationName("treeland-wallpaer-factory");
+
+ std::unique_ptr<TreelandWallpaperNotifierClientV1> produce;
</code_context>
<issue_to_address>
**nitpick (typo):** Application name string contains a typo
The application name is currently set to "treeland-wallpaer-factory"; if this isn’t intentional, please change it to "treeland-wallpaper-factory" to keep naming consistent across logs and desktop integration.
```suggestion
app.setApplicationName("treeland-wallpaper-factory");
```
</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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Add a new treeland-wallpaper-factory Wayland client that implements the treeland-wallpaper-shell-unstable-v1 workflow and renders image/video wallpapers via QtQuick (with mpv for video).
新增一个 treeland-wallpaper-factory Wayland 客户端,实现 treeland-wallpaper-shell-unstable-v1 工作流,并通过 QtQuick 渲染图片/视频壁纸(视频使用 mpv)。
Changes:
- Introduce a new wallpaper-factory executable with Wayland protocol integration and per-window wallpaper metadata attachment.
新增 wallpaper-factory 可执行程序,集成 Wayland 协议,并为每个窗口附加壁纸元数据。 - Add QML components for Image/Video wallpapers, including an mpv-backed OpenGL renderer item.
新增图片/视频壁纸的 QML 组件,并加入基于 mpv 的 OpenGL 渲染 Item。 - Wire build/packaging/CI changes to build and ship the new binary (CMake, Debian package, Arch CI mpv dependency).
更新构建/打包/CI 以编译并安装新二进制(CMake、Debian 包、Arch CI 的 mpv 依赖)。
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
CMakeLists.txt |
Adds wallpaper-factory/ to the build. |
.github/workflows/treeland-archlinux-build.yml |
Installs mpv in Arch CI to satisfy new dependency. |
debian/control |
Adds libmpv-dev build-dep and a new treeland-wallpaper-factory package stanza. |
debian/treeland-wallpaper-factory.install |
Installs the new treeland-wallpaper-factory binary. |
wallpaper-factory/CMakeLists.txt |
Defines the new executable, QML module, protocol generation, and mpv linkage. |
wallpaper-factory/main.cpp |
App entrypoint configuring QtQuick OpenGL and starting the notifier client. |
wallpaper-factory/loggings.h |
Declares logging category for wallpaper factory. |
wallpaper-factory/loggings.cpp |
Defines logging category name/level. |
wallpaper-factory/Image.qml |
QML component for image wallpapers. |
wallpaper-factory/Video.qml |
QML component for video wallpapers (mpv-backed). |
wallpaper-factory/mpvvideoitem.h |
Declares MpvVideoItem and renderer for QML video playback. |
wallpaper-factory/mpvvideoitem.cpp |
Implements mpv OpenGL rendering into a QtQuick FBO. |
wallpaper-factory/mpvvideocontroller.h |
Declares mpv controller API for threaded interaction. |
wallpaper-factory/mpvvideocontroller.cpp |
Implements mpv event loop and property/command bridging. |
wallpaper-factory/treelandwallpapernotifierclient.h |
Declares client extension handling compositor add/remove requests. |
wallpaper-factory/treelandwallpapernotifierclient.cpp |
Creates/removes wallpaper windows and resizes to screen changes. |
wallpaper-factory/wallpaperwindow.h |
Declares per-window attached properties for wallpaper metadata + shell integration hookup. |
wallpaper-factory/wallpaperwindow.cpp |
Implements shell integration initialization on surface creation. |
wallpaper-factory/qwaylandwallpapershellintegration_p.h |
Declares custom QtWayland shell integration wrapper. |
wallpaper-factory/qwaylandwallpapershellintegration.cpp |
Implements shell integration creating wallpaper surfaces. |
wallpaper-factory/qwaylandwallpapershellintegrationplugin.cpp |
Declares shell integration plugin metadata. |
wallpaper-factory/qwaylandwallpapersurface_p.h |
Declares wallpaper shell-surface wrapper. |
wallpaper-factory/qwaylandwallpapersurface.cpp |
Implements wallpaper surface creation and protocol stubs. |
wallpaper-factory/wallpaper-shell.json |
Declares plugin key metadata. |
wallpaper-factory/wallpaper-shell.json.license |
SPDX/copyright for the JSON metadata. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 13 comments.
Introduce a wallpaper producer client that generates wallpaper content (images and videos) and provides it to the Wayland compositor as wl_buffer objects for efficient display. The wallpaper producer runs as a multi-window Wayland client. Each surface follows the treeland-wallpaper-shell protocol to correctly integrate with the compositor’s wallpaper management workflow. Log: Tasks: https://pms.uniontech.com/story-view-39471.html Influence:
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: zccrs, zzxyb 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 |
Introduce a wallpaper producer client that generates wallpaper content (images and videos) and provides it to the Wayland compositor as wl_buffer objects for efficient display.
The wallpaper producer runs as a multi-window Wayland client. Each surface follows the treeland-wallpaper-shell protocol to correctly integrate with the compositor’s wallpaper management workflow.
Log:
Tasks: https://pms.uniontech.com/story-view-39471.html
Influence:
Summary by Sourcery
Add a new Treeland wallpaper producer client application that integrates with the treeland-wallpaper-shell Wayland protocol to provide image and video wallpapers via a QML/Qt-based implementation.
New Features:
Enhancements:
Build: