本文記錄如何在 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 也足夠舊了,基本上系統都會帶的
所有代碼放在 github 和 gitee 上,可以使用如下命令行拉取代碼
先創建一個空文件夾,接着使用命令行 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);
更新之後的代碼放在 github 和 gitee 上,歡迎拉取代碼閱讀和構建
參考文檔:
如何在屏幕上顯示一局部透明、鼠標點擊可穿過的窗口 - V2EX
更多 X11 開發請參閱 博客導航
關於在 Windows 系統下的 WPF 窗口點擊穿透,請參閱 WPF 製作支持點擊穿透的高性能的透明背景異形窗口