I am trying to have specific settings when I start a new emacs frame in X as opposed to in the terminal while using emacs-server. My initial attempt was using this solution, but it appears it isn't working for me (using Emacs 24.4.1).

Basically, my minimimal non-working example is this:

(defun new-frame-setup (&optional frame)
  (if (display-graphic-p)
      (message "window system")
    (message "not a window system")

;; run when regular emacs is started
;; run when a new frame is created using server
(add-hook 'after-make-frame-functions 'new-frame-setup)

When I start regular emacs and go to my Messages buffer it says:

window system

So this works just as I expected. However, if I run the command emacsclient -c, the Messages buffer says

not a window system
Starting Emacs daemon
not a window system

The first "not a window system" makes sense, as it's starting the emacs server in the terminal. However, the second one doesn't make sense, as that frame is already a graphical window. Furthermore, after the fact, if I evaluate (display-graphic-p) in my scratch buffer, it evaluates as t. Any ideas as to what I'm doing wrong?


So the big problem here is not that (display-graphic-p) doesn't work, it's that it doesn't know what frame to check. This was a problem for me since the daemon didn't have a window-system, but it's an even bigger problem if I have a terminal version and an X version of emacsclient running at the same time. For example, if I create a frame with emacsclient -c and have it change some settings, and then I create a frame with emacsclient -nw and have it change some other settings - all of those settings are getting changed at the global level.

So I guess the real question is: how can I get emacs to check the display of the most recently created frame, and then run some elisp code only on that frame? I have absolutely no idea if this is possible.

  • Try focus-in-hook instead. Jul 14, 2016 at 19:54
  • That somewhat works, but I really only want these setup functions to run once. For example, when i start an emacs client, i have it move to my left monitor - if i use focus-in-hook, it will make it unable to move my window after the fact.
    – rottweiler
    Jul 14, 2016 at 20:09
  • Did you try using the variable window-system instead?
    – theldoria
    Jul 14, 2016 at 20:11
  • using window-system gives the exact same problem. I used display-graphic-p because according to the docs "Use of this variable as a boolean is deprecated"
    – rottweiler
    Jul 14, 2016 at 20:18
  • 1
    @rottweiler You can then remove that function from that hook from within that function. It sounds crazy, but works. :) Jul 14, 2016 at 20:28

1 Answer 1


The hook on after-make-frame-functions runs after the frame is created but before it's selected (if it ever becomes selected). So you're asking whether the currently selected frame is on a graphical display, rather than whether the newly created frame is on a graphical display. This is easy to fix: pass the frame you're interested in to display-graphic-p.

(defun new-frame-setup (frame)
  (if (display-graphic-p frame)
      (message "window system")
    (message "not a window system")))

;; Run for already-existing frames
(mapc 'new-frame-setup (frame-list))
;; Run when a new frame is created
(add-hook 'after-make-frame-functions 'new-frame-setup)
  • This correctly answers my question. Even better (for my case) is to add &optional before frame, that way i can also have (unless (daemonp) (add-hook 'after-init-hook 'new-frame-setup)) for when i run emacs or emacs -nw. I do have a follow up question though: I believe any code that gets run in new-frame-setup is run globally, therefore I don't believe the mapc line does much. Is there a way to make something run only on a single frame and not globally? (This may be worth a posting a new question)
    – rottweiler
    Jul 16, 2016 at 18:39
  • @rottweiler I'm not sure I understand your follow-up question. You can run (new-frame-setup some-frame). The reason I used mapc was to make the code more robust: it runs on every existing frame. You don't need any special handling for a daemon or non-daemon startup, and you don't need to use after-init-hook. Jul 16, 2016 at 20:02
  • The main reason I want to do this is so that I can set specific settings (keybindings, mouse settings, line numbering, etc.) for emacsclient -c and emacsclient -nw, much like I currently do when I have separate instances of emacs/emacs-nw running. But for emacsserver, the main functionality of this fix is to change specific frame characteristics (like positioning and size) which are a small subset of functions. Here, if start a terminal version of emacsclient and then open a X version of emacsclient, all those new keybindings/etc get applied globally to all open frames.
    – rottweiler
    Jul 16, 2016 at 21:49
  • I found it useful to read add-to-list vs add-hook and the Emacs description of hooks, which explains why we use add-hook here instead of add-to-list. We know after-make-frame-functions is a "hook" (an "abnormal" hook) because its name ends with "-functions". And we use add-hook on hooks both to make code clearly self-descriptive of what we mean, and because add-hook knows how to handle edge cases like the hook variable being nil.
    – mtraceur
    Mar 30, 2023 at 23:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.