本文記錄我從 Avalonia 框架裏面學到如何創建一個全屏置頂的 X11 應用窗口的方法
開始之前,先從 Avalonia 或 CPF 裏面拷貝足夠的代碼,這部分代碼可以從本文末尾找到下載方法
設置全屏的核心代碼是以下三行
ChangeWMAtoms(false, XLib.XInternAtom(display, "_NET_WM_STATE_HIDDEN", true));
ChangeWMAtoms(true, XLib.XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", true));
ChangeWMAtoms(false, XLib.XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", true),
XLib.XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", true));
設置置頂的代碼如下
// 最頂層 類似 WPF 的 Topmost 功能
ChangeWMAtoms(true, XLib.XInternAtom(display, "_NET_WM_STATE_ABOVE", true));
以上代碼的 ChangeWMAtoms 是一個內部方法,實現如下
var wmState = XLib.XInternAtom(display, "_NET_WM_STATE", true);
void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
{
var xev = new XEvent
{
ClientMessageEvent =
{
type = XEventName.ClientMessage,
send_event = true,
window = window,
message_type = wmState,
format = 32,
ptr1 = new IntPtr(enable ? 1 : 0),
ptr2 = (IntPtr?)atoms[0] ?? IntPtr.Zero,
ptr3 = (IntPtr?)(atoms.Length > 1 ? atoms[1] : IntPtr.Zero) ?? IntPtr.Zero,
ptr4 = (IntPtr?)(atoms.Length > 2 ? atoms[2] : IntPtr.Zero) ?? IntPtr.Zero
}
};
XLib.XSendEvent(display, rootWindow, false,
new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
}
如此即可獲取一個全屏且在所有窗口,包括任務欄的上層的最頂層 X11 窗口
分別是 WindowState 屬性的 set 方法以及 SetTopmost 方法
爲了讓大家能夠看到窗口在最頂層的效果,接下來繪製兩條線段,用來作爲界面,代碼如下
var white = XLib.XWhitePixel(display, screen);
var black = XLib.XBlackPixel(display, screen);
XLib.XSetForeground(display, gc, white);
var xDisplayWidth = XLib.XDisplayWidth(display, screen);
var xDisplayHeight = XLib.XDisplayHeight(display, screen);
while (XLib.XNextEvent(display, out var xEvent) == default)
{
if (xEvent.type == XEventName.Expose)
{
XLib.XDrawLine(display, window, gc, 0, 0, xDisplayWidth, xDisplayHeight);
XLib.XDrawLine(display, window, gc, 0, xDisplayHeight, xDisplayWidth, 0);
}
}
完成之後運行代碼,以下是我在 Hyperv 虛擬機的運行效果,可以看到繪製的兩條線段在所有應用上方,也在任務欄上方
完全的 Program.cs 文件的代碼如下
using CeaherecelallLemlalnohuce;
XLib.XInitThreads();
var display = XLib.XOpenDisplay(0);
var screen = XLib.XDefaultScreen(display);
var defaultScreen = XLib.XDefaultScreen(display);
var rootWindow = XLib.XRootWindow(display, defaultScreen);
XLib.XMatchVisualInfo(display, screen, 32, 4, out var info);
var visual = info.visual;
var valueMask = SetWindowValuemask.BackPixmap
| SetWindowValuemask.BackPixel
| SetWindowValuemask.BorderPixel
| SetWindowValuemask.BitGravity
| SetWindowValuemask.WinGravity
| SetWindowValuemask.BackingStore
| SetWindowValuemask.ColorMap;
var attr = new XSetWindowAttributes
{
backing_store = 1,
bit_gravity = Gravity.NorthWestGravity,
win_gravity = Gravity.NorthWestGravity,
override_redirect = false, // 參數:_overrideRedirect
colormap = XLib.XCreateColormap(display, rootWindow, visual, 0),
};
var window = XLib.XCreateWindow(display, rootWindow, 100, 100, 320, 240, 0,
32,
(int)CreateWindowArgs.InputOutput,
visual,
(nuint)valueMask, ref attr);
XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |
XEventMask.PointerMotionHintMask;
var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
XLib.XSelectInput(display, window, mask);
var gc = XLib.XCreateGC(display, window, 0, 0);
XLib.XMapWindow(display, window);
XLib.XFlush(display);
#region 全屏 最頂層
var wmState = XLib.XInternAtom(display, "_NET_WM_STATE", true);
ChangeWMAtoms(false, XLib.XInternAtom(display, "_NET_WM_STATE_HIDDEN", true));
ChangeWMAtoms(true, XLib.XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", true));
ChangeWMAtoms(false, XLib.XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", true),
XLib.XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", true));
// 最頂層 類似 WPF 的 Topmost 功能
ChangeWMAtoms(true, XLib.XInternAtom(display, "_NET_WM_STATE_ABOVE", true));
void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
{
var xev = new XEvent
{
ClientMessageEvent =
{
type = XEventName.ClientMessage,
send_event = true,
window = window,
message_type = wmState,
format = 32,
ptr1 = new IntPtr(enable ? 1 : 0),
ptr2 = (IntPtr?)atoms[0] ?? IntPtr.Zero,
ptr3 = (IntPtr?)(atoms.Length > 1 ? atoms[1] : IntPtr.Zero) ?? IntPtr.Zero,
ptr4 = (IntPtr?)(atoms.Length > 2 ? atoms[2] : IntPtr.Zero) ?? IntPtr.Zero
}
};
XLib.XSendEvent(display, rootWindow, false,
new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
}
#endregion
var white = XLib.XWhitePixel(display, screen);
var black = XLib.XBlackPixel(display, screen);
XLib.XSetForeground(display, gc, white);
var xDisplayWidth = XLib.XDisplayWidth(display, screen);
var xDisplayHeight = XLib.XDisplayHeight(display, screen);
while (XLib.XNextEvent(display, out var xEvent) == default)
{
if (xEvent.type == XEventName.Expose)
{
XLib.XDrawLine(display, window, gc, 0, 0, xDisplayWidth, xDisplayHeight);
XLib.XDrawLine(display, window, gc, 0, xDisplayHeight, xDisplayWidth, 0);
}
}
XLib.XUnmapWindow(display, window);
XLib.XDestroyWindow(display, window);
本文代碼放在 github 和 gitee 上,可以使用如下命令行拉取代碼
先創建一個空文件夾,接着使用命令行 cd 命令進入此空文件夾,在命令行裏面輸入以下代碼,即可獲取到本文的代碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 693a137d9349bc65b5e2ed3a7c5d2480775e621a
以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換爲 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 693a137d9349bc65b5e2ed3a7c5d2480775e621a
獲取代碼之後,進入 CeaherecelallLemlalnohuce 文件夾,即可獲取到源代碼
更多 Avalonia 以及 X11 等相關技術,請參閱 博客導航