From ad22b0fe93e1c5010d73b31c5c3e7e1438d4094d Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Sun, 22 Mar 2026 02:12:31 +0800 Subject: [PATCH] =?UTF-8?q?core:=20=E6=B7=BB=E5=8A=A0=20pointer=20enter=20?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/min_core_draft/README.org | 14 ++++--- docs/min_core_draft/WM_EVENT_ROUTING.org | 52 ++++++++++++++++++++++-- docs/min_core_draft/wm_event.h | 12 ++++++ docs/min_core_draft/wm_policy.h | 1 + docs/min_core_draft/wm_policy_config.h | 1 + src/core/event.h | 8 ++++ 6 files changed, 80 insertions(+), 8 deletions(-) diff --git a/docs/min_core_draft/README.org b/docs/min_core_draft/README.org index 1b76eec..991bdb8 100644 --- a/docs/min_core_draft/README.org +++ b/docs/min_core_draft/README.org @@ -31,7 +31,8 @@ request event,而不是直接改 core 状态。 4. =policy= 负责把事件路由为命令,并把命令应用到状态。 =WM_EVENT_KEY_PRESS= / =WM_EVENT_POINTER_BUTTON_PRESS= 的路由都使用 bootstrap -固定下来的输入绑定表。 +固定下来的输入绑定表;像 =WM_EVENT_POINTER_ENTER= 这种不经绑定的输入事实, +则由 policy 配置决定是否转换为 =FOCUS_WINDOW=。 5. =layout= 负责为某个输出上当前显示的工作区中的平铺窗口计算外框几何。 @@ -224,16 +225,19 @@ end 1. =focus_raises= 焦点切换时是否隐式执行 raise。 -2. =sticky_windows_participate_in_direction_focus= +2. =pointer_enter_focuses_window= +收到 =WM_EVENT_POINTER_ENTER= 时是否切换焦点(focus-follows-mouse)。 + +3. =sticky_windows_participate_in_direction_focus= sticky 窗口是否参与方向焦点搜索。 -3. =manage_sets_focus= +4. =manage_sets_focus= 新受管窗口是否默认夺取所属 workspace 的焦点。 -4. =switch_workspace_restores_last_focus= +5. =switch_workspace_restores_last_focus= 切换工作区时是否恢复该 workspace 的最近焦点。 -5. =minimize_clears_focus= +6. =minimize_clears_focus= 最小化当前焦点窗口时是否清空或重选该 workspace 焦点。 * 边框约定 diff --git a/docs/min_core_draft/WM_EVENT_ROUTING.org b/docs/min_core_draft/WM_EVENT_ROUTING.org index 68cbe21..0fca1b1 100644 --- a/docs/min_core_draft/WM_EVENT_ROUTING.org +++ b/docs/min_core_draft/WM_EVENT_ROUTING.org @@ -26,6 +26,7 @@ void wm_runtime_process_event(wm_runtime_t *runtime, const wm_event_t *event) { if (!consumed) { wm_policy_route_event( &runtime->state, + &runtime->policy, &runtime->keybindings, &runtime->pointer_bindings, event, @@ -320,6 +321,7 @@ typedef struct wm_runtime_bootstrap_t { #+BEGIN_SRC c bool wm_policy_route_event(const wm_state_t *state, + const wm_policy_config_t *policy, const wm_key_binding_table_t *keybindings, const wm_pointer_binding_table_t *pointer_bindings, const wm_event_t *event, @@ -333,6 +335,10 @@ bool wm_policy_route_event(const wm_state_t *state, return route_pointer_press(state, pointer_bindings, &event->data.pointer_button_press, out); + case WM_EVENT_POINTER_ENTER: + return route_pointer_enter(state, policy, + &event->data.pointer_enter, out); + case WM_EVENT_WINDOW_MAP_REQUEST: return route_map_request(state, &event->data.window_map_request, out); @@ -514,6 +520,43 @@ static bool route_pointer_press(const wm_state_t *state, return wm_command_buffer_push(out, cmd); } +static bool route_pointer_enter(const wm_state_t *state, + const wm_policy_config_t *policy, + const wm_pointer_enter_event_t *e, + wm_command_buffer_t *out) { + const wm_window_t *win; + const wm_workspace_t *ws; + + if (!policy->pointer_enter_focuses_window) { + return false; + } + + if (e->window == WM_WINDOW_ID_INVALID) { + return false; + } + + win = wm_state_window_get(state, e->window); + if (!win) { + return false; + } + + if (win->workspace_id >= state->workspace_count) { + return false; + } + ws = &state->workspaces[win->workspace_id]; + + if (ws->focused_window_id == win->id) { + return false; + } + + return wm_command_buffer_push(out, (wm_command_t){ + .type = WM_COMMAND_FOCUS_WINDOW, + .as.focus_window = { + .window_id = win->id, + }, + }); +} + // 窗口映射请求路由 static bool route_map_request(const wm_state_t *state, const wm_window_map_request_event_t *e, @@ -757,6 +800,7 @@ static bool route_move_floating_event(wm_runtime_t *runtime, |---------|---------------|------| | KEY_PRESS | 任意命令(通过键盘绑定) | 绑定存在 | | POINTER_BUTTON_PRESS | 任意窗口命令 / BEGIN_*_INTERACTION(通过鼠标绑定) | 绑定存在 | +| POINTER_ENTER | FOCUS_WINDOW | policy.pointer_enter_focuses_window == true 且窗口已管理 | | WINDOW_MAP_REQUEST | MANAGE_WINDOW | 窗口未管理 | | WINDOW_REMOVE | UNMANAGE_WINDOW | 窗口已管理 | | WINDOW_ACTIVATE_REQUEST | SWITCH_WORKSPACE + FOCUS_WINDOW / SET_WINDOW_URGENT | source == pager / source != pager | @@ -768,9 +812,11 @@ static bool route_move_floating_event(wm_runtime_t *runtime, 1. `WM_EVENT_CONFIGURE_REQUEST` 只携带稀疏几何字段,不直接镜像 X11 全量协议载荷。 2. 最小核心当前只会把这些几何请求映射到 floating 窗口的 move/resize 语义。 -3. `border_width` / `sibling` / `stack_mode` 这类协议细节留在 backend / adapter 层。 -4. fullscreen / maximize / minimize 这类窗口模式切换不属于 configure request。 -5. `WM_EVENT_WINDOW_STATE_REQUEST` 表达后端看到的原始 add/remove/toggle 请求; +3. `WM_EVENT_POINTER_ENTER` 是“pointer focus 进入窗口”的输入事实,不经过 pointer binding 表。 +4. 类似 `raise_or_run` 这类程序化恢复鼠标位置导致的单次 crossing 抑制,应由 runtime 维护瞬时状态后在路由前过滤,而不是下沉到 backend 业务逻辑。 +5. `border_width` / `sibling` / `stack_mode` 这类协议细节留在 backend / adapter 层。 +6. fullscreen / maximize / minimize 这类窗口模式切换不属于 configure request。 +7. `WM_EVENT_WINDOW_STATE_REQUEST` 表达后端看到的原始 add/remove/toggle 请求; core command 仍保持 =SET_*= 这种最终状态语义。 * 事件处理的边界条件 diff --git a/docs/min_core_draft/wm_event.h b/docs/min_core_draft/wm_event.h index 737c42c..fc58c8b 100644 --- a/docs/min_core_draft/wm_event.h +++ b/docs/min_core_draft/wm_event.h @@ -16,6 +16,8 @@ typedef enum wm_event_type_t { WM_EVENT_POINTER_BUTTON_PRESS, WM_EVENT_POINTER_BUTTON_RELEASE, WM_EVENT_POINTER_MOTION, + // 指针焦点进入某个窗口;对应 X11 EnterNotify / Wayland wl_pointer.enter + WM_EVENT_POINTER_ENTER, WM_EVENT_WINDOW_MAP_REQUEST, WM_EVENT_WINDOW_REMOVE, WM_EVENT_WINDOW_METADATA_CHANGED, @@ -64,6 +66,15 @@ typedef struct wm_pointer_motion_event_t { wm_window_id_t window; } wm_pointer_motion_event_t; +typedef struct wm_pointer_enter_event_t { + // 全局桌面坐标 + wm_point_t root; + // 相对 window 的坐标;若 window == WM_WINDOW_ID_INVALID,则该值未定义 + wm_point_t local; + // 当前进入并获得 pointer focus 的窗口;若未命中窗口,则为 WM_WINDOW_ID_INVALID + wm_window_id_t window; +} wm_pointer_enter_event_t; + typedef struct wm_window_map_request_event_t { wm_window_info_t info; // 仅用于 manage 路由判断,不进入持久 state @@ -204,6 +215,7 @@ typedef struct wm_event_t { wm_pointer_button_event_t pointer_button_press; wm_pointer_button_event_t pointer_button_release; wm_pointer_motion_event_t pointer_motion; + wm_pointer_enter_event_t pointer_enter; wm_window_map_request_event_t window_map_request; wm_window_remove_event_t window_remove; wm_window_metadata_changed_event_t window_metadata_changed; diff --git a/docs/min_core_draft/wm_policy.h b/docs/min_core_draft/wm_policy.h index bcecd18..38468bf 100644 --- a/docs/min_core_draft/wm_policy.h +++ b/docs/min_core_draft/wm_policy.h @@ -21,6 +21,7 @@ void wm_command_buffer_shutdown(wm_command_buffer_t *buffer); bool wm_command_buffer_push(wm_command_buffer_t *buffer, wm_command_t command); bool wm_policy_route_event(const wm_state_t *state, + const wm_policy_config_t *policy, const wm_key_binding_table_t *keybindings, const wm_pointer_binding_table_t *pointer_bindings, const wm_event_t *event, diff --git a/docs/min_core_draft/wm_policy_config.h b/docs/min_core_draft/wm_policy_config.h index 632746c..140d776 100644 --- a/docs/min_core_draft/wm_policy_config.h +++ b/docs/min_core_draft/wm_policy_config.h @@ -4,6 +4,7 @@ typedef struct wm_policy_config_t { bool focus_raises; + bool pointer_enter_focuses_window; bool sticky_windows_participate_in_direction_focus; bool manage_sets_focus; bool switch_workspace_restores_last_focus; diff --git a/src/core/event.h b/src/core/event.h index cc2c508..2d522ed 100644 --- a/src/core/event.h +++ b/src/core/event.h @@ -9,6 +9,7 @@ typedef enum wm_event_type_t { WM_EVENT_POINTER_BUTTON_PRESS, WM_EVENT_POINTER_BUTTON_RELEASE, WM_EVENT_POINTER_MOTION, + WM_EVENT_POINTER_ENTER, WM_EVENT_WINDOW_MAP_REQUEST, WM_EVENT_WINDOW_REMOVE, WM_EVENT_WINDOW_METADATA_CHANGED, @@ -54,6 +55,12 @@ typedef struct wm_pointer_motion_event_t { wm_point_t local; } wm_pointer_motion_event_t; +typedef struct wm_pointer_enter_event_t { + wm_window_id_t window; + wm_point_t root; + wm_point_t local; +} wm_pointer_enter_event_t; + typedef struct wm_window_map_request_event_t { wm_window_info_t info; wm_window_id_t transient_for; @@ -155,6 +162,7 @@ typedef struct wm_event_t { wm_pointer_button_event_t pointer_button_press; wm_pointer_button_event_t pointer_button_release; wm_pointer_motion_event_t pointer_motion; + wm_pointer_enter_event_t pointer_enter; wm_window_map_request_event_t window_map_request; wm_window_remove_event_t window_remove; wm_window_metadata_change_event_t window_metadata_change;