Published: October 19, 2025


If you've ever tried using Emacs with emacs -nw (no window) in your terminal, you might've faced a frustrating experience: because Emacs binds keys starting with Ctrl and terminal input is first handled by the terminal emulator; some keys might not work as expected. They might provoke an action in the terminal but not in Emacs or be changed on the way.

A common issue is trying to use Ctrl+Backspace which should delete a word but instead inserts Ctrl+h instead when used in the terminal. There are also issues for more complex key combinations.

Fix

The fix consists of using the Kitty Keyboard Protocol in both Emacs and your terminal emulators.

I've tried to set a couple variables and tried some terminal emulators; the best experience I've had is with the kkp package and the ghostty terminal emulator.

With the following configuration, in your initialization file:

(use-package kkp :ensure t :config (global-kkp-mode +1))

Quality of life

When setting Emacs as your $EDITOR in the shell, I discovered that export EDITOR="emacs -nw" sometimes fail depending on your shell, because it expects an executable without any white-spaces. An alternative is to write a shell function and refer to that as the editor:

exec emacs -nw "$@"

I have it in /home/natfu/.local/bin/emacseditor and I've set my $EDITOR to this path.

Note that this spins a new instance of Emacs every time with a slight delay in starting up even with a rather optimized ~/.emacs.d/init.el. The alternative is to use the emacsclient. In my case, I'm using Linux with systemd so I can write a unit file that will start an Emacs daemon in the background and open a new frame instantly by calling it. On my system, I've found that file in those places:

  • /usr/share/emacs/30.2/etc/emacs.service
  • /usr/lib/systemd/user/emacs.service

To make it clear that I'll use it for my user, I'll copy the file under ~/.config/systemd/user/emacsd.service. You can tweak it to your liking, for example by adding an environment file only for that service (useful to set the path among other things).

[Unit] Description=Emacs text editor (GUI) Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/ [Service] Type=notify ExecStart=/usr/bin/emacs --fg-daemon ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)" SuccessExitStatus=15 Restart=on-failure [Install] WantedBy=default.target

Then, I can change the /home/natfu/.local/bin/emacseditor file to use the client instead.

exec emacsclient -t -a="" "$@"

Finally, start the service with systemctl --user start --now emacsd.service and systemctl --user enable --now emacsd.service to have the service launch Emacs for you at the start of a user session.

As always, the Arch wiki on Emacs is great information, even if you're not using Arch.

Important note for Linux

On most Linux systems, you'll be able to use .desktop files which not only do let you add icons and interact on a GUI level with an application (like icons in menus/desktops), it also lets you interact with the rest of the system, for example through shortcuts.

From my understanding, it's up to the distribution and package management to create those files, here's what Arch is doing for Emacs. Where the entry called 'Emacs (Client)' when clicked on, will try to create a frame by connecting to the client if it exists and create it otherwise. It lives in /usr/local/share/applications/emacsclient.desktop. If you want to create one that'll be picked up by system instead of that one, you should use ~/.local/share/applications/emacsclient.destkop.

[Desktop Entry] Name=Emacs (Client) GenericName=Text Editor Comment=Edit text MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;x-scheme-handler/org-protocol; Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec /usr/local/bin/emacsclient --alternate-editor= --reuse-frame \\"\\$@\\"; else exec emacsclient --alternate-editor= --create-frame; fi" sh %F Icon=emacs Type=Application Terminal=false Categories=Development;TextEditor; StartupNotify=true StartupWMClass=Emacs Keywords=emacsclient; Actions=new-window;new-instance; [Desktop Action new-window] Name=New Window Exec=/usr/local/bin/emacsclient --alternate-editor= --create-frame %F [Desktop Action new-instance] Name=New Instance Exec=emacs %F

Right now, I'm keeping the systemd services and just let that file pick up the existing client.

I've also bound calling the client to Super+E to get a new frame quickly wherever I am on the system and aliased e=$EDITOR to open the editor in the terminal.

Using multiple Emacs clients

I've had a couple annoyances since the Emacs configuration is loaded once when the client is created and changes, like themes, are reflected on the client and all frames connecting to it.

I generally change my theme based on the time of day in the GUI but I want it to stay a dark theme in the CLI in general. I also want to load some packages only in graphical mode, like org-mode. For example,

(use-package org :pin gnu :ensure nil :defer t :diminish "Οrg" :if (display-graphic-p) :custom :config

This prompted me to write a systemd service file for the GUI and the other for the TUI. Which Emacs lets us do easily: you can name the socket you expose the client on.

This is getting out of hand, now there are two of them!

[Unit] Description=Emacs text editor (GUI) Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/ [Service] Type=notify ExecStart=/usr/bin/emacs --fg-daemon=gui ExecStop=/usr/bin/emacsclient -s gui --eval "(kill-emacs)" SuccessExitStatus=15 Restart=on-failure [Install] WantedBy=default.target

and

[Unit] Description=Emacs text editor (TUI) Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/ [Service] Type=notify ExecStart=/usr/bin/emacs --fg-daemon=tui ExecStop=/usr/bin/emacsclient -s tui --eval "(kill-emacs)" SuccessExitStatus=15 Restart=on-failure [Install] WantedBy=default.target

As a result, the desktop file needs to refer to the gui client and the editor in the terminal needs to refer to the tui client.

[Desktop Entry] Name=Emacs (Client) GenericName=Text Editor Comment=Edit text MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;x-scheme-handler/org-protocol; Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec /usr/local/bin/emacsclient -s gui --reuse-frame \\"\\$@\\"; else exec /usr/local/bin/emacsclient -s gui --create-frame; fi" sh %F Icon=emacs Type=Application Terminal=false Categories=Development;TextEditor; StartupNotify=true StartupWMClass=Emacs Keywords=emacsclient; Actions=new-window;new-instance; [Desktop Action new-window] Name=New Window Exec=/usr/local/bin/emacsclient -s gui --create-frame %F [Desktop Action new-instance] Name=New Instance Exec=emacs --daemon=gui-temp -Q && emacsclient -s gui-temp --create-frame %F exec emacsclient -s tui -t -a="" "$@"

Finally, you can reload the systemd daemon and start then enable the services:

systemctl --user daemon-reload systemctl --user enable emacsd-gui.service emacsd-tui.service systemctl --user start emacsd-gui.service emacsd-tui.service

That's all for me, I honestly didn't think that trying to fix Ctrl+Backspace would become such an adventure.