Server Setup

I use Nix to install latest software, chezmoi to manage configuration files, and starship and fastfetch for my shell (zsh).

fd is installed as a dependency for Doom Emacs, and mosh is used with Blink to provide UDP roaming capability.

$ sudo dnf install zsh fd-find mosh fastfetch tmux git-core
$ nix-env -q --installed
chezmoi-2.46.0
diff-so-fancy-1.4.4
emacs-nox-29.2
glibc-locales-2.38-44
nix-2.19.3
starship-1.17.1
  1. Install the packages above.
  2. Add unset SSH_TTY to .zprofile. This is a workaround for clipboard integration. See tty and https://github.com/blinksh/blink/issues/1957.
  3. Set up starship and fastfetch in .zprofile.
  4. Alias emacs to env TERM=vscode-direct emacs for 24-bit color in mosh (without tmux). This requires a recent version of ncurses, see details and how to configure tmux below.
  5. Set up Doom Emacs according to https://github.com/doomemacs/doomemacs.
  6. Add (:if (featurep :system 'linux) (tty +osc)) to :os in doom/init.el for clipboard integration.
  7. Add (add-hook 'tty-setup-hook #'mouse-wheel-mode) to doom/config.el for mouse wheel support.
  8. For C-i in Evil to “work” in terminal, add the following to doom/config.el.
;; In TTY, C-i is always recognized as "TAB".
(map! :after evil
      :map evil-motion-state-map
      "TAB" #'evil-jump-forward)
  1. If you need arrow keys, avoid mapping M-O, but M-o is fine. See LispyVille.
(after! lispyville
  (evil-define-key 'normal lispyville-mode-map
    (kbd "M-O") nil))
  1. If Doom Emacs throws Lisp error: "Note: file is write protected" when SPC h f let* is pressed, it is likely because files in the nix store have r--r--r-- permissions and can be safely ignored. See backtrace below captured with debug-on-message.
  after-find-file(nil t)
  find-file-noselect-1(#<buffer eval.c> "/nix/store/wl840kj5v3f17ns5p685g1p61xdk7sc0-emacs-..." nil nil "/nix/store/wl840kj5v3f17ns5p685g1p61xdk7sc0-emacs-..." (55171408 64512))
  find-file-noselect("/nix/store/wl840kj5v3f17ns5p685g1p61xdk7sc0-emacs-nox-29.1/share/emacs/29.1/src/eval.c")
  helpful--open-if-needed("/nix/store/wl840kj5v3f17ns5p685g1p61xdk7sc0-emacs-...")
  helpful--definition(let* t)
  #<subr helpful-update>()
  apply(#<subr helpful-update> nil)
  helpful-update()
  helpful--update-and-switch-buffer(let* t)
  helpful-callable(let*)
  funcall-interactively(helpful-callable let*)
  command-execute(helpful-callable)

If you find it annoying, define the following advice in doom/config.el.

(after! helpful
  (defadvice! doomd--supress-helpful-open-buffer-noise (fn &rest args)
    :around #'helpful--open-if-needed
    (let ((noninteractive t))
      (apply fn args))))

24-bit color

toe -a lists all available terminal types in the system terminfo database.

You could use this script to check color support in your terminal. “True color gradient” should have a different color under each slash or backslash character and form a smooth gradient.

For emacs installed with Nix, vscode-direct is supported and can be verified with the following command.

find "$(nix-store -qR .nix-profile/bin/emacs | grep ncurses)" | grep .-direct

In case of mosh (without tmux), vscode-direct has to be used, because mosh does not understand SGR colon syntax yet.

You can add the following to .zprofile to apply it to Emacs. env command is used to avoid TERM affecting your shell in case its terminfo database is older.

alias emacs="env TERM=vscode-direct emacs"

For tmux, set the following in ~/.tmux.conf. terminal-overrides ensures colors work correctly, and tmux-direct is set as TERM and passed to applications. This works over both mosh and regular ssh.

set-option -s default-terminal "tmux-direct"
set-option -sa terminal-overrides ",xterm-256color:Tc"

Run M-x list-colors-display to display the Emacs-defined colors supported. Under xterm-256color it should end in color-255, and under xterm there are only 8 basic colors.

For more information, see https://chadaustin.me/2024/01/truecolor-terminal-emacs/.

Client Setup

  1. Install Blink
    1. from App Store, or
    2. build Blink from source, and
    3. with modifications (subject to change), Blink can be sideloaded with AltStore.
  2. Select JetBrains Mono Nerd Font in Settings > Keyboard > Custom Presses for icons in Doom Emacs.
  3. If your external keyboard does not have an Esc key, in Settings > Keyboard > ^ Control set Press Send to “Escape on Release” and in iOS settings map Caps Lock Key to Control.
  4. For C-SPC to work (IME switching shortcut in iOS), add a custom press in Settings > Keyboard > Custom Presses with a press action of the same combo.
  5. If you need hyper or super modifier keys for several key combos in Emacs, add a custom press. For example, hex code 1840732F (i.e. C-x @ s /) is equivalent to s-/, in which 18 is a control character CAN as defined in ASCII. This is an Emacs-only workaround and works for other modifiers as well (e.g. C-; with C-x @ c).
KeyHex CodeCustom String
C-S-j1840530A\x18@S\n
C-S-k1840530B\x18@S\v
C-;1840633B\x18@c;
s-/1840732F\x18@s/
C-SPCN/A (C-SPC)

Tips

  • Mosh sessions in Blink can survive device locks, even reboots. The drawback is that you need tmux to get scrollback.
  • To show the Context bar in Blink, tap the iOS Home Bar twice or press Cmd twice.
  • config is available as a command or via shortcut Cmd+,.

Custom Presses

  • Hyper and super modifiers does not work without custom press config. See https://emacs.stackexchange.com/a/5718.
  • C-S- keys does not work without custom press config, e.g. in Vertico. See https://emacs.stackexchange.com/a/32295.
  • Modifiers you can use without custom press is Ctrl (valid control characters only), Esc (i.e. Alt, because M-a is ESC a), and Shift (letters and keys with upper characters only, and output is capital letters or the alternative “upper” character).
  • You can’t add two modifiers in the custom press trick we made use of in Emacs, but C-<any letter> as a control character can count as one key, so C-S-j is still possible as C-x @ S \n.
  • C-i as a special case is still translated to TAB in Emacs, but we can re-bind the TAB key in Emacs.
  • C-x does not have a special ANSI-C quoting, but we could use \x18 in Custom String format, e.g. \x18@s/ for s-/.
  • C-; is not a control character, and therefore need a custom press to work in Emacs.

External Display

  1. When connected to an external display, Blink automatically creates a separate window on it. Press Cmd+O to switch there or back. See also https://docs.blink.sh/basics/tips-and-tricks#display-what-is-blink-window.
    1. If you don’t want this, change Settings > Appearance > External Display to None or Mirror. The default is Scale.
  2. You can set up a Mac to become an AirPlay Receiver. This is also treated as an external display in Blink. https://support.apple.com/guide/mac-help/mchleee00ec8/mac
    1. Note that Airplay is exclusive. You can’t multitask with other Mac apps while Airplaying.
  3. After several disconnects, Cmd+O might not be able to switch focus to the external display. Restarting Blink fixes it, but SSH connections would not persist through it.

Other Known Bugs

  1. On some external keyboards, j autorepeats, but w does not. This is related to iOS’s accent feature, where j does not have any corresponding accent character. https://github.com/blinksh/blink/issues/1965
  2. Emoji in PS1 causes cursor offset. See https://github.com/vercel/hyper/issues/2587 and https://github.com/blinksh/blink/issues/456.
  3. Thread-safety issues with ios_system. https://github.com/blinksh/blink/issues/1959
  4. Smart Keys not showing up in some cases. https://github.com/blinksh/blink/pull/1898
  5. Cmd key could be stuck when switching between apps. See https://docs.blink.sh/faq#cmd-key-stuck-while-switching-between-apps-with-cmd-tab.
  6. When editing an existing custom press of type “Custom String”, if the type is changed to “Hex Code”, it will be reverted if I go back one level and try to input the hex code. Going back two levels will be OK. https://github.com/blinksh/blink/issues/1968

Feature requests

  1. Disable font ligatures (workaround: use a font without ligatures) https://github.com/blinksh/blink/issues/828
  2. Inverted ANSI prompt https://github.com/blinksh/blink/issues/1451
  3. EternalTerminal support https://github.com/blinksh/blink/issues/597
  4. ping6 command and IPv6 support in ifconfig https://github.com/blinksh/blink/discussions/1925
  5. Can’t use trackpad or mouse on external display without Stage Manager.
  6. Sixel graphics, but mosh does not intend to support it. https://github.com/blinksh/blink/issues/750 and https://github.com/mobile-shell/mosh/issues/1081