diff options
| author | Martin Rudalics | 2019-03-05 10:46:19 +0100 |
|---|---|---|
| committer | Martin Rudalics | 2019-03-05 10:46:19 +0100 |
| commit | a552cc21dc324b1be71c181684b0ab2e6cf0a60f (patch) | |
| tree | 64ba993180246d1837d5a1ef3c30c1c8ed749ac2 | |
| parent | eb8dbafff11fded9c96294a0680455bcde70882c (diff) | |
| download | emacs-a552cc21dc324b1be71c181684b0ab2e6cf0a60f.tar.gz emacs-a552cc21dc324b1be71c181684b0ab2e6cf0a60f.zip | |
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.texi | 20 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/frame.el | 81 | ||||
| -rw-r--r-- | src/frame.c | 99 |
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 | |||
| 1884 | @code{nil}. If the parameter specifies a minibuffer window already, | 1884 | @code{nil}. If the parameter specifies a minibuffer window already, |
| 1885 | setting it to @code{nil} has no effect. | 1885 | setting it to @code{nil} has no effect. |
| 1886 | 1886 | ||
| 1887 | The special value @code{child-frame} means to make a minibuffer-only | ||
| 1888 | child frame (@pxref{Child Frames}) whose parent becomes the frame | ||
| 1889 | created. As if specified as @code{nil}, Emacs will set this parameter | ||
| 1890 | to the minibuffer window of the child frame but will not select the | ||
| 1891 | child frame after its creation. | ||
| 1892 | |||
| 1887 | @vindex buffer-predicate@r{, a frame parameter} | 1893 | @vindex buffer-predicate@r{, a frame parameter} |
| 1888 | @item buffer-predicate | 1894 | @item buffer-predicate |
| 1889 | The buffer-predicate function for this frame. The function | 1895 | 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 | |||
| 3214 | window---the desktop's root window. When a parent frame is iconified or | 3220 | window---the desktop's root window. When a parent frame is iconified or |
| 3215 | made invisible (@pxref{Visibility of Frames}), its child frames are made | 3221 | made invisible (@pxref{Visibility of Frames}), its child frames are made |
| 3216 | invisible. When a parent frame is deiconified or made visible, its | 3222 | invisible. When a parent frame is deiconified or made visible, its |
| 3217 | child frames are made visible. When a parent frame is about to be | 3223 | child frames are made visible. |
| 3218 | deleted (@pxref{Deleting Frames}), its child frames are recursively | 3224 | |
| 3219 | deleted before it. | 3225 | When a parent frame is about to be deleted (@pxref{Deleting |
| 3226 | Frames}), its child frames are recursively deleted before it. There | ||
| 3227 | is one exception to this rule: When the child frame serves as a | ||
| 3228 | surrogate minibuffer frame (@pxref{Minibuffers and Frames}) for | ||
| 3229 | another frame, it is retained until the parent frame has been deleted. | ||
| 3230 | If, at this time, no remaining frame uses the child frame as its | ||
| 3231 | minibuffer frame, Emacs will try to delete the child frame too. If | ||
| 3232 | that deletion fails for whatever reason, the child frame is made a | ||
| 3233 | top-level frame. | ||
| 3220 | 3234 | ||
| 3221 | Whether a child frame can have a menu or tool bar is window-system or | 3235 | Whether a child frame can have a menu or tool bar is window-system or |
| 3222 | window manager dependent. Most window-systems explicitly disallow menus | 3236 | window manager dependent. Most window-systems explicitly disallow menus |
| @@ -1166,6 +1166,11 @@ the 128...255 range, as expected. | |||
| 1166 | +++ | 1166 | +++ |
| 1167 | *** New command 'make-frame-on-monitor' makes a frame on the specified monitor. | 1167 | *** New command 'make-frame-on-monitor' makes a frame on the specified monitor. |
| 1168 | 1168 | ||
| 1169 | +++ | ||
| 1170 | *** New value of 'minibuffer' frame parameter 'child-frame'. | ||
| 1171 | This allows to create and parent immediately a minibuffer-only child | ||
| 1172 | frame when making a frame. | ||
| 1173 | |||
| 1169 | 1174 | ||
| 1170 | * New Modes and Packages in Emacs 27.1 | 1175 | * New Modes and Packages in Emacs 27.1 |
| 1171 | 1176 | ||
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)." | |||
| 316 | ;; want to use save-excursion here, because that may also try to set | 316 | ;; want to use save-excursion here, because that may also try to set |
| 317 | ;; the buffer of the selected window, which fails when the selected | 317 | ;; the buffer of the selected window, which fails when the selected |
| 318 | ;; window is the minibuffer. | 318 | ;; window is the minibuffer. |
| 319 | (let ((old-buffer (current-buffer)) | 319 | (let* ((old-buffer (current-buffer)) |
| 320 | (window-system-frame-alist | 320 | (window-system-frame-alist |
| 321 | (cdr (assq initial-window-system | 321 | (cdr (assq initial-window-system |
| 322 | window-system-default-frame-alist)))) | 322 | window-system-default-frame-alist))) |
| 323 | (minibuffer | ||
| 324 | (cdr (or (assq 'minibuffer initial-frame-alist) | ||
| 325 | (assq 'minibuffer window-system-frame-alist) | ||
| 326 | (assq 'minibuffer default-frame-alist) | ||
| 327 | '(minibuffer . t))))) | ||
| 323 | 328 | ||
| 324 | (when (and frame-notice-user-settings | 329 | (when (and frame-notice-user-settings |
| 325 | (null frame-initial-frame)) | 330 | (null frame-initial-frame)) |
| @@ -410,11 +415,7 @@ there (in decreasing order of priority)." | |||
| 410 | ;; default-frame-alist in the parameters of the screen we | 415 | ;; default-frame-alist in the parameters of the screen we |
| 411 | ;; create here, so that its new value, gleaned from the user's | 416 | ;; create here, so that its new value, gleaned from the user's |
| 412 | ;; init file, will be applied to the existing screen. | 417 | ;; init file, will be applied to the existing screen. |
| 413 | (if (not (eq (cdr (or (assq 'minibuffer initial-frame-alist) | 418 | (if (not (eq minibuffer t)) |
| 414 | (assq 'minibuffer window-system-frame-alist) | ||
| 415 | (assq 'minibuffer default-frame-alist) | ||
| 416 | '(minibuffer . t))) | ||
| 417 | t)) | ||
| 418 | ;; Create the new frame. | 419 | ;; Create the new frame. |
| 419 | (let (parms new) | 420 | (let (parms new) |
| 420 | ;; MS-Windows needs this to avoid inflooping below. | 421 | ;; MS-Windows needs this to avoid inflooping below. |
| @@ -442,7 +443,15 @@ there (in decreasing order of priority)." | |||
| 442 | parms | 443 | parms |
| 443 | nil)) | 444 | nil)) |
| 444 | 445 | ||
| 445 | ;; Get rid of `reverse', because that was handled | 446 | (when (eq minibuffer 'child-frame) |
| 447 | ;; When the minibuffer shall be shown in a child frame, | ||
| 448 | ;; remove the 'minibuffer' parameter from PARMS. It | ||
| 449 | ;; will get assigned by the usual routines to the child | ||
| 450 | ;; frame's root window below. | ||
| 451 | (setq parms (cons '(minibuffer) | ||
| 452 | (delq (assq 'minibuffer parms) parms)))) | ||
| 453 | |||
| 454 | ;; Get rid of `reverse', because that was handled | ||
| 446 | ;; when we first made the frame. | 455 | ;; when we first made the frame. |
| 447 | (setq parms (cons '(reverse) (delq (assq 'reverse parms) parms))) | 456 | (setq parms (cons '(reverse) (delq (assq 'reverse parms) parms))) |
| 448 | 457 | ||
| @@ -465,7 +474,18 @@ there (in decreasing order of priority)." | |||
| 465 | ;; the only frame with a minibuffer. If it is, create a | 474 | ;; the only frame with a minibuffer. If it is, create a |
| 466 | ;; new one. | 475 | ;; new one. |
| 467 | (or (delq frame-initial-frame (minibuffer-frame-list)) | 476 | (or (delq frame-initial-frame (minibuffer-frame-list)) |
| 468 | (make-initial-minibuffer-frame nil)) | 477 | (and (eq minibuffer 'child-frame) |
| 478 | ;; Create a minibuffer child frame and parent it | ||
| 479 | ;; immediately. Take any other parameters for | ||
| 480 | ;; the child frame from 'minibuffer-frame-list'. | ||
| 481 | (let* ((minibuffer-frame-alist | ||
| 482 | (cons `(parent-frame . ,new) minibuffer-frame-alist))) | ||
| 483 | (make-initial-minibuffer-frame nil) | ||
| 484 | ;; With a minibuffer child frame we do not want | ||
| 485 | ;; to select the minibuffer frame initially as | ||
| 486 | ;; we do for standard minibuffer-only frames. | ||
| 487 | (select-frame new))) | ||
| 488 | (make-initial-minibuffer-frame nil)) | ||
| 469 | 489 | ||
| 470 | ;; If the initial frame is serving as a surrogate | 490 | ;; If the initial frame is serving as a surrogate |
| 471 | ;; minibuffer frame for any frames, we need to wean them | 491 | ;; minibuffer frame for any frames, we need to wean them |
| @@ -795,7 +815,7 @@ the new frame according to its own rules." | |||
| 795 | (t window-system))) | 815 | (t window-system))) |
| 796 | (oldframe (selected-frame)) | 816 | (oldframe (selected-frame)) |
| 797 | (params parameters) | 817 | (params parameters) |
| 798 | frame) | 818 | frame child-frame) |
| 799 | 819 | ||
| 800 | (unless (get w 'window-system-initialized) | 820 | (unless (get w 'window-system-initialized) |
| 801 | (let ((window-system w)) ;Hack attack! | 821 | (let ((window-system w)) ;Hack attack! |
| @@ -811,17 +831,44 @@ the new frame according to its own rules." | |||
| 811 | (dolist (p default-frame-alist) | 831 | (dolist (p default-frame-alist) |
| 812 | (unless (assq (car p) params) | 832 | (unless (assq (car p) params) |
| 813 | (push p params))) | 833 | (push p params))) |
| 814 | ;; Now make the frame. | ||
| 815 | (run-hooks 'before-make-frame-hook) | ||
| 816 | 834 | ||
| 817 | ;; (setq frame-size-history '(1000)) | 835 | ;; (setq frame-size-history '(1000)) |
| 818 | 836 | ||
| 819 | (setq frame (let ((window-system w)) ;Hack attack! | 837 | (when (eq (cdr (or (assq 'minibuffer params) '(minibuffer . t))) |
| 838 | 'child-frame) | ||
| 839 | ;; If the 'minibuffer' parameter equals 'child-frame' make a | ||
| 840 | ;; frame without minibuffer first using the root window of | ||
| 841 | ;; 'default-minibuffer-frame' as its minibuffer window | ||
| 842 | (setq child-frame t) | ||
| 843 | (setq params (cons '(minibuffer) | ||
| 844 | (delq (assq 'minibuffer params) params)))) | ||
| 845 | |||
| 846 | ;; Now make the frame. | ||
| 847 | (run-hooks 'before-make-frame-hook) | ||
| 848 | |||
| 849 | (setq frame (let ((window-system w)) ; Hack attack! | ||
| 820 | (frame-creation-function params))) | 850 | (frame-creation-function params))) |
| 851 | |||
| 852 | (when child-frame | ||
| 853 | ;; When we want to equip the new frame with a minibuffer-only | ||
| 854 | ;; child frame, make that frame and reparent it immediately. | ||
| 855 | (setq child-frame | ||
| 856 | (make-frame | ||
| 857 | (append | ||
| 858 | `((display . ,display) (minibuffer . only) | ||
| 859 | (parent-frame . ,frame)) | ||
| 860 | minibuffer-frame-alist))) | ||
| 861 | (when (frame-live-p child-frame) | ||
| 862 | ;; Have the 'minibuffer' parameter of our new frame refer to | ||
| 863 | ;; its child frame's root window. | ||
| 864 | (set-frame-parameter | ||
| 865 | frame 'minibuffer (frame-root-window child-frame)))) | ||
| 866 | |||
| 821 | (normal-erase-is-backspace-setup-frame frame) | 867 | (normal-erase-is-backspace-setup-frame frame) |
| 822 | ;; Inherit the original frame's parameters. | 868 | ;; Inherit original frame's parameters unless they are overridden |
| 869 | ;; by explicit parameters. | ||
| 823 | (dolist (param frame-inherited-parameters) | 870 | (dolist (param frame-inherited-parameters) |
| 824 | (unless (assq param parameters) ;Overridden by explicit parameters. | 871 | (unless (assq param parameters) |
| 825 | (let ((val (frame-parameter oldframe param))) | 872 | (let ((val (frame-parameter oldframe param))) |
| 826 | (when val (set-frame-parameter frame param val))))) | 873 | (when val (set-frame-parameter frame param val))))) |
| 827 | 874 | ||
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) | |||
| 1849 | Lisp_Object frames, frame1; | 1849 | Lisp_Object frames, frame1; |
| 1850 | int minibuffer_selected, is_tooltip_frame; | 1850 | int minibuffer_selected, is_tooltip_frame; |
| 1851 | bool nochild = !FRAME_PARENT_FRAME (f); | 1851 | bool nochild = !FRAME_PARENT_FRAME (f); |
| 1852 | Lisp_Object minibuffer_child_frame = Qnil; | ||
| 1852 | 1853 | ||
| 1853 | if (!FRAME_LIVE_P (f)) | 1854 | if (!FRAME_LIVE_P (f)) |
| 1854 | return Qnil; | 1855 | return Qnil; |
| @@ -1865,13 +1866,33 @@ delete_frame (Lisp_Object frame, Lisp_Object force) | |||
| 1865 | /* Softly delete all frames with this frame as their parent frame or | 1866 | /* Softly delete all frames with this frame as their parent frame or |
| 1866 | as their `delete-before' frame parameter value. */ | 1867 | as their `delete-before' frame parameter value. */ |
| 1867 | FOR_EACH_FRAME (frames, frame1) | 1868 | FOR_EACH_FRAME (frames, frame1) |
| 1868 | if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f | 1869 | { |
| 1870 | struct frame *f1 = XFRAME (frame1); | ||
| 1871 | |||
| 1872 | if (EQ (frame1, frame) || FRAME_TOOLTIP_P (f1)) | ||
| 1873 | continue; | ||
| 1874 | else if (FRAME_PARENT_FRAME (f1) == f) | ||
| 1875 | { | ||
| 1876 | if (FRAME_HAS_MINIBUF_P (f1) && !FRAME_HAS_MINIBUF_P (f) | ||
| 1877 | && EQ (FRAME_MINIBUF_WINDOW (f), FRAME_MINIBUF_WINDOW (f1))) | ||
| 1878 | /* frame1 owns frame's minibuffer window so we must not | ||
| 1879 | delete it here to avoid a surrogate minibuffer error. | ||
| 1880 | Unparent frame1 and make it a top-level frame. */ | ||
| 1881 | { | ||
| 1882 | Fmodify_frame_parameters | ||
| 1883 | (frame1, Fcons (Fcons (Qparent_frame, Qnil), Qnil)); | ||
| 1884 | minibuffer_child_frame = frame1; | ||
| 1885 | } | ||
| 1886 | else | ||
| 1887 | delete_frame (frame1, Qnil); | ||
| 1888 | } | ||
| 1889 | else if (nochild | ||
| 1890 | && EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame)) | ||
| 1869 | /* Process `delete-before' parameter iff FRAME is not a child | 1891 | /* Process `delete-before' parameter iff FRAME is not a child |
| 1870 | frame. This avoids that we enter an infinite chain of mixed | 1892 | frame. This avoids that we enter an infinite chain of mixed |
| 1871 | dependencies. */ | 1893 | dependencies. */ |
| 1872 | || (nochild | 1894 | delete_frame (frame1, Qnil); |
| 1873 | && EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame))) | 1895 | } |
| 1874 | delete_frame (frame1, Qnil); | ||
| 1875 | 1896 | ||
| 1876 | /* Does this frame have a minibuffer, and is it the surrogate | 1897 | /* Does this frame have a minibuffer, and is it the surrogate |
| 1877 | minibuffer for any other frame? */ | 1898 | minibuffer for any other frame? */ |
| @@ -2136,18 +2157,27 @@ delete_frame (Lisp_Object frame, Lisp_Object force) | |||
| 2136 | { | 2157 | { |
| 2137 | struct frame *f1 = XFRAME (frame1); | 2158 | struct frame *f1 = XFRAME (frame1); |
| 2138 | 2159 | ||
| 2139 | /* Consider only frames on the same kboard | 2160 | /* Set frame_on_same_kboard to frame1 if it is on the same |
| 2140 | and only those with minibuffers. */ | 2161 | keyboard. Set frame_with_minibuf to frame1 if it also |
| 2141 | if (kb == FRAME_KBOARD (f1) | 2162 | has a minibuffer. Leave the loop immediately if frame1 |
| 2142 | && FRAME_HAS_MINIBUF_P (f1)) | 2163 | is also minibuffer-only. |
| 2164 | |||
| 2165 | Emacs 26 does _not_ set frame_on_same_kboard here when it | ||
| 2166 | finds a minibuffer-only frame and subsequently fails to | ||
| 2167 | set default_minibuffer_frame below. Not a great deal and | ||
| 2168 | never noticed since make_frame_without_minibuffer creates | ||
| 2169 | a new minibuffer frame in that case (which can be a minor | ||
| 2170 | annoyance though). To consider for Emacs 26.3. */ | ||
| 2171 | if (kb == FRAME_KBOARD (f1)) | ||
| 2143 | { | 2172 | { |
| 2144 | frame_with_minibuf = frame1; | 2173 | frame_on_same_kboard = frame1; |
| 2145 | if (FRAME_MINIBUF_ONLY_P (f1)) | 2174 | if (FRAME_HAS_MINIBUF_P (f1)) |
| 2146 | break; | 2175 | { |
| 2176 | frame_with_minibuf = frame1; | ||
| 2177 | if (FRAME_MINIBUF_ONLY_P (f1)) | ||
| 2178 | break; | ||
| 2179 | } | ||
| 2147 | } | 2180 | } |
| 2148 | |||
| 2149 | if (kb == FRAME_KBOARD (f1)) | ||
| 2150 | frame_on_same_kboard = frame1; | ||
| 2151 | } | 2181 | } |
| 2152 | 2182 | ||
| 2153 | if (!NILP (frame_on_same_kboard)) | 2183 | if (!NILP (frame_on_same_kboard)) |
| @@ -2180,7 +2210,46 @@ delete_frame (Lisp_Object frame, Lisp_Object force) | |||
| 2180 | = Fcons (list3 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame), | 2210 | = Fcons (list3 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame), |
| 2181 | pending_funcalls); | 2211 | pending_funcalls); |
| 2182 | else | 2212 | else |
| 2183 | safe_call2 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame); | 2213 | safe_call2 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame); |
| 2214 | |||
| 2215 | if (!NILP (minibuffer_child_frame)) | ||
| 2216 | /* If minibuffer_child_frame is non-nil, it was FRAME's minibuffer | ||
| 2217 | child frame. Delete it unless it's also the minibuffer frame | ||
| 2218 | of another frame in which case we make sure it's visible. */ | ||
| 2219 | { | ||
| 2220 | struct frame *f1 = XFRAME (minibuffer_child_frame); | ||
| 2221 | |||
| 2222 | if (FRAME_LIVE_P (f1)) | ||
| 2223 | { | ||
| 2224 | Lisp_Object window1 = FRAME_ROOT_WINDOW (f1); | ||
| 2225 | Lisp_Object frame2; | ||
| 2226 | |||
| 2227 | FOR_EACH_FRAME (frames, frame2) | ||
| 2228 | { | ||
| 2229 | struct frame *f2 = XFRAME (frame2); | ||
| 2230 | |||
| 2231 | if (EQ (frame2, minibuffer_child_frame) || FRAME_TOOLTIP_P (f2)) | ||
| 2232 | continue; | ||
| 2233 | else if (EQ (FRAME_MINIBUF_WINDOW (f2), window1)) | ||
| 2234 | { | ||
| 2235 | /* minibuffer_child_frame serves as minibuffer frame | ||
| 2236 | for at least one other frame - so make it visible | ||
| 2237 | and quit. */ | ||
| 2238 | if (!FRAME_VISIBLE_P (f1) && !FRAME_ICONIFIED_P (f1)) | ||
| 2239 | Fmake_frame_visible (frame1); | ||
| 2240 | |||
| 2241 | return Qnil; | ||
| 2242 | } | ||
| 2243 | } | ||
| 2244 | |||
| 2245 | /* No other frame found that uses minibuffer_child_frame as | ||
| 2246 | minibuffer frame. If FORCE is Qnoelisp or there are | ||
| 2247 | other visible frames left, delete minibuffer_child_frame | ||
| 2248 | since it presumably was used by FRAME only. */ | ||
| 2249 | if (EQ (force, Qnoelisp) || other_frames (f1, false, !NILP (force))) | ||
| 2250 | delete_frame (minibuffer_child_frame, Qnoelisp); | ||
| 2251 | } | ||
| 2252 | } | ||
| 2184 | 2253 | ||
| 2185 | return Qnil; | 2254 | return Qnil; |
| 2186 | } | 2255 | } |