Compare commits

..

3 Commits

Author SHA1 Message Date
6b2f0e3218 chore(config): 默认 Super+f 改为切换全屏 2026-07-29 04:53:11 +08:00
d7d16f1ac8 feat(window): 无标题窗口标题按优先级回退显示 instance/class/app_id
新增 window_get_showing_title,按 title → instance_name →
class_name → app_id 优先级取显示标题,窗口快照改用该函数,
避免标题缺失时显示为空。
2026-07-29 04:50:33 +08:00
88129293a0 feat: 补发 ConfigureNotify 通知客户端实际几何
当 WM 拒绝窗口的 ConfigureRequest(因窗口需要重新布局)或映射
窗口时,按 ICCCM 规范主动向客户端发送 synthetic ConfigureNotify,
告知其当前真实几何,避免客户端状态不一致。

- core: 新增 NOTIFY_CONFIGURE 命令与 CONFIGURE_NOTIFY 效果,
  由 policy 在 window_need_layout 分支生成,plan 层下发
- backend(x11): 新增 configure 批次,map/notify 时查询实际几何
  并合成 ConfigureNotify 事件下发
2026-07-29 04:31:28 +08:00
11 changed files with 92 additions and 2 deletions

View File

@@ -558,6 +558,7 @@ static void backend_merge_effects(
switch (e->type) { switch (e->type) {
case ZDWM_EFFECT_MAP_WINDOW: case ZDWM_EFFECT_MAP_WINDOW:
window_list_push(&backend->map, e->as.map.window); window_list_push(&backend->map, e->as.map.window);
window_list_push(&backend->configure, e->as.map.window);
break; break;
case ZDWM_EFFECT_UNMAP_WINDOW: case ZDWM_EFFECT_UNMAP_WINDOW:
window_list_push(&backend->unmap, e->as.unmap.window); window_list_push(&backend->unmap, e->as.unmap.window);
@@ -612,6 +613,9 @@ static void backend_merge_effects(
case ZDWM_EFFECT_CONFIGURE_WINDOW: case ZDWM_EFFECT_CONFIGURE_WINDOW:
merge_window_configure_params(backend, &e->as.configure); merge_window_configure_params(backend, &e->as.configure);
break; break;
case ZDWM_EFFECT_CONFIGURE_NOTIFY:
window_list_push(&backend->configure, e->as.configure_notify.window);
break;
case ZDWM_EFFECT_CHANGE_BORDER_COLOR: { case ZDWM_EFFECT_CHANGE_BORDER_COLOR: {
xcb_change_window_attributes_value_list_t value = { xcb_change_window_attributes_value_list_t value = {
.border_pixel = e->as.change_border_color.color->argb .border_pixel = e->as.change_border_color.color->argb
@@ -643,6 +647,46 @@ static void backend_merge_effects(
} }
} }
static void batch_send_configure_notify(
xcb_connection_t *conn,
xcb_window_t *windows,
size_t count
) {
if (count == 0) return;
auto cookie_list = p_new(xcb_get_geometry_cookie_t, count);
for (size_t i = 0; i < count; ++i) {
auto window = windows[i];
cookie_list[i] = xcb_get_geometry(conn, window);
}
uint32_t event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
for (size_t i = 0; i < count; ++i) {
auto window = windows[i];
auto cookie = cookie_list[i];
auto reply = xcb_get_geometry_reply(conn, cookie, nullptr);
if (!reply) continue;
xcb_configure_notify_event_t ev = {
.response_type = XCB_CONFIGURE_NOTIFY,
.event = window,
.window = window,
.above_sibling = XCB_WINDOW_NONE,
.x = reply->x,
.y = reply->y,
.width = reply->width,
.height = reply->height,
.border_width = reply->border_width,
.override_redirect = false,
};
xcb_send_event(conn, false, window, event_mask, (char *)&ev);
p_delete(&reply);
}
p_delete(&cookie_list);
}
static void backend_batch_apply_effects(backend_t *backend) { static void backend_batch_apply_effects(backend_t *backend) {
xcb_connection_t *conn = backend->conn; xcb_connection_t *conn = backend->conn;
if (backend->unmap.count) { if (backend->unmap.count) {
@@ -672,6 +716,12 @@ static void backend_batch_apply_effects(backend_t *backend) {
backend_apply_window_configure_list(backend); backend_apply_window_configure_list(backend);
{
auto windows = backend->configure.windows;
auto count = backend->configure.count;
batch_send_configure_notify(conn, windows, count);
}
if (backend->update_focus) { if (backend->update_focus) {
backend_focus_window(backend, backend->focus_window); backend_focus_window(backend, backend->focus_window);
} }
@@ -687,6 +737,7 @@ bool backend_apply_effect(
window_list_reset(&backend->unmap); window_list_reset(&backend->unmap);
window_list_reset(&backend->map); window_list_reset(&backend->map);
window_list_reset(&backend->kill); window_list_reset(&backend->kill);
window_list_reset(&backend->configure);
backend_merge_effects(backend, effects, effect_count); backend_merge_effects(backend, effects, effect_count);
backend_batch_apply_effects(backend); backend_batch_apply_effects(backend);

View File

@@ -115,6 +115,7 @@ typedef struct backend_t {
window_list_t unmap; window_list_t unmap;
window_list_t map; window_list_t map;
window_list_t kill; window_list_t kill;
window_list_t configure;
tray_host_t *tray; tray_host_t *tray;
} backend_t; } backend_t;

View File

@@ -122,7 +122,7 @@ bool config_defaults_build(
RAISE_OR_RUN(default_mode, Super("e"), editor); RAISE_OR_RUN(default_mode, Super("e"), editor);
RAISE_OR_RUN(default_mode, Super("q"), browser); RAISE_OR_RUN(default_mode, Super("q"), browser);
RAISE_OR_RUN(default_mode, Super("a"), chrome); RAISE_OR_RUN(default_mode, Super("a"), chrome);
BIND_DEFAULT(Super("f"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_FLOATING}); BIND_DEFAULT(Super("f"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_FULLSCREEN});
BIND_DEFAULT(Super("m"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE}); BIND_DEFAULT(Super("m"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE});
BIND_DEFAULT( BIND_DEFAULT(
Super("Control+space"), Super("Control+space"),

View File

@@ -11,6 +11,7 @@ typedef enum command_type_t {
ZDWM_COMMAND_RAISE_WINDOW, ZDWM_COMMAND_RAISE_WINDOW,
ZDWM_COMMAND_WITHDRAW_WINDOW, ZDWM_COMMAND_WITHDRAW_WINDOW,
ZDWM_COMMAND_CONFIGURE_WINDOW, ZDWM_COMMAND_CONFIGURE_WINDOW,
ZDWM_COMMAND_NOTIFY_CONFIGURE,
ZDWM_COMMAND_CHANGE_WINDOW_STATE, ZDWM_COMMAND_CHANGE_WINDOW_STATE,
ZDWM_COMMAND_START_MOVE_WINDOW, ZDWM_COMMAND_START_MOVE_WINDOW,
ZDWM_COMMAND_STOP_MOVE_WINDOW, ZDWM_COMMAND_STOP_MOVE_WINDOW,
@@ -102,6 +103,7 @@ typedef struct command_t {
only_window_data_t raise; only_window_data_t raise;
only_window_data_t withdraw; only_window_data_t withdraw;
configure_data_t configure; configure_data_t configure;
only_window_data_t notify_configure;
window_state_change_command_t state_change; window_state_change_command_t state_change;
start_interaction_command_t move; start_interaction_command_t move;
start_interaction_command_t resize; start_interaction_command_t resize;

View File

@@ -5,6 +5,7 @@
#include "core/binding.h" #include "core/binding.h"
#include "core/layout.h" #include "core/layout.h"
#include "core/state.h" #include "core/state.h"
#include "core/window.h"
#define FOR_EACH_LISTENER(FIELD, BODY) \ #define FOR_EACH_LISTENER(FIELD, BODY) \
do { \ do { \
@@ -117,7 +118,7 @@ static bool listeners_window_snapshot(
*out = (zdwm_window_t){ *out = (zdwm_window_t){
.workspace = window->workspace_id, .workspace = window->workspace_id,
.id = window->id, .id = window->id,
.title = window->title, .title = window_get_showing_title(window),
.focused = workspace->focused_window_id == window->id, .focused = workspace->focused_window_id == window->id,
.urgent = window->urgent, .urgent = window->urgent,
.skip_taskbar = window->skip_taskbar, .skip_taskbar = window->skip_taskbar,

View File

@@ -121,6 +121,16 @@ void plan_push_resize_effect(plan_t *plan, window_id_t window_id) {
plan_push_effect(plan, &effect); plan_push_effect(plan, &effect);
} }
void plan_push_configure_notify_effect(plan_t *plan, window_id_t window_id) {
if (window_id_invalid(window_id)) return;
effect_t effect = {
.type = ZDWM_EFFECT_CONFIGURE_NOTIFY,
.as.configure_notify.window = window_id,
};
plan_push_effect(plan, &effect);
}
void plan_push_fullscreen_effect( void plan_push_fullscreen_effect(
plan_t *plan, plan_t *plan,
window_id_t window_id, window_id_t window_id,

View File

@@ -26,6 +26,7 @@ void plan_push_kill_effect(plan_t *plan, window_id_t window_id);
void plan_push_withdraw_effect(plan_t *plan, window_id_t window_id); void plan_push_withdraw_effect(plan_t *plan, window_id_t window_id);
void plan_push_move_effect(plan_t *plan, window_id_t window_id); void plan_push_move_effect(plan_t *plan, window_id_t window_id);
void plan_push_resize_effect(plan_t *plan, window_id_t window_id); void plan_push_resize_effect(plan_t *plan, window_id_t window_id);
void plan_push_configure_notify_effect(plan_t *plan, window_id_t window_id);
void plan_push_fullscreen_effect( void plan_push_fullscreen_effect(
plan_t *plan, plan_t *plan,
window_id_t window_id, window_id_t window_id,

View File

@@ -771,6 +771,15 @@ static void route_window_state_request(
command_buffer_push(out, &change_window_state_cmd); command_buffer_push(out, &change_window_state_cmd);
} }
static void
add_notify_configure_command(command_buffer_t *out, window_id_t window) {
command_t notify_cmd = {
.type = ZDWM_COMMAND_NOTIFY_CONFIGURE,
.as.notify_configure = {.window = window},
};
command_buffer_push(out, &notify_cmd);
}
static void route_configure_request( static void route_configure_request(
state_t *state, state_t *state,
const configure_data_t *data, const configure_data_t *data,
@@ -788,6 +797,7 @@ static void route_configure_request(
} }
if (!state_workspace_show(state, window->workspace_id)) return; if (!state_workspace_show(state, window->workspace_id)) return;
add_notify_configure_command(out, data->window);
if (window_need_layout(window)) return; if (window_need_layout(window)) return;
auto workspace = state_workspace_get(state, window->workspace_id); auto workspace = state_workspace_get(state, window->workspace_id);
if (layout_get(layouts, workspace->layout_id)) return; if (layout_get(layouts, workspace->layout_id)) return;
@@ -1690,6 +1700,9 @@ void policy_apply_command(
case ZDWM_COMMAND_CONFIGURE_WINDOW: case ZDWM_COMMAND_CONFIGURE_WINDOW:
configure_window(state, &cmd->as.configure, plan); configure_window(state, &cmd->as.configure, plan);
break; break;
case ZDWM_COMMAND_NOTIFY_CONFIGURE:
plan_push_configure_notify_effect(plan, cmd->as.notify_configure.window);
break;
case ZDWM_COMMAND_CHANGE_WINDOW_STATE: case ZDWM_COMMAND_CHANGE_WINDOW_STATE:
change_window_state(ctx, &cmd->as.state_change, plan); change_window_state(ctx, &cmd->as.state_change, plan);
break; break;

View File

@@ -145,3 +145,11 @@ bool window_can_resize(const window_t *window) {
return true; return true;
} }
const char *window_get_showing_title(const window_t *window) {
if (window->title) return window->title;
if (window->instance_name) return window->instance_name;
if (window->class_name) return window->class_name;
if (window->app_id) return window->app_id;
return nullptr;
}

View File

@@ -67,3 +67,4 @@ bool window_need_resize(const window_t *window, int32_t width, int32_t height);
bool window_should_has_border(const window_t *window); bool window_should_has_border(const window_t *window);
bool window_should_fix_size(const window_t *window); bool window_should_fix_size(const window_t *window);
bool window_can_resize(const window_t *window); bool window_can_resize(const window_t *window);
const char *window_get_showing_title(const window_t *window);

View File

@@ -17,6 +17,7 @@ typedef enum effect_type_t {
ZDWM_EFFECT_MAXIMIZE_WINDOW, ZDWM_EFFECT_MAXIMIZE_WINDOW,
ZDWM_EFFECT_FULLSCREEN_WINDOW, ZDWM_EFFECT_FULLSCREEN_WINDOW,
ZDWM_EFFECT_CONFIGURE_WINDOW, ZDWM_EFFECT_CONFIGURE_WINDOW,
ZDWM_EFFECT_CONFIGURE_NOTIFY,
ZDWM_EFFECT_CHANGE_BORDER_COLOR, ZDWM_EFFECT_CHANGE_BORDER_COLOR,
ZDWM_EFFECT_CHANGE_WINDOW_LIST, ZDWM_EFFECT_CHANGE_WINDOW_LIST,
ZDWM_EFFECT_RESTACK_WINDOWS, ZDWM_EFFECT_RESTACK_WINDOWS,
@@ -86,6 +87,7 @@ typedef struct effect_t {
effect_bool_window_t maximize; effect_bool_window_t maximize;
effect_bool_window_t fullscreen; effect_bool_window_t fullscreen;
configure_data_t configure; configure_data_t configure;
only_window_data_t configure_notify;
effect_change_border_color_t change_border_color; effect_change_border_color_t change_border_color;
effect_window_list_t change_window_list; effect_window_list_t change_window_list;
effect_window_list_t restack_windows; effect_window_list_t restack_windows;