From a393308704733b5e960f8b54f17977f90dff13ee Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Tue, 28 Apr 2026 03:45:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(core+backend/X11):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E7=8A=B6=E6=80=81=E5=8F=98=E6=9B=B4=EF=BC=88?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=8C=96/=E6=9C=80=E5=B0=8F=E5=8C=96/?= =?UTF-8?q?=E5=85=A8=E5=B1=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/x11/backend.c | 27 ++++++ src/backend/x11/window.c | 18 ++++ src/backend/x11/window.h | 7 ++ src/core/command.h | 12 ++- src/core/event.h | 14 +-- src/core/plan.h | 11 +++ src/core/policy.c | 193 ++++++++++++++++++++++++++++++++++---- src/core/types.h | 12 +++ 8 files changed, 263 insertions(+), 31 deletions(-) diff --git a/src/backend/x11/backend.c b/src/backend/x11/backend.c index 31efd77..2bb09ea 100644 --- a/src/backend/x11/backend.c +++ b/src/backend/x11/backend.c @@ -502,6 +502,33 @@ static void backend_merge_effects( XCB_ICCCM_WM_STATE_WITHDRAWN ); break; + case ZDWM_EFFECT_MINIMIZE_WINDOW: { + auto window = e->as.minimize.window; + xcb_icccm_wm_state_t state = XCB_ICCCM_WM_STATE_NORMAL; + if (e->as.minimize.value) state = XCB_ICCCM_WM_STATE_ICONIC; + window_set_icccm_wm_state(backend->conn, window, state); + } break; + case ZDWM_EFFECT_MAXIMIZE_WINDOW: { + auto window = e->as.maximize.window; + if (e->as.maximize.value) { + xcb_atom_t max_atoms[] = { + backend->atoms._NET_WM_STATE_MAXIMIZED_HORZ, + backend->atoms._NET_WM_STATE_MAXIMIZED_VERT + }; + window_set_net_wm_state(backend, window, countof(max_atoms), max_atoms); + } else { + window_set_net_wm_state(backend, window, 0, nullptr); + } + } break; + case ZDWM_EFFECT_FULLSCREEN_WINDOW: { + auto window = e->as.fullscreen.window; + if (e->as.fullscreen.value) { + auto fullscreen_atom = backend->atoms._NET_WM_STATE_FULLSCREEN; + window_set_net_wm_state(backend, window, 1, &fullscreen_atom); + } else { + window_set_net_wm_state(backend, window, 0, nullptr); + } + } break; case ZDWM_EFFECT_MOVE_WINDOW: { window_configure_t *cfg = find_or_push_configure(configs, e->as.move.window); diff --git a/src/backend/x11/window.c b/src/backend/x11/window.c index 0a261d5..3fee753 100644 --- a/src/backend/x11/window.c +++ b/src/backend/x11/window.c @@ -340,6 +340,24 @@ void window_set_icccm_wm_state( xcb_icccm_set_wm_hints(conn, window, &hints); } +void window_set_net_wm_state( + backend_t *backend, + xcb_window_t window, + size_t count, + xcb_atom_t *atoms +) { + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, + window, + backend->atoms._NET_WM_STATE, + XCB_ATOM_ATOM, + 32, + count, + atoms + ); +} + 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); diff --git a/src/backend/x11/window.h b/src/backend/x11/window.h index 5815942..aec65eb 100644 --- a/src/backend/x11/window.h +++ b/src/backend/x11/window.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,12 @@ void window_set_icccm_wm_state( xcb_window_t window, xcb_icccm_wm_state_t state ); +void window_set_net_wm_state( + backend_t *backend, + xcb_window_t window, + size_t count, + xcb_atom_t *atoms +); void window_list_push(window_list_t *window_list, xcb_window_t window); void window_list_reset(window_list_t *window_list); diff --git a/src/core/command.h b/src/core/command.h index f55f443..e3f3a8c 100644 --- a/src/core/command.h +++ b/src/core/command.h @@ -6,9 +6,10 @@ typedef enum command_type_t { ZDWM_COMMAND_MANAGE_WINDOW, ZDWM_COMMAND_UNMANAGE_WINDOW, - ZDWM_COMMAND_WITHDRAW_WINDOW, ZDWM_COMMAND_FOCUS_WINDOW, ZDWM_COMMAND_KILL_WINDOW, + ZDWM_COMMAND_WITHDRAW_WINDOW, + ZDWM_COMMAND_CHANGE_WINDOW_STATE, ZDWM_COMMAND_SWITCH_WORKSPACE, } command_type_t; @@ -27,6 +28,12 @@ typedef struct only_window_command_t { window_id_t window; } only_window_command_t; +typedef struct window_state_change_command_t { + window_id_t window; + window_state_request_type_t type; + window_state_request_action_t action; +} window_state_change_command_t; + /** * @brief 非 owning 的命令值对象 * @details @@ -41,9 +48,10 @@ typedef struct command_t { union { manage_window_command_t manage_window; only_window_command_t unmanage; - only_window_command_t withdraw; only_window_command_t focus; only_window_command_t kill; + only_window_command_t withdraw; + window_state_change_command_t state_change; switch_workspace_command_t switch_workspace; } as; } command_t; diff --git a/src/core/event.h b/src/core/event.h index 7c4fd54..6243582 100644 --- a/src/core/event.h +++ b/src/core/event.h @@ -108,21 +108,9 @@ typedef struct window_activate_request_event_t { window_activation_source_t source; } window_activate_request_event_t; -typedef enum window_state_request_kind_t { - ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN, - ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED, - ZDWM_WINDOW_STATE_REQUEST_MINIMIZED, -} window_state_request_kind_t; - -typedef enum window_state_request_action_t { - ZDWM_WINDOW_STATE_REQUEST_ACTION_ADD, - ZDWM_WINDOW_STATE_REQUEST_ACTION_REMOVE, - ZDWM_WINDOW_STATE_REQUEST_ACTION_TOGGLE, -} window_state_request_action_t; - typedef struct window_state_request_event_t { window_id_t window; - window_state_request_kind_t kind; + window_state_request_type_t type; window_state_request_action_t action; } window_state_request_event_t; diff --git a/src/core/plan.h b/src/core/plan.h index a7bea3b..0a746a4 100644 --- a/src/core/plan.h +++ b/src/core/plan.h @@ -11,6 +11,9 @@ typedef enum effect_type_t { ZDWM_EFFECT_FOCUS_WINDOW, ZDWM_EFFECT_KILL_WINDOW, ZDWM_EFFECT_WITHDRAW_WINDOW, + ZDWM_EFFECT_MINIMIZE_WINDOW, + ZDWM_EFFECT_MAXIMIZE_WINDOW, + ZDWM_EFFECT_FULLSCREEN_WINDOW, ZDWM_EFFECT_MOVE_WINDOW, ZDWM_EFFECT_RESIZE_WINDOW, ZDWM_EFFECT_CHANGE_BORDER_COLOR, @@ -55,6 +58,11 @@ typedef struct effect_bind_key_t { size_t count; } effect_bind_key_t; +typedef struct effect_bool_window_t { + window_id_t window; + bool value; +} effect_bool_window_t; + typedef struct effect_t { effect_type_t type; union { @@ -63,6 +71,9 @@ typedef struct effect_t { effect_only_window_id_t focus; effect_only_window_id_t kill; effect_only_window_id_t withdraw; + effect_bool_window_t minimize; + effect_bool_window_t maximize; + effect_bool_window_t fullscreen; effect_move_window_t move; effect_resize_window_t resize; effect_change_border_color_t change_border_color; diff --git a/src/core/policy.c b/src/core/policy.c index 35b2c27..8da03f2 100644 --- a/src/core/policy.c +++ b/src/core/policy.c @@ -243,18 +243,6 @@ static void unmanage_window(state_t *state, window_id_t window, plan_t *plan) { } } -static void -withdraw_window(const state_t *state, window_id_t window, plan_t *plan) { - auto win = state_window_get(state, window); - if (!win) return; - - effect_t withdraw_window_effect = { - .type = ZDWM_EFFECT_WITHDRAW_WINDOW, - .as.withdraw.window = win->id, - }; - plan_push_effect(plan, &withdraw_window_effect); -} - static void focus_window(state_t *state, window_id_t window, plan_t *plan) { auto win = state_window_get(state, window); if (!win) return; @@ -284,6 +272,177 @@ static void kill_window(state_t *state, window_id_t window, plan_t *plan) { plan_push_effect(plan, &kill_effect); } +static void withdraw_window(state_t *state, window_id_t window, plan_t *plan) { + auto win = state_window_get(state, window); + if (!win) return; + + effect_t withdraw_window_effect = { + .type = ZDWM_EFFECT_WITHDRAW_WINDOW, + .as.withdraw.window = window, + }; + plan_push_effect(plan, &withdraw_window_effect); +} + +static void fullscreen_window( + state_t *state, + window_id_t window_id, + bool value, + plan_t *plan +) { + auto window = state_window_get(state, window_id); + if (!window) return; + + if ((value && window->geometry_mode == ZDWM_GEOMETRY_FULLSCREEN) || + (!value && window->geometry_mode != ZDWM_GEOMETRY_FULLSCREEN)) { + return; + } + + effect_t fullscreen_window_effect = { + .type = ZDWM_EFFECT_FULLSCREEN_WINDOW, + .as.fullscreen = {.window = window_id, .value = value}, + }; + plan_push_effect(plan, &fullscreen_window_effect); + + if (value) { + state_window_set_geometry_mode(state, window_id, ZDWM_GEOMETRY_FULLSCREEN); + if (window->floating) { + state_window_set_float_rect(state, window_id, window->frame_rect); + } + } else { + state_window_set_geometry_mode(state, window_id, ZDWM_GEOMETRY_NORMAL); + if (window->floating) { + state_window_set_frame_rect(state, window_id, window->float_rect); + } + } + + plan->need_relayout = true; +} + +static void maximize_window( + state_t *state, + window_id_t window_id, + bool value, + plan_t *plan +) { + auto window = state_window_get(state, window_id); + if (!window) return; + + if ((value && window->geometry_mode == ZDWM_GEOMETRY_MAXIMIZED) || + (!value && window->geometry_mode != ZDWM_GEOMETRY_MAXIMIZED)) { + return; + } + + effect_t maximize_window_effect = { + .type = ZDWM_EFFECT_MAXIMIZE_WINDOW, + .as.maximize = {.window = window_id, .value = value}, + }; + plan_push_effect(plan, &maximize_window_effect); + + if (value) { + state_window_set_geometry_mode(state, window_id, ZDWM_GEOMETRY_MAXIMIZED); + if (window->floating) { + state_window_set_float_rect(state, window_id, window->frame_rect); + } + } else { + state_window_set_geometry_mode(state, window_id, ZDWM_GEOMETRY_NORMAL); + if (window->floating) { + state_window_set_frame_rect(state, window_id, window->float_rect); + } + } + + plan->need_relayout = true; +} + +static void minimize_window( + state_t *state, + window_id_t window_id, + bool value, + plan_t *plan +) { + auto window = state_window_get(state, window_id); + if (!window) return; + + if ((value && window->geometry_mode == ZDWM_GEOMETRY_MINIMIZED) || + (!value && window->geometry_mode != ZDWM_GEOMETRY_MINIMIZED)) { + return; + } + + effect_t minimize_window_effect = { + .type = ZDWM_EFFECT_MINIMIZE_WINDOW, + .as.minimize = {.window = window_id, .value = value}, + }; + plan_push_effect(plan, &minimize_window_effect); + + if (value) { + state_window_set_geometry_mode(state, window_id, ZDWM_GEOMETRY_MINIMIZED); + + effect_t unmap_effect = { + .type = ZDWM_EFFECT_UNMAP_WINDOW, + .as.unmap.window = window_id, + }; + plan_push_effect(plan, &unmap_effect); + /* TODO: 可能需要处理焦点 */ + } else { + state_window_set_geometry_mode(state, window_id, ZDWM_GEOMETRY_NORMAL); + state_workspace_set_focused_window(state, window->workspace_id, window_id); + effect_t map_effect = { + .type = ZDWM_EFFECT_MAP_WINDOW, + .as.map.window = window_id + }; + effect_t focus_effect = { + .type = ZDWM_EFFECT_FOCUS_WINDOW, + .as.focus.window = window_id, + }; + plan_push_effect(plan, &map_effect); + plan_push_effect(plan, &focus_effect); + } + + plan->need_relayout = true; +} + +static void change_window_state( + state_t *state, + const window_state_change_command_t *command, + plan_t *plan +) { + auto window = state_window_get(state, command->window); + if (!window) return; + + bool value = false; + switch (command->action) { + case ZDWM_WINDOW_STATE_ACTION_ADD: + value = true; + break; + case ZDWM_WINDOW_STATE_ACTION_REMOVE: + value = false; + break; + case ZDWM_WINDOW_STATE_ACTION_TOGGLE: + switch (command->type) { + case ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN: + value = !(window->geometry_mode == ZDWM_GEOMETRY_FULLSCREEN); + break; + case ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED: + value = !(window->geometry_mode == ZDWM_GEOMETRY_MAXIMIZED); + break; + case ZDWM_WINDOW_STATE_REQUEST_MINIMIZED: + value = !(window->geometry_mode == ZDWM_GEOMETRY_MINIMIZED); + break; + } + } + + switch (command->type) { + case ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN: + fullscreen_window(state, window->id, value, plan); + break; + case ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED: + maximize_window(state, window->id, value, plan); + break; + case ZDWM_WINDOW_STATE_REQUEST_MINIMIZED: + minimize_window(state, window->id, value, plan); + break; + } +} + static void switch_workspace( state_t *state, const switch_workspace_command_t *command, @@ -321,19 +480,21 @@ void policy_apply_command( case ZDWM_COMMAND_UNMANAGE_WINDOW: unmanage_window(state, cmd->as.unmanage.window, plan); break; - case ZDWM_COMMAND_WITHDRAW_WINDOW: - withdraw_window(state, cmd->as.withdraw.window, plan); - break; case ZDWM_COMMAND_FOCUS_WINDOW: focus_window(state, cmd->as.focus.window, plan); break; case ZDWM_COMMAND_KILL_WINDOW: kill_window(state, cmd->as.kill.window, plan); break; + case ZDWM_COMMAND_WITHDRAW_WINDOW: + withdraw_window(state, cmd->as.withdraw.window, plan); + break; + case ZDWM_COMMAND_CHANGE_WINDOW_STATE: + change_window_state(state, &cmd->as.state_change, plan); + break; case ZDWM_COMMAND_SWITCH_WORKSPACE: switch_workspace(state, &cmd->as.switch_workspace, plan); break; - default: } } } diff --git a/src/core/types.h b/src/core/types.h index 9a89402..08d60f8 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -67,3 +67,15 @@ typedef struct key_bind_t { modifier_mask_t modifiers; keysym_t keysym; } key_bind_t; + +typedef enum window_state_request_type_t { + ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN, + ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED, + ZDWM_WINDOW_STATE_REQUEST_MINIMIZED, +} window_state_request_type_t; + +typedef enum window_state_request_action_t { + ZDWM_WINDOW_STATE_ACTION_ADD, + ZDWM_WINDOW_STATE_ACTION_REMOVE, + ZDWM_WINDOW_STATE_ACTION_TOGGLE, +} window_state_request_action_t;