dotnet 後臺線程發送 X11 窗口消息

本文將告訴大家如何在 dotnet 裏面的後臺線程向自己進程內的窗口發送消息

核心是通過 XSendEvent 發送消息,發送消息想要有反應需要另開 XOpenDisplay 獲取 display 對象,最後再將其關閉才能發送出去

核心代碼如下

_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));

        var display1 = XOpenDisplay(IntPtr.Zero);

        try
        {
            var @event = new XEvent
            {
                ClientMessageEvent =
                {
                    type = XEventName.ClientMessage,
                    send_event = true,
                    window = handle,
                    message_type = 0,
                    format = 32,
                    ptr1 = 0,
                    ptr2 = 0,
                    ptr3 = 0,
                    ptr4 = 0,
                }
            };
            XSendEvent(display1, handle, false, 0, ref @event);
        }
        finally
        {
            XCloseDisplay(display1);
        }
    }
});

以上的 handle 是一個 X11 窗口指針,代碼如下

var display = XOpenDisplay(IntPtr.Zero);
var screen = XDefaultScreen(display);

var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,
    32,
    (int)CreateWindowArgs.InputOutput,
    visual,
    (nuint)valueMask, ref xSetWindowAttributes);

如果在 Task.Run 後臺線程裏面,使用的是外面的 display 對象,則發送失敗

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

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

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

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

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

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

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

再經過更多的測試和閱讀大佬們的示例代碼,發現只需帶上 XFlush 即可,更改之後的代碼如下

_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));

        var @event = new XEvent
        {
            ClientMessageEvent =
            {
                type = XEventName.ClientMessage,
                send_event = true,
                window = handle,
                message_type = 0,
                format = 32,
                ptr1 = 0,
                ptr2 = 0,
                ptr3 = 0,
                ptr4 = 0,
            }
        };
        XSendEvent(display, handle, false, 0, ref @event);

        XFlush(display);
    }
});

這裏由於需要進行多線程共用一個 display 對象,根據 X11 文檔,需要添加 XInitThreads 方法才能確保安全

完全的代碼如下

// See https://aka.ms/new-console-template for more information

using CPF.Linux;
using System;
using System.Diagnostics;
using System.Runtime;
using static CPF.Linux.XLib;

XInitThreads();
var display = XOpenDisplay(IntPtr.Zero);
var screen = XDefaultScreen(display);

var rootWindow = XDefaultRootWindow(display);

XMatchVisualInfo(display, screen, 32, 4, out var info);
var visual = info.visual;

var valueMask =
        //SetWindowValuemask.BackPixmap
        0
        | SetWindowValuemask.BackPixel
        | SetWindowValuemask.BorderPixel
        | SetWindowValuemask.BitGravity
        | SetWindowValuemask.WinGravity
        | SetWindowValuemask.BackingStore
        | SetWindowValuemask.ColorMap
    //| SetWindowValuemask.OverrideRedirect
    ;
var xSetWindowAttributes = new XSetWindowAttributes
{
    backing_store = 1,
    bit_gravity = Gravity.NorthWestGravity,
    win_gravity = Gravity.NorthWestGravity,
    //override_redirect = true, // 設置窗口的override_redirect屬性爲True,以避免窗口管理器的干預
    colormap = XCreateColormap(display, rootWindow, visual, 0),
    border_pixel = 0,
    background_pixel = 0,
};

var xDisplayWidth = XDisplayWidth(display, screen) / 2;
var xDisplayHeight = XDisplayHeight(display, screen) / 2;
var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,
    32,
    (int)CreateWindowArgs.InputOutput,
    visual,
    (nuint)valueMask, ref xSetWindowAttributes);


XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |
                         XEventMask.PointerMotionHintMask;
var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
XSelectInput(display, handle, mask);

XMapWindow(display, handle);
XFlush(display);

var white = XWhitePixel(display, screen);
var black = XBlackPixel(display, screen);

var gc = XCreateGC(display, handle, 0, 0);
XSetForeground(display, gc, white);
XSync(display, false);

_ = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));

        var @event = new XEvent
        {
            ClientMessageEvent =
            {
                type = XEventName.ClientMessage,
                send_event = true,
                window = handle,
                message_type = 0,
                format = 32,
                ptr1 = 0,
                ptr2 = 0,
                ptr3 = 0,
                ptr4 = 0,
            }
        };
        XSendEvent(display, handle, false, 0, ref @event);

        XFlush(display);
    }
});

while (true)
{
    var xNextEvent = XNextEvent(display, out var @event);
    if (xNextEvent != 0)
    {
        Console.WriteLine($"xNextEvent {xNextEvent}");
        break;
    }

    if (@event.type == XEventName.Expose)
    {
        XDrawLine(display, handle, gc, 0, 0, 100, 100);
    }

    Console.WriteLine(@event.type);
}

Console.WriteLine("Hello, World!");

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

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

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

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

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

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

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

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