在Xorg透明窗口中添加完全不透明元素的实现方法
实现半透明背景+完全不透明元素的X11窗口(Cairo+XCB)
完全可以实现你要的效果——窗口背景保持半透明,图标、文字等元素完全不透明。核心问题在于你当前用_NET_WM_WINDOW_OPACITY全局控制窗口透明度,会让所有元素都被统一透明化。正确的做法是让窗口本身支持像素级Alpha通道,通过Cairo直接绘制带Alpha的背景和不透明的元素。
核心修改思路
- 替换窗口的visual为支持32位ARGB的类型,让X11允许窗口渲染带Alpha通道的内容。
- 移除
_NET_WM_WINDOW_OPACITY的全局透明度设置,改用Cairo绘制半透明背景。 - 在Cairo中直接绘制不透明的元素(alpha设为1.0),利用混合模式叠加在半透明背景上。
具体代码调整
1. 查找并使用ARGB32 Visual
X11默认的root visual可能不支持Alpha通道,需要先找到屏幕上的32位真彩色visual:
xcb_visualtype_t* find_argb_visual(xcb_connection_t* conn, xcb_screen_t* screen) { xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(screen); for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data); for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { xcb_visualtype_t* visual = visual_iter.data; // 筛选32位真彩色visual,适配CAIRO_FORMAT_ARGB32 if (depth_iter.data->depth == 32 && visual->class == XCB_VISUAL_CLASS_TRUE_COLOR) { return visual; } } } // 找不到则回退到默认root visual return screen->root_visual; }
然后修改窗口创建代码,替换原有的root_visual:
// 替换原有的visual_type获取逻辑 xcb_visualtype_t* argb_visual = find_argb_visual(win->con->connection, win->con->screen); // 创建对应colormap xcb_colormap_t colormap = xcb_generate_id(win->con->connection); xcb_create_colormap(win->con->connection, XCB_COLORMAP_ALLOC_NONE, colormap, win->con->screen->root, argb_visual->visual_id); // 窗口创建时使用argb_visual xcb_create_window(win->con->connection, XCB_COPY_FROM_PARENT, win->window, win->con->screen->root, win->x, win->y, win->width, win->height, 0, // border XCB_WINDOW_CLASS_INPUT_OUTPUT, argb_visual->visual_id, // 替换为ARGB visual value_mask, value_list);
2. 调整Cairo绘制逻辑
保留你现有的半透明背景绘制代码,添加不透明元素的绘制逻辑(注意元素的alpha设为1.0):
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(surface); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); // 绘制半透明背景(alpha=0.8) cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 0.8); cairo_paint(cr); // 绘制完全不透明的图标(示例) cairo_surface_t* icon_surface = cairo_image_surface_create_from_png("icon.png"); if (cairo_surface_status(icon_surface) == CAIRO_STATUS_SUCCESS) { cairo_set_operator(cr, CAIRO_OPERATOR_OVER); // 覆盖模式,默认就是这个,可省略 cairo_set_source_surface(cr, icon_surface, 20, 20); cairo_paint(cr); // 图标本身不透明的话,会完全覆盖背景 cairo_surface_destroy(icon_surface); } // 绘制完全不透明的文字 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); // rgb默认alpha=1.0 cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 18); cairo_move_to(cr, 20, 80); cairo_show_text(cr, "这是完全不透明的文字"); // 清理资源 cairo_destroy(cr);
3. 窗口绘制与属性调整
确保你的Cairo XCB表面使用ARGB visual创建:
// 初始化窗口的Cairo表面时传入argb_visual win->surface = cairo_xcb_surface_create(win->con->connection, win->window, argb_visual, win->width, win->height); win->cr = cairo_create(win->surface);
同时移除_NET_WM_WINDOW_OPACITY的设置代码——全局窗口透明度会覆盖像素级的Alpha通道,导致元素也被透明化。
4. 保留Shape扩展(可选)
你之前用的Shape扩展可以继续保留,用来实现自定义窗口形状,这和像素级Alpha通道不冲突:Shape控制窗口的输入/可见区域,Alpha控制像素的透明度,两者可以配合使用。
关键注意事项
- 确保窗口管理器支持ARGB窗口(现代WM如KWin、Mutter、i3都支持)。
- 绘制元素时,无论是图片还是文字,都要保证其Alpha通道为1.0(或直接使用RGB颜色)。
- 使用Cairo的
CAIRO_OPERATOR_OVER混合模式(默认),可以让不透明元素正确叠加在半透明背景上。
内容的提问来源于stack exchange,提问作者nect
相关产品推荐
相关产品推荐

