Another Emacs i3 Integration
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 my/wm-integration
(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 i3-msg-proxy
#!/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!
Conclusion
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.