```我的 I3-Emacs 集成```
My I3-Emacs Integration

原始链接: https://khz.ac/software/i3-integration.html

i3 窗口管理器通过在根窗口上使用 `xcb_grab_key` 并将 `owner_events` 设置为 0,从而实现全局拦截键盘快捷键。这种方式实际上是“窃取”了其他应用程序的按键事件。当按下按键时,i3 的 `handle_key_press` 函数会捕获该事件,识别对应的绑定,并执行相关命令。 尽管作者指出 i3 的源代码可读性强且结构良好,但在事件透传方面仍存在明显的局限性。尝试通过 `xcb_send_event` 重新发送被拦截的事件在技术上是可行的,但由于最初的全局捕获仍会中断事件流,这并不能阻止目标窗口失去焦点。作者建议将此机制作为在 i3 中实现真正事件透传的切入点,供有兴趣的开发者参考。

Sorry.
相关文章

原文

i3 uses xcb_grab_key() with owner_events = 0 on the root x window to intercept keys. the relevant code in src/bindings.c looks like all unpatched code snippets refer to i3 4.25.1, if you want to follow along.

172struct Binding_Keycode *binding_keycode;
173TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
174    const int keycode = binding_keycode->keycode;
175    const int mods = (binding_keycode->modifiers & 0xFFFF);
176    DLOG("Binding %p Grabbing keycode %d with mods %d\n", bind, keycode, mods);
177    xcb_grab_key(conn, 0, root, mods, keycode, XCB_GRAB_MODE_ASYNC,
178                 XCB_GRAB_MODE_ASYNC);
179}

this code isn't super relevant, except that i3 entirely steals its bindings from anyone else by intercepting on the root window. if you're thinking that setting owner_events = 1 to allow event passthrough so we don't have to re-emit… that would be great, but that appears to instruct x to pass the event through to only the root window. which is not what we want.

in i3's handle_event() in src/handlers.c, if it gets an xcb event, it sends it off to a specialized handler based on its type:

1481switch (type) {
1482case XCB_KEY_PRESS:
1483case XCB_KEY_RELEASE:
1484    handle_key_press((xcb_key_press_event_t *)event);
1485    break;
1486    }

handle_key_press() (src/key_press.c) looks like this — it receives a keypress event, looks up a binding based on that event, and, if it finds one, runs the associated command: yes, i do know one of the lines is too long. i opted to leave it that way, as that's how it is in the i3 source. i should note, though: i3 has really nice source code! i found it very readable and pleasant to work inside.

12
18void handle_key_press(xcb_key_press_event_t *event) {
19    const bool key_release = (event->response_type == XCB_KEY_RELEASE);
20
21    last_timestamp = event->time;
22
23    DLOG("%s %d, state raw = 0x%x\n", (key_release ? "KeyRelease" : "KeyPress"), event->detail, event->state);
24
25    Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
26
27    
28    if (bind == NULL) {
29        return;
30    }
31
32    CommandResult *result = run_binding(bind, NULL);
33    command_result_free(result);
34}

notably, this function receives the original xcb_key_press_event_t from xcb, which (after a bit of reading and experimentation) i realized you could just re-emit diretly via xcb_send_event(). unfortunately, the window receiving the event will still lose focus, as i3 is intercepting key events globally. i haven't fixed this; let me know if you know how.

this looks like a reasonable place to make a change!

联系我们 contact @ memedata.com