backend: X11 后端 window 信息查询接口使用原始封装代替语义化封装

- 拆分 window_get_skip_taskbar / window_get_urgency /
  window_get_states / window_get_geometry_mode 为更底层的
  window_get_atom_array / window_get_wm_hints / atom_to_window_state
- window_get_fixed_size 改为输出参数风格
- 扁平化 window_map_request_event_t,内联原 window_props_t 和
  window_geometry_t 的字段
- window_props_t → window_layer_props_t,仅保留 layer 分类所需的
  types/states
This commit is contained in:
2026-04-09 05:08:39 +08:00
parent 3884d3fc50
commit 0c7fdf08df
7 changed files with 159 additions and 197 deletions

View File

@@ -3,6 +3,7 @@
#include <stdio.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xproto.h>
#include "backend/x11/window.h"
@@ -12,55 +13,113 @@
#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 window_state_t *derive_window_states(const atoms_t *atoms,
const xcb_atom_t *raw,
uint32_t count, size_t *out_count) {
if (!raw || count == 0) {
*out_count = 0;
return nullptr;
}
window_state_t *buf = p_new(window_state_t, count);
size_t valid = 0;
for (uint32_t i = 0; i < count; i++) {
window_state_t s = atom_to_window_state(atoms, raw[i]);
if (s != (window_state_t)-1) buf[valid++] = s;
}
if (valid == 0) {
p_delete(&buf);
*out_count = 0;
return nullptr;
}
*out_count = valid;
return buf;
}
static bool derive_skip_taskbar(const atoms_t *atoms, const xcb_atom_t *raw,
uint32_t count) {
for (uint32_t i = 0; i < count; i++)
if (raw[i] == atoms->_NET_WM_STATE_SKIP_TASKBAR) return true;
return false;
}
static window_geometry_mode_t geometry_mode_from_hints(
const xcb_icccm_wm_hints_t *hints) {
if (hints->initial_state == XCB_ICCCM_WM_STATE_ICONIC)
return ZDWM_GEOMETRY_MINIMIZED;
return ZDWM_GEOMETRY_NORMAL;
}
static window_geometry_mode_t geometry_mode_from_states(
const atoms_t *atoms, const xcb_atom_t *state_atoms, uint32_t state_count) {
for (uint32_t i = 0; i < state_count; i++) {
if (state_atoms[i] == atoms->_NET_WM_STATE_FULLSCREEN)
return ZDWM_GEOMETRY_FULLSCREEN;
if (state_atoms[i] == atoms->_NET_WM_STATE_MAXIMIZED_VERT ||
state_atoms[i] == atoms->_NET_WM_STATE_MAXIMIZED_HORZ)
return ZDWM_GEOMETRY_MAXIMIZED;
}
return ZDWM_GEOMETRY_NORMAL;
}
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;
p_clear(event, 1);
event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST;
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;
window_map_request_event_t *ev = &event->data.window_map_request;
ev->window = (window_id_t)window;
ev->transient_for = window_get_transient_for(backend, window);
ev->geometry_mode = ZDWM_GEOMETRY_NORMAL;
{
xcb_get_window_attributes_reply_t *wa_reply =
xcb_get_window_attributes_reply_t *wa =
window_get_attributes(backend, window);
if (!wa_reply) return false;
props->override_redirect = (bool)wa_reply->override_redirect;
p_delete(&wa_reply);
if (!wa) return false;
ev->override_redirect = (bool)wa->override_redirect;
p_delete(&wa);
xcb_icccm_wm_hints_t wm_hints = {0};
if (window_get_wm_hints(backend, window, &wm_hints)) {
ev->urgent = (bool)xcb_icccm_wm_hints_get_urgency(&wm_hints);
ev->geometry_mode = geometry_mode_from_hints(&wm_hints);
}
if (!window_get_geometry(backend, window, &geometry->rect)) return false;
if (!window_get_fixed_size(backend, window, &ev->fixed_size)) return false;
if (!window_get_geometry(backend, window, &ev->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);
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);
const atoms_t *atoms = &backend->atoms;
xcb_atom_t *state_atoms = nullptr;
uint32_t state_count = 0;
if (!window_get_atom_array(backend, window, atoms->_NET_WM_STATE,
&state_atoms, &state_count)) {
return false;
}
if (ev->geometry_mode == ZDWM_GEOMETRY_NORMAL && state_atoms) {
ev->geometry_mode =
geometry_mode_from_states(atoms, state_atoms, state_count);
}
if (state_atoms) {
ev->skip_taskbar = derive_skip_taskbar(atoms, state_atoms, state_count);
ev->props.states = derive_window_states(atoms, state_atoms, state_count,
&ev->props.state_count);
}
p_delete(&state_atoms);
window_layer_props_t *props = &ev->props;
if (!window_get_types(backend, window, &props->types, &props->type_count)) {
p_delete(&props->states);
return false;
}
ev->metadata.role = window_get_role(backend, window);
ev->metadata.title = window_get_title(backend, window);
window_get_class(backend, window, &ev->metadata.class_name,
&ev->metadata.instance_name);
return true;
}

View File

@@ -81,60 +81,6 @@ xcb_get_window_attributes_reply_t *window_get_attributes(backend_t *backend,
return xcb_get_window_attributes_reply(backend->conn, cookie, nullptr);
}
bool window_get_skip_taskbar(backend_t *backend, xcb_window_t window) {
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(
backend->conn, false, window, backend->atoms._NET_WM_STATE, XCB_ATOM_ATOM,
0, UINT32_MAX);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(backend->conn, cookie, nullptr);
if (!reply) return false;
bool found = false;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32) {
xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
uint32_t len = reply->value_len;
for (uint32_t i = 0; i < len; i++) {
if (atoms[i] == backend->atoms._NET_WM_STATE_SKIP_TASKBAR) {
found = true;
break;
}
}
}
p_delete(&reply);
return found;
}
bool window_get_urgency(backend_t *backend, xcb_window_t window) {
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_hints_unchecked(backend->conn, window);
xcb_icccm_wm_hints_t hints;
xcb_icccm_get_wm_hints_reply(backend->conn, cookie, &hints, nullptr);
return (bool)xcb_icccm_wm_hints_get_urgency(&hints);
}
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 false;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->value_len) {
*out_len = reply->value_len;
*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 true;
}
static window_type_t atom_to_window_type(const atoms_t *atoms,
xcb_atom_t atom) {
if (atom == atoms->_NET_WM_WINDOW_TYPE_DESKTOP)
@@ -185,55 +131,9 @@ bool window_get_types(backend_t *backend, xcb_window_t window,
return true;
}
static window_state_t atom_to_window_state(const atoms_t *atoms,
xcb_atom_t atom) {
if (atom == atoms->_NET_WM_STATE_FULLSCREEN)
return ZDWM_WINDOW_STATE_FULLSCREEN;
if (atom == atoms->_NET_WM_STATE_ABOVE) return ZDWM_WINDOW_STATE_ABOVE;
if (atom == atoms->_NET_WM_STATE_STICKY) return ZDWM_WINDOW_STATE_STICKY;
if (atom == atoms->_NET_WM_STATE_MODAL) return ZDWM_WINDOW_STATE_MODAL;
return (window_state_t)-1;
}
bool window_get_states(backend_t *backend, xcb_window_t window,
window_state_t **states, size_t *count) {
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 false;
}
if (!atoms) {
*states = nullptr;
*count = 0;
return true;
}
/* 只保留已知 state跳过未识别的 atom */
window_state_t *buf = p_new(window_state_t, atom_count);
size_t valid = 0;
for (uint32_t i = 0; i < atom_count; i++) {
window_state_t s = atom_to_window_state(&backend->atoms, atoms[i]);
if (s != (window_state_t)-1) buf[valid++] = s;
}
p_delete(&atoms);
if (valid == 0) {
p_delete(&buf);
*states = nullptr;
*count = 0;
return true;
}
*states = buf;
*count = valid;
return true;
}
bool window_get_fixed_size(backend_t *backend, xcb_window_t window) {
bool window_get_fixed_size(backend_t *backend, xcb_window_t window, bool *out) {
xcb_size_hints_t hints = {0};
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)) {
@@ -242,11 +142,12 @@ bool window_get_fixed_size(backend_t *backend, xcb_window_t window) {
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 &&
*out = hints.min_width == hints.max_width &&
hints.min_height == hints.max_height;
} else {
*out = false;
}
return false;
return true;
}
bool window_get_geometry(backend_t *backend, xcb_window_t window, rect_t *out) {
@@ -264,39 +165,44 @@ bool window_get_geometry(backend_t *backend, xcb_window_t window, rect_t *out) {
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;
}
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 false;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->value_len) {
*out_len = reply->value_len;
*out_atoms =
p_copy((xcb_atom_t *)xcb_get_property_value(reply), reply->value_len);
} else {
*out_len = 0;
*out_atoms = nullptr;
}
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;
p_delete(&reply);
return true;
}
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;
}
bool window_get_wm_hints(backend_t *backend, xcb_window_t window,
xcb_icccm_wm_hints_t *out) {
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_hints_unchecked(backend->conn, window);
if (!xcb_icccm_get_wm_hints_reply(backend->conn, cookie, out, nullptr)) {
p_clear(out, 1);
return false;
}
return true;
}
p_delete(&atoms);
return mode;
window_state_t atom_to_window_state(const atoms_t *atoms, xcb_atom_t atom) {
if (atom == atoms->_NET_WM_STATE_FULLSCREEN)
return ZDWM_WINDOW_STATE_FULLSCREEN;
if (atom == atoms->_NET_WM_STATE_ABOVE) return ZDWM_WINDOW_STATE_ABOVE;
if (atom == atoms->_NET_WM_STATE_STICKY) return ZDWM_WINDOW_STATE_STICKY;
if (atom == atoms->_NET_WM_STATE_MODAL) return ZDWM_WINDOW_STATE_MODAL;
return (window_state_t)-1;
}

View File

@@ -1,11 +1,15 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xproto.h>
#include "core/backend.h"
#include "core/window.h"
typedef struct atoms_t atoms_t;
/**
* @brief 获取窗口的 role 属性
* @param backend 后端实例指针
@@ -33,18 +37,16 @@ char *window_get_title(backend_t *backend, xcb_window_t window);
*/
void window_get_class(backend_t *backend, xcb_window_t window, char **class_out,
char **instance_out);
xcb_window_t window_get_transient_for(backend_t *backend, xcb_window_t window);
xcb_get_window_attributes_reply_t *window_get_attributes(backend_t *backend,
xcb_window_t window);
bool window_get_skip_taskbar(backend_t *backend, xcb_window_t window);
bool window_get_urgency(backend_t *backend, xcb_window_t window);
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_fixed_size(backend_t *backend, xcb_window_t window, bool *out);
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);
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);
bool window_get_wm_hints(backend_t *backend, xcb_window_t window,
xcb_icccm_wm_hints_t *out);
window_state_t atom_to_window_state(const atoms_t *atoms, xcb_atom_t atom);

View File

@@ -64,9 +64,16 @@ typedef struct pointer_enter_event_t {
typedef struct window_map_request_event_t {
window_id_t window;
window_props_t props;
window_id_t transient_for;
bool override_redirect;
bool skip_taskbar;
bool urgent;
bool fixed_size;
window_geometry_mode_t geometry_mode;
rect_t rect;
window_layer_props_t props;
window_metadata_t metadata;
window_geometry_t geometry;
} window_map_request_event_t;
typedef enum window_remove_reason_t {

View File

@@ -88,7 +88,7 @@ bool layer_stack_lower(layer_stack_t *layer, window_id_t window) {
return true;
}
layer_type_t layer_classify(const window_props_t *props) {
layer_type_t layer_classify(const window_layer_props_t *props) {
for (size_t i = 0; i < props->type_count; ++i) {
window_type_t type = props->types[i];
if (type == ZDWM_WINDOW_TYPE_NOTIFICATION) return ZDWM_WINDOW_LAYER_OVERLAY;

View File

@@ -57,4 +57,4 @@ bool layer_stack_raise(layer_stack_t *layer, window_id_t window);
*/
bool layer_stack_lower(layer_stack_t *layer, window_id_t window);
layer_type_t layer_classify(const window_props_t *props);
layer_type_t layer_classify(const window_layer_props_t *props);

View File

@@ -30,19 +30,13 @@ typedef enum window_state_t {
ZDWM_WINDOW_STATE_STICKY,
} window_state_t;
typedef struct window_props_t {
window_id_t transient_for;
bool override_redirect;
bool skip_taskbar;
bool urgent;
typedef struct window_layer_props_t {
window_type_t *types;
size_t type_count;
window_state_t *states;
size_t state_count;
} window_props_t;
} window_layer_props_t;
typedef struct window_metadata_t {
char *title;
@@ -51,9 +45,3 @@ typedef struct window_metadata_t {
char *class_name;
char *instance_name;
} window_metadata_t;
typedef struct window_geometry_t {
window_geometry_mode_t mode;
rect_t rect;
bool fixed_size;
} window_geometry_t;