Occasionally we’d have bits of configuration that are depending on whether Emacs is running in a terminal or in GUI mode. This article aims to cover how to best handle such situations.1

At first it seems the solution is super simple:

(if (display-graphic-p)
    ;; GUI mode
    ;; Terminal mode

In the past the variable window-system provided another way to check for this, but it has been deprecated for a while, so I’d advise against using it.

While the above solution works, it fails to address one fundamental aspect of Emacs - you can have multiple Emacs frames (or windows, in non-Emacs terminology) associated with one Emacs instance. Some of those might be on a terminal, and others might be on a window system. That is to say, you can get different values of display-graphics-p even within a single Emacs instance.

For example, you can start a GUI Emacs and then connect to it via emacsclient -t in a terminal; the resulting terminal frame will see a value of nil for display-graphics-p. Similarly, you can start Emacs in daemon mode (emacs --daemon), then later tell it to create a graphical frame with emacsclient -c.

What this means in practice is that it’s usually best to put code that’s GUI/terminal specific in after-make-frame-functions:

(add-hook 'after-make-frame-functions
  (lambda ()
    ;; we want some font only in GUI Emacs
    (when (display-graphics-p)
      (set-frame-font "DejaVu Sans Mono 28")))


(add-hook 'after-make-frame-functions
  (lambda ()
    ;; we do something only in terminal Emacs
    (unless (display-graphics-p)
      (xterm-mouse-mode 1)))

That way the check would happen separately for each frame and you’d be able to adjust the settings for it accordingly. And yeah - probably enabling a global mode like xterm-mouse-mode is not the best example for a frame-specific action to take, but I hope you get the general idea.

Note that the after-make-frame-functions hook isn’t run for the initial frame, so it’s often necessary to also add frame-related hook functions like that above to after-init-hook.

That’s all I have for you today. Keep hacking!

  1. The article is inspired by this StackOverflow discussion