dotnet X11 窗口之间发送鼠标消息 模拟鼠标输入

news/2024/9/21 12:37:53

本文记录我阅读 Avalonia 代码过程中所学习到的在 X11 的窗口之间发送鼠标消息,可以跨进程给其他进程的窗口发送鼠标消息,通过此方式可以实现模拟鼠标输入

直接使用 XSendEvent 给指定窗口发送消息即可,如以下示例代码

            var xEvent = new XEvent{MotionEvent ={type = XEventName.MotionNotify,send_event = true,window = Window,display = Display,x = x,y = y}};XSendEvent(Display, Window, propagate: false, new IntPtr((int) (EventMask.ButtonMotionMask)), ref xEvent);

以上的 Window 是自己进程的主窗口,发送的相关定义代码是我从 Avalonia 和 CPF 代码仓库里面抄的,所有代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

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

获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码

以上代码是对自己进程内的主窗口发送鼠标移动消息的示例,核心代码如下

while (true)
{var xNextEvent = XNextEvent(Display, out var @event);if (@event.type == XEventName.MotionNotify){var x = @event.MotionEvent.x;var y = @event.MotionEvent.y;XDrawLine(Display, Window, GC, x, y, x + 100, y);}var count = XEventsQueued(Display, 0 /*QueuedAlready*/);if (count == 0){for (int i = 0; i < 100; i++){var xEvent = new XEvent{MotionEvent ={type = XEventName.MotionNotify,send_event = true,window = Window,display = Display,x = i,y = i}};XSendEvent(Display, Window, propagate: false, new IntPtr((int) (EventMask.ButtonMotionMask)), ref xEvent);}}
}

如上述代码可以看到只需更改 XSendEvent 里面的 Window 对应的参数,即可决定发送给哪个窗口。比如有两个窗口,可以通过此方式让窗口 2 收到鼠标消息时,自动转发给窗口 1 上,核心代码如下


var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,32,(int)CreateWindowArgs.InputOutput,visual,(nuint)valueMask, ref xSetWindowAttributes);var window1 = new FooWindow(handle, display);var window2 = new FooWindow(XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,32,(int) CreateWindowArgs.InputOutput,visual,(nuint) valueMask, ref xSetWindowAttributes), display);while (true)
{var xNextEvent = XNextEvent(display, out var @event);if (xNextEvent != 0){break;}if (@event.type == XEventName.MotionNotify){var x = @event.MotionEvent.x;var y = @event.MotionEvent.y;if (@event.MotionEvent.window == window1.Window){XDrawLine(display, window1.Window, window1.GC, x, y, x + 100, y);}else{var xEvent = new XEvent{MotionEvent ={type = XEventName.MotionNotify,send_event = true,window = window1.Window,display = display,x = x,y = y}};XSendEvent(display, window1.Window, propagate: false, new IntPtr((int)(EventMask.ButtonMotionMask)),ref xEvent);}}
}class FooWindow
{public FooWindow(nint windowHandle, nint display){Window = windowHandle;XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |XEventMask.PointerMotionHintMask;var mask = new IntPtr(0xffffff ^ (int)ignoredMask);XSelectInput(display, windowHandle, mask);XMapWindow(display, windowHandle);XFlush(display);var screen = XDefaultScreen(display);var white = XWhitePixel(display, screen);var gc = XCreateGC(display, windowHandle, 0, 0);XSetForeground(display, gc, white);GC = gc;}public nint Window { get; }public IntPtr GC { get; }
}

以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

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

获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码

以上测试的是相同进程内的,跨进程其实也可以,只需要获取其他进程的窗口对应的指针即可。其实在这里我不确定 X11 的窗口 IntPtr 是否称为指针是合适的。但行为上看起来和 Windows 下的句柄非常类似

如以下的测试代码,启动自身作为新的进程,然后传入当前进程的窗口,让另一个进程获取当前进程的窗口,接着测试在另一个进程将鼠标消息发送到当前进程上

var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,32,(int)CreateWindowArgs.InputOutput,visual,(nuint)valueMask, ref xSetWindowAttributes);var window1 = new FooWindow(handle, display);
XSync(display, false);IntPtr window2Handle = IntPtr.Zero;if (args.Length == 0)
{var currentProcess = Process.GetCurrentProcess();var mainModuleFileName = currentProcess.MainModule!.FileName;Process.Start(mainModuleFileName, [window1.Window.ToString(), window1.GC.ToString()]);
}
else if (args.Length == 2)
{if (long.TryParse(args[0], out var otherProcessWindowHandle)){window2Handle = new IntPtr(otherProcessWindowHandle);}
}while (true)
{var xNextEvent = XNextEvent(display, out var @event);if (xNextEvent != 0){Console.WriteLine($"xNextEvent {xNextEvent}");break;}if (@event.type == XEventName.Expose){if (args.Length == 0){XDrawLine(display, window1.Window, window1.GC, 0, 0, 100, 100);}}else if (@event.type == XEventName.MotionNotify){var x = @event.MotionEvent.x;var y = @event.MotionEvent.y;if (window2Handle != 0 && window2GCHandle != 0){// 绘制是无效的//XDrawLine(display, window2Handle, window2GCHandle, x, y, x + 100, y);var xEvent = new XEvent{MotionEvent ={type = XEventName.MotionNotify,send_event = true,window = window2Handle,display = display,x = x,y = y}};XSendEvent(display, window2Handle, propagate: false, new IntPtr((int)(EventMask.ButtonMotionMask)),ref xEvent);}else{XDrawLine(display, window1.Window, window1.GC, x, y, x + 100, y);}}
}

放在 github 和 gitee 上,可以使用如下命令行拉取代码

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

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

获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码

通过以上测试可以发现 X11 的鼠标输入是完全可以进行模拟输入的,只需要拿到窗口指针,使用 XSendEvent 进行发送即可

再进一步的实验,也许大家也发现上面代码里面有被我注释的 XDrawLine 的调用。在 XDrawLine 里面也是传入 GC 和 Window 指针即可绘制线段,我就想着如果传入别的进程的窗口是否适合,是否就能在其他进程的窗口上绘制出内容

我尝试从另一个进程将 GC 传回来,如下面代码

IntPtr window2Handle = IntPtr.Zero;
IntPtr window2GCHandle = IntPtr.Zero;if (args.Length == 0)
{var currentProcess = Process.GetCurrentProcess();var mainModuleFileName = currentProcess.MainModule!.FileName;Process.Start(mainModuleFileName, [window1.Window.ToString(), window1.GC.ToString()]);
}
else if (args.Length == 2)
{if (long.TryParse(args[0], out var otherProcessWindowHandle)){window2Handle = new IntPtr(otherProcessWindowHandle);}if (long.TryParse(args[1], out var otherProcessGCHandle)){window2GCHandle = new IntPtr(otherProcessGCHandle);}
}... // 忽略其他代码XDrawLine(display, window2Handle, window2GCHandle, x, y, x + 100, y);

此时发现运行代码,进入到 XDrawLine 报段错误,进程挂掉。原因是 gc 指针看起来是不能跨进程使用的,以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

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

获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码

尝试自己进程创建 GC 指针,如以下核心代码

IntPtr window2Handle = IntPtr.Zero;
IntPtr window2GCHandle = IntPtr.Zero;if (args.Length == 0)
{var currentProcess = Process.GetCurrentProcess();var mainModuleFileName = currentProcess.MainModule!.FileName;Process.Start(mainModuleFileName, [window1.Window.ToString(), window1.GC.ToString()]);
}
else if (args.Length == 2)
{if (long.TryParse(args[0], out var otherProcessWindowHandle)){window2Handle = new IntPtr(otherProcessWindowHandle);}//if (long.TryParse(args[1], out var otherProcessGCHandle))//{//    window2GCHandle = new IntPtr(otherProcessGCHandle);//}// 不用别人传的,从窗口进行创建window2GCHandle = XCreateGC(display, window2Handle, 0, 0);Console.WriteLine($"XCreateGC Window2 {window2GCHandle}");
}

如此代码经过实际测试发现没有任何效果,当然了,也不会导致当前进程挂掉。以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

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

获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码

更多 X11 开发请参阅 博客导航

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/31680.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

28-SpringMVC源码解析

Quiz:Spring和SpringMVC整合使用时,会创建一个容器还是两个容器(父子容器?) DispatcherServlet初始化过程中做了什么? 请求的执行流程是怎么样的?SpringMVC 是基于 Servlet 和 Spring 容器设计的 Web 框架。1. Servlet 回顾 Servlet 接口及其实现类结构:public interfa…

读人工智能时代与人类未来笔记03_演变

读人工智能时代与人类未来笔记03_演变1. 演变 1.1. 每个社会都找到了属于自己的一套适应世界的方法 1.1.1. 适应的核心,是有关人类心智与现实之间关系的概念 1.1.2. 人类认识周围环境的能力 1.1.2.1. 这种能力通过知识获得,同时…

爱芯通元产品与关键技术

爱芯通元产品与关键技术 混合精度NPU 以算子为原子指令集的AI计算处理器爱芯通元NPU采用多线程异构多核设计,实现算子、网络微结构、数据流和内存访问优化,高效支持混合精度算法设计,natively支持Transformer网络结构,为大模型在边缘侧、端侧的应用提供良好的基础。 经过多…

ffmpeg 去除音频中的静音

去除音频中的静音 //去除所有超过0.3秒的静音部分 ffmpeg -i input.mp3 -af silenceremove=stop_periods=-1:stop_duration=0.3:stop_threshold=-30dB -y output.mp3ffmpeg音频处理常用命令:调节音量大小ffmpeg -y -i input.mp3 -af volume=-10dB output.mp3 \n 主要是volume参…

基于异步协程的增量式微博网页版爬虫(一)思路篇

项目介绍 本项目旨在利用高级搜索功能,爬取微博网页版的详细数据。而大多数爬虫以单线程为主,但单线程存在资源利用率低的不足,针对这以问题,本项目主要使用如下技术: (1)多线程+协程技术+Redis实现增量式爬虫。实现过程中存在两个技术难点:一是使用redis数据传输时开销…

(9)FIFO ip核

一、FIFO简介 二、FIFO ip使用 使用方案: 操作: 在IP Catalog中找到这个 配置fifo: 生成ip:

imx6ull Linux内核构建

IMX6ULL的Linux内核编译步骤用户文档下载 在NXP官网的文档搜索界面,搜索Linux和User两个关键字就能查询到相关的文档 官网文档搜索地址:https://www.nxp.com.cn/design/documentation:DOCUMENTATION#/内核下载 在上面文档的第七章有交怎么下载以及编译内核#git安装 sudo apt-…

MASM中Group的作用

Masm5以后推出的simplified segment模式及.model标准模型中,都将段组合成一个group,group的作用及优点是什么呢? 一、Group的作用: 将组(group)后的所有段加入一个组,位于这些段内的的label(标号)或variables(变量)的偏移地址都参照Group的起始地址进行计算,而不是所在…