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.