Compare commits

...

3 Commits

12 changed files with 143 additions and 11 deletions

View File

@@ -31,7 +31,8 @@ request event而不是直接改 core 状态。
4. =policy= 4. =policy=
负责把事件路由为命令,并把命令应用到状态。 负责把事件路由为命令,并把命令应用到状态。
=WM_EVENT_KEY_PRESS= / =WM_EVENT_POINTER_BUTTON_PRESS= 的路由都使用 bootstrap =WM_EVENT_KEY_PRESS= / =WM_EVENT_POINTER_BUTTON_PRESS= 的路由都使用 bootstrap
固定下来的输入绑定表 固定下来的输入绑定表;像 =WM_EVENT_POINTER_ENTER= 这种不经绑定的输入事实,
则由 policy 配置决定是否转换为 =FOCUS_WINDOW=。
5. =layout= 5. =layout=
负责为某个输出上当前显示的工作区中的平铺窗口计算外框几何。 负责为某个输出上当前显示的工作区中的平铺窗口计算外框几何。
@@ -224,16 +225,19 @@ end
1. =focus_raises= 1. =focus_raises=
焦点切换时是否隐式执行 raise。 焦点切换时是否隐式执行 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 窗口是否参与方向焦点搜索。 sticky 窗口是否参与方向焦点搜索。
3. =manage_sets_focus= 4. =manage_sets_focus=
新受管窗口是否默认夺取所属 workspace 的焦点。 新受管窗口是否默认夺取所属 workspace 的焦点。
4. =switch_workspace_restores_last_focus= 5. =switch_workspace_restores_last_focus=
切换工作区时是否恢复该 workspace 的最近焦点。 切换工作区时是否恢复该 workspace 的最近焦点。
5. =minimize_clears_focus= 6. =minimize_clears_focus=
最小化当前焦点窗口时是否清空或重选该 workspace 焦点。 最小化当前焦点窗口时是否清空或重选该 workspace 焦点。
* 边框约定 * 边框约定

View File

@@ -26,6 +26,7 @@ void wm_runtime_process_event(wm_runtime_t *runtime, const wm_event_t *event) {
if (!consumed) { if (!consumed) {
wm_policy_route_event( wm_policy_route_event(
&runtime->state, &runtime->state,
&runtime->policy,
&runtime->keybindings, &runtime->keybindings,
&runtime->pointer_bindings, &runtime->pointer_bindings,
event, event,
@@ -320,6 +321,7 @@ typedef struct wm_runtime_bootstrap_t {
#+BEGIN_SRC c #+BEGIN_SRC c
bool wm_policy_route_event(const wm_state_t *state, 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_key_binding_table_t *keybindings,
const wm_pointer_binding_table_t *pointer_bindings, const wm_pointer_binding_table_t *pointer_bindings,
const wm_event_t *event, 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, return route_pointer_press(state, pointer_bindings,
&event->data.pointer_button_press, out); &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: case WM_EVENT_WINDOW_MAP_REQUEST:
return route_map_request(state, &event->data.window_map_request, out); 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); 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, static bool route_map_request(const wm_state_t *state,
const wm_window_map_request_event_t *e, const wm_window_map_request_event_t *e,
@@ -757,6 +800,7 @@ static bool route_move_floating_event(wm_runtime_t *runtime,
|---------|---------------|------| |---------|---------------|------|
| KEY_PRESS | 任意命令(通过键盘绑定) | 绑定存在 | | KEY_PRESS | 任意命令(通过键盘绑定) | 绑定存在 |
| POINTER_BUTTON_PRESS | 任意窗口命令 / BEGIN_*_INTERACTION通过鼠标绑定 | 绑定存在 | | POINTER_BUTTON_PRESS | 任意窗口命令 / BEGIN_*_INTERACTION通过鼠标绑定 | 绑定存在 |
| POINTER_ENTER | FOCUS_WINDOW | policy.pointer_enter_focuses_window == true 且窗口已管理 |
| WINDOW_MAP_REQUEST | MANAGE_WINDOW | 窗口未管理 | | WINDOW_MAP_REQUEST | MANAGE_WINDOW | 窗口未管理 |
| WINDOW_REMOVE | UNMANAGE_WINDOW | 窗口已管理 | | WINDOW_REMOVE | UNMANAGE_WINDOW | 窗口已管理 |
| WINDOW_ACTIVATE_REQUEST | SWITCH_WORKSPACE + FOCUS_WINDOW / SET_WINDOW_URGENT | source == pager / source != pager | | 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 全量协议载荷。 1. `WM_EVENT_CONFIGURE_REQUEST` 只携带稀疏几何字段,不直接镜像 X11 全量协议载荷。
2. 最小核心当前只会把这些几何请求映射到 floating 窗口的 move/resize 语义。 2. 最小核心当前只会把这些几何请求映射到 floating 窗口的 move/resize 语义。
3. `border_width` / `sibling` / `stack_mode` 这类协议细节留在 backend / adapter 层 3. `WM_EVENT_POINTER_ENTER` 是“pointer focus 进入窗口”的输入事实,不经过 pointer binding 表
4. fullscreen / maximize / minimize 这类窗口模式切换不属于 configure request 4. 类似 `raise_or_run` 这类程序化恢复鼠标位置导致的单次 crossing 抑制,应由 runtime 维护瞬时状态后在路由前过滤,而不是下沉到 backend 业务逻辑
5. `WM_EVENT_WINDOW_STATE_REQUEST` 表达后端看到的原始 add/remove/toggle 请求; 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_*= 这种最终状态语义。 core command 仍保持 =SET_*= 这种最终状态语义。
* 事件处理的边界条件 * 事件处理的边界条件

View File

@@ -16,6 +16,8 @@ typedef enum wm_event_type_t {
WM_EVENT_POINTER_BUTTON_PRESS, WM_EVENT_POINTER_BUTTON_PRESS,
WM_EVENT_POINTER_BUTTON_RELEASE, WM_EVENT_POINTER_BUTTON_RELEASE,
WM_EVENT_POINTER_MOTION, WM_EVENT_POINTER_MOTION,
// 指针焦点进入某个窗口;对应 X11 EnterNotify / Wayland wl_pointer.enter
WM_EVENT_POINTER_ENTER,
WM_EVENT_WINDOW_MAP_REQUEST, WM_EVENT_WINDOW_MAP_REQUEST,
WM_EVENT_WINDOW_REMOVE, WM_EVENT_WINDOW_REMOVE,
WM_EVENT_WINDOW_METADATA_CHANGED, WM_EVENT_WINDOW_METADATA_CHANGED,
@@ -64,6 +66,15 @@ typedef struct wm_pointer_motion_event_t {
wm_window_id_t window; wm_window_id_t window;
} wm_pointer_motion_event_t; } 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 { typedef struct wm_window_map_request_event_t {
wm_window_info_t info; wm_window_info_t info;
// 仅用于 manage 路由判断,不进入持久 state // 仅用于 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_press;
wm_pointer_button_event_t pointer_button_release; wm_pointer_button_event_t pointer_button_release;
wm_pointer_motion_event_t pointer_motion; 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_map_request_event_t window_map_request;
wm_window_remove_event_t window_remove; wm_window_remove_event_t window_remove;
wm_window_metadata_changed_event_t window_metadata_changed; wm_window_metadata_changed_event_t window_metadata_changed;

View File

@@ -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_command_buffer_push(wm_command_buffer_t *buffer, wm_command_t command);
bool wm_policy_route_event(const wm_state_t *state, 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_key_binding_table_t *keybindings,
const wm_pointer_binding_table_t *pointer_bindings, const wm_pointer_binding_table_t *pointer_bindings,
const wm_event_t *event, const wm_event_t *event,

View File

@@ -4,6 +4,7 @@
typedef struct wm_policy_config_t { typedef struct wm_policy_config_t {
bool focus_raises; bool focus_raises;
bool pointer_enter_focuses_window;
bool sticky_windows_participate_in_direction_focus; bool sticky_windows_participate_in_direction_focus;
bool manage_sets_focus; bool manage_sets_focus;
bool switch_workspace_restores_last_focus; bool switch_workspace_restores_last_focus;

View File

@@ -8,6 +8,7 @@
#include "monitor.h" #include "monitor.h"
#include "types.h" #include "types.h"
#include "wm.h" #include "wm.h"
#include "xcursor.h"
void focus_client_in_same_tag(const user_action_arg_t *arg) { void focus_client_in_same_tag(const user_action_arg_t *arg) {
bool next = arg->b; 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) { void raise_or_run(const user_action_arg_t *arg) {
const char *class = ((const char **)arg->ptr)[0]; const char *class = ((const char **)arg->ptr)[0];
client_t *client = client_get_next_by_class(wm.client_focused, class); client_t *client = client_get_next_by_class(wm.client_focused, class);
if (client && client == wm.client_focused) return;
if (client) { 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); wm_set_current_monitor(client->monitor, true);
monitor_select_tag(client->monitor, client->tags); monitor_select_tag(client->monitor, client->tags);
client_stack_raise(client); client_stack_raise(client);

View File

@@ -9,6 +9,7 @@ typedef enum wm_event_type_t {
WM_EVENT_POINTER_BUTTON_PRESS, WM_EVENT_POINTER_BUTTON_PRESS,
WM_EVENT_POINTER_BUTTON_RELEASE, WM_EVENT_POINTER_BUTTON_RELEASE,
WM_EVENT_POINTER_MOTION, WM_EVENT_POINTER_MOTION,
WM_EVENT_POINTER_ENTER,
WM_EVENT_WINDOW_MAP_REQUEST, WM_EVENT_WINDOW_MAP_REQUEST,
WM_EVENT_WINDOW_REMOVE, WM_EVENT_WINDOW_REMOVE,
WM_EVENT_WINDOW_METADATA_CHANGED, WM_EVENT_WINDOW_METADATA_CHANGED,
@@ -54,6 +55,12 @@ typedef struct wm_pointer_motion_event_t {
wm_point_t local; wm_point_t local;
} wm_pointer_motion_event_t; } 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 { typedef struct wm_window_map_request_event_t {
wm_window_info_t info; wm_window_info_t info;
wm_window_id_t transient_for; 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_press;
wm_pointer_button_event_t pointer_button_release; wm_pointer_button_event_t pointer_button_release;
wm_pointer_motion_event_t pointer_motion; 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_map_request_event_t window_map_request;
wm_window_remove_event_t window_remove; wm_window_remove_event_t window_remove;
wm_window_metadata_change_event_t window_metadata_change; wm_window_metadata_change_event_t window_metadata_change;

View File

@@ -313,6 +313,8 @@ static void expose(xcb_expose_event_t *ev) {
} }
static void enter_notify(xcb_enter_notify_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); client_t *client = client_get_by_window(ev->event);
if (!client || wm.client_focused == client) return; if (!client || wm.client_focused == client) return;

View File

@@ -437,7 +437,7 @@ void monitor_save_cursor_point(monitor_t *monitor) {
monitor->position_inited = true; 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) { if (!monitor->position_inited) {
point_t point = xcursor_query_pointer_position(); point_t point = xcursor_query_pointer_position();
monitor_t *m = wm_get_monitor_by_point(point); 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; 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));
} }

View File

@@ -13,4 +13,5 @@ void monitor_init_bar(monitor_t *monitor);
void monitor_draw_bar(monitor_t *monitor); void monitor_draw_bar(monitor_t *monitor);
void monitor_arrange(monitor_t *monitor); void monitor_arrange(monitor_t *monitor);
void monitor_save_cursor_point(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); void monitor_restore_cursor_point(monitor_t *monitor);

View File

@@ -57,6 +57,7 @@ static void wm_update_status(status_t *status);
static void wm_run_autostart(const char *const commands[]); static void wm_run_autostart(const char *const commands[]);
static void run_once(const char *command); static void run_once(const char *command);
static bool command_already_running(const char *command); static bool command_already_running(const char *command);
static gboolean clear_ignore_enter_notify(gpointer data);
wm_t wm; wm_t wm;
@@ -81,6 +82,39 @@ static void signal_fatal(int signal_number) {
static guint sources[3] = {0}; 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) { void wm_setup_signal(void) {
sources[0] = g_unix_signal_add(SIGINT, exit_on_signal, nullptr); sources[0] = g_unix_signal_add(SIGINT, exit_on_signal, nullptr);
sources[1] = g_unix_signal_add(SIGTERM, 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]; guint source_id = sources[i];
if (source_id) g_source_remove(source_id); 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_ACTIVE_WINDOW);
xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_SUPPORTING_WM_CHECK); xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_SUPPORTING_WM_CHECK);

View File

@@ -28,6 +28,9 @@ typedef struct wm_t {
client_t *client_focused; client_t *client_focused;
monitor_t *monitor_list; monitor_t *monitor_list;
monitor_t *current_monitor; 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; const layout_t *layout_list;
char *font_family; char *font_family;
@@ -63,6 +66,8 @@ extern wm_t wm;
void wm_restart(void); void wm_restart(void);
void wm_quit(void); void wm_quit(void);
void wm_restack_clients(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); 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_area(area_t area);
monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_point( monitor_t *__attribute__((returns_nonnull)) wm_get_monitor_by_point(