dotnet X11 設置窗口鼠標觸摸命中穿透

本文記錄如何在 X11 應用裏面,使用 XShapeCombineRegion 方法配置一個 X11 窗口支持和 Win32 窗口一樣的命中測試穿透功能,即對應 Win32 的 WS_EX_TRANSPARENT 的鼠標、觸摸等的點擊等動作的穿透功能,可以實現在窗口中挖空一塊範圍直接穿透到後面的窗口

在 X11 窗口中,想要實現讓窗口不可命中,即所有的鼠標、觸摸等的事件穿透到後面的窗口上,可以採用 libXext.so 提供的 XShapeCombineRegion 方法,也可以使用有爭議的 libXfixes.so 提供的 XFixesSetWindowShapeRegion 方法

通過以上兩個方法即可讓 X11 窗口不響應鼠標或觸摸的點擊輸入,讓其輸入到窗口後面的窗口。適合用來製作一個僅用來展示渲染的窗口,讓這個窗口不參與到交互裏面

使用比較有爭議的 libXfixes.so 提供的 XFixesSetWindowShapeRegion 方法的示例代碼如下

// 以下的 childWindowHandle 是一個 X11 窗口

var region = XFixesCreateRegion(display, 0, 0);
XFixesSetWindowShapeRegion(display, childWindowHandle, ShapeInput, 0, 0, region);

    [DllImport("libXfixes.so.3")]
    public static extern IntPtr XFixesCreateRegion(IntPtr display, IntPtr rectangles, int nrectangles);

    [DllImport("libXfixes.so.3")]
    public static extern void XFixesSetWindowShapeRegion(IntPtr display, IntPtr window, int shape_type, int x_offset,
        int y_offset, IntPtr region);

採用 libXext.so 提供的 XShapeCombineRegion 方法的示例代碼如下

var region = XCreateRegion();
XShapeCombineRegion(display, childWindowHandle, ShapeInput, 0, 0, region, ShapeSet);

        [DllImport(libX11)]
        public static extern IntPtr XCreateRegion();

        [DllImport("libXext.so.6")]
        public static extern void XShapeCombineRegion(IntPtr display, IntPtr dest, int destKind, int xOff, int yOff, IntPtr region, int op);

我嘗試創建兩個窗口,其中一個窗口調用了 XShapeCombineRegion 方法,運行程序,將設置了的 XShapeCombineRegion 的窗口激活作爲前臺窗口,點擊此窗口的內容,可以看到點擊穿透到後面的窗口

以上兩個方法都能實現功能,且通過閱讀 X Server的代碼,可以發現以上兩個方法核心實現基本相同。爲了可能的坑點在於 libXfixes.so 可能在某些系統上被砍掉。只是這個 libXfixes.so 也足夠舊了,基本上系統都會帶的

所有代碼放在 githubgitee 上,可以使用如下命令行拉取代碼

先創建一個空文件夾,接着使用命令行 cd 命令進入此空文件夾,在命令行裏面輸入以下代碼,即可獲取到本文的代碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 67cd9188399e7f45bfe83e1af9daf10236b3171c

以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換爲 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 67cd9188399e7f45bfe83e1af9daf10236b3171c

獲取代碼之後,進入 DikalehebeekaJaqunicobo 文件夾,即可獲取到源代碼

以上代碼經過我在 UOS 系統上測試通過,在 UOS 上的 KWin_X11 能夠符合預期工作

如運行代碼提示找不到 libXext.so 文件,錯誤內容如下

System.DllNotFoundException: Unable to load shared library 'libXext.so' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
/home/uos/Downloads/lin/libXext.so: 無法打開共享對象文件: 沒有那個文件或目錄
/home/uos/Downloads/lin/liblibXext.so: 無法打開共享對象文件: 沒有那個文件或目錄
/home/uos/Downloads/lin/libXext.so.so: 無法打開共享對象文件: 沒有那個文件或目錄
/home/uos/Downloads/lin/liblibXext.so.so: 無法打開共享對象文件: 沒有那個文件或目錄

   at CPF.Linux.XLib.XShapeCombineRegion(IntPtr display, IntPtr dest, Int32 destKind, Int32 xOff, Int32 yOff, IntPtr region, Int32 op)
   at UnoInk.X11Ink.X11InkWindow..ctor(X11Info x11Info, IntPtr mainWindowHandle)
   at UnoInk.X11Ink.X11InkProvider.Start(Window unoWindow)

可以嘗試配置使用 libXext.so.6 版本,代碼如下

        [DllImport("libXext.so.6")]
        public static extern void XShapeCombineRegion(IntPtr display, IntPtr dest, int destKind, int xOff, int yOff, IntPtr region, int op);

更新之後的代碼放在 githubgitee 上,歡迎拉取代碼閱讀和構建

參考文檔:

如何在屏幕上顯示一局部透明、鼠標點擊可穿過的窗口 - V2EX

2021-08-21窗口管理器雜談 - 簡書

更多 X11 開發請參閱 博客導航

關於在 Windows 系統下的 WPF 窗口點擊穿透,請參閱 WPF 製作支持點擊穿透的高性能的透明背景異形窗口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章