diff --git a/src/backend/x11/backend.c b/src/backend/x11/backend.c index cb5a3b9..f11b701 100644 --- a/src/backend/x11/backend.c +++ b/src/backend/x11/backend.c @@ -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; } } } diff --git a/src/backend/x11/event.c b/src/backend/x11/event.c index f6f8ddc..c05719f 100644 --- a/src/backend/x11/event.c +++ b/src/backend/x11/event.c @@ -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; diff --git a/src/core/command.h b/src/core/command.h index 5fcaf6f..2c622f9 100644 --- a/src/core/command.h +++ b/src/core/command.h @@ -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; diff --git a/src/core/plan.c b/src/core/plan.c index 0d4bb53..9a26751 100644 --- a/src/core/plan.c +++ b/src/core/plan.c @@ -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); +} diff --git a/src/core/plan.h b/src/core/plan.h index 5e3ef31..11f3f22 100644 --- a/src/core/plan.h +++ b/src/core/plan.h @@ -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); diff --git a/src/core/policy.c b/src/core/policy.c index 43a78b3..14b68cf 100644 --- a/src/core/policy.c +++ b/src/core/policy.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -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; diff --git a/src/core/policy.h b/src/core/policy.h index ed47bad..c5f7791 100644 --- a/src/core/policy.h +++ b/src/core/policy.h @@ -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; diff --git a/src/core/types.h b/src/core/types.h index 47f9cba..aacbd83 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -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; } diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 98244b5..9bdbccf 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -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,