mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-19 23:30:44 +08:00
513 lines
13 KiB
Markdown
513 lines
13 KiB
Markdown
# AtomUI.Native — Linux 平台 API 文档
|
||
|
||
## 概述
|
||
|
||
Linux 平台通过 XCB(X C Binding)库调用 X11 协议的 **SHAPE 扩展**,利用输入形状(Input Shape)机制实现窗口级鼠标事件穿透。所有 API 均标注 `[SupportedOSPlatform("linux")]`。
|
||
|
||
**依赖的原生库:**
|
||
|
||
| 库 | 说明 |
|
||
|----|------|
|
||
| `libxcb.so.1` | XCB 核心库(X11 连接、窗口几何查询等) |
|
||
| `libxcb-shape.so.0` | XCB SHAPE 扩展库(窗口形状操作) |
|
||
|
||
---
|
||
|
||
## 1. 公共扩展方法
|
||
|
||
### 1.1 SetWindowIgnoreMouseEvents
|
||
|
||
设置窗口是否忽略(穿透)鼠标输入事件。
|
||
|
||
```csharp
|
||
internal static void SetWindowIgnoreMouseEvents(this WindowBase window, bool flag)
|
||
```
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `window` | `WindowBase` | Avalonia 窗口实例 |
|
||
| `flag` | `bool` | `true` 启用穿透,`false` 禁用穿透 |
|
||
|
||
**异常:**
|
||
|
||
| 异常类型 | 条件 |
|
||
|----------|------|
|
||
| `ArgumentException` | 窗口句柄无效(`IntPtr.Zero`) |
|
||
| `InvalidOperationException` | XCB 连接失败 |
|
||
| `InvalidOperationException` | SHAPE 扩展不可用 |
|
||
| `InvalidOperationException` | 禁用穿透时获取窗口几何信息失败 |
|
||
|
||
**Linux 实现原理:**
|
||
|
||
利用 X11 SHAPE 扩展的 `Input Shape`(输入形状)概念:
|
||
|
||
- **启用穿透**:将窗口的输入形状设置为 **空区域**(`rects_len=0, rects=NULL`),所有鼠标事件将穿过该窗口
|
||
- **禁用穿透**:将窗口的输入形状恢复为 **整个窗口区域**(一个覆盖全窗口的矩形)
|
||
|
||
**启用穿透流程:**
|
||
|
||
```
|
||
1. xcb_connect() → 连接 X 服务器
|
||
2. IsShapeExtensionAvailable() → 检查 SHAPE 扩展
|
||
3. xcb_shape_rectangles_checked(
|
||
op=SET, kind=INPUT,
|
||
rects_len=0, rects=NULL) → 设置输入形状为空
|
||
4. xcb_request_check() → 检查错误
|
||
5. xcb_flush() → 刷新请求
|
||
6. xcb_disconnect() → 断开连接
|
||
```
|
||
|
||
**禁用穿透流程:**
|
||
|
||
```
|
||
1. xcb_connect() → 连接 X 服务器
|
||
2. IsShapeExtensionAvailable() → 检查 SHAPE 扩展
|
||
3. xcb_get_geometry() → 获取窗口尺寸
|
||
4. 创建 xcb_rectangle_t { 0, 0, width, height }
|
||
5. Marshal.AllocHGlobal() → 分配非托管内存
|
||
6. xcb_shape_rectangles_checked(
|
||
op=SET, kind=INPUT,
|
||
rects_len=1, rects=&rect) → 恢复输入形状为全窗口
|
||
7. Marshal.FreeHGlobal() → 释放非托管内存
|
||
8. xcb_request_check() → 检查错误
|
||
9. xcb_flush() → 刷新请求
|
||
10. xcb_disconnect() → 断开连接
|
||
```
|
||
|
||
---
|
||
|
||
### 1.2 IsWindowIgnoreMouseEvents
|
||
|
||
查询窗口当前是否处于鼠标事件穿透状态。
|
||
|
||
```csharp
|
||
internal static bool IsWindowIgnoreMouseEvents(this WindowBase window)
|
||
```
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `window` | `WindowBase` | Avalonia 窗口实例 |
|
||
|
||
**返回值:**
|
||
|
||
| 类型 | 说明 |
|
||
|------|------|
|
||
| `bool` | `true` 表示窗口正在穿透鼠标事件(输入形状矩形数量为 0) |
|
||
|
||
**Linux 实现原理:**
|
||
|
||
查询窗口 Input Shape 中的矩形数量:
|
||
|
||
```
|
||
1. xcb_connect() → 连接 X 服务器
|
||
2. IsShapeExtensionAvailable() → 检查 SHAPE 扩展
|
||
3. xcb_shape_get_rectangles(kind=INPUT) → 查询输入形状矩形
|
||
4. xcb_shape_get_rectangles_rectangles_length() → 获取矩形数量
|
||
5. numRects == 0 → true (穿透中)
|
||
6. xcb_disconnect() → 断开连接
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 内部辅助方法
|
||
|
||
### 2.1 IsShapeExtensionAvailable
|
||
|
||
检查 X 服务器是否支持 SHAPE 扩展。
|
||
|
||
```csharp
|
||
[SupportedOSPlatform("linux")]
|
||
private static bool IsShapeExtensionAvailable(IntPtr connection)
|
||
```
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `connection` | `IntPtr` | XCB 连接指针 |
|
||
|
||
**返回值:** `true` 表示 SHAPE 扩展可用。
|
||
|
||
**实现:** 调用 `xcb_shape_query_version` 并检查是否返回有效的版本信息。
|
||
|
||
---
|
||
|
||
### 2.2 GetWindowGeometry
|
||
|
||
获取窗口的几何信息(位置和尺寸)。
|
||
|
||
```csharp
|
||
[SupportedOSPlatform("linux")]
|
||
private static IntPtr GetWindowGeometry(IntPtr connection, uint windowId)
|
||
```
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `connection` | `IntPtr` | XCB 连接指针 |
|
||
| `windowId` | `uint` | X11 窗口 ID |
|
||
|
||
**返回值:** 指向 `xcb_get_geometry_reply_t` 的非托管内存指针。调用方负责使用 `Marshal.FreeHGlobal` 释放。
|
||
|
||
---
|
||
|
||
## 3. P/Invoke 声明
|
||
|
||
> 命名空间:`AtomUI.Native.Linux`
|
||
> 类:`internal static class WindowUtilsInterop`
|
||
> 平台:`[SupportedOSPlatform("linux")]`
|
||
|
||
### 3.1 XCB 核心函数 (libxcb.so.1)
|
||
|
||
#### xcb_connect
|
||
|
||
连接到 X 服务器。
|
||
|
||
```csharp
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern IntPtr xcb_connect(string display, IntPtr screen);
|
||
```
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `display` | `string` | X 显示标识(空字符串使用默认 `$DISPLAY`) |
|
||
| `screen` | `IntPtr` | 屏幕号输出指针(可传 `IntPtr.Zero`) |
|
||
|
||
**返回值:** XCB 连接指针。
|
||
|
||
---
|
||
|
||
#### xcb_disconnect
|
||
|
||
断开 X 服务器连接。
|
||
|
||
```csharp
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern void xcb_disconnect(IntPtr connection);
|
||
```
|
||
|
||
---
|
||
|
||
#### xcb_connection_has_error
|
||
|
||
检查连接是否有错误。
|
||
|
||
```csharp
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern int xcb_connection_has_error(IntPtr connection);
|
||
```
|
||
|
||
**返回值:** `0` 表示无错误,非零值表示有错误。
|
||
|
||
---
|
||
|
||
#### xcb_flush
|
||
|
||
刷新待发送的请求到 X 服务器。
|
||
|
||
```csharp
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern int xcb_flush(IntPtr connection);
|
||
```
|
||
|
||
---
|
||
|
||
#### xcb_request_check
|
||
|
||
检查已发送请求的错误状态。
|
||
|
||
```csharp
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern IntPtr xcb_request_check(IntPtr connection, xcb_void_cookie_t cookie);
|
||
```
|
||
|
||
**返回值:** 错误指针,`IntPtr.Zero` 表示无错误。
|
||
|
||
---
|
||
|
||
#### xcb_get_geometry / xcb_get_geometry_reply
|
||
|
||
获取窗口几何信息(位置、尺寸、深度等)。
|
||
|
||
```csharp
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern uint xcb_get_geometry(IntPtr connection, uint window);
|
||
|
||
[DllImport("libxcb.so.1")]
|
||
internal static extern IntPtr xcb_get_geometry_reply(IntPtr connection, uint cookie, IntPtr error);
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 XCB SHAPE 扩展函数 (libxcb-shape.so.0)
|
||
|
||
#### xcb_shape_query_version / _reply
|
||
|
||
查询 SHAPE 扩展版本。
|
||
|
||
```csharp
|
||
[DllImport("libxcb-shape.so.0")]
|
||
internal static extern uint xcb_shape_query_version(IntPtr connection);
|
||
|
||
[DllImport("libxcb-shape.so.0")]
|
||
internal static extern IntPtr xcb_shape_query_version_reply(
|
||
IntPtr connection, uint cookie, IntPtr error);
|
||
```
|
||
|
||
---
|
||
|
||
#### xcb_shape_rectangles_checked
|
||
|
||
设置窗口的形状区域(核心操作)。
|
||
|
||
```csharp
|
||
[DllImport("libxcb-shape.so.0")]
|
||
internal static extern xcb_void_cookie_t xcb_shape_rectangles_checked(
|
||
IntPtr c,
|
||
xcb_shape_op_t operation,
|
||
xcb_shape_kind_t destination_kind,
|
||
byte ordering,
|
||
uint destination_window,
|
||
short x_offset,
|
||
short y_offset,
|
||
uint rectangles_len,
|
||
IntPtr rectangles);
|
||
```
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `c` | `IntPtr` | XCB 连接 |
|
||
| `operation` | `xcb_shape_op_t` | 形状操作类型(通常使用 `SET`) |
|
||
| `destination_kind` | `xcb_shape_kind_t` | 形状类别(`INPUT` 用于鼠标穿透) |
|
||
| `ordering` | `byte` | 矩形排序方式 |
|
||
| `destination_window` | `uint` | 目标窗口 ID |
|
||
| `x_offset` | `short` | X 偏移 |
|
||
| `y_offset` | `short` | Y 偏移 |
|
||
| `rectangles_len` | `uint` | 矩形数量(`0` = 空区域 = 穿透) |
|
||
| `rectangles` | `IntPtr` | 矩形数组指针(`IntPtr.Zero` = 空) |
|
||
|
||
---
|
||
|
||
#### xcb_shape_get_rectangles / _reply / _rectangles_length
|
||
|
||
查询窗口的形状矩形。
|
||
|
||
```csharp
|
||
[DllImport("libxcb-shape.so.0")]
|
||
internal static extern uint xcb_shape_get_rectangles(
|
||
IntPtr connection, uint window, xcb_shape_kind_t shapeKind);
|
||
|
||
[DllImport("libxcb-shape.so.0")]
|
||
internal static extern IntPtr xcb_shape_get_rectangles_reply(
|
||
IntPtr connection, uint cookie, IntPtr error);
|
||
|
||
[DllImport("libxcb-shape.so.0")]
|
||
internal static extern int xcb_shape_get_rectangles_rectangles_length(IntPtr reply);
|
||
```
|
||
|
||
---
|
||
|
||
### 3.3 结构体定义
|
||
|
||
#### xcb_connection_t
|
||
|
||
XCB 连接句柄(不透明类型)。
|
||
|
||
```csharp
|
||
internal struct xcb_connection_t { }
|
||
```
|
||
|
||
#### xcb_window_t
|
||
|
||
X11 窗口标识符。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_window_t
|
||
{
|
||
public uint Id;
|
||
}
|
||
```
|
||
|
||
#### xcb_void_cookie_t
|
||
|
||
异步请求的 Cookie,用于后续错误检查。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_void_cookie_t
|
||
{
|
||
public uint Sequence;
|
||
}
|
||
```
|
||
|
||
#### xcb_rectangle_t
|
||
|
||
矩形区域,用于定义窗口形状。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_rectangle_t
|
||
{
|
||
public short X; // 左上角 X
|
||
public short Y; // 左上角 Y
|
||
public ushort Width; // 宽度
|
||
public ushort Height; // 高度
|
||
}
|
||
```
|
||
|
||
#### xcb_generic_error_t
|
||
|
||
通用错误信息结构体。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_generic_error_t
|
||
{
|
||
public byte ResponseType;
|
||
public byte ErrorCode; // 错误代码
|
||
public ushort Sequence;
|
||
public uint ResourceId;
|
||
public ushort MinorCode;
|
||
public byte MajorCode;
|
||
public byte Pad0;
|
||
public uint FullSequence;
|
||
}
|
||
```
|
||
|
||
#### xcb_get_geometry_reply_t
|
||
|
||
窗口几何信息。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_get_geometry_reply_t
|
||
{
|
||
public byte ResponseType;
|
||
public byte Depth; // 颜色深度
|
||
public ushort Sequence;
|
||
public uint Length;
|
||
public short X; // 窗口 X 坐标
|
||
public short Y; // 窗口 Y 坐标
|
||
public ushort Width; // 窗口宽度
|
||
public ushort Height; // 窗口高度
|
||
public ushort BorderWidth; // 边框宽度
|
||
public byte Pad0;
|
||
public byte Pad1;
|
||
}
|
||
```
|
||
|
||
#### xcb_shape_query_version_reply_t
|
||
|
||
SHAPE 扩展版本信息。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_shape_query_version_reply_t
|
||
{
|
||
public byte ResponseType;
|
||
public byte Pad0;
|
||
public ushort Sequence;
|
||
public uint Length;
|
||
public ushort MajorVersion; // 主版本号
|
||
public ushort MinorVersion; // 次版本号
|
||
public uint Pad1;
|
||
}
|
||
```
|
||
|
||
#### xcb_shape_get_rectangles_reply_t
|
||
|
||
SHAPE 矩形查询结果。
|
||
|
||
```csharp
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
internal struct xcb_shape_get_rectangles_reply_t
|
||
{
|
||
public byte ResponseType;
|
||
public byte Ordering;
|
||
public ushort Sequence;
|
||
public uint Length;
|
||
public uint RectanglesLen; // 矩形数量
|
||
public uint Pad0;
|
||
public uint Pad1;
|
||
public uint Pad2;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3.4 枚举定义
|
||
|
||
#### xcb_shape_op_t
|
||
|
||
形状操作类型。
|
||
|
||
```csharp
|
||
internal enum xcb_shape_op_t : byte
|
||
{
|
||
XCB_SHAPE_SO_SET = 0, // 替换(覆盖现有形状)
|
||
XCB_SHAPE_SO_UNION = 1, // 合并
|
||
XCB_SHAPE_SO_INTERSECT = 2, // 交集
|
||
XCB_SHAPE_SO_SUBTRACT = 3, // 差集
|
||
XCB_SHAPE_SO_INVERT = 4 // 反转
|
||
}
|
||
```
|
||
|
||
#### xcb_shape_kind_t
|
||
|
||
形状类别。
|
||
|
||
```csharp
|
||
internal enum xcb_shape_kind_t : byte
|
||
{
|
||
XCB_SHAPE_SK_BOUNDING = 0, // 边界形状(影响窗口可见区域)
|
||
XCB_SHAPE_SK_CLIP = 1, // 裁剪形状(影响子窗口裁剪)
|
||
XCB_SHAPE_SK_INPUT = 2 // 输入形状(影响鼠标事件接收区域)⭐
|
||
}
|
||
```
|
||
|
||
> ⭐ `XCB_SHAPE_SK_INPUT` 是实现鼠标穿透的关键类别。
|
||
|
||
#### xcb_clip_ordering_t
|
||
|
||
裁剪矩形排序方式。
|
||
|
||
```csharp
|
||
internal enum xcb_clip_ordering_t : byte
|
||
{
|
||
XCB_CLIP_ORDERING_UNSORTED = 0, // 未排序
|
||
XCB_CLIP_ORDERING_Y_SORTED = 1, // 按 Y 排序
|
||
XCB_CLIP_ORDERING_YX_SORTED = 2, // 按 Y-X 排序
|
||
XCB_CLIP_ORDERING_YX_BANDED = 3 // 按 Y-X 带状排序
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 使用示例
|
||
|
||
```csharp
|
||
// 启用窗口鼠标事件穿透
|
||
window.SetWindowIgnoreMouseEvents(true);
|
||
|
||
// 查询是否处于穿透状态
|
||
bool isPassThrough = window.IsWindowIgnoreMouseEvents();
|
||
|
||
// 禁用穿透,恢复正常鼠标交互
|
||
window.SetWindowIgnoreMouseEvents(false);
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 注意事项
|
||
|
||
1. **SHAPE 扩展依赖** — 实现依赖 `libxcb-shape.so.0`,如果目标系统缺少该库会抛出 `DllNotFoundException`
|
||
2. **连接管理** — 每次操作都会创建新的 XCB 连接并在完成后断开(`try/finally` 保证释放),不复用连接
|
||
3. **非托管内存安全** — 禁用穿透时通过 `Marshal.AllocHGlobal` 分配的矩形内存在 `try/finally` 中确保释放
|
||
4. **Wayland 兼容性** — 当前实现基于 X11/XCB 协议,在纯 Wayland 环境下不可用。Wayland 需要通过 compositor 特定协议实现类似功能
|
||
5. **句柄转换** — Avalonia 的窗口句柄(`IntPtr`)通过 `(uint)handle.ToInt64()` 转换为 X11 窗口 ID
|
||
6. **错误日志** — SHAPE 操作错误通过 `Console.Error.WriteLine` 输出到标准错误流,不会抛出异常中断执行
|
||
7. **SHAPE 扩展版本检查** — 每次操作前通过 `xcb_shape_query_version` 验证扩展可用性,提供优雅的错误报告
|
||
|