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

软件和网站开发以及相关技术探讨
头像
wxf
帖子: 50
注册时间: 2008-05-28 8:50

【已解决】有懂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库了,还是用了别的方法截图了?
上次由 wxf 在 2023-07-03 18:06,总共编辑 1 次。
头像
astolia
论坛版主
帖子: 6459
注册时间: 2008-09-18 13:11

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

#2

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

我觉得你应该把文字设成窗口形状掩码,文字部分不透明
wxf 写了: 2022-11-12 11:30 另外,我还要做阻止截图的功能,为什么我把几种最常用的截图相关函数hook了,还是拦截不住截图程序,比如xGetImage 、xcb_get_image_reply 等。是不是这些截图程序静态链接x11库了,还是用了别的方法截图了?
去看截图程序的代码啊
头像
wxf
帖子: 50
注册时间: 2008-05-28 8:50

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
论坛版主
帖子: 6459
注册时间: 2008-09-18 13:11

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;
}
头像
wxf
帖子: 50
注册时间: 2008-05-28 8:50

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

#5

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

太感谢了,我学习一下
头像
wxf
帖子: 50
注册时间: 2008-05-28 8:50

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

#6

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

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

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

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

#7

帖子 astolia