Add predicate for initial_terminal
This introduces the predicate frame-initial-p, which uses struct frame.output_method or struct terminal.type to detect initial_terminal without relying on its name (bug#80629). For some prior discussion, see: https://lists.gnu.org/r/emacs-devel/2019-12/msg00480.html https://lists.gnu.org/r/emacs-devel/2020-01/msg00120.html * doc/lispref/frames.texi (Frames): Document frame-initial-p. (Finding All Frames): Fix grammar. * etc/NEWS (Lisp Changes in Emacs 31.1): Announce frame-initial-p. * lisp/desktop.el (desktop--check-dont-save): * lisp/emacs-lisp/debug.el (debug): * lisp/frameset.el (frameset-restore): * lisp/menu-bar.el (menu-bar-update-buffers): * lisp/xt-mouse.el (turn-on-xterm-mouse-tracking-on-terminal): Use frame-initial-p instead of checking the "initial_terminal" name. * lisp/emacs-lisp/byte-opt.el: Mark frame-initial-p as error-free. * src/pgtkterm.c (pgtk_focus_changed): Use IS_DAEMON in place of Fdaemonp, thus also accepting a named daemon session. * src/terminal.c (decode_tty_terminal): Clarify commentary. (Fframe_initial_p): New function. (syms_of_terminal): Expose it. (init_initial_terminal): Update commentary now that menu-bar-update-buffers uses frame-initial-p (bug#53740). * test/lisp/xt-mouse-tests.el (with-xterm-mouse-mode): Simulate the lack of an initial terminal by overriding frame-initial-p now that turn-on-xterm-mouse-tracking-on-terminal uses it. * test/src/terminal-tests.el: New file.
This commit is contained in:
@@ -89,6 +89,20 @@ displayed on that terminal; the list of possible values is the same as
|
||||
for @code{framep} above.
|
||||
@end defun
|
||||
|
||||
@defun frame-initial-p &optional frame
|
||||
This predicate returns non-@code{nil} if @var{frame} is or holds the
|
||||
initial text frame that is used internally during daemon mode
|
||||
(@pxref{Initial Options, daemon,, emacs, The GNU Emacs Manual}), batch
|
||||
mode (@pxref{Batch Mode}), and the early stages of startup
|
||||
(@pxref{Startup Summary}). Interactive and graphical programs, for
|
||||
instance, can use this predicate to avoid operating on the initial
|
||||
frame, which is never displayed.
|
||||
|
||||
If @var{frame} is a terminal, this function returns non-@code{nil} if
|
||||
@var{frame} holds the initial frame. If @var{frame} is omitted or
|
||||
@code{nil}, it defaults to the selected one.
|
||||
@end defun
|
||||
|
||||
@cindex top-level frame
|
||||
On a graphical terminal we distinguish two types of frames: A normal
|
||||
@dfn{top-level frame} is a frame whose window-system window is a child
|
||||
@@ -3029,7 +3043,7 @@ direction.
|
||||
See also @code{next-window} and @code{previous-window}, in @ref{Cyclic
|
||||
Window Ordering}.
|
||||
|
||||
Some Lisp programs need to find one or more frames that satisfy a
|
||||
Some Lisp programs need to find one or more frames that satisfy
|
||||
given criteria. The function @code{filtered-frame-list} is provided for
|
||||
this purpose.
|
||||
|
||||
|
||||
8
etc/NEWS
8
etc/NEWS
@@ -4101,6 +4101,14 @@ to display its char argument on a given frame. This new function,
|
||||
unlike 'char-displayable-p', does not check whether the character can be
|
||||
encoded by the underlying terminal.
|
||||
|
||||
+++
|
||||
** New function 'frame-initial-p'.
|
||||
This predicate returns non-nil if a given frame or terminal is or holds,
|
||||
respectively, the initial text frame that is used internally during
|
||||
daemon mode, batch mode, and the early stages of startup. Interactive
|
||||
and graphical programs, for instance, can use this predicate to avoid
|
||||
operating on the initial frame, which is never displayed.
|
||||
|
||||
+++
|
||||
** New macros 'static-when' and 'static-unless'.
|
||||
Like 'static-if', these macros evaluate their condition at
|
||||
|
||||
@@ -775,6 +775,7 @@ if different)."
|
||||
;; Don't delete daemon's initial frame, or
|
||||
;; we'll never be able to close the last
|
||||
;; client's frame (Bug#26912).
|
||||
;; Use `frame-initial-p'?
|
||||
(and (daemonp) (eq frame terminal-frame))
|
||||
(frame-parameter frame 'desktop-dont-clear))
|
||||
(delete-frame frame))
|
||||
@@ -1067,9 +1068,8 @@ DIRNAME must be the directory in which the desktop file will be saved."
|
||||
(and (not (frame-parameter frame 'desktop-dont-save))
|
||||
;; Don't save daemon initial frames, since we cannot (and don't
|
||||
;; need to) restore them.
|
||||
(not (and (daemonp)
|
||||
(equal (terminal-name (frame-terminal frame))
|
||||
"initial_terminal")))))
|
||||
(not (and (daemonp) ;; FIXME: Remove `daemonp'?
|
||||
(frame-initial-p frame)))))
|
||||
|
||||
(defconst desktop--app-id `(desktop . ,desktop-file-version))
|
||||
|
||||
@@ -1260,7 +1260,7 @@ This function also sets `desktop-dirname' to nil."
|
||||
"True if calling `desktop-restore-frameset' will actually restore it."
|
||||
(and desktop-restore-frames desktop-saved-frameset
|
||||
;; Don't restore frames when the selected frame is the daemon's
|
||||
;; initial frame.
|
||||
;; initial frame. Use `frame-initial-p'?
|
||||
(not (and (daemonp) (eq (selected-frame) terminal-frame)))
|
||||
t))
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ See Info node `Displaying Boundaries' for details."
|
||||
(defun display-fill-column-indicator--turn-on ()
|
||||
"Turn on `display-fill-column-indicator-mode'."
|
||||
(unless (or (minibufferp)
|
||||
;; Use `frame-initial-p'?
|
||||
(and (daemonp) (eq (selected-frame) terminal-frame)))
|
||||
(display-fill-column-indicator-mode)))
|
||||
|
||||
|
||||
@@ -1901,6 +1901,8 @@ See Info node `(elisp) Integer Basics'."
|
||||
sqlite-available-p sqlitep
|
||||
;; syntax.c
|
||||
standard-syntax-table syntax-table syntax-table-p
|
||||
;; terminal.c
|
||||
frame-initial-p
|
||||
;; thread.c
|
||||
current-thread
|
||||
;; timefns.c
|
||||
|
||||
@@ -195,8 +195,7 @@ the debugger will not be entered."
|
||||
;; backtrace to stdout. This happens for example while
|
||||
;; handling an error in code from early-init.el with
|
||||
;; --debug-init.
|
||||
(and (eq t (framep (selected-frame)))
|
||||
(equal "initial_terminal" (terminal-name)))))
|
||||
(frame-initial-p)))
|
||||
;; Don't let `inhibit-message' get in our way (especially important if
|
||||
;; `non-interactive-frame' evaluated to a non-nil value.
|
||||
(inhibit-message nil)
|
||||
|
||||
@@ -372,6 +372,7 @@ entirely by setting `warning-suppress-types' or
|
||||
(if (bolp)
|
||||
(forward-char -1))
|
||||
(message "%s" (buffer-substring start (point))))))
|
||||
;; Use `frame-initial-p'?
|
||||
((and (daemonp) (eq (selected-frame) terminal-frame))
|
||||
;; Display daemon startup warnings on the first client frame.
|
||||
(letrec ((afterfun
|
||||
|
||||
@@ -493,6 +493,7 @@ there (in decreasing order of priority)."
|
||||
(setq parms (append initial-frame-alist window-system-frame-alist
|
||||
default-frame-alist parms nil))
|
||||
;; Don't enable tab-bar in daemon's initial frame.
|
||||
;; Use `frame-initial-p'?
|
||||
(when (and (daemonp) (eq (selected-frame) terminal-frame))
|
||||
(setq parms (delq (assq 'tab-bar-lines parms) parms)))
|
||||
parms))
|
||||
|
||||
@@ -1370,12 +1370,10 @@ All keyword parameters default to nil."
|
||||
;; frame, as that would only trigger
|
||||
;; warnings.
|
||||
(not
|
||||
(and (daemonp)
|
||||
(equal (terminal-name (frame-terminal
|
||||
frame))
|
||||
"initial_terminal"))))
|
||||
(delete-frame frame)))
|
||||
cleanup-frames)))
|
||||
(and (daemonp) ;; FIXME: Remove `daemonp'?
|
||||
(frame-initial-p frame))))
|
||||
(delete-frame frame)))
|
||||
cleanup-frames)))
|
||||
(maphash (lambda (frame _action) (push frame map)) frameset--action-map)
|
||||
(dolist (frame (sort map
|
||||
;; Minibufferless frames must go first to avoid
|
||||
|
||||
@@ -2496,8 +2496,7 @@ It must accept a buffer as its only required argument.")
|
||||
;; Ignore the initial frame if present. It can happen if
|
||||
;; Emacs was started as a daemon. (bug#53740)
|
||||
(dolist (frame (frame-list))
|
||||
(unless (equal (terminal-name (frame-terminal frame))
|
||||
"initial_terminal")
|
||||
(unless (frame-initial-p frame)
|
||||
(push frame frames)))
|
||||
;; Make the menu of buffers proper.
|
||||
(setq buffers-menu
|
||||
|
||||
@@ -129,6 +129,7 @@ Linum mode is a buffer-local minor mode."
|
||||
;; Note that nowadays, this actually doesn't show line
|
||||
;; numbers in client frames at all, because we visit the
|
||||
;; file before creating the client frame. See bug#35726.
|
||||
;; Use `frame-initial-p'?
|
||||
(and (daemonp) (eq (selected-frame) terminal-frame)))
|
||||
(linum-mode 1)))
|
||||
|
||||
|
||||
@@ -1330,6 +1330,8 @@ Interactively, with a prefix arg, FORCE is t."
|
||||
(buffer (current-buffer)))
|
||||
(cl-labels
|
||||
((visible-buffer-window ()
|
||||
;; This can use `frame-initial-p' once
|
||||
;; we can assume Emacs 31 or later.
|
||||
(and (or (not (daemonp))
|
||||
(not (eq (selected-frame) terminal-frame)))
|
||||
(get-buffer-window (current-buffer))))
|
||||
|
||||
@@ -706,6 +706,7 @@ the `server-process' variable."
|
||||
;; when we can't get user input, which may happen when
|
||||
;; doing emacsclient --eval "(kill-emacs)" in daemon mode.
|
||||
(cond
|
||||
;; Use `frame-initial-p'?
|
||||
((and (daemonp)
|
||||
(null (cdr (frame-list)))
|
||||
(eq (selected-frame) terminal-frame))
|
||||
@@ -1429,6 +1430,7 @@ The following commands are accepted by the client:
|
||||
(or (eq use-current-frame 'always)
|
||||
;; We can't use the Emacs daemon's
|
||||
;; terminal frame.
|
||||
;; Use `frame-initial-p'?
|
||||
(not (and (daemonp)
|
||||
(null (cdr (frame-list)))
|
||||
(eq (selected-frame)
|
||||
@@ -1453,6 +1455,7 @@ The following commands are accepted by the client:
|
||||
;; If there won't be a current frame to use, fall
|
||||
;; back to trying to create a new one.
|
||||
((and use-current-frame
|
||||
;; Use `frame-initial-p'?
|
||||
(daemonp)
|
||||
(null (cdr (frame-list)))
|
||||
(eq (selected-frame) terminal-frame)
|
||||
|
||||
@@ -292,6 +292,7 @@ a list of frames to update."
|
||||
(and (eq auto-resize-tab-bars 'grow-only)
|
||||
(> (frame-parameter frame 'tab-bar-lines) 1))
|
||||
;; Don't enable tab-bar in daemon's initial frame.
|
||||
;; Use `frame-initial-p'?
|
||||
(and (daemonp) (eq frame terminal-frame)))
|
||||
(set-frame-parameter frame 'tab-bar-lines
|
||||
(tab-bar--tab-bar-lines-for-frame frame)))))
|
||||
|
||||
@@ -977,6 +977,7 @@ In the latter case, VC mode is deactivated for this buffer."
|
||||
noninteractive
|
||||
;; Copied from server-start. Seems like there should
|
||||
;; be a better way to ask "can we get user input?"...
|
||||
;; Use `frame-initial-p'?
|
||||
(and (daemonp)
|
||||
(null (cdr (frame-list)))
|
||||
(eq (selected-frame) terminal-frame))
|
||||
|
||||
@@ -509,16 +509,14 @@ enable, ?l to disable)."
|
||||
"Enable xterm mouse tracking on TERMINAL."
|
||||
(when (and xterm-mouse-mode (eq t (terminal-live-p terminal))
|
||||
;; Avoid the initial terminal which is not a termcap device.
|
||||
;; FIXME: is there more elegant way to detect the initial
|
||||
;; terminal?
|
||||
(not (string= (terminal-name terminal) "initial_terminal")))
|
||||
(not (frame-initial-p terminal)))
|
||||
(unless (terminal-parameter terminal 'xterm-mouse-mode)
|
||||
;; Simulate selecting a terminal by selecting one of its frames
|
||||
;; so that we can set the terminal-local `input-decode-map'.
|
||||
;; Use the tty-top-frame to avoid accidentally making an invisible
|
||||
;; child frame visible by selecting it (bug#79960).
|
||||
;; The test for match mode is here because xt-mouse-tests run in
|
||||
;; match mode, and there is no top-frame in that case.
|
||||
;; The test for batch mode is here because xt-mouse-tests run in
|
||||
;; batch mode, and there is no top-frame in that case.
|
||||
(with-selected-frame (if noninteractive
|
||||
(car (frame-list))
|
||||
(tty-top-frame terminal))
|
||||
|
||||
@@ -5693,10 +5693,11 @@ pgtk_focus_changed (gboolean is_enter, int state,
|
||||
|
||||
/* Don't stop displaying the initial startup message
|
||||
for a switch-frame event we don't need. */
|
||||
/* When run as a daemon, Vterminal_frame is always NIL. */
|
||||
/* When run as a daemon, Vterminal_frame is always nil.
|
||||
FIXME: Isn't it actually the other way around? */
|
||||
bufp->ie.arg = (((NILP (Vterminal_frame)
|
||||
|| !FRAME_PGTK_P (XFRAME (Vterminal_frame))
|
||||
|| EQ (Fdaemonp (), Qt))
|
||||
|| IS_DAEMON)
|
||||
&& CONSP (Vframe_list)
|
||||
&& !NILP (XCDR (Vframe_list))) ? Qt : Qnil);
|
||||
bufp->ie.kind = FOCUS_IN_EVENT;
|
||||
|
||||
@@ -244,8 +244,8 @@ decode_live_terminal (Lisp_Object terminal)
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Like decode_terminal, but ensure that the resulting terminal object refers
|
||||
to a text-based terminal device. */
|
||||
/* Like decode_live_terminal, but ensure that the resulting terminal
|
||||
object refers to a text-based terminal device. */
|
||||
|
||||
struct terminal *
|
||||
decode_tty_terminal (Lisp_Object terminal)
|
||||
@@ -479,6 +479,25 @@ return values. */)
|
||||
}
|
||||
}
|
||||
|
||||
DEFUN ("frame-initial-p", Fframe_initial_p, Sframe_initial_p, 0, 1, 0,
|
||||
doc: /* Return non-nil if FRAME is the initial frame.
|
||||
That is, the initial text frame used internally during daemon mode,
|
||||
batch mode, and the early stages of startup.
|
||||
If FRAME is a terminal object, return non-nil if it holds
|
||||
the initial frame. FRAME defaults to the selected frame. */)
|
||||
(Lisp_Object frame)
|
||||
{
|
||||
if (NILP (frame))
|
||||
frame = selected_frame;
|
||||
if (FRAMEP (frame))
|
||||
{
|
||||
struct frame *f = XFRAME (frame);
|
||||
return FRAME_LIVE_P (f) && FRAME_INITIAL_P (f) ? Qt : Qnil;
|
||||
}
|
||||
struct terminal *t = decode_terminal (frame);
|
||||
return t && t->type == output_initial ? Qt : Qnil;
|
||||
}
|
||||
|
||||
DEFUN ("terminal-list", Fterminal_list, Sterminal_list, 0, 0, 0,
|
||||
doc: /* Return a list of all terminal devices. */)
|
||||
(void)
|
||||
@@ -647,8 +666,6 @@ init_initial_terminal (void)
|
||||
emacs_abort ();
|
||||
|
||||
initial_terminal = create_terminal (output_initial, NULL);
|
||||
/* Note: menu-bar.el:menu-bar-update-buffers knows about this
|
||||
special name of the initial terminal. */
|
||||
initial_terminal->name = xstrdup ("initial_terminal");
|
||||
initial_terminal->kboard = initial_kboard;
|
||||
initial_terminal->delete_terminal_hook = &delete_initial_terminal;
|
||||
@@ -688,12 +705,14 @@ or some time later. */);
|
||||
Vdelete_terminal_functions = Qnil;
|
||||
|
||||
DEFSYM (Qterminal_live_p, "terminal-live-p");
|
||||
DEFSYM (Qframe_initial_p, "frame-initial-p");
|
||||
DEFSYM (Qdelete_terminal_functions, "delete-terminal-functions");
|
||||
DEFSYM (Qrun_hook_with_args, "run-hook-with-args");
|
||||
|
||||
defsubr (&Sdelete_terminal);
|
||||
defsubr (&Sframe_terminal);
|
||||
defsubr (&Sterminal_live_p);
|
||||
defsubr (&Sframe_initial_p);
|
||||
defsubr (&Sterminal_list);
|
||||
defsubr (&Sterminal_name);
|
||||
defsubr (&Sterminal_parameters);
|
||||
|
||||
@@ -50,8 +50,7 @@
|
||||
;; `xterm-mouse-mode' doesn't work in the initial
|
||||
;; terminal. Since we can't create a second
|
||||
;; terminal in batch mode, fake it temporarily.
|
||||
(cl-letf (((symbol-function 'terminal-name)
|
||||
(lambda (&optional _terminal) "fake-terminal")))
|
||||
(cl-letf (((symbol-function 'frame-initial-p) #'ignore))
|
||||
(xterm-mouse-mode 1))
|
||||
,@body)
|
||||
(xterm-mouse-mode 0))))
|
||||
|
||||
55
test/src/terminal-tests.el
Normal file
55
test/src/terminal-tests.el
Normal file
@@ -0,0 +1,55 @@
|
||||
;;; terminal-tests.el --- tests for terminal.c -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
|
||||
(ert-deftest frame-initial-p ()
|
||||
"Test `frame-initial-p' behavior."
|
||||
(should-not (frame-initial-p t))
|
||||
(should-not (frame-initial-p (current-buffer)))
|
||||
(should-not (frame-initial-p (selected-window)))
|
||||
;; "Initial frame" implies "initial terminal", and
|
||||
;; no other terminal can have the initial frame.
|
||||
(should-not (xor (equal (terminal-name) "initial_terminal")
|
||||
(frame-initial-p)))
|
||||
;; Initial frame implies its terminal is a termcap-like
|
||||
;; text-mode terminal.
|
||||
(should (or (not (frame-initial-p))
|
||||
(eq (terminal-live-p nil) t)))
|
||||
;; It similarly implies a termcap-like text-mode frame.
|
||||
(should (or (not (frame-initial-p))
|
||||
(eq (frame-live-p (selected-frame)) t)))
|
||||
(dolist (ft (append '(nil) (frame-list) (terminal-list)))
|
||||
(ert-info ((prin1-to-string ft) :prefix "Argument: ")
|
||||
(should-not (xor (equal (terminal-name ft) "initial_terminal")
|
||||
(frame-initial-p ft)))
|
||||
(should (or (not (frame-initial-p ft))
|
||||
(eq (terminal-live-p ft) t)))))
|
||||
(cond (noninteractive
|
||||
;; Batch mode should have an initial frame.
|
||||
(should (any #'frame-initial-p (frame-list)))
|
||||
(should (any #'frame-initial-p (terminal-list))))
|
||||
((not (daemonp))
|
||||
;; Non-daemon interactive mode should have none.
|
||||
(should-not (any #'frame-initial-p (frame-list)))
|
||||
(should-not (any #'frame-initial-p (terminal-list))))))
|
||||
|
||||
;;; terminal-tests.el ends here
|
||||
Reference in New Issue
Block a user