feat(backend): 支持鼠标抓取功能

This commit is contained in:
2026-06-20 01:04:00 +08:00
parent 7a47f4d44d
commit b8cd40df9f
12 changed files with 178 additions and 54 deletions

View File

@@ -26,6 +26,7 @@ project(${APP_NAME}
add_executable(${APP_NAME}
${SOURCE_DIR}/backend/output_utils.c
${SOURCE_DIR}/backend/x11/backend.c
${SOURCE_DIR}/backend/x11/common.c
${SOURCE_DIR}/backend/x11/cursor.c
${SOURCE_DIR}/backend/x11/event.c
${SOURCE_DIR}/backend/x11/window.c

View File

@@ -522,6 +522,22 @@ backend_bind_key(backend_t *backend, const effect_bind_key_t *bind_key) {
window_grab_keys(backend, root, bind_key->keys, bind_key->count);
}
static void backend_grab_button(
backend_t *backend,
const effect_grab_button_t *grab_button
) {
auto conn = backend->conn;
auto window = grab_button->window;
xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, window, XCB_MOD_MASK_ANY);
auto buttons = grab_button->buttons;
for (size_t i = 0; i < grab_button->count; ++i) {
auto button = buttons[i].button;
auto modifiers = buttons[i].modifiers;
window_grab_button(backend, window, button, modifiers);
}
}
static void backend_merge_effects(
backend_t *backend,
const effect_t *effects,
@@ -601,6 +617,8 @@ static void backend_merge_effects(
case ZDWM_EFFECT_BIND_KEY:
backend_bind_key(backend, &e->as.bind_key);
break;
case ZDWM_EFFECT_GRAB_BUTTON:
backend_grab_button(backend, &e->as.grab_button);
}
}
}

71
src/backend/x11/common.c Normal file
View File

@@ -0,0 +1,71 @@
#include "common.h"
#include <stddef.h>
#include <stdint.h>
#include <xcb/xproto.h>
#include "base/macros.h"
#include "core/types.h"
typedef struct modifier_map_t {
modifier_bit_t modifier;
xcb_mod_mask_t mask;
} modifier_map_t;
static constexpr modifier_map_t modifier_map[] = {
{ZDWM_MOD_SHIFT, XCB_MOD_MASK_SHIFT},
{ZDWM_MOD_CONTROL, XCB_MOD_MASK_CONTROL},
{ZDWM_MOD_1, XCB_MOD_MASK_1},
{ZDWM_MOD_2, XCB_MOD_MASK_2},
{ZDWM_MOD_3, XCB_MOD_MASK_3},
{ZDWM_MOD_4, XCB_MOD_MASK_4},
{ZDWM_MOD_5, XCB_MOD_MASK_5},
};
modifier_mask_t modifiers_xcb_to_zdwm(uint16_t mask) {
modifier_mask_t mods = ZDWM_MOD_NONE;
for (size_t i = 0; i < countof(modifier_map); ++i) {
auto item = &modifier_map[i];
if (mask & item->mask) mods |= item->modifier;
}
return mods;
}
uint16_t modifiers_zdwm_to_xcb(modifier_mask_t mask) {
uint16_t mods = 0U;
for (size_t i = 0; i < countof(modifier_map); ++i) {
auto item = &modifier_map[i];
if (mask & item->modifier) mods |= item->mask;
}
return mods;
}
typedef struct button_map_t {
button_t zdwm_button;
xcb_button_index_t xcb_button;
} button_map_t;
static constexpr button_map_t button_map[] = {
{ZDWM_BUTTON_LEFT, XCB_BUTTON_INDEX_1},
{ZDWM_BUTTON_RIGHT, XCB_BUTTON_INDEX_3},
{ZDWM_BUTTON_MIDDLE, XCB_BUTTON_INDEX_2},
/* TODO: 后续可以考虑加上鼠标滚轮事件 */
};
button_t button_xcb_to_zdwm(xcb_button_t button) {
for (size_t i = 0; i < countof(button_map); ++i) {
auto item = &button_map[i];
if (button == item->xcb_button) return item->zdwm_button;
}
return ZDWM_BUTTON_NONE;
}
xcb_button_t button_zdwm_to_xcb(button_t button) {
for (size_t i = 0; i < countof(button_map); ++i) {
auto item = &button_map[i];
if (button == item->zdwm_button) return item->xcb_button;
}
return XCB_BUTTON_INDEX_ANY;
}

12
src/backend/x11/common.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
#include <xcb/xproto.h>
#include "core/types.h"
modifier_mask_t modifiers_xcb_to_zdwm(uint16_t mask);
uint16_t modifiers_zdwm_to_xcb(modifier_mask_t mask);
button_t button_xcb_to_zdwm(xcb_button_t button);
xcb_button_t button_zdwm_to_xcb(button_t button);

View File

@@ -9,8 +9,8 @@
#include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h>
#include "backend/x11/common.h"
#include "backend/x11/window.h"
#include "base/macros.h"
#include "base/memory.h"
#include "core/backend.h"
#include "core/types.h"
@@ -233,29 +233,6 @@ static bool handle_configure_request(
return true;
}
static modifier_mask_t get_modifiers(uint16_t mask) {
typedef struct modifier_map_t {
modifier_bit_t modifier;
xcb_mod_mask_t mask;
} modifier_map_t;
static constexpr modifier_map_t modifier_map[] = {
{ZDWM_MOD_SHIFT, XCB_MOD_MASK_SHIFT},
{ZDWM_MOD_CONTROL, XCB_MOD_MASK_CONTROL},
{ZDWM_MOD_1, XCB_MOD_MASK_1},
{ZDWM_MOD_2, XCB_MOD_MASK_2},
{ZDWM_MOD_3, XCB_MOD_MASK_3},
{ZDWM_MOD_4, XCB_MOD_MASK_4},
{ZDWM_MOD_5, XCB_MOD_MASK_5},
};
auto mods = ZDWM_MOD_NONE;
for (size_t i = 0; i < countof(modifier_map); ++i) {
auto item = &modifier_map[i];
if (mask & item->mask) mods |= item->modifier;
}
return mods;
}
static bool handle_key_press(
backend_t *backend,
event_t *event,
@@ -269,39 +246,18 @@ static bool handle_key_press(
event->type = ZDWM_EVENT_KEY_PRESS;
event->as.key_press.keycode = keycode;
event->as.key_press.keysym = keysym;
event->as.key_press.modifiers = get_modifiers(xcb_event->state);
event->as.key_press.modifiers = modifiers_xcb_to_zdwm(xcb_event->state);
return true;
}
static bool get_button(xcb_button_t button_detail, uint32_t *button) {
assert(button != nullptr);
switch (button_detail) {
case XCB_BUTTON_INDEX_1:
*button = ZDWM_BUTTON_LEFT;
return true;
case XCB_BUTTON_INDEX_2:
*button = ZDWM_BUTTON_RIGHT;
return true;
case XCB_BUTTON_INDEX_3:
*button = ZDWM_BUTTON_MIDDLE;
return true;
case XCB_BUTTON_INDEX_4:
case XCB_BUTTON_INDEX_5:
/* TODO: 后续可以考虑加上鼠标滚轮事件 */
default:
return false;
}
}
static bool fill_button_event(
const xcb_button_press_event_t *xcb_event,
pointer_button_event_t *data
) {
uint32_t button = 0;
if (!get_button(xcb_event->detail, &button)) return false;
auto button = button_xcb_to_zdwm(xcb_event->detail);
data->modifiers = get_modifiers(xcb_event->state);
data->modifiers = modifiers_xcb_to_zdwm(xcb_event->state);
data->button = button;
data->window = xcb_event->event;
data->root = (point_t){.x = xcb_event->root_x, .y = xcb_event->root_y};

View File

@@ -8,6 +8,7 @@
#include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h>
#include "backend/x11/common.h"
#include "backend/x11/cursor.h"
#include "base/macros.h"
#include "base/memory.h"
@@ -463,3 +464,26 @@ void window_change_cursor(
xcb_params_cw_t params = {.cursor = cursor_get_xcb_cursor(backend, cursor)};
xcb_aux_change_window_attributes(conn, window, XCB_CW_CURSOR, &params);
}
void window_grab_button(
backend_t *backend,
xcb_window_t window,
button_t button,
modifier_mask_t modifiers
) {
auto xcb_button = button_zdwm_to_xcb(button);
if (xcb_button == XCB_BUTTON_INDEX_ANY) return;
xcb_grab_button(
backend->conn,
false, /* 事件仅发送给抓取者,不发送给其他窗口 */
window,
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
XCB_GRAB_MODE_ASYNC,
XCB_GRAB_MODE_SYNC,
XCB_WINDOW_NONE,
XCB_CURSOR_NONE,
xcb_button,
modifiers_zdwm_to_xcb(modifiers)
);
}

View File

@@ -124,3 +124,10 @@ void window_change_cursor(
xcb_window_t window,
cursor_t cursor
);
void window_grab_button(
backend_t *backend,
xcb_window_t window,
button_t button,
modifier_mask_t modifiers
);

View File

@@ -27,12 +27,6 @@ typedef struct key_press_event_t {
uint32_t keycode;
} key_press_event_t;
typedef enum button_t {
ZDWM_BUTTON_LEFT,
ZDWM_BUTTON_RIGHT,
ZDWM_BUTTON_MIDDLE,
} button_t;
typedef struct pointer_button_event_t {
modifier_mask_t modifiers;
button_t button;

View File

@@ -20,6 +20,8 @@ static void free_memory_hold_by_effects(effect_t *effects, size_t count) {
case ZDWM_EFFECT_BIND_KEY:
p_delete(&effect->as.bind_key.keys);
break;
case ZDWM_EFFECT_GRAB_BUTTON:
p_delete(&effect->as.grab_button.buttons);
default:
}
}

View File

@@ -20,6 +20,7 @@ typedef enum effect_type_t {
ZDWM_EFFECT_CHANGE_WINDOW_LIST,
ZDWM_EFFECT_RESTACK_WINDOWS,
ZDWM_EFFECT_BIND_KEY,
ZDWM_EFFECT_GRAB_BUTTON,
} effect_type_t;
typedef struct effect_move_window_t {
@@ -48,6 +49,12 @@ typedef struct effect_bind_key_t {
size_t count;
} effect_bind_key_t;
typedef struct effect_grab_button_t {
window_id_t window;
const grab_button_t *buttons;
size_t count;
} effect_grab_button_t;
typedef struct effect_bool_window_t {
window_id_t window;
bool value;
@@ -69,6 +76,7 @@ typedef struct effect_t {
effect_window_list_t change_window_list;
effect_window_list_t restack_windows;
effect_bind_key_t bind_key;
effect_grab_button_t grab_button;
} as;
} effect_t;

View File

@@ -762,6 +762,24 @@ static void push_window_list_effect(const state_t *state, plan_t *plan) {
plan_push_effect(plan, &effect);
}
static void plan_push_grab_button_effect(plan_t *plan, window_id_t window) {
/* TODO: 后续会改成配置 */
static const grab_button_t buttons[] = {
[0] = {.modifiers = ZDWM_MOD_4, .button = ZDWM_BUTTON_LEFT},
[1] = {.modifiers = ZDWM_MOD_4, .button = ZDWM_BUTTON_RIGHT},
};
constexpr const size_t count = countof(buttons);
auto btns = p_new(grab_button_t, count);
memcpy(btns, buttons, sizeof(buttons));
effect_t grab_button_effect = {
.type = ZDWM_EFFECT_GRAB_BUTTON,
.as.grab_button = {.window = window, .buttons = btns, .count = count},
};
plan_push_effect(plan, &grab_button_effect);
}
static void manage_window(
const policy_context_t *ctx,
const manage_window_command_t *command,
@@ -774,6 +792,7 @@ static void manage_window(
state_window_set_workspace(state, window_id, workspace_id);
state_window_set_floating(state, window_id, command->floating);
set_foucs_window(ctx, workspace_id, window_id, plan);
plan_push_grab_button_effect(plan, window_id);
push_window_list_effect(state, plan);
listeners_notify_window_added(ctx->listeners, state, window_id);

View File

@@ -55,6 +55,18 @@ typedef struct key_bind_t {
keysym_t keysym;
} key_bind_t;
typedef enum button_t {
ZDWM_BUTTON_NONE,
ZDWM_BUTTON_LEFT,
ZDWM_BUTTON_RIGHT,
ZDWM_BUTTON_MIDDLE,
} button_t;
typedef struct grab_button_t {
modifier_mask_t modifiers;
button_t button;
} grab_button_t;
typedef enum window_state_request_type_t {
ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN,
ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED,