On my system, Emacs segfaults if this is run in a daemon before a window is created:
(set-fontset-font (face-attribute 'default :fontset)
'han "Noto Sans Mono CJK JP")
I don't really know how this works, but I just need to prevent it from running during that time. So how do we check if Emacs is running as a daemon but a window hasn't been created?
(I'll go back to calling them “frames” in the rest of this article.)
TL;DR: (terminal-name)
is always "initial_terminal"
in a daemon before a frame is created. Like this:
Testing
Let's launch a daemon to test with. In one terminal:
emacs -Q --fg-daemon
We use emacs -Q
to not load any extra init files.
Now let's try the obvious. The Emacs manual implies that there will be no existing frame when Emacs is started as a daemon:
If the Emacs process has no existing frame—which can happen if it was started as a daemon (see Emacs Server)—then Emacs opens a frame on the terminal in which you called
emacsclient
.
emacsclient --eval "(frame-list)"
(#<frame F1 0x5562c8db0ea0>)
Uhh, what?
emacsclient --eval "(selected-frame)"
#<frame F1 0x5562c8db0ea0>
This frame is even selected. (frame-live-p)
on it returns t
, which means it claims to be displayed on a text based terminal.
Let's try this instead:
emacsclient --eval "(frame-terminal (car (frame-list)))"
#<terminal 0 on initial_terminal>
Now we're getting somewhere. We can use terminal-name
to extract the initial_terminal
name as a string:
emacsclient --eval "(terminal-name (frame-terminal (car (frame-list))))"
initial_terminal
And, in fact, because this frame is selected already and terminal-name
automatically uses the selected frame's terminal, we can omit its argument:
emacsclient --eval "(terminal-name)"
initial_terminal
In a normal frame, this will return something else:
(terminal-name)
:0
Additional reading
After finding out the term initial_terminal
it became easier to search for the same answer.
-
Why does
(frame-list)
return one extra frame in daemon mode?It's a "physically invisible" frame (even though
frame-visible-p
says otherwise) associated with initial terminal where the daemon was started. I suspect that a sole reason for its existence is that emacs is not ready to run with no frames at all, and it's hard enough to fix it. -
Deferring interactive prompts during startup in daemon mode.
This uses
(string-equal (terminal-name (get-device-terminal nil)) "initial_terminal")
to determine the same thing: whether we're in daemon mode and before any frames have been created.
(get-device-terminal nil)
returns the display terminal of the selected frame, so(terminal-name (get-device-terminal nil))
is exactly the same as(terminal-name)
. -
This technique is in fact used in Emacs itself, in
debug.el
. This was added in Emacs 27, in this commit. -
What is the difference between a terminal and a frame? Terminals are basically output devices. Multiple frames can share the same terminal.
-
The “initial terminal” in its current form was added in this commit, released in Emacs 23.