翼度科技»论坛 编程开发 .net 查看内容

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

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
本文记录我阅读 Avalonia 代码过程中所学习到的在 X11 的窗口之间发送鼠标消息,可以跨进程给其他进程的窗口发送鼠标消息,通过此方式可以实现模拟鼠标输入
直接使用 XSendEvent 给指定窗口发送消息即可,如以下示例代码
  1.             var xEvent = new XEvent
  2.             {
  3.                 MotionEvent =
  4.                 {
  5.                     type = XEventName.MotionNotify,
  6.                     send_event = true,
  7.                     window = Window,
  8.                     display = Display,
  9.                     x = x,
  10.                     y = y
  11.                 }
  12.             };
  13.             XSendEvent(Display, Window, propagate: false, new IntPtr((int) (EventMask.ButtonMotionMask)), ref xEvent);
复制代码
以上的 Window 是自己进程的主窗口,发送的相关定义代码是我从 Avalonia 和 CPF 代码仓库里面抄的,所有代码放在 githubgitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin 7636387e97780403ce473f553540a9cc1e0652ef
复制代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin 7636387e97780403ce473f553540a9cc1e0652ef
复制代码
获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码
以上代码是对自己进程内的主窗口发送鼠标移动消息的示例,核心代码如下
  1. while (true)
  2. {
  3.     var xNextEvent = XNextEvent(Display, out var @event);
  4.     if (@event.type == XEventName.MotionNotify)
  5.     {
  6.         var x = @event.MotionEvent.x;
  7.         var y = @event.MotionEvent.y;
  8.         XDrawLine(Display, Window, GC, x, y, x + 100, y);
  9.     }
  10.     var count = XEventsQueued(Display, 0 /*QueuedAlready*/);
  11.     if (count == 0)
  12.     {
  13.         for (int i = 0; i < 100; i++)
  14.         {
  15.             var xEvent = new XEvent
  16.             {
  17.                 MotionEvent =
  18.                 {
  19.                     type = XEventName.MotionNotify,
  20.                     send_event = true,
  21.                     window = Window,
  22.                     display = Display,
  23.                     x = i,
  24.                     y = i
  25.                 }
  26.             };
  27.             XSendEvent(Display, Window, propagate: false, new IntPtr((int) (EventMask.ButtonMotionMask)), ref xEvent);
  28.         }
  29.     }
  30. }
复制代码
如上述代码可以看到只需更改 XSendEvent 里面的 Window 对应的参数,即可决定发送给哪个窗口。比如有两个窗口,可以通过此方式让窗口 2 收到鼠标消息时,自动转发给窗口 1 上,核心代码如下
  1. var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,
  2.     32,
  3.     (int)CreateWindowArgs.InputOutput,
  4.     visual,
  5.     (nuint)valueMask, ref xSetWindowAttributes);
  6. var window1 = new FooWindow(handle, display);
  7. var window2 = new FooWindow(XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,
  8.     32,
  9.     (int) CreateWindowArgs.InputOutput,
  10.     visual,
  11.     (nuint) valueMask, ref xSetWindowAttributes), display);
  12. while (true)
  13. {
  14.     var xNextEvent = XNextEvent(display, out var @event);
  15.     if (xNextEvent != 0)
  16.     {
  17.         break;
  18.     }
  19.     if (@event.type == XEventName.MotionNotify)
  20.     {
  21.         var x = @event.MotionEvent.x;
  22.         var y = @event.MotionEvent.y;
  23.         if (@event.MotionEvent.window == window1.Window)
  24.         {
  25.             XDrawLine(display, window1.Window, window1.GC, x, y, x + 100, y);
  26.         }
  27.         else
  28.         {
  29.             var xEvent = new XEvent
  30.             {
  31.                 MotionEvent =
  32.                 {
  33.                     type = XEventName.MotionNotify,
  34.                     send_event = true,
  35.                     window = window1.Window,
  36.                     display = display,
  37.                     x = x,
  38.                     y = y
  39.                 }
  40.             };
  41.             XSendEvent(display, window1.Window, propagate: false, new IntPtr((int)(EventMask.ButtonMotionMask)),
  42.                 ref xEvent);
  43.         }
  44.     }
  45. }
  46. class FooWindow
  47. {
  48.     public FooWindow(nint windowHandle, nint display)
  49.     {
  50.         Window = windowHandle;
  51.         XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |
  52.                                  XEventMask.PointerMotionHintMask;
  53.         var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
  54.         XSelectInput(display, windowHandle, mask);
  55.         XMapWindow(display, windowHandle);
  56.         XFlush(display);
  57.         var screen = XDefaultScreen(display);
  58.         var white = XWhitePixel(display, screen);
  59.         var gc = XCreateGC(display, windowHandle, 0, 0);
  60.         XSetForeground(display, gc, white);
  61.         GC = gc;
  62.     }
  63.     public nint Window { get; }
  64.     public IntPtr GC { get; }
  65. }
复制代码
以上代码放在 githubgitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin c8354f643998d01eed8f56757a558623e4d94a8a
复制代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin c8354f643998d01eed8f56757a558623e4d94a8a
复制代码
获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码
以上测试的是相同进程内的,跨进程其实也可以,只需要获取其他进程的窗口对应的指针即可。其实在这里我不确定 X11 的窗口 IntPtr 是否称为指针是合适的。但行为上看起来和 Windows 下的句柄非常类似
如以下的测试代码,启动自身作为新的进程,然后传入当前进程的窗口,让另一个进程获取当前进程的窗口,接着测试在另一个进程将鼠标消息发送到当前进程上
  1. var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,
  2.     32,
  3.     (int)CreateWindowArgs.InputOutput,
  4.     visual,
  5.     (nuint)valueMask, ref xSetWindowAttributes);
  6. var window1 = new FooWindow(handle, display);
  7. XSync(display, false);
  8. IntPtr window2Handle = IntPtr.Zero;
  9. if (args.Length == 0)
  10. {
  11.     var currentProcess = Process.GetCurrentProcess();
  12.     var mainModuleFileName = currentProcess.MainModule!.FileName;
  13.     Process.Start(mainModuleFileName, [window1.Window.ToString(), window1.GC.ToString()]);
  14. }
  15. else if (args.Length == 2)
  16. {
  17.     if (long.TryParse(args[0], out var otherProcessWindowHandle))
  18.     {
  19.         window2Handle = new IntPtr(otherProcessWindowHandle);
  20.     }
  21. }
  22. while (true)
  23. {
  24.     var xNextEvent = XNextEvent(display, out var @event);
  25.     if (xNextEvent != 0)
  26.     {
  27.         Console.WriteLine($"xNextEvent {xNextEvent}");
  28.         break;
  29.     }
  30.     if (@event.type == XEventName.Expose)
  31.     {
  32.         if (args.Length == 0)
  33.         {
  34.             XDrawLine(display, window1.Window, window1.GC, 0, 0, 100, 100);
  35.         }
  36.     }
  37.     else if (@event.type == XEventName.MotionNotify)
  38.     {
  39.         var x = @event.MotionEvent.x;
  40.         var y = @event.MotionEvent.y;
  41.         if (window2Handle != 0 && window2GCHandle != 0)
  42.         {
  43.             // 绘制是无效的
  44.             //XDrawLine(display, window2Handle, window2GCHandle, x, y, x + 100, y);
  45.             var xEvent = new XEvent
  46.             {
  47.                 MotionEvent =
  48.                 {
  49.                     type = XEventName.MotionNotify,
  50.                     send_event = true,
  51.                     window = window2Handle,
  52.                     display = display,
  53.                     x = x,
  54.                     y = y
  55.                 }
  56.             };
  57.             XSendEvent(display, window2Handle, propagate: false, new IntPtr((int)(EventMask.ButtonMotionMask)),
  58.                 ref xEvent);
  59.         }
  60.         else
  61.         {
  62.             XDrawLine(display, window1.Window, window1.GC, x, y, x + 100, y);
  63.         }
  64.     }
  65. }
复制代码
放在 githubgitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin ec8242cfe08a0eb23ba637c655083fceb0a8edb3
复制代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin ec8242cfe08a0eb23ba637c655083fceb0a8edb3
复制代码
获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码
通过以上测试可以发现 X11 的鼠标输入是完全可以进行模拟输入的,只需要拿到窗口指针,使用 XSendEvent 进行发送即可
再进一步的实验,也许大家也发现上面代码里面有被我注释的 XDrawLine 的调用。在 XDrawLine 里面也是传入 GC 和 Window 指针即可绘制线段,我就想着如果传入别的进程的窗口是否适合,是否就能在其他进程的窗口上绘制出内容
我尝试从另一个进程将 GC 传回来,如下面代码
  1. IntPtr window2Handle = IntPtr.Zero;
  2. IntPtr window2GCHandle = IntPtr.Zero;
  3. if (args.Length == 0)
  4. {
  5.     var currentProcess = Process.GetCurrentProcess();
  6.     var mainModuleFileName = currentProcess.MainModule!.FileName;
  7.     Process.Start(mainModuleFileName, [window1.Window.ToString(), window1.GC.ToString()]);
  8. }
  9. else if (args.Length == 2)
  10. {
  11.     if (long.TryParse(args[0], out var otherProcessWindowHandle))
  12.     {
  13.         window2Handle = new IntPtr(otherProcessWindowHandle);
  14.     }
  15.     if (long.TryParse(args[1], out var otherProcessGCHandle))
  16.     {
  17.         window2GCHandle = new IntPtr(otherProcessGCHandle);
  18.     }
  19. }
  20.   ... // 忽略其他代码
  21.          XDrawLine(display, window2Handle, window2GCHandle, x, y, x + 100, y);
复制代码
此时发现运行代码,进入到 XDrawLine 报段错误,进程挂掉。原因是 gc 指针看起来是不能跨进程使用的,以上代码放在 githubgitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin c397b872a4d2cba187e1c04f7b015c8b2ca7092c
复制代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin c397b872a4d2cba187e1c04f7b015c8b2ca7092c
复制代码
获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码
尝试自己进程创建 GC 指针,如以下核心代码
  1. IntPtr window2Handle = IntPtr.Zero;
  2. IntPtr window2GCHandle = IntPtr.Zero;
  3. if (args.Length == 0)
  4. {
  5.     var currentProcess = Process.GetCurrentProcess();
  6.     var mainModuleFileName = currentProcess.MainModule!.FileName;
  7.     Process.Start(mainModuleFileName, [window1.Window.ToString(), window1.GC.ToString()]);
  8. }
  9. else if (args.Length == 2)
  10. {
  11.     if (long.TryParse(args[0], out var otherProcessWindowHandle))
  12.     {
  13.         window2Handle = new IntPtr(otherProcessWindowHandle);
  14.     }
  15.     //if (long.TryParse(args[1], out var otherProcessGCHandle))
  16.     //{
  17.     //    window2GCHandle = new IntPtr(otherProcessGCHandle);
  18.     //}
  19.     // 不用别人传的,从窗口进行创建
  20.     window2GCHandle = XCreateGC(display, window2Handle, 0, 0);
  21.     Console.WriteLine($"XCreateGC Window2 {window2GCHandle}");
  22. }
复制代码
如此代码经过实际测试发现没有任何效果,当然了,也不会导致当前进程挂掉。以上代码放在 githubgitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin f0cb9bd3b4e4e9184fed831bdd84ef7e4b103888
复制代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin f0cb9bd3b4e4e9184fed831bdd84ef7e4b103888
复制代码
获取代码之后,进入 DikalehebeekaJaqunicobo 文件夹,即可获取到源代码
更多 X11 开发请参阅 博客导航

来源:https://www.cnblogs.com/lindexi/p/18192607
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具