13 KiB
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
设置窗口是否忽略(穿透)鼠标输入事件。
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
查询窗口当前是否处于鼠标事件穿透状态。
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 扩展。
[SupportedOSPlatform("linux")]
private static bool IsShapeExtensionAvailable(IntPtr connection)
| 参数 | 类型 | 说明 |
|---|---|---|
connection |
IntPtr |
XCB 连接指针 |
返回值: true 表示 SHAPE 扩展可用。
实现: 调用 xcb_shape_query_version 并检查是否返回有效的版本信息。
2.2 GetWindowGeometry
获取窗口的几何信息(位置和尺寸)。
[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 服务器。
[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 服务器连接。
[DllImport("libxcb.so.1")]
internal static extern void xcb_disconnect(IntPtr connection);
xcb_connection_has_error
检查连接是否有错误。
[DllImport("libxcb.so.1")]
internal static extern int xcb_connection_has_error(IntPtr connection);
返回值: 0 表示无错误,非零值表示有错误。
xcb_flush
刷新待发送的请求到 X 服务器。
[DllImport("libxcb.so.1")]
internal static extern int xcb_flush(IntPtr connection);
xcb_request_check
检查已发送请求的错误状态。
[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
获取窗口几何信息(位置、尺寸、深度等)。
[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 扩展版本。
[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
设置窗口的形状区域(核心操作)。
[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
查询窗口的形状矩形。
[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 连接句柄(不透明类型)。
internal struct xcb_connection_t { }
xcb_window_t
X11 窗口标识符。
[StructLayout(LayoutKind.Sequential)]
internal struct xcb_window_t
{
public uint Id;
}
xcb_void_cookie_t
异步请求的 Cookie,用于后续错误检查。
[StructLayout(LayoutKind.Sequential)]
internal struct xcb_void_cookie_t
{
public uint Sequence;
}
xcb_rectangle_t
矩形区域,用于定义窗口形状。
[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
通用错误信息结构体。
[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
窗口几何信息。
[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 扩展版本信息。
[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 矩形查询结果。
[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
形状操作类型。
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
形状类别。
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
裁剪矩形排序方式。
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. 使用示例
// 启用窗口鼠标事件穿透
window.SetWindowIgnoreMouseEvents(true);
// 查询是否处于穿透状态
bool isPassThrough = window.IsWindowIgnoreMouseEvents();
// 禁用穿透,恢复正常鼠标交互
window.SetWindowIgnoreMouseEvents(false);
5. 注意事项
- SHAPE 扩展依赖 — 实现依赖
libxcb-shape.so.0,如果目标系统缺少该库会抛出DllNotFoundException - 连接管理 — 每次操作都会创建新的 XCB 连接并在完成后断开(
try/finally保证释放),不复用连接 - 非托管内存安全 — 禁用穿透时通过
Marshal.AllocHGlobal分配的矩形内存在try/finally中确保释放 - Wayland 兼容性 — 当前实现基于 X11/XCB 协议,在纯 Wayland 环境下不可用。Wayland 需要通过 compositor 特定协议实现类似功能
- 句柄转换 — Avalonia 的窗口句柄(
IntPtr)通过(uint)handle.ToInt64()转换为 X11 窗口 ID - 错误日志 — SHAPE 操作错误通过
Console.Error.WriteLine输出到标准错误流,不会抛出异常中断执行 - SHAPE 扩展版本检查 — 每次操作前通过
xcb_shape_query_version验证扩展可用性,提供优雅的错误报告