Compare commits
3 Commits
5d89caff0a
...
ad22b0fe93
| Author | SHA1 | Date | |
|---|---|---|---|
|
ad22b0fe93
|
|||
|
f573575ed6
|
|||
|
86a89452a7
|
@@ -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 焦点。
|
||||
|
||||
* 边框约定
|
||||
|
||||
@@ -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_*= 这种最终状态语义。
|
||||
|
||||
* 事件处理的边界条件
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
10
src/action.c
10
src/action.c
@@ -8,6 +8,7 @@
|
||||
#include "monitor.h"
|
||||
#include "types.h"
|
||||
#include "wm.h"
|
||||
#include "xcursor.h"
|
||||
|
||||
void focus_client_in_same_tag(const user_action_arg_t *arg) {
|
||||
bool next = arg->b;
|
||||
@@ -72,9 +73,16 @@ void quit(const user_action_arg_t *arg) {
|
||||
void raise_or_run(const user_action_arg_t *arg) {
|
||||
const char *class = ((const char **)arg->ptr)[0];
|
||||
client_t *client = client_get_next_by_class(wm.client_focused, class);
|
||||
if (client && client == wm.client_focused) return;
|
||||
|
||||
if (client) {
|
||||
bool monitor_changed = client->monitor != wm.current_monitor;
|
||||
bool tag_changed = client->tags != client->monitor->selected_tag->mask;
|
||||
if (monitor_changed || tag_changed) {
|
||||
point_t point = monitor_changed ? monitor_get_restore_cursor_point(
|
||||
client->monitor)
|
||||
: xcursor_query_pointer_position();
|
||||
wm_ignore_enter_notify_at_point(point);
|
||||
}
|
||||
wm_set_current_monitor(client->monitor, true);
|
||||
monitor_select_tag(client->monitor, client->tags);
|
||||
client_stack_raise(client);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -313,6 +313,8 @@ static void expose(xcb_expose_event_t *ev) {
|
||||
}
|
||||
|
||||
static void enter_notify(xcb_enter_notify_event_t *ev) {
|
||||
if (wm_should_ignore_enter_notify(ev)) return;
|
||||
|
||||
client_t *client = client_get_by_window(ev->event);
|
||||
if (!client || wm.client_focused == client) return;
|
||||
|
||||
|
||||
@@ -437,7 +437,7 @@ void monitor_save_cursor_point(monitor_t *monitor) {
|
||||
monitor->position_inited = true;
|
||||
}
|
||||
|
||||
void monitor_restore_cursor_point(monitor_t *monitor) {
|
||||
point_t monitor_get_restore_cursor_point(monitor_t *monitor) {
|
||||
if (!monitor->position_inited) {
|
||||
point_t point = xcursor_query_pointer_position();
|
||||
monitor_t *m = wm_get_monitor_by_point(point);
|
||||
@@ -446,5 +446,9 @@ void monitor_restore_cursor_point(monitor_t *monitor) {
|
||||
monitor->position_inited = true;
|
||||
}
|
||||
|
||||
xcursor_set_pointer_position(monitor->cursor_position);
|
||||
return monitor->cursor_position;
|
||||
}
|
||||
|
||||
void monitor_restore_cursor_point(monitor_t *monitor) {
|
||||
xcursor_set_pointer_position(monitor_get_restore_cursor_point(monitor));
|
||||
}
|
||||
|
||||
@@ -13,4 +13,5 @@ void monitor_init_bar(monitor_t *monitor);
|
||||
void monitor_draw_bar(monitor_t *monitor);
|
||||
void monitor_arrange(monitor_t *monitor);
|
||||
void monitor_save_cursor_point(monitor_t *monitor);
|
||||
point_t monitor_get_restore_cursor_point(monitor_t *monitor);
|
||||
void monitor_restore_cursor_point(monitor_t *monitor);
|
||||
|
||||
40
src/wm.c
40
src/wm.c
@@ -57,6 +57,7 @@ static void wm_update_status(status_t *status);
|
||||
static void wm_run_autostart(const char *const commands[]);
|
||||
static void run_once(const char *command);
|
||||
static bool command_already_running(const char *command);
|
||||
static gboolean clear_ignore_enter_notify(gpointer data);
|
||||
|
||||
wm_t wm;
|
||||
|
||||
@@ -81,6 +82,39 @@ static void signal_fatal(int signal_number) {
|
||||
|
||||
static guint sources[3] = {0};
|
||||
|
||||
static gboolean clear_ignore_enter_notify(gpointer data) {
|
||||
wm.ignore_enter_notify = false;
|
||||
wm.ignored_enter_notify_point = (point_t){0};
|
||||
wm.clear_ignore_enter_notify_source = 0;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void wm_ignore_enter_notify_at_point(point_t point) {
|
||||
wm.ignore_enter_notify = true;
|
||||
wm.ignored_enter_notify_point = point;
|
||||
if (wm.clear_ignore_enter_notify_source) {
|
||||
g_source_remove(wm.clear_ignore_enter_notify_source);
|
||||
}
|
||||
wm.clear_ignore_enter_notify_source =
|
||||
g_idle_add(clear_ignore_enter_notify, nullptr);
|
||||
}
|
||||
|
||||
bool wm_should_ignore_enter_notify(const xcb_enter_notify_event_t *ev) {
|
||||
if (!wm.ignore_enter_notify) return false;
|
||||
if (ev->root_x != wm.ignored_enter_notify_point.x ||
|
||||
ev->root_y != wm.ignored_enter_notify_point.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wm.clear_ignore_enter_notify_source) {
|
||||
g_source_remove(wm.clear_ignore_enter_notify_source);
|
||||
wm.clear_ignore_enter_notify_source = 0;
|
||||
}
|
||||
wm.ignore_enter_notify = false;
|
||||
wm.ignored_enter_notify_point = (point_t){0};
|
||||
return true;
|
||||
}
|
||||
|
||||
void wm_setup_signal(void) {
|
||||
sources[0] = g_unix_signal_add(SIGINT, exit_on_signal, nullptr);
|
||||
sources[1] = g_unix_signal_add(SIGTERM, exit_on_signal, nullptr);
|
||||
@@ -405,6 +439,12 @@ void wm_clean(void) {
|
||||
guint source_id = sources[i];
|
||||
if (source_id) g_source_remove(source_id);
|
||||
}
|
||||
if (wm.clear_ignore_enter_notify_source) {
|
||||
g_source_remove(wm.clear_ignore_enter_notify_source);
|
||||
wm.clear_ignore_enter_notify_source = 0;
|
||||
}
|
||||
wm.ignore_enter_notify = false;
|
||||
wm.ignored_enter_notify_point = (point_t){0};
|
||||
|
||||
xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_ACTIVE_WINDOW);
|
||||
xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_SUPPORTING_WM_CHECK);
|
||||
|
||||
5
src/wm.h
5
src/wm.h
@@ -28,6 +28,9 @@ typedef struct wm_t {
|
||||
client_t *client_focused;
|
||||
monitor_t *monitor_list;
|
||||
monitor_t *current_monitor;
|
||||
bool ignore_enter_notify;
|
||||
point_t ignored_enter_notify_point;
|
||||
guint clear_ignore_enter_notify_source;
|
||||
const layout_t *layout_list;
|
||||
|
||||
char *font_family;
|
||||
@@ -63,6 +66,8 @@ extern wm_t wm;
|
||||
void wm_restart(void);
|
||||
void wm_quit(void);
|
||||
void wm_restack_clients(void);
|
||||
void wm_ignore_enter_notify_at_point(point_t point);
|
||||
bool wm_should_ignore_enter_notify(const xcb_enter_notify_event_t *ev);
|
||||
void wm_set_current_monitor(monitor_t *monitor, bool restore_cursor);
|
||||
monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_area(area_t area);
|
||||
monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_point(
|
||||
|
||||
Reference in New Issue
Block a user