From acdd6b5e13d8d3d864e15249695477a448e8d599 Mon Sep 17 00:00:00 2001 From: rewine Date: Tue, 20 Oct 2026 15:05:52 +0800 Subject: [PATCH] feat: add treeland-output-group-manager, deprecate virtual-output-v1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add dde/treeland-output-group-manager-unstable-v1.xml with a manager/group split (treeland_output_group_manager_v1 / treeland_output_group_v1). Outputs are addressed by opaque uuid strings from treeland-output-uuid-unstable-v1 (public/); mirror uuids are queried through treeland-output-uuid-head-unstable-v1. 2. Groups are created with create_group(source) so observers never see an empty-source group; group names are assigned by the compositor and announced via the name event, with get_group available on the manager. 3. Enumeration uses group_added/groups_done/group_removed events; the group outputs event carries a count plus concatenated fixed-length uuids, replacing incremental added/removed events. 4. Distinguished configured vs effective state: unplugged or disabled outputs stay configured and resume automatically, there is no automatic source promotion, and groups persist across compositor restarts. 5. Split group failures by severity: invalid_output, duplicate_output, output_in_use and not_in_group are reported non-fatally through operation_failed (code + uuid), so transient output state never terminates the client connection; only already_dissolved and a bad source uuid at creation remain fatal protocol errors. 6. Move treeland-virtual-output-manager-v1.xml to deprecated/ unchanged, still installed by default for migration; register the new protocol in TREELAND_PROTOCOL_DDE_XML_FILES and the old one in TREELAND_PROTOCOL_DEPRECATED_XML_FILES. 7. Update dde README.md and README.zh_CN.md: new protocol table row and a 0.8.0 breaking-changes subsection describing the wire-level differences from virtual-output-v1. 8. Bump the project version to 0.8.0 for the new breaking release. Log: Replace the output-name/string-array based virtual output protocol with an output group protocol built on stable per-output uuids, explicit source/mirror separation, compositor-assigned names, full-list enumeration, persistent groups, and non-fatal transient failures. Influence: 1. Verify wayland-scanner generates client/server headers and code for the new XML without errors. 2. Verify cmake configure/build/install places the new file in share/treeland-protocols and the deprecated file still installs from deprecated/. 3. Verify an add_output failing because the output was unplugged emits operation_failed without terminating the connection. 4. Verify dde README.md and README.zh_CN.md table rows and the 0.8.0 breaking-changes subsection are present and in sync. 5. Verify the generated treeland-protocols.pc reports Version: 0.8.0. feat: 新增输出分组协议,废弃 virtual-output-v1 1. 新增 dde/treeland-output-group-manager-unstable-v1.xml,采用 manager/group 分离(treeland_output_group_manager_v1 / treeland_output_group_v1)。输出以来自 public/ 中 treeland-output-uuid-unstable-v1 的不透明 uuid 字符串寻址;镜像 输出的 uuid 经 treeland-output-uuid-head-unstable-v1 获取。 2. 组通过 create_group(source) 创建,观察者不会看到空源组;组名由 合成器分配并通过 name 事件告知,manager 上提供 get_group。 3. 枚举通过 group_added/groups_done/group_removed 事件完成;组的 outputs 事件携带数量与定长 uuid 拼接数组,替代增量式 added/removed 事件。 4. 区分配置态与运行时有效态:拔出或禁用的输出保留配置并在恢复后 自动生效,不再自动提升源,组配置跨合成器重启持久化。 5. 按严重程度划分组错误:invalid_output、duplicate_output、 output_in_use 与 not_in_group 通过 operation_failed(code + uuid) 非致命上报,瞬态输出状态不再终止客户端连接;仅 already_dissolved 与创建时非法的 source uuid 仍为致命协议错误。 6. 将 treeland-virtual-output-manager-v1.xml 原样移至 deprecated/, 默认仍安装以供迁移;新协议注册到 TREELAND_PROTOCOL_DDE_XML_FILES,旧协议注册到 TREELAND_PROTOCOL_DEPRECATED_XML_FILES。 7. 更新 dde README.md 与 README.zh_CN.md:新增协议表行,并在双语 README 中新增 0.8.0 破坏性变更子节,描述相对 virtual-output-v1 的线缆级差异。 8. 项目版本升至 0.8.0,对应该破坏性变更版本。 Log: 以基于稳定输出 uuid 的输出分组协议取代基于输出名称/字符串数组 的虚拟输出协议,显式区分源与镜像、组名由合成器分配、全量枚举、 组配置持久化,并采用非致命的瞬态失败上报。 Influence: 1. 验证 wayland-scanner 对新 XML 生成 client/server 头文件与代码无 报错。 2. 验证 cmake 配置/构建/安装将新文件安装至 share/treeland-protocols, 废弃文件仍从 deprecated/ 安装。 3. 验证 add_output 因输出拔出失败时发送 operation_failed 且不终止 连接。 4. 验证 dde README.md 与 README.zh_CN.md 表行及 0.8.0 破坏性变更 子节存在且内容同步。 5. 验证生成的 treeland-protocols.pc 中 Version 为 0.8.0。 PMS: TASK-395091 --- CMakeLists.txt | 5 +- dde/README.md | 21 +- dde/README.zh_CN.md | 21 +- ...eland-output-group-manager-unstable-v1.xml | 537 ++++++++++++++++++ .../treeland-virtual-output-manager-v1.xml | 0 public/treeland-output-uuid-unstable-v1.xml | 5 + 6 files changed, 585 insertions(+), 4 deletions(-) create mode 100644 dde/treeland-output-group-manager-unstable-v1.xml rename {dde => deprecated}/treeland-virtual-output-manager-v1.xml (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 858eda8..1873c4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25.0) -project(TreelandProtocols VERSION 0.7.0 LANGUAGES NONE) +project(TreelandProtocols VERSION 0.8.0 LANGUAGES NONE) # https://gitlab.kitware.com/cmake/cmake/-/work_items/23461 # Suppress GNUInstallDirs warning for LANGUAGES NONE projects @@ -31,7 +31,7 @@ set(TREELAND_PROTOCOL_DDE_XML_FILES dde/treeland-shortcut-manager-unstable-v3.xml dde/treeland-compositor-action-unstable-v1.xml dde/treeland-appearance-manager-unstable-v1.xml - dde/treeland-virtual-output-manager-v1.xml + dde/treeland-output-group-manager-unstable-v1.xml dde/treeland-wallpaper-manager-unstable-v1.xml dde/treeland-layer-shell-extension-unstable-v1.xml dde/treeland-xwindow-control-unstable-v1.xml @@ -70,6 +70,7 @@ set(TREELAND_PROTOCOL_DEPRECATED_XML_FILES deprecated/treeland-wallpaper-color-v1.xml deprecated/treeland-window-management-v1.xml deprecated/treeland-foreign-toplevel-manager-v1.xml + deprecated/treeland-virtual-output-manager-v1.xml ) option(TREELAND_PROTOCOLS_INSTALL_DEPRECATED "Install deprecated protocol files" ON) diff --git a/dde/README.md b/dde/README.md index af6ccee..4843267 100644 --- a/dde/README.md +++ b/dde/README.md @@ -11,7 +11,7 @@ For DDE desktop components. Installed via `TREELAND_PROTOCOL_DDE_XML_FILES`. | `treeland-shortcut-manager-unstable-v3.xml` | `treeland_shortcut_manager_unstable_v3` | `treeland_shortcut_manager_v3`, `treeland_shortcut_capture_v3` | Global keyboard shortcut binding with key/touch/multi-touch gesture support and one-shot shortcut capture | | `treeland-compositor-action-unstable-v1.xml` | `treeland_compositor_action_unstable_v1` | `treeland_compositor_action_v1` | One-shot privileged triggering of window-independent compositor actions: workspace switching, show-desktop, multitask overview, screen zoom, FPS overlay, lockscreen, shutdown menu, user switch; window-level and task-switch actions are intentionally excluded and remain only as shortcuts in `treeland_shortcut_manager_v3` | | `treeland-appearance-manager-unstable-v1.xml` | `treeland_appearance_manager_unstable_v1` | `treeland_appearance_manager_v1` | Privileged user-level appearance configuration: cursor theme/size, global font, icon theme, accent color, window opacity, color scheme, titlebar height, global corner radius | -| `treeland-virtual-output-manager-v1.xml` | `treeland_virtual_output_manager_v1` | `treeland_virtual_output_manager_v1`, `treeland_virtual_output_v1` | Virtual (mirrored) output creation and management | +| `treeland-output-group-manager-unstable-v1.xml` | `treeland_output_group_manager_unstable_v1` | `treeland_output_group_manager_v1`, `treeland_output_group_v1` | Output mirroring (copy mode) groups: designate a source output mirrored to one or more mirror outputs, addressed by uuid, with registry-pattern enumeration | | `treeland-wallpaper-manager-unstable-v1.xml` | `treeland_wallpaper_manager_unstable_v1` | `treeland_wallpaper_manager_v1`, `treeland_wallpaper_v1` | Per-output wallpaper configuration with image/video sources | | `treeland-layer-shell-extension-unstable-v1.xml` | `treeland_layer_shell_extension_unstable_v1` | `treeland_layer_shell_extension_manager_v1`, `treeland_layer_shell_extension_object_v1` | Compositor-driven interactive resize for layer-shell surfaces (dock / side bar / status bar): begin_resize with seat+serial, per-resize size limits, and rejection reasons | | `treeland-xwindow-control-unstable-v1.xml` | `treeland_xwindow_control_unstable_v1` | `treeland_xwindow_control_v1` | XWayland window placement: move an XWayland window (by X11 window ID) to a position relative to another surface, with wl_callback result feedback | @@ -23,6 +23,25 @@ For DDE desktop components. Installed via `TREELAND_PROTOCOL_DDE_XML_FILES`. Breaking changes are grouped by version. Under each version heading, one subsection per affected protocol explains what changed, what replaces it, and how existing consumers should adapt. +### 0.8.0 + +#### `treeland-virtual-output-manager-v1.xml` + +Superseded by `treeland-output-group-manager-unstable-v1.xml`; the old v1 file moved to `deprecated/` unchanged. The protocol was redesigned around uuid-based output identity, compositor-assigned group names, persisted configuration, and a registry-pattern enumeration model. Wire-level differences: + +1. Protocol and interfaces renamed: `treeland_virtual_output_manager_v1` → `treeland_output_group_manager_unstable_v1`, with interfaces `treeland_virtual_output_manager_v1` → `treeland_output_group_manager_v1` and `treeland_virtual_output_v1` → `treeland_output_group_v1`; interface versions reset to 1. +2. Groups are addressed by compositor-assigned names instead of client-supplied ones: `create_group` takes only the initial `source` uuid (empty for no source) and the compositor reports the name it picked through the new `name` event, emitted before any other event on the object; a new `get_group` request obtains a group object by that name. The `name` argument of `create_virtual_output` and the `invalid_group_name`/`name_exists` manager errors are removed. +3. Output identity switched from name strings to uuids: the `name`/`outputs` arguments of `create_virtual_output`, the `names` array of `virtual_output_list`, and the `outputs` array of the `outputs` event are gone. Outputs are referenced by uuid strings on `set_source`, `add_output`, and `remove_output`, and on the `state` event (empty source when the group has no available source). The old `outputs[0] = source, outputs[1..] = mirrors` positional convention is replaced by an explicit source plus an ordered mirror list. +4. Mirror uuids come from `treeland-output-uuid-head-unstable-v1`, not `treeland-output-uuid-unstable-v1`: while copy mode is engaged the compositor exposes a `wl_output` only for the source output, so mirror outputs cannot be identified through `wl_output`-based protocols. +5. Enumeration replaced by a registry push model: `get_virtual_output_list` and the `virtual_output_list` snapshot event are removed; `get_virtual_output` is replaced by `get_group`. On binding the manager the compositor emits one `group_added` event carrying the name of each existing group, followed by a single `groups_done` event, and `group_added` again whenever any client creates a group, while `group_removed` (carrying the group name) is emitted when a group is dissolved. `group_added` carries only the group name, not a `new_id` object; clients obtain a group object with `get_group`. This removes the list-then-lookup TOCTOU window. +6. Incremental mirror events are replaced by a single full-state event: `output_added`/`output_removed` are gone, and a single `state` event carries the effective source uuid plus a count and the concatenated fixed-length uuids of the mirrors in list order on creation and after every change. A new `done` event on each group object marks the end of the initial state batch (`name`, `state`, `done`). +7. Changes are queued and committed: `set_source`, `add_output`, and `remove_output` no longer take effect immediately; they queue changes that a new `commit` request applies in queue order. A failed change is skipped with an `operation_failed` event and the remaining changes are still applied. The `state` event is emitted once per commit that changes the effective state. Queued changes do not reserve outputs: an output queued in another group is not treated as in use until that group commits, so when two groups queue the same output the first to commit wins and the other is reported with `output_in_use`. +8. Configuration is persisted and separated from effective state: the compositor stores each group's name, configured source, and configured mirror list across restarts and restores them on startup, so groups reappear as `group_added` on bind and auto-resume when their outputs return. The single `state` event reports effective runtime state only — an unplugged or disabled output stays configured but disappears from it. When the configured source becomes unavailable the compositor auto-promotes the first available mirror (the earliest in the configured mirror list order) to the effective source; whether the promotion is temporary (the configured source reclaims the role when it returns) or permanent (the selected output becomes the new configured source) is a compositor implementation detail. To pin an auto-promoted mirror as the permanent source a client calls `set_source` with its uuid and then commits; the change succeeds idempotently when that output is the current effective source. +9. Failures are all non-fatal on the manager: the per-group `error` event and the codes `invalid_group_name` (0), `invalid_screen_number` (1), and `invalid_output` (2) are removed, and the manager has no fatal `error` enum. Business failures — `invalid_output`, `duplicate_output`, `output_in_use`, `not_in_group`, `invalid_name` — are reported through the new `operation_failed` event (code + uuid), so no failure terminates the connection. A failed `create_group` source still creates the group with an empty source; an invalid `get_group` name still creates the object, but it is an invalid group object that receives no `name` or `state` event and rejects every request except `destroy`. Only `already_dissolved` — sending any request other than `destroy` on a dissolved group — remains a fatal protocol error. +10. Object lifecycle redefined: `destroy` only releases that client's handle and neither dissolves the group nor emits `group_removed`; a separate `dissolve` request tears the group down, discarding any queued changes, emitting `removed` on every bound group object and `group_removed` on the manager. Initial state is pushed on every group object as one batch: `name`, then `state`, then `done`; a failed `create_group` source inserts an `operation_failed` before `done`, and an invalid `get_group` object receives only `operation_failed` then `done`. + +Consumers should rebind the global as `treeland_output_group_manager_v1`, address groups by the compositor-assigned name from the `name` event (or `get_group`), obtain mirror uuids from `treeland-output-uuid-head-unstable-v1`, queue changes with `set_source`/`add_output`/`remove_output` and apply them with `commit`, read the effective source and mirror list from the `state` event (and wait for `done` before acting on initial state), and dissolve groups with `dissolve` rather than by destroying a group object; the old v1 XML is kept installed during migration but must not be used in new code. + ### 0.7.0 #### `treeland-appearance-manager-unstable-v1.xml` diff --git a/dde/README.zh_CN.md b/dde/README.zh_CN.md index 8dec8a9..4ec4f9d 100644 --- a/dde/README.zh_CN.md +++ b/dde/README.zh_CN.md @@ -11,7 +11,7 @@ | `treeland-shortcut-manager-unstable-v3.xml` | `treeland_shortcut_manager_unstable_v3` | `treeland_shortcut_manager_v3`, `treeland_shortcut_capture_v3` | 全局键盘快捷键绑定,支持按键/触摸/多点触摸手势及一次性快捷键捕获 | | `treeland-compositor-action-unstable-v1.xml` | `treeland_compositor_action_unstable_v1` | `treeland_compositor_action_v1` | 特权客户端一次性触发与当前窗口无关的合成器动作:工作区切换、显示桌面、多任务视图、屏幕缩放、FPS 覆盖层、锁屏、关机菜单、用户切换;窗口级与任务切换动作被有意排除,仅作为 `treeland_shortcut_manager_v3` 中的快捷键保留 | | `treeland-appearance-manager-unstable-v1.xml` | `treeland_appearance_manager_unstable_v1` | `treeland_appearance_manager_v1` | 特权用户级外观配置:光标主题/大小、全局字体、图标主题、强调色、窗口不透明度、配色方案、标题栏高度、全局圆角 | -| `treeland-virtual-output-manager-v1.xml` | `treeland_virtual_output_manager_v1` | `treeland_virtual_output_manager_v1`, `treeland_virtual_output_v1` | 虚拟(镜像)输出创建和管理 | +| `treeland-output-group-manager-unstable-v1.xml` | `treeland_output_group_manager_unstable_v1` | `treeland_output_group_manager_v1`, `treeland_output_group_v1` | 输出镜像(复制模式)分组管理:指定一个源输出并镜像到一个或多个镜像输出,按 uuid 寻址,采用 registry 模式枚举 | | `treeland-wallpaper-manager-unstable-v1.xml` | `treeland_wallpaper_manager_unstable_v1` | `treeland_wallpaper_manager_v1`, `treeland_wallpaper_v1` | 逐输出壁纸配置,支持图片/视频来源 | | `treeland-layer-shell-extension-unstable-v1.xml` | `treeland_layer_shell_extension_unstable_v1` | `treeland_layer_shell_extension_manager_v1`, `treeland_layer_shell_extension_object_v1` | 合成器驱动的 layer-shell 表面交互式缩放(dock / 侧边栏 / 状态栏):begin_resize 携带 seat+serial 与单次尺寸限制,及拒绝原因 | | `treeland-xwindow-control-unstable-v1.xml` | `treeland_xwindow_control_unstable_v1` | `treeland_xwindow_control_v1` | XWayland 窗口定位:将 XWayland 窗口(按 X11 窗口 ID)移动到相对另一 surface 的位置,结果经 wl_callback 回报 | @@ -23,6 +23,25 @@ 破坏性变更按版本分组。每个版本标题下,每个受影响协议有一个子节说明变更内容、替代方案以及现有消费者如何适配。 +### 0.8.0 + +#### `treeland-virtual-output-manager-v1.xml` + +被 `treeland-output-group-manager-unstable-v1.xml` 取代;旧 v1 文件原样移至 `deprecated/`。协议围绕基于 uuid 的输出身份、合成器分配的组名、持久化配置与 registry 模式枚举重新设计。线缆级差异: + +1. 协议与接口重命名:`treeland_virtual_output_manager_v1` → `treeland_output_group_manager_unstable_v1`,接口 `treeland_virtual_output_manager_v1` → `treeland_output_group_manager_v1`、`treeland_virtual_output_v1` → `treeland_output_group_v1`;接口版本重置为 1。 +2. 组改由合成器分配的名字寻址,而非客户端传入:`create_group` 只接收初始 `source` uuid(空表示无源),合成器通过新增的 `name` 事件告知它选定的名字,该事件在对象上的任何其他事件之前发送;新增 `get_group` 请求按该名字获取组对象。`create_virtual_output` 的 `name` 参数以及 `invalid_group_name`/`name_exists` 管理器错误被移除。 +3. 输出身份由名字字符串改为 uuid:`create_virtual_output` 的 `name`/`outputs` 参数、`virtual_output_list` 的 `names` 数组、`outputs` 事件的 `outputs` 数组均被移除。`set_source`、`add_output`、`remove_output` 以及 `state` 事件(组无可用源时源为空)均以 uuid 字符串引用输出。原先 `outputs[0] = 源,outputs[1..] = 镜像` 的位置约定,改为显式的源加有序镜像列表。 +4. 镜像 uuid 需经 `treeland-output-uuid-head-unstable-v1` 获取,而非 `treeland-output-uuid-unstable-v1`:复制模式生效期间合成器只为源输出暴露 `wl_output`,镜像输出无法通过基于 `wl_output` 的协议识别。 +5. 枚举改为 registry 推送模型:`get_virtual_output_list` 与 `virtual_output_list` 快照事件移除,`get_virtual_output` 由 `get_group` 取代。绑定 manager 时合成器为每个已存在的组发送一个携带组名的 `group_added` 事件,随后发送单个 `groups_done` 事件;任何客户端创建组时再次发送 `group_added`;组被解散时发送 `group_removed`(携带组名)。`group_added` 只携带组名而不携带 `new_id` 对象,客户端用 `get_group` 获取组对象。这消除了「先取列表再查找」的 TOCTOU 窗口。 +6. 增量镜像事件改为单个全量状态事件:`output_added`/`output_removed` 移除,单个 `state` 事件携带有效源 uuid、数量与按列表顺序拼接的定长 uuid;创建时与每次变更后各发送一次。每个组对象上新增 `done` 事件,标记初始状态批次(`name`、`state`、`done`)的结束。 +7. 变更改为排队 + 提交:`set_source`、`add_output`、`remove_output` 不再立即生效,而是排队变更,由新增的 `commit` 请求按队列顺序应用。失败的变更被跳过并发 `operation_failed` 事件,其余变更仍会应用;每次改变有效状态的 commit 之后发送一次 `state` 事件。排队不会预留输出:已在其他组排队的输出在该组 commit 之前不视为被占用,因此两个组排队同一输出时先 commit 者胜出,后者报 `output_in_use`。 +8. 配置持久化并与运行时有效态分离:合成器跨重启保存每个组的名字、配置的源与配置的镜像列表,并在启动时恢复,因此组在 bind 时以 `group_added` 重新出现,输出恢复后自动生效。单个 `state` 事件只报告运行时有效态——拔出或禁用的输出保留配置但从该事件中消失。当配置源不可用时,合成器将第一个可用镜像(配置列表顺序最靠前的)提升为有效源;提升是临时的(配置源恢复后重新夺回源角色)还是永久的(被选中的输出成为新的配置源),属合成器实现细节。要将被提升的镜像固化永久为源,客户端对其 uuid 调用 `set_source` 并提交;当该输出是当前有效源时该变更幂等成功。 +9. manager 上的失败均为非致命:组的 `error` 事件及 `invalid_group_name`(0)、`invalid_screen_number`(1)、`invalid_output`(2)错误码移除,manager 不再有致命 `error` 枚举。业务失败——`invalid_output`、`duplicate_output`、`output_in_use`、`not_in_group`、`invalid_name`——通过新增的 `operation_failed` 事件(code + uuid)上报,任何失败都不会终止连接。`create_group` 的 source 失败仍会创建组但源为空;`get_group` 名字无效仍会创建对象,但该对象是无效组对象,不接收 `name` 或 `state` 事件,除 `destroy` 外的所有请求均被拒绝。仅 `already_dissolved`——对已解散组发送除 `destroy` 以外的任何请求——仍为致命协议错误。 +10. 对象生命周期重新定义:`destroy` 仅释放该客户端的句柄,既不解散组也不发送 `group_removed`;单独的 `dissolve` 请求解散整个组(丢弃所有排队变更),向每个绑定该组的对象发送 `removed`,并在 manager 上发送 `group_removed`。每个组对象的初始状态以一个批次推送:`name`、`state`、`done`;`create_group` 的 source 失败时在 `done` 之前插入一次 `operation_failed`,而无效 `get_group` 对象只接收 `operation_failed` 与 `done`。 + +消费者应将全局对象重新绑定为 `treeland_output_group_manager_v1`,用 `name` 事件(或 `get_group`)给出的合成器分配名字寻址组,从 `treeland-output-uuid-head-unstable-v1` 获取镜像 uuid,用 `set_source`/`add_output`/`remove_output` 排队变更并以 `commit` 应用,从 `state` 事件读取有效源与镜像列表(并在收到 `done` 后再基于初始状态行动),以 `dissolve` 而非销毁组对象来解散组;旧 v1 XML 在迁移期间仍会安装,但不得用于新代码。 + ### 0.7.0 #### `treeland-appearance-manager-unstable-v1.xml` diff --git a/dde/treeland-output-group-manager-unstable-v1.xml b/dde/treeland-output-group-manager-unstable-v1.xml new file mode 100644 index 0000000..de547ce --- /dev/null +++ b/dde/treeland-output-group-manager-unstable-v1.xml @@ -0,0 +1,537 @@ + + + + + + This protocol lets a privileged client such as the DDE display settings + panel organize physical outputs into mirroring groups: one source + output whose rendered content is duplicated to one or more mirror + outputs, so that several screens show the same desktop. This is the + "copy mode" of the compositor, as opposed to the default "extension" + mode where each output shows an independent desktop region. + + The protocol exposes a singleton manager global that creates and + tracks treeland_output_group_v1 objects. Each group designates a + single source output and an ordered list of mirror outputs, all + addressed by opaque uuid strings. While copy mode is engaged the + compositor exposes a wl_output only for the source output; mirror + uuids come from treeland-output-uuid-head-unstable-v1 instead (see + treeland_output_group_v1 for details). An output may belong to at + most one group at a time, and a group drives copy mode only while + it has both a source and at least one mirror. + + The compositor persists the set of groups and their configuration + (name, configured source uuid, and configured mirror uuid list) + across compositor restarts and restores them on startup, so groups + survive reboots and reappear as group_added events on bind; this + lets a privileged client configure mirroring once and have it + reapply automatically later. An output must be connected and + enabled when it is first added to a group (via create_group, + set_source, or add_output); once configured it is kept across + unplug/replug and compositor restart and auto-resumes when the + output returns. The name never changes; the configured source and + mirror list persist until a client changes them with set_source, + add_output, remove_output, or dissolve. + + The terms "must", "must not", "should", and "may" in this document are + to be interpreted as described in RFC 2119. + + Warning! This protocol is EXPERIMENTAL and under active development. + It may change at any time, including in backward-incompatible ways, + without incrementing the interface major version and without prior + notice. No compatibility guarantees of any kind are provided. Clients + and compositors must track the upstream definition in treeland-protocols + and must not rely on the current interface names, requests, events, or + semantics remaining stable across releases. + + + + + A singleton global that creates treeland_output_group_v1 + objects and broadcasts the set of existing groups to bound clients. + + On bind the compositor emits one group_added event carrying the + name of each existing group, followed by a single groups_done + event, so the client can distinguish the initial dump of + pre-existing groups from later group_added events for groups + created after the bind. When any client creates a group every + other bound client receives group_added for it, and when a + group is dissolved every bound client receives group_removed. + Clients therefore never poll for the group set; they obtain a + group object for a name with the get_group request when they + need to observe or edit that group. + + Multiple privileged clients such as the DDE display settings + panel may bind and use the protocol concurrently. Commits from + all clients are applied in arrival order; an output claimed by + one group is rejected for another with the non-fatal + output_in_use failure, so concurrent edits never terminate a + connection and clients may retry or adjust. + + + + + Destroy the treeland_output_group_manager_v1 object and + release the client's binding to the manager global. + + Existing treeland_output_group_v1 objects are not + affected and remain valid until they are destroyed or the + compositor dissolves the group. The client must destroy each + group object separately; the compositor does not cascade + destruction from the manager. + + + + + + Create a new treeland_output_group_v1 object with the + given initial source output. An empty source creates + a group with no source; the client adds mirrors afterwards + with add_output and may change the source with set_source, + then commits to apply the changes. Copy mode engages once + the group has both a source and at least one mirror after + commit. + + The compositor assigns a unique name to the new group; names + are chosen by the compositor, not by clients. The client + learns the name from the name event, which the compositor + emits on the new group object before any other event. The + name identifies the group in the manager's group_removed + event. + + If the source cannot be applied — a non-empty source that + does not identify a connected enabled output, or one that + is already a member of another group — the group is still + created with an empty source: the initial state event + reports an empty source uuid, and the compositor emits an + operation_failed event on the new group object before its + done event, with invalid_output when the uuid does not + identify a connected enabled output or output_in_use when + it is already claimed by another group. The client may + later set a source with set_source or add mirrors with + add_output, then commit to apply them. + + On success the compositor broadcasts a group_added event + carrying the new group's name to every other bound client; + the caller learns the name from the name event on its new + group object and does not get a group_added event for it. + Other clients obtain a group object for that name with + get_group. + + + + + + + + Create a new treeland_output_group_v1 object bound to the + existing group with the given name, for example a group + created by another client or one whose object the client + has since destroyed. On success the compositor sends the + initial state of the group on the new object: one name event, + one state event carrying the effective source and the full + mirror list in list order, then one done event marking the + end of the initial batch. + + The name is the compositor-assigned name reported by the + name event on group objects. If the name is empty, or does + not identify an existing group at the time the request is + processed — for example because the group was dissolved + between the group_removed event and this request — the + object is still created, but it is an invalid group object: + the compositor sends no name event and no state event, + emits an operation_failed event with invalid_name before + the done event, and afterwards rejects every request except + destroy with operation_failed invalid_name. The client + should destroy the object. + + On success the returned object is an independent handle: + destroying it does not dissolve the group, and several + clients may hold objects bound to the same group at the + same time. + + + + + + + + Emitted when an output group is created by any client. Each + bound client receives this event once per existing group on + bind, and again whenever a new group is created — except for + the client that created the group, which learns the name + from the name event on its create_group return object + instead. + + The name argument is the compositor-assigned name of the new + group. A client that wants to observe or edit the group + obtains a group object with the manager's get_group request. + + + + + + + Emitted exactly once, after the compositor has finished + sending the bind-time group_added events for all groups that + existed when the client bound. A client receiving this event + knows it has seen the complete initial set and that any + later group_added is for a group created after the bind. + + + + + + Emitted when an output group is dissolved, whether because a + client sent the dissolve request or because the compositor + removed it as a whole. Every bound client receives this event + for the group; any group objects that client holds for it + receive a removed event and must be destroyed afterwards. + + + + + + + + Represents one mirroring group: a source output, identified by + its uuid, whose content is duplicated to an ordered list of + mirror outputs, each identified by its uuid. Output uuids are + never empty; an empty uuid in events represents the absence of + a source. + + While the group has both a source and at least one mirror, the + compositor exposes a wl_output global only for the source + output. Mirror outputs have no wl_output while copy mode is + engaged, so clients cannot use wl_output-based protocols + (including treeland-output-uuid-unstable-v1, which requires a + wl_output) to identify them; they must instead obtain mirror + uuids from the treeland-output-uuid-head-unstable-v1 protocol, + which resolves a zwlr_output_head_v1 to the same uuid. A + configured mirror only loses its wl_output while copy mode is + actually engaged; while the group is inert (no effective source + or no effective mirrors) the mirror retains its wl_output and + behaves as a normal extension output. When copy mode exits — + the last mirror is removed, the effective source becomes empty, + or the group is dissolved — the affected outputs become normal + extension outputs again and their wl_output globals come back. + + The group keeps a configured source (zero or one) and a + configured mirror list (zero or more). set_source, add_output, + and remove_output queue changes that take effect only when the + client sends commit. The state event reports the effective + runtime state, not the configured state: a configured source or + mirror whose output is currently unplugged or disabled is + absent from that event but remains in the persisted + configuration, so copy mode auto-resumes when the output comes + back. An output may belong to at most one group at a time; one + already in another group fails with the output_in_use failure. + + Pending set_source, add_output, and remove_output requests that + cannot be applied because of transient output state (e.g. an + output unplugged between the request and the commit) are + reported via a non-fatal operation_failed event when the commit + is processed; the group is otherwise unchanged and the client + may retry. Only already_dissolved — sending any request other + than destroy on a dissolved group — is a fatal protocol error. + + On creation the compositor sends the current state as one + initial batch: one name event carrying the compositor-assigned + group name, then one state event carrying the effective source + (empty uuid when none is available) and the full mirror list, + followed by one done event marking the end of the initial + batch. If the create_group source was rejected, an + operation_failed event is sent before the done event; for an + invalid group object from get_group the batch is only an + operation_failed event followed by done. Later changes are + reported by further state events; the name never changes. + + Several group objects may be bound to the same group, for example + one from create_group and others from get_group; they all + observe the same state. Destroying one + object only releases that client's handle. The dissolve request tears down the group as a + whole, emitting removed to every bound group object and + group_removed on the manager. After removed the object is inert: + only destroy is valid, and anything else is rejected with the + already_dissolved error. + + When a member output is unplugged or disabled the compositor + updates the effective runtime state without dissolving the + group: an unavailable mirror is dropped from the mirror list, + and when the configured source becomes unavailable the + compositor auto-promotes the first available mirror — the + earliest one in the configured mirror list order — to the + effective source, reporting its uuid as the source and + dropping it from the mirror list in the same state event. + Whether the promotion is temporary — the configured source + reclaims the role when it returns — or permanent — the + selected output becomes the new configured source — is a + compositor implementation detail. Clients observe only the + effective source and mirror list carried by each state event + and must not assume either behavior. To pin a particular output + as the permanent source, a client calls set_source with its + uuid and then commits; if that output is currently the + effective source the request succeeds idempotently. + + The source uuid is empty only when the group has no available + output at all: no configured source is available (or none is + configured) and there is no available mirror to promote. The + group itself remains. Client-driven remove_output, by contrast, + removes the output from the persisted configuration. + + + + + Raised as fatal protocol errors on the group object. + + + + + + + Returned by the operation_failed event when a create_group + source, get_group, or a committed set_source, add_output, or + remove_output request cannot be applied, or when a request + is sent to an invalid group object. These reflect transient + or dynamic output state (e.g. an output unplugged between + the request and the commit, or already claimed by another + group), not a protocol violation; the client may retry or + adjust and the connection is not terminated. + + + + + + + + + + + Destroy this group object and release the client's handle. + The group itself is not affected: copy mode continues, other + handles remain valid, and the compositor does not emit + group_removed. Use dissolve to tear down the group as a whole. + It is safe to destroy an object that has already received the + removed event. + + + + + + Queue the output identified by the given uuid as the + configured source of the group, replacing any current + configured source. The change takes effect only when the + client sends commit. The previous configured source, if + any, becomes a mirror: it is appended to the end of the + mirror list, and when its output is available it keeps + participating in copy mode as an effective mirror. + + If the request cannot be applied at commit time, the + compositor emits an operation_failed event and leaves the + pending change unapplied: invalid_output if the uuid does + not identify a connected enabled output, duplicate_output + if the output is already in this group and is not the + current effective source (to make a current mirror the + configured source, remove it first with remove_output and + then call set_source), or output_in_use if the output + belongs to another group. + + If the output is already the current effective source, the + request succeeds idempotently: the output becomes the + configured source, any previous configured source becomes a + mirror. This lets a client pin an auto-promoted mirror as + the permanent source. + + A later set_source replaces an earlier uncommitted one; the + compositor applies only the most recent source designation + on commit. + + + + + + + Queue the output identified by the given uuid to be appended + to the end of the group's mirror list at commit time. + + If the request cannot be applied at commit time, the + compositor emits an operation_failed event and leaves the + pending addition unapplied: invalid_output if the uuid does + not identify a connected enabled output, duplicate_output + if the output is already in this group, or output_in_use if + it belongs to another group. + + + + + + + Queue the removal of the output identified by the given + uuid from the configured membership of the group at commit + time. If the output is not a configured member at commit + time, the compositor emits an operation_failed event with + not_in_group and the removal is unapplied; this covers an + output that was never added as well as one whose + configuration was already removed. + + If the removed output is the configured source or a + configured mirror, the compositor re-resolves the effective + source and mirror list as described in the interface + description and reports the result in a state event. + + + + + + + Apply all pending set_source, add_output, and remove_output + requests in the order they were queued. Requests that cannot + be applied are skipped, each reported by an operation_failed + event, and the remaining requests are still applied. The + queue is then cleared. If any applied change altered the + effective source or mirror list, the compositor emits one + state event carrying the resulting effective state; + otherwise no state event is emitted. + + Sending commit with an empty queue is valid and does + nothing. dissolve discards the pending queue. + + + + + + Tear down the group: exit copy mode, remove it from the + manager, and release the affected outputs. The configured + source and mirror list are discarded and the group is not + restored on compositor restart. The compositor emits removed + to every group object bound to this group and group_removed + on the manager. After removed the object is inert; only + destroy is valid, and anything else — including dissolve + again — is rejected with already_dissolved. + + + + + + Reports the name the compositor assigned to this group. + Emitted exactly once on each group object, before any other + event, so the creator of the group learns its name without a + group_added event. The name is unique among existing groups + and never changes; a dissolved group's name may be reused by + a group created later. It identifies the group in the + manager's group_removed event and is the name accepted by + the manager's get_group request. + + + + + + + Emitted exactly once on each group object, immediately after + the initial name and state events — or, for an invalid + group object, after the operation_failed event — marking + the end of the initial state batch. A client receiving this + event knows it has the complete initial state of the group + and that any later state event reflects a change. It is + sent even when the initial source is empty and the mirror + list is empty. + + + + + + Reports the current effective runtime state of the group in + a single atomic event: the effective source uuid and the + complete ordered list of effective mirror uuids. The client + replaces any previously known state with the contents of + this event; the source and the mirror list are always + consistent within one emission. + + The effective source is the configured source if it is + currently connected and enabled; otherwise it is an + auto-promoted mirror or empty, as described in the + interface description. The mirror list holds the configured + mirrors that are currently connected and enabled, in list + order; the source output, if any, is not part of the list, + and a count of zero means the group has no effective + mirrors. + + Emitted once on creation and again whenever the effective + source or the effective mirror list changes: after a + successful commit, and when any member output is unplugged, + disabled, re-enabled, or replugged. + + The uuids argument contains the concatenation of count + uuids, one per mirror, in list order. Output uuids assigned + through treeland-output-uuid-unstable-v1 are fixed-length + and never contain the NUL byte; the compositor must preserve + this property for every uuid it assigns, and clients may rely + on it to split the array. Each uuid is therefore the array + size in bytes divided by count, and parsing needs no + separators: the client reads count fixed-length uuids back + to back. The count argument always matches the number of + uuids in the array; a client may use it to validate the + array size. The list order follows the order in which + mirrors were added; unavailable mirrors are filtered out + without shifting the relative order of the others. + + + + + + + + + Emitted to every group object bound to the group when the + group is dissolved. After this event the object is inert and + the client must destroy it. + + + + + + Emitted when a create_group source, get_group, or a + committed set_source, add_output, or remove_output request + cannot be applied, or when a request is sent to an invalid + group object. For a committed set_source, add_output, or + remove_output request the cause is a transient or dynamic + output state, such as an output being unplugged between the + request and the commit or already claimed by another group; + that change is skipped and the client may retry. For a + failed create_group source the group is created with an + empty source and the event is emitted on the new group + object before its done event. For an invalid get_group the + object is invalid, no name event is sent, and every later + request except destroy is rejected with invalid_name. This + is not a protocol error and the connection is not + terminated. + + + + + + diff --git a/dde/treeland-virtual-output-manager-v1.xml b/deprecated/treeland-virtual-output-manager-v1.xml similarity index 100% rename from dde/treeland-virtual-output-manager-v1.xml rename to deprecated/treeland-virtual-output-manager-v1.xml diff --git a/public/treeland-output-uuid-unstable-v1.xml b/public/treeland-output-uuid-unstable-v1.xml index 87f44fc..d54a00c 100644 --- a/public/treeland-output-uuid-unstable-v1.xml +++ b/public/treeland-output-uuid-unstable-v1.xml @@ -12,6 +12,11 @@ and output reconnections, so clients and the compositor can persist per-output settings and restore them after a restart. + Uuids are fixed-length, exactly 36 bytes, and never contain the + NUL byte; the canonical form is a lowercase 8-4-4-4-12 hexadecimal + string with hyphens, for example + 8dd6d52e-1f60-4649-9c2c-6fb7e055272e. + The uuid is unique among the currently connected outputs: the compositor must ensure that no two connected outputs report the same uuid at the same time. The compositor assigns the uuid to each