# 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`