Another Emacs i3 Integration
2021-10-15 #emacs #i3wm
Pavel Korytov wrote an inspiring blog post to get a consistent set of keybindings between i3 and Emacs. It caught my attention because I use exactly these two tools heavily. Before the integration, I have to define a different keybinding for switching window inside Emacs and i3. After the integration, I can use the same keybinding everywhere. Check out the video in the original blog post to see how cool it is!
I followed the blog post and implemented it immediately. It worked and I loved that! However, the proposed (i3 -> Emacs -> i3) solution introduced a coupling between i3 and Emacs. i3 has to know Emacs, and Emacs has to know i3. It does not fit into the Law of Demeter. It makes the solution harder to scale to other tiling window managers or applications.
Hence, I implemented another (i3 -> Emacs) solution with less coupling (only the
focus command for now).
1: Add an Elisp function
(require 'windmove) (defun my/wm-integration (command) (pcase command ((rx bos "focus") (windmove-do-window-select (intern (elt (split-string command) 1)))) (- (error command))))
my/wm-integration handles the command from the window manager.
Emacs does not need to know what the window manager is, and do not need to call
i3-msg. It return a non-zero code if the command is not handled, or a zero code if handled.
nit. Emacs still use the protocol defined by i3, like the
focus command. Well… we have to use a protocol anyway. Let’s pick the i3 protocol in this post.
2: Create a Script
#!/bin/sh # # Proxy the `i3-msg` command to the focused window. # Proxy to Emacs if it is the active window if [[ "$(xdotool getactivewindow getwindowclassname)" == "Emacs" ]]; then command="(my/wm-integration \"$@\")" if emacsclient -e "$command"; then exit fi fi # fallback to i3 i3-msg $@
This script proxies the
i3-msg command to the focused window. If the focused window has handled it (return zero), then exit. Otherwise (return non-zero), fallback to i3.
This mechanism (using the return code) can work with other applications, like Tmux.
3: Update the i3 config
bindsym $mod+Left exec i3-msg-proxy focus left bindsym $mod+Down exec i3-msg-proxy focus down bindsym $mod+Up exec i3-msg-proxy focus up bindsym $mod+Right exec i3-msg-proxy focus right
Finally, update the i3 config to proxy the command to the focused window. Now, I can move my focus between i3 and Emacs!
The key difference between this and the original solution is how Emacs calls i3 back when it failed to handle the command. The original solution calls
i3-msg inside Emacs. This solution returns non-zero from Emacs.