feat: 添加 MOD4 + 鼠标左键移动窗口功能

This commit is contained in:
2026-06-21 16:30:59 +08:00
parent b8cd40df9f
commit 4e7ab45864
9 changed files with 167 additions and 1 deletions

View File

@@ -538,6 +538,17 @@ static void backend_grab_button(
}
}
static void backend_start_move_window(backend_t *backend, xcb_window_t window) {
auto conn = backend->conn;
auto root = backend->screen->root;
auto mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION;
auto mode = XCB_GRAB_MODE_ASYNC;
auto cursor = cursor_get_xcb_cursor(backend, ZDWM_CURSOR_MOVE);
auto time = XCB_TIME_CURRENT_TIME;
xcb_grab_pointer(conn, false, root, mask, mode, mode, root, cursor, time);
}
static void backend_merge_effects(
backend_t *backend,
const effect_t *effects,
@@ -566,6 +577,9 @@ static void backend_merge_effects(
XCB_ICCCM_WM_STATE_WITHDRAWN
);
break;
case ZDWM_EFFECT_START_MOVE_WINDOW:
backend_start_move_window(backend, e->as.move.window);
break;
case ZDWM_EFFECT_MINIMIZE_WINDOW: {
auto window = e->as.minimize.window;
xcb_icccm_wm_state_t state = XCB_ICCCM_WM_STATE_NORMAL;
@@ -619,6 +633,10 @@ static void backend_merge_effects(
break;
case ZDWM_EFFECT_GRAB_BUTTON:
backend_grab_button(backend, &e->as.grab_button);
break;
case ZDWM_EFFECT_UNGRAB_POINTER:
xcb_ungrab_pointer(backend->conn, XCB_CURRENT_TIME);
break;
}
}
}

View File

@@ -271,7 +271,8 @@ static bool handle_button_press(
event_t *event,
const xcb_button_press_event_t *xcb_event
) {
auto handled = fill_button_event(xcb_event, &event->as.pointer_button_press);
auto press = &event->as.pointer_button_press;
auto handled = fill_button_event(xcb_event, press);
if (handled) event->type = ZDWM_EVENT_POINTER_BUTTON_PRESS;
return handled;

View File

@@ -12,6 +12,8 @@ typedef enum command_type_t {
ZDWM_COMMAND_WITHDRAW_WINDOW,
ZDWM_COMMAND_CONFIGURE_WINDOW,
ZDWM_COMMAND_CHANGE_WINDOW_STATE,
ZDWM_COMMAND_START_MOVE_WINDOW,
ZDWM_COMMAND_STOP_MOVE_WINDOW,
ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE,
ZDWM_COMMAND_WINDOW_SET_FLOATING,
ZDWM_COMMAND_WINDOW_SET_STICKY,
@@ -42,6 +44,11 @@ typedef struct window_state_change_command_t {
window_state_request_action_t action;
} window_state_change_command_t;
typedef struct window_start_move_command_t {
window_id_t window;
point_t pointer_coordinate;
} window_start_move_command_t;
typedef struct window_send_to_workspace_command_t {
window_id_t window;
workspace_id_t workspace;
@@ -93,6 +100,7 @@ typedef struct command_t {
only_window_data_t withdraw;
configure_data_t configure;
window_state_change_command_t state_change;
window_start_move_command_t move;
window_send_to_workspace_command_t send_to_workspace;
window_bool_state_t floating;
window_bool_state_t sticky;

View File

@@ -100,6 +100,16 @@ void plan_push_withdraw_effect(plan_t *plan, window_id_t window_id) {
plan_push_effect(plan, &effect);
}
void plan_push_move_effect(plan_t *plan, window_id_t window_id) {
if (window_id_invalid(window_id)) return;
effect_t effect = {
.type = ZDWM_EFFECT_START_MOVE_WINDOW,
.as.move.window = window_id,
};
plan_push_effect(plan, &effect);
}
void plan_push_fullscreen_effect(
plan_t *plan,
window_id_t window_id,
@@ -167,3 +177,8 @@ void plan_push_change_border_color_effect(
};
plan_push_effect(plan, &effect);
}
void plan_push_ungrab_pointer(plan_t *plan) {
effect_t effect = {.type = ZDWM_EFFECT_UNGRAB_POINTER};
plan_push_effect(plan, &effect);
}

View File

@@ -12,6 +12,7 @@ typedef enum effect_type_t {
ZDWM_EFFECT_FOCUS_WINDOW,
ZDWM_EFFECT_KILL_WINDOW,
ZDWM_EFFECT_WITHDRAW_WINDOW,
ZDWM_EFFECT_START_MOVE_WINDOW,
ZDWM_EFFECT_MINIMIZE_WINDOW,
ZDWM_EFFECT_MAXIMIZE_WINDOW,
ZDWM_EFFECT_FULLSCREEN_WINDOW,
@@ -21,6 +22,7 @@ typedef enum effect_type_t {
ZDWM_EFFECT_RESTACK_WINDOWS,
ZDWM_EFFECT_BIND_KEY,
ZDWM_EFFECT_GRAB_BUTTON,
ZDWM_EFFECT_UNGRAB_POINTER,
} effect_type_t;
typedef struct effect_move_window_t {
@@ -68,6 +70,7 @@ typedef struct effect_t {
only_window_data_t focus;
only_window_data_t kill;
only_window_data_t withdraw;
only_window_data_t move;
effect_bool_window_t minimize;
effect_bool_window_t maximize;
effect_bool_window_t fullscreen;
@@ -98,6 +101,7 @@ void plan_push_unmap_effect(plan_t *plan, window_id_t window_id);
void plan_push_focus_effect(plan_t *plan, window_id_t window_id);
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_move_effect(plan_t *plan, window_id_t window_id);
void plan_push_fullscreen_effect(
plan_t *plan,
window_id_t window_id,
@@ -110,3 +114,4 @@ void plan_push_change_border_color_effect(
window_id_t window_id,
const color_t *color
);
void plan_push_ungrab_pointer(plan_t *plan);

View File

@@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <zdwm/action.h>
#include <zdwm/types.h>
@@ -459,6 +460,72 @@ static void route_key_press(
}
}
static void route_pointer_press(
const policy_context_t *ctx,
const pointer_button_event_t *data,
command_buffer_t *out
) {
auto window_id = data->window;
auto window = state_window_get(ctx->state, window_id);
if (!window) return;
/* TODO: 后续改为配置 */
if (data->button == ZDWM_BUTTON_LEFT && data->modifiers == ZDWM_MOD_4) {
command_t start_move_cmd = {
.type = ZDWM_COMMAND_START_MOVE_WINDOW,
.as.move = {
.window = window_id,
.pointer_coordinate = data->root,
},
};
command_buffer_push(out, &start_move_cmd);
}
}
static void route_pointer_release(
const policy_context_t *ctx,
const pointer_button_event_t *data,
command_buffer_t *out
) {
switch (ctx->interaction->mode) {
case ZDWM_WINDOW_INTERACTION_NONE:
break;
case ZDWM_WINDOW_INTERACTION_MOVE: {
command_t stop_move_cmd = {.type = ZDWM_COMMAND_STOP_MOVE_WINDOW};
command_buffer_push(out, &stop_move_cmd);
} break;
}
}
static void route_pointer_motion(
const policy_context_t *ctx,
const pointer_motion_event_t *data,
command_buffer_t *out
) {
auto interaction = ctx->interaction;
switch (interaction->mode) {
case ZDWM_WINDOW_INTERACTION_NONE:
break;
case ZDWM_WINDOW_INTERACTION_MOVE: {
auto rect = interaction->origin_rect;
auto start = interaction->start_coordinate;
auto end = data->root;
command_t configure = {
.type = ZDWM_COMMAND_CONFIGURE_WINDOW,
.as.configure = {
.window = interaction->window,
.changed_fields = ZDWM_CONFIGURE_FIELD_X | ZDWM_CONFIGURE_FIELD_Y,
.x = rect.x + (end.x - start.x),
.y = rect.y + (end.y - start.y),
},
};
command_buffer_push(out, &configure);
} break;
}
}
static void
route_pointer_enter(state_t *state, window_id_t window, command_buffer_t *out) {
add_focus_window_command(out, window);
@@ -676,6 +743,15 @@ void policy_route_event(
case ZDWM_EVENT_KEY_PRESS:
route_key_press(ctx, &event->as.key_press, out);
break;
case ZDWM_EVENT_POINTER_BUTTON_PRESS:
route_pointer_press(ctx, &event->as.pointer_button_press, out);
break;
case ZDWM_EVENT_POINTER_BUTTON_RELEASE:
route_pointer_release(ctx, &event->as.pointer_button_release, out);
break;
case ZDWM_EVENT_POINTER_MOTION:
route_pointer_motion(ctx, &event->as.pointer_motion, out);
break;
case ZDWM_EVENT_POINTER_ENTER:
route_pointer_enter(state, event->as.pointer_enter.window, out);
break;
@@ -1219,6 +1295,25 @@ static void change_window_state(
listeners_notify_window_updated(ctx->listeners, state, command->window);
}
static void start_window_move(
const policy_context_t *ctx,
const window_start_move_command_t *command,
plan_t *plan
) {
auto window_id = command->window;
auto window = state_window_get(ctx->state, window_id);
if (!window) return;
plan_push_move_effect(plan, window_id);
*ctx->interaction = (window_interaction_state_t){
.mode = ZDWM_WINDOW_INTERACTION_MOVE,
.window = window_id,
.start_coordinate = command->pointer_coordinate,
.origin_rect = window->frame_rect,
};
}
static void send_window_to_workspace(
const policy_context_t *ctx,
const window_send_to_workspace_command_t *command,
@@ -1491,6 +1586,13 @@ void policy_apply_command(
case ZDWM_COMMAND_CHANGE_WINDOW_STATE:
change_window_state(ctx, &cmd->as.state_change, plan);
break;
case ZDWM_COMMAND_START_MOVE_WINDOW:
start_window_move(ctx, &cmd->as.move, plan);
break;
case ZDWM_COMMAND_STOP_MOVE_WINDOW:
plan_push_ungrab_pointer(plan);
p_clear(ctx->interaction, 1);
break;
case ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE:
send_window_to_workspace(ctx, &cmd->as.send_to_workspace, plan);
break;

View File

@@ -22,6 +22,7 @@ typedef struct policy_bar_t {
} policy_bar_t;
typedef struct policy_context_t {
window_interaction_state_t *interaction;
binding_table_t *bind_table;
state_t *state;
const rules_t *rules;

View File

@@ -114,6 +114,18 @@ typedef struct only_window_data_t {
window_id_t window;
} only_window_data_t;
typedef enum window_interaction_mode_t {
ZDWM_WINDOW_INTERACTION_NONE,
ZDWM_WINDOW_INTERACTION_MOVE,
} window_interaction_mode_t;
typedef struct window_interaction_state_t {
window_interaction_mode_t mode;
window_id_t window;
point_t start_coordinate;
rect_t origin_rect;
} window_interaction_state_t;
static inline bool window_id_invalid(window_id_t window_id) {
return window_id == ZDWM_WINDOW_ID_INVALID;
}

View File

@@ -48,6 +48,7 @@ typedef struct runtime_t {
void *config_module_handle;
binding_table_t *binding_table;
listeners_t listeners;
window_interaction_state_t interaction;
bar_t bar;
window_list_t bar_windows;
@@ -197,6 +198,7 @@ static void runtime_shutdown(runtime_t *runtime) {
binding_table_destroy(runtime->binding_table);
runtime->binding_table = nullptr;
listeners_cleanup(&runtime->listeners);
p_clear(&runtime->interaction, 1);
window_list_cleanup(&runtime->bar_windows);
backend_destroy(runtime->backend);
runtime->backend = nullptr;
@@ -402,6 +404,8 @@ static void runtime_arrange(runtime_t *runtime) {
static policy_context_t policy_context_init(runtime_t *runtime) {
policy_context_t ctx = {
.interaction = &runtime->interaction,
.bind_table = runtime->binding_table,
.state = &runtime->state,
.rules = &runtime->rules,