I've long counted myself among the grumpy old-timers who grudgingly accept the shift towards web-based-everything and just try to make the most of it, wistfully remembering the days when I could just do everything from within Emacs. One of my core survival strategies in this web-first world has been to trick my browser into at least having the decency to pretend to be Emacs. I accomplished this in Firefox1 with the Keysnail extension. Keysnail has remarkable flexibility in how it overrides Firefox's default key bindings to match those of Emacs, and everything has been more or less great.

Unfortunately, a soon-to-be-released update to Firefox will remove the extension mechanism used by Keysnail.

I have felt very conflicted about this, because the old state of affairs is admittedly untenable. Firefox currently uses Gecko, a decades-old rendering engine written in C++, and like much software written in C++ it has a pretty distressing security track record. Version 57 of Firefox replaces parts of Gecko with functionality from Servo, a browser engine implemented in the Rust programming language. Most of the bugs in Gecko which have led to embarrassing security flaws are simply impossible in Servo. The fact that so much safety-critical code is still being written in C++ and similar languages is a sad state of affairs, and we should celebrate changes that mean end users will no longer bear the penalty for programmers' reluctance to move beyond the technology of the 1980s.

But on the other hand, losing the ability to shape your computing environment to your whims is awful. I lost track of how many times (when using Chromium or other keysnail-less browsers) I've wanted to throw my laptop out the window when I held down ctrl-n to scroll down and it opened seventeen new windows instead. I can't remember ever wanting to open a new browser window in the past decade; why should I be stuck with a key bound to that command and no way to disable it?

Of course, the new Firefox will still have an extension mechanism, but it's a pale shadow of the old one. Citing the flimsy2 excuse of security, key bindings like C-n are hard-coded into the browser and forbidden from being overridden.

Things were looking bleak for me, and I contemplated whether I would switch to curl or just give up software development altogether for a career in goat-herding. I ended up finding a solution from a most unlikely place.

EXWM saves the day

I had heard of EXWM a while ago, and it struck me as a quixotic curiosity. The X Window System uses a network socket for its control protocol, allowing a lot of flexibility including native forwarding of interfaces for remote programs. The developer of EXWM had taken an XML description of the specification for the network protocol and written a compiler to turn it into a library of Emacs Lisp functions which he then used to implement a window manager in pure Emacs Lisp. While I admired the chutzpah this must have taken, I assumed it was a novelty that could never be practical.

Eventually the Firefox conundrum prompted me to give it a second look due to a feature called Simulation Keys. The exwm-input-set-simulation-keys function allows you to define a translation mapping so that a certain key combination will be intercepted by EXWM when a non-Emacs program has focus, and a different set of key input events will be sent instead. It seemed too good to be true; I could let go of Keysnail and instead get the same features applied to every program I use3.

I'm happy to report that EXWM does actually function startlingly well as a window manager. The simulation keys feature is amazing and puts my Firefox-related fears at ease, and having all configuration written in a single language simplifies my setup dramatically. Every X window you launch is given an Emacs buffer, and all your normal splits and window resizing commands work great with it. With the tiling window managers I used in the past, it was so unusual for me to use something other than the "one fullscreen window per display" setup that I would often forget the key bindings for splitting and rearranging windows. EXWM even integrates "system tray" programs into the Emacs echo area, so your wifi connect tool shows up unobtrusively in the bottom right corner.

There are a handful of gotchas. Emacs Lisp lacks general-purpose concurrency features, but it does allow for concurrency when dealing with subprocesses and network communication. Most well-written Emacs Lisp will never block the main event loop, which is good because when using EXWM that means the entire window manager is stuck until the blocking operation completes. I only came across two exceptions to this rule. One of them is smtpmail-send-it , which can be replaced by the smtpmail-async library. The other is the racket-run command, which I was able to patch in about an hour to remove the blocking call4.

Other folks might run into more problems if they use other third-party libraries which don't take care to use the network functions properly. But for my use5, it's been very smooth, and I'm thrilled to have it.

Update: I've started configuring my browser to open everything in new windows instead of new tabs, which sounds crazy, but is very useful, because it means that you can use Emacs's built-in buffer switching tools to change tabs, which are much better than anything I've seen inside a browser.

๛