Files

190 lines
11 KiB
Markdown
Raw Permalink 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 架构
## 定位
`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`。
```text
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 合并、取消和产品行为。
## 目录和职责
```text
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_GETMINMAXINFO` track size 修正和
`WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE` live 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 的加载策略、搜索路径或
用户目录信任模型。
## 设计约束
1. 平台判断和后端判断是两层条件。Linux 不等于 X11,非 X11 也不等于 Wayland。
2. 只有 Avalonia 公共 API 无法表达、且涉及原生调用或原生协议细节的能力才进入 Native。
3. Window 相关 native 能力可以进入 Native;限制点是不能在 Native 层拥有 Window/Dialog 控件策略。
4. Native 层可以持有原生资源或可释放 hook,但 owner 必须清晰,上层必须有明确释放点。
5. Native 层不修改 Avalonia control properties,不决定 resize band、主题 token 或 Dialog/Window 行为。
6. Native 层不订阅控件业务事件;需要事件驱动时,上层负责订阅 Avalonia 事件并调用 Native 能力。
7. 原生调用失败不能让 Window manager 的更新队列永久卡死;异步/延迟状态更新需要 `try/finally` 恢复标志。
8. 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 的架构契约。