有懂x11的吗,如何创建一个背景透明的子窗口?

软件和网站开发以及相关技术探讨
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

有懂x11的吗,如何创建一个背景透明的子窗口?

#1

帖子 wxf » 2022-11-12 11:30

需求是这样的,我做了一个hook so,通过 LD_PRELOAD=/opt/dss/dsshook.so /opt/kingsoft/wps-office/office6/wps 的方式,让so进入wps进程空间,我在XMapWindow 的hook函数内判断要映射的窗口是不是wps主窗口,是的话就创建一个子窗口,并且让这个子窗口背景透明,但我还要在上面输出一些文字作为假水印。此外,我还要使鼠标键盘事件穿透下去

现在子窗口可以加,鼠标键盘事件可以穿透到主窗口,但是没办法让其背景透明。

用子窗口的目的,是使其跟着主窗口移动,省去自己管理的麻烦。

用top-level窗口也行,但是toplevel的会切换z序,用瞬态窗口(设置transient-for属性的窗口)可以达到不切换z序的目的,但是有标题栏,而不我不需要标题栏,而且透明后,输出的文字也跟着透明了。

我的代码如下,请指点:

代码: 全选


int XMapWindow(Display* display, Window w)
{
	static void* handle = NULL;
	static Func_XMapWindow old_XMapWindow = NULL;

	if (!handle)
	{
		handle = dlopen("libX11.so", RTLD_LAZY);
		old_XMapWindow = (Func_XMapWindow)dlsym(handle, "XMapWindow");
	}

	//获得窗口标题
	char title[300] = { '\0' };
	get_window_title4(display, w, title);

        if (strstr(title, "WPS文字")) //“WPS文字”是无文档时的标题 随后打开文档了会变成“11.doc - WPS 文字 - 兼容模式” 这样的形式。 再此切回无文档tab页时,标题会变成“WPS 文字 - WPS文字”
	{
		int ret = old_XMapWindow(display, w);

		//如果不存在水印窗口, 创建,让鼠标键盘事件穿透,背景透明,让主窗口变化大小时也跟着变化
		if (g_water_print_window_exist == False)
		{
			XVisualInfo vinfo;
			XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);

			XSetWindowAttributes attr;
			attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vinfo.visual, AllocNone);
			attr.border_pixel = 0;
			attr.background_pixel = 0x00ffffff;// 0x80808080;
			attr.event_mask = StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | ExposureMask;
			//attr.override_redirect = True;


			g_water_print_window = XCreateWindow(display, w , 0, 0, 400, 400, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel | CWEventMask , &attr);
			//g_water_print_window = XCreateSimpleWindow(display, w, 0, 0, 400, 400, 1, BlackPixel(display, DefaultScreen(display)),0x00ffffff );

			printf("%s,%d,g_water_print_window:%lx\n", __FILE__, __LINE__, g_water_print_window);


			//使事件穿透下去
			XShapeCombineRectangles(display, g_water_print_window, ShapeInput, 0, 0, NULL, 0, ShapeSet, YXBanded);

                        //使子窗口背景透明
			Pixmap mask = XCreatePixmap(display, g_water_print_window, 400, 400, 1);
			XShapeCombineMask(display, g_water_print_window, ShapeBounding, 0, 0, mask, ShapeSet);// ShapeClip  ShapeBounding
			
			old_XMapWindow(display, g_water_print_window);


			GC gc = XCreateGC(display, g_water_print_window, 0, 0);
			XDrawString(display, g_water_print_window, gc, 190, 190, "test", 4);
			XFlushGC(display, gc);


			////设置半透明,对子窗口无效。大约是因为子窗口不归窗口管理器管理?
			//double alpha = 0.1;
			//unsigned long opacity = (unsigned long)(0xFFFFFFFFul * alpha);
			//Atom XA_NET_WM_WINDOW_OPACITY = XInternAtom(display, "_NET_WM_WINDOW_OPACITY", False);
			//XChangeProperty(display, g_water_print_window, XA_NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&opacity, 1L);

			g_water_print_window_exist = True;
		}

		return ret;
	}
	return old_XMapWindow(display, w);
}


效果是这样的
图片
请高手指点

另外,我还要做阻止截图的功能,为什么我把几种最常用的截图相关函数hook了,还是拦截不住截图程序,比如xGetImage 、xcb_get_image_reply 等。是不是这些截图程序静态链接x11库了,还是用了别的方法截图了?
头像
astolia
论坛版主
帖子: 5968
注册时间: 2008-09-18 13:11
送出感谢: 1 次
接收感谢: 1037 次

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#2

帖子 astolia » 2022-11-12 14:22

我觉得你应该把文字设成窗口形状掩码,文字部分不透明
wxf 写了:
2022-11-12 11:30
另外,我还要做阻止截图的功能,为什么我把几种最常用的截图相关函数hook了,还是拦截不住截图程序,比如xGetImage 、xcb_get_image_reply 等。是不是这些截图程序静态链接x11库了,还是用了别的方法截图了?
去看截图程序的代码啊
这些用户感谢了作者 astolia 于这个帖子:
wxf (2022-11-12 18:37)
评价: 3.7%
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#3

帖子 wxf » 2022-11-12 19:09

astolia 写了:
2022-11-12 14:22
我觉得你应该把文字设成窗口形状掩码,文字部分不透明
wxf 写了:
2022-11-12 11:30
另外,我还要做阻止截图的功能,为什么我把几种最常用的截图相关函数hook了,还是拦截不住截图程序,比如xGetImage 、xcb_get_image_reply 等。是不是这些截图程序静态链接x11库了,还是用了别的方法截图了?
去看截图程序的代码啊
截图程序有的用的Qt,有的用gtk。QT源代码看了看天,看不懂它底层到底怎么实现的。有的用gtk,比如 xfce4-screenshooter,看来看去,好像底层用的什么cairo,好像也没用到x11的东西。linux里图形界面的实现实在是多样化又复杂,又没有成熟详细文档,让人迷惑得不行。

你说把文字设成掩码,听起来挺正确的,就是不知道怎么实现啊,可以给示例代码吗
头像
astolia
论坛版主
帖子: 5968
注册时间: 2008-09-18 13:11
送出感谢: 1 次
接收感谢: 1037 次

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#4

帖子 astolia » 2022-11-14 16:59

直接往掩码图上输出文字没成功,换了个方式。

代码: 全选

#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <X11/Xft/Xft.h>
#include <X11/Xatom.h>
#include <string.h>

int main() {
    Display *d = XOpenDisplay(NULL);
    Window r = DefaultRootWindow(d);
    int s = DefaultScreen(d);
    unsigned long whitePixel = WhitePixel(d, s);

    const char *text = "水印测试";
    XftFont *f = XftFontOpenName(d, s, "黑体:size=256");
    int height = f->ascent + f->descent, width = f->max_advance_width * 4;

    XVisualInfo vinfo;
    XMatchVisualInfo(d, s, 32, TrueColor, &vinfo);
    XSetWindowAttributes attr;
    attr.colormap = XCreateColormap(d, r, vinfo.visual, AllocNone);
    attr.border_pixel = 0;
    attr.background_pixel = 0x80808080;
    attr.event_mask = StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | ExposureMask;
    Window w = XCreateWindow(d, r, 0, 0, width, height, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel | CWEventMask , &attr);
    //Window w = XCreateSimpleWindow(d, r, 0, 0, width, height, 0, BlackPixel(d,s), whitePixel);

    Atom wintype = XInternAtom(d, "_NET_WM_WINDOW_TYPE", False);
    long dockType = XInternAtom(d, "_NET_WM_WINDOW_TYPE_DOCK", False);
    XChangeProperty(d, w, wintype, XA_ATOM, 32, PropModeReplace, (unsigned char *) &dockType, 1);

    XMapWindow(d, w);

    XShapeCombineRectangles(d, w, ShapeInput, 0, 0, NULL, 0, ShapeSet, YXBanded);

    Pixmap textPixmap = XCreatePixmap(d, w, width, height, DefaultDepth(d, s));
    GC textGc = XCreateGC(d, textPixmap, 0, 0);
    XftDraw *textDraw = XftDrawCreate(d, textPixmap, DefaultVisual(d, s), DefaultColormap(d, s));
    XftColor blackColor;
    XSetForeground(d, textGc, whitePixel);
    XFillRectangle(d, textPixmap, textGc, 0, 0, width, height);
    XftColorAllocName(d, DefaultVisual(d, s), DefaultColormap(d, s), "black", &blackColor);
    XftDrawStringUtf8(textDraw, &blackColor, f, 0, f->ascent, (const FcChar8 *)text, strlen(text));
    XImage *image = XGetImage(d, textPixmap, 0, 0, width, height, AllPlanes, ZPixmap);

    Pixmap mask = XCreatePixmap(d, w, width, height, 1);
    GC gc = XCreateGC(d, mask, 0, 0);
    XSetForeground (d, gc, 0);
    XFillRectangle(d, mask, gc, 0, 0, width, height);
    XSetForeground (d, gc, 1);
    for (int x = 0; x < width; x++)
        for (int y = 0; y < height; y++)
            if (XGetPixel(image, x, y) != whitePixel)
                XDrawPoint(d, mask, gc, x, y);
    XShapeCombineMask(d, w, ShapeBounding, 0, 0, mask, ShapeSet);

    XEvent e;
    while (1) {
        XNextEvent(d, &e);
    }
    return 0;
}
这些用户感谢了作者 astolia 于这个帖子:
wxf (2022-11-14 19:09)
评价: 3.7%
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#5

帖子 wxf » 2022-11-14 19:09

太感谢了,我学习一下
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#6

帖子 wxf » 2022-11-17 10:10

astolia 写了:
2022-11-14 16:59
直接往掩码图上输出文字没成功,换了个方式。
请问一下,linux里边图形的刷新机制是什么?在windows里,只要处理WM_PAINT消息就行了,x11里怎么办? Expose事件好像不管用啊

还有,用XDrawString 函数输出文字时,怎么改变输出方向,比如斜45度
头像
astolia
论坛版主
帖子: 5968
注册时间: 2008-09-18 13:11
送出感谢: 1 次
接收感谢: 1037 次

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#7

帖子 astolia » 2022-11-17 13:46

wxf 写了:
2022-11-17 10:10
请问一下,linux里边图形的刷新机制是什么?在windows里,只要处理WM_PAINT消息就行了,x11里怎么办? Expose事件好像不管用啊
当某个client认为自己需要主动更新窗口内容时,直接向xserver提交更新就是,不需要等待来自xserver的事件消息。Expose只是xserver需要显示了但手上缺少窗口的图形时才发送,算是被动更新。你可以用xscope http://xorg.freedesktop.org/archive/ind ... 4.1.tar.gz,或者源里的xtrace来监测某个client与xserver之间的消息交互
wxf 写了:
2022-11-17 10:10
还有,用XDrawString 函数输出文字时,怎么改变输出方向,比如斜45度
输出个正常方向的,把输出的图形视作矩阵,再乘以旋转矩阵,就得到了旋转后的图形。这个算是计算机图形学的一个基础了,可以参考 https://zhuanlan.zhihu.com/p/144323332
另外不要再用原始的XDrawString系列函数了,至少上Xft吧
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#8

帖子 wxf » 2022-11-17 17:21

astolia 写了:
2022-11-17 13:46
当某个client认为自己需要主动更新窗口内容时,直接向xserver提交更新就是,不需要等待来自xserver的事件消息。Expose只是xserver需要显示了但手上缺少窗口的图形时才发送,算是被动更新。你可以用xscope http://xorg.freedesktop.org/archive/ind ... 4.1.tar.gz,或者源里的xtrace来监测某个client与xserver之间的消息交互
如何在别的程序的窗口上输出文字,让它始终存在。如wps程序,我用hook了 XNextEvent 函数,得到Expose事件后,就向主窗口的一个子窗口(我怀疑这个窗口相当于windows程序里的客户区)输出水印文字,通过拉动边框使窗口大小变化,可以观察到有文字输出,但都是一闪而过。有时显示出来了,鼠标点一下窗口又消失了。 有什么办法让水印文字一直在窗口里?

有没有好的资料介绍一下,我系统学习一下。目前我只看了一下 “Xlib – C Language X Interface.pdf” 还有 “X Window 程式设计 - - C++博客” 这样一篇文章。许多东西还是不了解啊,不知道linux里,图形到底是绘制的,是xserver吗? window mangager 和 client、 server、之间是什么关系? xcb 是实现x协议的一个库,相当于通常使用的x11库吗? carigo 是什么?
头像
astolia
论坛版主
帖子: 5968
注册时间: 2008-09-18 13:11
送出感谢: 1 次
接收感谢: 1037 次

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#9

帖子 astolia » 2022-11-18 12:37

wxf 写了:
2022-11-17 17:21
如何在别的程序的窗口上输出文字,让它始终存在。如wps程序,我用hook了 XNextEvent 函数,得到Expose事件后,就向主窗口的一个子窗口(我怀疑这个窗口相当于windows程序里的客户区)输出水印文字,通过拉动边框使窗口大小变化,可以观察到有文字输出,但都是一闪而过。有时显示出来了,鼠标点一下窗口又消失了。 有什么办法让水印文字一直在窗口里?
这个有点麻烦。我上面说过,绘制可以分为程序主动绘制和Expose事件引发的被动绘制。你只hook了XNextEvent的话,你所有的绘制内容都可能被之后的主动绘制覆盖掉,特别是gtk/qt这类自己实现了一层抽象的图形库。要么你去hook所有用到的主动绘制函数,要么你在xserver那边动手,要么用个独立的窗口覆盖在目标程序上不去碰目标的GC。
wxf 写了:
2022-11-17 17:21
有没有好的资料介绍一下,我系统学习一下。目前我只看了一下 “Xlib – C Language X Interface.pdf” 还有 “X Window 程式设计 - - C++博客” 这样一篇文章。许多东西还是不了解啊,不知道linux里,图形到底是绘制的,是xserver吗? window mangager 和 client、 server、之间是什么关系? xcb 是实现x协议的一个库,相当于通常使用的x11库吗? carigo 是什么?
你可以把这篇文章当成个起点 https://sh.alynx.one/posts/Difference-b ... d-Wayland/ 里面提到的两个链接,特别是英文那个,也非常值得一读。
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#10

帖子 wxf » 2022-11-19 11:39

astolia 写了:
2022-11-18 12:37
这个有点麻烦。我上面说过,绘制可以分为程序主动绘制和Expose事件引发的被动绘制。你只hook了XNextEvent的话,你所有的绘制内容都可能被之后的主动绘制覆盖掉,特别是gtk/qt这类自己实现了一层抽象的图形库。要么你去hook所有用到的主动绘制函数,要么你在xserver那边动手,要么用个独立的窗口覆盖在目标程序上不去碰目标的GC。
我做了个top level窗口,在里边用你的代码显示文字,用 XSetTransientForHint 函数设定好水印窗口和主窗口的关系,使水印窗口总是在主窗口上边,然后有它跟着主窗口的大小变化而变化,但是主窗口移动时没法让他跟着移动,因为窗口移动完成后才收到通知,而不是一直收到通知,所以窗口的办法做不来。

直接往主窗口上边覆盖文字的思路也搞不定。暂时放弃做这个功能了。 linux里开发图形界面太坑了


看了一下你介绍的那个文章,Wayland 没有server,那好得很,赶紧推广。x11什么网络透明全是狗屁,有什么用。同一个机子上还搞个client和server,还要用socket统信,不是自找麻烦吗
头像
astolia
论坛版主
帖子: 5968
注册时间: 2008-09-18 13:11
送出感谢: 1 次
接收感谢: 1037 次

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#11

帖子 astolia » 2022-11-21 10:59

wxf 写了:
2022-11-19 11:39
但是主窗口移动时没法让他跟着移动,因为窗口移动完成后才收到通知,而不是一直收到通知,所以窗口的办法做不来。
窗口移动时会发送ConfigureNotify消息。如下面动图所示,点开看。
b.gif
如果只在移动完成后才收到ConfigureNotify消息,说明屏幕上显示的移动过程是window manager/compositor耍的花招,窗口并没有真正移动过
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#12

帖子 wxf » 2022-11-28 16:22

astolia 写了:
2022-11-21 10:59
wxf 写了:
2022-11-19 11:39
但是主窗口移动时没法让他跟着移动,因为窗口移动完成后才收到通知,而不是一直收到通知,所以窗口的办法做不来。
窗口移动时会发送ConfigureNotify消息。如下面动图所示,点开看。
b.gif
如果只在移动完成后才收到ConfigureNotify消息,说明屏幕上显示的移动过程是window manager/compositor耍的花招,窗口并没有真正移动过
你这个动图里,移动时窗口时收到消息了。我实验时没有收到,我用xev -id 命令来检测事件。 是系统的原因吗,我用的是国产系统麒麟。还是需要修改窗口的事件掩码?我看了看,好像没这样的掩码,ConfigureNotify不就是窗口大小、位置变化的事件吗。 那该怎么办呢
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#13

帖子 wxf » 2022-11-29 0:05

astolia 写了:
2022-11-21 10:59
wxf 写了:
2022-11-19 11:39
但是主窗口移动时没法让他跟着移动,因为窗口移动完成后才收到通知,而不是一直收到通知,所以窗口的办法做不来。
窗口移动时会发送ConfigureNotify消息。如下面动图所示,点开看。
b.gif
如果只在移动完成后才收到ConfigureNotify消息,说明屏幕上显示的移动过程是window manager/compositor耍的花招,窗口并没有真正移动过
我把你上边写的代码放到子窗口里了,这样,就不用自己跟踪主窗口的位置了,只负责显示/隐藏和改变水印窗口的大小就行。还有两个问题,(1)你的这个代码能不能把输出的文字弄成半透明呢,就是和窗口下边的颜色混合。(2)方向改成斜45读。有没有这方便的示例代码呢,给我个链接也行,我去看看。多谢 :Adore
wxf
帖子: 16
注册时间: 2008-05-28 8:50
送出感谢: 3 次
接收感谢: 0

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#14

帖子 wxf » 2022-11-29 0:17

代码: 全选

	XColor color;
	color.red = 50;
	color.green = 60;
	color.blue = 32;

	XAllocColor(display, DefaultColormap(display, s), &color);

	XftColor* FTcolor = NULL;
	FTcolor = (XftColor*)malloc(sizeof(XftColor));
	FTcolor->color.red = color.red;
	FTcolor->color.green = color.green;
	FTcolor->color.blue = color.blue;
	FTcolor->color.alpha = 0x1111;// 0xffff;

	for (int i = 200; i < height; i += 200)
		for (int j = 0; j < width; j += 200)
		{
			//XftDrawStringUtf8(textDraw, &blackColor, f, j, i, (const FcChar8*)text, strlen(text));
			XftDrawStringUtf8(textDraw, FTcolor, f, j, i, (const FcChar8*)text, strlen(text));
		}

	//XftColorFree(display, DefaultVisual(display, s),DefaultColormap(display, s), &blackColor);
	XftColorFree(display, DefaultVisual(display, s), DefaultColormap(display, s), FTcolor);
linux里的便alpha到底是多少位的值?16位的?怎么设置透明度呢。windows里边是一个字节,最大255,最小是0,用alpha值和RGB值乘。linux里是怎么算的。

我用你的代码,改来改去,只有两种结果:输出不透明的灰色字符串,要不就是不输出
头像
astolia
论坛版主
帖子: 5968
注册时间: 2008-09-18 13:11
送出感谢: 1 次
接收感谢: 1037 次

Re: 有懂x11的吗,如何创建一个背景透明的子窗口?

#15

帖子 astolia » 2022-11-30 18:01

wxf 写了:
2022-11-29 0:05
(1)你的这个代码能不能把输出的文字弄成半透明呢,就是和窗口下边的颜色混合。
你只能自行获取下方的内容再自行混合颜色,非常麻烦。原因见9楼链接中最后的加黑文字。
wxf 写了:
2022-11-29 0:05
(2)方向改成斜45读。有没有这方便的示例代码呢,给我个链接也行,我去看看。
7楼已经给过链接了,不理解的话可以看看 https://www.bilibili.com/video/BV1ys411472E?p=4
这些用户感谢了作者 astolia 于这个帖子:
wxf (2022-11-30 21:42)
评价: 3.7%
回复