backend: X11 后端添加获取窗口 metadata 信息方式

This commit is contained in:
2026-04-07 03:16:51 +08:00
parent ab961d6d79
commit 9728fad871
8 changed files with 176 additions and 11 deletions

View File

@@ -13,10 +13,47 @@
#include "backend/output_utils.h"
#include "base/log.h"
#include "base/macros.h"
#include "base/memory.h"
#include "core/types.h"
#include "internal.h" /* IWYU pragma: keep */
typedef struct atom_item_t {
const char *name;
size_t len;
xcb_atom_t *atom;
} atom_item_t;
static void atoms_init(backend_t *backend) {
xcb_connection_t *conn = backend->conn;
atoms_t *atoms = &backend->atoms;
atom_item_t atom_list[] = {
{"COMPOUND_TEXT", sizeof("COMPOUND_TEXT") - 1, &atoms->COMPOUND_TEXT},
{"UTF8_STRING", sizeof("UTF8_STRING") - 1, &atoms->UTF8_STRING},
{"WM_WINDOW_ROLE", sizeof("WM_WINDOW_ROLE") - 1, &atoms->WM_WINDOW_ROLE},
{"WM_NAME", sizeof("WM_NAME") - 1, &atoms->WM_NAME},
{"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &atoms->_NET_WM_NAME},
};
xcb_intern_atom_cookie_t cookies[countof(atom_list)];
for (size_t i = 0; i < countof(atom_list); ++i) {
const atom_item_t *atom = &atom_list[i];
cookies[i] = xcb_intern_atom_unchecked(conn, false, atom->len, atom->name);
}
xcb_intern_atom_reply_t *reply = nullptr;
for (size_t i = 0; i < countof(atom_list); ++i) {
reply = xcb_intern_atom_reply(conn, cookies[i], nullptr);
if (!reply) continue;
atom_item_t *atom = &atom_list[i];
*atom->atom = reply->atom;
p_delete(&reply);
}
}
backend_t *backend_create(const char *display_name) {
int screen_num = 0;
xcb_connection_t *conn = xcb_connect(display_name, &screen_num);
@@ -63,6 +100,8 @@ backend_t *backend_create(const char *display_name) {
backend->have_xfixes = false;
}
atoms_init(backend);
return backend;
}

View File

@@ -1,21 +1,32 @@
#include "core/event.h"
#include <stdio.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xproto.h>
#include "backend/x11/window.h"
#include "base/memory.h"
#include "core/backend.h"
#include "core/window.h"
#include "internal.h" /* IWYU pragma: keep */
static bool handle_map_request(event_t *event,
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;
window_metadata_t *metadata = &event->data.window_map_request.metadata;
metadata->app_id = window_get_role(backend, window);
metadata->title = window_get_title(backend, window);
window_get_class(backend, window, &metadata->class_name,
&metadata->instance_name);
/* TODO: */
return true;
}
static bool handle_unmap_notify(event_t *event,
static bool handle_unmap_notify(backend_t *backend, event_t *event,
const xcb_unmap_notify_event_t *xcb_event) {
p_clear(event, 1);
event->type = ZDWM_EVENT_WINDOW_REMOVE;
@@ -23,7 +34,7 @@ static bool handle_unmap_notify(event_t *event,
return true;
}
static bool handle_destroy_notify(event_t *event,
static bool handle_destroy_notify(backend_t *backend, event_t *event,
const xcb_destroy_notify_event_t *xcb_event) {
p_clear(event, 1);
event->type = ZDWM_EVENT_WINDOW_REMOVE;
@@ -32,7 +43,8 @@ static bool handle_destroy_notify(event_t *event,
}
static bool handle_configure_request(
event_t *event, const xcb_configure_request_event_t *xcb_event) {
backend_t *backend, event_t *event,
const xcb_configure_request_event_t *xcb_event) {
p_clear(event, 1);
event->type = ZDWM_EVENT_CONFIGURE_REQUEST;
/* TODO: */
@@ -52,9 +64,9 @@ bool backend_next_event(backend_t *backend, event_t *event) {
printf("xcb event type: %u\n", response_type);
switch (response_type) {
#define EVENT(type, handler) \
case type: \
handled = handler(event, (void *)raw_event); \
#define EVENT(type, handler) \
case type: \
handled = handler(backend, event, (void *)raw_event); \
break
EVENT(XCB_MAP_REQUEST, handle_map_request);

View File

@@ -1,12 +1,23 @@
#pragma once
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include "core/backend.h" /* IWYU pragma: keep */
typedef struct atoms_t {
xcb_atom_t COMPOUND_TEXT;
xcb_atom_t UTF8_STRING;
xcb_atom_t WM_WINDOW_ROLE;
xcb_atom_t WM_NAME;
xcb_atom_t _NET_WM_NAME;
} atoms_t;
struct backend_t {
xcb_connection_t *conn;
xcb_screen_t *screen;
int screenp;
bool have_xfixes;
atoms_t atoms;
};

63
src/backend/x11/window.c Normal file
View File

@@ -0,0 +1,63 @@
#include "window.h"
#include <stddef.h>
#include <stdint.h>
#include <xcb/xcb.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xproto.h>
#include "base/memory.h"
#include "internal.h"
static char *window_get_text_property(backend_t *backend, xcb_window_t window,
xcb_atom_t property) {
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(
backend->conn, false, window, property, XCB_ATOM_ANY, 0, UINT32_MAX);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(backend->conn, cookie, nullptr);
if (!reply) return nullptr;
int length = xcb_get_property_value_length(reply);
char *value = (char *)xcb_get_property_value(reply);
char *text = nullptr;
if (length && reply->format == 8 &&
(reply->type == XCB_ATOM_STRING ||
reply->type == backend->atoms.UTF8_STRING ||
reply->type == backend->atoms.COMPOUND_TEXT)) {
text = p_strndup(value, (size_t)length);
}
p_delete(&reply);
return text;
}
char *window_get_role(backend_t *backend, xcb_window_t window) {
xcb_atom_t property = backend->atoms.WM_WINDOW_ROLE;
return window_get_text_property(backend, window, property);
}
char *window_get_title(backend_t *backend, xcb_window_t window) {
xcb_atom_t property = backend->atoms._NET_WM_NAME;
char *name = window_get_text_property(backend, window, property);
if (!name) {
property = backend->atoms.WM_NAME;
name = window_get_text_property(backend, window, property);
}
return name;
}
void window_get_class(backend_t *backend, xcb_window_t window, char **class_out,
char **instance_out) {
xcb_icccm_get_wm_class_reply_t prop;
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_class_unchecked(backend->conn, window);
if (!xcb_icccm_get_wm_class_reply(backend->conn, cookie, &prop, nullptr)) {
return;
}
if (class_out) *class_out = p_strdup_nullable(prop.class_name);
if (instance_out) *instance_out = p_strdup_nullable(prop.instance_name);
xcb_icccm_get_wm_class_reply_wipe(&prop);
}

33
src/backend/x11/window.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <xcb/xproto.h>
#include "core/backend.h"
/**
* @brief 获取窗口的 role 属性
* @param backend 后端实例指针
* @param window 目标窗口
* @return 返回窗口的 role 属性字符串,调用者持有其内存并负责调用 free 释放
*/
char *window_get_role(backend_t *backend, xcb_window_t window);
/**
* @brief 获取窗口标题
* @details 获取窗口的 _NET_WM_NAME/WM_NAME 属性作为其 title
* @param backend 后端实例指针
* @param window 目标窗口
* @return 返回窗口标题字符串,由调用者持有其内存并负责调用 free 释放
*/
char *window_get_title(backend_t *backend, xcb_window_t window);
/**
* @brief 获取窗口的 class 和 instance 信息
* @param backend 后端实例指针
* @param window 目标窗口
* @param class_out class 信息返回指针,调用者持有其内存并负责调用 free 释放
* @param instance_out instance 信息返回指针,调用者持有其内存并负责调用 free
* 释放
*/
void window_get_class(backend_t *backend, xcb_window_t window, char **class_out,
char **instance_out);

View File

@@ -24,6 +24,12 @@ static inline char *p_strdup(const char *text) {
return r;
}
static inline char *p_strndup(const char *text, size_t n) {
char *r = strndup(text, n);
if (!r) abort();
return r;
}
static inline char *p_strdup_nullable(const char *text) {
if (!text) return nullptr;
return p_strdup(text);

View File

@@ -45,10 +45,10 @@ typedef struct window_props_t {
} window_props_t;
typedef struct window_metadata_t {
const char *title;
const char *app_id;
const char *class_name;
const char *instance_name;
char *title;
char *app_id; /* X11 中将其当 role 用 */
char *class_name;
char *instance_name;
} window_metadata_t;
typedef struct window_geometry_t {

View File

@@ -9,6 +9,7 @@ add_executable(${TEST_APP_NAME}
${SOURCE_DIR}/config/runtime_config.c
${SOURCE_DIR}/backend/x11/backend.c
${SOURCE_DIR}/backend/x11/event.c
${SOURCE_DIR}/backend/x11/window.c
${SOURCE_DIR}/core/layer.c
${SOURCE_DIR}/core/layout.c
${SOURCE_DIR}/core/runtime.c