mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 17:24:23 +00:00
Add frame identifiers (bug#80138)
A unique frame id is assigned to a new or cloned frame, and reused on an undeleted frame. The id facilitates unambiguous identification among frames that share identical names or titles, deleted frames where a live frame object no longer exists that we can resurrect by id, for example via 'tab-bar-undo-close-tab'. It also aids debugging at the C level using the frame struct member id. Rewrite 'clone-frame' and 'undelete-frame' to not let bind variables that 'make-frame' uses to avoid conflicts with nested 'make-frame' calls, for example via 'after-make-frame-functions'. * lisp/frame.el (clone-frame, undelete-frame): Use 'frame--purify-parameters' to supply parameters explicitly. (undelete-frame--save-deleted-frame): Save frame id for restoration. (undelete-frame): Restore frame id. (frame--purify-parameters): New defun. (make-frame): Assign a new id for a new or cloned frame, reuse for undeleted frame. * src/frame.h (struct frame): Add id member. (frame_next_id): New extern. * src/frame.c (frame_next_id): New global counter. (frame_set_id, frame_set_id_from_params): New function. (Fframe_id): New DEFUN. (syms_of_frame <Sframe_id>): New defsubr. (syms_of_frame <Qinternal_id>): New DEFSYM. (syms_of_frame <frame_internal_parameters>): Add 'Qinternal_id'. * src/androidfns.c (Fx_create_frame): * src/haikufns.c (Fx_create_frame): * src/nsfns.m (Fx_create_frame): * src/pgtkfns.c (Fx_create_frame): * src/w32fns.c (Fx_create_frame): * src/xfns.c (Fx_create_frame): Call 'frame_set_id_from_params'. * doc/lispref/frames.texi: Add documentation. * etc/NEWS: Announce frame id.
This commit is contained in:
parent
52875b51bf
commit
785059a1f7
11 changed files with 171 additions and 16 deletions
|
|
@ -109,6 +109,24 @@ must be a root frame, which means it cannot be a child frame itself
|
|||
descending from it.
|
||||
@end defun
|
||||
|
||||
@defun frame-id &optional frame
|
||||
This function returns the unique identifier of a frame, an integer,
|
||||
assigned to @var{frame}. If @var{frame} is @code{nil} or unspecified,
|
||||
it defaults to the selected frame (@pxref{Input Focus}). This can be
|
||||
used to unambiguously identify a frame in a context where you do not or
|
||||
cannot use a frame object.
|
||||
|
||||
A frame undeleted using @command{undelete-frame} will retain its
|
||||
identifier. A frame cloned using @command{clone-frame} will not retain
|
||||
its original identifier. @xref{Frame Commands,,,emacs, the Emacs
|
||||
Manual}.
|
||||
|
||||
Frame identifiers are not persisted using the desktop library
|
||||
(@pxref{Desktop Save Mode}), @command{frameset-to-register}, or
|
||||
@code{frameset-save}, and each of their restored frames will bear a new
|
||||
unique id.
|
||||
@end defun
|
||||
|
||||
@menu
|
||||
* Creating Frames:: Creating additional frames.
|
||||
* Multiple Terminals:: Displaying on several different devices.
|
||||
|
|
|
|||
8
etc/NEWS
8
etc/NEWS
|
|
@ -500,6 +500,14 @@ These are useful if you need to detect a cloned frame or undeleted frame
|
|||
in hooks like 'after-make-frame-functions' and
|
||||
'server-after-make-frame-hook'.
|
||||
|
||||
---
|
||||
*** Frames now have unique ids and the new function 'frame-id'.
|
||||
Each non-tooltip frame is assigned a unique integer id. This allows you
|
||||
to unambiguously identify frames even if they share the same name or
|
||||
title. When 'undelete-frame-mode' is enabled, each deleted frame's id
|
||||
is stored for resurrection. The function 'frame-id' returns a frame's
|
||||
id (in C, use the frame struct member id).
|
||||
|
||||
** Mode Line
|
||||
|
||||
+++
|
||||
|
|
|
|||
|
|
@ -951,24 +951,24 @@ and lines for the clone.
|
|||
|
||||
FRAME defaults to the selected frame. The frame is created on the
|
||||
same terminal as FRAME. If the terminal is a text-only terminal then
|
||||
also select the new frame."
|
||||
also select the new frame.
|
||||
|
||||
A cloned frame is assigned a new frame ID. See `frame-id'."
|
||||
(interactive (list (selected-frame) current-prefix-arg))
|
||||
(let* ((frame (or frame (selected-frame)))
|
||||
(windows (unless no-windows
|
||||
(window-state-get (frame-root-window frame))))
|
||||
(default-frame-alist
|
||||
(parameters
|
||||
(append `((cloned-from . ,frame))
|
||||
(seq-remove (lambda (elem)
|
||||
(memq (car elem) frame-internal-parameters))
|
||||
(frame-parameters frame))))
|
||||
(frame--purify-parameters (frame-parameters frame))))
|
||||
new-frame)
|
||||
(when (and frame-resize-pixelwise
|
||||
(display-graphic-p frame))
|
||||
(push (cons 'width (cons 'text-pixels (frame-text-width frame)))
|
||||
default-frame-alist)
|
||||
parameters)
|
||||
(push (cons 'height (cons 'text-pixels (frame-text-height frame)))
|
||||
default-frame-alist))
|
||||
(setq new-frame (make-frame))
|
||||
parameters))
|
||||
(setq new-frame (make-frame parameters))
|
||||
(when windows
|
||||
(window-state-put windows (frame-root-window new-frame) 'safe))
|
||||
(unless (display-graphic-p frame)
|
||||
|
|
@ -995,6 +995,24 @@ frame, unless you add them to the hook in your early-init file.")
|
|||
|
||||
(defvar x-display-name)
|
||||
|
||||
(defun frame--purify-parameters (parameters)
|
||||
"Return PARAMETERS without internals and ignoring unset parameters.
|
||||
Use this helper function so that `make-frame' does not override any
|
||||
parameters.
|
||||
|
||||
In the return value, assign nil to each parameter in
|
||||
`default-frame-alist', `window-system-default-frame-alist',
|
||||
`frame-inherited-parameters', which is not in PARAMETERS, and remove all
|
||||
parameters in `frame-internal-parameters' from PARAMETERS."
|
||||
(dolist (p (append default-frame-alist
|
||||
window-system-default-frame-alist
|
||||
frame-inherited-parameters))
|
||||
(unless (assq (car p) parameters)
|
||||
(push (cons (car p) nil) parameters)))
|
||||
(seq-remove (lambda (elem)
|
||||
(memq (car elem) frame-internal-parameters))
|
||||
parameters))
|
||||
|
||||
(defun make-frame (&optional parameters)
|
||||
"Return a newly created frame displaying the current buffer.
|
||||
Optional argument PARAMETERS is an alist of frame parameters for
|
||||
|
|
@ -1094,6 +1112,12 @@ current buffer even if it is hidden."
|
|||
(setq params (cons '(minibuffer)
|
||||
(delq (assq 'minibuffer params) params))))
|
||||
|
||||
;; Let the `frame-creation-function' apparatus assign a new frame id
|
||||
;; for a new or cloned frame. For an undeleted frame, send the old
|
||||
;; id via a frame parameter.
|
||||
(when-let* ((id (cdr (assq 'undeleted params))))
|
||||
(push (cons 'frame-id id) params))
|
||||
|
||||
;; Now make the frame.
|
||||
(run-hooks 'before-make-frame-hook)
|
||||
|
||||
|
|
@ -1125,7 +1149,7 @@ current buffer even if it is hidden."
|
|||
;; buffers for these windows were set (Bug#79606).
|
||||
(let* ((root (frame-root-window frame))
|
||||
(buffer (window-buffer root)))
|
||||
(with-current-buffer buffer
|
||||
(with-current-buffer buffer
|
||||
(set-window-fringes
|
||||
root left-fringe-width right-fringe-width fringes-outside-margins)
|
||||
(set-window-scroll-bars
|
||||
|
|
@ -1135,7 +1159,7 @@ current buffer even if it is hidden."
|
|||
root left-margin-width right-margin-width)))
|
||||
(let* ((mini (minibuffer-window frame))
|
||||
(buffer (window-buffer mini)))
|
||||
(when (eq (window-frame mini) frame)
|
||||
(when (eq (window-frame mini) frame)
|
||||
(with-current-buffer buffer
|
||||
(set-window-fringes
|
||||
mini left-fringe-width right-fringe-width fringes-outside-margins)
|
||||
|
|
@ -3126,7 +3150,8 @@ Only the 16 most recently deleted frames are saved."
|
|||
;; to restore a graphical frame.
|
||||
(and (eq (car elem) 'display) (not (display-graphic-p)))))
|
||||
(frame-parameters frame))
|
||||
(window-state-get (frame-root-window frame)))
|
||||
(window-state-get (frame-root-window frame))
|
||||
(frame-id frame))
|
||||
undelete-frame--deleted-frames))
|
||||
(if (> (length undelete-frame--deleted-frames) 16)
|
||||
(setq undelete-frame--deleted-frames
|
||||
|
|
@ -3149,7 +3174,9 @@ Without a prefix argument, undelete the most recently deleted
|
|||
frame.
|
||||
With a numerical prefix argument ARG between 1 and 16, where 1 is
|
||||
most recently deleted frame, undelete the ARGth deleted frame.
|
||||
When called from Lisp, returns the new frame."
|
||||
When called from Lisp, returns the new frame.
|
||||
|
||||
An undeleted frame retains its original frame ID. See `frame-id'."
|
||||
(interactive "P")
|
||||
(if (not undelete-frame-mode)
|
||||
(user-error "Undelete-Frame mode is disabled")
|
||||
|
|
@ -3170,10 +3197,11 @@ When called from Lisp, returns the new frame."
|
|||
(if graphic "graphic" "non-graphic"))
|
||||
(setq undelete-frame--deleted-frames
|
||||
(delq frame-data undelete-frame--deleted-frames))
|
||||
(let* ((default-frame-alist
|
||||
(append `((undeleted . t))
|
||||
(nth 1 frame-data)))
|
||||
(frame (make-frame)))
|
||||
(let* ((parameters
|
||||
;; `undeleted' signals to `make-frame' to reuse its id.
|
||||
(append `((undeleted . ,(nth 3 frame-data)))
|
||||
(frame--purify-parameters (nth 1 frame-data))))
|
||||
(frame (make-frame parameters)))
|
||||
(window-state-put (nth 2 frame-data) (frame-root-window frame) 'safe)
|
||||
(select-frame-set-input-focus frame)
|
||||
frame))))))))
|
||||
|
|
|
|||
|
|
@ -858,6 +858,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
|
|||
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
frame_set_id_from_params (f, parms);
|
||||
|
||||
f->terminal = dpyinfo->terminal;
|
||||
|
||||
f->output_method = output_android;
|
||||
|
|
|
|||
82
src/frame.c
82
src/frame.c
|
|
@ -337,6 +337,83 @@ return values. */)
|
|||
: Qnil);
|
||||
}
|
||||
|
||||
|
||||
/* Frame id. */
|
||||
|
||||
EMACS_UINT frame_next_id = 1; /* 0 indicates no id (yet) set. */
|
||||
|
||||
DEFUN ("frame-id", Fframe_id, Sframe_id, 0, 1, 0,
|
||||
doc: /* Return FRAME's id.
|
||||
If FRAME is nil, use the selected frame.
|
||||
Return nil if the id has not been set. */)
|
||||
(Lisp_Object frame)
|
||||
{
|
||||
if (NILP (frame))
|
||||
frame = selected_frame;
|
||||
struct frame *f = decode_live_frame (frame);
|
||||
if (f->id == 0)
|
||||
return Qnil;
|
||||
else
|
||||
return make_fixnum (f->id);
|
||||
}
|
||||
|
||||
/** frame_set_id: Set frame F's id to ID.
|
||||
|
||||
If ID is 0 and F's ID is 0, use frame_next_id and increment it,
|
||||
otherwise, use ID.
|
||||
|
||||
Signal an error if ID >= frame_next_id.
|
||||
Signal an error if ID is in use on another live frame.
|
||||
|
||||
Return ID if it was used, 0 otherwise. */
|
||||
EMACS_UINT
|
||||
frame_set_id (struct frame *f, EMACS_UINT id)
|
||||
{
|
||||
if (id >= frame_next_id)
|
||||
error ("Specified frame ID unassigned");
|
||||
|
||||
if (id > 0)
|
||||
{
|
||||
eassume (CONSP (Vframe_list));
|
||||
Lisp_Object frame, tail = Qnil;
|
||||
FOR_EACH_FRAME (tail, frame)
|
||||
{
|
||||
if (id == XFRAME (frame)->id)
|
||||
error ("Specified frame ID already in use");
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
if (f->id != 0)
|
||||
return 0;
|
||||
else
|
||||
f->id = frame_next_id++;
|
||||
else
|
||||
f->id = id;
|
||||
return f->id;
|
||||
}
|
||||
|
||||
/** frame_set_id_from_params: Set frame F's id from params, if present.
|
||||
|
||||
Call frame_set_id to using the frame parameter 'frame-id, if present
|
||||
and a valid positive integer greater than 0, otherwise use 0.
|
||||
|
||||
Return frame_set_id's return value. */
|
||||
EMACS_UINT
|
||||
frame_set_id_from_params (struct frame *f, Lisp_Object params)
|
||||
{
|
||||
EMACS_UINT id = 0;
|
||||
Lisp_Object param_id = Fcdr (Fassq (Qframe_id, params));
|
||||
if (TYPE_RANGED_FIXNUMP (int, param_id))
|
||||
{
|
||||
EMACS_INT id_1 = XFIXNUM (param_id);
|
||||
if (id_1 > 0)
|
||||
id = (EMACS_UINT) id_1;
|
||||
}
|
||||
return frame_set_id (f, id);
|
||||
}
|
||||
|
||||
|
||||
DEFUN ("window-system", Fwindow_system, Swindow_system, 0, 1, 0,
|
||||
doc: /* The name of the window system that FRAME is displaying through.
|
||||
The value is a symbol:
|
||||
|
|
@ -1358,6 +1435,7 @@ make_initial_frame (void)
|
|||
|
||||
f = make_frame (true);
|
||||
XSETFRAME (frame, f);
|
||||
frame_set_id (f, 0);
|
||||
|
||||
Vframe_list = Fcons (frame, Vframe_list);
|
||||
|
||||
|
|
@ -1742,6 +1820,7 @@ affects all frames on the same terminal device. */)
|
|||
frames don't obscure other frames. */
|
||||
Lisp_Object parent = Fcdr (Fassq (Qparent_frame, parms));
|
||||
struct frame *f = make_terminal_frame (t, parent, parms);
|
||||
frame_set_id_from_params (f, parms);
|
||||
|
||||
if (!noninteractive)
|
||||
init_frame_faces (f);
|
||||
|
|
@ -7195,6 +7274,7 @@ syms_of_frame (void)
|
|||
DEFSYM (Qfont_parameter, "font-parameter");
|
||||
DEFSYM (Qforce, "force");
|
||||
DEFSYM (Qinhibit, "inhibit");
|
||||
DEFSYM (Qframe_id, "frame-id");
|
||||
DEFSYM (Qcloned_from, "cloned-from");
|
||||
DEFSYM (Qundeleted, "undeleted");
|
||||
|
||||
|
|
@ -7581,6 +7661,7 @@ allow `make-frame' to show the current buffer even if its hidden. */);
|
|||
#else
|
||||
frame_internal_parameters = list3 (Qname, Qparent_id, Qwindow_id);
|
||||
#endif
|
||||
frame_internal_parameters = Fcons (Qframe_id, frame_internal_parameters);
|
||||
frame_internal_parameters = Fcons (Qcloned_from, frame_internal_parameters);
|
||||
frame_internal_parameters = Fcons (Qundeleted, frame_internal_parameters);
|
||||
|
||||
|
|
@ -7607,6 +7688,7 @@ The default is \\+`inhibit' in NS builds and nil everywhere else. */);
|
|||
alter_fullscreen_frames = Qnil;
|
||||
#endif
|
||||
|
||||
defsubr (&Sframe_id);
|
||||
defsubr (&Sframep);
|
||||
defsubr (&Sframe_live_p);
|
||||
defsubr (&Swindow_system);
|
||||
|
|
|
|||
|
|
@ -292,6 +292,9 @@ struct frame
|
|||
struct image_cache *image_cache;
|
||||
#endif /* HAVE_WINDOW_SYSTEM */
|
||||
|
||||
/* Unique frame id. */
|
||||
EMACS_UINT id;
|
||||
|
||||
/* Tab-bar item index of the item on which a mouse button was pressed. */
|
||||
int last_tab_bar_item;
|
||||
|
||||
|
|
@ -1415,6 +1418,10 @@ FRAME_PARENT_FRAME (struct frame *f)
|
|||
#define AUTO_FRAME_ARG(name, parameter, value) \
|
||||
AUTO_LIST1 (name, AUTO_CONS_EXPR (parameter, value))
|
||||
|
||||
extern EMACS_UINT frame_next_id;
|
||||
extern EMACS_UINT frame_set_id (struct frame *f, EMACS_UINT id);
|
||||
extern EMACS_UINT frame_set_id_from_params (struct frame *f, Lisp_Object params);
|
||||
|
||||
/* False means there are no visible garbaged frames. */
|
||||
extern bool frame_garbaged;
|
||||
|
||||
|
|
|
|||
|
|
@ -750,6 +750,8 @@ haiku_create_frame (Lisp_Object parms)
|
|||
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
frame_set_id_from_params (f, parms);
|
||||
|
||||
f->terminal = dpyinfo->terminal;
|
||||
|
||||
f->output_method = output_haiku;
|
||||
|
|
|
|||
|
|
@ -1262,6 +1262,8 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
|
|||
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
frame_set_id_from_params (f, parms);
|
||||
|
||||
f->terminal = dpyinfo->terminal;
|
||||
|
||||
f->output_method = output_ns;
|
||||
|
|
|
|||
|
|
@ -1283,6 +1283,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, 1, 1, 0,
|
|||
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
frame_set_id_from_params (f, parms);
|
||||
|
||||
f->terminal = dpyinfo->terminal;
|
||||
|
||||
f->output_method = output_pgtk;
|
||||
|
|
|
|||
|
|
@ -6315,6 +6315,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
|
|||
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
frame_set_id_from_params (f, parameters);
|
||||
|
||||
parent_frame = gui_display_get_arg (dpyinfo, parameters, Qparent_frame,
|
||||
NULL, NULL,
|
||||
RES_TYPE_SYMBOL);
|
||||
|
|
|
|||
|
|
@ -5020,6 +5020,8 @@ This function is an internal primitive--use `make-frame' instead. */)
|
|||
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
frame_set_id_from_params (f, parms);
|
||||
|
||||
f->terminal = dpyinfo->terminal;
|
||||
|
||||
f->output_method = output_x_window;
|
||||
|
|
|
|||
Loading…
Reference in a new issue