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

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

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

#16

帖子 wxf » 2022-11-30 21:38

好吧,你的意思是让我获取主窗口的像素值,和旋转后的子窗口里的像素值混合。这是很复杂的。。。
多谢了
头像
wxf
帖子: 50
注册时间: 2008-05-28 8:50

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

#17

帖子 wxf » 2023-07-20 17:03

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
我后来用cairo库解决了这个问题,可以简单地倾斜输出了。不过字迹看上去有点模糊,换字体也一样,还是模糊
上次由 wxf 在 2023-09-06 10:53,总共编辑 1 次。
ffmmm
帖子: 1
注册时间: 2023-09-05 10:53

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

#18

帖子 ffmmm » 2023-09-05 11:10

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库了,还是用了别的方法截图了?
大哥,你的这段代码如何实现的hook,学习一下
头像
wxf
帖子: 50
注册时间: 2008-05-28 8:50

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

#19

帖子 wxf » 2023-09-06 10:39

ffmmm 写了: 2023-09-05 11:10 大哥,你的这段代码如何实现的hook,学习一下
对于Qt程序,拦截xcb_copy_area 函数就行:

代码: 全选

// Qt's screenshot function is filterd by this code 
typedef xcb_void_cookie_t(*Func_xcb_copy_area)(
xcb_connection_t* c,
xcb_drawable_t 	src_drawable,
xcb_drawable_t 	dst_drawable,
xcb_gcontext_t 	gc,
int16_t 	src_x,
int16_t 	src_y,
int16_t 	dst_x,
int16_t 	dst_y,
uint16_t 	width,
uint16_t 	height
);
static Func_xcb_copy_area old_xcb_copy_area = NULL;
static xcb_pixmap_t  g_pixmap_t3 = 0;
xcb_void_cookie_t xcb_copy_area(
	xcb_connection_t* c,
	xcb_drawable_t 	src_drawable,
	xcb_drawable_t 	dst_drawable,
	xcb_gcontext_t 	gc,
	int16_t 	src_x,
	int16_t 	src_y,
	int16_t 	dst_x,
	int16_t 	dst_y,
	uint16_t 	width,
	uint16_t 	height)
{
	if (!old_xcb_copy_area)
	{
		old_xcb_copy_area = (Func_xcb_copy_area)dlsym(RTLD_NEXT, "xcb_copy_area");

		if (old_xcb_copy_area == NULL)
			printf("%s,%d, old_xcb_copy_area == NULL\n", __FILE__, __LINE__);
		else
			printf("%s,%d, old_xcb_copy_area found\n", __FILE__, __LINE__);
	}

	xcb_get_geometry_reply_t* georeply = xcb_get_geometry_reply(c, xcb_get_geometry(c, src_drawable), NULL);

	//if not a window
	if (!georeply)
	{
		printf("%s,%d, src_drawable is not a window id\n", __FILE__, __LINE__);
		return old_xcb_copy_area(c, src_drawable, dst_drawable, gc, src_x, src_y, dst_x, dst_y, width, height);
	}

	uint8_t depth = georeply->depth;
	free(georeply);


	//if src_drawable is root window
	Display* display = XOpenDisplay(NULL);
	if (src_drawable == DefaultRootWindow(display))
	{
		printf("%s,%d, src_drawable is root window:%08x\n", __FILE__, __LINE__, (unsigned int)src_drawable);

		//if exists crypter process
		int count = 0;
		if (DssGetCryptProcessCount(&count) && count > 0)
		{
			printf("%s,%d, screenshot is not allowed, a fake bitmap is gave\n", __FILE__, __LINE__);

			if (g_pixmap_t3 != 0)
				xcb_free_pixmap(c, g_pixmap_t3);

			//return fake data
			g_pixmap_t3 = xcb_generate_id(c);
			xcb_create_pixmap(c, depth, g_pixmap_t3, src_drawable, width, height);

			/* Generate a Graphics Context and fill the pixmap with background color (for images that are smaller than your screen) */
			xcb_gcontext_t gc = xcb_generate_id(c);

			xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
			uint32_t  value[1];
			value[0] = screen->black_pixel;
			xcb_create_gc(c, gc, g_pixmap_t3, XCB_GC_FOREGROUND, value);
			xcb_rectangle_t rect = { 0, 0, width, height };
			xcb_poly_fill_rectangle(c, g_pixmap_t3, gc, 1, &rect);
			xcb_free_gc(c, gc);

			return old_xcb_copy_area(c, g_pixmap_t3, dst_drawable, gc, src_x, src_y, dst_x, dst_y, width, height);
		}
		else
			return old_xcb_copy_area(c, src_drawable, dst_drawable, gc, src_x, src_y, dst_x, dst_y, width, height);
	}


	//get destination window's pid
	pid_t pid;
	if (GetWindowPid(display, src_drawable, &pid))
	{
		printf("%s,%d, pid:%d\n", __FILE__, __LINE__, pid);

		//query permission value
		Bool flag = False;
		if (pid != getpid() && isCryptProcess(pid))
		{
			DWORD dwpermission = 0;
			if (getpermission(pid, &dwpermission) && !(dwpermission & DSS_PRIVILEGE_SAVEAS))
			{
				printf("%s,%d, permission:%ld\n", __FILE__, __LINE__, dwpermission);
				flag = true;
			}
		}

		//if screenshot is not allowed
		if (flag)
		{
			printf("%s,%d, 禁止截图\n", __FILE__, __LINE__);
			//通知托盘

			if (g_pixmap_t3 != 0)
				xcb_free_pixmap(c, g_pixmap_t3);

			//return fake data
			g_pixmap_t3 = xcb_generate_id(c);
			xcb_create_pixmap(c, depth, g_pixmap_t3, src_drawable, width, height);

			/* Generate a Graphics Context and fill the pixmap with background color (for images that are smaller than your screen) */
			xcb_gcontext_t gc = xcb_generate_id(c);

			xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
			uint32_t  value[1];
			value[0] = screen->black_pixel;
			xcb_create_gc(c, gc, g_pixmap_t3, XCB_GC_FOREGROUND, value);
			xcb_rectangle_t rect = { 0, 0, width, height };
			xcb_poly_fill_rectangle(c, g_pixmap_t3, gc, 1, &rect);
			xcb_free_gc(c, gc);

			return old_xcb_copy_area(c, g_pixmap_t3, dst_drawable, gc, src_x, src_y, dst_x, dst_y, width, height);
		}
		else
		{
			return old_xcb_copy_area(c, src_drawable, dst_drawable, gc, src_x, src_y, dst_x, dst_y, width, height);
		}
	}
	else
	{
		printf("%s,%d, old_xcb_copy_area was called\n", __FILE__, __LINE__);
		return old_xcb_copy_area(c, src_drawable, dst_drawable, gc, src_x, src_y, dst_x, dst_y, width, height);
	}
}
对于gtk程序,拦截gdk_pixbuf_get_from_window函数:

代码: 全选

//gtk 's screenshot function is filterd by this code 
typedef GdkDisplay* (*Func_gdk_display_get_default)();
static Func_gdk_display_get_default old_gdk_display_get_default = NULL;

typedef GdkScreen* (*Func_gdk_display_get_default_screen)(GdkDisplay* display);
static Func_gdk_display_get_default_screen old_gdk_display_get_default_screen = NULL;

typedef GdkWindow* (*Func_gdk_screen_get_root_window)(GdkScreen* screen);
static Func_gdk_screen_get_root_window old_gdk_screen_get_root_window = NULL;

typedef GdkPixbuf* (*Func_gdk_pixbuf_get_from_surface)(cairo_surface_t* surface,gint src_x,gint src_y,gint width,gint height);
static Func_gdk_pixbuf_get_from_surface old_gdk_pixbuf_get_from_surface = NULL;

typedef GdkPixbuf* (*Func_gdk_pixbuf_get_from_window)(GdkWindow* window,gint src_x,gint src_y,gint width,gint height);
static Func_gdk_pixbuf_get_from_window old_gdk_pixbuf_get_from_window = NULL;
//static GdkWindow* g_win = NULL;
GdkPixbuf* gdk_pixbuf_get_from_window(GdkWindow* window, gint src_x, gint src_y, gint width, gint height)
{
	if (!old_gdk_pixbuf_get_from_window)
	{
		old_gdk_pixbuf_get_from_window = (Func_gdk_pixbuf_get_from_window)dlsym(RTLD_NEXT, "gdk_pixbuf_get_from_window");

		if (old_gdk_pixbuf_get_from_window == NULL)
			printf("%s,%d, old_gdk_pixbuf_get_from_window == NULL\n", __FILE__, __LINE__);
		else
			printf("%s,%d, old_gdk_pixbuf_get_from_window found\n", __FILE__, __LINE__);
	}

	if (!old_gdk_display_get_default)
	{
		old_gdk_display_get_default = (Func_gdk_display_get_default)dlsym(RTLD_NEXT, "gdk_display_get_default");

		if (old_gdk_display_get_default == NULL)
			printf("%s,%d, old_gdk_display_get_default == NULL\n", __FILE__, __LINE__);
		else
			printf("%s,%d, old_gdk_display_get_default found\n", __FILE__, __LINE__);
	}

	if (!old_gdk_display_get_default_screen)
	{
		old_gdk_display_get_default_screen = (Func_gdk_display_get_default_screen)dlsym(RTLD_NEXT, "gdk_display_get_default_screen");

		if (old_gdk_display_get_default_screen == NULL)
			printf("%s,%d, old_gdk_display_get_default_screen == NULL\n", __FILE__, __LINE__);
		else
			printf("%s,%d, old_gdk_display_get_default_screen found\n", __FILE__, __LINE__);
	}

	if (!old_gdk_screen_get_root_window)
	{
		old_gdk_screen_get_root_window = (Func_gdk_screen_get_root_window)dlsym(RTLD_NEXT, "gdk_screen_get_root_window");

		if (old_gdk_screen_get_root_window == NULL)
			printf("%s,%d, old_gdk_screen_get_root_window  == NULL\n", __FILE__, __LINE__);
		else
			printf("%s,%d, old_gdk_screen_get_root_window found\n", __FILE__, __LINE__);
	}

	if (!old_gdk_pixbuf_get_from_surface)
	{
		old_gdk_pixbuf_get_from_surface = (Func_gdk_pixbuf_get_from_surface)dlsym(RTLD_NEXT, "gdk_pixbuf_get_from_surface");

		if (old_gdk_pixbuf_get_from_surface == NULL)
			printf("%s,%d, old_gdk_pixbuf_get_from_surface == NULL\n", __FILE__, __LINE__);
		else
			printf("%s,%d, old_gdk_pixbuf_get_from_surface found\n", __FILE__, __LINE__);
	}

	GdkDisplay* display = old_gdk_display_get_default();
	GdkScreen* screen = old_gdk_display_get_default_screen(display);
	GdkWindow* root = old_gdk_screen_get_root_window(screen);
	//printf("%s,%d, %08x, %08x\n", __FILE__, __LINE__, root, window);

	if (window == root)
	{
		printf("%s,%d, gdk_pixbuf_get_from_window, root window\n", __FILE__, __LINE__);
		//printf("%s,%d, gdk_pixbuf_get_from_window, x:%d,y:%d,w:%d,h:%d\n", __FILE__, __LINE__, src_x, src_y, width, height);

		int count = 0;
		if (DssGetCryptProcessCount(&count) && count > 0)
		{
			printf("%s,%d, screenshot is not allowed, a fake bitmap is gave\n", __FILE__, __LINE__);

			//give a fake pixbuf
			cairo_surface_t* fake_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
			return old_gdk_pixbuf_get_from_surface(fake_surface, 0, 0, width, height);
		}
		else
		{
			return old_gdk_pixbuf_get_from_window(window, src_x, src_y, width, height);
		}
	}

	return old_gdk_pixbuf_get_from_window(window, src_x, src_y, width, height);
}
要添加的头文件主要是这些:
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/Xlocale.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/XShm.h>

//#include <X11/Xft/Xft.h>
#include <xcb/xproto.h>
#include <xcb/shm.h>
#include <xcb/composite.h>

#include <list>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#include <gdk/gdkx.h>
#include <gtk/gtkunixprint.h>

编译选项主要是这些
-lX11 -lXtst `pkg-config --libs gtk+-3.0`

别的函数,比如x_get_image之类的都差不多这个样子实现的。
可能有错误,请指出来
回复