An Emacs Application Launcher for Regolith
I run the Regolith Desktop Environment on my laptop, which I love because it provides a convenient GNOME wrapper and interface for the i3 tiling window manager. Regolith relies on a program called `ilia` for application launching, and sometimes `ilia` gets caught in some kind of CPU-churning state that locks up my whole system. I have not been able to figure out what is causing it, so I (of course) turned to Emacs for a solution.
## Turning to `consult-omni`
Armin Darvish has created a powerful Emacs package called `consult-omni`, which provides a wrapper around `consult` for searching through any number of information sources. I believe `consult-omni` was originally intended to query web search engines and document databases, but Darvish has also provided a search mode for your local desktop applications, and can act as an application launcher.
Darvish provides an example application launcher in his `consult-omni` YouTube tutorial. The source code is straightforward, but I wanted to tweak it just a little. You can view his original on the project’s wiki on GitHub. You can watch him explain his technique below.
After a few tweaks, here is what I came up with.
(defun consult-launcher ()
"A launcher suitable for use from a window manager."
(interactive)
(let* ((width (floor (* 0.6 (display-pixel-width))))
(height (floor (* 0.6 (display-pixel-height))))
(left (floor (* 0.2 (display-pixel-width))))
(top (floor (* 0.2 (display-pixel-height))))
(params `((name . "omni-launcher")
(width . ,(cons 'text-pixels width))
(height . ,(cons 'text-pixels height))
(left . ,left)
(top . ,top)
(minibuffer . only)))
(frame (make-frame params)))
(with-selected-frame frame
(select-frame-set-input-focus frame)
;; If i3 is running and there is a control socket, let's tell
;; it we are a floating frame.
(if (getenv "I3SOCK")
(call-process "i3-msg" nil nil nil
(format "[id=%s] floating enable"
(s-trim (shell-command-to-string "xdotool getactivewindow")))))
(unwind-protect
(progn (consult-omni-apps-static ".*" (propertize "> " 'face 'consult-omni-path-face))
nil)
(progn
(when (frame-live-p frame) (delete-frame frame))
nil)))))
I made two changes to get this to work nicely with `i3`. First, I removed the `yequake` dependency. Second, I added a call to `i3-msg` that sets the launcher frame as floating, which makes it much nicer to use. Like Darvish’s version, you can run this from the command line:
emacsclient -e '(consult-launcher)'
## Adding an `ilia` fallback
Don’t tell all the other Emacs users, but I don’t have Emacs set up to launch automatically when I start my computer and log into X11. I probably should, huh? Also, there are times when I (gasp!) shut down Emacs, usually to restart it or fix something that I have broken. When those times happen, I want to be able to launch applications, so I need a failsafe in case `consult-launcher` isn’t available!
To solve this, I created a simple shell wrapper script, which looks like this:
#!/bin/bash
# Check if Emacs server is running by looking for the server socket
# Default server name is "server", but you can change this if needed
SERVER_NAME="${EMACS_SERVER_NAME:-server}"
SERVER_FILE="${XDG_RUNTIME_DIR:-/tmp}/emacs/${SERVER_NAME}"
if [ -S "$SERVER_FILE" ]; then
# Emacs is running, use emacsclient to launch your application
emacsclient -e '(consult-launcher)'
else
# Emacs is not running, fall back to ilia
ilia -p apps
fi
If you want to use this, the important part is that `SERVER_FILE` points to the socket that your Emacs server uses. Make sure that `emacsclient` and `ilia` are both in a reasonable location so your shell can find them, then bind this command to whatever you usually use to launch `ilia`.
By the way, if you are using Regolith’s normal method of launching `ilia`, you can add your shell script to your Regolith configuration pretty easily. Open `$HOME/.config/regolith3/Xresources` in your text editor, and add the line:
wm.program.launcher.app: /path/to/your/launcher.sh
You can then run `xrdb -override $HOME/.config/regolith3/Xresources` and it should work! Good luck.
## Drawbacks
One of the nice things about `ilia` is that it keeps track of applications your run frequently, so they tend to bubble up to the top of its application listing. The Emacs method doesn’t do that. I don’t mind so much, I always end up typing in application names. It is fun to use Emacs as an application launcher, and I hope that it helps me avoid the CPU-churn problem that `ilia` has been experiencing far too often.
Have I come up with a clever solution, or a lazy workaround? I’m looking forward to hearing your thoughts.