• Automatically Kill Running Processes on Exit

    One of the things I really hate in Emacs is that prompt you normally get when you try to exit Emacs when there are some sub-processes running (e.g. some external shell you’ve started).

    I’m a big believer in just doing all the necessary exit cleanup automatically when the user requests an exit. Simple as that. On a practical note - that prompt tends to interrupt the rather inept shutdown/restart sequence of macOS, which waits for all applications to be gracefully closed before proceeding. The time I’ve spent on macOS was the reason that prompted me to look for some solution of this issue.

    So, can we do something about this? Historically we couldn’t, but Emacs 26 finally introduced a configuration setting for this - confirm-kill-processes. By default it’s set to t, meaning you’ll get the confirmation prompt on exit, but you can easily change this:

    (setq confirm-kill-processes nil)

    Problem solved. That’s all I have for you today.

  • Installing Emacs via Snap

    Note: That article is only relevant for Linux users.

    For as long as I can remember I’ve been installing Emacs (primarily) in one of two ways:

    • Via the standard package manager of the operating system I’m using (e.g. apt on Ubuntu or brew on macOS)1
    • From source

    Typically, I’d go with the first option unless I am doing some development work on Emacs or I want to experiment with some build options, when I’d go with the second approach. Obviously, nothing beats the convenience of the built-in package manager, but from time to time you’d want to use a version of Emacs that’s not available in your package manager’s repositories and then you have to get more creative (e.g. find third-party repos, some (random) pre-built packages, or build Emacs from source).

    Turns out today there’s a third option (at least for Linux users) - installing Emacs via snap. Simply put, snap is a distro-agnostic package management framework that distributes self-contained applications (they don’t have any external dependencies). Think of it as something similar to Apple’s App Store. There are other similar projects in the realm of Linux (e.g. AppImage and Flatpak), but snap seems to be the most popular today, mostly because it was developed by the makers of Ubuntu, Canonical.

    Emacs is available at in 3 flavors:

    • latest stable version (published to the latest/stable channel)
    • latest release candidate (published weekly to the latest/beta channel)
    • snapshot build (published weekly to the latest/edge channel)

    Provided you’ve already setup snap, installing Emacs is trivial:2

    # install stable version
    $ sudo snap install emacs --classic
    # install release candidate
    $ sudo snap install emacs --beta --classic
    # install snapshot version
    $ sudo snap install emacs --edge --classic

    Super simple, right?

    While I still plan to use apt most of the time (these days I’m using Ubuntu), I have to admit that snap gives you a trivial way to get access to the latest and greatest version of Emacs on all major Linux distros. I think that’s a very convenient option for anyone who’s not using a rolling release distro, or simply doesn’t want to waste time on upgrading their Emacs.

    That’s all I have for you today! Meta-end!

    1. In the interest of full disclosure - I’ve also used pre-built binary packages for macOS from

    2. provides setup instructions for all major Linux distros. Here are the instructions for Fedora as an example. 

  • Instant Access to Your Shell Init Files

    A long time I presented a simple hack that allowed you to quickly navigate to your shell’s user config file (e.g. .bashrc or .zshrc). While the solution gets the job done it was pretty basic and limited - most notably it’d ignore the fact that you typically have several shell config files that are often built on top of each other - e.g. /etc/profile, ~/.bash_profile and ~/.bashrc. Fortunately the original hack evolved rather nicely and today lives in the crux library under the name crux-find-shell-init-file.1

    Provided you’ve installed crux all you need to do is run that command (e.g. with M-x crux-find-shell-init-file) and you’ll get something like this as the result:


    Pretty neat, right? Even in its updated state the command is not complex at all:

    (defun crux-find-shell-init-file ()
      "Edit the shell init file in another window."
      (let* ((shell (file-name-nondirectory (getenv "SHELL")))
             (shell-init-file (cond
                               ((string= "zsh" shell) crux-shell-zsh-init-files)
                               ((string= "bash" shell) crux-shell-bash-init-files)
                               ((string= "tcsh" shell) crux-shell-tcsh-init-files)
                               ((string= "fish" shell) crux-shell-fish-init-files)
                               ((string-prefix-p "ksh" shell) crux-shell-ksh-init-files)
                               (t (error "Unknown shell"))))
             (candidates (cl-remove-if-not 'file-exists-p (mapcar 'substitute-in-file-name shell-init-file))))
        (if (> (length candidates) 1)
            (find-file-other-window (completing-read "Choose shell init file: " candidates))
          (find-file-other-window (car candidates)))))

    I guess one thing that we can improve down the road is adding an option to display the shell config in the same window, but that’s a small thing. The variables like crux-shell-bash-init-files are simply lists of all potential files that we should look for, that’s why I’ve opted to omit them from the code listing.

    I stand by my original suggestion to bind this useful command to C-c S:

    (global-set-key (kbd "C-c S") #'crux-find-shell-init-file)

    That’s all I have for you today! Meta-x forever!

    1. Did you notice the subtle difference in the names of the original and the updated article? 

  • Running Emacs with systemd

    Recently I’ve switched back to Linux, after having used macOS for the past 9 years. While I was generally happy with my overall macOS experience, I was also disappointed that Emacs simply didn’t work as well there, as it does on Linux for various reasons.

    When I was a Linux user I’d always run Emacs as a daemon (server) and I’d connect this daemon from multiple instances of emacsclient. This was both elegant and efficient - my clients started instantly and shared access to everything that was running on the daemon instance. While this was doable to some extent in macOS, it never worked quite as well for me, and I abandoned that workflow eventually. Now, however, I’m back! Time to revive the workflow!

    Historically I ran the Emacs daemon by adding something like this to my shell init (e.g. .bashrc):

    export ALTERNATE_EDITOR=''
    alias e='emacsclient --tty'

    The magic is in the first line - leaving ALTERNATE_EDITOR blank. That way the first time I ran emacsclient it’d start an Emacs daemon and connect to it. Many people preferred to make the daemon a “proper” service that they can start, restart and monitor, but this felt like an overkill to me. I’ve noticed, however, that Emacs 26.1 bundles a systemd unit, so it’s now trivial to control your Emacs daemon with systemd. It all boils down to running this command:

    $ systemctl --user enable --now emacs

    Run this command with your regular user (or whatever user account you want to be running Emacs). Don’t run it as root, though! You’ll get a message that the unit file was copied to /usr/lib/systemd/user/emacs.service and you can examine it if you’re curious:

    Description=Emacs text editor
    Documentation=info:emacs man:emacs(1)
    ExecStart=/usr/bin/emacs --fg-daemon
    ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"

    Pretty straightforward.

    At this point your Emacs daemon is up and you can connect to it using both terminal clients (emacsclient -t) and GUI clients (emacsclient -c). You might also want to create some desktop icon that runs emacsclient -c, instead of emacs. You might also want to set both EDITOR and VISUAL to emacsclient -t:

    export EDITOR='emacsclient -t'
    export VISUAL='emacsclient -t'

    I typically add two more aliases just to be on the safe side:1

    alias vi='emacsclient -t'
    alias vim='emacsclient -t'

    One thing to keep in mind is that when you’re running Emacs in this manner it won’t read your user environment variables (at least not those coming from your .bash_profile and .bashrc). That’s why it’s a good idea to install the popular package exec-path-from-shell.

    Alternatively you can use one of systemd’s own mechanisms for setting environment variables - e.g. environment.d. The Arch Linux Wiki has a few good examples of using environment.d and also mentions other approaches that you can consider.

    That’s all I have for you today. I hope you’ve learned something useful. In parentheses we trust!

    1. Old habits die hard. 

  • Remap Enter to Control in GNU/Linux (2020 Edition)

    Note: Check out my original article from 2013 about the rationale behind this remapping.

    Recently I’ve switched back from macOS to GNU/Linux, as my primary development environment, and I found out that my old article on remapping Enter to Control was no longer the optimal way to achieve this (e.g. - xcape operates at the X level, which means it doesn’t work with Wayland or without a GUI). It took me a bit of digging, but eventually I found dual-function-keys (a plugin for the interception framework), which does exactly what I needed and it does it splendidly.

    Unfortunately, the tool is not packaged for most GNU/Linux distros1, but setting it up from source is not that complex. In this article I’ll share instructions that are specific to Ubuntu, but they should be easy to modify for other Linux distros.

    Let’s kick it off by downloading and installing the interception framework and dual-function-keys:

    # install build deps
    $ sudo apt install libudev-dev libyaml-cpp-dev libevdev-dev cmake
    # create a folder where to clone the source code
    $ mkdir src && cd src
    # clone the necessary code
    $ git clone
    $ git clone
    # build and install the interception framework
    $ cd tools
    $ mkdir build
    $ cd build
    $ cmake ..
    $ make
    $ sudo make install
    $ cd ../..
    # build the dual-function-keys plugin
    $ cd dual-functions-keys
    $ make && sudo make install

    That wasn’t so hard, right? Now we have to create a couple of configuration files and we’re ready for action. The first one is .dual-function-keys.yaml (normally placed in your home folder):

    # /home/username/.dual-function-keys.yaml
      TAP_MILLISEC: 200
      - KEY: KEY_ENTER
        TAP: KEY_ENTER

    That’s the main config for dual-function-keys, where we’re specifying the duration of a tap and double tap and our remapping rules. In our case there’s a single rule - Enter acts as Enter on tap (when pressed briefly) and as (right) Control when held down longer.

    Then we need to create /etc/udevmon.yaml (you’ll need sudo for this):

    # /etc/udevmon.yaml
    - JOB: "intercept -g $DEVNODE | dual-function-keys -c /home/bozhidar/.dual-function-keys.yaml | uinput -d $DEVNODE"
          EV_KEY: [KEY_ENTER]

    Note: Update the path the .dual-function-keys.yaml accordingly.

    Finally we need to create a systemd service definition file for udevmon and start the new service:

    # /etc/systemd/system/udevmon.service
    ExecStart=/usr/bin/nice -n -20 /usr/local/bin/udevmon -c /etc/udevmon.yaml

    Now we simply have to enable the udevmon service our remapping will kick in:

    $ sudo systemctl enable --now udevmon

    That’s all! Now you can start enjoying your beloved productivity boost!

    You can achieve a lot more with dual-function-keys, so I’d advice you to explore the tool further. Keep hacking!


    Another option I considered was xkeysnail, which seemed a bit simpler to setup, as it’s written in Python, and even has an example config geared towards Emacs users. You might want to check it out.

    If someone’s using another approach to achieve the same result I’d love to hear about it!

    1. Seems currently it’s only packaged for Arch Linux and family (e.g. Manjaro). 

Subscribe via RSS | View Older Posts