Files
AtomUI/docs/Native/Linux.md
T

13 KiB
Raw Permalink Blame History

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;
}

异步请求的 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. 注意事项

  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 验证扩展可用性,提供优雅的错误报告