refactor(backend/X11): 重构 effect 应用为延迟批量模式并添加 KILL_WINDOW 支持
- 添加 ZDWM_EFFECT_KILL_WINDOW 副作用和 WM_DELETE_WINDOW 协议支持 - 将 backend_apply_effect 从即时调用 X11 拆分为 merge + batch apply 两阶段: - 先收集 effect 到后端持有的列表 - 再按 unmap(grab server) → map → kill → configure → focus 顺序统一提交
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "backend/output_utils.h"
|
||||
#include "backend/x11/window.h"
|
||||
#include "base/array.h"
|
||||
#include "base/log.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
@@ -169,6 +170,15 @@ backend_t *backend_create(const char *display_name) {
|
||||
void backend_destroy(backend_t *backend) {
|
||||
if (!backend) return;
|
||||
|
||||
p_delete(&backend->config_list.cfgs);
|
||||
p_clear(&backend->config_list, 1);
|
||||
|
||||
p_delete(&backend->unmap.windows);
|
||||
p_clear(&backend->unmap, 1);
|
||||
|
||||
p_delete(&backend->kill.windows);
|
||||
p_clear(&backend->kill, 1);
|
||||
|
||||
xcb_disconnect(backend->conn);
|
||||
backend->conn = nullptr;
|
||||
free(backend);
|
||||
@@ -415,70 +425,70 @@ backend_restack_windows(backend_t *backend, const effect_window_list_t *list) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct window_configure_t {
|
||||
xcb_window_t window;
|
||||
uint16_t mask;
|
||||
xcb_configure_window_value_list_t value;
|
||||
} window_configure_t;
|
||||
static void window_configure_list_reset(window_configure_list_t *configs) {
|
||||
p_clear(configs->cfgs, configs->count);
|
||||
configs->count = 0;
|
||||
}
|
||||
|
||||
static void backend_apply_window_configure_list(backend_t *backend) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
window_configure_list_t *configs = &backend->config_list;
|
||||
for (size_t i = 0; i < configs->count; ++i) {
|
||||
window_configure_t *cfg = &configs->cfgs[i];
|
||||
if (!cfg->mask) continue;
|
||||
|
||||
static void
|
||||
flush_configure(xcb_connection_t *conn, const window_configure_t *cfg) {
|
||||
if (cfg->mask) {
|
||||
xcb_configure_window_aux(conn, cfg->window, cfg->mask, &cfg->value);
|
||||
}
|
||||
}
|
||||
|
||||
static window_configure_t *find_or_push_configure(
|
||||
window_configure_t *cfgs,
|
||||
size_t *cfg_count,
|
||||
xcb_window_t window
|
||||
) {
|
||||
for (size_t j = 0; j < *cfg_count; ++j) {
|
||||
if (cfgs[j].window == window) return &cfgs[j];
|
||||
static window_configure_t *
|
||||
find_or_push_configure(window_configure_list_t *configs, xcb_window_t window) {
|
||||
window_configure_t *cfg = nullptr;
|
||||
for (size_t i = 0; i < configs->count; ++i) {
|
||||
cfg = &configs->cfgs[i];
|
||||
if (cfg->window == window) return cfg;
|
||||
}
|
||||
window_configure_t *cfg = &cfgs[(*cfg_count)++];
|
||||
|
||||
cfg = array_push(configs->cfgs, configs->count, configs->capacity);
|
||||
p_clear(cfg, 1);
|
||||
cfg->window = window;
|
||||
cfg->mask = 0;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
bool backend_apply_effect(
|
||||
static void backend_merge_effects(
|
||||
backend_t *backend,
|
||||
const effect_t *effects,
|
||||
size_t effect_count
|
||||
) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
|
||||
/* collect configure-like effects per window */
|
||||
window_configure_t *cfgs = p_new(window_configure_t, effect_count);
|
||||
size_t cfg_count = 0;
|
||||
|
||||
bool focus_window_changed = false;
|
||||
xcb_window_t focus_window = XCB_WINDOW_NONE;
|
||||
window_configure_list_t *configs = &backend->config_list;
|
||||
|
||||
for (size_t i = 0; i < effect_count; ++i) {
|
||||
const effect_t *e = &effects[i];
|
||||
switch (e->type) {
|
||||
case ZDWM_EFFECT_MAP_WINDOW:
|
||||
xcb_map_window(conn, e->as.map.window);
|
||||
window_list_push(&backend->map, e->as.map.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_UNMAP_WINDOW:
|
||||
xcb_unmap_window(conn, e->as.unmap.window);
|
||||
window_list_push(&backend->unmap, e->as.unmap.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_FOCUS_WINDOW:
|
||||
focus_window = e->as.focus.window;
|
||||
focus_window_changed = true;
|
||||
backend->focus_window = e->as.focus.window;
|
||||
backend->update_focus = true;
|
||||
break;
|
||||
case ZDWM_EFFECT_KILL_WINDOW:
|
||||
window_list_push(&backend->kill, e->as.kill.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_MOVE_WINDOW: {
|
||||
window_configure_t *cfg =
|
||||
find_or_push_configure(cfgs, &cfg_count, e->as.move.window);
|
||||
find_or_push_configure(configs, e->as.move.window);
|
||||
cfg->mask |= XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
|
||||
cfg->value.x = e->as.move.left_top_point.x;
|
||||
cfg->value.y = e->as.move.left_top_point.y;
|
||||
} break;
|
||||
case ZDWM_EFFECT_RESIZE_WINDOW: {
|
||||
window_configure_t *cfg =
|
||||
find_or_push_configure(cfgs, &cfg_count, e->as.resize.window);
|
||||
find_or_push_configure(configs, e->as.resize.window);
|
||||
cfg->mask |= XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
|
||||
cfg->value.width = (uint32_t)e->as.resize.width;
|
||||
cfg->value.height = (uint32_t)e->as.resize.height;
|
||||
@@ -488,18 +498,15 @@ bool backend_apply_effect(
|
||||
.border_pixel = e->as.change_border_color.color->argb
|
||||
};
|
||||
xcb_change_window_attributes_aux(
|
||||
conn,
|
||||
backend->conn,
|
||||
e->as.change_border_color.window,
|
||||
XCB_CW_BORDER_PIXEL,
|
||||
&value
|
||||
);
|
||||
} break;
|
||||
case ZDWM_EFFECT_CHANGE_BORDER_WIDTH: {
|
||||
window_configure_t *cfg = find_or_push_configure(
|
||||
cfgs,
|
||||
&cfg_count,
|
||||
e->as.change_border_width.window
|
||||
);
|
||||
window_configure_t *cfg =
|
||||
find_or_push_configure(configs, e->as.change_border_width.window);
|
||||
cfg->mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
|
||||
cfg->value.border_width = e->as.change_border_width.border_width;
|
||||
} break;
|
||||
@@ -512,14 +519,56 @@ bool backend_apply_effect(
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cfg_count; ++i) {
|
||||
flush_configure(conn, &cfgs[i]);
|
||||
static void backend_batch_apply_effects(backend_t *backend) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
if (backend->unmap.count) {
|
||||
xcb_grab_server(conn);
|
||||
window_clean_event_mask(conn, backend->screen->root);
|
||||
|
||||
window_list_t *unmap = &backend->unmap;
|
||||
for (size_t i = 0; i < unmap->count; ++i) {
|
||||
xcb_window_t window = unmap->windows[i];
|
||||
window_clean_event_mask(conn, window);
|
||||
xcb_unmap_window(conn, window);
|
||||
}
|
||||
p_delete(&cfgs);
|
||||
|
||||
if (focus_window_changed) backend_focus_window(backend, focus_window);
|
||||
root_set_event_mask(backend);
|
||||
xcb_ungrab_server(conn);
|
||||
}
|
||||
|
||||
xcb_flush(conn);
|
||||
for (size_t i = 0; i < backend->map.count; ++i) {
|
||||
xcb_window_t window = backend->map.windows[i];
|
||||
window_set_event_mask(conn, window);
|
||||
xcb_map_window(conn, window);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < backend->kill.count; ++i) {
|
||||
window_kill(backend, backend->kill.windows[i]);
|
||||
}
|
||||
|
||||
backend_apply_window_configure_list(backend);
|
||||
|
||||
if (backend->update_focus) {
|
||||
backend_focus_window(backend, backend->focus_window);
|
||||
}
|
||||
}
|
||||
|
||||
bool backend_apply_effect(
|
||||
backend_t *backend,
|
||||
const effect_t *effects,
|
||||
size_t effect_count
|
||||
) {
|
||||
backend->update_focus = false;
|
||||
window_configure_list_reset(&backend->config_list);
|
||||
window_list_reset(&backend->unmap);
|
||||
window_list_reset(&backend->map);
|
||||
window_list_reset(&backend->kill);
|
||||
|
||||
backend_merge_effects(backend, effects, effect_count);
|
||||
backend_batch_apply_effects(backend);
|
||||
|
||||
xcb_flush(backend->conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
@@ -13,6 +15,7 @@
|
||||
\
|
||||
X(WM_PROTOCOLS) \
|
||||
X(WM_TAKE_FOCUS) \
|
||||
X(WM_DELETE_WINDOW) \
|
||||
X(_NET_ACTIVE_WINDOW) \
|
||||
\
|
||||
X(_NET_SUPPORTING_WM_CHECK) \
|
||||
@@ -54,6 +57,24 @@ typedef struct atoms_t {
|
||||
#undef DECLARATION_ATOM
|
||||
} atoms_t;
|
||||
|
||||
typedef struct window_configure_t {
|
||||
xcb_window_t window;
|
||||
uint16_t mask;
|
||||
xcb_configure_window_value_list_t value;
|
||||
} window_configure_t;
|
||||
|
||||
typedef struct window_configure_list_t {
|
||||
window_configure_t *cfgs;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
} window_configure_list_t;
|
||||
|
||||
typedef struct window_list_t {
|
||||
xcb_window_t *windows;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
} window_list_t;
|
||||
|
||||
struct backend_t {
|
||||
xcb_connection_t *conn;
|
||||
xcb_screen_t *screen;
|
||||
@@ -65,4 +86,12 @@ struct backend_t {
|
||||
/* When no window should take focus, then focus this window */
|
||||
xcb_window_t window_no_focus;
|
||||
xcb_window_t wm_check_window;
|
||||
|
||||
xcb_window_t focus_window;
|
||||
bool update_focus;
|
||||
|
||||
window_configure_list_t config_list;
|
||||
window_list_t unmap;
|
||||
window_list_t map;
|
||||
window_list_t kill;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/backend.h"
|
||||
#include "internal.h"
|
||||
@@ -244,16 +245,84 @@ window_state_t atom_to_window_state(const atoms_t *atoms, xcb_atom_t atom) {
|
||||
return (window_state_t)-1;
|
||||
}
|
||||
|
||||
void window_takefocus(backend_t *backend, xcb_window_t window) {
|
||||
xcb_client_message_event_t ev = {0};
|
||||
|
||||
ev.response_type = XCB_CLIENT_MESSAGE;
|
||||
ev.window = window;
|
||||
ev.format = 32;
|
||||
ev.type = backend->atoms.WM_PROTOCOLS,
|
||||
ev.data.data32[0] = backend->atoms.WM_TAKE_FOCUS;
|
||||
ev.data.data32[1] = XCB_CURRENT_TIME;
|
||||
static bool
|
||||
window_send_event(backend_t *backend, xcb_window_t window, xcb_atom_t atom) {
|
||||
bool exist = false;
|
||||
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
auto WM_PROTOCOLS = backend->atoms.WM_PROTOCOLS;
|
||||
|
||||
xcb_get_property_cookie_t cookie =
|
||||
xcb_icccm_get_wm_protocols_unchecked(conn, window, WM_PROTOCOLS);
|
||||
xcb_icccm_get_wm_protocols_reply_t reply = {0};
|
||||
if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &reply, nullptr)) {
|
||||
for (uint32_t i = 0; !exist && i < reply.atoms_len; ++i) {
|
||||
exist = reply.atoms[i] == atom;
|
||||
}
|
||||
xcb_icccm_get_wm_protocols_reply_wipe(&reply);
|
||||
}
|
||||
|
||||
if (exist) {
|
||||
xcb_client_message_event_t ev = {
|
||||
.response_type = XCB_CLIENT_MESSAGE,
|
||||
.format = 32,
|
||||
.window = window,
|
||||
.type = WM_PROTOCOLS,
|
||||
.data.data32 = {atom, XCB_CURRENT_TIME},
|
||||
};
|
||||
xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)&ev);
|
||||
}
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
void window_takefocus(backend_t *backend, xcb_window_t window) {
|
||||
window_send_event(backend, window, backend->atoms.WM_TAKE_FOCUS);
|
||||
}
|
||||
|
||||
void window_kill(backend_t *backend, xcb_window_t window) {
|
||||
if (window_send_event(backend, window, backend->atoms.WM_DELETE_WINDOW)) {
|
||||
xcb_kill_client(backend->conn, window);
|
||||
}
|
||||
}
|
||||
|
||||
void root_set_event_mask(backend_t *backend) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
xcb_window_t root = backend->screen->root;
|
||||
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
|
||||
xcb_change_window_attributes_value_list_t value_list = {
|
||||
.event_mask =
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_KEY_PRESS,
|
||||
};
|
||||
xcb_change_window_attributes_aux(conn, root, change_mask, &value_list);
|
||||
}
|
||||
|
||||
void window_set_event_mask(xcb_connection_t *conn, xcb_window_t window) {
|
||||
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
|
||||
xcb_change_window_attributes_value_list_t value_list = {
|
||||
.event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
|
||||
};
|
||||
xcb_change_window_attributes_aux(conn, window, change_mask, &value_list);
|
||||
}
|
||||
|
||||
void window_clean_event_mask(xcb_connection_t *conn, xcb_window_t window) {
|
||||
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
|
||||
xcb_change_window_attributes_value_list_t value_list = {
|
||||
.event_mask = XCB_EVENT_MASK_NO_EVENT,
|
||||
};
|
||||
xcb_change_window_attributes_aux(conn, window, change_mask, &value_list);
|
||||
}
|
||||
|
||||
void window_list_push(window_list_t *window_list, xcb_window_t window) {
|
||||
xcb_window_t *win =
|
||||
array_push(window_list->windows, window_list->count, window_list->capacity);
|
||||
*win = window;
|
||||
}
|
||||
|
||||
void window_list_reset(window_list_t *window_list) {
|
||||
p_clear(window_list->windows, window_list->count);
|
||||
window_list->count = 0;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "core/backend.h"
|
||||
#include "core/window.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef struct atoms_t atoms_t;
|
||||
typedef struct window_list_t window_list_t;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口的 role 属性
|
||||
@@ -78,3 +79,11 @@ window_state_t atom_to_window_state(const atoms_t *atoms, xcb_atom_t atom);
|
||||
xcb_icccm_set_wm_class(conn, win, sizeof(instance_class), instance_class)
|
||||
|
||||
void window_takefocus(backend_t *backend, xcb_window_t window);
|
||||
void window_kill(backend_t *backend, xcb_window_t window);
|
||||
|
||||
void root_set_event_mask(backend_t *backend);
|
||||
void window_set_event_mask(xcb_connection_t *conn, xcb_window_t window);
|
||||
void window_clean_event_mask(xcb_connection_t *conn, xcb_window_t window);
|
||||
|
||||
void window_list_push(window_list_t *window_list, xcb_window_t window);
|
||||
void window_list_reset(window_list_t *window_list);
|
||||
|
||||
@@ -9,6 +9,7 @@ typedef enum effect_type_t {
|
||||
ZDWM_EFFECT_MAP_WINDOW = 1,
|
||||
ZDWM_EFFECT_UNMAP_WINDOW,
|
||||
ZDWM_EFFECT_FOCUS_WINDOW,
|
||||
ZDWM_EFFECT_KILL_WINDOW,
|
||||
ZDWM_EFFECT_MOVE_WINDOW,
|
||||
ZDWM_EFFECT_RESIZE_WINDOW,
|
||||
ZDWM_EFFECT_CHANGE_BORDER_COLOR,
|
||||
@@ -53,6 +54,7 @@ typedef struct effect_t {
|
||||
effect_only_window_id_t map;
|
||||
effect_only_window_id_t unmap;
|
||||
effect_only_window_id_t focus;
|
||||
effect_only_window_id_t kill;
|
||||
effect_move_window_t move;
|
||||
effect_resize_window_t resize;
|
||||
effect_change_border_color_t change_border_color;
|
||||
|
||||
Reference in New Issue
Block a user