feat(backend/X11): 实现 backend_apply_effect 接口以提交 X11 副作用
This commit is contained in:
@@ -12,9 +12,11 @@
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "backend/output_utils.h"
|
||||
#include "backend/x11/window.h"
|
||||
#include "base/log.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/plan.h"
|
||||
#include "core/types.h"
|
||||
#include "internal.h"
|
||||
|
||||
@@ -49,6 +51,40 @@ static void atoms_init(backend_t *backend) {
|
||||
}
|
||||
}
|
||||
|
||||
static void create_no_focus_window(backend_t *backend) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
|
||||
xcb_window_t win = xcb_generate_id(conn);
|
||||
uint32_t value_mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
|
||||
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP;
|
||||
const xcb_create_window_value_list_t value_list = {
|
||||
.override_redirect = true,
|
||||
.background_pixel = backend->screen->black_pixel,
|
||||
.border_pixel = backend->screen->black_pixel,
|
||||
.colormap = backend->screen->default_colormap,
|
||||
};
|
||||
xcb_create_window_aux(
|
||||
conn,
|
||||
backend->screen->root_depth,
|
||||
win,
|
||||
backend->screen->root,
|
||||
-1,
|
||||
-1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
XCB_WINDOW_CLASS_COPY_FROM_PARENT,
|
||||
backend->screen->root_visual,
|
||||
value_mask,
|
||||
&value_list
|
||||
);
|
||||
window_set_class_instance(conn, win);
|
||||
window_set_name_static(conn, win, "zdwm no input window");
|
||||
xcb_map_window(conn, win);
|
||||
|
||||
backend->window_no_focus = win;
|
||||
}
|
||||
|
||||
backend_t *backend_create(const char *display_name) {
|
||||
int screen_num = 0;
|
||||
xcb_connection_t *conn = xcb_connect(display_name, &screen_num);
|
||||
@@ -100,6 +136,7 @@ backend_t *backend_create(const char *display_name) {
|
||||
}
|
||||
|
||||
atoms_init(backend);
|
||||
create_no_focus_window(backend);
|
||||
|
||||
return backend;
|
||||
}
|
||||
@@ -299,11 +336,159 @@ void backend_detect_destroy(backend_detect_t *detect) {
|
||||
free(detect);
|
||||
}
|
||||
|
||||
static void backend_focus_window(backend_t *backend, xcb_window_t window) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
xcb_window_t root = backend->screen->root;
|
||||
const atoms_t *atoms = &backend->atoms;
|
||||
xcb_atom_t property = atoms->_NET_ACTIVE_WINDOW;
|
||||
xcb_timestamp_t time = XCB_TIME_CURRENT_TIME;
|
||||
if (window == XCB_WINDOW_NONE || window == root) {
|
||||
window = backend->window_no_focus;
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, window, time);
|
||||
xcb_delete_property(conn, root, property);
|
||||
} else {
|
||||
uint8_t mode = XCB_PROP_MODE_REPLACE;
|
||||
xcb_atom_t type = XCB_ATOM_WINDOW;
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, window, time);
|
||||
xcb_change_property(conn, mode, root, property, type, 32, 1, &window);
|
||||
window_takefocus(backend, window);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void backend_change_window_list(
|
||||
backend_t *backend,
|
||||
bool stack_list,
|
||||
const effect_window_list_t *list
|
||||
) {
|
||||
xcb_change_property(
|
||||
backend->conn,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
backend->screen->root,
|
||||
stack_list ? backend->atoms._NET_CLIENT_LIST_STACKING
|
||||
: backend->atoms._NET_CLIENT_LIST,
|
||||
XCB_ATOM_WINDOW,
|
||||
32,
|
||||
list->count,
|
||||
list->windows
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
backend_restack_windows(backend_t *backend, const effect_window_list_t *list) {
|
||||
if (list->count == 0) return;
|
||||
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
xcb_window_t sibling_window = backend->window_no_focus;
|
||||
for (size_t i = 0; i < list->count; ++i) {
|
||||
uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
|
||||
xcb_configure_window_value_list_t params = {
|
||||
.sibling = sibling_window,
|
||||
.stack_mode = XCB_STACK_MODE_ABOVE
|
||||
};
|
||||
sibling_window = list->windows[i];
|
||||
xcb_configure_window_aux(conn, sibling_window, mask, ¶ms);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct window_configure_t {
|
||||
xcb_window_t window;
|
||||
uint16_t mask;
|
||||
xcb_configure_window_value_list_t value;
|
||||
} window_configure_t;
|
||||
|
||||
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];
|
||||
}
|
||||
window_configure_t *cfg = &cfgs[(*cfg_count)++];
|
||||
cfg->window = window;
|
||||
cfg->mask = 0;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
bool backend_apply_effect(
|
||||
backend_t *backend,
|
||||
const effect_t *effects,
|
||||
size_t effect_count
|
||||
) {
|
||||
/* TODO: */
|
||||
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;
|
||||
|
||||
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);
|
||||
break;
|
||||
case ZDWM_EFFECT_UNMAP_WINDOW:
|
||||
xcb_unmap_window(conn, e->as.unmap.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_FOCUS_WINDOW:
|
||||
backend_focus_window(backend, e->as.focus.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_MOVE_WINDOW: {
|
||||
window_configure_t *cfg =
|
||||
find_or_push_configure(cfgs, &cfg_count, 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);
|
||||
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;
|
||||
} break;
|
||||
case ZDWM_EFFECT_CHANGE_BORDER_COLOR: {
|
||||
xcb_change_window_attributes_value_list_t value = {
|
||||
.border_pixel = e->as.change_border_color.color->argb
|
||||
};
|
||||
xcb_change_window_attributes_aux(
|
||||
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
|
||||
);
|
||||
cfg->mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
|
||||
cfg->value.border_width = e->as.change_border_width.border_width;
|
||||
} break;
|
||||
case ZDWM_EFFECT_CHANGE_WINDOW_LIST:
|
||||
backend_change_window_list(backend, false, &e->as.change_window_list);
|
||||
break;
|
||||
case ZDWM_EFFECT_RESTACK_WINDOWS:
|
||||
backend_restack_windows(backend, &e->as.restack_windows);
|
||||
backend_change_window_list(backend, true, &e->as.restack_windows);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cfg_count; ++i) {
|
||||
flush_configure(conn, &cfgs[i]);
|
||||
}
|
||||
p_delete(&cfgs);
|
||||
|
||||
xcb_flush(conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
X(WM_NAME) \
|
||||
X(_NET_WM_NAME) \
|
||||
\
|
||||
X(WM_PROTOCOLS) \
|
||||
X(WM_TAKE_FOCUS) \
|
||||
X(_NET_ACTIVE_WINDOW) \
|
||||
\
|
||||
X(_NET_CLIENT_LIST) \
|
||||
X(_NET_CLIENT_LIST_STACKING) \
|
||||
\
|
||||
X(_NET_WM_STATE) \
|
||||
X(_NET_WM_STATE_FULLSCREEN) \
|
||||
X(_NET_WM_STATE_ABOVE) \
|
||||
@@ -49,4 +56,7 @@ struct backend_t {
|
||||
bool have_xfixes;
|
||||
|
||||
atoms_t atoms;
|
||||
|
||||
/* When no window should take focus, then focus this window */
|
||||
xcb_window_t window_no_focus;
|
||||
};
|
||||
|
||||
@@ -243,3 +243,17 @@ window_state_t atom_to_window_state(const atoms_t *atoms, xcb_atom_t atom) {
|
||||
if (atom == atoms->_NET_WM_STATE_MODAL) return ZDWM_WINDOW_STATE_MODAL;
|
||||
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;
|
||||
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)&ev);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
@@ -65,3 +66,15 @@ bool window_get_wm_hints(
|
||||
xcb_icccm_wm_hints_t *out
|
||||
);
|
||||
window_state_t atom_to_window_state(const atoms_t *atoms, xcb_atom_t atom);
|
||||
|
||||
#define window_set_name_static(conn, win, name) \
|
||||
xcb_icccm_set_wm_name(conn, win, XCB_ATOM_STRING, 8, sizeof(name) - 1, name)
|
||||
|
||||
#define window_set_class_instance(conn, win) \
|
||||
window_set_class_instance_static(conn, win, "zdwm", "zdwm")
|
||||
#define window_set_class_instance_static(conn, win, instance, class) \
|
||||
_window_set_class_instance_static(conn, win, instance "\0" class)
|
||||
#define _window_set_class_instance_static(conn, win, instance_class) \
|
||||
xcb_icccm_set_wm_class(conn, win, sizeof(instance_class), instance_class)
|
||||
|
||||
void window_takefocus(backend_t *backend, xcb_window_t window);
|
||||
|
||||
@@ -13,8 +13,7 @@ typedef enum dirty_flags_t : dirty_mask_t {
|
||||
} dirty_flags_t;
|
||||
|
||||
typedef enum effect_type_t {
|
||||
ZDWM_EFFECT_NONE,
|
||||
ZDWM_EFFECT_MAP_WINDOW,
|
||||
ZDWM_EFFECT_MAP_WINDOW = 1,
|
||||
ZDWM_EFFECT_UNMAP_WINDOW,
|
||||
ZDWM_EFFECT_FOCUS_WINDOW,
|
||||
ZDWM_EFFECT_MOVE_WINDOW,
|
||||
|
||||
Reference in New Issue
Block a user