backend: X11 后端添加窗口几何信息查询并修正错误语义

- 添加 window_get_geometry/window_get_fixed_size/window_get_geometry_mode
- window_get_atom_array 改为 bool 返回,区分 XCB 调用错误和空数据
- window_get_types/window_get_states 属性为空时返回 true 而非 false
- handle_map_request 重排操作顺序:错误检查提前,失败路径无内存泄漏
- 补充 _NET_WM_STATE_MAXIMIZED_VERT/HORZ atom 注册
This commit is contained in:
2026-04-09 02:23:06 +08:00
parent 0edb6ad6aa
commit 3884d3fc50
6 changed files with 141 additions and 32 deletions

View File

@@ -47,6 +47,10 @@ static void atoms_init(backend_t *backend) {
&atoms->_NET_WM_STATE_MODAL},
{"_NET_WM_STATE_SKIP_TASKBAR", sizeof("_NET_WM_STATE_SKIP_TASKBAR") - 1,
&atoms->_NET_WM_STATE_SKIP_TASKBAR},
{"_NET_WM_STATE_MAXIMIZED_VERT", sizeof("_NET_WM_STATE_MAXIMIZED_VERT") - 1,
&atoms->_NET_WM_STATE_MAXIMIZED_VERT},
{"_NET_WM_STATE_MAXIMIZED_HORZ", sizeof("_NET_WM_STATE_MAXIMIZED_HORZ") - 1,
&atoms->_NET_WM_STATE_MAXIMIZED_HORZ},
{"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1,
&atoms->_NET_WM_WINDOW_TYPE},

View File

@@ -8,33 +8,58 @@
#include "backend/x11/window.h"
#include "base/memory.h"
#include "core/backend.h"
#include "core/types.h"
#include "core/window.h"
#include "internal.h" /* IWYU pragma: keep */
static void map_request_cleanup(event_t *event) {
window_metadata_t *metadata = &event->data.window_map_request.metadata;
window_props_t *props = &event->data.window_map_request.props;
p_delete(&metadata->role);
p_delete(&metadata->title);
p_delete(&metadata->class_name);
p_delete(&metadata->instance_name);
p_delete(&props->types);
p_delete(&props->states);
}
static bool handle_map_request(backend_t *backend, event_t *event,
const xcb_map_request_event_t *xcb_event) {
p_clear(event, 1);
xcb_window_t window = xcb_event->window;
event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST;
event->data.window_map_request.window = window;
event->data.window_map_request.window = (window_id_t)window;
window_metadata_t *metadata = &event->data.window_map_request.metadata;
window_props_t *props = &event->data.window_map_request.props;
window_geometry_t *geometry = &event->data.window_map_request.geometry;
{
xcb_get_window_attributes_reply_t *wa_reply =
window_get_attributes(backend, window);
if (!wa_reply) return false;
props->override_redirect = (bool)wa_reply->override_redirect;
p_delete(&wa_reply);
}
if (!window_get_geometry(backend, window, &geometry->rect)) return false;
props->transient_for = window_get_transient_for(backend, window);
props->skip_taskbar = window_get_skip_taskbar(backend, window);
props->urgent = window_get_urgency(backend, window);
geometry->mode = window_get_geometry_mode(backend, window);
geometry->fixed_size = window_get_fixed_size(backend, window);
metadata->role = window_get_role(backend, window);
metadata->title = window_get_title(backend, window);
window_get_class(backend, window, &metadata->class_name,
&metadata->instance_name);
window_props_t *props = &event->data.window_map_request.props;
props->transient_for = window_get_transient_for(backend, window);
xcb_get_window_attributes_reply_t *wa_reply =
window_get_attributes(backend, window);
props->override_redirect = (bool)wa_reply->override_redirect;
p_delete(&wa_reply);
props->skip_taskbar = window_get_skip_taskbar(backend, window);
props->urgent = window_get_urgency(backend, window);
window_get_types(backend, window, &props->types, &props->type_count);
window_get_states(backend, window, &props->states, &props->state_count);
/* TODO: */
if (!window_get_types(backend, window, &props->types, &props->type_count) ||
!window_get_states(backend, window, &props->states, &props->state_count)) {
map_request_cleanup(event);
return false;
}
return true;
}

View File

@@ -17,6 +17,8 @@ typedef struct atoms_t {
xcb_atom_t _NET_WM_STATE_STICKY;
xcb_atom_t _NET_WM_STATE_MODAL;
xcb_atom_t _NET_WM_STATE_SKIP_TASKBAR;
xcb_atom_t _NET_WM_STATE_MAXIMIZED_VERT;
xcb_atom_t _NET_WM_STATE_MAXIMIZED_HORZ;
xcb_atom_t _NET_WM_WINDOW_TYPE;
xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;

View File

@@ -113,25 +113,26 @@ bool window_get_urgency(backend_t *backend, xcb_window_t window) {
return (bool)xcb_icccm_wm_hints_get_urgency(&hints);
}
static xcb_atom_t *window_get_atom_array(backend_t *backend,
xcb_window_t window,
xcb_atom_t property,
static bool window_get_atom_array(backend_t *backend, xcb_window_t window,
xcb_atom_t property, xcb_atom_t **out_atoms,
uint32_t *out_len) {
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(
backend->conn, false, window, property, XCB_ATOM_ATOM, 0, UINT32_MAX);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(backend->conn, cookie, nullptr);
if (!reply) return nullptr;
if (!reply) return false;
xcb_atom_t *result = nullptr;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->value_len) {
*out_len = reply->value_len;
result =
*out_atoms =
p_copy((xcb_atom_t *)xcb_get_property_value(reply), reply->value_len);
} else {
*out_len = 0;
*out_atoms = nullptr;
}
p_delete(&reply);
return result;
return true;
}
static window_type_t atom_to_window_type(const atoms_t *atoms,
@@ -161,15 +162,17 @@ static window_type_t atom_to_window_type(const atoms_t *atoms,
bool window_get_types(backend_t *backend, xcb_window_t window,
window_type_t **types, size_t *count) {
if (!types || !count) return false;
uint32_t atom_count = 0;
xcb_atom_t *atoms = window_get_atom_array(
backend, window, backend->atoms._NET_WM_WINDOW_TYPE, &atom_count);
xcb_atom_t *atoms = nullptr;
if (!window_get_atom_array(backend, window,
backend->atoms._NET_WM_WINDOW_TYPE, &atoms,
&atom_count)) {
return false;
}
if (!atoms) {
*types = nullptr;
*count = 0;
return false;
return true;
}
*count = atom_count;
@@ -194,15 +197,16 @@ static window_state_t atom_to_window_state(const atoms_t *atoms,
bool window_get_states(backend_t *backend, xcb_window_t window,
window_state_t **states, size_t *count) {
if (!states || !count) return false;
uint32_t atom_count = 0;
xcb_atom_t *atoms = window_get_atom_array(
backend, window, backend->atoms._NET_WM_STATE, &atom_count);
xcb_atom_t *atoms = nullptr;
if (!window_get_atom_array(backend, window, backend->atoms._NET_WM_STATE,
&atoms, &atom_count)) {
return false;
}
if (!atoms) {
*states = nullptr;
*count = 0;
return false;
return true;
}
/* 只保留已知 state跳过未识别的 atom */
@@ -219,10 +223,80 @@ bool window_get_states(backend_t *backend, xcb_window_t window,
p_delete(&buf);
*states = nullptr;
*count = 0;
return false;
return true;
}
*states = buf;
*count = valid;
return true;
}
bool window_get_fixed_size(backend_t *backend, xcb_window_t window) {
xcb_connection_t *conn = backend->conn;
xcb_size_hints_t hints;
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_normal_hints_unchecked(conn, window);
if (!xcb_icccm_get_wm_normal_hints_reply(conn, cookie, &hints, nullptr)) {
return false;
}
if ((hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) &&
(hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) {
return hints.min_width == hints.max_width &&
hints.min_height == hints.max_height;
}
return false;
}
bool window_get_geometry(backend_t *backend, xcb_window_t window, rect_t *out) {
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(backend->conn, window);
xcb_get_geometry_reply_t *reply =
xcb_get_geometry_reply(backend->conn, cookie, nullptr);
if (!reply) return false;
out->x = reply->x;
out->y = reply->y;
out->width = reply->width;
out->height = reply->height;
p_delete(&reply);
return true;
}
window_geometry_mode_t window_get_geometry_mode(backend_t *backend,
xcb_window_t window) {
xcb_connection_t *conn = backend->conn;
xcb_icccm_wm_hints_t hints;
xcb_get_property_cookie_t hints_cookie =
xcb_icccm_get_wm_hints_unchecked(conn, window);
if (xcb_icccm_get_wm_hints_reply(conn, hints_cookie, &hints, nullptr)) {
if (hints.initial_state == XCB_ICCCM_WM_STATE_ICONIC) {
return ZDWM_GEOMETRY_MINIMIZED;
}
}
uint32_t atom_count = 0;
xcb_atom_t *atoms = nullptr;
if (!window_get_atom_array(backend, window, backend->atoms._NET_WM_STATE,
&atoms, &atom_count)) {
return ZDWM_GEOMETRY_NORMAL;
}
window_geometry_mode_t mode = ZDWM_GEOMETRY_NORMAL;
for (uint32_t i = 0; i < atom_count; i++) {
if (atoms[i] == backend->atoms._NET_WM_STATE_FULLSCREEN) {
mode = ZDWM_GEOMETRY_FULLSCREEN;
break;
}
if (atoms[i] == backend->atoms._NET_WM_STATE_MAXIMIZED_VERT ||
atoms[i] == backend->atoms._NET_WM_STATE_MAXIMIZED_HORZ) {
mode = ZDWM_GEOMETRY_MAXIMIZED;
break;
}
}
p_delete(&atoms);
return mode;
}

View File

@@ -44,3 +44,7 @@ bool window_get_types(backend_t *backend, xcb_window_t window,
window_type_t **types, size_t *count);
bool window_get_states(backend_t *backend, xcb_window_t window,
window_state_t **states, size_t *count);
bool window_get_fixed_size(backend_t *backend, xcb_window_t window);
bool window_get_geometry(backend_t *backend, xcb_window_t window, rect_t *out);
window_geometry_mode_t window_get_geometry_mode(backend_t *backend,
xcb_window_t window);

View File

@@ -66,7 +66,7 @@ typedef struct window_map_request_event_t {
window_id_t window;
window_props_t props;
window_metadata_t metadata;
window_geometry_mode_t geometry;
window_geometry_t geometry;
} window_map_request_event_t;
typedef enum window_remove_reason_t {