11 KiB
AtomUI.Native 架构
定位
AtomUI.Native 是 AtomUI 的内部底层 OS 能力封装层。它按“是否属于操作系统、窗口系统或原生运行时
调用”划定边界,而不是按 Window、Dialog、主题、控件等上层概念划定边界。只要实现需要直接接触
P/Invoke、原生 handle、原生结构体、平台协议对象、native hook 或平台错误码,就应优先收敛到
AtomUI.Native。
窗口相关能力可以进入 AtomUI.Native。Win32、DWM、Objective-C runtime、Xlib/XCB、Wayland protocol
等窗口系统原生调用,本质上是底层 OS/backend 能力,不应因为服务于 Window/Dialog 就回到控件层。
AtomUI.Native 不决定控件策略、主题策略或用户可观察行为。上层库负责选择何时启用某个原生能力,
并承担 Avalonia 控件生命周期、事件订阅、属性同步和模板状态。Native 层提供能力,不拥有业务状态。
Avalonia drawn decorations、TopLevelHost、resize grip layer 等 Desktop 控件私有结构发现通常保留在
AtomUI.Desktop.Controls。只有当实现必须解析原生协议对象、调用原生 API,或把 P/Invoke/协议细节
压回 Native 边界时,才应进入 AtomUI.Native。
AtomUI.Desktop.Controls
- 选择 Window chrome manager
- 管理 Window 事件、属性和模板生命周期
- 决定何时启用某个原生能力
|
v
AtomUI.Native
- 校验原生 handle / 参数
- 调用 Win32、Objective-C、Xlib/XCB 或 NWayland
- 封装原生结构体、常量、消息和协议对象
- 管理原生资源、hook 和错误边界
所有成员目前都是 internal,通过 InternalsVisibleTo 向 AtomUI.Desktop.Controls 和目标程序集名称
AtomUI.Mobile.Controls 开放。当前仓库没有 Mobile 实现消费者;这项预留只定义未来可见性边界,不是 Mobile 源码、
平台验证或发布证据。它是实现包,不是公共 Native SDK。
能力与策略分界
| 层级 | 可以做 | 不应该做 |
|---|---|---|
AtomUI.Native |
封装 OS/backend 原生调用、窗口系统底层能力、消息结构体、hook/façade、句柄校验、原生资源释放 | 决定窗口主题、Dialog 行为、控件状态机、Token、默认平台策略 |
AtomUI.Core |
AtomUI 基础设施、主题/Token/语言、资源、动画、应用启动默认选项;必要时调用 AtomUI.Native 获取底层 OS 能力 |
直接散落 P/Invoke、原生结构体、平台协议细节,或依赖具体控件生命周期 |
AtomUI.Desktop.Controls |
Window/Dialog/Popup 等桌面控件语义、生命周期、何时调用 Native 能力 | 直接维护 P/Invoke、原生结构体或重复实现 OS 协议 |
AtomUI.Mobile.Controls(目标) |
Viewport、Gesture、Overlay、Theme、owner 生命周期、平台默认策略,以及何时调用 Native 能力 | 直接维护 P/Invoke、原生 handle/结构体、不可释放 hook,或把 OS 类型暴露到 Public Control API |
AtomUI.Core 可以依赖 AtomUI.Native。Core 是 AtomUI 的基础设施层,不等于必须排除所有 Window
或 OS 概念;判断标准是能力本身是否属于底层 OS/native 操作。例如安全打开文件、解析真实路径、读取系统
窗口 metric、封装窗口系统原生调用,都可以由 Core 通过 AtomUI.Native 使用。相反,Dialog 的默认尺寸、
最大/最小尺寸策略、控件状态同步和模板生命周期仍属于上层控件库。
当前依赖
| 依赖 | 用途 | 影响 |
|---|---|---|
Avalonia |
Window、WindowBase、geometry 和 platform handle |
AtomUI 基础依赖 |
NWayland |
WlSurface、WlCompositor、WlRegion |
会进入所有引用 Native 的依赖图 |
AtomUI.Native 当前与 Avalonia.Wayland 共享 NWayland 类型。具体版本以集中包管理和最终 restore
依赖图为准;升级任一依赖时必须同步核对类型版本和跨包 ABI,不能把当前偶然一致视为兼容保证。
上层项目是否引用 AtomUI.Native 取决于是否存在真实底层 OS/native 调用点。不能为了让 Core 看起来
“纯净”而把 P/Invoke、native struct 或平台错误处理留在 Core;也不能因为某个 native 能力最终服务于
Window/Dialog,就把底层 OS 细节放回 AtomUI.Desktop.Controls。
未来 Mobile adapter 同样遵循这条边界:优先使用 Avalonia 公共 API;只有 Safe Area、system bar、input pane、返回手势、
haptic 或生命周期能力确实需要原生调用时才进入 Native。Native 负责底层资源和确定释放,Mobile Runtime 负责
TopLevel owner、订阅、snapshot 合并、取消和产品行为。
目录和职责
src/AtomUI.Native/
├── AtomUI.Native.csproj
├── NativeWindowSizing.cs
├── WindowExtensions.cs
├── FileSystem/
│ └── NativeFileSystem.cs
├── Windows/
│ ├── WindowsCsdSizingHook.cs
│ ├── WindowUtils.Interop.cs
│ └── WindowUtils.Windows.cs
├── MacOS/
│ ├── WindowUtils.Interop.cs
│ └── WindowUtils.MacOS.cs
└── Linux/
├── WindowUtils.Interop.cs
├── WindowUtils.Linux.cs
├── XcbConnectionHolder.cs
├── WaylandWindowReflectionExtensions.cs
└── WaylandWindowUtils.cs
Windows
SetWindowIgnoreMouseEventsWindows()修改WS_EX_TRANSPARENT/WS_EX_LAYERED。WindowsCsdSizingHook封装 opt-in 的WM_GETMINMAXINFOtrack size 修正和WM_ENTERSIZEMOVE/WM_EXITSIZEMOVElive resize 信号。- Windows 目录可以封装 DWM 属性、系统 metric、Win32 消息结构体和受限的可释放 hook。
- Windows 目录不决定 live resize 策略、合成后端策略、CSD/SSD 策略或控件行为。
- 窗口装饰与 resize hit-test 由 Avalonia CSD 管理;AtomUI 不处理
WM_NCCALCSIZE或WM_NCHITTEST,不返回 resize hit-test。 - 标题栏按钮通过 Avalonia 公共
WindowDecorationProperties.ElementRole接入原生行为, 不需要 AtomUI 自定义 caption WndProc hook。 - 允许 Native 层提供窄范围、显式 opt-in、可释放的 sizing/message helper,例如只处理
WM_GETMINMAXINFO或 live resize 生命周期信号;这类 helper 不能实现第二套 window chrome。
macOS
- Objective-C runtime 调用
NSWindow.ignoresMouseEvents。 - 查询和重排 standard window buttons。
- x64 的 32-byte
NSRect返回走objc_msgSend_stret,其他架构走objc_msgSend。
Linux/X11
- Xlib 设置 map 前初始 geometry 和
WM_NORMAL_HINTS。 _GTK_FRAME_EXTENTS告知 Mutter/KWin CSD 阴影范围。- 进程级独立 XCB 连接复用 SHAPE input-region 请求。
X11 能力必须以 IPlatformHandle.HandleDescriptor == "XID" 为前置条件,不能只判断 Linux OS。
Linux/Wayland
WaylandWindowUtils 只接受已经解析出的 NWayland WlSurface/WlCompositor,创建 region 并调用
SetInputRegion()。Avalonia 私有字段解析集中在 WaylandWindowReflectionExtensions,不能扩散到
WaylandWindowUtils 或上层业务控件。
但 Avalonia Wayland 后端的真实 surface 属于专用 worker。当前调用方从 UI 线程越过
WXdgTopLevelProxy 直接调用真实 WlSurface,不符合上游线程和重连契约。DynamicDependency 只保证
reflection metadata 不被裁剪,不能解决该问题。正确边界应由 Avalonia persistent surface/proxy 提供
input-region 方法,并通过 WaylandWorkerClient.PostWithCommit 下发。
FileSystem
NativeFileSystem封装跨平台文件系统原生调用,例如 no-follow 打开文件和解析目录 canonical path。- 文件系统能力可以被
AtomUI.Core使用;判断标准是是否需要 P/Invoke、平台错误码、native handle 或 OS-specific flag,而不是调用方是否位于 Core。 - Native 层只负责底层安全打开、路径解析和错误转换,不决定 ThemeDefinition 的加载策略、搜索路径或 用户目录信任模型。
设计约束
- 平台判断和后端判断是两层条件。Linux 不等于 X11,非 X11 也不等于 Wayland。
- 只有 Avalonia 公共 API 无法表达、且涉及原生调用或原生协议细节的能力才进入 Native。
- Window 相关 native 能力可以进入 Native;限制点是不能在 Native 层拥有 Window/Dialog 控件策略。
- Native 层可以持有原生资源或可释放 hook,但 owner 必须清晰,上层必须有明确释放点。
- Native 层不修改 Avalonia control properties,不决定 resize band、主题 token 或 Dialog/Window 行为。
- Native 层不订阅控件业务事件;需要事件驱动时,上层负责订阅 Avalonia 事件并调用 Native 能力。
- 原生调用失败不能让 Window manager 的更新队列永久卡死;异步/延迟状态更新需要
try/finally恢复标志。 - AOT 要同时满足 metadata 保留和真实 publish;
DynamicDependency、普通 build、源码字符串测试分别只能 覆盖一部分风险。
已知结构问题
平台依赖扩散
AtomUI.Native 当前只面向桌面控件等需要原生能力的上层库开放。AtomUI.Desktop.Controls 直接引用
Avalonia.Desktop 和 Avalonia.Wayland,Browser 当前通过 WASM 内部 item 过滤部分 desktop assembly,
这只是构建补偿,不是清晰的依赖边界。
长期建议把 desktop bootstrap 与具体后端拆成可选 assembly,例如 X11、Wayland、Win32、macOS backend 包;Browser 只引用平台无关 controls,不应先引入桌面包再从 bundle 中删除 assembly。
API 命名和校验
WindowExtensions 只承担内部跨平台路由,具体实现保留在 Windows、MacOS 和 Linux 目录。
只有 Avalonia 公共 API 无法表达的能力才进入 Native;平台实现应统一校验 handle descriptor、零句柄、
尺寸范围和平台。是否继续拆分文件取决于资源所有权和生命周期复杂度,不为了形式上的分层搬移代码。
测试层级
当前 Native 相关测试主要读取源码并断言字符串。这能防止某些旧代码回归,但不能验证 ABI、线程、资源释放、 协议顺序或真实 NativeAOT。至少需要:
- 纯计算和参数校验单元测试。
- 可替换 native façade 的调用顺序/错误路径测试。
- X11/Wayland 集成测试或最小 smoke app。
- Linux/Windows/macOS 的 NativeAOT publish matrix。
依赖升级检查
Native 架构结论以 AtomUI 的平台边界、公开契约和验证矩阵为准。升级 Avalonia 或原生协议依赖时,至少重新验证:
- Wayland proxy、worker 和 persistent surface 的线程所有权与 commit 顺序。
- CSD/SSD 协商、shadow extents 和 window geometry 的状态传播。
- X11 handle descriptor、drawn decorations gating 和 input-region 行为。
- Win32/macOS 平台能力仍由公开 API 或隔离的 Native 边界承担。
外部框架的类型名可以作为调查入口,但文件路径、源码行号和某次源码快照都不是 AtomUI 的架构契约。