backend: X11 后端添加窗口属性和状态查询接口

This commit is contained in:
2026-04-08 19:32:53 +08:00
parent 0f8c35ae6c
commit 0edb6ad6aa
5 changed files with 259 additions and 0 deletions

View File

@@ -35,6 +35,52 @@ static void atoms_init(backend_t *backend) {
{"WM_WINDOW_ROLE", sizeof("WM_WINDOW_ROLE") - 1, &atoms->WM_WINDOW_ROLE}, {"WM_WINDOW_ROLE", sizeof("WM_WINDOW_ROLE") - 1, &atoms->WM_WINDOW_ROLE},
{"WM_NAME", sizeof("WM_NAME") - 1, &atoms->WM_NAME}, {"WM_NAME", sizeof("WM_NAME") - 1, &atoms->WM_NAME},
{"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &atoms->_NET_WM_NAME}, {"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &atoms->_NET_WM_NAME},
{"_NET_WM_STATE", sizeof("_NET_WM_STATE") - 1, &atoms->_NET_WM_STATE},
{"_NET_WM_STATE_FULLSCREEN", sizeof("_NET_WM_STATE_FULLSCREEN") - 1,
&atoms->_NET_WM_STATE_FULLSCREEN},
{"_NET_WM_STATE_ABOVE", sizeof("_NET_WM_STATE_ABOVE") - 1,
&atoms->_NET_WM_STATE_ABOVE},
{"_NET_WM_STATE_STICKY", sizeof("_NET_WM_STATE_STICKY") - 1,
&atoms->_NET_WM_STATE_STICKY},
{"_NET_WM_STATE_MODAL", sizeof("_NET_WM_STATE_MODAL") - 1,
&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_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1,
&atoms->_NET_WM_WINDOW_TYPE},
{"_NET_WM_WINDOW_TYPE_NORMAL", sizeof("_NET_WM_WINDOW_TYPE_NORMAL") - 1,
&atoms->_NET_WM_WINDOW_TYPE_NORMAL},
{"_NET_WM_WINDOW_TYPE_DESKTOP", sizeof("_NET_WM_WINDOW_TYPE_DESKTOP") - 1,
&atoms->_NET_WM_WINDOW_TYPE_DESKTOP},
{"_NET_WM_WINDOW_TYPE_DOCK", sizeof("_NET_WM_WINDOW_TYPE_DOCK") - 1,
&atoms->_NET_WM_WINDOW_TYPE_DOCK},
{"_NET_WM_WINDOW_TYPE_TOOLBAR", sizeof("_NET_WM_WINDOW_TYPE_TOOLBAR") - 1,
&atoms->_NET_WM_WINDOW_TYPE_TOOLBAR},
{"_NET_WM_WINDOW_TYPE_DIALOG", sizeof("_NET_WM_WINDOW_TYPE_DIALOG") - 1,
&atoms->_NET_WM_WINDOW_TYPE_DIALOG},
{"_NET_WM_WINDOW_TYPE_UTILITY", sizeof("_NET_WM_WINDOW_TYPE_UTILITY") - 1,
&atoms->_NET_WM_WINDOW_TYPE_UTILITY},
{"_NET_WM_WINDOW_TYPE_SPLASH", sizeof("_NET_WM_WINDOW_TYPE_SPLASH") - 1,
&atoms->_NET_WM_WINDOW_TYPE_SPLASH},
{"_NET_WM_WINDOW_TYPE_MENU", sizeof("_NET_WM_WINDOW_TYPE_MENU") - 1,
&atoms->_NET_WM_WINDOW_TYPE_MENU},
{"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
sizeof("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU") - 1,
&atoms->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU},
{"_NET_WM_WINDOW_TYPE_POPUP_MENU",
sizeof("_NET_WM_WINDOW_TYPE_POPUP_MENU") - 1,
&atoms->_NET_WM_WINDOW_TYPE_POPUP_MENU},
{"_NET_WM_WINDOW_TYPE_TOOLTIP", sizeof("_NET_WM_WINDOW_TYPE_TOOLTIP") - 1,
&atoms->_NET_WM_WINDOW_TYPE_TOOLTIP},
{"_NET_WM_WINDOW_TYPE_COMBO", sizeof("_NET_WM_WINDOW_TYPE_COMBO") - 1,
&atoms->_NET_WM_WINDOW_TYPE_COMBO},
{"_NET_WM_WINDOW_TYPE_DND", sizeof("_NET_WM_WINDOW_TYPE_DND") - 1,
&atoms->_NET_WM_WINDOW_TYPE_DND},
{"_NET_WM_WINDOW_TYPE_NOTIFICATION",
sizeof("_NET_WM_WINDOW_TYPE_NOTIFICATION") - 1,
&atoms->_NET_WM_WINDOW_TYPE_NOTIFICATION},
}; };
xcb_intern_atom_cookie_t cookies[countof(atom_list)]; xcb_intern_atom_cookie_t cookies[countof(atom_list)];

View File

@@ -17,12 +17,25 @@ static bool handle_map_request(backend_t *backend, event_t *event,
xcb_window_t window = xcb_event->window; xcb_window_t window = xcb_event->window;
event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST; event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST;
event->data.window_map_request.window = window; event->data.window_map_request.window = window;
window_metadata_t *metadata = &event->data.window_map_request.metadata; window_metadata_t *metadata = &event->data.window_map_request.metadata;
metadata->role = window_get_role(backend, window); metadata->role = window_get_role(backend, window);
metadata->title = window_get_title(backend, window); metadata->title = window_get_title(backend, window);
window_get_class(backend, window, &metadata->class_name, window_get_class(backend, window, &metadata->class_name,
&metadata->instance_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: */ /* TODO: */
return true; return true;
} }

View File

@@ -11,6 +11,28 @@ typedef struct atoms_t {
xcb_atom_t WM_WINDOW_ROLE; xcb_atom_t WM_WINDOW_ROLE;
xcb_atom_t WM_NAME; xcb_atom_t WM_NAME;
xcb_atom_t _NET_WM_NAME; xcb_atom_t _NET_WM_NAME;
xcb_atom_t _NET_WM_STATE;
xcb_atom_t _NET_WM_STATE_FULLSCREEN;
xcb_atom_t _NET_WM_STATE_ABOVE;
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_WINDOW_TYPE;
xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL;
xcb_atom_t _NET_WM_WINDOW_TYPE_DESKTOP;
xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK;
xcb_atom_t _NET_WM_WINDOW_TYPE_TOOLBAR;
xcb_atom_t _NET_WM_WINDOW_TYPE_DIALOG;
xcb_atom_t _NET_WM_WINDOW_TYPE_UTILITY;
xcb_atom_t _NET_WM_WINDOW_TYPE_SPLASH;
xcb_atom_t _NET_WM_WINDOW_TYPE_MENU;
xcb_atom_t _NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
xcb_atom_t _NET_WM_WINDOW_TYPE_POPUP_MENU;
xcb_atom_t _NET_WM_WINDOW_TYPE_TOOLTIP;
xcb_atom_t _NET_WM_WINDOW_TYPE_COMBO;
xcb_atom_t _NET_WM_WINDOW_TYPE_DND;
xcb_atom_t _NET_WM_WINDOW_TYPE_NOTIFICATION;
} atoms_t; } atoms_t;
struct backend_t { struct backend_t {

View File

@@ -61,3 +61,168 @@ void window_get_class(backend_t *backend, xcb_window_t window, char **class_out,
xcb_icccm_get_wm_class_reply_wipe(&prop); xcb_icccm_get_wm_class_reply_wipe(&prop);
} }
xcb_window_t window_get_transient_for(backend_t *backend, xcb_window_t window) {
xcb_connection_t *conn = backend->conn;
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_transient_for_unchecked(conn, window);
xcb_window_t trans_for = XCB_WINDOW_NONE;
if (xcb_icccm_get_wm_transient_for_reply(conn, cookie, &trans_for, nullptr)) {
return trans_for;
}
return XCB_WINDOW_NONE;
}
xcb_get_window_attributes_reply_t *window_get_attributes(backend_t *backend,
xcb_window_t window) {
xcb_get_window_attributes_cookie_t cookie =
xcb_get_window_attributes(backend->conn, window);
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 xcb_atom_t *window_get_atom_array(backend_t *backend,
xcb_window_t window,
xcb_atom_t property,
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;
xcb_atom_t *result = nullptr;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->value_len) {
*out_len = reply->value_len;
result =
p_copy((xcb_atom_t *)xcb_get_property_value(reply), reply->value_len);
}
p_delete(&reply);
return result;
}
static window_type_t atom_to_window_type(const atoms_t *atoms,
xcb_atom_t atom) {
if (atom == atoms->_NET_WM_WINDOW_TYPE_DESKTOP)
return ZDWM_WINDOW_TYPE_DESKTOP;
if (atom == atoms->_NET_WM_WINDOW_TYPE_DOCK) return ZDWM_WINDOW_TYPE_DOCK;
if (atom == atoms->_NET_WM_WINDOW_TYPE_TOOLBAR)
return ZDWM_WINDOW_TYPE_TOOLBAR;
if (atom == atoms->_NET_WM_WINDOW_TYPE_DIALOG) return ZDWM_WINDOW_TYPE_DIALOG;
if (atom == atoms->_NET_WM_WINDOW_TYPE_UTILITY)
return ZDWM_WINDOW_TYPE_UTILITY;
if (atom == atoms->_NET_WM_WINDOW_TYPE_SPLASH) return ZDWM_WINDOW_TYPE_SPLASH;
if (atom == atoms->_NET_WM_WINDOW_TYPE_MENU) return ZDWM_WINDOW_TYPE_MENU;
if (atom == atoms->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
return ZDWM_WINDOW_TYPE_DROPDOWN_MENU;
if (atom == atoms->_NET_WM_WINDOW_TYPE_POPUP_MENU)
return ZDWM_WINDOW_TYPE_POPUP_MENU;
if (atom == atoms->_NET_WM_WINDOW_TYPE_TOOLTIP)
return ZDWM_WINDOW_TYPE_TOOLTIP;
if (atom == atoms->_NET_WM_WINDOW_TYPE_COMBO) return ZDWM_WINDOW_TYPE_COMBO;
if (atom == atoms->_NET_WM_WINDOW_TYPE_DND) return ZDWM_WINDOW_TYPE_DND;
if (atom == atoms->_NET_WM_WINDOW_TYPE_NOTIFICATION)
return ZDWM_WINDOW_TYPE_NOTIFICATION;
return ZDWM_WINDOW_TYPE_NORMAL;
}
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);
if (!atoms) {
*types = nullptr;
*count = 0;
return false;
}
*count = atom_count;
*types = p_new(window_type_t, atom_count);
for (uint32_t i = 0; i < atom_count; i++) {
(*types)[i] = atom_to_window_type(&backend->atoms, atoms[i]);
}
p_delete(&atoms);
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) {
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);
if (!atoms) {
*states = nullptr;
*count = 0;
return false;
}
/* 只保留已知 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 false;
}
*states = buf;
*count = valid;
return true;
}

View File

@@ -1,8 +1,10 @@
#pragma once #pragma once
#include <stddef.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include "core/backend.h" #include "core/backend.h"
#include "core/window.h"
/** /**
* @brief 获取窗口的 role 属性 * @brief 获取窗口的 role 属性
@@ -31,3 +33,14 @@ 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, void window_get_class(backend_t *backend, xcb_window_t window, char **class_out,
char **instance_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);