summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Rudalics <rudalics@gmx.at>2019-03-05 10:46:19 +0100
committerMartin Rudalics <rudalics@gmx.at>2019-03-05 10:46:19 +0100
commita552cc21dc324b1be71c181684b0ab2e6cf0a60f (patch)
tree64ba993180246d1837d5a1ef3c30c1c8ed749ac2
parenteb8dbafff11fded9c96294a0680455bcde70882c (diff)
downloademacs-a552cc21dc324b1be71c181684b0ab2e6cf0a60f.tar.gz
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 deleted. * 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.
-rw-r--r--doc/lispref/frames.texi20
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/frame.el81
-rw-r--r--src/frame.c99
4 files changed, 170 insertions, 35 deletions
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 820006a5675..9b3e02f4de0 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -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
diff --git a/etc/NEWS b/etc/NEWS
index fc4e2c57268..3e347b5318a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -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
diff --git a/lisp/frame.el b/lisp/frame.el
index d71a3fe5e8e..cdb2ac4af11 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -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))
- (window-system-frame-alist
- (cdr (assq initial-window-system
- window-system-default-frame-alist))))
+ (let* ((old-buffer (current-buffer))
+ (window-system-frame-alist
+ (cdr (assq initial-window-system
+ window-system-default-frame-alist)))
+ (minibuffer
+ (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)))
- 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)."
parms
nil))
- ;; 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)
+ 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)))
+ 'child-frame)
+ ;; 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
+ (make-frame
+ (append
+ `((display . ,display) (minibuffer . only)
+ (parent-frame . ,frame))
+ minibuffer-frame-alist)))
+ (when (frame-live-p child-frame)
+ ;; Have the 'minibuffer' parameter of our new frame refer to
+ ;; its child frame's root window.
+ (set-frame-parameter
+ 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)))))
diff --git a/src/frame.c b/src/frame.c
index 165ed4a4e52..3d83dc0a0d8 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -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))
+ continue;
+ else if (FRAME_PARENT_FRAME (f1) == f)
+ {
+ if (FRAME_HAS_MINIBUF_P (f1) && !FRAME_HAS_MINIBUF_P (f)
+ && EQ (FRAME_MINIBUF_WINDOW (f), FRAME_MINIBUF_WINDOW (f1)))
+ /* 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. */
+ {
+ Fmodify_frame_parameters
+ (frame1, Fcons (Fcons (Qparent_frame, Qnil), Qnil));
+ minibuffer_child_frame = frame1;
+ }
+ else
+ 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)
- && FRAME_HAS_MINIBUF_P (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;
- if (FRAME_MINIBUF_ONLY_P (f1))
- break;
+ frame_on_same_kboard = frame1;
+ if (FRAME_HAS_MINIBUF_P (f1))
+ {
+ frame_with_minibuf = frame1;
+ if (FRAME_MINIBUF_ONLY_P (f1))
+ break;
+ }
}
-
- 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),
pending_funcalls);
else
- 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))
+ continue;
+ 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. */
+ if (!FRAME_VISIBLE_P (f1) && !FRAME_ICONIFIED_P (f1))
+ 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;
}