基于wlroots-0.19的自定义Wayland合成器:xdg_surface无法映射且wlr_surface_get_texture()始终返回NULL
基于wlroots-0.19的自定义Wayland合成器:xdg_surface无法映射且wlr_surface_get_texture()始终返回NULL
看起来你遇到的问题根源是核心逻辑判断完全颠倒,再加上几个关键步骤的实现疏漏,导致窗口根本没被正确处理,自然不会有纹理和映射事件。我来一步步帮你排查修复:
1. 最致命的错误:窗口类型判断逻辑完全搞反了
先看你的_handle_new_xdg_shell_surface函数,这段条件判断直接把所有需要处理的窗口都跳过了:
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL || xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { return; }
你现在的逻辑是:只要是普通窗口(TOPLEVEL)或弹窗(POPUP),就直接返回不处理——这完全和你需要的逻辑相反!Wayland中只有TOPLEVEL(普通应用窗口)和POPUP(右键菜单、弹窗)是需要合成器处理的有效窗口类型,其他小众角色才应该跳过。
修复后的判断逻辑:
// 只跳过非TOPLEVEL/非POPUP的无效surface类型 if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL && xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) { return; }
修正后,你的合成器才会真正处理那些需要显示的窗口,而不是直接丢弃它们。
2. 为什么没收到map事件?
客户端只有在收到合成器的configure事件后,才会发送map事件并提交窗口纹理。你之前的逻辑跳过了TOPLEVEL窗口的处理,自然不会调用_configure_xdg发送configure事件,客户端就一直处于"等待合成器确认"的状态,永远不会映射窗口。
另外,你_configure_xdg函数的代码被截断了,而且wl_array_add的用法也错了——它需要传入枚举值的指针,而不是直接传枚举值:
修复后的_configure_xdg:
void Window::_configure_xdg() { wl_array states; wl_array_init(&states); uint32_t state; if (_maximized) { state = XDG_TOPLEVEL_STATE_MAXIMIZED; wl_array_add(&states, &state); } if (_fullscreened) { state = XDG_TOPLEVEL_STATE_FULLSCREEN; wl_array_add(&states, &state); } if (_resizing) { state = XDG_TOPLEVEL_STATE_RESIZING; wl_array_add(&states, &state); } if (_activated) { // 替换成你判断窗口是否激活的逻辑 state = XDG_TOPLEVEL_STATE_ACTIVATED; wl_array_add(&states, &state); } // 发送configure事件,告诉客户端我们接受这个窗口,并传递尺寸/状态 wlr_xdg_toplevel_send_configure(_xdg_surface->toplevel, &states, static_cast<int>(_width), static_cast<int>(_height)); wl_array_release(&states); }
3. 为什么wlr_surface_get_texture()返回NULL?
即使修复了上面的问题,你也需要在获取纹理前做一个关键检查:只有当surface被映射(mapped)后,才会有有效纹理。你可以在commit handler中添加这个判断:
void Window::_handle_commit(struct wl_listener *listener, void *data) { auto surface = static_cast<struct wlr_surface*>(data); if (!wlr_surface_mapped(surface)) { return; // 跳过未映射的surface,此时没有有效纹理 } wlr_texture *texture = wlr_surface_get_texture(surface); if (texture != nullptr) { // 这里可以处理纹理更新,比如标记窗口需要重绘 _dirty = true; } }
4. 其他需要注意的细节
- 确保
wlr_scene_xdg_surface_create返回的场景节点是有效的:你的Node::alloc_scene_tree需要正确创建或返回合成器的根场景树,否则窗口不会被添加到渲染队列中。 - 检查
Window构造函数中的listener是否正确绑定:比如_map_listener、_commit_listener等,要确保它们的notify函数被正确设置并添加到对应的wl_signal上。 wlr_xdg_surface_ping的位置没问题,但要确保在创建完所有listener后调用,避免客户端响应ping时我们还没准备好处理事件。
总结修复步骤
- 立刻修正
_handle_new_xdg_shell_surface中的窗口类型判断逻辑,这是一切问题的根源。 - 修复
_configure_xdg函数,确保正确发送configure事件给客户端。 - 在commit handler中添加surface映射状态检查,避免获取无效纹理。
- 验证所有wl_signal listener都正确绑定,没有遗漏关键事件(比如map、unmap、destroy)。
按照这些步骤修复后,你应该能正常收到map事件,并且wlr_surface_get_texture()也能返回有效纹理了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

