feat(core+backend/X11): 实现窗口状态变更(最大化/最小化/全屏)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user