Files
AtomUI/docs/modules/native/linux.md
T

139 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AtomUI.Native Linux 平台 API
## 范围
AtomUI 在 Linux 上有两条完全不同的窗口后端路径:
| 后端 | Avalonia 包 | 原生对象 | AtomUI 可使用的能力 |
|---|---|---|---|
| X11 / XWayland | `Avalonia.X11` | `IPlatformHandle`,descriptor 为 `XID` | Xlib 初始几何、`_GTK_FRAME_EXTENTS`、XCB SHAPE input region |
| 原生 Wayland | `Avalonia.Wayland` | `IWindowImpl.Handle == null` | xdg-shell move/resize、CSD/SSD、surface geometry;无公开 input-region API |
不得只用 `OperatingSystem.IsLinux()` 决定调用 X11 API。所有 Xlib/XCB 路径还必须验证窗口后端为
X11;原生 Wayland、Linux framebuffer 和 headless 后端都不是 X11。
本文以 `/workspace/projects/ReferenceProjects/Avalonia` 的 `12.1.0` tag
(commit `a21b9f573172f705a944dcc8aad7f036b9986f39`)为 Avalonia 行为事实源。
## X11 能力
### 初始窗口几何
`WindowExtensions.ConfigureLinuxInitialWindowGeometry()` 将 Avalonia DIP 尺寸转成 device pixel,
再调用 `WindowUtilsLinux.ConfigureInitialWindowGeometry()`:
1. `XOpenDisplay()` 打开独立 Xlib 连接。
2. `XSetWMNormalHints()` 写入 position、size、min/max size 和 resize increment。
3. `XMoveResizeWindow()` 在 map 前设置窗口位置和大小。
4. `XFlush()` 后关闭该次 Xlib 连接。
该逻辑依赖 XID 和全局屏幕坐标,只能由 `X11WindowChromeManager` 调用。Wayland surface 没有全局位置,
`Avalonia.Wayland.WindowImpl.Move(PixelPoint)` 明确是 no-op。
### CSD frame extents
`WindowExtensions.SetLinuxX11CsdFrameExtents()` 先验证 platform handle descriptor 为 `XID`,然后由
`WindowUtilsLinux.SetX11CsdFrameExtents()` 写入 `_GTK_FRAME_EXTENTS`。
属性格式为四个 32-bit CARDINAL,顺序是 `left, right, top, bottom`,单位为 device pixel。AtomUI 在
normal 状态写入阴影 extents;最大化和全屏写入全零,避免 Mutter/KWin 继续按旧阴影范围做放置和吸附。
### SHAPE input region
`ClickThroughShadowExtensions` 用 XCB SHAPE 的 `XCB_SHAPE_SK_INPUT` 把输入区限制为一个矩形,目的
是让视觉阴影区域的点击穿透到下方窗口,同时保留窄 resize band。
当前实现的真实生命周期:
1. `XcbConnectionHolder` 懒创建一条进程级独立 XCB 连接。
2. 首次初始化时查询并缓存 SHAPE 扩展是否可用。
3. 所有请求在 `XcbConnectionHolder.SyncRoot` 下串行执行。
4. `xcb_shape_rectangles_checked()` 替换 input region,随后检查请求并 flush。
5. 该连接在进程生命周期内复用;不是每次调用都 connect/disconnect。
`ClickThroughShadowExtensions` 监听 `Bounds`、`WindowDecorationMargin`、`WindowState`、
`TransparencyLevelHint` 以及调用方给出的额外属性。坐标先按 `RenderScaling` 从 DIP 转成 device pixel。
最大化或全屏时 input region 恢复为整个 surface。
这不是“整窗忽略鼠标”的 API。Linux 版本的 `SetWindowIgnoreMouseEventsLinux()`、
`IsWindowIgnoreMouseEventsLinux()`、`xcb_get_geometry()` 和 shape-query 旧实现已经删除;文档和新代码都
不得继续引用这些成员。
## Wayland 能力与线程模型
Avalonia 12.1 的 Wayland 后端使用专用 `AvaloniaWayland` worker 线程:
```text
UI thread WindowImpl
-> WXdgTopLevelProxy
-> WaylandWorkerClient.Marshaller
-> PostWithCommit / PostOob
-> worker-thread WSurface / WlSurface
```
源码中的关键约束:
- `WaylandWorkerClient.CreateTopLevelHandle()` 只向 UI 线程暴露 `WXdgTopLevelProxy`,注释明确说明真实
`WXdgTopLevel` 是 worker-thread state。
- 普通 surface 状态由 `PostWithCommit` 排队,与 compositor commit 保持顺序;move/resize 等需要输入
serial 的请求使用 OOB 路径。
- `WSurface.OnBeforeNewBufferAttached()` 在 attach/commit 前统一应用 frame callback、ack configure、
window geometry、viewport 和 buffer scale。
- `WindowImpl.SetShadowExtents()` 通过 proxy 更新 persistent surface;`WSurface` 再根据 shadow extents
调用 `xdg_surface.set_window_geometry`。
Wayland 核心协议虽然有 `wl_surface.set_input_region`,Avalonia 12.1 还没有公开对应 API。AtomUI 当前
`WaylandWindowReflectionExtensions` 从私有 proxy 中取出真实 target,并由 `WaylandWindowUtils` 直接调用
NWayland。这绕过了 worker marshalling 和重连模型,只能作为当前技术债记录,不能视为线程安全或受
Avalonia 支持的 Native 扩展点。后续正确实现应进入 Avalonia persistent surface/proxy,并通过
`PostWithCommit` 下发。
## Wayland CSD/SSD
Wayland 的 `NeedsManagedDecorations` 来自 xdg-decoration 协商:
- Client-side mode:请求 `TitleBar | Border | Shadow | ResizeGrips`。
- Server-side mode:`NeedsManagedDecorations=false`,由 compositor 提供 SSD。
- 协商结果变化时触发 `DrawnDecorationsRequestChanged`。
`WindowDecorations` 在 Wayland 上不是可逆的 CSD/SSD toggle。Avalonia 12.1 的
`SetWindowDecorations(None/TitleBar)` 会设置 `_csdSticky=true` 并销毁 decoration object,之后设置
`Full` 也不会恢复 SSD。因此,AtomUI 不能在 `IsCsdEnabled=False` 时无条件设置
`WindowDecorations=None`;这会把刚协商出的 SSD 立即永久切回 CSD。
## 依赖和边界
`AtomUI.Native` 当前直接引用 `NWayland 0.11.0`,而 `Avalonia.Wayland 12.1.0` 也依赖同一版本。
`NWayland` 因而会进入所有引用 `AtomUI.Native` 的依赖图,包括 Browser 的共享项目闭包。Browser 项目
目前通过内部 WASM item 过滤桌面 assembly,这能消除已知 P/Invoke collector warning,但不能消除
restore 图中的桌面依赖,也不是理想的模块边界。
长期结构应把 X11、Wayland、Win32、macOS 后端能力放到可选平台 assembly,避免
`AtomUI.Desktop.Controls`/Browser 为单个平台实现携带所有桌面后端和协议依赖。平台 manager 负责
Avalonia 控件生命周期和状态机;`AtomUI.Native` 只负责已经确定后端之后的原生调用,不负责主题、
reflection discovery 或窗口策略。
## 关键源码
### AtomUI
- `src/AtomUI.Native/Linux/WindowUtils.Interop.cs`
- `src/AtomUI.Native/Linux/WindowUtils.Linux.cs`
- `src/AtomUI.Native/Linux/XcbConnectionHolder.cs`
- `src/AtomUI.Native/Linux/ClickThroughShadowExtensions.cs`
- `src/AtomUI.Native/Linux/WaylandWindowUtils.cs`
- `src/AtomUI.Native/WindowExtensions.cs`
- `src/AtomUI.Desktop.Controls/Window/X11WindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/WaylandWindowChromeManager.cs`
- `src/AtomUI.Desktop.Controls/Window/WaylandWindowReflectionExtensions.cs`
### Avalonia 12.1.0
- `src/Avalonia.Wayland/WindowImpl.cs`
- `src/Avalonia.Wayland/WindowImpl.Sink.cs`
- `src/Avalonia.Wayland/Server/WaylandWorkerClient.cs`
- `src/Avalonia.Wayland/Server/Persistent/IWXdgTopLevel.cs`
- `src/Avalonia.Wayland/Server/Persistent/WSurface.cs`
- `src/Avalonia.Controls/TopLevelHost.Decorations.cs`
- `src/Avalonia.X11/X11Window.cs`