清除具有桌面背景像素的X11窗口,并在其上放置具有透明像素的XImage?

时间:2019-07-14 13:13:38

标签: c x11 xlib

我正在尝试创建一个将图形化地重复鼠标指针的应用程序,因此我最终可以为Ubuntu 18.04制作一个鼠标跟踪程序-看来,做到这一点的方法是通过X11 / Xlib-尽管这些我什至不知道的日子,就像我的机器也说wayland

$ loginctl | while IFS= read line; do echo "$line"; if [[ $line == *"tty"* ]]; then sessnum=$(echo "$line" | awk '{print $1;}'); echo sessnum: $sessnum\; $(loginctl show-session $sessnum -p Type); fi; done

   SESSION        UID USER             SEAT             TTY
        c1        121 gdm              seat0            tty1
sessnum: c1; Type=wayland
         2       1000 administrator    seat0            tty2
sessnum: 2; Type=x11

2 sessions listed.

无论如何,我设法将以下内容组合在一起:

  • https://keithp.com/blogs/Cursor_tracking/-设置用于捕获原始鼠标事件的程序,因此只要鼠标指针位置发生变化,就可以提取鼠标指针位置(并触发重新绘制)
  • xosd.c(通过https://github.com/AndreRenaud/XOSD)-我最初认为屏幕显示会在顶部绘制一种特殊的方法-但这会设置一个最顶层的窗口,即根的子级,所有图形发生;并设置事件和计时器线程

....加上大量其他代码片段(大部分来自SO),这些都是我想要的(即使我没有真正完全理解其中的所有层和组合)。我将其发布为gist: xosd_track_cursor.c,因为它有700多行(但可以在需要的地方发布)。

这是应用程序的行为方式(另请参见完整分辨率imgur .mp4 video

xosd_track_cursor_01_800

基本上,一开始就设置了“ OSD”最上面的窗口,它比桌面窗口小得多-这有助于我们查看周围的窗口边框装饰(最终,我将使该窗口的大小与桌面)。

开始时,此窗口位置处的桌面像素似乎已被复制为窗口背景。

一旦鼠标指针进入OSD窗口,就会画出一个圆圈,该圆圈成为OSD窗口的遮罩(再次可以通过窗口边框装饰看到)-该圆形窗口跟随鼠标。然后,在其中绘制一个XFillRectangle来绘制一个石灰矩形,然后绘制XPutImage来绘制从最新鼠标指针捕获的像素(视频不显示它,但是复制的光标在正常显示时也发生了变化,例如从left_ptrbottom_sidexterm光标位图)。

到目前为止还不错-但这是问题和疑问:

  • 所有绘制-石灰矩形和鼠标指针副本-都保留在OSD窗口中,并且在重绘时不会清除(这在鼠标指针离开OSD窗口时非常明显,因此没有遮罩) 。每次呈现新状态时,如何擦除这些先前的图纸?
  • 当我单击窗口以更改焦点时,很明显(尤其是当鼠标指针离开OSD窗口时,因此没有遮罩),OSD窗口中显示的桌面“背景”显示了当程序启动。如何捕获桌面背景的当前状态(即OSD窗口的后面),以便在上一步中使用它来清除OSD窗口?
    • (我以为我可以隐藏OSD窗口,然后用XGetImage捕获桌面,也许是(?)-然后显示该窗口;但是show总是发送Expose事件,否则将运行{ {1}}函数可以进行重绘,因此我得到了一堆递归调用,从而拖延了应用程序的运行时间
  • 鼠标指针副本呈现黑色背景-如何使鼠标指针副本的图形透明(现在为黑色)?

还有一个奖励问题(这里只是很好奇-显然我宁愿不要剩菜剩饭):

  • 我首先执行XFillRectangle绘制一个石灰矩形,然后执行XPutImage绘制鼠标指针副本的像素。我希望这会在绿色像素的顶部显示鼠标光标的副本-的确如此,而OSD窗口被圆圈遮住了。但是,当OSD窗口完整显示时,剩余的部分似乎使绿色像素被绘制在鼠标光标复制像素的顶部。为什么会这样呢?

1 个答案:

答案 0 :(得分:0)

好吧,我想我到达了某个地方-结果出在同一要点上,只是版本不同:gist: xosd_track_cursor.c (a31e9dff5);看起来像这样:

xosd_track_cursor_mouse_trail

因此,回答我的问题:

  

每次呈现新状态时,如何擦除这些先前的图纸?

不能-与以前代码的设置方式不同。它被设置为override_redirect,这意味着它将不受任何窗口管理器的管理。此外,默认位深度为24,这意味着不支持透明性,这意味着要“隐藏”桌面(用作“清晰”背景),我们必须先隐藏然后显示窗口,由于对公开事件的反应而导致递归。

但是,我在How to make an OpenGL rendering context with transparent background?中看到使用glXCreateContext可能会有所帮助-确实如此。但是,事实证明,没有必要-一旦XMatchVisualInfo成功为OSD窗口返回了32位深度的匹配项(支持alpha透明性),便可以定义“完全透明”的颜色,通过XSetForeground设置为0x00000000(据我所知,它是0xAARRGGBB格式)-并使用它直接在XFillRectangle->窗口上绘制,从而可以透明地清除整个OSD窗口。

  

鼠标指针副本呈现黑色背景-如何使鼠标指针副本的图形透明(现在为黑色)?

结果证明,使用XCreateWindow使用XMatchVisualInfo中的设置为32位深度的窗口创建窗口后,此操作也开始起作用。就是说,我的意思是XPutImage的结果是如此,以至于光标图像中的透明点现在是“透明的” /透明的-而以前的XPutImage的结果显示为黑色像素这些位置。

  

但是,当OSD窗口完全显示时,剩余的部分似乎使绿色像素被绘制在鼠标光标复制像素的顶部。为什么会这样呢?

显然,我不正确地记得绘制像素的顺序。进行演示捕获时,确实首先复制了鼠标光标像素,然后在顶部复制了绿色像素。 (这现在改变了问题-鼠标光标为什么在该捕获中是可见的?!但是现在解决了整个问题,我并不那么好奇:))

否则,关于gist: xosd_track_cursor.c (a31e9dff5)的更多注意事项:由于X11具有客户端/服务器体系结构,这意味着用户程序只能将请求排队到服务器,因此所有绘图调用都是异步的/非阻塞的-因此,当我们运行XFillRectangle并退出时,不是并不意味着像素绘制已完成-只是请求已发送到队列,最终被发送到服务器。此外,尽管使用了XFlushXSync之类的命令,也无法保证我们可以等待完成的绘制操作。并且也不保证服务器将满足任何给定的请求。

但是,您尝试做的越少,X Server兑现请求的可能性就越大。因此,此版本的代码实际上创建了一个较小的窗口(60x60像素),然后对其进行设置,使其被鼠标指针的运动拖动(中心对齐)。然后,只需将(主)鼠标指针复制到此窗口中的相同相对位置即可。

最后,有一种原始的尝试来进行鼠标跟踪,方法是渲染鼠标指针的两个“幽灵”副本,并让它们被鼠标运动增量矢量的历史记录所取代-效果在鼠标上可见gif并不是真的很棒,但是至少它可以作为一种“概念证明”存在。另外,该窗口在开始时使用XShapeCombineRectangles设置为“点击”-表示OSD窗口不直接拾取/处理任何鼠标事件(单击),而是将所有内容自动传递给窗口在它下面,因此交互保持不变,就好像该程序根本没有运行。

(请注意,要获得gif中显示的gist: xosd_track_cursor.c (a31e9dff5)的行为,您应该查找定义DEBUGPRINTMOUSE_TRAIL,并在构建时取消注释)