Commit a552cc21 authored by Martin Rudalics's avatar Martin Rudalics

Fix handling of minibuffer-only child frames (Bug#33498)

* doc/lispref/frames.texi (Buffer Parameters): Describe how to
make a minibuffer-only child frame.
(Child Frames): Describe how minbuffer child frames are
* src/frame.c (delete_frame): Handle deletion of minibuffer
child frames (Bug#33498).  In the course, fix reassigning of
'default-minibuffer-frame' with minibuffer-only frames.
* lisp/frame.el (frame-notice-user-settings): Handle creation of
initial minibuffer-only child frame.
(make-frame): Handle creation of frame with a minibuffer-only
child frame.
parent eb8dbaff
Pipeline #920 passed with stage
in 52 minutes and 16 seconds
......@@ -1884,6 +1884,12 @@ minibuffer window to @code{t} and vice-versa, or from @code{t} to
@code{nil}. If the parameter specifies a minibuffer window already,
setting it to @code{nil} has no effect.
The special value @code{child-frame} means to make a minibuffer-only
child frame (@pxref{Child Frames}) whose parent becomes the frame
created. As if specified as @code{nil}, Emacs will set this parameter
to the minibuffer window of the child frame but will not select the
child frame after its creation.
@vindex buffer-predicate@r{, a frame parameter}
@item buffer-predicate
The buffer-predicate function for this frame. The function
......@@ -3214,9 +3220,17 @@ top-level frame which also always appears on top of its parent
window---the desktop's root window. When a parent frame is iconified or
made invisible (@pxref{Visibility of Frames}), its child frames are made
invisible. When a parent frame is deiconified or made visible, its
child frames are made visible. When a parent frame is about to be
deleted (@pxref{Deleting Frames}), its child frames are recursively
deleted before it.
child frames are made visible.
When a parent frame is about to be deleted (@pxref{Deleting
Frames}), its child frames are recursively deleted before it. There
is one exception to this rule: When the child frame serves as a
surrogate minibuffer frame (@pxref{Minibuffers and Frames}) for
another frame, it is retained until the parent frame has been deleted.
If, at this time, no remaining frame uses the child frame as its
minibuffer frame, Emacs will try to delete the child frame too. If
that deletion fails for whatever reason, the child frame is made a
top-level frame.
Whether a child frame can have a menu or tool bar is window-system or
window manager dependent. Most window-systems explicitly disallow menus
......@@ -1166,6 +1166,11 @@ the 128...255 range, as expected.
*** New command 'make-frame-on-monitor' makes a frame on the specified monitor.
*** New value of 'minibuffer' frame parameter 'child-frame'.
This allows to create and parent immediately a minibuffer-only child
frame when making a frame.
* New Modes and Packages in Emacs 27.1
......@@ -316,10 +316,15 @@ there (in decreasing order of priority)."
;; want to use save-excursion here, because that may also try to set
;; the buffer of the selected window, which fails when the selected
;; window is the minibuffer.
(let ((old-buffer (current-buffer))
(cdr (assq initial-window-system
(let* ((old-buffer (current-buffer))
(cdr (assq initial-window-system
(cdr (or (assq 'minibuffer initial-frame-alist)
(assq 'minibuffer window-system-frame-alist)
(assq 'minibuffer default-frame-alist)
'(minibuffer . t)))))
(when (and frame-notice-user-settings
(null frame-initial-frame))
......@@ -410,11 +415,7 @@ there (in decreasing order of priority)."
;; default-frame-alist in the parameters of the screen we
;; create here, so that its new value, gleaned from the user's
;; init file, will be applied to the existing screen.
(if (not (eq (cdr (or (assq 'minibuffer initial-frame-alist)
(assq 'minibuffer window-system-frame-alist)
(assq 'minibuffer default-frame-alist)
'(minibuffer . t)))
(if (not (eq minibuffer t))
;; Create the new frame.
(let (parms new)
;; MS-Windows needs this to avoid inflooping below.
......@@ -442,7 +443,15 @@ there (in decreasing order of priority)."
;; Get rid of `reverse', because that was handled
(when (eq minibuffer 'child-frame)
;; When the minibuffer shall be shown in a child frame,
;; remove the 'minibuffer' parameter from PARMS. It
;; will get assigned by the usual routines to the child
;; frame's root window below.
(setq parms (cons '(minibuffer)
(delq (assq 'minibuffer parms) parms))))
;; Get rid of `reverse', because that was handled
;; when we first made the frame.
(setq parms (cons '(reverse) (delq (assq 'reverse parms) parms)))
......@@ -465,7 +474,18 @@ there (in decreasing order of priority)."
;; the only frame with a minibuffer. If it is, create a
;; new one.
(or (delq frame-initial-frame (minibuffer-frame-list))
(make-initial-minibuffer-frame nil))
(and (eq minibuffer 'child-frame)
;; Create a minibuffer child frame and parent it
;; immediately. Take any other parameters for
;; the child frame from 'minibuffer-frame-list'.
(let* ((minibuffer-frame-alist
(cons `(parent-frame . ,new) minibuffer-frame-alist)))
(make-initial-minibuffer-frame nil)
;; With a minibuffer child frame we do not want
;; to select the minibuffer frame initially as
;; we do for standard minibuffer-only frames.
(select-frame new)))
(make-initial-minibuffer-frame nil))
;; If the initial frame is serving as a surrogate
;; minibuffer frame for any frames, we need to wean them
......@@ -795,7 +815,7 @@ the new frame according to its own rules."
(t window-system)))
(oldframe (selected-frame))
(params parameters)
frame child-frame)
(unless (get w 'window-system-initialized)
(let ((window-system w)) ;Hack attack!
......@@ -811,17 +831,44 @@ the new frame according to its own rules."
(dolist (p default-frame-alist)
(unless (assq (car p) params)
(push p params)))
;; Now make the frame.
(run-hooks 'before-make-frame-hook)
;; (setq frame-size-history '(1000))
(setq frame (let ((window-system w)) ;Hack attack!
(when (eq (cdr (or (assq 'minibuffer params) '(minibuffer . t)))
;; If the 'minibuffer' parameter equals 'child-frame' make a
;; frame without minibuffer first using the root window of
;; 'default-minibuffer-frame' as its minibuffer window
(setq child-frame t)
(setq params (cons '(minibuffer)
(delq (assq 'minibuffer params) params))))
;; Now make the frame.
(run-hooks 'before-make-frame-hook)
(setq frame (let ((window-system w)) ; Hack attack!
(frame-creation-function params)))
(when child-frame
;; When we want to equip the new frame with a minibuffer-only
;; child frame, make that frame and reparent it immediately.
(setq child-frame
`((display . ,display) (minibuffer . only)
(parent-frame . ,frame))
(when (frame-live-p child-frame)
;; Have the 'minibuffer' parameter of our new frame refer to
;; its child frame's root window.
frame 'minibuffer (frame-root-window child-frame))))
(normal-erase-is-backspace-setup-frame frame)
;; Inherit the original frame's parameters.
;; Inherit original frame's parameters unless they are overridden
;; by explicit parameters.
(dolist (param frame-inherited-parameters)
(unless (assq param parameters) ;Overridden by explicit parameters.
(unless (assq param parameters)
(let ((val (frame-parameter oldframe param)))
(when val (set-frame-parameter frame param val)))))
......@@ -1849,6 +1849,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
Lisp_Object frames, frame1;
int minibuffer_selected, is_tooltip_frame;
bool nochild = !FRAME_PARENT_FRAME (f);
Lisp_Object minibuffer_child_frame = Qnil;
if (!FRAME_LIVE_P (f))
return Qnil;
......@@ -1865,13 +1866,33 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
/* Softly delete all frames with this frame as their parent frame or
as their `delete-before' frame parameter value. */
FOR_EACH_FRAME (frames, frame1)
if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f
struct frame *f1 = XFRAME (frame1);
if (EQ (frame1, frame) || FRAME_TOOLTIP_P (f1))
else if (FRAME_PARENT_FRAME (f1) == f)
/* frame1 owns frame's minibuffer window so we must not
delete it here to avoid a surrogate minibuffer error.
Unparent frame1 and make it a top-level frame. */
(frame1, Fcons (Fcons (Qparent_frame, Qnil), Qnil));
minibuffer_child_frame = frame1;
delete_frame (frame1, Qnil);
else if (nochild
&& EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame))
/* Process `delete-before' parameter iff FRAME is not a child
frame. This avoids that we enter an infinite chain of mixed
dependencies. */
|| (nochild
&& EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame)))
delete_frame (frame1, Qnil);
delete_frame (frame1, Qnil);
/* Does this frame have a minibuffer, and is it the surrogate
minibuffer for any other frame? */
......@@ -2136,18 +2157,27 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
struct frame *f1 = XFRAME (frame1);
/* Consider only frames on the same kboard
and only those with minibuffers. */
if (kb == FRAME_KBOARD (f1)
/* Set frame_on_same_kboard to frame1 if it is on the same
keyboard. Set frame_with_minibuf to frame1 if it also
has a minibuffer. Leave the loop immediately if frame1
is also minibuffer-only.
Emacs 26 does _not_ set frame_on_same_kboard here when it
finds a minibuffer-only frame and subsequently fails to
set default_minibuffer_frame below. Not a great deal and
never noticed since make_frame_without_minibuffer creates
a new minibuffer frame in that case (which can be a minor
annoyance though). To consider for Emacs 26.3. */
if (kb == FRAME_KBOARD (f1))
frame_with_minibuf = frame1;
frame_on_same_kboard = frame1;
frame_with_minibuf = frame1;
if (kb == FRAME_KBOARD (f1))
frame_on_same_kboard = frame1;
if (!NILP (frame_on_same_kboard))
......@@ -2180,7 +2210,46 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
= Fcons (list3 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame),
safe_call2 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame);
safe_call2 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame);
if (!NILP (minibuffer_child_frame))
/* If minibuffer_child_frame is non-nil, it was FRAME's minibuffer
child frame. Delete it unless it's also the minibuffer frame
of another frame in which case we make sure it's visible. */
struct frame *f1 = XFRAME (minibuffer_child_frame);
if (FRAME_LIVE_P (f1))
Lisp_Object window1 = FRAME_ROOT_WINDOW (f1);
Lisp_Object frame2;
FOR_EACH_FRAME (frames, frame2)
struct frame *f2 = XFRAME (frame2);
if (EQ (frame2, minibuffer_child_frame) || FRAME_TOOLTIP_P (f2))
else if (EQ (FRAME_MINIBUF_WINDOW (f2), window1))
/* minibuffer_child_frame serves as minibuffer frame
for at least one other frame - so make it visible
and quit. */
Fmake_frame_visible (frame1);
return Qnil;
/* No other frame found that uses minibuffer_child_frame as
minibuffer frame. If FORCE is Qnoelisp or there are
other visible frames left, delete minibuffer_child_frame
since it presumably was used by FRAME only. */
if (EQ (force, Qnoelisp) || other_frames (f1, false, !NILP (force)))
delete_frame (minibuffer_child_frame, Qnoelisp);
return Qnil;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment