unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
@ 2018-07-24 12:05 Noam Postavsky
  2018-07-26  1:25 ` Noam Postavsky
  0 siblings, 1 reply; 27+ messages in thread
From: Noam Postavsky @ 2018-07-24 12:05 UTC (permalink / raw)
  To: 32257; +Cc: lars ingebrigtsen, andy moreton

X-Debbugs-CC: Andy Moreton <andrewjmoreton@gmail.com>, Lars Ingebrigtsen <larsi@gnus.org>

Starting from emacs -Q, evaluate:

    (require 'rmc)
    (read-multiple-choice "choice? " '((?a "ay") (?b "bee") (?c "see")))

then click the mouse somewhere instead of answering the prompt, notice
Emacs maxing out CPU, hit C-g.  *Messages* will have a log of the prompt
being repeated many times:

    choice?  (ay, bee, [c] see, ?):  [4435 times]

In GNU Emacs 26.1 (build 1, x86_64-pc-linux-gnu, X toolkit, Xaw scroll bars)
 of 2018-04-09 built on zebian
Repository revision: c267421647510319d2a70554e42f0d1c394dba0a
Windowing system distributor 'The X.Org Foundation', version 11.0.11902000
System Description:	Debian GNU/Linux 9.4 (stretch)





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-24 12:05 bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks Noam Postavsky
@ 2018-07-26  1:25 ` Noam Postavsky
  2018-07-27  9:08   ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Noam Postavsky @ 2018-07-26  1:25 UTC (permalink / raw)
  To: 32257; +Cc: lars ingebrigtsen, andy moreton

Noam Postavsky <npostavs@gmail.com> writes:

> X-Debbugs-CC: Andy Moreton <andrewjmoreton@gmail.com>, Lars Ingebrigtsen <larsi@gnus.org>
>
> Starting from emacs -Q, evaluate:
>
>     (require 'rmc)
>     (read-multiple-choice "choice? " '((?a "ay") (?b "bee") (?c "see")))
>
> then click the mouse somewhere instead of answering the prompt, notice
> Emacs maxing out CPU, hit C-g.  *Messages* will have a log of the prompt
> being repeated many times:
>
>     choice?  (ay, bee, [c] see, ?):  [4435 times]

The problem seems to be that read-char doesn't "use up" non-character
events, e.g., with the following:

(defun read-char-or-err ()
  (condition-case err
      (read-char)
    (error err)))

(list (read-char-or-err)
      (read-char-or-err)
      (read-char-or-err)
      (read-char-or-err)
      (read-char-or-err))

this returns ((error "Non-character input-event") (error "Non-character
input-event") ...) regardless of how many calls to read-char-or-err
there are.

The patch below fixes the inf looping, although it still doesn't allow
any other user interaction (unlike read-from-minibuffer).

--- i/lisp/emacs-lisp/rmc.el
+++ w/lisp/emacs-lisp/rmc.el
@@ -118,8 +118,10 @@ read-multiple-choice
                             choices)))
                   (condition-case nil
                       (let ((cursor-in-echo-area t))
-                        (read-char))
+                        (read-event))
                     (error nil))))
+          (unless (characterp tchar)
+            (setq tchar nil))
           (setq answer (lookup-key query-replace-map (vector tchar) t))
           (setq tchar
                 (cond







^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-26  1:25 ` Noam Postavsky
@ 2018-07-27  9:08   ` Eli Zaretskii
  2018-07-27 11:35     ` Andy Moreton
  0 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-07-27  9:08 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: 32257, larsi, andrewjmoreton

> From: Noam Postavsky <npostavs@gmail.com>
> Date: Wed, 25 Jul 2018 21:25:46 -0400
> Cc: lars ingebrigtsen <larsi@gnus.org>, andy moreton <andrewjmoreton@gmail.com>
> > Starting from emacs -Q, evaluate:
> >
> >     (require 'rmc)
> >     (read-multiple-choice "choice? " '((?a "ay") (?b "bee") (?c "see")))
> >
> > then click the mouse somewhere instead of answering the prompt, notice
> > Emacs maxing out CPU, hit C-g.  *Messages* will have a log of the prompt
> > being repeated many times:
> >
> >     choice?  (ay, bee, [c] see, ?):  [4435 times]
> 
> The problem seems to be that read-char doesn't "use up" non-character
> events, e.g., with the following:
> 
> (defun read-char-or-err ()
>   (condition-case err
>       (read-char)
>     (error err)))
> 
> (list (read-char-or-err)
>       (read-char-or-err)
>       (read-char-or-err)
>       (read-char-or-err)
>       (read-char-or-err))
> 
> this returns ((error "Non-character input-event") (error "Non-character
> input-event") ...) regardless of how many calls to read-char-or-err
> there are.
> 
> The patch below fixes the inf looping, although it still doesn't allow
> any other user interaction (unlike read-from-minibuffer).

If we want this change on emacs-26, we should carefully audit all the
other users of rmc.el (and in generally, I'd prefer some more local
change in nsm.el on the release branch).  We've had our share of
subtle bugs introduced by switching to an "almost-compatible" method
of reading input.

Thanks.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-27  9:08   ` Eli Zaretskii
@ 2018-07-27 11:35     ` Andy Moreton
  2018-07-27 12:30       ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Andy Moreton @ 2018-07-27 11:35 UTC (permalink / raw)
  To: 32257

On Fri 27 Jul 2018, Eli Zaretskii wrote:

>> From: Noam Postavsky <npostavs@gmail.com>
>> Date: Wed, 25 Jul 2018 21:25:46 -0400
>> Cc: lars ingebrigtsen <larsi@gnus.org>, andy moreton <andrewjmoreton@gmail.com>
>> > Starting from emacs -Q, evaluate:
>> >
>> >     (require 'rmc)
>> >     (read-multiple-choice "choice? " '((?a "ay") (?b "bee") (?c "see")))
>> >
>> > then click the mouse somewhere instead of answering the prompt, notice
>> > Emacs maxing out CPU, hit C-g.  *Messages* will have a log of the prompt
>> > being repeated many times:
>> >
>> >     choice?  (ay, bee, [c] see, ?):  [4435 times]
>> 
>> The problem seems to be that read-char doesn't "use up" non-character
>> events, e.g., with the following:
>> 
>> (defun read-char-or-err ()
>>   (condition-case err
>>       (read-char)
>>     (error err)))
>> 
>> (list (read-char-or-err)
>>       (read-char-or-err)
>>       (read-char-or-err)
>>       (read-char-or-err)
>>       (read-char-or-err))
>> 
>> this returns ((error "Non-character input-event") (error "Non-character
>> input-event") ...) regardless of how many calls to read-char-or-err
>> there are.
>> 
>> The patch below fixes the inf looping, although it still doesn't allow
>> any other user interaction (unlike read-from-minibuffer).

I've tested this on emacs-26, and it fixes the original problem from NSM
that resulted in this bug report.

> If we want this change on emacs-26, we should carefully audit all the
> other users of rmc.el (and in generally, I'd prefer some more local
> change in nsm.el on the release branch).  We've had our share of
> subtle bugs introduced by switching to an "almost-compatible" method
> of reading input.

read-multiple choice only has two callers (`nsm-query-user' and
`message-fix-before-sending') in both master and emacs-26.

While I understand caution over changes to emacs-26, the only place that
needs fixing is read-multiple-choice. It seems odd to prefer changes
in its callers, that will more likely introduce additional bugs without
fixing the original issue.

    AndyM







^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-27 11:35     ` Andy Moreton
@ 2018-07-27 12:30       ` Eli Zaretskii
  2018-07-27 12:45         ` Noam Postavsky
  0 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-07-27 12:30 UTC (permalink / raw)
  To: Andy Moreton; +Cc: 32257

> From: Andy Moreton <andrewjmoreton@gmail.com>
> Date: Fri, 27 Jul 2018 12:35:27 +0100
> 
> > If we want this change on emacs-26, we should carefully audit all the
> > other users of rmc.el (and in generally, I'd prefer some more local
> > change in nsm.el on the release branch).  We've had our share of
> > subtle bugs introduced by switching to an "almost-compatible" method
> > of reading input.
> 
> read-multiple choice only has two callers (`nsm-query-user' and
> `message-fix-before-sending') in both master and emacs-26.

In Emacs, yes.  But what about the world out there?

> While I understand caution over changes to emacs-26, the only place that
> needs fixing is read-multiple-choice. It seems odd to prefer changes
> in its callers, that will more likely introduce additional bugs without
> fixing the original issue.

In case it wasn't clear I meant to make a safer change on emacs-26,
and change read-multiple-choice on master.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-27 12:30       ` Eli Zaretskii
@ 2018-07-27 12:45         ` Noam Postavsky
  2018-07-27 13:50           ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Noam Postavsky @ 2018-07-27 12:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Andy Moreton, 32257

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Andy Moreton <andrewjmoreton@gmail.com>
>> Date: Fri, 27 Jul 2018 12:35:27 +0100
>> 
>> > If we want this change on emacs-26, we should carefully audit all the
>> > other users of rmc.el

What should we check for though?

>> > (and in generally, I'd prefer some more local
>> > change in nsm.el on the release branch).  We've had our share of
>> > subtle bugs introduced by switching to an "almost-compatible" method
>> > of reading input.
>> 
>> read-multiple choice only has two callers (`nsm-query-user' and
>> `message-fix-before-sending') in both master and emacs-26.
>
> In Emacs, yes.  But what about the world out there?

I expect all the callers out in the world will exhibit the same bug.

> In case it wasn't clear I meant to make a safer change on emacs-26,
> and change read-multiple-choice on master.

At any rate, I don't see a way of fixing it in the caller, short of
temporarily fsetting read-char into read-event.  Also,
read-multiple-choice is new in Emacs 26, so it seems to me we should
rather fix it in 26.2.






^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-27 12:45         ` Noam Postavsky
@ 2018-07-27 13:50           ` Eli Zaretskii
  2018-07-31  1:52             ` Noam Postavsky
  0 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-07-27 13:50 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: andrewjmoreton, 32257

> From: Noam Postavsky <npostavs@gmail.com>
> Cc: Andy Moreton <andrewjmoreton@gmail.com>,  32257@debbugs.gnu.org
> Date: Fri, 27 Jul 2018 08:45:03 -0400
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Andy Moreton <andrewjmoreton@gmail.com>
> >> Date: Fri, 27 Jul 2018 12:35:27 +0100
> >> 
> >> > If we want this change on emacs-26, we should carefully audit all the
> >> > other users of rmc.el
> 
> What should we check for though?

Anything that read-event handles differently from read-char.  Maybe a
good starting point is to compile a list of the differences in
behavior between the two.

> >> read-multiple choice only has two callers (`nsm-query-user' and
> >> `message-fix-before-sending') in both master and emacs-26.
> >
> > In Emacs, yes.  But what about the world out there?
> 
> I expect all the callers out in the world will exhibit the same bug.

What about the features they expect?

> At any rate, I don't see a way of fixing it in the caller, short of
> temporarily fsetting read-char into read-event.

If there's no better idea, we could copy read-multiple-choice into
nsm.el and replace read-char with read-event there.  But maybe there
are more elegant ideas.

> Also, read-multiple-choice is new in Emacs 26, so it seems to me we
> should rather fix it in 26.2.

I agree, I just am not convince that replacing read-char with
read-event is the right fix.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-27 13:50           ` Eli Zaretskii
@ 2018-07-31  1:52             ` Noam Postavsky
  2018-07-31 11:59               ` Andy Moreton
  2018-07-31 16:04               ` Eli Zaretskii
  0 siblings, 2 replies; 27+ messages in thread
From: Noam Postavsky @ 2018-07-31  1:52 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 32257, andrewjmoreton

Eli Zaretskii <eliz@gnu.org> writes:

>> >> > If we want this change on emacs-26, we should carefully audit all the
>> >> > other users of rmc.el
>> 
>> What should we check for though?
>
> Anything that read-event handles differently from read-char.  Maybe a
> good starting point is to compile a list of the differences in
> behavior between the two.

Looking at the implementation, they both delegate to
read_filtered_event, but read-char passes 1 for the first three args,
where read-event passes 0.

   If NO_SWITCH_FRAME, switch-frame events are stashed
   until we get a character we like, and then stuffed into
   unread_switch_frame.

   If ASCII_REQUIRED, check function key events to see
   if the unmodified version of the symbol has a Qascii_character
   property, and use that character, if present.

   If ERROR_NONASCII, signal an error if the input we
   get isn't an ASCII character with modifiers.  If it's false but
   ASCII_REQUIRED is true, just re-read until we get an ASCII
   character.

Another possibility is to use read-char-exclusive which only changes
ERROR_NON_ASCII to 0.  The downside is that when there are mouse clicks
it prints the events into the minibuffer, covering the prompt (but
typing "?"  still works to restore the prompt).

--- i/lisp/emacs-lisp/rmc.el
+++ w/lisp/emacs-lisp/rmc.el
@@ -118,7 +118,7 @@ read-multiple-choice
                             choices)))
                   (condition-case nil
                       (let ((cursor-in-echo-area t))
-                        (read-char))
+                        (read-char-exclusive))
                     (error nil))))
           (setq answer (lookup-key query-replace-map (vector tchar) t))
           (setq tchar





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31  1:52             ` Noam Postavsky
@ 2018-07-31 11:59               ` Andy Moreton
  2018-07-31 12:27                 ` martin rudalics
  2018-07-31 16:06                 ` Eli Zaretskii
  2018-07-31 16:04               ` Eli Zaretskii
  1 sibling, 2 replies; 27+ messages in thread
From: Andy Moreton @ 2018-07-31 11:59 UTC (permalink / raw)
  To: 32257

On Mon 30 Jul 2018, Noam Postavsky wrote:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> >> > If we want this change on emacs-26, we should carefully audit all the
>>> >> > other users of rmc.el
>>> 
>>> What should we check for though?
>>
>> Anything that read-event handles differently from read-char.  Maybe a
>> good starting point is to compile a list of the differences in
>> behavior between the two.
>
> Looking at the implementation, they both delegate to
> read_filtered_event, but read-char passes 1 for the first three args,
> where read-event passes 0.
>
>    If NO_SWITCH_FRAME, switch-frame events are stashed
>    until we get a character we like, and then stuffed into
>    unread_switch_frame.
>
>    If ASCII_REQUIRED, check function key events to see
>    if the unmodified version of the symbol has a Qascii_character
>    property, and use that character, if present.
>
>    If ERROR_NONASCII, signal an error if the input we
>    get isn't an ASCII character with modifiers.  If it's false but
>    ASCII_REQUIRED is true, just re-read until we get an ASCII
>    character.

So perhaps this `read-char' to `read-event' change will result in different
frame switching behaviour. I'm not sure how to test that though.

> Another possibility is to use read-char-exclusive which only changes
> ERROR_NON_ASCII to 0.  The downside is that when there are mouse clicks
> it prints the events into the minibuffer, covering the prompt (but
> typing "?"  still works to restore the prompt).

AS you have done, I read the implementation and tried a patch using
`read-char-exclusive'. The result is unusable as the printed events
obscure the original question.

The proper long term fix for this is to make read-multiple-choice use a
dedicated buffer rather than the minibuffer, and give better visibility
when more than one question is asked in succession. Perhaps also some
way to stop the echo area from obscuring the minibuffer ?

    AndyM






^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 11:59               ` Andy Moreton
@ 2018-07-31 12:27                 ` martin rudalics
  2018-07-31 12:38                   ` Andy Moreton
  2018-07-31 16:06                 ` Eli Zaretskii
  1 sibling, 1 reply; 27+ messages in thread
From: martin rudalics @ 2018-07-31 12:27 UTC (permalink / raw)
  To: Andy Moreton, 32257

 > Perhaps also some
 > way to stop the echo area from obscuring the minibuffer ?

Here I use separate echo (area) and minibuffer windows.  If you want
to play around with a patch please tell me.

martin





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 12:27                 ` martin rudalics
@ 2018-07-31 12:38                   ` Andy Moreton
  2018-08-01  8:44                     ` martin rudalics
  0 siblings, 1 reply; 27+ messages in thread
From: Andy Moreton @ 2018-07-31 12:38 UTC (permalink / raw)
  To: 32257

On Tue 31 Jul 2018, martin rudalics wrote:

>> Perhaps also some
>> way to stop the echo area from obscuring the minibuffer ?
>
> Here I use separate echo (area) and minibuffer windows.  If you want
> to play around with a patch please tell me.
>
> martin

Yes please - prefeably in a new bug report for the enhancement patch,
so it can be tracked separately from this bug.

Thanks,

    AndyM






^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31  1:52             ` Noam Postavsky
  2018-07-31 11:59               ` Andy Moreton
@ 2018-07-31 16:04               ` Eli Zaretskii
  2018-07-31 19:29                 ` Andy Moreton
  1 sibling, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-07-31 16:04 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: 32257, andrewjmoreton

> From: Noam Postavsky <npostavs@gmail.com>
> Cc: 32257@debbugs.gnu.org,  andrewjmoreton@gmail.com
> Date: Mon, 30 Jul 2018 21:52:40 -0400
> 
> Another possibility is to use read-char-exclusive which only changes
> ERROR_NON_ASCII to 0.  The downside is that when there are mouse clicks
> it prints the events into the minibuffer, covering the prompt (but
> typing "?"  still works to restore the prompt).
> 
> --- i/lisp/emacs-lisp/rmc.el
> +++ w/lisp/emacs-lisp/rmc.el
> @@ -118,7 +118,7 @@ read-multiple-choice
>                              choices)))
>                    (condition-case nil
>                        (let ((cursor-in-echo-area t))
> -                        (read-char))
> +                        (read-char-exclusive))
>                      (error nil))))
>            (setq answer (lookup-key query-replace-map (vector tchar) t))
>            (setq tchar

Maybe this is the best compromise for emacs-026.  We could search for
a better (and bolder) solution on master.

Thanks.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 11:59               ` Andy Moreton
  2018-07-31 12:27                 ` martin rudalics
@ 2018-07-31 16:06                 ` Eli Zaretskii
  2018-07-31 19:26                   ` Andy Moreton
  1 sibling, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-07-31 16:06 UTC (permalink / raw)
  To: Andy Moreton; +Cc: 32257

> From: Andy Moreton <andrewjmoreton@gmail.com>
> Date: Tue, 31 Jul 2018 12:59:37 +0100
> 
> > Another possibility is to use read-char-exclusive which only changes
> > ERROR_NON_ASCII to 0.  The downside is that when there are mouse clicks
> > it prints the events into the minibuffer, covering the prompt (but
> > typing "?"  still works to restore the prompt).
> 
> AS you have done, I read the implementation and tried a patch using
> `read-char-exclusive'. The result is unusable as the printed events
> obscure the original question.

I think "unusable" is an exaggeration, since you can recover the
prompt.  And the result is definitely better than the current
situation, where you just get stuck.

> The proper long term fix for this is to make read-multiple-choice use a
> dedicated buffer rather than the minibuffer, and give better visibility
> when more than one question is asked in succession. Perhaps also some
> way to stop the echo area from obscuring the minibuffer ?

The echo-area messages are fine, but we should have a feature that
allows to suppress echoing the mouse clicks.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 16:06                 ` Eli Zaretskii
@ 2018-07-31 19:26                   ` Andy Moreton
  2018-08-01  5:27                     ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Andy Moreton @ 2018-07-31 19:26 UTC (permalink / raw)
  To: 32257

On Tue 31 Jul 2018, Eli Zaretskii wrote:

>> From: Andy Moreton <andrewjmoreton@gmail.com>
>> Date: Tue, 31 Jul 2018 12:59:37 +0100
>> 
>> > Another possibility is to use read-char-exclusive which only changes
>> > ERROR_NON_ASCII to 0.  The downside is that when there are mouse clicks
>> > it prints the events into the minibuffer, covering the prompt (but
>> > typing "?"  still works to restore the prompt).
>> 
>> AS you have done, I read the implementation and tried a patch using
>> `read-char-exclusive'. The result is unusable as the printed events
>> obscure the original question.
>
> I think "unusable" is an exaggeration, since you can recover the
> prompt.  And the result is definitely better than the current
> situation, where you just get stuck.

No. It is is just as unusable, as the only way to get past the stream of
event message hiding the question you are trying to answer is to hit ^G
until it goes away. Using `read-char-exclusive' does not make any improvement on the
original problem.

We already have a working patch that solves the original problem.

>> The proper long term fix for this is to make read-multiple-choice use a
>> dedicated buffer rather than the minibuffer, and give better visibility
>> when more than one question is asked in succession. Perhaps also some
>> way to stop the echo area from obscuring the minibuffer ?
>
> The echo-area messages are fine, but we should have a feature that
> allows to suppress echoing the mouse clicks.

They are a pain if you are asked more than one question, but the echo
area message gets in the way of the minibuffer prompt that you need to
read in order to answer the question.

    AndyM






^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 16:04               ` Eli Zaretskii
@ 2018-07-31 19:29                 ` Andy Moreton
  2018-08-01  5:29                   ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Andy Moreton @ 2018-07-31 19:29 UTC (permalink / raw)
  To: 32257

On Tue 31 Jul 2018, Eli Zaretskii wrote:

>> From: Noam Postavsky <npostavs@gmail.com>
>> Cc: 32257@debbugs.gnu.org,  andrewjmoreton@gmail.com
>> Date: Mon, 30 Jul 2018 21:52:40 -0400
>> 
>> Another possibility is to use read-char-exclusive which only changes
>> ERROR_NON_ASCII to 0.  The downside is that when there are mouse clicks
>> it prints the events into the minibuffer, covering the prompt (but
>> typing "?"  still works to restore the prompt).

Only if you know in advance that typing "?" will do that.

>> --- i/lisp/emacs-lisp/rmc.el
>> +++ w/lisp/emacs-lisp/rmc.el
>> @@ -118,7 +118,7 @@ read-multiple-choice
>>                              choices)))
>>                    (condition-case nil
>>                        (let ((cursor-in-echo-area t))
>> -                        (read-char))
>> +                        (read-char-exclusive))
>>                      (error nil))))
>>            (setq answer (lookup-key query-replace-map (vector tchar) t))
>>            (setq tchar
>
> Maybe this is the best compromise for emacs-026.  We could search for
> a better (and bolder) solution on master.

This is no better than the status quo. Noam's original patch may bring
some possibility of new problems, but it does at least fix the original
problem (on emacs-26 and master).

    AndyM






^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 19:26                   ` Andy Moreton
@ 2018-08-01  5:27                     ` Eli Zaretskii
  0 siblings, 0 replies; 27+ messages in thread
From: Eli Zaretskii @ 2018-08-01  5:27 UTC (permalink / raw)
  To: Andy Moreton; +Cc: 32257

> From: Andy Moreton <andrewjmoreton@gmail.com>
> Date: Tue, 31 Jul 2018 20:26:33 +0100
> 
> > I think "unusable" is an exaggeration, since you can recover the
> > prompt.  And the result is definitely better than the current
> > situation, where you just get stuck.
> 
> No. It is is just as unusable, as the only way to get past the stream of
> event message hiding the question you are trying to answer is to hit ^G
> until it goes away. Using `read-char-exclusive' does not make any improvement on the
> original problem.

It is here, because typing ? brings back the original prompt, as does
any other character.  So I still think you are exaggerating.

> > The echo-area messages are fine, but we should have a feature that
> > allows to suppress echoing the mouse clicks.
> 
> They are a pain if you are asked more than one question, but the echo
> area message gets in the way of the minibuffer prompt that you need to
> read in order to answer the question.

Echo area messages in general go away to reveal the prompt.  The only
problem in this case is with the echo for mouse clicks, AFAICT.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 19:29                 ` Andy Moreton
@ 2018-08-01  5:29                   ` Eli Zaretskii
  2018-08-01 12:05                     ` Andy Moreton
  0 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2018-08-01  5:29 UTC (permalink / raw)
  To: Andy Moreton; +Cc: 32257

> From: Andy Moreton <andrewjmoreton@gmail.com>
> Date: Tue, 31 Jul 2018 20:29:31 +0100
> 
> Noam's original patch may bring some possibility of new problems,
> but it does at least fix the original problem (on emacs-26 and
> master).

Fixing a problem and introducing another doesn't sound wise to me.
We've been there several times during the last years, and I don't want
to make the same mistake again.  Sorry.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-07-31 12:38                   ` Andy Moreton
@ 2018-08-01  8:44                     ` martin rudalics
  2018-08-01 12:30                       ` Noam Postavsky
                                         ` (2 more replies)
  0 siblings, 3 replies; 27+ messages in thread
From: martin rudalics @ 2018-08-01  8:44 UTC (permalink / raw)
  To: Andy Moreton, 32257

[-- Attachment #1: Type: text/plain, Size: 643 bytes --]

 >> Here I use separate echo (area) and minibuffer windows.  If you want
 >> to play around with a patch please tell me.
 >>
 >> martin
 >
 > Yes please - prefeably in a new bug report for the enhancement patch,
 > so it can be tracked separately from this bug.

It's not yet ready for broader discussion.  I attach it here so you
can just test whether making the echo interference go away during
the minibuffer dialog.

To put it into use run emacs with

(setq default-frame-alist '((minibuffer . loose) (echo-area . t)))

Looks better with window dividers enabled.

Eventually I'll make a remote branch.  But this may take its time.

martin

[-- Attachment #2: echomini.diff --]
[-- Type: text/plain, Size: 276531 bytes --]

diff --git a/lisp/bindings.el b/lisp/bindings.el
index 9797052..5cac66e 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -137,10 +137,10 @@ mode-line-default-help-echo
           ;; this frame can be resized.  This matches a corresponding
           ;; check in `mouse-drag-mode-line'.
           (or (not (window-at-side-p window 'bottom))
-              (let ((mini-window (minibuffer-window frame)))
-                (and (eq frame (window-frame mini-window))
-                     (or (minibuffer-window-active-p mini-window)
-                         (not resize-mini-windows))))))
+              (and (eq (minibuffer-window-type frame) 'bottom)
+                   (or (minibuffer-window-active-p
+                        (minibuffer-window frame 'maybe-nil))
+                       (not resize-mini-windows)))))
          (line-2
           ;; Show text make window occupy the whole frame
           ;; only if it doesn't already do that.
diff --git a/lisp/calculator.el b/lisp/calculator.el
index b6959af..529f5b4 100644
--- a/lisp/calculator.el
+++ b/lisp/calculator.el
@@ -716,8 +716,10 @@ calculator
     (save-window-excursion
       (require 'electric) (message nil) ; hide load message
       (let ((old-buf (window-buffer (minibuffer-window)))
+            (old-dedicated (window-dedicated-p (minibuffer-window)))
             (echo-keystrokes 0)
             (garbage-collection-messages nil)) ; no gc msg when electric
+        (set-window-dedicated-p (minibuffer-window) nil)
         (set-window-buffer (minibuffer-window) calculator-buffer)
         (select-window (minibuffer-window))
         (calculator-reset)
@@ -733,6 +735,7 @@ calculator
                nil
                (lambda (_x _y) (calculator-update-display))))
           (set-window-buffer (minibuffer-window) old-buf)
+          (set-window-dedicated-p (minibuffer-window) old-dedicated)
           (kill-buffer calculator-buffer))))
     (progn
       (cond
diff --git a/lisp/cedet/semantic/idle.el b/lisp/cedet/semantic/idle.el
index 07b7af8..bf0d233 100644
--- a/lisp/cedet/semantic/idle.el
+++ b/lisp/cedet/semantic/idle.el
@@ -759,13 +759,13 @@ semantic-idle-summary-idle-function
 	;; Show the message with eldoc functions
         (unless (and str (boundp 'eldoc-echo-area-use-multiline-p)
                      eldoc-echo-area-use-multiline-p)
-          (let ((w (1- (window-width (minibuffer-window)))))
+          (let ((w (1- (window-width (echo-window)))))
             (if (> (length str) w)
                 (setq str (substring str 0 w)))))
 	;; I borrowed some bits from eldoc to shorten the
 	;; message.
 	(when semantic-idle-truncate-long-summaries
-	  (let ((ea-width (1- (window-width (minibuffer-window))))
+	  (let ((ea-width (1- (window-width (echo-window))))
 		(strlen (length str)))
 	    (when (> strlen ea-width)
 	      (setq str (substring str 0 ea-width)))))
@@ -800,7 +800,10 @@ semantic-idle-summary-refresh-echo-area
        (if (and (not executing-kbd-macro)
 		(not (and (boundp 'edebug-active) edebug-active))
 		(not cursor-in-echo-area)
-		(not (eq (selected-window) (minibuffer-window))))
+                ;; echo-window should be OK here - when a separate
+                ;; minibuffer window is selected, 'eldoc-message'
+                ;; won't do any harm.
+		(not (eq (selected-window) (echo-window))))
            (eldoc-message eldoc-last-message)
          (setq eldoc-last-message nil))))

diff --git a/lisp/descr-text.el b/lisp/descr-text.el
index d8f8188..148ce66 100644
--- a/lisp/descr-text.el
+++ b/lisp/descr-text.el
@@ -929,7 +929,7 @@ describe-char-eldoc
       (describe-char-eldoc--format
        ch
        (unless (eq eldoc-echo-area-use-multiline-p t)
-         (1- (window-width (minibuffer-window))))))))
+         (1- (window-width (echo-window))))))))

 (provide 'descr-text)

diff --git a/lisp/desktop.el b/lisp/desktop.el
index a9fa287..7933965 100644
--- a/lisp/desktop.el
+++ b/lisp/desktop.el
@@ -715,7 +715,7 @@ desktop-clear
 	     ;; which already takes care of frame restoration and deletion.
 	     (called-interactively-p 'any))
     (let* ((this (selected-frame))
-	   (mini (window-frame (minibuffer-window this)))) ; in case they differ
+	   (mini (window-frame (minibuffer-window this 'maybe-nil))))
       (dolist (frame (sort (frame-list) #'frameset-minibufferless-first-p))
 	(condition-case err
 	    (unless (or (eq frame this)
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 49ba71f..c2954d6 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -258,7 +258,12 @@ eldoc-minibuffer-message
 		  nil t)
 	(with-current-buffer
 	    (window-buffer
-	     (or (window-in-direction 'above (minibuffer-window))
+	     (or (let ((mini-window (minibuffer-window nil 'maybe-nil)))
+                   (and mini-window
+                        (memq (window-minibuffer-p mini-window) '(bottom loose))
+                        ;; Use the window above the minibuffer window
+                        ;; provide there is one.
+                        (window-in-direction 'above mini-window)))
 		 (minibuffer-selected-window)
 		 (get-largest-window)))
     (when mode-line-format
@@ -380,8 +385,14 @@ eldoc-docstring-format-sym-doc
   (let* ((ea-multi eldoc-echo-area-use-multiline-p)
          ;; Subtract 1 from window width since emacs will not write
          ;; any chars to the last column, or in later versions, will
-         ;; cause a wraparound and resize of the echo area.
-         (ea-width (1- (window-width (minibuffer-window))))
+         ;; cause a wraparound and resize of the echo area.  However,
+         ;; if the frame does not have a minibuffer window, do not
+         ;; create one here but use the width of the frame's selected
+         ;; window.
+         (mini-window (echo-window nil 'maybe-nil))
+         (ea-width (if mini-window
+                       (1- (window-width mini-window))
+                     (1- (window-width (selected-window)))))
          (strip (- (+ (length prefix) (length doc)) ea-width)))
     (cond ((or (<= strip 0)
                (eq ea-multi t)
diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el
index cae18f6..123de38 100644
--- a/lisp/erc/erc-track.el
+++ b/lisp/erc/erc-track.el
@@ -637,7 +637,8 @@ erc-buffer-visible
 ;;; Tracking the channel modifications

 (defun erc-window-configuration-change ()
-  (unless (minibuffer-window-active-p (minibuffer-window))
+  (unless (and (minibuffer-window nil 'maybe-nil)
+	       (minibuffer-window-active-p (minibuffer-window)))
     ;; delay this until command has finished to make sure window is
     ;; actually visible before clearing activity
     (add-hook 'post-command-hook 'erc-modified-channels-update)))
diff --git a/lisp/frame.el b/lisp/frame.el
index 56b8c54..a21d905 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -319,7 +319,8 @@ frame-notice-user-settings
   (let ((old-buffer (current-buffer))
 	(window-system-frame-alist
          (cdr (assq initial-window-system
-                    window-system-default-frame-alist))))
+                    window-system-default-frame-alist)))
+        loose)

     (when (and frame-notice-user-settings
 	       (null frame-initial-frame))
@@ -461,46 +462,48 @@ frame-notice-user-settings
 		   (append frame-initial-geometry-arguments
 			   '((user-size . t) (user-position . t))
 			   parms)))
+
+            (setq loose (eq (cdr (assq 'minibuffer parms)) 'loose))
+
 	    ;; The initial frame, which we are about to delete, may be
 	    ;; 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))
+	    ;; new one unless we're using loose minibuffer windows.
+	    (unless (or loose (delq frame-initial-frame (minibuffer-frame-list)))
+              (make-initial-minibuffer-frame nil))

 	    ;; If the initial frame is serving as a surrogate
 	    ;; minibuffer frame for any frames, we need to wean them
 	    ;; onto a new frame.  The default-minibuffer-frame
 	    ;; variable must be handled similarly.
-	    (let ((users-of-initial
-		   (filtered-frame-list
-                    (lambda (frame)
-                      (and (not (eq frame frame-initial-frame))
-                           (eq (window-frame
-                                (minibuffer-window frame))
-                               frame-initial-frame))))))
-              (if (or users-of-initial
-		      (eq default-minibuffer-frame frame-initial-frame))
-
-		  ;; Choose an appropriate frame.  Prefer frames which
-		  ;; are only minibuffers.
+            (unless loose
+              (let ((users-of-initial
+                     (filtered-frame-list
+                      (lambda (frame)
+                        (and (not (eq frame frame-initial-frame))
+                             (minibuffer-window frame 'maybe-nil)
+                             (eq (window-frame (minibuffer-window frame))
+                                 frame-initial-frame))))))
+                (when (or users-of-initial
+		          (eq default-minibuffer-frame frame-initial-frame))
+
+		  ;; Choose an appropriate frame.  Prefer minibuffer
+		  ;; only frames.
 		  (let* ((new-surrogate
-			  (car
-			   (or (filtered-frame-list
-                                (lambda (frame)
-                                  (eq (cdr (assq 'minibuffer
-                                                 (frame-parameters frame)))
-                                      'only)))
-			       (minibuffer-frame-list))))
-			 (new-minibuffer (minibuffer-window new-surrogate)))
-
-		    (if (eq default-minibuffer-frame frame-initial-frame)
-			(setq default-minibuffer-frame new-surrogate))
-
-		    ;; Wean the frames using frame-initial-frame as
-		    ;; their minibuffer frame.
-		    (dolist (frame users-of-initial)
-                      (modify-frame-parameters
-                       frame (list (cons 'minibuffer new-minibuffer)))))))
+			  (car (or (filtered-frame-list
+                                    (lambda (frame)
+                                      (eq (minibuffer-window-type frame) 'only)))
+			           (minibuffer-frame-list))))
+                         (new-minibuffer
+                          (and new-surrogate (minibuffer-window new-surrogate))))
+		    (when new-minibuffer
+                      (when (eq default-minibuffer-frame frame-initial-frame)
+		        (setq default-minibuffer-frame new-surrogate))
+
+		      ;; Wean the frames using frame-initial-frame as
+		      ;; their minibuffer frame.
+		      (dolist (frame users-of-initial)
+                        (modify-frame-parameters
+                         frame (list (cons 'minibuffer new-minibuffer)))))))))

             ;; Redirect events enqueued at this frame to the new frame.
 	    ;; Is this a good idea?
@@ -573,6 +576,7 @@ frame-notice-user-settings
     (setq frame-initial-frame nil)))

 (defun make-initial-minibuffer-frame (display)
+  "Make a new minibuffer-only frame on DISPLAY."
   (let ((parms (append minibuffer-frame-alist '((minibuffer . only)))))
     (if display
 	(make-frame-on-display display parms)
@@ -763,44 +767,45 @@ make-frame
 	 (params parameters)
 	 frame)

-    (unless (get w 'window-system-initialized)
-      (let ((window-system w))          ;Hack attack!
-        (window-system-initialization display))
-      (setq x-display-name display)
-      (put w 'window-system-initialized t))
-
-    ;; Add parameters from `window-system-default-frame-alist'.
-    (dolist (p (cdr (assq w window-system-default-frame-alist)))
-      (unless (assq (car p) params)
-	(push p params)))
-    ;; Add parameters from `default-frame-alist'.
-    (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!
-                  (frame-creation-function params)))
-    (normal-erase-is-backspace-setup-frame frame)
-    ;; Inherit the original frame's parameters.
-    (dolist (param frame-inherited-parameters)
-      (unless (assq param parameters)   ;Overridden by explicit parameters.
-        (let ((val (frame-parameter oldframe param)))
-          (when val (set-frame-parameter frame param val)))))
-
-    (when (numberp (car frame-size-history))
-      (setq frame-size-history
-	    (cons (1- (car frame-size-history))
-		  (cons (list frame "MAKE-FRAME")
-			(cdr frame-size-history)))))
-
-    ;; We can run `window-configuration-change-hook' for this frame now.
-    (frame-after-make-frame frame t)
-    (run-hook-with-args 'after-make-frame-functions frame)
-    frame))
+    (unless (assq 'no-make-frame params)
+      (unless (get w 'window-system-initialized)
+        (let ((window-system w))                ;Hack attack!
+          (window-system-initialization display))
+        (setq x-display-name display)
+        (put w 'window-system-initialized t))
+
+      ;; Add parameters from `window-system-default-frame-alist'.
+      (dolist (p (cdr (assq w window-system-default-frame-alist)))
+        (unless (assq (car p) params)
+	  (push p params)))
+      ;; Add parameters from `default-frame-alist'.
+      (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!
+                    (frame-creation-function params)))
+      (normal-erase-is-backspace-setup-frame frame)
+      ;; Inherit the original frame's parameters.
+      (dolist (param frame-inherited-parameters)
+        (unless (assq param parameters) ;Overridden by explicit parameters.
+          (let ((val (frame-parameter oldframe param)))
+            (when val (set-frame-parameter frame param val)))))
+
+      (when (numberp (car frame-size-history))
+        (setq frame-size-history
+	      (cons (1- (car frame-size-history))
+		    (cons (list frame "MAKE-FRAME")
+			  (cdr frame-size-history)))))
+
+      ;; We can run `window-configuration-change-hook' for this frame now.
+      (frame-after-make-frame frame t)
+      (run-hook-with-args 'after-make-frame-functions frame)
+      frame)))

 (defun filtered-frame-list (predicate)
   "Return a list of all live frames which satisfy PREDICATE."
@@ -816,7 +821,9 @@ minibuffer-frame-list
   "Return a list of all frames with their own minibuffers."
   (filtered-frame-list
    (lambda (frame)
-     (eq frame (window-frame (minibuffer-window frame))))))
+     (let ((minibuffer-window (minibuffer-window frame 'maybe-nil)))
+       (and (window-live-p minibuffer-window)
+            (eq frame (window-frame minibuffer-window)))))))

 ;; Used to be called `terminal-id' in termdev.el.
 (defun get-device-terminal (device)
@@ -1744,12 +1751,10 @@ frame-size-changed-p
 for FRAME."
   (let* ((frame (window-normalize-frame frame))
          (root (frame-root-window frame))
-         (mini (minibuffer-window frame))
+         (mini (minibuffer-window frame 'maybe-nil))
          (mini-height-before-size-change 0)
          (mini-height 0))
-    ;; FRAME's minibuffer window counts iff it's on FRAME and FRAME is
-    ;; not a minibuffer-only frame.
-    (when (and (eq (window-frame mini) frame) (not (eq mini root)))
+    (when (and mini (window-minibuffer-fixed-p mini))
       (setq mini-height-before-size-change
             (window-pixel-height-before-size-change mini))
       (setq mini-height (window-pixel-height mini)))
@@ -2159,19 +2164,24 @@ delete-other-frames
 a live frame and defaults to the selected one."
   (interactive)
   (setq frame (window-normalize-frame frame))
-  (let ((minibuffer-frame (window-frame (minibuffer-window frame)))
+  (let ((minibuffer-frame (window-frame (minibuffer-window frame 'maybe-nil)))
         (this (next-frame frame t))
         (parent (frame-parent frame))
-        next)
+        next mini-window mini-frame)
     ;; In a first round consider minibuffer-less frames only.
     (while (not (eq this frame))
       (setq next (next-frame this t))
-      (unless (or (eq (window-frame (minibuffer-window this)) this)
-                  ;; When FRAME is a child frame, delete its siblings
-                  ;; only.
-                  (and parent (not (eq (frame-parent this) parent)))
-                  ;; Do not delete a child frame of FRAME.
-                  (eq (frame-parent this) frame))
+      ;; Probably we should disallow non-loose minibuffer window
+      ;; frames to have a loose minibuffer window on another frame.
+      (when (and (not (eq (minibuffer-window-type this) 'loose))
+                 (setq mini-window (minibuffer-window this 'maybe-nil))
+                 (not (eq (setq mini-frame (window-frame mini-window)) this))
+                 (window-minibuffer-non-loose-p mini-frame)
+                 ;; When FRAME is a child frame, delete its siblings
+                 ;; only.
+                 (or (not parent) (eq (frame-parent this) parent))
+                 ;; Do not delete a child frame of FRAME.
+                 (not (eq (frame-parent this) frame)))
         (delete-frame this))
       (setq this next))
     ;; In a second round consider all remaining frames.
diff --git a/lisp/frameset.el b/lisp/frameset.el
index 0d7e802..0a569a1 100644
--- a/lisp/frameset.el
+++ b/lisp/frameset.el
@@ -787,19 +787,21 @@ frameset--record-relationships
            frame 'frameset--mouse-wheel-frame
            (frameset-frame-id mouse-wheel-frame)))
         (when nomini
-          (let ((mb-frame (window-frame (minibuffer-window frame))))
+          (let* ((mw-window (minibuffer-window frame 'maybe-nil))
+                 (mb-frame (and mw-window (window-frame mw-window))))
             ;; For minibufferless frames, frameset--mini is a cons
             ;; (nil . FRAME-ID), where FRAME-ID is the frameset--id of
             ;; the frame containing its minibuffer window.
             ;; FRAME-ID can be set to nil, if FRAME-LIST doesn't contain
             ;; the minibuffer frame of a minibufferless frame; we allow
             ;; it without trying to second-guess the user.
-            (set-frame-parameter
-             frame
-             'frameset--mini
-             (cons nil
-                   (and mb-frame
-                        (frameset-frame-id mb-frame)))))))))
+            (when mb-frame
+              (set-frame-parameter
+               frame
+               'frameset--mini
+               (cons nil
+                     (and mb-frame
+                          (frameset-frame-id mb-frame))))))))))
   ;; Now store text-pixel width and height if `frame-resize-pixelwise'
   ;; is set.  (Bug#30141)
   (dolist (frame frame-list)
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index ceb9842..21f2004 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -12152,7 +12152,7 @@ gnus-summary-pipe-output
 		     (progn
 		       (end-of-line 0)
 		       (<= (current-column)
-			   (window-width (minibuffer-window)))))
+			   (window-width (echo-window)))))
 	      (goto-char (point-min))))
 	  (message "%s" (substring result 0 -1))
 	(message nil)
diff --git a/lisp/info-look.el b/lisp/info-look.el
index dec16cf..a4b71dd 100644
--- a/lisp/info-look.el
+++ b/lisp/info-look.el
@@ -573,13 +573,13 @@ info-lookup-guess-default*
 	(while (and (re-search-backward regexp nil t)
 		    (looking-at regexp)
 		    (>= (match-end 0) end))
-	  (setq result (match-string subexp)))
+	  (setq result (match-string-no-properties subexp)))
 	(if (not result)
 	    (progn
 	      (goto-char start)
 	      (skip-chars-forward " \t\n")
 	      (and (looking-at regexp)
-		   (setq result (match-string subexp)))))))
+		   (setq result (match-string-no-properties subexp)))))))
     result))

 (defun info-lookup-guess-c-symbol ()
diff --git a/lisp/international/kkc.el b/lisp/international/kkc.el
index a56d3cb..b179b94 100644
--- a/lisp/international/kkc.el
+++ b/lisp/international/kkc.el
@@ -553,7 +553,7 @@ kkc-show-conversion-list-update
 	  (setq this-idx next-idx msg nil)))
     (if (not msg)
 	(let ((len (length kkc-current-conversions))
-	      (max-width (window-width (minibuffer-window)))
+	      (max-width (window-width (echo-window)))
 	      (width-table kkc-current-conversions-width)
 	      (width 0)
 	      (idx this-idx)
diff --git a/lisp/international/quail.el b/lisp/international/quail.el
index ec15cca..d4dcad0 100644
--- a/lisp/international/quail.el
+++ b/lisp/international/quail.el
@@ -586,7 +586,7 @@ quail-activate
     (quail-show-guidance)
     ;; If we are in minibuffer, turn off the current input method
     ;; before exiting.
-    (when (eq (selected-window) (minibuffer-window))
+    (when (eq (selected-window) (minibuffer-window nil 'maybe-nil))
       (add-hook 'minibuffer-exit-hook 'quail-exit-from-minibuffer)
       (add-hook 'post-command-hook 'quail-show-guidance nil t))
     (run-hooks 'quail-activate-hook)
@@ -1995,7 +1995,7 @@ quail-require-guidance-buf
   "Return t if the current Quail package requires showing guidance buffer."
   (and input-method-verbose-flag
        (if (eq input-method-verbose-flag 'default)
-	   (not (and (eq (selected-window) (minibuffer-window))
+	   (not (and (eq (selected-window) (minibuffer-window nil 'maybe-nil))
 		     (quail-simple)))
 	 (if (eq input-method-verbose-flag 'complex-only)
 	     (not (quail-simple))
@@ -2033,7 +2033,7 @@ quail-show-guidance
 	     (null unread-command-events)
 	     (null unread-post-input-method-events))
     (if (minibufferp)
-	(if (eq (minibuffer-window) (frame-root-window))
+	(if (eq (minibuffer-window nil 'maybe-nil) (frame-root-window))
 	    ;; Use another frame.  It is sure that we are using some
 	    ;; window system.
 	    (let ((guidance quail-guidance-str))
@@ -2064,9 +2064,9 @@ quail-show-guidance
 (defun quail-hide-guidance ()
   "Hide the Quail guidance."
   (when (and (quail-require-guidance-buf)
-	     (or (eq (selected-window) (minibuffer-window))
+	     (or (eq (selected-window) (minibuffer-window nil 'maybe-nil))
 		 input-method-use-echo-area)
-	     (eq (minibuffer-window) (frame-root-window)))
+	     (eq (minibuffer-window nil 'maybe-nil) (frame-root-window)))
     ;; We are using another frame for the guidance.
     (if (frame-live-p quail-guidance-frame)
 	(delete-frame quail-guidance-frame))
diff --git a/lisp/international/robin.el b/lisp/international/robin.el
index 94d2bf1..2adb14c 100644
--- a/lisp/international/robin.el
+++ b/lisp/international/robin.el
@@ -420,7 +420,7 @@ robin-activate
     (setq robin-mode t
       	  describe-current-input-method-function 'robin-help
 	  deactivate-current-input-method-function 'robin-deactivate)
-    (if (eq (selected-window) (minibuffer-window))
+    (if (eq (selected-window) (minibuffer-window nil 'maybe-nil))
 	(add-hook 'minibuffer-exit-hook 'robin-exit-from-minibuffer))
     (run-hooks 'input-method-activate-hook
 	       'robin-activate-hook)
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 1e785a4..db3737b 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -2069,8 +2069,9 @@ isearch-mouse-2
   (let ((w (posn-window (event-start click)))
         (binding (let ((overriding-terminal-local-map nil))
                    (key-binding (this-command-keys-vector) t))))
-    (if (and (window-minibuffer-p w)
-	     (not (minibuffer-window-active-p w))) ; in echo area
+    (if (and (window-echo-p w)
+	     (or (not (eq w (minibuffer-window nil 'maybe-nil)))
+                 (not (minibuffer-window-active-p w)))) ; in echo area
 	(isearch-yank-x-selection)
       (when (functionp binding)
 	(call-interactively binding)))))
diff --git a/lisp/leim/quail/hangul.el b/lisp/leim/quail/hangul.el
index ba2b320..4fbcfa6 100644
--- a/lisp/leim/quail/hangul.el
+++ b/lisp/leim/quail/hangul.el
@@ -523,7 +523,7 @@ hangul-input-method-activate
 	describe-current-input-method-function 'hangul-input-method-help
 	hangul-input-method-help-text help-text)
   (quail-delete-overlays)
-  (if (eq (selected-window) (minibuffer-window))
+  (if (eq (selected-window) (minibuffer-window nil 'maybe-nil))
       (add-hook 'minibuffer-exit-hook 'quail-exit-from-minibuffer))
   (set (make-local-variable 'input-method-function) func))

diff --git a/lisp/leim/quail/uni-input.el b/lisp/leim/quail/uni-input.el
index 4014ebc..6ee336e 100644
--- a/lisp/leim/quail/uni-input.el
+++ b/lisp/leim/quail/uni-input.el
@@ -103,7 +103,7 @@ ucs-input-activate
     (setq deactivate-current-input-method-function 'ucs-input-deactivate)
     (setq describe-current-input-method-function 'ucs-input-help)
     (quail-delete-overlays)
-    (if (eq (selected-window) (minibuffer-window))
+    (if (eq (selected-window) (minibuffer-window nil 'maybe-nil))
 	(add-hook 'minibuffer-exit-hook 'quail-exit-from-minibuffer))
     (set (make-local-variable 'input-method-function)
 	 'ucs-input-method)))
diff --git a/lisp/mh-e/mh-show.el b/lisp/mh-e/mh-show.el
index dae8de0..095b323 100644
--- a/lisp/mh-e/mh-show.el
+++ b/lisp/mh-e/mh-show.el
@@ -136,7 +136,7 @@ mh-show-msg
         (clean-message-header mh-clean-message-header-flag)
         (show-window (get-buffer-window mh-show-buffer))
         (display-mime-buttons-flag mh-display-buttons-for-inline-parts-flag))
-    (if (not (eq (next-window (minibuffer-window)) (selected-window)))
+    (if (not (eq (next-window (minibuffer-window nil 'maybe-nil)) (selected-window)))
         (delete-other-windows))         ; force ourself to the top window
     (mh-in-show-buffer (mh-show-buffer)
       (setq mh-display-buttons-for-inline-parts-flag display-mime-buttons-flag)
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index a7e6a87..ef22e4f 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -1837,7 +1837,7 @@ minibuffer-completion-help
              ;; Use `display-buffer-below-selected' for inline completions,
              ;; but not in the minibuffer (e.g. in `eval-expression')
              ;; for which `display-buffer-at-bottom' is used.
-             ,(if (eq (selected-window) (minibuffer-window))
+             ,(if (eq (selected-window) (minibuffer-window nil 'maybe-nil))
                   'display-buffer-at-bottom
                 'display-buffer-below-selected))
 	    ,(if temp-buffer-resize-mode
diff --git a/lisp/mouse.el b/lisp/mouse.el
index d5c132f..1b80df6 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -533,16 +533,13 @@ mouse-drag-mode-line
           ;; Allow resizing the minibuffer window if it's on the
           ;; same frame as and immediately below `window', and it's
           ;; either active or `resize-mini-windows' is nil.
-          (let ((minibuffer-window (minibuffer-window frame)))
-            (and (eq (window-frame minibuffer-window) frame)
-                 (or (not resize-mini-windows)
-                     (eq minibuffer-window
-                         (active-minibuffer-window))))))
+          (and (eq (minibuffer-window-type frame) 'bottom)
+               (or (minibuffer-window-active-p (minibuffer-window frame))
+                   (not resize-mini-windows))))
       (mouse-drag-line start-event 'mode))
      ((and (frame-parameter frame 'drag-with-mode-line)
            (window-at-side-p window 'bottom)
-           (let ((minibuffer-window (minibuffer-window frame)))
-             (not (eq (window-frame minibuffer-window) frame))))
+           (memq (minibuffer-window-type frame) '(loose top)))
       ;; Drag frame when the window is on the bottom of its frame and
       ;; there is no minibuffer window below.
       (mouse-drag-frame start-event 'move)))))
@@ -556,13 +553,19 @@ mouse-drag-header-line
 the frame instead."
   (interactive "e")
   (let* ((start (event-start start-event))
-	 (window (posn-window start)))
-    (if (and (window-live-p window)
-             (not (window-at-side-p window 'top)))
-        (mouse-drag-line start-event 'header)
-      (let ((frame (window-frame window)))
-        (when (frame-parameter frame 'drag-with-header-line)
-          (mouse-drag-frame start-event 'move))))))
+	 (window (posn-window start))
+         (frame (window-frame window)))
+    (cond
+     ((and (window-live-p window)
+           (or (not (window-at-side-p window 'top))
+               (and (eq (minibuffer-window-type frame) 'top)
+                    (or (minibuffer-window-active-p (minibuffer-window frame))
+                        (not resize-mini-windows)))))
+      (mouse-drag-line start-event 'header))
+     ((and (frame-parameter frame 'drag-with-header-line)
+           (window-at-side-p window 'top)
+           (not (eq (minibuffer-window-type frame) 'top)))
+      (mouse-drag-frame start-event 'move)))))

 (defun mouse-drag-vertical-line (start-event)
   "Change the width of a window by dragging on a vertical line.
@@ -1285,7 +1288,7 @@ mouse-drag-track
      t (lambda ()
          (setq track-mouse old-track-mouse)
          (setq auto-hscroll-mode auto-hscroll-mode-saved)
-          (deactivate-mark)
+         (deactivate-mark)
          (pop-mark)))))

 (defun mouse--drag-set-mark-and-point (start click click-count)
@@ -2446,12 +2449,13 @@ mouse-drag-and-drop-region

     (ignore-errors
       (track-mouse
+        (setq track-mouse 'dropping)
         ;; When event was "click" instead of "drag", skip loop.
         (while (progn
                  (setq event (read-key))      ; read-event or read-key
                  (or (mouse-movement-p event)
                      ;; Handle `mouse-autoselect-window'.
-                     (eq (car-safe event) 'select-window)))
+                     (memq (car event) '(select-window switch-frame))))
           ;; Obtain the dragged text in region.  When the loop was
           ;; skipped, value-selection remains nil.
           (unless value-selection
diff --git a/lisp/net/ange-ftp.el b/lisp/net/ange-ftp.el
index 2fc7ac2..f3058aa 100644
--- a/lisp/net/ange-ftp.el
+++ b/lisp/net/ange-ftp.el
@@ -1090,7 +1090,7 @@ ange-ftp-message
   "Display message in echo area, but indicate if truncated.
 Args are as in `message': a format string, plus arguments to be formatted."
   (let ((msg (apply #'format-message fmt args))
-	(max (window-width (minibuffer-window))))
+	(max (window-width (echo-window))))
     (if noninteractive
 	msg
       (if (>= (length msg) max)
@@ -1586,7 +1586,7 @@ ange-ftp-process-handle-hash
   (and ange-ftp-hash-mark-unit
        ange-ftp-process-msg
        ange-ftp-process-verbose
-       (not (eq (selected-window) (minibuffer-window)))
+       (not (eq (selected-window) (minibuffer-window nil 'maybe-nil)))
        (not (boundp 'search-message))	;screws up isearch otherwise
        (not cursor-in-echo-area)	;screws up y-or-n-p otherwise
        (let ((kbytes (ash (* ange-ftp-hash-mark-unit
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index 108e368..28cb881 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -2055,7 +2055,8 @@ rcirc-visible-buffers

 (defvar rcirc-visible-buffers nil)
 (defun rcirc-window-configuration-change ()
-  (unless (minibuffer-window-active-p (minibuffer-window))
+  (unless (and (minibuffer-window nil 'maybe-nil)
+               (minibuffer-window-active-p (minibuffer-window)))
     ;; delay this until command has finished to make sure window is
     ;; actually visible before clearing activity
     (add-hook 'post-command-hook 'rcirc-window-configuration-change-1)))
@@ -2200,7 +2201,7 @@ join
                           split-channels))
          (channels (mapconcat 'identity split-channels ",")))
     (rcirc-send-string process (concat "JOIN " channels))
-    (when (not (eq (selected-window) (minibuffer-window)))
+    (when (not (eq (selected-window) (minibuffer-window nil 'maybe-nil)))
       (dolist (b buffers) ;; order the new channel buffers in the buffer list
         (switch-to-buffer b)))))

diff --git a/lisp/simple.el b/lisp/simple.el
index 8d77047..a2a650e 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2451,7 +2451,7 @@ undo
     ;; Check to see whether we're hitting a redo record, and if
     ;; so, ask the user whether she wants to skip the redo/undo pair.
     (let ((equiv (gethash pending-undo-list undo-equiv-table)))
-      (or (eq (selected-window) (minibuffer-window))
+      (or (eq (selected-window) (minibuffer-window nil 'maybe-nil))
 	  (setq message (format "%s%s!"
                                 (if (or undo-no-redo (not equiv))
                                     "Undo" "Redo")
@@ -3627,7 +3627,7 @@ display-message-or-buffer
 	   (let ((lines
 		  (if (= (buffer-size) 0)
 		      0
-		    (count-screen-lines nil nil nil (minibuffer-window)))))
+		    (count-screen-lines nil nil nil (echo-window)))))
 	     (cond ((= lines 0))
 		   ((and (or (<= lines 1)
 			     (<= lines
diff --git a/lisp/term.el b/lisp/term.el
index 9f8f1f7..c051980 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -4009,7 +4009,7 @@ term-dynamic-complete-filename
 Returns t if successful."
   (interactive)
   (when (term-match-partial-filename)
-    (prog2 (or (eq (selected-window) (minibuffer-window))
+    (prog2 (or (eq (selected-window) (minibuffer-window nil 'maybe-nil))
 	       (message "Completing file name..."))
 	(term-dynamic-complete-as-filename))))

@@ -4030,7 +4030,7 @@ term-dynamic-complete-as-filename
 	 (pathnondir (file-name-nondirectory filename))
 	 (directory (if pathdir (term-directory pathdir) default-directory))
 	 (completion (file-name-completion pathnondir directory))
-	 (mini-flag (eq (selected-window) (minibuffer-window))))
+	 (mini-flag (eq (selected-window) (minibuffer-window nil 'maybe-nil))))
     (cond ((null completion)
            (message "No completions of %s" filename)
 	   (setq success nil))
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el
index e6f436f..a094e76 100644
--- a/lisp/textmodes/ispell.el
+++ b/lisp/textmodes/ispell.el
@@ -306,7 +306,7 @@ ispell-following-word
 (defcustom ispell-help-in-bufferp nil
   "Non-nil means display interactive keymap help in a buffer.
 The following values are supported:
-  nil        Expand the minibuffer and display a short help message
+  nil        Expand the echo window and display a short help message
              there for a couple of seconds.
   t          Pop up a new buffer and display a short help message there
              for a couple of seconds.
@@ -2458,16 +2458,10 @@ ispell-help
 	      (ispell-display-buffer buffer)
 	      (sit-for 5)
 	      (kill-buffer "*Ispell Help*"))
-	  (unwind-protect
-	      (let ((resize-mini-windows 'grow-only))
-		(select-window (minibuffer-window))
-		(erase-buffer)
-		(message nil)
-		;;(set-minibuffer-window (selected-window))
-		(enlarge-window 2)
-		(insert (concat help-1 "\n" help-2 "\n" help-3))
-		(sit-for 5))
-	    (erase-buffer)))))))
+	  (let ((resize-mini-windows 'exactly)
+                message-log-max)
+	    (message (concat help-1 "\n" help-2 "\n" help-3))
+	    (sit-for 5)))))))

 (define-obsolete-function-alias 'lookup-words 'ispell-lookup-words "24.4")

diff --git a/lisp/type-break.el b/lisp/type-break.el
index c7cdc46..aeb42ca 100644
--- a/lisp/type-break.el
+++ b/lisp/type-break.el
@@ -535,7 +535,7 @@ type-break
     (while continue
       (save-window-excursion
         ;; Eat the screen.
-        (and (eq (selected-window) (minibuffer-window))
+        (and (eq (selected-window) (minibuffer-window nil 'maybe-nil))
              (other-window 1))
         (delete-other-windows)
         (scroll-right (window-width))
@@ -700,7 +700,7 @@ type-break-check
      (type-break-alarm-p
       (cond
        ((input-pending-p))
-       ((eq (selected-window) (minibuffer-window)))
+       ((eq (selected-window) (minibuffer-window nil 'maybe-nil)))
        ((and min-threshold
              (< type-break-keystroke-count min-threshold))
         (type-break-schedule))
@@ -722,7 +722,7 @@ type-break-check
      ((and max-threshold
            (> type-break-keystroke-count max-threshold)
            (not (input-pending-p))
-           (not (eq (selected-window) (minibuffer-window))))
+           (not (eq (selected-window) (minibuffer-window nil 'maybe-nil))))
       (type-break-keystroke-reset)
       (setq type-break-keystroke-count (or min-threshold 0))
       (type-break-query)))))
@@ -812,7 +812,7 @@ type-break-time-warning
                                                 type-break-time-next-break)))
       (setq type-break-warning-countdown-string (number-to-string timeleft))
       (cond
-       ((eq (selected-window) (minibuffer-window)))
+       ((eq (selected-window) (minibuffer-window nil 'maybe-nil)))
        ;; Do nothing if the command was just a prefix arg, since that will
        ;; immediately be followed by some other interactive command.
        ;; Otherwise, it is particularly annoying for the sit-for below to
@@ -837,7 +837,7 @@ type-break-keystroke-warning
           (number-to-string (- (cdr type-break-keystroke-threshold)
                                type-break-keystroke-count)))
     (cond
-     ((eq (selected-window) (minibuffer-window)))
+     ((eq (selected-window) (minibuffer-window nil 'maybe-nil)))
      ;; Do nothing if the command was just a prefix arg, since that will
      ;; immediately be followed by some other interactive command.
      ;; Otherwise, it is particularly annoying for the sit-for below to
diff --git a/lisp/vc/ediff-wind.el b/lisp/vc/ediff-wind.el
index 0535aa6..61b5336 100644
--- a/lisp/vc/ediff-wind.el
+++ b/lisp/vc/ediff-wind.el
@@ -327,7 +327,7 @@ ediff-setup-windows
   ;; Make sure we are not in the minibuffer window when we try to delete
   ;; all other windows.
   (run-hooks 'ediff-before-setup-windows-hook)
-  (if (eq (selected-window) (minibuffer-window))
+  (if (eq (selected-window) (minibuffer-window nil 'maybe-nil))
       (other-window 1))

   ;; in case user did a no-no on a tty
diff --git a/lisp/vc/emerge.el b/lisp/vc/emerge.el
index fc8c318..8a975f1 100644
--- a/lisp/vc/emerge.el
+++ b/lisp/vc/emerge.el
@@ -1330,7 +1330,7 @@ emerge-merge-directories
 (defun emerge-setup-windows (buffer-A buffer-B merge-buffer &optional pos)
   ;; Make sure we are not in the minibuffer window when we try to delete
   ;; all other windows.
-  (if (eq (selected-window) (minibuffer-window))
+  (if (eq (selected-window) (minibuffer-window nil 'maybe-nil))
       (other-window 1))
   (delete-other-windows)
   (switch-to-buffer merge-buffer)
@@ -2970,7 +2970,7 @@ emerge-define-key-if-possible
 ;; Show the name of the file in the buffer.
 (defun emerge-show-file-name ()
   "Displays the name of the file loaded into the current buffer.
-If the name won't fit on one line, the minibuffer is expanded to hold it,
+If the name won't fit on one line, the echo window is expanded to hold it,
 and the command waits for a keystroke from the user.  If the keystroke is
 SPC, it is ignored; if it is anything else, it is processed as a command."
   (interactive)
@@ -2978,7 +2978,7 @@ emerge-show-file-name
     (or name
 	(setq name "Buffer has no file name."))
     (save-window-excursion
-      (select-window (minibuffer-window))
+      (select-window (echo-window))
       (unwind-protect
 	  (progn
 	    (erase-buffer)
diff --git a/lisp/window.el b/lisp/window.el
index d56bed6..bc5edda 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -582,12 +582,12 @@ walk-window-tree
   (walk-window-tree-1 fun (frame-root-window frame) any)
   (when (memq minibuf '(nil t))
     ;; Run FUN on FRAME's minibuffer window if requested.
-    (let ((minibuffer-window (minibuffer-window frame)))
-      (when (and (window-live-p minibuffer-window)
-		 (eq (window-frame minibuffer-window) frame)
+    (let ((mini-window (minibuffer-window frame 'maybe-nil)))
+      (when (and mini-window
+                 (eq (window-frame mini-window) frame)
 		 (or (eq minibuf t)
-		     (minibuffer-window-active-p minibuffer-window)))
-	(funcall fun minibuffer-window)))))
+		     (minibuffer-window-active-p mini-window)))
+	(funcall fun mini-window)))))

 (defun walk-window-subtree (fun &optional window any)
   "Run function FUN on the subtree of windows rooted at WINDOW.
@@ -1594,8 +1594,11 @@ window--min-size-1
 	  value)
       (with-current-buffer (window-buffer window)
 	(cond
-	 ((window-minibuffer-p window)
-	  (if pixelwise (frame-char-height (window-frame window)) 1))
+	 ((window-mini-p window)
+	  (if pixelwise
+              (+ (frame-char-height (window-frame window))
+                 (window-bottom-divider-width window))
+            1))
 	 ((window-size-fixed-p window horizontal ignore)
 	  ;; The minimum size of a fixed size window is its size.
 	  (window-size window horizontal pixelwise))
@@ -2042,6 +2045,18 @@ window-resizable-p
 (defalias 'window-height 'window-total-height)
 (defalias 'window-width 'window-body-width)

+(defun window-minibuffer-fixed-p (&optional window)
+  "Return non-nil if WINDOW is a fixed minibuffer window.
+A minibuffer window is \"fixed\" if it is live and its minibuffer
+window type is either 'top' or 'bottom'."
+  (memq (window-minibuffer-p window) '(top bottom)))
+
+(defun window-minibuffer-non-loose-p (&optional window)
+  "Return non-nil if WINDOW is a non-loose minibuffer window.
+A minibuffer window is \"non-loose\" if it is live and its
+minibuffer window type is 'top', 'bottom' or 'only'."
+  (memq (window-minibuffer-p window) '(top bottom only)))
+
 (defun window-full-height-p (&optional window)
   "Return t if WINDOW is as high as its containing frame.
 More precisely, return t if and only if the total height of
@@ -2049,8 +2064,12 @@ window-full-height-p
 frame.  WINDOW must be a valid window and defaults to the
 selected one."
   (setq window (window-normalize-window window))
-  (if (window-minibuffer-p window)
+  (if (window-minibuffer-non-loose-p window)
+      ;; If WINDOW is a non-loose minibuffer window, it is full-height
+      ;; iff there are no other windows on its frame (which holds only
+      ;; for minibuffer-only frames).
       (eq window (frame-root-window (window-frame window)))
+    ;; Otherwise, WINDOW must me as high as its frame's root window.
     (= (window-pixel-height window)
        (window-pixel-height (frame-root-window window)))))

@@ -2078,35 +2097,35 @@ window-body-size
 (declare-function font-info "font.c" (name &optional frame))

 (defun window-font-width (&optional window face)
-   "Return average character width for the font of FACE used in WINDOW.
+  "Return average character width for the font of FACE used in WINDOW.
 WINDOW must be a live window and defaults to the selected one.

 If FACE is nil or omitted, the default face is used.  If FACE is
 remapped (see `face-remapping-alist'), the function returns the
 information for the remapped face."
-   (with-selected-window (window-normalize-window window t)
-     (if (display-multi-font-p)
-	 (let* ((face (if face face 'default))
-		(info (font-info (face-font face)))
-		(width (aref info 11)))
-	   (if (> width 0)
+  (with-selected-window (window-normalize-window window t)
+    (if (display-multi-font-p)
+	(let* ((face (if face face 'default))
+	       (info (font-info (face-font face)))
+	       (width (aref info 11)))
+	  (if (> width 0)
 	      width
-	     (aref info 10)))
-       (frame-char-width))))
+	    (aref info 10)))
+      (frame-char-width))))

 (defun window-font-height (&optional window face)
-   "Return character height for the font of FACE used in WINDOW.
+  "Return character height for the font of FACE used in WINDOW.
 WINDOW must be a live window and defaults to the selected one.

 If FACE is nil or omitted, the default face is used.  If FACE is
 remapped (see `face-remapping-alist'), the function returns the
 information for the remapped face."
-   (with-selected-window (window-normalize-window window t)
-     (if (display-multi-font-p)
-	 (let* ((face (if face face 'default))
-		(info (font-info (face-font face))))
-	   (aref info 3))
-       (frame-char-height))))
+  (with-selected-window (window-normalize-window window t)
+    (if (display-multi-font-p)
+	(let* ((face (if face face 'default))
+	       (info (font-info (face-font face))))
+	  (aref info 3))
+      (frame-char-height))))

 (defvar overflow-newline-into-fringe)

@@ -2297,7 +2316,7 @@ window-in-direction
 		  (window-pixel-top window)))
 	 (last (+ first (window-size window hor t)))
 	 ;; The column / row value of `posn-at-point' can be nil for the
-	 ;; mini-window, guard against that.
+	 ;; minibuffer window, guard against that.
 	 (posn
 	  (cond
 	   ((and (numberp sign) (< sign 0))
@@ -2671,30 +2690,31 @@ window--pixel-to-total
 	 (root-size (window-size root horizontal t))
 	 ;; We have to care about the minibuffer window only if it
 	 ;; appears together with the root window on this frame.
-	 (mini (let ((mini (minibuffer-window frame)))
-		 (and (eq (window-frame mini) frame)
-		      (not (eq mini root)) mini)))
-	 (mini-size (and mini (window-size mini horizontal t))))
+	 (mini-window
+          (and (memq (minibuffer-window-type frame) '(bottom top))
+               (minibuffer-window frame 'maybe-nil)))
+	 (mini-size (and mini-window (window-size mini-window horizontal t))))
     ;; We round the line/column sizes of windows here to the nearest
     ;; integer.  In some cases this can make windows appear _larger_
     ;; than the containing frame (line/column-wise) because the latter's
     ;; sizes are not (yet) rounded.  We might eventually fix that.
-    (if (and mini (not horizontal))
+    (if (and mini-window (not horizontal))
 	(let (lines)
 	  (set-window-new-total root (max (/ root-size char-size) 1))
-	  (set-window-new-total mini (max (/ mini-size char-size) 1))
+	  (set-window-new-total mini-window (max (/ mini-size char-size) 1))
 	  (setq lines (- (round (+ root-size mini-size) char-size)
-			 (+ (window-new-total root) (window-new-total mini))))
+			 (+ (window-new-total root)
+                            (window-new-total mini-window))))
 	  (while (> lines 0)
 	    (if (>= (% root-size (window-new-total root))
-		    (% mini-size (window-new-total mini)))
+		    (% mini-size (window-new-total mini-window)))
 		(set-window-new-total root 1 t)
-	      (set-window-new-total mini 1 t))
+	      (set-window-new-total mini-window 1 t))
 	    (setq lines (1- lines))))
       (set-window-new-total root (round root-size char-size))
-      (when mini
+      (when mini-window
 	;; This is taken in the horizontal case only.
-	(set-window-new-total mini (round mini-size char-size))))
+	(set-window-new-total mini-window (round mini-size char-size))))
     (unless (window-buffer root)
       (window--pixel-to-total-1 root horizontal char-size))
     ;; Apply the new sizes.
@@ -2727,22 +2747,35 @@ window--resize-mini-window
   "Resize minibuffer window WINDOW by DELTA pixels.
 If WINDOW cannot be resized by DELTA pixels make it as large (or
 as small) as possible, but don't signal an error."
-  (when (window-minibuffer-p window)
+  (when (window-mini-p window)
     (let* ((frame (window-frame window))
 	   (root (frame-root-window frame))
 	   (height (window-pixel-height window))
+           (char-height (frame-char-height frame))
 	   (min-delta
-	    (- (window-pixel-height root)
-	       (window-min-size root nil nil t))))
+            (if (window-minibuffer-fixed-p window)
+                (- (window-pixel-height root)
+	           (window-min-size root nil nil t))
+              (window--resizable window delta nil window nil nil nil t))))
       ;; Sanitize DELTA.
       (cond
        ((<= (+ height delta) 0)
-	(setq delta (- (frame-char-height (window-frame window)) height)))
+	(setq delta (- (frame-char-height frame) height)))
        ((> delta min-delta)
 	(setq delta min-delta)))

       (unless (zerop delta)
-	;; Resize now.
+        (if (or (eq (window-minibuffer-p window) 'loose)
+                (eq (window-echo-p window) 'loose))
+            ;; A loose mini window gets resized like a normal window.
+            ;; Bind `window-resize-pixelwise' to non-nil when DELTA is
+            ;; not a multiple of the frame's character height so we
+            ;; can accommodate a divider below it.
+            (let ((window-resize-pixelwise
+                   (not (zerop (mod delta char-height)))))
+              (window-resize window delta nil window t)
+              (window-preserve-size window nil t))
+        ;; Resize now.
 	(window--resize-reset frame)
 	;; Ideally we should be able to resize just the last child of root
 	;; here.  See the comment in `resize-root-window-vertically' for
@@ -2751,9 +2784,9 @@ window--resize-mini-window
 	(set-window-new-pixel window (+ height delta))
 	;; The following routine catches the case where we want to resize
 	;; a minibuffer-only frame.
-	(when (resize-mini-window-internal window)
+	(when (resize-minibuffer-window-internal window)
 	  (window--pixel-to-total frame)
-	  (run-window-configuration-change-hook frame))))))
+	  (run-window-configuration-change-hook frame)))))))

 (defun window--resize-apply-p (frame &optional horizontal)
   "Return t when a window on FRAME shall be resized vertically.
@@ -2801,26 +2834,27 @@ window-resize
 instead."
   (setq window (window-normalize-window window))
   (let* ((frame (window-frame window))
-	 (minibuffer-window (minibuffer-window frame))
+	 (mini-window (minibuffer-window frame 'maybe-nil))
 	 sibling)
     (setq delta (window--size-to-pixel
 		 window delta horizontal pixelwise t))
     (cond
      ((eq window (frame-root-window frame))
       (error "Cannot resize the root window of a frame"))
-     ((window-minibuffer-p window)
+     ((window-minibuffer-non-loose-p window)
       (if horizontal
 	  (error "Cannot resize minibuffer window horizontally")
 	(window--resize-mini-window window delta)))
      ((and (not horizontal)
 	   (window-full-height-p window)
-	   (eq (window-frame minibuffer-window) frame)
-	   (or (not resize-mini-windows)
-	       (eq minibuffer-window (active-minibuffer-window))))
+           mini-window
+           (window-minibuffer-fixed-p mini-window)
+           (or (not resize-mini-windows)
+	       (eq mini-window (active-minibuffer-window))))
       ;; If WINDOW is full height and either `resize-mini-windows' is
       ;; nil or the minibuffer window is active, resize the minibuffer
       ;; window.
-      (window--resize-mini-window minibuffer-window (- delta)))
+      (window--resize-mini-window mini-window (- delta)))
      ((or (window--resizable-p
 	   window delta horizontal ignore nil nil nil t)
 	  (and (not ignore)
@@ -3344,11 +3378,16 @@ window--resize-root-window-vertically
      ((< pixel-delta 0)
       (setq pixel-delta (window-sizable window pixel-delta nil nil pixelwise))
       (window--resize-reset frame)
-      ;; When shrinking the root window, emulate an edge drag in order
-      ;; to not resize other windows if we can avoid it (Bug#12419).
-      (window--resize-this-window
-       window pixel-delta nil ignore t 'before
-       (+ (window-pixel-top window) (window-pixel-height window)))
+      (if (eq (minibuffer-window-type frame) 'bottom)
+          ;; When the mini window is at the bottom, emulate an edge
+          ;; drag in order to not resize other windows if we can avoid
+          ;; it (Bug#12419).
+          (window--resize-this-window
+           window pixel-delta nil ignore t 'before
+           (+ (window-pixel-top window) (window-pixel-height window)))
+        ;; With the minibuffer window elsewhere this doesn't make
+        ;; sense though.
+        (window--resize-this-window window pixel-delta nil ignore t))
       ;; Don't record new normal sizes to make sure that shrinking back
       ;; proportionally works as intended.
       (walk-window-tree
@@ -3366,6 +3405,18 @@ window--resize-root-window-vertically
 	 pixel-delta
        (/ pixel-delta (frame-char-height frame)))))

+(defun window--resize-minibuffer-frame (root delta)
+  "Resize height of ROOT's frame by DELTA pixels.
+ROOT must specify the root window of a live minibuffer-only
+frame."
+  (let* ((frame (window-frame root))
+         (height
+          (+ (- (frame-text-height frame)
+                (frame-inner-height frame))
+             (window-pixel-height root)
+             delta)))
+    (set-frame-height frame height nil t)))
+
 (defun window--sanitize-window-sizes (horizontal)
   "Assert that all windows on selected frame are large enough.
 If necessary and possible, make sure that every window on frame
@@ -3403,10 +3454,9 @@ adjust-window-trailing-edge
 move it as far as possible in the desired direction."
   (setq window (window-normalize-window window))
   (let* ((frame (window-frame window))
-	 (minibuffer-window (minibuffer-window frame))
+	 (mini-window (minibuffer-window frame 'maybe-nil))
 	 (right window)
 	 left first-left first-right this-delta min-delta max-delta ignore)
-
     (unless pixelwise
       (setq pixelwise t)
       (setq delta (* delta (frame-char-size window horizontal))))
@@ -3417,15 +3467,24 @@ adjust-window-trailing-edge
 		(setq right (window-parent right))))
     (cond
      ((and (not right) (not horizontal)
-	   ;; Resize the minibuffer window if it's on the same frame as
-	   ;; and immediately below WINDOW and it's either active or
-	   ;; `resize-mini-windows' is nil.
-	   (eq (window-frame minibuffer-window) frame)
-	   (= (nth 1 (window-pixel-edges minibuffer-window))
-	      (nth 3 (window-pixel-edges window)))
-	   (or (not resize-mini-windows)
-	       (eq minibuffer-window (active-minibuffer-window))))
-      (window--resize-mini-window minibuffer-window (- delta)))
+	   ;; Resize the mini window if it's just below WINDOW and
+	   ;; it's either active or `resize-mini-windows' is nil.
+           (window-at-side-p window 'bottom)
+           mini-window (not (eq window mini-window))
+           (eq (window-minibuffer-p mini-window) 'bottom))
+      (if (or (eq mini-window (active-minibuffer-window))
+              (not resize-mini-windows))
+          (window--resize-mini-window mini-window (- delta))
+        ;; The following error is usually caught elsewhere.
+	(user-error "Cannot resize inactive minibuffer window")))
+     ((and (not right) (not horizontal)
+	   ;; Resize WINDOW if it's a mini window fixed at the top and
+           ;; it's either active or `resize-mini-windows' is nil.
+           (eq (window-minibuffer-p window) 'top))
+      (if (or (eq window (active-minibuffer-window))
+              (not resize-mini-windows))
+          (window--resize-mini-window window delta)
+	(user-error "Cannot resize inactive minibuffer window")))
      ((or (not (setq left right)) (not (setq right (window-right right))))
       (if horizontal
 	  (user-error "No window on the right of this one")
@@ -3583,26 +3642,29 @@ enlarge-window
 make selected window wider by DELTA columns.  If DELTA is
 negative, shrink selected window by -DELTA lines or columns."
   (interactive "p")
-  (let ((minibuffer-window (minibuffer-window)))
+  (let ((mini-window (minibuffer-window nil 'maybe-nil)))
     (when (window-preserved-size nil horizontal)
       (window-preserve-size nil horizontal))
     (cond
      ((zerop delta))
      ((window-size-fixed-p nil horizontal)
       (user-error "Selected window has fixed size"))
-     ((window-minibuffer-p)
+     ((window-minibuffer-non-loose-p)
       (if horizontal
 	  (user-error "Cannot resize minibuffer window horizontally")
 	(window--resize-mini-window
          (selected-window) (* delta (frame-char-height)))))
      ((and (not horizontal)
 	   (window-full-height-p)
-	   (eq (window-frame minibuffer-window) (selected-frame))
-	   (not resize-mini-windows))
-      ;; If the selected window is full height and `resize-mini-windows'
-      ;; is nil, resize the minibuffer window.
+           mini-window
+           (window-minibuffer-fixed-p mini-window)
+           (or (not resize-mini-windows)
+	       (eq mini-window (active-minibuffer-window))))
+      ;; If WINDOW is full height and either `resize-mini-windows' is
+      ;; nil or the minibuffer window is active, resize the minibuffer
+      ;; window.
       (window--resize-mini-window
-       minibuffer-window (* (- delta) (frame-char-height))))
+       mini-window (* (- delta) (frame-char-height))))
      ((window--resizable-p nil delta horizontal)
       (window-resize nil delta horizontal))
      ((window--resizable-p nil delta horizontal 'preserved)
@@ -3626,26 +3688,28 @@ shrink-window
 make selected window narrower by DELTA columns.  If DELTA is
 negative, enlarge selected window by -DELTA lines or columns."
   (interactive "p")
-  (let ((minibuffer-window (minibuffer-window)))
+  (let ((mini-window (minibuffer-window nil 'maybe-nil)))
     (when (window-preserved-size nil horizontal)
       (window-preserve-size nil horizontal))
     (cond
      ((zerop delta))
      ((window-size-fixed-p nil horizontal)
       (user-error "Selected window has fixed size"))
-     ((window-minibuffer-p)
+     ((window-minibuffer-non-loose-p)
       (if horizontal
 	  (user-error "Cannot resize minibuffer window horizontally")
 	(window--resize-mini-window
          (selected-window) (* (- delta) (frame-char-height)))))
      ((and (not horizontal)
 	   (window-full-height-p)
-	   (eq (window-frame minibuffer-window) (selected-frame))
-	   (not resize-mini-windows))
+           mini-window
+           (window-minibuffer-fixed-p mini-window)
+           (or (not resize-mini-windows)
+	       (eq mini-window (active-minibuffer-window))))
       ;; If the selected window is full height and `resize-mini-windows'
       ;; is nil, resize the minibuffer window.
       (window--resize-mini-window
-       minibuffer-window (* delta (frame-char-height))))
+       mini-window (* delta (frame-char-height))))
      ((window--resizable-p nil (- delta) horizontal)
       (window-resize nil (- delta) horizontal))
      ((window--resizable-p nil (- delta) horizontal 'preserved)
@@ -3964,11 +4028,12 @@ one-window-p

 Anything else means consider all windows on the selected frame
 and no others."
-  (let ((base-window (selected-window)))
-    (if (and nomini (eq base-window (minibuffer-window)))
-	(setq base-window (next-window base-window)))
+  (let ((base-window (selected-window))
+        (mini-window (minibuffer-window nil 'maybe-nil)))
+    (when (and nomini (eq base-window mini-window))
+      (setq base-window (next-window base-window)))
     (eq base-window
-	(next-window base-window (if nomini 'arg) all-frames))))
+	(next-window base-window (and nomini 'arg) all-frames))))
 \f
 ;;; Deleting windows.
 (defun window-deletable-p (&optional window)
@@ -3996,16 +4061,15 @@ window-deletable-p
 		  ;; currently uses our minibuffer window.
 		  (catch 'other
 		    (dolist (other (frame-list))
-		      (when (and (not (eq other frame))
-				 (eq (window-frame (minibuffer-window other))
-				     frame))
-			(throw 'other t))))
-		  (let ((minibuf (active-minibuffer-window)))
-		    (and minibuf (eq frame (window-frame minibuf)))))
+                      (let ((mini-window (minibuffer-window other 'maybe-nil)))
+                        (when (and (not (eq other frame)) mini-window
+				   (eq (window-frame mini-window) frame))
+			  (throw 'other t)))))
+		  (let ((mini-window (active-minibuffer-window)))
+		    (and mini-window (eq frame (window-frame mini-window)))))
 	'frame))
-     ((window-minibuffer-p window)
-      ;; If WINDOW is the minibuffer window of a non-minibuffer-only
-      ;; frame, it cannot be deleted separately.
+     ((window-minibuffer-fixed-p window)
+      ;; A fixed minibuffer window cannot be removed from its frame.
       nil)
      ((or ignore-window-parameters
 	  (not (eq window (window-main-window frame))))
@@ -4149,9 +4213,8 @@ delete-other-windows
 	  (throw 'done (delete-other-windows atom-root))))
        ((window-parameter window 'window-side)
 	(error "Cannot make side window the only window"))
-       ((and (window-minibuffer-p window)
-	     (not (eq window (frame-root-window window))))
-	(error "Can't expand minibuffer to full frame")))
+       ((window-minibuffer-fixed-p window)
+	(error "Can't expand fixed minibuffer window to full frame")))

       (cond
        ((or ignore-window-parameters
@@ -4344,7 +4407,7 @@ switch-to-prev-buffer
 	 (next-buffers (window-next-buffers window))
          (pred (frame-parameter frame 'buffer-predicate))
 	 entry new-buffer killed-buffers visible)
-    (when (window-minibuffer-p window)
+    (when (window-minibuffer-non-loose-p window)
       ;; Don't switch in minibuffer window.
       (unless (setq window (minibuffer-selected-window))
 	(error "Window %s is a minibuffer window" window)))
@@ -4466,7 +4529,7 @@ switch-to-next-buffer
 	 (next-buffers (window-next-buffers window))
          (pred (frame-parameter frame 'buffer-predicate))
 	 new-buffer entry killed-buffers visible)
-    (when (window-minibuffer-p window)
+    (when (window-minibuffer-non-loose-p window)
       ;; Don't switch in minibuffer window.
       (unless (setq window (minibuffer-selected-window))
 	(error "Window %s is a minibuffer window" window)))
@@ -4661,7 +4724,7 @@ next-buffer
   "In selected window switch to next buffer."
   (interactive)
   (cond
-   ((window-minibuffer-p)
+   ((window-minibuffer-non-loose-p)
     (error "Cannot switch buffers in minibuffer window"))
    ((eq (window-dedicated-p) t)
     (error "Window is strongly dedicated to its buffer"))
@@ -4672,7 +4735,7 @@ previous-buffer
   "In selected window switch to previous buffer."
   (interactive)
   (cond
-   ((window-minibuffer-p)
+   ((window-minibuffer-non-loose-p)
     (error "Cannot switch buffers in minibuffer window"))
    ((eq (window-dedicated-p) t)
     (error "Window is strongly dedicated to its buffer"))
@@ -4897,6 +4960,24 @@ quit-windows-on
 	;; If a window doesn't show BUFFER, unrecord BUFFER in it.
 	(unrecord-window-buffer window buffer)))))
 \f
+(defun window--combination-resizable (parent &optional horizontal)
+  "Return number of pixels recoverable from height of window PARENT.
+PARENT must be a vertical (horizontal if HORIZONTAL is non-nil)
+window combination.  The return value is the sum of the pixel
+heights of all non-fixed height child windows of PARENT divided
+by their number plus 1.  If HORIZONTAL is non-nil, return the sum
+of the pixel widths of all non-fixed width child windows of
+PARENT divided by their number plus 1."
+  (let ((sibling (window-child parent))
+	(number 0)
+	(size 0))
+    (while sibling
+      (unless (window-size-fixed-p sibling horizontal)
+	(setq number (1+ number))
+	(setq size (+ (window-size sibling horizontal t) size)))
+      (setq sibling (window-next-sibling sibling)))
+    (/ size (1+ number))))
+
 (defun split-window (&optional window size side pixelwise)
   "Make a new window adjacent to WINDOW.
 WINDOW must be a valid window and defaults to the selected one.
@@ -5029,8 +5110,7 @@ split-window
 		    ;; average size of a window in its combination.
 		    (max (min (- parent-pixel-size
 				 (window-min-size parent horizontal nil t))
-			      (/ parent-pixel-size
-				 (1+ (window-combinations parent horizontal))))
+			      (window--combination-resizable parent horizontal))
 			 (window-min-pixel-size))
 		  ;; Else try to give the new window half the size
 		  ;; of WINDOW (plus an eventual odd pixel).
@@ -6592,7 +6672,7 @@ split-window-sensibly
                                     (throw 'done nil)))
                                 frame)
               t)))
-	 (not (window-minibuffer-p window))
+	 (not (window-minibuffer-non-loose-p window))
 	 (let ((split-height-threshold 0))
 	   (when (window-splittable-p window)
 	     (with-selected-window window
@@ -6632,7 +6712,7 @@ window--frame-usable-p
       ;; "dead" by `window-live-p'.  Hence if `window' is not live we
       ;; implicitly know that `frame' has a visible window we can use.
       (unless (and (window-live-p window)
-                   (or (window-minibuffer-p window)
+                   (or (eq (window-minibuffer-p window) 'only)
                        ;; If the window is soft-dedicated, the frame is usable.
                        ;; Actually, even if the window is really dedicated,
                        ;; the frame is still usable by splitting it.
@@ -6685,15 +6765,20 @@ window--display-buffer
 TYPE must be one of the symbols `reuse', `window' or `frame' and
 is passed unaltered to `display-buffer-record-window'.  ALIST is
 the alist argument of `display-buffer'.  Set `window-dedicated-p'
-to DEDICATED if non-nil.  Return WINDOW if BUFFER and WINDOW are
-live."
+to DEDICATED or to the value of a 'dedicated' entry in ALIST, if
+either of these is non-nil.  Return WINDOW if BUFFER and WINDOW
+are live."
   (when (and (buffer-live-p buffer) (window-live-p window))
     (display-buffer-record-window type window buffer)
     (unless (eq buffer (window-buffer window))
       (set-window-dedicated-p window nil)
       (set-window-buffer window buffer))
-    (when dedicated
-      (set-window-dedicated-p window dedicated))
+    (let ((alist-dedicated (assq 'dedicated alist)))
+      (cond
+       (alist-dedicated
+        (set-window-dedicated-p window (cdr alist-dedicated)))
+       (dedicated
+        (set-window-dedicated-p window dedicated))))
     (when (memq type '(window frame))
       (set-window-prev-buffers window nil))
     (let ((quit-restore (window-parameter window 'quit-restore))
@@ -7094,7 +7179,7 @@ display-buffer-same-window
 another buffer; in that case, return nil.  Otherwise, return the
 selected window."
   (unless (or (cdr (assq 'inhibit-same-window alist))
-	      (window-minibuffer-p)
+	      (window-minibuffer-non-loose-p)
 	      (window-dedicated-p))
     (window--display-buffer buffer (selected-window) 'reuse alist)))

@@ -7405,7 +7490,7 @@ display-buffer-at-bottom
 	     (window--display-buffer
 	      buffer bottom-window 'reuse alist display-buffer-mark-dedicated))
 	(and (not (frame-parameter nil 'unsplittable))
-	     (let (split-width-threshold)
+	     (let (split-height-threshold)
 	       (setq window (window--try-to-split-window bottom-window alist)))
 	     (window--display-buffer
 	      buffer window 'window alist display-buffer-mark-dedicated))
@@ -7418,6 +7503,40 @@ display-buffer-at-bottom
 	     (window--display-buffer
 	      buffer window 'reuse alist display-buffer-mark-dedicated)))))

+(defun display-buffer-at-top (buffer alist)
+  "Try displaying BUFFER in a window at the top of the selected frame.
+This either reuses such a window provided it shows BUFFER
+already, splits a window at the top of the frame or the frame's
+root window, or reuses some window at the top of the selected
+frame."
+  (let (top-window top-window-shows-buffer window)
+    (walk-window-tree
+     (lambda (window)
+       (cond
+	((window-in-direction 'above window))
+	((and (not top-window-shows-buffer)
+	      (eq buffer (window-buffer window)))
+	 (setq top-window-shows-buffer t)
+	 (setq top-window window))
+	((not top-window)
+	 (setq top-window window))))
+     nil nil 'nomini)
+    (or (and top-window-shows-buffer
+	     (window--display-buffer
+	      buffer top-window 'reuse alist display-buffer-mark-dedicated))
+	(and (not (frame-parameter nil 'unsplittable))
+             (setq window (split-window-no-error top-window nil 'above))
+	     (window--display-buffer
+	      buffer window 'window alist display-buffer-mark-dedicated))
+	(and (not (frame-parameter nil 'unsplittable))
+             (setq window (split-window-no-error
+                           (window-main-window) nil 'above))
+	     (window--display-buffer
+	      buffer window 'window alist display-buffer-mark-dedicated))
+	(and (not (window-dedicated-p top-window))
+	     (window--display-buffer
+	      buffer top-window 'reuse alist display-buffer-mark-dedicated)))))
+
 (defun display-buffer-in-previous-window (buffer alist)
   "Display BUFFER in a window previously showing it.
 If ALIST has a non-nil `inhibit-same-window' entry, the selected
@@ -7707,7 +7826,7 @@ switch-to-buffer
   (interactive
    (let ((force-same-window
           (cond
-           ((window-minibuffer-p) nil)
+           ((window-mini-p) nil)
            ((not (eq (window-dedicated-p) t)) 'force-same-window)
            ((pcase switch-to-buffer-in-dedicated-window
               (`nil (user-error
@@ -7729,13 +7848,13 @@ switch-to-buffer
      ;; Don't call set-window-buffer if it's not needed since it
      ;; might signal an error (e.g. if the window is dedicated).
      ((eq buffer (window-buffer)))
-     ((window-minibuffer-p)
+     ((window-minibuffer-non-loose-p)
       (if force-same-window
-          (user-error "Cannot switch buffers in minibuffer window")
+          (user-error "Cannot switch buffers in this minibuffer window")
         (pop-to-buffer buffer norecord)))
      ((eq (window-dedicated-p) t)
       (if force-same-window
-          (user-error "Cannot switch buffers in a dedicated window")
+          (user-error "Cannot switch buffers in dedicated window")
         (pop-to-buffer buffer norecord)))
      (t
       (let* ((entry (assq buffer (window-prev-buffers)))
@@ -7803,6 +7922,60 @@ switch-to-buffer-other-frame
   (interactive
    (list (read-buffer-to-switch "Switch to buffer in other frame: ")))
   (pop-to-buffer buffer-or-name display-buffer--other-frame-action norecord))
+
+(defcustom display-minibuffer-action
+  '((display-buffer-reuse-window
+     display-buffer-below-selected
+     display-buffer-pop-up-window)
+    (reusable-frames . 0)
+    (window-height . 1)
+    (preserve-size . (nil . t))
+    (dedicated . t))
+  "The `display-buffer' action for displaying the minibuffer window.
+This affects the display of loose minibuffer windows only."
+  :type display-buffer--action-custom-type
+  :risky t
+  :version "27.1"
+  :group 'windows)
+
+(defun display-minibuffer (buffer frame)
+  "Display minibuffer BUFFER for frame FRAME.
+This function is called to display a new, loose minibuffer window
+for FRAME.  Its default display action is specified by the
+variable `display-minibuffer-action'.  If it returns nil, Emacs
+will try to make a new minibuffer-only frame on FRAME's display
+via `make-initial-minibuffer-frame'."
+  (with-selected-frame frame
+    (condition-case nil
+        (display-buffer buffer display-minibuffer-action)
+      (error nil))))
+
+(defcustom display-echo-action
+  '((display-buffer-reuse-window
+     display-buffer-at-bottom)
+    (reusable-frames . 0)
+    (window-height . 1)
+    (preserve-size . (nil . t))
+    (dedicated . t)
+    (window-parameters
+     (no-other-window . t)
+     (no-delete-other-windows . t)))
+  "The `display-buffer' action for displaying the echo window.
+This affects the display of separate echo windows only."
+  :type display-buffer--action-custom-type
+  :risky t
+  :version "27.1"
+  :group 'windows)
+
+(defun display-echo (buffer frame)
+  "Display BUFFER in echo window for frame FRAME.
+This function is called to display a new echo window.  Its
+default display action is specified by the variable
+`display-echo-action'."
+  (with-selected-frame frame
+    (condition-case nil
+        (display-buffer buffer display-echo-action)
+      (error nil))))
 \f
 (defun set-window-text-height (window height)
   "Set the height in lines of the text display area of WINDOW to HEIGHT.
@@ -8003,10 +8176,13 @@ window--sanitize-margin
 (defun fit-frame-to-buffer (&optional frame max-height min-height max-width min-width only)
   "Adjust size of FRAME to display the contents of its buffer exactly.
 FRAME can be any live frame and defaults to the selected one.
-Fit only if FRAME's root window is live.  MAX-HEIGHT, MIN-HEIGHT,
-MAX-WIDTH and MIN-WIDTH specify bounds on the new total size of
-FRAME's root window.  MIN-HEIGHT and MIN-WIDTH default to the values of
-`window-min-height' and `window-min-width' respectively.
+Fit only if FRAME's root window is live.
+
+MAX-HEIGHT, MIN-HEIGHT, MAX-WIDTH and MIN-WIDTH specify bounds on
+the new total size of FRAME's root window.  MIN-HEIGHT and
+MIN-WIDTH default to the values of `window-min-height' and
+`window-min-width' respectively.  These arguments are specified
+in the canonical character width and height of WINDOW's frame.

 If the optional argument ONLY is `vertically', resize the frame
 vertically only.  If ONLY is `horizontally', resize the frame
@@ -9257,3 +9433,37 @@ ctl-x-map
 (define-key ctl-x-4-map "0" 'kill-buffer-and-window)

 ;;; window.el ends here
+
+(defun display-buffer-expected-window-size (buffer-or-name &optional full)
+  (let* ((selected-window (selected-window))
+         (point (window-point selected-window))
+         (pop-up-frame-alist
+          (cons (if full '(visibility . nil) '(no-make-frame . t))
+                pop-up-frame-alist))
+	 (confs
+          (let (list)
+            (dolist (frame (frame-list))
+              (push (cons frame (current-window-configuration frame)) list))
+            list))
+	 (window (display-buffer buffer-or-name))
+         (delete (catch 'new
+                   (when window
+                     (let ((frame (window-frame window)))
+                       (dolist (conf confs)
+                         (when (eq frame (car conf))
+                           (throw 'new frame)))))))
+         (value (when window
+		  (cons (window-pixel-width window)
+			(window-pixel-height window)))))
+    (when delete (delete-frame delete))
+
+    (dolist (conf confs)
+      (let ((frame (car conf)))
+        (when (frame-live-p frame)
+          (with-selected-frame frame
+            (set-window-configuration (cdr conf))))))
+
+    (when (window-live-p selected-window)
+      (set-window-point selected-window point))
+
+    value))
diff --git a/src/buffer.c b/src/buffer.c
index 244c185..1b4115d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1733,8 +1733,9 @@ This does not change the name of the visited file (if any).  */)
      to kill the buffer.  This must be done after the questions
      since anything can happen within do_yes_or_no_p.  */

-  /* Don't kill the minibuffer now current.  */
-  if (EQ (buffer, XWINDOW (minibuf_window)->contents))
+  /* Don't kill the buffer of the minibuffer window.  */
+  if (WINDOW_LIVE_P (minibuf_window)
+      && EQ (buffer, WINDOW_BUFFER (XWINDOW (minibuf_window))))
     return Qnil;

   /* When we kill an ordinary buffer which shares its buffer text
@@ -1781,7 +1782,8 @@ This does not change the name of the visited file (if any).  */)
   /* If the buffer now current is shown in the minibuffer and our buffer
      is the sole other buffer give up.  */
   XSETBUFFER (tem, current_buffer);
-  if (EQ (tem, XWINDOW (minibuf_window)->contents)
+  if (WINDOW_LIVE_P (minibuf_window)
+      && EQ (tem, WINDOW_BUFFER (XWINDOW (minibuf_window)))
       && EQ (buffer, Fother_buffer (buffer, Qnil, Qnil)))
     return Qnil;

diff --git a/src/callint.c b/src/callint.c
index 807e1cc..3962a33 100644
--- a/src/callint.c
+++ b/src/callint.c
@@ -26,6 +26,7 @@
 #include "buffer.h"
 #include "keyboard.h"
 #include "window.h"
+#include "frame.h"

 static Lisp_Object preserved_fns;

@@ -391,7 +392,7 @@
 	      && (w = XCAR (w), CONSP (w))
 	      && (w = XCAR (w), WINDOWP (w)))
 	    {
-	      if (MINI_WINDOW_P (XWINDOW (w))
+	      if (MINIBUFFER_WINDOW_P (XWINDOW (w))
 		  && ! (minibuf_level > 0 && EQ (w, minibuf_window)))
 		error ("Attempt to select inactive minibuffer window");

diff --git a/src/dispextern.h b/src/dispextern.h
index 2180c9a..ab3ffe9 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -1422,8 +1422,9 @@ struct glyph_string
      ((!mode_line_in_non_selected_windows			\
        || (SELW) == XWINDOW (selected_window)			\
        || (minibuf_level > 0					\
-           && !NILP (minibuf_selected_window)			\
-           && (MBW) == XWINDOW (minibuf_window)			\
+	   && WINDOW_LIVE_P (minibuf_window)			\
+	   && (MBW) == XWINDOW (minibuf_window)			\
+	   && WINDOW_LIVE_P (minibuf_selected_window)		\
            && (SCRW) == XWINDOW (minibuf_selected_window)))	\
       ? MODE_LINE_FACE_ID					\
       : MODE_LINE_INACTIVE_FACE_ID)
@@ -3227,7 +3228,7 @@ struct glyph_row *row_containing_pos (struct window *, ptrdiff_t,
 int line_bottom_y (struct it *);
 int default_line_pixel_height (struct window *);
 bool display_prop_intangible_p (Lisp_Object, Lisp_Object, ptrdiff_t, ptrdiff_t);
-void resize_echo_area_exactly (void);
+void resize_mini_windows_exactly (void);
 bool resize_mini_window (struct window *, bool);
 void set_vertical_scroll_bar (struct window *);
 void set_horizontal_scroll_bar (struct window *);
diff --git a/src/dispnew.c b/src/dispnew.c
index fc6f9e2..1834946 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -1996,8 +1996,7 @@ struct glyph_row *
   window_change_flags = 0;
   matrix_dim
     = allocate_matrices_for_frame_redisplay (FRAME_ROOT_WINDOW (f),
-					     0, top_window_y,
-					     1,
+					     0, top_window_y, 1,
 					     &window_change_flags);

   /* Add in menu bar lines, if any.  */
@@ -3399,9 +3398,9 @@ struct glyph_row *
   if (!force_p)
     detect_input_pending_ignore_squeezables ();

-  /* If forced to complete the update, or if no input is pending, do
-     the update.  */
-  if (force_p || !input_pending || !NILP (do_mouse_tracking))
+  /* If forced to complete the update, no input is pending or we are
+     tracking the mouse do the update.  */
+  if (force_p || !input_pending || !NILP (track_mouse))
     {
       struct glyph_row *row, *end;
       struct glyph_row *mode_line_row;
@@ -3900,14 +3899,17 @@ struct glyph_row *

   if (cursor_in_echo_area
       && !NILP (echo_area_buffer[0])
+      /* The following conditions seem over-engineered and could be
+	 probably simplified.  */
       /* If we are showing a message instead of the mini-buffer,
 	 show the cursor for the message instead.  */
-      && XWINDOW (minibuf_window) == w
+      && WINDOW_LIVE_P (minibuf_window)
+      && w == XWINDOW (minibuf_window)
       && EQ (minibuf_window, echo_area_window)
       /* These cases apply only to the frame that contains
 	 the active mini-buffer window.  */
-      && FRAME_HAS_MINIBUF_P (f)
-      && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window))
+      && MINIBUFFER_WINDOW_LOCAL_P (f)
+      && EQ (MINIBUFFER_WINDOW (f), echo_area_window))
     {
       cx = cy = vpos = hpos = 0;

@@ -4547,6 +4549,8 @@ struct row_entry
   if (!pause_p && set_cursor_p)
     {
       if ((cursor_in_echo_area
+	   /* The following conditions seem over-engineered and could
+	      be probably simplified.  */
 	   /* If we are showing a message instead of the mini-buffer,
 	      show the cursor for the message instead of for the
 	      (now hidden) mini-buffer contents.  */
@@ -4555,16 +4559,18 @@ struct row_entry
 	       && !NILP (echo_area_buffer[0])))
 	  /* These cases apply only to the frame that contains
 	     the active mini-buffer window.  */
-	  && FRAME_HAS_MINIBUF_P (f)
-	  && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window))
+	  && MINIBUFFER_WINDOW_LOCAL_P (f)
+	  && EQ (MINIBUFFER_WINDOW (f), echo_area_window))
 	{
-	  int top = WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f)));
+	  int top = WINDOW_TOP_EDGE_LINE (XWINDOW (MINIBUFFER_WINDOW (f)));
 	  int col;

 	  /* Put cursor at the end of the prompt.  If the mini-buffer
 	     is several lines high, find the last line that has
 	     any text on it.  */
-	  int row = FRAME_TOTAL_LINES (f);
+	  int row = (MINIBUFFER_WINDOW_TOP_P (f)
+		     ? WINDOW_BOTTOM_EDGE_LINE (XWINDOW (MINIBUFFER_WINDOW (f)))
+		     : FRAME_TOTAL_LINES (f));
 	  do
 	    {
 	      row--;
diff --git a/src/frame.c b/src/frame.c
index 85ec740..1b4759e 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -83,14 +83,9 @@
 {
   f->buffer_predicate = val;
 }
-static void
-fset_minibuffer_window (struct frame *f, Lisp_Object val)
-{
-  f->minibuffer_window = val;
-}

 struct frame *
-decode_live_frame (register Lisp_Object frame)
+decode_live_frame (Lisp_Object frame)
 {
   if (NILP (frame))
     frame = selected_frame;
@@ -99,7 +94,7 @@ struct frame *
 }

 struct frame *
-decode_any_frame (register Lisp_Object frame)
+decode_any_frame (Lisp_Object frame)
 {
   if (NILP (frame))
     frame = selected_frame;
@@ -107,6 +102,14 @@ struct frame *
   return XFRAME (frame);
 }

+struct frame *
+decode_frame_or_window (Lisp_Object frame_or_window)
+{
+  return (WINDOW_VALID_P (frame_or_window)
+	  ? WINDOW_XFRAME (XWINDOW (frame_or_window))
+	  : decode_live_frame (frame_or_window));
+}
+
 #ifdef HAVE_WINDOW_SYSTEM
 bool
 display_available (void)
@@ -119,6 +122,7 @@ struct frame *
 decode_window_system_frame (Lisp_Object frame)
 {
   struct frame *f = decode_live_frame (frame);
+
   check_window_system (f);
 #ifdef HAVE_WINDOW_SYSTEM
   return f;
@@ -211,7 +215,7 @@ struct frame *
      most of the commands try to apply themselves to the minibuffer
      frame itself, and get an error because you can't switch buffers
      in or split the minibuffer window.  */
-  if (FRAME_MINIBUF_ONLY_P (f))
+  if (MINIBUFFER_WINDOW_ONLY_P (f))
     return;

   if (TYPE_RANGED_INTEGERP (int, value))
@@ -574,8 +578,8 @@ struct frame *
   int old_windows_width = WINDOW_PIXEL_WIDTH (r);
   int old_windows_height
     = (WINDOW_PIXEL_HEIGHT (r)
-       + ((FRAME_HAS_MINIBUF_P (f) && !FRAME_MINIBUF_ONLY_P (f))
-	  ? WINDOW_PIXEL_HEIGHT (XWINDOW (FRAME_MINIBUF_WINDOW (f)))
+       + (MINIBUFFER_WINDOW_FIXED_P (f)
+	  ? WINDOW_PIXEL_HEIGHT (XWINDOW (MINIBUFFER_WINDOW (f)))
 	  : 0));
   int new_windows_width, new_windows_height;
   int old_text_width = FRAME_TEXT_WIDTH (f);
@@ -796,14 +800,41 @@ struct frame *
   return ALLOCATE_ZEROED_PSEUDOVECTOR (struct frame, face_cache, PVEC_FRAME);
 }

+/**
+ * set_minibuffer_window_buffer:
+ *
+ * Set buffer of MINIBUFFER_WINDOW to the first one on Vminibuffer_list or
+ * the return value of get_minibuffer (0).
+ */
+static void
+set_minibuffer_window_buffer (Lisp_Object minibuffer_window)
+{
+  /* Use set_window_buffer instead of Fset_window_buffer (see
+     discussion of bug#11984, bug#12025, bug#12026).  */
+  set_window_buffer (minibuffer_window,
+		     (NILP (Vminibuffer_list)
+		      ? get_minibuffer (0)
+		      : Fcar (Vminibuffer_list)),
+		     0, 0);
+}
+
+/**
+ * make_frame:
+ *
+ * Make a new frame and return it.  MINI_TYPE specifies the type of
+ * the minibuffer window and must be either Qnil (specifying that no
+ * minibuffer window is made) or Qtop or Qbottom (specifying that the
+ * minibuffer window should appear on the top or bottom of the frame).
+ */
 struct frame *
-make_frame (bool mini_p)
+make_frame (Lisp_Object mini_type)
 {
   Lisp_Object frame;
   struct frame *f;
   struct window *rw, *mw UNINIT;
   Lisp_Object root_window;
-  Lisp_Object mini_window;
+  Lisp_Object minibuffer_window = Qnil;
+  Lisp_Object buffer = Fcurrent_buffer ();

   f = allocate_frame ();
   XSETFRAME (frame, f);
@@ -852,80 +883,81 @@ struct frame *
 #endif
 #endif

-  root_window = make_window ();
-  rw = XWINDOW (root_window);
-  if (mini_p)
-    {
-      mini_window = make_window ();
-      mw = XWINDOW (mini_window);
-      wset_next (rw, mini_window);
-      wset_prev (mw, root_window);
-      mw->mini = 1;
-      wset_frame (mw, frame);
-      fset_minibuffer_window (f, mini_window);
-      store_frame_param (f, Qminibuffer, Qt);
-    }
-  else
-    {
-      mini_window = Qnil;
-      wset_next (rw, Qnil);
-      fset_minibuffer_window (f, Qnil);
-    }
-
-  wset_frame (rw, frame);
-
-  /* 10 is arbitrary,
-     just so that there is "something there."
+  /* 10 is arbitrary, just so that there is "something there."
      Correct size will be set up later with adjust_frame_size.  */
-
   SET_FRAME_COLS (f, 10);
   SET_FRAME_LINES (f, 10);
   SET_FRAME_WIDTH (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f));
   SET_FRAME_HEIGHT (f, FRAME_LINES (f) * FRAME_LINE_HEIGHT (f));

+  root_window = make_window ();
+  rw = XWINDOW (root_window);
+  wset_frame (rw, frame);
   rw->total_cols = 10;
   rw->pixel_width = rw->total_cols * FRAME_COLUMN_WIDTH (f);
-  rw->total_lines = mini_p ? 9 : 10;
-  rw->pixel_height = rw->total_lines * FRAME_LINE_HEIGHT (f);

-  if (mini_p)
+  if (!NILP (mini_type))
     {
-      mw->top_line = rw->total_lines;
-      mw->pixel_top = rw->pixel_height;
+      minibuffer_window = make_window ();
+      mw = XWINDOW (minibuffer_window);
+      /* Note: The mini window is always the "next" window of the root
+      window regardless of whether it is at the top or the bottom of
+      the frame.  This avoids unnecessary complications in the display
+      routines which always scan the root window first and scan any
+      fixed minibuffer window via the root windows's next field.  */
+      wset_next (rw, minibuffer_window);
+      wset_prev (mw, root_window);
+      wset_frame (mw, frame);
+      /* No horizontal scroll bars in minibuffer window.  */
+      wset_horizontal_scroll_bar (mw, Qnil);
+      mw->minibuffer = true;
+      fset_minibuffer_window (f, minibuffer_window);
+      store_frame_param (f, Qminibuffer, minibuffer_window);
+      /* Share minibuffer window with echo area.  */
+      mw->echo = true;
+      fset_echo_window (f, minibuffer_window);
+      store_frame_param (f, Qecho_area, Qnil);
+      /* Set up geometry for root and minibuffer window.  */
       mw->total_cols = rw->total_cols;
       mw->pixel_width = rw->pixel_width;
+      rw->total_lines = 9;
       mw->total_lines = 1;
       mw->pixel_height = FRAME_LINE_HEIGHT (f);
+      /* Adjust vertical positions according to the type of the
+	 minibuffer window.  */
+      if (EQ (mini_type, Qtop))
+	{
+	  f->minibuffer_window_type = minibuffer_window_top;
+	  rw->top_line = 1;
+	  rw->pixel_top = FRAME_LINE_HEIGHT (f);
+	}
+      else
+	{
+	  f->minibuffer_window_type = minibuffer_window_bottom;
+	  mw->top_line = rw->total_lines;
+	  mw->pixel_top = rw->pixel_height;
+	}
+
+      /* Initialize the minibuffer window's buffer.  */
+      set_minibuffer_window_buffer (minibuffer_window);
     }
+  else
+    rw->total_lines = 10;

-  /* Choose a buffer for the frame's root window.  */
-  {
-    Lisp_Object buf = Fcurrent_buffer ();
-
-    /* If current buffer is hidden, try to find another one.  */
-    if (BUFFER_HIDDEN_P (XBUFFER (buf)))
-      buf = other_buffer_safely (buf);
-
-    /* Use set_window_buffer, not Fset_window_buffer, and don't let
-       hooks be run by it.  The reason is that the whole frame/window
-       arrangement is not yet fully initialized at this point.  Windows
-       don't have the right size, glyph matrices aren't initialized
-       etc.  Running Lisp functions at this point surely ends in a
-       SEGV.  */
-    set_window_buffer (root_window, buf, 0, 0);
-    fset_buffer_list (f, list1 (buf));
-  }
+  rw->pixel_height = rw->total_lines * FRAME_LINE_HEIGHT (f);

-  if (mini_p)
-    {
-      set_window_buffer (mini_window,
-			 (NILP (Vminibuffer_list)
-			  ? get_minibuffer (0)
-			  : Fcar (Vminibuffer_list)),
-			 0, 0);
-      /* No horizontal scroll bars in minibuffers.  */
-      wset_horizontal_scroll_bar (mw, Qnil);
-    }
+  /* If the current buffer is hidden, try to find another one.  */
+  if (BUFFER_HIDDEN_P (XBUFFER (buffer)))
+    buffer = other_buffer_safely (buffer);
+
+  /* Use set_window_buffer, not Fset_window_buffer, and don't let
+     hooks be run by it.  The reason is that the whole frame/window
+     arrangement is not yet fully initialized at this point.  Windows
+     don't have the right size, glyph matrices aren't initialized
+     etc.  Running Lisp functions at this point surely ends in a
+     SEGV.  */
+  set_window_buffer (root_window, buffer, 0, 0);
+  fset_buffer_list (f, list1 (buffer));

   fset_root_window (f, root_window);
   fset_selected_window (f, root_window);
@@ -937,97 +969,106 @@ struct frame *
 }
 \f
 #ifdef HAVE_WINDOW_SYSTEM
-/* Make a frame using a separate minibuffer window on another frame.
-   MINI_WINDOW is the minibuffer window to use.  nil means use the
-   default (the global minibuffer).  */
-
+/**
+ * make_frame_without_minibuffer:
+ *
+ * Make a new minibuffer-less frame on DISPLAY and return it.
+ * MINIBUFFER_WINDOW specifies the minibuffer window to use.  If it is
+ * Qnil, this means to use the minibuffer window of the default
+ * minibuffer frame for keyboard KB, creating a minibuffer-only frame
+ * on DISPLAY, frame for keyboard KB, creating a minibuffer-only frame
+ * on DISPLAY, if necessary.  Otherwise, MINIBUFFER_WINDOW has to
+ * specify an existing minibuffer window on KB.
+ */
 struct frame *
-make_frame_without_minibuffer (Lisp_Object mini_window, KBOARD *kb,
+make_frame_without_minibuffer (Lisp_Object minibuffer_window, KBOARD *kb,
 			       Lisp_Object display)
 {
   struct frame *f;

-  if (!NILP (mini_window))
-    CHECK_LIVE_WINDOW (mini_window);
-
-  if (!NILP (mini_window)
-      && FRAME_KBOARD (XFRAME (XWINDOW (mini_window)->frame)) != kb)
-    error ("Frame and minibuffer must be on the same terminal");
+  if (!NILP (minibuffer_window) && !EQ (minibuffer_window, Qloose))
+    {
+      CHECK_LIVE_WINDOW (minibuffer_window);
+      if (FRAME_KBOARD (WINDOW_XFRAME (XWINDOW (minibuffer_window))) != kb)
+	error ("Frame and minibuffer window must be on same keyboard");
+    }

   /* Make a frame containing just a root window.  */
-  f = make_frame (0);
+  f = make_frame (Qnil);

-  if (NILP (mini_window))
+  if (EQ (minibuffer_window, Qloose))
     {
-      /* Use default-minibuffer-frame if possible.  */
-      if (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
-	  || ! FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame))))
-	{
-          Lisp_Object frame_dummy;
-
-          XSETFRAME (frame_dummy, f);
-	  /* If there's no minibuffer frame to use, create one.  */
-	  kset_default_minibuffer_frame
-	    (kb, call1 (intern ("make-initial-minibuffer-frame"), display));
-	}
+      /* For a loose minibuffer window just store the minibuffer type
+	 and the minibuffer parameter and return.  */
+      f->minibuffer_window_type = minibuffer_window_loose;
+      store_frame_param (f, Qminibuffer, Qloose);

-      mini_window
-	= XFRAME (KVAR (kb, Vdefault_minibuffer_frame))->minibuffer_window;
+      return f;
+    }
+  else if (NILP (minibuffer_window))
+    {
+      /* Use 'default-minibuffer-frame' if it is live and has a local
+	 minibuffer window.  */
+      Lisp_Object minibuffer_frame = KVAR (kb, Vdefault_minibuffer_frame);
+
+      if (!FRAMEP (minibuffer_frame)
+	  || !FRAME_LIVE_P (XFRAME (minibuffer_frame))
+	  || !MINIBUFFER_WINDOW_LOCAL_P (XFRAME (minibuffer_frame)))
+	/* If there's no suitable 'default-minibuffer-frame', make
+	   one.  */
+	kset_default_minibuffer_frame
+	  (kb, call1 (Qmake_initial_minibuffer_frame, display));
+
+      minibuffer_window
+	= MINIBUFFER_WINDOW (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)));
     }

-  fset_minibuffer_window (f, mini_window);
-  store_frame_param (f, Qminibuffer, mini_window);
+  fset_minibuffer_window (f, minibuffer_window);
+  store_frame_param (f, Qminibuffer, minibuffer_window);
+  /* A non-loose minibuffer window shares the echo area.  */
+  fset_echo_window (f, minibuffer_window);
+  store_frame_param (f, Qecho_area, Qnil);

   /* Make the chosen minibuffer window display the proper minibuffer,
      unless it is already showing a minibuffer.  */
-  if (NILP (Fmemq (XWINDOW (mini_window)->contents, Vminibuffer_list)))
-    /* Use set_window_buffer instead of Fset_window_buffer (see
-       discussion of bug#11984, bug#12025, bug#12026).  */
-    set_window_buffer (mini_window,
-		       (NILP (Vminibuffer_list)
-			? get_minibuffer (0)
-			: Fcar (Vminibuffer_list)), 0, 0);
+  if (NILP (Fmemq (XWINDOW (minibuffer_window)->contents, Vminibuffer_list)))
+    set_minibuffer_window_buffer (minibuffer_window);
+
   return f;
 }

-/* Make a frame containing only a minibuffer window.  */
-
+/**
+ * make_minibuffer_frame:
+ *
+ * Make a new minibuffer-only frame and return it.
+ */
 struct frame *
 make_minibuffer_frame (void)
 {
-  /* First make a frame containing just a root window, no minibuffer.  */
-
-  register struct frame *f = make_frame (0);
-  register Lisp_Object mini_window;
-  register Lisp_Object frame;
+  /* First make a frame containing just a root window.  */
+  struct frame *f = make_frame (Qnil);
+  Lisp_Object minibuffer_window = f->root_window;
+  Lisp_Object frame;

   XSETFRAME (frame, f);

-  f->auto_raise = 0;
-  f->auto_lower = 0;
-  f->no_split = 1;
-  f->wants_modeline = 0;
+  f->auto_raise = false;
+  f->auto_lower = false;
+  f->no_split = true;
+  f->wants_modeline = false;

-  /* Now label the root window as also being the minibuffer.
-     Avoid infinite looping on the window chain by marking next pointer
-     as nil. */
-
-  mini_window = f->root_window;
-  fset_minibuffer_window (f, mini_window);
+  /* The minibuffer window is the root window.  */
+  XWINDOW (minibuffer_window)->minibuffer = true;
+  fset_minibuffer_window (f, minibuffer_window);
+  f->minibuffer_window_type = minibuffer_window_only;
   store_frame_param (f, Qminibuffer, Qonly);
-  XWINDOW (mini_window)->mini = 1;
-  wset_next (XWINDOW (mini_window), Qnil);
-  wset_prev (XWINDOW (mini_window), Qnil);
-  wset_frame (XWINDOW (mini_window), frame);
-
-  /* Put the proper buffer in that window.  */
+  /* Share minibuffer window with echo area.  */
+  XWINDOW (minibuffer_window)->echo = true;
+  fset_echo_window (f, minibuffer_window);
+  store_frame_param (f, Qecho_area, Qnil);
+  /* Initialize the minibuffer window's buffer.  */
+  set_minibuffer_window_buffer (minibuffer_window);

-  /* Use set_window_buffer instead of Fset_window_buffer (see
-     discussion of bug#11984, bug#12025, bug#12026).  */
-  set_window_buffer (mini_window,
-		     (NILP (Vminibuffer_list)
-		      ? get_minibuffer (0)
-		      : Fcar (Vminibuffer_list)), 0, 0);
   return f;
 }
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -1036,6 +1077,12 @@ struct frame *

 static printmax_t tty_frame_count;

+/**
+ * make_initial_frame:
+ *
+ * Make the initial frame and return it.  The initial frame is always
+ * a frame with a minibuffer window at the bottom.
+ */
 struct frame *
 make_initial_frame (void)
 {
@@ -1051,7 +1098,7 @@ struct frame *

   terminal = init_initial_terminal ();

-  f = make_frame (1);
+  f = make_frame (Qbottom);
   XSETFRAME (frame, f);

   Vframe_list = Fcons (frame, Vframe_list);
@@ -1092,6 +1139,12 @@ struct frame *
 }


+/**
+ * make_terminal_frame:
+ *
+ * Make a terminal frame and return it.  A terminal frame is always a
+ * frame with a minibuffer window at the bottom.
+ */
 static struct frame *
 make_terminal_frame (struct terminal *terminal)
 {
@@ -1102,7 +1155,7 @@ struct frame *
   if (!terminal->name)
     error ("Terminal is not live, can't create new frames on it");

-  f = make_frame (1);
+  f = make_frame (Qbottom);

   XSETFRAME (frame, f);
   Vframe_list = Fcons (frame, Vframe_list);
@@ -1372,15 +1425,16 @@ of them (the selected terminal frame) is actually displayed.
 	      /* Redirect frame focus also when FRAME has its minibuffer
 		 window on the selected frame (see Bug#24500).  */
 	      || (NILP (focus)
-		  && EQ (FRAME_MINIBUF_WINDOW (f), sf->selected_window)))
+		  && EQ (MINIBUFFER_WINDOW (f), sf->selected_window)))
 	    Fredirect_frame_focus (xfocus, frame);
 	}
     }
 #endif /* HAVE_X_WINDOWS */
 #endif /* ! 0 */

-  if (!for_deletion && FRAME_HAS_MINIBUF_P (sf))
-    resize_mini_window (XWINDOW (FRAME_MINIBUF_WINDOW (sf)), 1);
+  if (!for_deletion && MINIBUFFER_WINDOW_LOCAL_P (sf)
+      && !Vfit_minibuffer_only_frames)
+    resize_mini_window (XWINDOW (MINIBUFFER_WINDOW (sf)), 1);

   if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f))
     {
@@ -1408,8 +1462,8 @@ of them (the selected terminal frame) is actually displayed.
     }

   selected_frame = frame;
-  if (! FRAME_MINIBUF_ONLY_P (XFRAME (selected_frame)))
-    last_nonminibuf_frame = XFRAME (selected_frame);
+  if (!MINIBUFFER_WINDOW_P (XWINDOW (FRAME_ROOT_WINDOW (XFRAME (frame)))))
+    last_nonminibuf_frame = XFRAME (frame);

   Fselect_window (f->selected_window, norecord);

@@ -1553,18 +1607,26 @@ of them (the selected terminal frame) is actually displayed.
 #endif
   }

-/* Return CANDIDATE if it can be used as 'other-than-FRAME' frame on the
-   same tty (for tty frames) or among frames which uses FRAME's keyboard.
-   If MINIBUF is nil, do not consider minibuffer-only candidate.
-   If MINIBUF is `visible', do not consider an invisible candidate.
-   If MINIBUF is a window, consider only its own frame and candidate now
-   using that window as the minibuffer.
-   If MINIBUF is 0, consider candidate if it is visible or iconified.
-   Otherwise consider any candidate and return nil if CANDIDATE is not
-   acceptable.  */
-
+/**
+ * Return CANDIDATE if it can be used as "other frame" frame on the
+ * same tty (for tty frames) or among all frames which use FRAME's
+ * keyboard.  Both, CANDIDATE and FRAME must be live frames.  Return
+ * nil if CANDIDATE is not acceptable.  A frame with a non-nil
+ * 'no-other-frame' parameter is not usable.
+ *
+ * PRED specifies which frames are acceptable candidates.  If nil,
+ * this means to exclude minibuffer-only frames as candidates.  If
+ * PRED is a window, accept only that window's frame and any frame now
+ * using that window as its minibuffer window as candidates.  If PRED
+ * is 'visible', include only visible frames.  If PRED is 0, include
+ * all visible and iconified frames.  Otherwise, consider all frames
+ * as candidates.
+ *
+ * This function is called from prev_frame and next_frame only and
+ * does not check the validity of its arguments.
+*/
 static Lisp_Object
-candidate_frame (Lisp_Object candidate, Lisp_Object frame, Lisp_Object minibuf)
+candidate_frame (Lisp_Object candidate, Lisp_Object frame, Lisp_Object pred)
 {
   struct frame *c = XFRAME (candidate), *f = XFRAME (frame);

@@ -1575,25 +1637,24 @@ of them (the selected terminal frame) is actually displayed.
     {
       if (!NILP (get_frame_param (c, Qno_other_frame)))
 	return Qnil;
-      else if (NILP (minibuf))
+      else if (NILP (pred))
 	{
-	  if (!FRAME_MINIBUF_ONLY_P (c))
+	  if (!MINIBUFFER_WINDOW_ONLY_P (c))
 	    return candidate;
 	}
-      else if (EQ (minibuf, Qvisible))
+      else if (EQ (pred, Qvisible))
 	{
 	  if (FRAME_VISIBLE_P (c))
 	    return candidate;
 	}
-      else if (WINDOWP (minibuf))
+      else if (WINDOWP (pred))
 	{
-	  if (EQ (FRAME_MINIBUF_WINDOW (c), minibuf)
-	      || EQ (WINDOW_FRAME (XWINDOW (minibuf)), candidate)
-	      || EQ (WINDOW_FRAME (XWINDOW (minibuf)),
-		     FRAME_FOCUS_FRAME (c)))
+	  if (EQ (MINIBUFFER_WINDOW (c), pred)
+	      || EQ (WINDOW_FRAME (XWINDOW (pred)), candidate)
+	      || EQ (WINDOW_FRAME (XWINDOW (pred)), FRAME_FOCUS_FRAME (c)))
 	    return candidate;
 	}
-      else if (INTEGERP (minibuf) && XINT (minibuf) == 0)
+      else if (INTEGERP (pred) && XINT (pred) == 0)
 	{
 	  if (FRAME_VISIBLE_P (c) || FRAME_ICONIFIED_P (c))
 	    return candidate;
@@ -1601,50 +1662,71 @@ of them (the selected terminal frame) is actually displayed.
       else
 	return candidate;
     }
+
   return Qnil;
 }

-/* Return the next frame in the frame list after FRAME.  */
-
+/**
+ * next_frame:
+ *
+ * Return frame following FRAME on the list of live frames.
+ * For the meaning of FRAME and PRED see the doc-string of
+ * Fnext_frame.
+ *
+ * This function is called from Fnext_frame only and does not check
+ * the validity of its arguments.
+ */
 static Lisp_Object
-next_frame (Lisp_Object frame, Lisp_Object minibuf)
+next_frame (Lisp_Object frame, Lisp_Object pred)
 {
-  Lisp_Object f, tail;
+  Lisp_Object frames, frame1, next;
   int passed = 0;

   eassume (CONSP (Vframe_list));
-
   while (passed < 2)
-    FOR_EACH_FRAME (tail, f)
+    FOR_EACH_FRAME (frames, frame1)
       {
 	if (passed)
 	  {
-	    f = candidate_frame (f, frame, minibuf);
-	    if (!NILP (f))
-	      return f;
+	    /* Pre Emacs 27 versions could run into an infinite loop
+	       here when frame1 was FRAME and FRAME itself did not
+	       qualify as candidate.  */
+	    next = candidate_frame (frame1, frame, pred);
+	    if (!NILP (next))
+	      return next;
 	  }
-	if (EQ (frame, f))
+
+	if (EQ (frame1, frame))
 	  passed++;
       }
+
   return frame;
 }

-/* Return the previous frame in the frame list before FRAME.  */
-
+/**
+ * prev_frame:
+ *
+ * Return frame preceding FRAME on the list of live frames.
+ * For the meaning of FRAME and PRED see the doc-string of
+ * Fprev_frame.
+ *
+ * This function is called from Fprev_frame only and does not check
+ * the validity of its arguments.
+ */
 static Lisp_Object
-prev_frame (Lisp_Object frame, Lisp_Object minibuf)
+prev_frame (Lisp_Object frame, Lisp_Object pred)
 {
-  Lisp_Object f, tail, prev = Qnil;
+  Lisp_Object frames, frame1, prev = Qnil;

   eassume (CONSP (Vframe_list));
-
-  FOR_EACH_FRAME (tail, f)
+  FOR_EACH_FRAME (frames, frame1)
     {
-      if (EQ (frame, f) && !NILP (prev))
+      if (EQ (frame1, frame) && !NILP (prev))
 	return prev;
-      f = candidate_frame (f, frame, minibuf);
-      if (!NILP (f))
-	prev = f;
+
+      frame1 = candidate_frame (frame1, frame, pred);
+      if (!NILP (frame1))
+	prev = frame1;
     }

   /* We've scanned the entire list.  */
@@ -1653,54 +1735,62 @@ of them (the selected terminal frame) is actually displayed.
        acceptable frame.  Return the original frame.  */
     return frame;
   else
-    /* There were no acceptable frames in the list before FRAME; otherwise,
-       we would have returned directly from the loop.  Since PREV is the last
-       acceptable frame in the list, return it.  */
+    /* There were no acceptable frames in the list before FRAME;
+       otherwise, we would have returned directly from the loop.
+       Since PREV is the last acceptable frame in the list, return
+       it.  */
     return prev;
 }

-
 DEFUN ("next-frame", Fnext_frame, Snext_frame, 0, 2, 0,
-       doc: /* Return the next frame in the frame list after FRAME.
-It considers only frames on the same terminal as FRAME.
-By default, skip minibuffer-only frames.
-If omitted, FRAME defaults to the selected frame.
-If optional argument MINIFRAME is nil, exclude minibuffer-only frames.
-If MINIFRAME is a window, include only its own frame
-and any frame now using that window as the minibuffer.
-If MINIFRAME is `visible', include all visible frames.
-If MINIFRAME is 0, include all visible and iconified frames.
-Otherwise, include all frames.  */)
-  (Lisp_Object frame, Lisp_Object miniframe)
+       doc: /* Return frame following FRAME on the list of live frames.
+FRAME must be a live frame and defaults to the selected frame.  Any
+candidate frame must be on the same terminal as FRAME and its
+'no-other-frame' parameter must be nil.  Return FRAME if no acceptable
+candidate frame can be found.
+
+PRED specifies which frames to consider as candidates.  If omitted or
+nil, this means to exclude minibuffer-only frames as candidates.  If
+PRED is a window, include only that window's frame and any frame now
+using that window as its minibuffer window.  If PRED is 'visible',
+include only visible frames.  If PRED is 0, include all visible and
+iconified frames.  Otherwise, include all frames as candidates.  */)
+  (Lisp_Object frame, Lisp_Object pred)
 {
   if (NILP (frame))
     frame = selected_frame;
-  CHECK_LIVE_FRAME (frame);
-  return next_frame (frame, miniframe);
+  else
+    CHECK_LIVE_FRAME (frame);
+
+  return next_frame (frame, pred);
 }

 DEFUN ("previous-frame", Fprevious_frame, Sprevious_frame, 0, 2, 0,
-       doc: /* Return the previous frame in the frame list before FRAME.
-It considers only frames on the same terminal as FRAME.
-By default, skip minibuffer-only frames.
-If omitted, FRAME defaults to the selected frame.
-If optional argument MINIFRAME is nil, exclude minibuffer-only frames.
-If MINIFRAME is a window, include only its own frame
-and any frame now using that window as the minibuffer.
-If MINIFRAME is `visible', include all visible frames.
-If MINIFRAME is 0, include all visible and iconified frames.
-Otherwise, include all frames.  */)
-  (Lisp_Object frame, Lisp_Object miniframe)
+       doc: /* Return frame preceding FRAME on the list of live frames.
+FRAME must be a live frame and defaults to the selected frame.  Any
+candidate frame must be on the same terminal as FRAME and its
+'no-other-frame' parameter must be nil.  Return FRAME if no acceptable
+candidate frame can be found.
+
+PRED specifies which frames to consider as candidates.  If omitted or
+nil, this means to exclude minibuffer-only frames as candidates.  If
+PRED is a window, include only that window's frame and any frame now
+using that window as its minibuffer window.  If PRED is 'visible',
+include only visible frames.  If PRED is 0, include all visible and
+iconified frames.  Otherwise, include all frames as candidates.  */)
+  (Lisp_Object frame, Lisp_Object pred)
 {
   if (NILP (frame))
     frame = selected_frame;
-  CHECK_LIVE_FRAME (frame);
-  return prev_frame (frame, miniframe);
+  else
+    CHECK_LIVE_FRAME (frame);
+
+  return prev_frame (frame, pred);
 }

 DEFUN ("last-nonminibuffer-frame", Flast_nonminibuf_frame,
        Slast_nonminibuf_frame, 0, 0, 0,
-       doc: /* Return last non-minibuffer frame selected. */)
+       doc: /* Return last non-minibuffer frame selected.  */)
   (void)
 {
   Lisp_Object frame = Qnil;
@@ -1710,13 +1800,45 @@ of them (the selected terminal frame) is actually displayed.

   return frame;
 }
+
+DEFUN ("minibuffer-window-type", Fminibuffer_window_type,
+       Sminibuffer_window_type, 0, 1, 0,
+       doc: /* Return type of FRAME's minibuffer window.
+Return nil when FRAME does not own a minibuffer window.  Return 'top'
+when the minibuffer window is located at the top and 'bottom' when the
+minibuffer window appears at the bottom of FRAME.  Return 'only' when
+FRAME's only window is its minibuffer window.  Return 'embed' when
+FRAME has an embedded minibuffer window.  */)
+     (Lisp_Object frame)
+{
+  struct frame *f = decode_live_frame (frame);
+
+  switch (f->minibuffer_window_type)
+    {
+    case minibuffer_window_loose:
+      return Qloose;
+
+    case minibuffer_window_top:
+      return Qtop;
+
+    case minibuffer_window_bottom:
+      return Qbottom;
+
+    case minibuffer_window_only:
+      return Qonly;
+
+    default:
+      emacs_abort ();
+    }
+}
 \f
 /**
- * other_frames:
+ * other_frames_p:
  *
  * Return true if there exists at least one visible or iconified frame
- * but F.  Tooltip frames do not qualify as candidates.  Return false
- * if no such frame exists.
+ * but F.  Return false if no such frame exists.  Tooltip frames do
+ * not qualify as candidates, neither do child frames or frames with a
+ * non-nil 'delete-before' parameter.
  *
  * INVISIBLE true means we are called from make_frame_invisible where
  * such a frame must be visible or iconified.  INVISIBLE nil means we
@@ -1727,21 +1849,17 @@ of them (the selected terminal frame) is actually displayed.
  * least one X frame exists.
  */
 static bool
-other_frames (struct frame *f, bool invisible, bool force)
+other_frames_p (struct frame *f, bool invisible, bool force)
 {
   Lisp_Object frames, frame, frame1;
-  Lisp_Object minibuffer_window = FRAME_MINIBUF_WINDOW (f);

   XSETFRAME (frame, f);
-  if (WINDOWP (minibuffer_window)
-      && !EQ (frame, WINDOW_FRAME (XWINDOW (minibuffer_window))))
-    minibuffer_window = Qnil;

   FOR_EACH_FRAME (frames, frame1)
     {
       struct frame *f1 = XFRAME (frame1);

-      if (f != f1)
+      if (f1 != f)
 	{
 	  /* Verify that we can still talk to the frame's X window, and
 	     note any recent change in visibility.  */
@@ -1753,7 +1871,7 @@ of them (the selected terminal frame) is actually displayed.
 	      /* Tooltips and child frames count neither for
 		 invisibility nor for deletions.  */
 	      && !FRAME_PARENT_FRAME (f1)
-	      /* Frames with a non-nil `delete-before' parameter don't
+	      /* Frames with a non-nil 'delete-before' parameter don't
 		 count for deletions.  */
 	      && (invisible || NILP (get_frame_param (f1, Qdelete_before)))
 	      /* For invisibility and normal deletions, at least one
@@ -1771,52 +1889,65 @@ of them (the selected terminal frame) is actually displayed.
   return false;
 }

-/* Make sure that minibuf_window doesn't refer to FRAME's minibuffer
-   window.  Preferably use the selected frame's minibuffer window
-   instead.  If the selected frame doesn't have one, get some other
-   frame's minibuffer window.  SELECT non-zero means select the new
-   minibuffer window.  */
+/**
+ * sanitize_minibuf_window:
+ *
+ * Make sure that minibuf_window doesn't refer to FRAME's minibuffer
+ * window.  Preferably use the selected frame's minibuffer window
+ * instead.  If the selected frame doesn't have one, get some other
+ * frame's minibuffer window.  SELECT true means select the new
+ * minibuf_window.
+ *
+ * This routine is currently called by delete_frame (where FRAME is
+ * the frame that shall be deleted) and Fmake_frame_invisible (where
+ * FRAME is the frame that shall become invisible).
+ */
 static void
-check_minibuf_window (Lisp_Object frame, int select)
+sanitize_minibuf_window (Lisp_Object frame, bool select)
 {
   struct frame *f = decode_live_frame (frame);

   XSETFRAME (frame, f);

-  if (WINDOWP (minibuf_window) && EQ (f->minibuffer_window, minibuf_window))
+  if (MINIBUFFER_WINDOW_LOCAL_P (f)
+      && EQ (minibuf_window, MINIBUFFER_WINDOW (f)))
     {
-      Lisp_Object frames, this, window = make_number (0);
+      Lisp_Object frames, frame1, window = Qnil;

       if (!EQ (frame, selected_frame)
-	  && FRAME_HAS_MINIBUF_P (XFRAME (selected_frame)))
-	window = FRAME_MINIBUF_WINDOW (XFRAME (selected_frame));
+	  && MINIBUFFER_WINDOW_LOCAL_P (XFRAME (selected_frame)))
+	window = MINIBUFFER_WINDOW (XFRAME (selected_frame));
       else
-	FOR_EACH_FRAME (frames, this)
+	FOR_EACH_FRAME (frames, frame1)
 	  {
-	    if (!EQ (this, frame) && FRAME_HAS_MINIBUF_P (XFRAME (this)))
+	    if (!EQ (frame1, frame)
+		&& MINIBUFFER_WINDOW_LOCAL_P (XFRAME (frame1)))
 	      {
-		window = FRAME_MINIBUF_WINDOW (XFRAME (this));
+		window = MINIBUFFER_WINDOW (XFRAME (frame1));
 		break;
 	      }
 	  }

       /* Don't abort if no window was found (Bug#15247).  */
-      if (WINDOWP (window))
+      if (WINDOW_LIVE_P (window))
 	{
 	  /* Use set_window_buffer instead of Fset_window_buffer (see
 	     discussion of bug#11984, bug#12025, bug#12026).  */
-	  set_window_buffer (window, XWINDOW (minibuf_window)->contents, 0, 0);
+	  set_window_buffer
+	    (window, XWINDOW (minibuf_window)->contents, 0, 0);
 	  minibuf_window = window;

 	  /* SELECT non-zero usually means that FRAME's minibuffer
 	     window was selected; select the new one.  */
 	  if (select)
-	    Fselect_window (minibuf_window, Qnil);
+	    Fselect_window (window, Qnil);
 	}
+      else
+	/* Code now must handle this.  */
+	minibuf_window = Qnil;
     }
 }

-
 /**
  * delete_frame:
  *
@@ -1831,12 +1962,15 @@ of them (the selected terminal frame) is actually displayed.
   struct frame *sf;
   struct kboard *kb;
   Lisp_Object frames, frame1;
-  int minibuffer_selected, is_tooltip_frame;
+  Lisp_Object delete_after = Qnil;
+  bool tooltip_frame_p = FRAME_TOOLTIP_P (f);
+  bool minibuf_window_selected_p = EQ (minibuf_window, selected_window);
   bool nochild = !FRAME_PARENT_FRAME (f);
+  Lisp_Object minibuffer_window = MINIBUFFER_WINDOW (f);

   if (!FRAME_LIVE_P (f))
     return Qnil;
-  else if (!EQ (force, Qnoelisp) && !other_frames (f, false, !NILP (force)))
+  else if (!EQ (force, Qnoelisp) && !other_frames_p (f, false, !NILP (force)))
     {
       if (NILP (force))
 	error ("Attempt to delete the sole visible or iconified frame");
@@ -1846,49 +1980,83 @@ of them (the selected terminal frame) is actually displayed.

   XSETFRAME (frame, f);

-  /* Softly delete all frames with this frame as their parent frame or
-     as their `delete-before' frame parameter value.  */
+  /* Delete all frames with this frame as their parent frame or as
+     their `delete-before' frame parameter value.
+
+     This will delete any such frame even if FRAME will not be deleted
+     due to a delete surrogate minibuffer frame error below.  However,
+     child and delete-before frames often use FRAME's minibuffer
+     window as their surrogate minibuffer window and therefore should
+     get deleted first to avoid the delete surrogate minibuffer frame
+     error below.  */
   FOR_EACH_FRAME (frames, frame1)
-    if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f
-	/* 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);
-
-  /* Does this frame have a minibuffer, and is it the surrogate
-     minibuffer for any other frame?  */
-  if (FRAME_HAS_MINIBUF_P (f))
     {
-      FOR_EACH_FRAME (frames, frame1)
-	{
-	  Lisp_Object fminiw;
-
-	  if (EQ (frame1, frame))
-	    continue;
+      struct frame *f1 = XFRAME (frame1);

-	  fminiw = FRAME_MINIBUF_WINDOW (XFRAME (frame1));
+      if (FRAME_PARENT_FRAME (f1) == f)
+	{
+	  /* If frame1 is FRAME's minibuffer(-only) window we cannot
+	     delete it right away because this would get us a delete
+	     surrogate mini window error.  So we (1) make frame1 a
+	     top-level frame and (2) mark it for later deletion.  */
+	  if (MINIBUFFER_WINDOW_LOCAL_P (f1)
+	      && EQ (minibuffer_window, MINIBUFFER_WINDOW (f1)))
+	    {
+#ifdef HAVE_WINDOW_SYSTEM
+	      x_set_frame_parameters (f1, Fcons (Fcons (Qparent_frame, Qnil),
+						 Qnil));
+#endif
+	      delete_after = Fcons (frame1, delete_after);
+	    }
+	  /* Otherwise try to delete frame1 passing FORCE only if it
+	     equals Qnoelisp.  */
+	  else
+	    delete_frame (frame1, EQ (force, Qnoelisp) ? Qnoelisp : Qnil);
+	}
+      /* Ignore `delete-before' when FRAME is a child frame to avoid
+	 entering an infinite chain of mixed dependencies.  */
+      else if (nochild && EQ (get_frame_param (f1, Qdelete_before), frame))
+	{
+	  /* If frame1 owns FRAME's minibuffer(-only) window we cannot
+	     delete it "before" FRAME because this would get us a
+	     delete surrogate minibuffer window error.  So we mark it
+	     for later deletion.  Whether that deletion passes through
+	     will depend on whether there are still other frames that
+	     use frame1's minibuffer window as surrogate.  */
+	  if (MINIBUFFER_WINDOW_LOCAL_P (f1)
+	      && EQ (minibuffer_window, MINIBUFFER_WINDOW (f1)))
+	    delete_after = Fcons (frame1, delete_after);
+	  else
+	    /* Pass FORCE only if it equals Qnoelisp.  */
+	    delete_frame (frame1, EQ (force, Qnoelisp) ? Qnoelisp : Qnil);
+	}
+    }

-	  if (WINDOWP (fminiw) && EQ (frame, WINDOW_FRAME (XWINDOW (fminiw))))
+  /* Does FRAME's minibuffer window serve as surrogate minibuffer
+     window for any other frame?  */
+  if (MINIBUFFER_WINDOW_LOCAL_P (f))
+    {
+      FOR_EACH_FRAME (frames, frame1)
+	{
+	  if (!EQ (frame1, frame)
+	      && EQ (MINIBUFFER_WINDOW (XFRAME (frame1)), minibuffer_window))
 	    {
 	      /* If we MUST delete this frame, delete the other first.
-		 But do this only if FORCE equals `noelisp'.  */
+		 But do this only if FORCE equals 'noelisp'.  */
 	      if (EQ (force, Qnoelisp))
 		delete_frame (frame1, Qnoelisp);
-	      else
-		error ("Attempt to delete a surrogate minibuffer frame");
+/*** XXXXXX Reinstate this eventually XXXXXXXX ***/
+/** 	      else **/
+/** 		error ("Attempt to delete a surrogate minibuffer frame"); **/
 	    }
 	}
     }

-  is_tooltip_frame = FRAME_TOOLTIP_P (f);
-
   /* Run `delete-frame-functions' unless FORCE is `noelisp' or
      frame is a tooltip.  FORCE is set to `noelisp' when handling
      a disconnect from the terminal, so we don't dare call Lisp
      code.  */
-  if (NILP (Vrun_hooks) || is_tooltip_frame)
+  if (NILP (Vrun_hooks) || tooltip_frame_p)
     ;
   else if (EQ (force, Qnoelisp))
     pending_funcalls
@@ -1907,8 +2075,8 @@ of them (the selected terminal frame) is actually displayed.
   /* delete_frame_functions may have deleted any frame, including this
      one.  */
   if (!FRAME_LIVE_P (f))
-    return Qnil;
-  else if (!EQ (force, Qnoelisp) && !other_frames (f, false, !NILP (force)))
+    goto delete_after;
+  else if (!EQ (force, Qnoelisp) && !other_frames_p (f, false, !NILP (force)))
     {
       if (NILP (force))
 	error ("Attempt to delete the sole visible or iconified frame");
@@ -1918,39 +2086,37 @@ of them (the selected terminal frame) is actually displayed.

   /* At this point, we are committed to deleting the frame.
      There is no more chance for errors to prevent it.  */
-  minibuffer_selected = EQ (minibuf_window, selected_window);
   sf = SELECTED_FRAME ();
   /* Don't let the frame remain selected.  */
   if (f == sf)
     {
-      Lisp_Object tail;
       Lisp_Object frame1 UNINIT;  /* This line works around GCC bug 85563.  */
+
       eassume (CONSP (Vframe_list));

       /* Look for another visible frame on the same terminal.
 	 Do not call next_frame here because it may loop forever.
 	 See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=15025.  */
-      FOR_EACH_FRAME (tail, frame1)
+      FOR_EACH_FRAME (frames, frame1)
 	{
 	  struct frame *f1 = XFRAME (frame1);

-	  if (!EQ (frame, frame1)
-	      && !FRAME_TOOLTIP_P (f1)
+	  if (!EQ (frame1, frame)
 	      && FRAME_TERMINAL (f) == FRAME_TERMINAL (f1)
-	      && FRAME_VISIBLE_P (f1))
+	      && !FRAME_TOOLTIP_P (f1)
+	      && FRAME_VISIBLE_P (f1)
+	      && NILP (Fmemq (frame1, delete_after)))
 	    break;
 	}

       /* If there is none, find *some* other frame.  */
       if (NILP (frame1) || EQ (frame1, frame))
 	{
-	  FOR_EACH_FRAME (tail, frame1)
+	  FOR_EACH_FRAME (frames, frame1)
 	    {
 	      struct frame *f1 = XFRAME (frame1);

-	      if (!EQ (frame, frame1)
-		  && FRAME_LIVE_P (f1)
-		  && !FRAME_TOOLTIP_P (f1))
+	      if (!EQ (frame1, frame) && !FRAME_TOOLTIP_P (f1))
 		{
 		  if (FRAME_TERMCAP_P (f1) || FRAME_MSDOS_P (f1))
 		    {
@@ -1959,10 +2125,12 @@ of them (the selected terminal frame) is actually displayed.
 		      if (!EQ (top_frame, frame))
 			frame1 = top_frame;
 		    }
-		  break;
+
+		break;
 		}
 	    }
 	}
+
 #ifdef NS_IMPL_COCOA
       else
 	/* Under NS, there is no system mechanism for choosing a new
@@ -1978,11 +2146,12 @@ of them (the selected terminal frame) is actually displayed.
     }

   /* Don't allow minibuf_window to remain on a deleted frame.  */
-  check_minibuf_window (frame, minibuffer_selected);
+  sanitize_minibuf_window (frame, minibuf_window_selected_p);

-  /* Don't let echo_area_window to remain on a deleted frame.  */
-  if (EQ (f->minibuffer_window, echo_area_window))
-    echo_area_window = sf->minibuffer_window;
+  /* Don't let echo_area_window to remain on a deleted frame (with
+     minibuf_window hopefully sanitized).  */
+  if (EQ (echo_area_window, minibuffer_window))
+    echo_area_window = minibuf_window;

   /* Clear any X selections for this frame.  */
 #ifdef HAVE_X_WINDOWS
@@ -2082,7 +2251,7 @@ of them (the selected terminal frame) is actually displayed.
 	{
 	  struct frame *f1 = XFRAME (frame1);

-	  if (!FRAME_MINIBUF_ONLY_P (f1))
+	  if (!MINIBUFFER_WINDOW_ONLY_P (f1))
 	    {
 	      last_nonminibuf_frame = f1;
 	      break;
@@ -2098,7 +2267,7 @@ of them (the selected terminal frame) is actually displayed.
       Lisp_Object frame_on_same_kboard = Qnil;

       FOR_EACH_FRAME (frames, frame1)
-	if (kb == FRAME_KBOARD (XFRAME (frame1)))
+	if (FRAME_KBOARD (XFRAME (frame1)) == kb)
 	  frame_on_same_kboard = frame1;

       if (NILP (frame_on_same_kboard))
@@ -2111,41 +2280,45 @@ of them (the selected terminal frame) is actually displayed.
      frames with other windows.  */
   if (kb != NULL && EQ (frame, KVAR (kb, Vdefault_minibuffer_frame)))
     {
-      /* The last frame we saw with a minibuffer, minibuffer-only or not.  */
-      Lisp_Object frame_with_minibuf = Qnil;
-      /* Some frame we found on the same kboard, or nil if there are none.  */
+      /* The last frame we saw with a minibuffer window,
+	 minibuffer-only or not.  */
+      Lisp_Object frame_with_minibuffer_window = Qnil;
+      /* Some frame we found on the same kboard, or nil if there are
+	 none.  */
       Lisp_Object frame_on_same_kboard = Qnil;

       FOR_EACH_FRAME (frames, frame1)
 	{
 	  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))
+	  if (FRAME_KBOARD (f1) == kb)
 	    {
-	      frame_with_minibuf = frame1;
-	      if (FRAME_MINIBUF_ONLY_P (f1))
-		break;
+	      /* Note: Pre-27 code did not choose minibuffer-separate
+		 frames here (for whatever reason).  We do now.  */
+	      frame_on_same_kboard = frame1;
+	      if (MINIBUFFER_WINDOW_LOCAL_P (f1))
+		{
+		  frame_with_minibuffer_window = frame1;
+		  if (MINIBUFFER_WINDOW_ONLY_P (f1))
+		    /* Prefer minibuffer-only frames.  */
+		    break;
+		}
 	    }
-
-	  if (kb == FRAME_KBOARD (f1))
-	    frame_on_same_kboard = frame1;
 	}

       if (!NILP (frame_on_same_kboard))
 	{
-	  /* We know that there must be some frame with a minibuffer out
-	     there.  If this were not true, all of the frames present
-	     would have to be minibufferless, which implies that at some
-	     point their minibuffer frames must have been deleted, but
-	     that is prohibited at the top; you can't delete surrogate
-	     minibuffer frames.  */
-	  if (NILP (frame_with_minibuf))
-	    emacs_abort ();
-
-	  kset_default_minibuffer_frame (kb, frame_with_minibuf);
+	  /* We know that there must be some frame with a minibuffer
+	     out there.  If this were not true, all frames on this
+	     keyboard would have to be minibufferless, which implies
+	     that at some point their minibuffer frames must have been
+	     deleted, but that is prohibited at the top; you can't
+	     delete surrogate minibuffer frames.  */
+/*** XXXXXX Do not reinstate this XXXXXXXXXXX ***/
+/** 	  if (NILP (frame_with_minibuffer_window)) **/
+/** 	    emacs_abort (); **/
+
+	  kset_default_minibuffer_frame (kb, frame_with_minibuffer_window);
 	}
       else
 	/* No frames left on this kboard--say no minibuffer either.  */
@@ -2153,11 +2326,18 @@ of them (the selected terminal frame) is actually displayed.
     }

   /* Cause frame titles to update--necessary if we now have just one frame.  */
-  if (!is_tooltip_frame)
+  if (!tooltip_frame_p)
     update_mode_lines = 15;

+ delete_after:
+  /* Deal with frames that we couldn't delete earlier usually because
+     FRAME's minibuffer window was on them.  Each of the following
+     calls may fail and it's the user's responsibility if it does.  */
+  for (; CONSP (delete_after); delete_after = XCDR (delete_after))
+    delete_frame (XCAR (delete_after), EQ (force, Qnoelisp) ? Qnoelisp : Qnil);
+
   /* Now run the post-deletion hooks.  */
-  if (NILP (Vrun_hooks) || is_tooltip_frame)
+  if (NILP (Vrun_hooks) || tooltip_frame_p)
     ;
   else if (EQ (force, Qnoelisp))
     pending_funcalls
@@ -2280,8 +2460,6 @@ enum internal_border_part
 }
 #endif

-/* Return mouse position in character cell units.  */
-
 DEFUN ("mouse-position", Fmouse_position, Smouse_position, 0, 0, 0,
        doc: /* Return a list (FRAME X . Y) giving the current mouse frame and position.
 The position is given in canonical character cells, where (0, 0) is the
@@ -2497,6 +2675,8 @@ enum internal_border_part
 {
   struct frame *f = decode_live_frame (frame);

+  XSETFRAME (frame, f);
+
   /* I think this should be done with a hook.  */
 #ifdef HAVE_WINDOW_SYSTEM
   if (FRAME_WINDOW_P (f))
@@ -2508,7 +2688,6 @@ enum internal_border_part
   /* Make menu bar update for the Buffers and Frames menus.  */
   /* windows_or_buffers_changed = 15; FIXME: Why?  */

-  XSETFRAME (frame, f);
   return frame;
 }

@@ -2547,11 +2726,15 @@ enum internal_border_part
 {
   struct frame *f = decode_live_frame (frame);

-  if (NILP (force) && !other_frames (f, true, false))
+  XSETFRAME (frame, f);
+
+  if (NILP (force) && !other_frames_p (f, true, false))
     error ("Attempt to make invisible the sole visible or iconified frame");

-  /* Don't allow minibuf_window to remain on an invisible frame.  */
-  check_minibuf_window (frame, EQ (minibuf_window, selected_window));
+  /* Don't allow minibuf_window to remain on an invisible frame.  But
+    if this is the only frame with a minibuffer there won't be another
+    choice.  */
+  sanitize_minibuf_window (frame, EQ (minibuf_window, selected_window));

   /* I think this should be done with a hook.  */
 #ifdef HAVE_WINDOW_SYSTEM
@@ -2575,6 +2758,9 @@ enum internal_border_part
   (Lisp_Object frame)
 {
   struct frame *f = decode_live_frame (frame);
+
+  XSETFRAME (frame, f);
+
 #ifdef HAVE_WINDOW_SYSTEM
   Lisp_Object parent = f->parent_frame;

@@ -2598,8 +2784,10 @@ enum internal_border_part
     }
 #endif	/* HAVE_WINDOW_SYSTEM */

-  /* Don't allow minibuf_window to remain on an iconified frame.  */
-  check_minibuf_window (frame, EQ (minibuf_window, selected_window));
+  /* Don't allow minibuf_window to remain on an iconified frame.  But
+     why do things differently from iconifying a frame from the title
+     bar?  So let's experimentally leave it on an iconified frame.  */
+/**   sanitize_minibuf_window (frame, EQ (minibuf_window, selected_window)); **/

   /* I think this should be done with a hook.  */
   if (FRAME_WINDOW_P (f))
@@ -2648,7 +2836,6 @@ enum internal_border_part
   return value;
 }

-
 DEFUN ("raise-frame", Fraise_frame, Sraise_frame, 0, 1, "",
        doc: /* Bring FRAME to the front, so it occludes any frames it overlaps.
 If FRAME is invisible or iconified, make it visible.
@@ -2689,7 +2876,6 @@ enum internal_border_part

   return Qnil;
 }
-
 \f
 DEFUN ("redirect-frame-focus", Fredirect_frame_focus, Sredirect_frame_focus,
        1, 2, 0,
@@ -2734,7 +2920,6 @@ enum internal_border_part
   return Qnil;
 }

-
 DEFUN ("frame-focus", Fframe_focus, Sframe_focus, 0, 1, 0,
        doc: /* Return the frame to which FRAME's keystrokes are currently being sent.
 If FRAME is omitted or nil, the selected frame is used.
@@ -2774,12 +2959,12 @@ enum internal_border_part
   (Lisp_Object frame, Lisp_Object made)
 {
   struct frame *f = decode_live_frame (frame);
+
   f->after_make_frame = !NILP (made);
   f->inhibit_horizontal_resize = false;
   f->inhibit_vertical_resize = false;
   return made;
 }
-
 \f
 /* Discard BUFFER from the buffer-list and buried-buffer-list of each frame.  */

@@ -2869,23 +3054,27 @@ enum internal_border_part

   if (EQ (prop, Qminibuffer))
     {
-      if (WINDOWP (val))
+      if (WINDOW_LIVE_P (val))
 	{
-	  if (!MINI_WINDOW_P (XWINDOW (val)))
+	  if (!MINIBUFFER_WINDOW_P (XWINDOW (val)))
 	    error ("The `minibuffer' parameter does not specify a valid minibuffer window");
-	  else if (FRAME_MINIBUF_ONLY_P (f))
+	  else if (MINIBUFFER_WINDOW_ONLY_P (f))
 	    {
-	      if (EQ (val, FRAME_MINIBUF_WINDOW (f)))
+	      if (EQ (val, MINIBUFFER_WINDOW (f)))
 		val = Qonly;
 	      else
 		error ("Can't change the minibuffer window of a minibuffer-only frame");
 	    }
-	  else if (FRAME_HAS_MINIBUF_P (f))
+	  else if (MINIBUFFER_WINDOW_LOCAL_P (f))
 	    {
-	      if (EQ (val, FRAME_MINIBUF_WINDOW (f)))
+	      if (EQ (val, MINIBUFFER_WINDOW (f)))
 		val = Qt;
 	      else
-		error ("Can't change the minibuffer window of a frame with its own minibuffer");
+		/* We must not allow a frame with its own minibuffer
+		   window use another frame's minibuffer window.  This
+		   could lead to circular dependencies which cannot be
+		   resolved when deleting one of these frames.  */
+		error ("Can't change the minibuffer window of a frame with its own minibuffer window");
 	    }
 	  else
 	    /* Store the chosen minibuffer window.  */
@@ -2897,12 +3086,12 @@ enum internal_border_part

 	  if (!NILP (old_val))
 	    {
-	      if (WINDOWP (old_val) && NILP (val))
+	      if (WINDOW_LIVE_P (old_val) && NILP (val))
 		/* Don't change the value for a minibuffer-less frame if
 		   only nil was specified as new value.  */
 		val = old_val;
 	      else if (!EQ (old_val, val))
-		error ("Can't change the `minibuffer' parameter of this frame");
+		error ("Can't change the 'minibuffer' parameter of this frame");
 	    }
 	}
     }
@@ -4631,6 +4820,10 @@ enum frame_float_type

       SET_FRAME_GARBAGED (f);
     }
+  else
+    /* When called from 'frame-notice-user-settings' ARG can be
+       zero.  Make sure the default height is set in that case.  */
+    x_set_scroll_bar_default_height (f);

   XWINDOW (FRAME_SELECTED_WINDOW (f))->cursor.vpos = 0;
   XWINDOW (FRAME_SELECTED_WINDOW (f))->cursor.y = 0;
@@ -5618,6 +5811,9 @@ or a list (- N) meaning -N pixels relative to bottom/right corner.
   DEFSYM (Qheight, "height");
   DEFSYM (Qicon, "icon");
   DEFSYM (Qminibuffer, "minibuffer");
+  DEFSYM (Qecho_area, "echo-area");
+  DEFSYM (Qloose, "loose");
+  DEFSYM (Qchild, "child");
   DEFSYM (Qundecorated, "undecorated");
   DEFSYM (Qno_special_glyphs, "no-special-glyphs");
   DEFSYM (Qparent_frame, "parent-frame");
@@ -5775,6 +5971,7 @@ or a list (- N) meaning -N pixels relative to bottom/right corner.
   DEFSYM (Qtop_only, "top-only");
   DEFSYM (Qiconify_top_level, "iconify-top-level");
   DEFSYM (Qmake_invisible, "make-invisible");
+  DEFSYM (Qmake_initial_minibuffer_frame, "make-initial-minibuffer-frame");

   {
     int i;
@@ -6103,6 +6300,7 @@ This variable is effective only with the X toolkit (and there only when
   defsubr (&Snext_frame);
   defsubr (&Sprevious_frame);
   defsubr (&Slast_nonminibuf_frame);
+  defsubr (&Sminibuffer_window_type);
   defsubr (&Sdelete_frame);
   defsubr (&Smouse_position);
   defsubr (&Smouse_pixel_position);
diff --git a/src/frame.h b/src/frame.h
index 1f438d3..3c7b3a1 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -31,6 +31,14 @@ enum vertical_scroll_bar_type
   vertical_scroll_bar_right
 };

+enum minibuffer_window_type
+{
+  minibuffer_window_loose,
+  minibuffer_window_top,
+  minibuffer_window_bottom,
+  minibuffer_window_only,
+};
+
 #ifdef HAVE_WINDOW_SYSTEM

 enum fullscreen_type
@@ -131,6 +139,11 @@ struct frame
      can actually appear to exist.  */
   Lisp_Object minibuffer_window;

+  /* This frame's echo window.
+     If this frame shares minibuffer and echo area, then this frame's
+     echo and minibuffer windows are the same window.  */
+  Lisp_Object echo_window;
+
   /* Parameter alist of this frame.
      These are the parameters specified when creating the frame
      or modified with modify-frame-parameters.  */
@@ -374,7 +387,7 @@ struct frame
      via mouse clicks or by moving the mouse into it.  */
   bool_bf no_accept_focus : 1;

-  /* The z-group this frame's window belongs to. */
+  /* The z-group this frame's window belongs to.  */
   ENUM_BF (z_group) z_group : 2;

   /* Non-zero if display of truncation and continuation glyphs outside
@@ -404,6 +417,9 @@ struct frame
   /* Non-zero if this frame's faces need to be recomputed.  */
   bool_bf face_change : 1;

+  /* The mini window type for this frame.  */
+  ENUM_BF (minibuffer_window_type) minibuffer_window_type : 2;
+
   /* Bitfield area ends here.  */

   /* Number of lines (rounded up) of tool bar.  REMOVE THIS  */
@@ -652,6 +668,16 @@ struct frame
   f->root_window = val;
 }
 INLINE void
+fset_minibuffer_window (struct frame *f, Lisp_Object val)
+{
+  f->minibuffer_window = val;
+}
+INLINE void
+fset_echo_window (struct frame *f, Lisp_Object val)
+{
+  f->echo_window = val;
+}
+INLINE void
 fset_scroll_bars (struct frame *f, Lisp_Object val)
 {
   f->scroll_bars = val;
@@ -729,9 +755,6 @@ struct frame
   (eassert (FRAMEP (p)), XUNTAG (p, Lisp_Vectorlike, struct frame))
 #define XSETFRAME(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_FRAME))

-/* Given a window, return its frame as a Lisp_Object.  */
-#define WINDOW_FRAME(w) ((w)->frame)
-
 /* Test a frame for particular kinds of display methods.  */
 #define FRAME_INITIAL_P(f) ((f)->output_method == output_initial)
 #define FRAME_TERMCAP_P(f) ((f)->output_method == output_termcap)
@@ -803,15 +826,72 @@ struct frame
 /* True if frame F is still alive (not deleted).  */
 #define FRAME_LIVE_P(f) ((f)->terminal != 0)

-/* True if frame F is a minibuffer-only frame.  */
-#define FRAME_MINIBUF_ONLY_P(f) \
-  EQ (FRAME_ROOT_WINDOW (f), FRAME_MINIBUF_WINDOW (f))
+/* True if frame FRAME is a frame and live.  */
+#define FRAME_VALID_P(FRAME)				\
+  (FRAMEP (FRAME) && FRAME_LIVE_P (XFRAME (FRAME)))
+
+/* The minibuffer window of frame F, if it has one; otherwise nil.  */
+#define MINIBUFFER_WINDOW(f) ((f)->minibuffer_window)

-/* True if frame F contains it's own minibuffer window.  Frame always has
-   minibuffer window, but it could use minibuffer window of another frame.  */
-#define FRAME_HAS_MINIBUF_P(f)					\
-  (WINDOWP (f->minibuffer_window)				\
-   && XFRAME (XWINDOW (f->minibuffer_window)->frame) == f)
+/* The echo window of frame F, if it has one; otherwise nil.  */
+#define ECHO_WINDOW(f) ((f)->echo_window)
+
+/* The root window of the window tree of frame F.  */
+#define FRAME_ROOT_WINDOW(f) ((f)->root_window)
+
+/* The currently selected window of the window tree of frame F.  */
+#define FRAME_SELECTED_WINDOW(f) ((f)->selected_window)
+
+/* The type of the mini window of frame F.  */
+#define MINIBUFFER_WINDOW_TYPE(f) ((f)->minibuffer_window_type)
+
+/* True if frame F contains its own minibuffer window.  Most frames
+   have a minibuffer window but it could be on another frame.  */
+#define MINIBUFFER_WINDOW_LOCAL_P(f)				\
+  (WINDOW_LIVE_P (MINIBUFFER_WINDOW (f))			\
+   && WINDOW_XFRAME (XWINDOW (MINIBUFFER_WINDOW (f))) == f)
+
+/* True if frame F contains its own echo window.  */
+#define ECHO_WINDOW_LOCAL_P(f)					\
+  (WINDOW_LIVE_P (ECHO_WINDOW (f))				\
+   && WINDOW_XFRAME (XWINDOW (ECHO_WINDOW (f))) == f)
+
+/* True if frame F is a minibuffer-only frame.  */
+#define MINIBUFFER_WINDOW_ONLY_P(f)				\
+  (MINIBUFFER_WINDOW_LOCAL_P (f)				\
+   && MINIBUFFER_WINDOW_TYPE (f) == minibuffer_window_only)
+
+/* True if frame F has a minibuffer window fixed at its top or
+   bottom.  */
+#define MINIBUFFER_WINDOW_FIXED_P(f)					\
+  (MINIBUFFER_WINDOW_LOCAL_P (f)					\
+   && (MINIBUFFER_WINDOW_TYPE (f) == minibuffer_window_top		\
+       || MINIBUFFER_WINDOW_TYPE (f) == minibuffer_window_bottom))
+
+/* True if frame F has a minibuffer window fixed at its top.  */
+#define MINIBUFFER_WINDOW_TOP_P(f)				\
+  (MINIBUFFER_WINDOW_LOCAL_P (f)				\
+   && MINIBUFFER_WINDOW_TYPE (f) == minibuffer_window_top)
+
+/* True if frame F has a minibuffer window fixed at its bottom.  */
+#define MINIBUFFER_WINDOW_BOTTOM_P(f)				\
+  (MINIBUFFER_WINDOW_LOCAL_P (f)				\
+   && MINIBUFFER_WINDOW_TYPE (f) == minibuffer_window_bottom)
+
+/* True if frame F has a loose minibuffer window.  */
+#define MINIBUFFER_WINDOW_LOOSE_P(f)				\
+  (MINIBUFFER_WINDOW_LOCAL_P (f)				\
+   && MINIBUFFER_WINDOW_TYPE (f) == minibuffer_window_loose)
+
+/* True if frame F has a remote minibuffer window.  */
+#define MINIBUFFER_WINDOW_REMOTE_P(f)				\
+  (WINDOW_LIVE_P (MINIBUFFER_WINDOW (f))			\
+   && WINDOW_XFRAME (XWINDOW (MINIBUFFER_WINDOW (f))) != f)
+
+/* True if frame F has a remote echo window.  */
+#define ECHO_WINDOW_REMOTE_P(f)					\
+  (WINDOW_LIVE_P (ECHO_WINDOW (f))				\
+   && WINDOW_XFRAME (XWINDOW (ECHO_WINDOW (f))) != f)

 /* Pixel width of frame F.  */
 #define FRAME_PIXEL_WIDTH(f) ((f)->pixel_width)
@@ -866,7 +946,7 @@ struct frame
 #define FRAME_TOOL_BAR_HEIGHT(f) (f)->tool_bar_height

 /* Lines above the top-most window in frame F.  */
-#define FRAME_TOP_MARGIN(F) \
+#define FRAME_TOP_MARGIN(F)				\
   (FRAME_MENU_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F))

 /* Pixel height of frame F's top margin.  */
@@ -913,15 +993,6 @@ struct frame
 #define FRAME_WINDOW_CONFIGURATION_CHANGED(f)	\
   (f)->window_configuration_changed

-/* The minibuffer window of frame F, if it has one; otherwise nil.  */
-#define FRAME_MINIBUF_WINDOW(f) f->minibuffer_window
-
-/* The root window of the window tree of frame F.  */
-#define FRAME_ROOT_WINDOW(f) f->root_window
-
-/* The currently selected window of the window tree of frame F.  */
-#define FRAME_SELECTED_WINDOW(f) f->selected_window
-
 #define FRAME_INSERT_COST(f) (f)->insert_line_cost
 #define FRAME_DELETE_COST(f) (f)->delete_line_cost
 #define FRAME_INSERTN_COST(f) (f)->insert_n_lines_cost
@@ -1230,8 +1301,9 @@ struct frame
   decode_window_system_frame (Lisp_Object);
 extern struct frame *decode_live_frame (Lisp_Object);
 extern struct frame *decode_any_frame (Lisp_Object);
+extern struct frame *decode_frame_or_window (Lisp_Object);
 extern struct frame *make_initial_frame (void);
-extern struct frame *make_frame (bool);
+extern struct frame *make_frame (Lisp_Object);
 #ifdef HAVE_WINDOW_SYSTEM
 extern struct frame *make_minibuffer_frame (void);
 extern struct frame *make_frame_without_minibuffer (Lisp_Object,
diff --git a/src/keyboard.c b/src/keyboard.c
index 7ab9a60..e909411 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1149,14 +1149,14 @@ struct kboard_stack
   user_error ("No recursive edit is in progress");
 }
 \f
-/* Restore mouse tracking enablement.  See Ftrack_mouse for the only use
-   of this function.  */
+/* Restore mouse tracking enablement.  See Finternal_track_mouse for
+   the only use of this function.  */

 static void
-tracking_off (Lisp_Object old_value)
+tracking_off (Lisp_Object old_track_mouse)
 {
-  do_mouse_tracking = old_value;
-  if (NILP (old_value))
+  track_mouse = old_track_mouse;
+  if (NILP (old_track_mouse))
     {
       /* Redisplay may have been preempted because there was input
 	 available, and it assumes it will be called again after the
@@ -1171,24 +1171,24 @@ struct kboard_stack
     }
 }

-DEFUN ("internal--track-mouse", Ftrack_mouse, Strack_mouse, 1, 1, 0,
+DEFUN ("internal--track-mouse", Finternal_track_mouse, Sinternal_track_mouse,
+       1, 1, 0,
        doc: /* Call BODYFUN with mouse movement events enabled.  */)
   (Lisp_Object bodyfun)
 {
   ptrdiff_t count = SPECPDL_INDEX ();
   Lisp_Object val;

-  record_unwind_protect (tracking_off, do_mouse_tracking);
+  record_unwind_protect (tracking_off, track_mouse);

-  do_mouse_tracking = Qt;
+  track_mouse = Qt;

   val = call0 (bodyfun);
   return unbind_to (count, val);
 }

-/* If mouse has moved on some frame, return one of those frames.
-
-   Return 0 otherwise.
+/* If mouse has moved on some frame and we are tracking the mouse,
+   return one of those frames.  Return NULL otherwise.

    If ignore_mouse_drag_p is non-zero, ignore (implicit) mouse movement
    after resizing the tool-bar window.  */
@@ -1200,11 +1200,8 @@ struct kboard_stack
 {
   Lisp_Object tail, frame;

-  if (ignore_mouse_drag_p)
-    {
-      /* ignore_mouse_drag_p = 0; */
-      return 0;
-    }
+  if (NILP (track_mouse) || ignore_mouse_drag_p)
+    return NULL;

   FOR_EACH_FRAME (tail, frame)
     {
@@ -1212,7 +1209,7 @@ struct kboard_stack
 	return XFRAME (frame);
     }

-  return 0;
+  return NULL;
 }

 \f
@@ -1249,10 +1246,8 @@ static int read_key_sequence (Lisp_Object *, Lisp_Object,
       if (!NILP (Vpost_command_hook) && !NILP (Vrun_hooks))
 	safe_run_hooks (Qpost_command_hook);

-      /* If displaying a message, resize the echo area window to fit
-	 that message's size exactly.  */
-      if (!NILP (echo_area_buffer[0]))
-	resize_echo_area_exactly ();
+      /* Resize our mini windows.  */
+      resize_mini_windows_exactly ();

       /* If there are warnings waiting, process them.  */
       if (!NILP (Vdelayed_warnings_list))
@@ -1466,8 +1461,8 @@ static int read_key_sequence (Lisp_Object *, Lisp_Object,

       /* If displaying a message, resize the echo area window to fit
 	 that message's size exactly.  */
-      if (!NILP (echo_area_buffer[0]))
-	resize_echo_area_exactly ();
+      if (!NILP (echo_area_buffer[0]) || minibuf_level > 0)
+	resize_mini_windows_exactly ();

       /* If there are warnings waiting, process them.  */
       if (!NILP (Vdelayed_warnings_list))
@@ -2057,7 +2052,8 @@ static int read_key_sequence (Lisp_Object *, Lisp_Object,
 	 This causes trouble if we are trying to read a mouse motion
 	 event (i.e., if we are inside a `track-mouse' form), so we
 	 restore the mouse_moved flag.  */
-      struct frame *f = NILP (do_mouse_tracking) ? NULL : some_mouse_moved ();
+      struct frame *f = some_mouse_moved ();
+
       help = call1 (Qmouse_fixup_help_message, help);
       if (f)
 	f->mouse_moved = true;
@@ -2662,7 +2658,7 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,

       /* Slow down auto saves logarithmically in size of current buffer,
 	 and garbage collect while we're at it.  */
-      if (! MINI_WINDOW_P (XWINDOW (selected_window)))
+      if (!MINIBUFFER_WINDOW_P (XWINDOW (selected_window)))
 	last_non_minibuf_size = Z - BEG;
       buffer_size = (last_non_minibuf_size >> 8) + 1;
       delay_level = 0;
@@ -3360,8 +3356,7 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,
 	return 1;
     }

-  if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES)
-      && !NILP (do_mouse_tracking) && some_mouse_moved ())
+  if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ())
     return 1;
   if (single_kboard)
     {
@@ -3763,7 +3758,7 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,

       if (kbd_fetch_ptr != kbd_store_ptr)
 	break;
-      if (!NILP (do_mouse_tracking) && some_mouse_moved ())
+      if (some_mouse_moved ())
 	break;

       /* If the quit flag is set, then read_char will return
@@ -3779,7 +3774,7 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,
 #endif
       if (kbd_fetch_ptr != kbd_store_ptr)
 	break;
-      if (!NILP (do_mouse_tracking) && some_mouse_moved ())
+      if (some_mouse_moved ())
 	break;
       if (end_time)
 	{
@@ -3918,8 +3913,9 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,
         break;
       default:
 	{
-	  /* If this event is on a different frame, return a switch-frame this
-	     time, and leave the event in the queue for next time.  */
+	  /* If this event is on a different frame, return a
+	     switch-frame this time and leave the event in the queue
+	     for next time.  */
 	  Lisp_Object frame;
 	  Lisp_Object focus;

@@ -3933,14 +3929,13 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,
 	  if (! NILP (focus))
 	    frame = focus;

-	  if (! EQ (frame, internal_last_event_frame)
+	  if (!EQ (frame, internal_last_event_frame)
 	      && !EQ (frame, selected_frame))
 	    obj = make_lispy_switch_frame (frame);
 	  internal_last_event_frame = frame;

 	  /* If we didn't decide to make a switch-frame event, go ahead
 	     and build a real event from the queue entry.  */
-
 	  if (NILP (obj))
 	    {
 	      obj = make_lispy_event (&event->ie);
@@ -3973,7 +3968,7 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,
       }
     }
   /* Try generating a mouse motion event.  */
-  else if (!NILP (do_mouse_tracking) && some_mouse_moved ())
+  else if (some_mouse_moved ())
     {
       struct frame *f = some_mouse_moved ();
       Lisp_Object bar_window;
@@ -4005,7 +4000,7 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu,
 	  if (NILP (frame))
 	    XSETFRAME (frame, f);

-	  if (! EQ (frame, internal_last_event_frame)
+	  if (!EQ (frame, internal_last_event_frame)
 	      && !EQ (frame, selected_frame))
 	    obj = make_lispy_switch_frame (frame);
 	  internal_last_event_frame = frame;
@@ -10917,7 +10912,7 @@ The value is a list of the form (INTERRUPT FLOW META QUIT), where
   recent_keys_index = 0;
   kbd_fetch_ptr = kbd_buffer;
   kbd_store_ptr = kbd_buffer;
-  do_mouse_tracking = Qnil;
+  track_mouse = Qnil;
   input_pending = false;
   interrupt_input_blocked = 0;
   pending_signals = false;
@@ -11269,7 +11264,7 @@ struct event_head
   defsubr (&Sread_key_sequence);
   defsubr (&Sread_key_sequence_vector);
   defsubr (&Srecursive_edit);
-  defsubr (&Strack_mouse);
+  defsubr (&Sinternal_track_mouse);
   defsubr (&Sinput_pending_p);
   defsubr (&Srecent_keys);
   defsubr (&Sthis_command_keys);
@@ -11608,8 +11603,15 @@ struct event_head
 	       doc: /* Keymap defining bindings for special events to execute at low level.  */);
   Vspecial_event_map = list1 (Qkeymap);

-  DEFVAR_LISP ("track-mouse", do_mouse_tracking,
-	       doc: /* Non-nil means generate motion events for mouse motion.  */);
+  DEFVAR_LISP ("track-mouse", track_mouse,
+	       doc: /* Non-nil means generate motion events for mouse motion.
+The sepecial values 'dragging' and 'dropping' assert that the moue
+cursor retains its appearance during mouse motion.  Any non-nil value
+but 'dropping' asserts that motion events always relate to the frame
+where the the mouse movement started.  The value 'dropping' asserts
+that motion events relate to the frame where the mouse cursor is seen
+when generating the event.  If there's no such frame, such motion
+events relate to the frame where the the mouse movement started.  */);

   DEFVAR_KBOARD ("system-key-alist", Vsystem_key_alist,
 		 doc: /* Alist of system-specific X windows key symbols.
diff --git a/src/lisp.h b/src/lisp.h
index 96de60e..d8c8a0b 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3507,6 +3507,7 @@ extern void replace_range_2 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t,
 /* Defined in xdisp.c.  */
 extern bool noninteractive_need_newline;
 extern Lisp_Object echo_area_buffer[2];
+extern Lisp_Object echo_buffer[2];
 extern void add_to_log (char const *, ...);
 extern void vadd_to_log (char const *, va_list);
 extern void check_message_stack (void);
diff --git a/src/minibuf.c b/src/minibuf.c
index abc4866..0e1075b 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -72,25 +72,73 @@
 {
   if (FRAMEP (selected_frame)
       && FRAME_LIVE_P (XFRAME (selected_frame))
-      && !EQ (minibuf_window, XFRAME (selected_frame)->minibuffer_window))
+      && (!WINDOW_LIVE_P (minibuf_window)
+	  || !EQ (minibuf_window,
+		  MINIBUFFER_WINDOW (XFRAME (selected_frame)))))
     {
       struct frame *sf = XFRAME (selected_frame);
       Lisp_Object buffer;

-      /* I don't think that any frames may validly have a null minibuffer
-	 window anymore.  */
-      if (NILP (sf->minibuffer_window))
-	emacs_abort ();
+      if (!WINDOW_LIVE_P (sf->minibuffer_window))
+	minibuf_window = minibuffer_window (sf);
+      /* Under X, we come here with minibuf_window being the
+	 minibuffer window of the unused termcap window created in
+	 init_window_once.  That window doesn't have a buffer.  */
+      else if (WINDOW_LIVE_P (minibuf_window))
+	{
+	  buffer = XWINDOW (minibuf_window)->contents;
+	  if (BUFFERP (buffer))
+	    /* Use set_window_buffer instead of Fset_window_buffer (see
+	       discussion of bug#11984, bug#12025, bug#12026).  */
+	    set_window_buffer (sf->minibuffer_window, buffer, 0, 0);
+
+	  minibuf_window = sf->minibuffer_window;
+	}
+      else
+	minibuf_window = sf->minibuffer_window;
+    }
+
+  /* Make sure no other frame has a minibuffer as its selected window,
+     because the text would not be displayed in it, and that would be
+     confusing.  Only allow the selected frame to do this,
+     and that only if the minibuffer is active.  */
+  {
+    Lisp_Object tail, frame;
+
+    FOR_EACH_FRAME (tail, frame)
+      if (MINIBUFFER_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame))))
+	  && !(EQ (frame, selected_frame) && minibuf_level > 0))
+	Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil);
+  }
+}
+
+static void
+choose_minibuf_frame_unwind (void)
+{
+  if (FRAMEP (selected_frame)
+      && FRAME_LIVE_P (XFRAME (selected_frame))
+      && (!WINDOW_LIVE_P (minibuf_window)
+	  || !EQ (minibuf_window,
+		  MINIBUFFER_WINDOW (XFRAME (selected_frame)))))
+    {
+      struct frame *sf = XFRAME (selected_frame);
+      Lisp_Object buffer;

       /* Under X, we come here with minibuf_window being the
 	 minibuffer window of the unused termcap window created in
 	 init_window_once.  That window doesn't have a buffer.  */
-      buffer = XWINDOW (minibuf_window)->contents;
-      if (BUFFERP (buffer))
-	/* Use set_window_buffer instead of Fset_window_buffer (see
-	   discussion of bug#11984, bug#12025, bug#12026).  */
-	set_window_buffer (sf->minibuffer_window, buffer, 0, 0);
-      minibuf_window = sf->minibuffer_window;
+      if (WINDOW_LIVE_P (minibuf_window))
+	{
+	  buffer = XWINDOW (minibuf_window)->contents;
+	  if (BUFFERP (buffer))
+	    /* Use set_window_buffer instead of Fset_window_buffer (see
+	       discussion of bug#11984, bug#12025, bug#12026).  */
+	    set_window_buffer (sf->minibuffer_window, buffer, 0, 0);
+
+	  minibuf_window = sf->minibuffer_window;
+	}
+      else
+	minibuf_window = sf->minibuffer_window;
     }

   /* Make sure no other frame has a minibuffer as its selected window,
@@ -101,9 +149,8 @@
     Lisp_Object tail, frame;

     FOR_EACH_FRAME (tail, frame)
-      if (MINI_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame))))
-	  && !(EQ (frame, selected_frame)
-	       && minibuf_level > 0))
+      if (MINIBUFFER_WINDOW_P (XWINDOW (FRAME_SELECTED_WINDOW (XFRAME (frame))))
+	  && !(EQ (frame, selected_frame) && minibuf_level > 0))
 	Fset_frame_selected_window (frame, Fframe_first_window (frame), Qnil);
   }
 }
@@ -113,7 +160,9 @@
        doc: /* Return the currently active minibuffer window, or nil if none.  */)
      (void)
 {
-  return minibuf_level ? minibuf_window : Qnil;
+  return ((minibuf_level && WINDOW_LIVE_P (minibuf_window))
+	  ? minibuf_window
+	  : Qnil);
 }

 DEFUN ("set-minibuffer-window", Fset_minibuffer_window,
@@ -124,7 +173,7 @@
   (Lisp_Object window)
 {
   CHECK_WINDOW (window);
-  if (! MINI_WINDOW_P (XWINDOW (window)))
+  if (!MINIBUFFER_WINDOW_P (XWINDOW (window)))
     error ("Window is not a minibuffer window");

   minibuf_window = window;
@@ -443,13 +492,18 @@
      calling Fset_frame_selected_window may change it (Bug#12766).  */
   record_unwind_protect (restore_buffer, Fcurrent_buffer ());

-  choose_minibuf_frame ();
-
-  record_unwind_protect_void (choose_minibuf_frame);
+  record_unwind_protect_void (choose_minibuf_frame_unwind);

   record_unwind_protect (restore_window_configuration,
 			 Fcurrent_window_configuration (Qnil));

+  /* Assure that minibuf_window is set up to a minibuffer window.
+     Moved here as to assure that if this creates a new minibuffer
+     window on the selected frame, that minibuffer window will be
+     deleted when the old window configuration for this frame gets
+     restored.  */
+  choose_minibuf_frame ();
+
   /* If the minibuffer window is on a different frame, save that
      frame's configuration too.  */
   mini_frame = WINDOW_FRAME (XWINDOW (minibuf_window));
@@ -577,14 +631,12 @@

   FOR_EACH_FRAME (dummy, frame)
     {
-      Lisp_Object root_window = Fframe_root_window (frame);
-      Lisp_Object mini_window = XWINDOW (root_window)->next;
-
-      if (! NILP (mini_window) && ! EQ (mini_window, minibuf_window)
-	  && !NILP (Fwindow_minibuffer_p (mini_window)))
+      if (MINIBUFFER_WINDOW_LOCAL_P (XFRAME (frame))
+	  && !EQ (MINIBUFFER_WINDOW (XFRAME (frame)), minibuf_window))
 	/* Use set_window_buffer instead of Fset_window_buffer (see
 	   discussion of bug#11984, bug#12025, bug#12026).  */
-	set_window_buffer (mini_window, empty_minibuf, 0, 0);
+	set_window_buffer (MINIBUFFER_WINDOW (XFRAME (frame)),
+			   empty_minibuf, 0, 0);
     }

   /* Display this minibuffer in the proper window.  */
@@ -825,8 +877,9 @@
   }

   /* When we get to the outmost level, make sure we resize the
-     mini-window back to its normal size.  */
-  if (minibuf_level == 0)
+     mini-window back to its normal size (provided it's still
+     there).  */
+  if (WINDOW_LIVE_P (window) && minibuf_level == 0)
     resize_mini_window (XWINDOW (window), 0);

   /* Deal with frames that should be removed when exiting the
@@ -857,7 +910,8 @@
      dead, we may keep displaying this buffer (tho it's inactive), so reset it,
      to make sure we don't leave around bindings and stuff which only
      made sense during the read_minibuf invocation.  */
-  call0 (intern ("minibuffer-inactive-mode"));
+  if (WINDOW_LIVE_P (window))
+    call0 (intern ("minibuffer-inactive-mode"));
 }
 \f

@@ -962,7 +1016,8 @@
  empty string.
 Fifth arg INHERIT-INPUT-METHOD, if non-nil, means the minibuffer inherits
  the current input method and the setting of `enable-multibyte-characters'.  */)
-  (Lisp_Object prompt, Lisp_Object initial_input, Lisp_Object history, Lisp_Object default_value, Lisp_Object inherit_input_method)
+  (Lisp_Object prompt, Lisp_Object initial_input, Lisp_Object history,
+   Lisp_Object default_value, Lisp_Object inherit_input_method)
 {
   Lisp_Object val;
   ptrdiff_t count = SPECPDL_INDEX ();
diff --git a/src/msdos.c b/src/msdos.c
index 6c0dfa0..3201768 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -1180,7 +1180,7 @@ extern unsigned char *encode_terminal_code (struct glyph *, int,
   /* If we are in the echo area, put the cursor at the
      end of the echo area message.  */
   if (!update_cursor_pos
-      && WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f))) <= new_pos_Y)
+      && WINDOW_TOP_EDGE_LINE (XWINDOW (MINIBUFFER_WINDOW (f))) <= new_pos_Y)
     {
       int tem_X = current_pos_X, dummy;

@@ -1310,8 +1310,8 @@ extern unsigned char *encode_terminal_code (struct glyph *, int,
   sw = XWINDOW (f->selected_window);
   frame_desired_cursor = Fcdr (Fassq (Qcursor_type, f->param_alist));
   if (cursor_in_echo_area
-      && FRAME_HAS_MINIBUF_P (f)
-      && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)
+      && MINIBUFFER_WINDOW_LOCAL_P (f)
+      && EQ (MINIBUFFER_WINDOW (f), echo_area_window)
       && sw == XWINDOW (echo_area_window))
     new_cursor = frame_desired_cursor;
   else
diff --git a/src/nsfns.m b/src/nsfns.m
index 184657f..0ba13c2 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -571,7 +571,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
       if (! FRAME_NS_P (f)) continue;
       w = XWINDOW (FRAME_SELECTED_WINDOW (f));
       view = FRAME_NS_VIEW (f);
-      if (!MINI_WINDOW_P (w))
+      if (!MINIBUFFER_WINDOW_P (w))
         edited = ! NILP (Fbuffer_modified_p (w->contents)) &&
           ! NILP (Fbuffer_file_name (w->contents));
       [[view window] setDocumentEdited: edited];
@@ -586,7 +586,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
 x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 {
   int nlines;
-  if (FRAME_MINIBUF_ONLY_P (f))
+  if (MINIBUFFER_WINDOW_ONLY_P (f))
     return;

   if (TYPE_RANGED_INTEGERP (int, value))
@@ -624,7 +624,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.

   NSTRACE ("x_set_tool_bar_lines");

-  if (FRAME_MINIBUF_ONLY_P (f))
+  if (MINIBUFFER_WINDOW_ONLY_P (f))
     return;

   if (RANGED_INTEGERP (0, value, INT_MAX))
@@ -1101,13 +1101,25 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
       f = make_minibuffer_frame ();
       minibuffer_only = 1;
     }
-  else if (WINDOWP (tem))
-      f = make_frame_without_minibuffer (tem, kb, display);
+  else if (WINDOWP (tem) || EQ (tem, Qloose))
+    f = make_frame_without_minibuffer (tem, kb, display);
+  else if (EQ (tem, Qtop))
+    f = make_frame (Qtop);
   else
-      f = make_frame (1);
+    f = make_frame (Qbottom);

   XSETFRAME (frame, f);

+  if (EQ (tem, Qloose))
+    {
+      /* Process 'echo-area' parameter here to make sure it gets
+	 applied when an echo window is needed.  */
+      tem = x_get_arg (dpyinfo, parms, Qecho_area, "echo-area", "Echo-area",
+		       RES_TYPE_SYMBOL);
+      if (!NILP (tem))
+	store_frame_param (f, Qecho_area, tem);
+    }
+
   f->terminal = dpyinfo->terminal;

   f->output_method = output_ns;
@@ -1384,7 +1396,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
         }
     }

-  if (FRAME_HAS_MINIBUF_P (f)
+  if (MINIBUFFER_WINDOW_LOCAL_P (f)
       && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
           || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
     kset_default_minibuffer_frame (kb, frame);
diff --git a/src/nsterm.m b/src/nsterm.m
index a15684d..2dff9aa 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -2595,11 +2595,13 @@ so some key presses (TAB) are swallowed by the system.  */
       XFRAME (frame)->mouse_moved = 0;

   dpyinfo->last_mouse_scroll_bar = nil;
+  f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();
   if (dpyinfo->last_mouse_frame
+      /* While dropping use the last mouse frame only if there is no
+	 currently focused frame.  */
+      && (!EQ (track_mouse, Qdropping) || !f)
       && FRAME_LIVE_P (dpyinfo->last_mouse_frame))
     f = dpyinfo->last_mouse_frame;
-  else
-    f = dpyinfo->x_focus_frame ? dpyinfo->x_focus_frame : SELECTED_FRAME ();

   if (f && FRAME_NS_P (f))
     {
diff --git a/src/term.c b/src/term.c
index f5fca7f..586ad16 100644
--- a/src/term.c
+++ b/src/term.c
@@ -3044,18 +3044,18 @@ struct tty_menu_state
       bool usable_input = 1;
       mi_result st = MI_CONTINUE;
       struct tty_display_info *tty = FRAME_TTY (sf);
-      Lisp_Object saved_mouse_tracking = do_mouse_tracking;
+      Lisp_Object old_track_mouse = track_mouse;

       /* Signal the keyboard reading routines we are displaying a menu
 	 on this terminal.  */
       tty->showing_menu = 1;
       /* We want mouse movements be reported by read_menu_command.  */
-      do_mouse_tracking = Qt;
+      track_mouse = Qt;
       do {
 	cmd = read_menu_command ();
       } while (NILP (cmd));
       tty->showing_menu = 0;
-      do_mouse_tracking = saved_mouse_tracking;
+      track_mouse = old_track_mouse;

       if (EQ (cmd, Qt) || EQ (cmd, Qtty_menu_exit)
 	  /* If some input switched frames under our feet, exit the
@@ -3664,11 +3664,12 @@ struct tty_pop_down_menu
       uly = dispheight - height;
     }

-  if (FRAME_HAS_MINIBUF_P (f) && uly + height > dispheight - 2)
+  if (MINIBUFFER_WINDOW_LOCAL_P (f) && uly + height > dispheight - 2)
     {
       /* Move the menu away of the echo area, to avoid overwriting the
 	 menu with help echo messages or vice versa.  */
-      if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
+      if (BUFFERP (echo_area_buffer[0])
+	  && WINDOW_LIVE_P (echo_area_window))
 	{
 	  y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window)) + 1;
 	  uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window)) + 1;
diff --git a/src/w32fns.c b/src/w32fns.c
index 760801c..de8443e 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -1722,7 +1722,7 @@ struct frame *
      frame itself, and get an error because you can't switch buffers in
      or split the minibuffer window.  Child frames don't like menu bars
      either.  */
-  if (!FRAME_MINIBUF_ONLY_P (f) && !FRAME_PARENT_FRAME (f))
+  if (!MINIBUFFER_WINDOW_ONLY_P (f) && !FRAME_PARENT_FRAME (f))
     {
       boolean old = FRAME_EXTERNAL_MENU_BAR (f);
       boolean new = (INTEGERP (value) && XINT (value) > 0) ? true : false;
@@ -1776,7 +1776,7 @@ struct frame *
   int nlines;

   /* Treat tool bars like menu bars.  */
-  if (FRAME_MINIBUF_ONLY_P (f))
+  if (MINIBUFFER_WINDOW_ONLY_P (f))
     return;

   /* Use VALUE only if an integer >= 0.  */
@@ -4576,7 +4576,8 @@ struct frame *
 	if (button_state & this)
 	  return 0;

-	if (button_state == 0)
+	/* Don't capture mouse when dropping.  */
+	if (button_state == 0 && !EQ (track_mouse, Qdropping))
 	  SetCapture (hwnd);

 	button_state |= this;
@@ -4697,8 +4698,11 @@ struct frame *

 	if (parse_button (msg, HIWORD (wParam), &button, &up))
 	  {
-	    if (up) ReleaseCapture ();
-	    else SetCapture (hwnd);
+	    if (up)
+	      ReleaseCapture ();
+	    /* Don't capture mouse when dropping.  */
+	    else if (!EQ (track_mouse, Qdropping))
+	      SetCapture (hwnd);
 	    button = (button == 0) ? LMOUSE :
 	      ((button == 1) ? MMOUSE  : RMOUSE);
 	    if (up)
@@ -5341,8 +5345,9 @@ struct frame *
 	else if (button_state & RMOUSE)
 	  flags |= TPM_RIGHTBUTTON;

-	/* Remember we did a SetCapture on the initial mouse down event,
-	   so for safety, we make sure the capture is canceled now.  */
+	/* We may have done a SetCapture on the initial mouse down
+	   event, so for safety, make sure the capture is canceled
+	   now.  */
 	ReleaseCapture ();
 	button_state = 0;

@@ -5743,13 +5748,25 @@ struct frame *
       f = make_minibuffer_frame ();
       minibuffer_only = true;
     }
-  else if (WINDOWP (tem))
+  else if (WINDOWP (tem) || EQ (tem, Qloose))
     f = make_frame_without_minibuffer (tem, kb, display);
+  else if (EQ (tem, Qtop))
+    f = make_frame (Qtop);
   else
-    f = make_frame (true);
+    f = make_frame (Qbottom);

   XSETFRAME (frame, f);

+  if (EQ (tem, Qloose))
+    {
+      /* Process 'echo-area' parameter here to make sure it gets
+	 applied when an echo window is needed.  */
+      tem = x_get_arg (dpyinfo, parameters, Qecho_area, "echo-area", "Echo-area",
+		       RES_TYPE_SYMBOL);
+      if (!NILP (tem))
+	store_frame_param (f, Qecho_area, tem);
+    }
+
   parent_frame = x_get_arg (dpyinfo, parameters, Qparent_frame, NULL, NULL,
 			    RES_TYPE_SYMBOL);
   /* Apply `parent-frame' parameter only when no `parent-id' was
@@ -6060,7 +6077,7 @@ struct frame *

   /* Initialize `default-minibuffer-frame' in case this is the first
      frame on this terminal.  */
-  if (FRAME_HAS_MINIBUF_P (f)
+  if (MINIBUFFER_WINDOW_LOCAL_P (f)
       && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
 	  || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
     kset_default_minibuffer_frame (kb, frame);
@@ -7142,8 +7159,8 @@ static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object,
  *
  * Hide currently visible tooltip and cancel its timer.
  *
- * This will try to make tooltip_frame invisible (if DELETE is false)
- * or delete tooltip_frame (if DELETE is true).
+ * This will try to make tip_frame invisible (if DELETE is false) or
+ * delete tip_frame (if DELETE is true).
  *
  * Return Qt if the tooltip was either deleted or made invisible, Qnil
  * otherwise.
diff --git a/src/w32term.c b/src/w32term.c
index 0ae173a..e547c85 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -3490,72 +3490,78 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object

       /* Now we have a position on the root; find the innermost window
 	 containing the pointer.  */
-      {
-	/* If mouse was grabbed on a frame, give coords for that
-	   frame even if the mouse is now outside it.  Otherwise
-	   check for window under mouse on one of our frames.  */
-	if (x_mouse_grabbed (dpyinfo))
-	  f1 = dpyinfo->last_mouse_frame;
-	else
-	  {
-	    HWND wfp = WindowFromPoint (pt);

-	    if (wfp)
-	      {
-		f1 = x_any_window_to_frame (dpyinfo, wfp);
-		if (f1)
-		  {
-		    HWND cwfp = ChildWindowFromPoint (wfp, pt);
+      /* If mouse was grabbed on a frame and we are not dropping,
+	 give coords for that frame even if the mouse is now outside
+	 it.  Otherwise check for window under mouse on one of our
+	 frames.  */
+      if (x_mouse_grabbed (dpyinfo) && !EQ (track_mouse, Qdropping))
+	f1 = dpyinfo->last_mouse_frame;
+      else
+	{
+	  HWND wfp = WindowFromPoint (pt);

-		    if (cwfp)
-		      {
-			struct frame *f2 = x_any_window_to_frame (dpyinfo, cwfp);
+	  if (wfp)
+	    {
+	      f1 = x_window_to_frame (dpyinfo, wfp);
+	      if (f1)
+		{
+		  HWND cwfp = ChildWindowFromPoint (wfp, pt);

-			/* If a child window was found, make sure that its
-			   frame is a child frame (Bug#26615, maybe).  */
-			if (f2 && FRAME_PARENT_FRAME (f2))
-			  f1 = f2;
-		      }
-		  }
-	      }
-	  }
+		  if (cwfp)
+		    {
+		      struct frame *f2 = x_window_to_frame (dpyinfo, cwfp);

-	/* If not, is it one of our scroll bars?  */
-	if (! f1)
-	  {
-	    struct scroll_bar *bar
-              = x_window_to_scroll_bar (WindowFromPoint (pt), 2);
+		      /* If a child window was found, make sure that its
+			 frame is a child frame (Bug#26615, maybe).  */
+		      if (f2 && FRAME_PARENT_FRAME (f2))
+			f1 = f2;
+		    }
+		}
+	    }
+	}

-	    if (bar)
-	      f1 = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
-	  }
+      if (!f1 || FRAME_TOOLTIP_P (f1))
+	/* Don't use a tooltip frame.  */
+	f1 = ((EQ (track_mouse, Qdropping) && x_mouse_grabbed (dpyinfo))
+	      ? dpyinfo->last_mouse_frame
+	      : NULL);
+
+      /* If not, is it one of our scroll bars?  */
+      if (!f1)
+	{
+	  struct scroll_bar *bar
+	    = x_window_to_scroll_bar (WindowFromPoint (pt), 2);

-	if (f1 == 0 && insist > 0)
-	  f1 = SELECTED_FRAME ();
+	  if (bar)
+	    f1 = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+	}

-	if (f1)
-	  {
-	    /* Ok, we found a frame.  Store all the values.
-	       last_mouse_glyph is a rectangle used to reduce the
-	       generation of mouse events.  To not miss any motion
-	       events, we must divide the frame into rectangles of the
-	       size of the smallest character that could be displayed
-	       on it, i.e. into the same rectangles that matrices on
-	       the frame are divided into.  */
-
-	    dpyinfo = FRAME_DISPLAY_INFO (f1);
-	    ScreenToClient (FRAME_W32_WINDOW (f1), &pt);
-	    remember_mouse_glyph (f1, pt.x, pt.y, &dpyinfo->last_mouse_glyph);
-	    dpyinfo->last_mouse_glyph_frame = f1;
-
-	    *bar_window = Qnil;
-	    *part = scroll_bar_above_handle;
-	    *fp = f1;
-	    XSETINT (*x, pt.x);
-	    XSETINT (*y, pt.y);
-	    *time = dpyinfo->last_mouse_movement_time;
-	  }
-      }
+      if (!f1 && insist > 0)
+	f1 = SELECTED_FRAME ();
+
+      if (f1)
+	{
+	  /* Ok, we found a frame.  Store all the values.
+	     last_mouse_glyph is a rectangle used to reduce the
+	     generation of mouse events.  To not miss any motion
+	     events, we must divide the frame into rectangles of the
+	     size of the smallest character that could be displayed
+	     on it, i.e. into the same rectangles that matrices on
+	     the frame are divided into.  */
+
+	  dpyinfo = FRAME_DISPLAY_INFO (f1);
+	  ScreenToClient (FRAME_W32_WINDOW (f1), &pt);
+	  remember_mouse_glyph (f1, pt.x, pt.y, &dpyinfo->last_mouse_glyph);
+	  dpyinfo->last_mouse_glyph_frame = f1;
+
+	  *bar_window = Qnil;
+	  *part = scroll_bar_above_handle;
+	  *fp = f1;
+	  XSETINT (*x, pt.x);
+	  XSETINT (*y, pt.y);
+	  *time = dpyinfo->last_mouse_movement_time;
+	}
     }

   unblock_input ();
@@ -4631,6 +4637,42 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object
 /* Temporarily store lead byte of DBCS input sequences.  */
 static char dbcs_lead = 0;

+/**
+ * mouse_or_wdesc_frame:
+ *
+ * When not dropping and the mouse was grabbed for DPYINFO, return the
+ * frame where the mouse was seen last.  If there's no such frame,
+ * return the frame according to WDESC.
+ *
+ * When dropping, return the frame according to WDESC.  If there's no
+ * such frame and the mouse was grabbed for DPYINFO, return the frame
+ * where the mouse was seen last.
+ *
+ * In either case, never return a tooltip frame.
+ */
+static struct frame *
+mouse_or_wdesc_frame (struct w32_display_info *dpyinfo, HWND wdesc)
+{
+  struct frame *lm_f = (x_mouse_grabbed (dpyinfo)
+			? dpyinfo->last_mouse_frame
+			: NULL);
+
+  if (lm_f && !EQ (track_mouse, Qdropping))
+    return lm_f;
+  else
+    {
+      struct frame *w_f = x_window_to_frame (dpyinfo, wdesc);
+
+      /* Do not return a tooltip frame.  */
+      if (!w_f || FRAME_TOOLTIP_P (w_f))
+	return EQ (track_mouse, Qdropping) ? lm_f : NULL;
+      else
+	/* When dropping it would be probably nice to raise w_f
+	   here.  */
+	return w_f;
+    }
+}
+
 /* Read events coming from the W32 shell.
    This routine is called by the SIGIO handler.
    We return as soon as there are no more events to be read.
@@ -4903,16 +4945,13 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object

           previous_help_echo_string = help_echo_string;
 	  help_echo_string = Qnil;
-
-	  f = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame
-	       : x_window_to_frame (dpyinfo, msg.msg.hwnd));
-
 	  if (hlinfo->mouse_face_hidden)
 	    {
 	      hlinfo->mouse_face_hidden = false;
 	      clear_mouse_face (hlinfo);
 	    }

+	  f = mouse_or_wdesc_frame (dpyinfo, msg.msg.hwnd);
 	  if (f)
 	    {
 	      /* Maybe generate SELECT_WINDOW_EVENTs for
@@ -4984,9 +5023,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object
 	    int button = 0;
 	    int up = 0;

-	    f = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame
-		 : x_window_to_frame (dpyinfo, msg.msg.hwnd));
-
+	    f = mouse_or_wdesc_frame (dpyinfo, msg.msg.hwnd);
 	    if (f)
 	      {
                 construct_mouse_click (&inev, &msg, f);
@@ -5045,9 +5082,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object
 	case WM_MOUSEWHEEL:
         case WM_MOUSEHWHEEL:
 	  {
-	    f = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame
-		 : x_window_to_frame (dpyinfo, msg.msg.hwnd));
-
+	    f = mouse_or_wdesc_frame (dpyinfo, msg.msg.hwnd);
 	    if (f)
 	      {
 		if (!dpyinfo->w32_focus_frame
@@ -5403,6 +5438,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object
 	      if (any_help_event_p)
 		do_help = -1;
 	    }
+
 	  break;

 	case WM_SETFOCUS:
diff --git a/src/window.c b/src/window.c
index 422b06a..87616a6 100644
--- a/src/window.c
+++ b/src/window.c
@@ -82,7 +82,7 @@ static struct window *set_window_scroll_bars (struct window *, Lisp_Object,
    by setting it to nil.  */
 Lisp_Object Vwindow_list;

-/* The mini-buffer window of the selected frame.
+/* The last minibuffer-window used.
    Note that you cannot test for mini-bufferness of an arbitrary window
    by comparing against this; but you can test for mini-bufferness of
    the selected window.  */
@@ -340,63 +340,270 @@ struct window *

 DEFUN ("frame-root-window", Fframe_root_window, Sframe_root_window, 0, 1, 0,
        doc: /* Return the root window of FRAME-OR-WINDOW.
-If omitted, FRAME-OR-WINDOW defaults to the currently selected frame.
-With a frame argument, return that frame's root window.
-With a window argument, return the root window of that window's frame.  */)
+If FRAME-OR-WINDOW is nil or omitted, return the root window of the
+selected frame.  If FRAME-OR-WINDOW denotes a live frame, return the
+root window of that frame.  If FRAME-OR-WINDOW denotes a valid window,
+return the root window of that window's frame.  Otherwise, signal an
+error.  */)
   (Lisp_Object frame_or_window)
 {
-  Lisp_Object window;
+  return FRAME_ROOT_WINDOW (decode_frame_or_window (frame_or_window));
+}

-  if (NILP (frame_or_window))
-    window = SELECTED_FRAME ()->root_window;
-  else if (WINDOW_VALID_P (frame_or_window))
-      window = XFRAME (XWINDOW (frame_or_window)->frame)->root_window;
-  else
+/**
+ * display_buffer:
+ *
+ * Display BUFFER via `display-buffer'.
+ */
+static Lisp_Object
+display_buffer (Lisp_Object buffer, Lisp_Object not_this_window_p, Lisp_Object override_frame)
+{
+  return call3 (Qdisplay_buffer, buffer, not_this_window_p, override_frame);
+}
+
+/**
+ * display_minibuffer:
+ *
+ * Display BUFFER via `display-minibuffer' on frame FRAME.
+ */
+static Lisp_Object
+display_minibuffer (Lisp_Object buffer, Lisp_Object frame)
+{
+  return call2 (Qdisplay_minibuffer, buffer, frame);
+}
+
+/**
+ * minibuffer_window:
+ *
+ * Return minibuffer window for frame F.  If F has a live minibuffer
+ * window, return that window.  Otherwise, if F is supposed to have a
+ * loose minibuffer window, try to make a minibuffer window by calling
+ * `display-minibuffer'.  If this fails or if F is supposed to have a
+ * fixed minibuffer window, make a new minibuffer-only frame on F's
+ * display.  In either case, set F's minibuffer window to the new
+ * minibuffer window and return that window.  Abort if creating the
+ * new minibuffer-only frame fails.
+ */
+Lisp_Object
+minibuffer_window (struct frame *f)
+{
+  Lisp_Object minibuffer_window = MINIBUFFER_WINDOW (f);
+
+  if (!WINDOW_LIVE_P (minibuffer_window))
     {
-      CHECK_LIVE_FRAME (frame_or_window);
-      window = XFRAME (frame_or_window)->root_window;
+      Lisp_Object frame, display, mini_frame;
+
+      XSETFRAME (frame, f);
+
+      if (EQ (get_frame_param (f, Qminibuffer), Qloose))
+	{
+	  Lisp_Object buffer = (NILP (Vminibuffer_list)
+				? get_minibuffer (0)
+				: Fcar (Vminibuffer_list));
+
+	  XSETFRAME (frame, f);
+	  /* If FRAME does not have a live minibuffer window, call
+	     `display-minibuffer' to get one.  */
+	  minibuffer_window = display_minibuffer (buffer, frame);
+	  if (WINDOW_LIVE_P (minibuffer_window))
+	    {
+	      fset_minibuffer_window (f, minibuffer_window);
+	      XWINDOW (minibuffer_window)->minibuffer = true;
+
+	      return minibuffer_window;
+	    }
+	}
+
+      display = Fcdr (Fassq (Qdisplay, Fframe_parameters (frame)));
+      mini_frame = call1 (Qmake_initial_minibuffer_frame, display);
+      if (!NILP (mini_frame))
+	{
+	  fset_minibuffer_window (f, FRAME_ROOT_WINDOW (XFRAME (mini_frame)));
+	  minibuffer_window = f->minibuffer_window;
+	}
+      else
+	emacs_abort ();
     }

-  return window;
+  return minibuffer_window;
 }

-DEFUN ("minibuffer-window", Fminibuffer_window, Sminibuffer_window, 0, 1, 0,
-       doc: /* Return the minibuffer window for frame FRAME.
-If FRAME is omitted or nil, it defaults to the selected frame.  */)
-  (Lisp_Object frame)
+DEFUN ("minibuffer-window", Fminibuffer_window, Sminibuffer_window, 0, 2, 0,
+       doc: /* Return the minibuffer window of FRAME-OR-WINDOW.
+If FRAME-OR-WINDOW is nil or omitted, return the minibuffer window of
+the selected frame.  If FRAME-OR-WINDOW denotes a live frame, return
+the minibuffer window of that frame.  If FRAME-OR-WINDOW denotes a
+valid window, return the minibuffer window of that window's frame.
+Otherwise, signal an error.
+
+If the optional argument MAYBE-NIL is nil or omitted, this function
+will try to create a minibuffer window and return it if the frame
+specified by FRAME-OR-WINDOW does not have a live minibuffer window.
+MAYBE-NIL non-nil means that this function will not bother to create a
+new minibuffer window in that case and return nil.  */)
+     (Lisp_Object frame_or_window, Lisp_Object maybe_nil)
 {
-  return FRAME_MINIBUF_WINDOW (decode_live_frame (frame));
+  struct frame *f = decode_frame_or_window (frame_or_window);
+
+  if (NILP (maybe_nil))
+    return minibuffer_window (f);
+  else
+    {
+      Lisp_Object minibuffer_window = MINIBUFFER_WINDOW (f);
+
+      return (WINDOW_LIVE_P (minibuffer_window)
+	      ? minibuffer_window
+	      : Qnil);
+    }
 }

-DEFUN ("window-minibuffer-p", Fwindow_minibuffer_p,
-       Swindow_minibuffer_p, 0, 1, 0,
+DEFUN ("window-minibuffer-p", Fwindow_minibuffer_p, Swindow_minibuffer_p,
+       0, 1, 0,
        doc: /* Return non-nil if WINDOW is a minibuffer window.
-WINDOW must be a valid window and defaults to the selected one.  */)
-  (Lisp_Object window)
+WINDOW must be a valid window and defaults to the selected one.
+
+If WINDOW is a minibuffer window, the return value is the minibuffer
+window type of WINDOW - 'bottom', 'top', 'only' or 'loose'.  */)
+     (Lisp_Object window)
 {
-  return MINI_WINDOW_P (decode_valid_window (window)) ? Qt : Qnil;
+  struct window *w = decode_valid_window (window);
+
+  return (MINIBUFFER_WINDOW_P (w)
+	  ? Fminibuffer_window_type (WINDOW_FRAME (w))
+	  : Qnil);
 }

-/* Don't move this to window.el - this must be a safe routine.  */
-DEFUN ("frame-first-window", Fframe_first_window, Sframe_first_window, 0, 1, 0,
-       doc: /* Return the topmost, leftmost live window on FRAME-OR-WINDOW.
-If omitted, FRAME-OR-WINDOW defaults to the currently selected frame.
-Else if FRAME-OR-WINDOW denotes a valid window, return the first window
-of that window's frame.  If FRAME-OR-WINDOW denotes a live frame, return
-the first window of that frame.  */)
-  (Lisp_Object frame_or_window)
+/**
+ * display_echo:
+ *
+ * Display BUFFER via `display-echo' on frame FRAME.
+ */
+static Lisp_Object
+display_echo (Lisp_Object buffer, Lisp_Object frame)
 {
-  Lisp_Object window;
+  return call2 (Qdisplay_echo, buffer, frame);
+}
+
+/**
+ * echo_window:
+ *
+ * Return echo window for frame F.  If F has a live echo window,
+ * return that window.  Otherwise, if F is supposed to have a separate
+ * echo window, try to make it via `display-echo'.  If this fails or
+ * if F is not supposed to have a separate echo window, return the
+ * minibuffer window on F's display, making one if necessary.  Abort
+ * if that operation fails.
+ */
+Lisp_Object
+echo_window (struct frame *f)
+{
+  Lisp_Object echo_window = ECHO_WINDOW (f);
+
+  if (!WINDOW_LIVE_P (echo_window))
+    {
+      Lisp_Object frame;

-  if (NILP (frame_or_window))
-    window = SELECTED_FRAME ()->root_window;
-  else if (WINDOW_VALID_P (frame_or_window))
-    window = XFRAME (WINDOW_FRAME (XWINDOW (frame_or_window)))->root_window;
+      XSETFRAME (frame, f);
+
+      /* Call `display-echo' to make a new echo window with
+	 echo_buffer[0] as its buffer.  */
+      if (!NILP (get_frame_param (f, Qecho_area))
+	  && BUFFERP (echo_buffer[0])
+	  && BUFFER_LIVE_P (XBUFFER (echo_buffer[0])))
+	{
+	  XSETFRAME (frame, f);
+	  echo_window = display_echo (echo_buffer[0], frame);
+
+	  if (WINDOW_LIVE_P (echo_window))
+	    {
+	      fset_echo_window (f, echo_window);
+	      XWINDOW (echo_window)->echo = true;
+
+	      return echo_window;
+	    }
+	}
+
+      echo_window = minibuffer_window (f);
+      XWINDOW (echo_window)->echo = true;
+      fset_echo_window (f, echo_window);
+    }
+
+  return echo_window;
+}
+
+DEFUN ("echo-window", Fecho_window, Secho_window, 0, 2, 0,
+       doc: /* Return the echo window of FRAME-OR-WINDOW.
+If FRAME-OR-WINDOW is nil or omitted, return the echo window of the
+selected frame.  If FRAME-OR-WINDOW denotes a live frame, return the
+echo window of that frame.  If FRAME-OR-WINDOW denotes a valid window,
+return the echo window of that window's frame.  Otherwise, signal an
+error.
+
+If the optional argument MAYBE-NIL is nil or omitted, this function
+will try to create an echo window and return it if the frame specified
+by FRAME-OR-WINDOW does not have a live echo window.  MAYBE-NIL
+non-nil means that this function will not bother to create a new echo
+window in that case and return nil.  */)
+     (Lisp_Object frame_or_window, Lisp_Object maybe_nil)
+{
+  struct frame *f = decode_frame_or_window (frame_or_window);
+
+  if (NILP (maybe_nil))
+    return echo_window (f);
   else
     {
-      CHECK_LIVE_FRAME (frame_or_window);
-      window = XFRAME (frame_or_window)->root_window;
+      Lisp_Object echo_window = ECHO_WINDOW (f);
+
+      return (WINDOW_LIVE_P (echo_window)
+	      ? echo_window
+	      : Qnil);
     }
+}
+
+DEFUN ("window-echo-p", Fwindow_echo_p, Swindow_echo_p,
+       0, 1, 0,
+       doc: /* Return non-nil if WINDOW is an echo window.
+WINDOW must be a valid window and defaults to the selected one.
+
+If WINDOW is an echo window, the return value is the minibuffer window
+type of WINDOW's frame - 'bottom', 'top', 'only' or 'loose'.  */)
+     (Lisp_Object window)
+{
+  struct window *w = decode_valid_window (window);
+
+  return (ECHO_WINDOW_P (w)
+	  ? Fminibuffer_window_type (WINDOW_FRAME (w))
+	  : Qnil);
+}
+
+DEFUN ("window-mini-p", Fwindow_mini_p, Swindow_mini_p,
+       0, 1, 0,
+       doc: /* Return non-nil if WINDOW is a mini (minibuffer or echo) window.
+WINDOW must be a valid window and defaults to the selected one.  */)
+     (Lisp_Object window)
+{
+  struct window *w = decode_valid_window (window);
+
+  return MINI_WINDOW_P (w) ? Qt : Qnil;
+}
+
+/* Don't move this to window.el - this must be a safe routine.  */
+DEFUN ("frame-first-window", Fframe_first_window, Sframe_first_window, 0, 1, 0,
+       doc: /* Return the first window on FRAME-OR-WINDOW.
+The first window of a frame is the topmost, leftmost live window of
+that frame's root window.  If the frame has a minibuffer window on its
+top, it is the topmost, leftmost live window displayed beneath the
+minibuffer window.
+
+If FRAME-OR-WINDOW is nil or omitted, return the first window of the
+selected frame.  If FRAME-OR-WINDOW denotes a live frame, return the
+first window of that frame.  If FRAME-OR-WINDOW denotes a valid
+window, return the first window of that window's frame.  Otherwise,
+signal an error.  */)
+  (Lisp_Object frame_or_window)
+{
+  Lisp_Object window = FRAME_ROOT_WINDOW (decode_frame_or_window
+					  (frame_or_window));

   while (WINDOWP (XWINDOW (window)->contents))
     window = XWINDOW (window)->contents;
@@ -407,25 +614,14 @@ struct window *
 DEFUN ("frame-selected-window", Fframe_selected_window,
        Sframe_selected_window, 0, 1, 0,
        doc: /* Return the selected window of FRAME-OR-WINDOW.
-If omitted, FRAME-OR-WINDOW defaults to the currently selected frame.
-Else if FRAME-OR-WINDOW denotes a valid window, return the selected
-window of that window's frame.  If FRAME-OR-WINDOW denotes a live frame,
-return the selected window of that frame.  */)
+If FRAME-OR-WINDOW is nil or omitted, return the selected window.  If
+FRAME-OR-WINDOW denotes a live frame, return the selected window of
+that frame.  If FRAME-OR-WINDOW denotes a valid window, return the
+selected window of that window's frame.  Otherwise, signal an
+error.  */)
   (Lisp_Object frame_or_window)
 {
-  Lisp_Object window;
-
-  if (NILP (frame_or_window))
-    window = SELECTED_FRAME ()->selected_window;
-  else if (WINDOW_VALID_P (frame_or_window))
-    window = XFRAME (WINDOW_FRAME (XWINDOW (frame_or_window)))->selected_window;
-  else
-    {
-      CHECK_LIVE_FRAME (frame_or_window);
-      window = XFRAME (frame_or_window)->selected_window;
-    }
-
-  return window;
+  return FRAME_SELECTED_WINDOW (decode_frame_or_window (frame_or_window));
 }

 DEFUN ("set-frame-selected-window", Fset_frame_selected_window,
@@ -483,6 +679,9 @@ struct window *

   w = XWINDOW (window);

+  if (ECHO_WINDOW_P (w) && !MINIBUFFER_WINDOW_P (w))
+    w = XWINDOW (window);
+
   /* Make the selected window's buffer current.  */
   Fset_buffer (w->contents);

@@ -518,6 +717,7 @@ struct window *
     fset_selected_window (sf, window);

   select_window_1 (window, inhibit_point_swap);
+
   bset_last_selected_window (XBUFFER (w->contents), window);

  record_and_return:
@@ -2389,7 +2589,7 @@ struct Lisp_Char_Table *
 }

 /* If WINDOW can be deleted, delete it.  */
-static void
+void
 delete_deletable_window (Lisp_Object window)
 {
   if (!NILP (call1 (Qwindow_deletable_p, window)))
@@ -2443,26 +2643,33 @@ struct Lisp_Char_Table *
   return Vwindow_list;
 }

-
-/* Value is true if WINDOW satisfies the constraints given by
-   OWINDOW, MINIBUF and ALL_FRAMES.
-
-   MINIBUF	t means WINDOW may be minibuffer windows.
-		`lambda' means WINDOW may not be a minibuffer window.
-		a window means a specific minibuffer window
-
-   ALL_FRAMES	t means search all frames,
-		nil means search just current frame,
-		`visible' means search just visible frames on the
-                current terminal,
-		0 means search visible and iconified frames on the
-                current terminal,
-		a window means search the frame that window belongs to,
-		a frame means consider windows on that frame, only.  */
-
+/**
+ * candidate_window_p:
+ *
+ * Value is true if WINDOW satisfies the constraints given by OWINDOW,
+ * MINI and ALL_FRAMES.
+ *
+ * MINI 	t means WINDOW may be a mini window (a minibuffer or
+ *		echo window),
+ *		'lambda' means WINDOW may not be a mini window,
+ *		'minibuffer' means WINDOW may be a minibuffer but not
+ *		an echo window,
+ *		'echo-area' means WINDOW may be an echo but not a
+ *		minibuffer window,
+ *		a window means a specific minibuffer window.
+ *
+ * ALL_FRAMES	t means search all frames,
+ *		nil means search just current frame,
+ *		'visible' means search just visible frames on the
+ *              current terminal,
+ *		0 means search visible and iconified frames on the
+ *              current terminal,
+ *		a window means search the frame that window belongs to,
+ *		a frame means consider windows on that frame, only.
+ */
 static bool
 candidate_window_p (Lisp_Object window, Lisp_Object owindow,
-		    Lisp_Object minibuf, Lisp_Object all_frames)
+		    Lisp_Object mini, Lisp_Object all_frames)
 {
   struct window *w = XWINDOW (window);
   struct frame *f = XFRAME (w->frame);
@@ -2470,11 +2677,19 @@ struct Lisp_Char_Table *

   if (!BUFFERP (w->contents))
     candidate_p = false;
-  else if (MINI_WINDOW_P (w)
-           && (EQ (minibuf, Qlambda)
-	       || (WINDOWP (minibuf) && !EQ (minibuf, window))))
+  else if (MINIBUFFER_WINDOW_P (w)
+           && (EQ (mini, Qlambda) || EQ (mini, Qecho_area)
+	       || (WINDOWP (mini) && !EQ (mini, window))))
+    {
+      /* If MINI is 'lambda' or 'echo-area' don't consider minibuffer
+         windows.  If it is a window, consider only that one.  */
+      candidate_p = false;
+    }
+  else if (ECHO_WINDOW_P (w)
+           && (EQ (mini, Qlambda) || EQ (mini, Qminibuffer)
+	       || (WINDOWP (mini) && !EQ (mini, window))))
     {
-      /* If MINIBUF is `lambda' don't consider any mini-windows.
+      /* If MINI is 'lambda' or 'echo' don't consider echo windows.
          If it is a window, consider only that one.  */
       candidate_p = false;
     }
@@ -2514,6 +2729,9 @@ struct Lisp_Char_Table *
 	to just share the minibuffer window - it must be active as well
 	(see Bug#24500).  */
     candidate_p = (EQ (XWINDOW (all_frames)->frame, w->frame)
+		   || (EQ (MINIBUFFER_WINDOW (f), all_frames)
+		       && EQ (MINIBUFFER_WINDOW (f), minibuf_window)
+		       && minibuf_level)
 		   || EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f)));
   else if (FRAMEP (all_frames))
     candidate_p = EQ (all_frames, w->frame);
@@ -2522,32 +2740,34 @@ struct Lisp_Char_Table *
 }


-/* Decode arguments as allowed by Fnext_window, Fprevious_window, and
-   Fwindow_list.  See candidate_window_p for the meaning of WINDOW,
-   MINIBUF, and ALL_FRAMES.  */
-
+/**
+ * decode_next_window_args:
+ *
+ * Decode arguments as allowed by Fnext_window, Fprevious_window, and
+ * Fwindow_list.  See candidate_window_p for the meaning of WINDOW,
+ * MINI, and ALL_FRAMES.
+ */
 static void
-decode_next_window_args (Lisp_Object *window, Lisp_Object *minibuf, Lisp_Object *all_frames)
+decode_next_window_args (Lisp_Object *window, Lisp_Object *mini, Lisp_Object *all_frames)
 {
   struct window *w = decode_live_window (*window);

   XSETWINDOW (*window, w);
-  /* MINIBUF nil may or may not include minibuffers.  Decide if it
+  /* MINI nil may or may not include minibuffer windows.  Decide if it
      does.  */
-  if (NILP (*minibuf))
-    *minibuf = minibuf_level ? minibuf_window : Qlambda;
-  else if (!EQ (*minibuf, Qt))
-    *minibuf = Qlambda;
-
-  /* Now *MINIBUF can be t => count all minibuffer windows, `lambda'
-     => count none of them, or a specific minibuffer window (the
-     active one) to count.  */
+  if (NILP (*mini))
+    *mini = minibuf_level ? minibuf_window : Qlambda;
+  else if (!EQ (*mini, Qt) && !EQ (*mini, Qminibuffer)
+	   && !EQ (*mini, Qecho_area))
+    *mini = Qlambda;

   /* ALL_FRAMES nil doesn't specify which frames to include.  */
   if (NILP (*all_frames))
     *all_frames
-      = (!EQ (*minibuf, Qlambda)
-	 ? FRAME_MINIBUF_WINDOW (XFRAME (w->frame))
+      = ((!EQ (*mini, Qlambda) && !EQ (*mini, Qecho_area))
+	 /* Only a minibuffer window can be shared here, not the echo
+	    window.  */
+	 ? MINIBUFFER_WINDOW (XFRAME (w->frame))
 	 : Qnil);
   else if (EQ (*all_frames, Qvisible))
     ;
@@ -2560,16 +2780,19 @@ struct Lisp_Char_Table *
 }


-/* Return the next or previous window of WINDOW in cyclic ordering
-   of windows.  NEXT_P means return the next window.  See the
-   documentation string of next-window for the meaning of MINIBUF and
-   ALL_FRAMES.  */
-
+/**
+ * next_window:
+ *
+ * Return the next or previous window of WINDOW in cyclic ordering of
+ * windows.  NEXT_P means return the next window.  See the
+ * documentation string of 'next-window' for the meaning of MINI and
+ * ALL_FRAMES.
+ */
 static Lisp_Object
-next_window (Lisp_Object window, Lisp_Object minibuf, Lisp_Object all_frames,
+next_window (Lisp_Object window, Lisp_Object mini, Lisp_Object all_frames,
 	     bool next_p)
 {
-  decode_next_window_args (&window, &minibuf, &all_frames);
+  decode_next_window_args (&window, &mini, &all_frames);

   /* If ALL_FRAMES is a frame, and WINDOW isn't on that frame, just
      return the first window on the frame.  */
@@ -2587,7 +2810,7 @@ struct Lisp_Char_Table *
       /* Scan forward from WINDOW to the end of the window list.  */
       if (CONSP (list))
 	for (list = XCDR (list); CONSP (list); list = XCDR (list))
-	  if (candidate_window_p (XCAR (list), window, minibuf, all_frames))
+	  if (candidate_window_p (XCAR (list), window, mini, all_frames))
 	    break;

       /* Scan from the start of the window list up to WINDOW.  */
@@ -2595,7 +2818,7 @@ struct Lisp_Char_Table *
 	for (list = Vwindow_list;
 	     CONSP (list) && !EQ (XCAR (list), window);
 	     list = XCDR (list))
-	  if (candidate_window_p (XCAR (list), window, minibuf, all_frames))
+	  if (candidate_window_p (XCAR (list), window, mini, all_frames))
 	    break;

       if (CONSP (list))
@@ -2617,8 +2840,7 @@ struct Lisp_Char_Table *
 	      if (WINDOWP (candidate))
 		break;
 	    }
-	  else if (candidate_window_p (XCAR (list), window, minibuf,
-				       all_frames))
+	  else if (candidate_window_p (XCAR (list), window, mini, all_frames))
 	    candidate = XCAR (list);
 	}

@@ -2633,23 +2855,25 @@ struct Lisp_Char_Table *
 DEFUN ("next-window", Fnext_window, Snext_window, 0, 3, 0,
        doc: /* Return live window after WINDOW in the cyclic ordering of windows.
 WINDOW must be a live window and defaults to the selected one.  The
-optional arguments MINIBUF and ALL-FRAMES specify the set of windows to
+optional arguments MINI and ALL-FRAMES specify the set of windows to
 consider.

-MINIBUF nil or omitted means consider the minibuffer window only if the
-minibuffer is active.  MINIBUF t means consider the minibuffer window
-even if the minibuffer is not active.  Any other value means do not
-consider the minibuffer window even if the minibuffer is active.
+MINI nil or omitted means consider a minibuffer window only if it is
+active.  MINI t means consider any mini window (minibuffer window or
+echo window).  MINI 'minibuffer' means consider any minibuffer window
+but no echo windows.  MINI 'echo-area' means consider any echo window
+but not a minibuffer window.  Any other value means do not consider a
+mini window even if the minibuffer is active.

-ALL-FRAMES nil or omitted means consider all windows on WINDOW's frame,
-plus the minibuffer window if specified by the MINIBUF argument.  If the
-minibuffer counts, consider all windows on all frames that share that
-minibuffer too.  The following non-nil values of ALL-FRAMES have special
-meanings:
+ALL-FRAMES nil or omitted means consider all windows on WINDOW's
+frame, plus any mini window if specified by the MINI argument.  If a
+mini window counts, consider all windows on all frames that share that
+mini window too.  The following non-nil values of ALL-FRAMES have
+special meanings:

 - t means consider all windows on all existing frames.

-- `visible' means consider all windows on all visible frames.
+- 'visible' means consider all windows on all visible frames.

 - 0 (the number zero) means consider all windows on all visible and
   iconified frames.
@@ -2659,36 +2883,38 @@ struct Lisp_Char_Table *
 Anything else means consider all windows on WINDOW's frame and no
 others.

-If you use consistent values for MINIBUF and ALL-FRAMES, you can use
+If you use consistent values for MINI and ALL-FRAMES, you can use
 `next-window' to iterate through the entire cycle of acceptable
 windows, eventually ending up back at the window you started with.
 `previous-window' traverses the same cycle, in the reverse order.  */)
-  (Lisp_Object window, Lisp_Object minibuf, Lisp_Object all_frames)
+  (Lisp_Object window, Lisp_Object mini, Lisp_Object all_frames)
 {
-  return next_window (window, minibuf, all_frames, true);
+  return next_window (window, mini, all_frames, true);
 }


 DEFUN ("previous-window", Fprevious_window, Sprevious_window, 0, 3, 0,
        doc: /* Return live window before WINDOW in the cyclic ordering of windows.
 WINDOW must be a live window and defaults to the selected one.  The
-optional arguments MINIBUF and ALL-FRAMES specify the set of windows to
+optional arguments MINI and ALL-FRAMES specify the set of windows to
 consider.

-MINIBUF nil or omitted means consider the minibuffer window only if the
-minibuffer is active.  MINIBUF t means consider the minibuffer window
-even if the minibuffer is not active.  Any other value means do not
-consider the minibuffer window even if the minibuffer is active.
+MINI nil or omitted means consider a minibuffer window only if it is
+active.  MINI t means consider any mini window (minibuffer window or
+echo window).  MINI 'minibuffer' means consider any minibuffer window
+but no echo windows.  MINI 'echo-area' means consider any echo window
+but not a minibuffer window.  Any other value means do not consider a
+mini window even if the minibuffer is active.

-ALL-FRAMES nil or omitted means consider all windows on WINDOW's frame,
-plus the minibuffer window if specified by the MINIBUF argument.  If the
-minibuffer counts, consider all windows on all frames that share that
-minibuffer too.  The following non-nil values of ALL-FRAMES have special
-meanings:
+ALL-FRAMES nil or omitted means consider all windows on WINDOW's
+frame, plus any mini window if specified by the MINI argument.  If a
+mini window counts, consider all windows on all frames that share that
+mini window too.  The following non-nil values of ALL-FRAMES have
+special meanings:

 - t means consider all windows on all existing frames.

-- `visible' means consider all windows on all visible frames.
+- 'visible' means consider all windows on all visible frames.

 - 0 (the number zero) means consider all windows on all visible and
   iconified frames.
@@ -2698,30 +2924,31 @@ struct Lisp_Char_Table *
 Anything else means consider all windows on WINDOW's frame and no
 others.

-If you use consistent values for MINIBUF and ALL-FRAMES, you can
-use `previous-window' to iterate through the entire cycle of
-acceptable windows, eventually ending up back at the window you
-started with.  `next-window' traverses the same cycle, in the
-reverse order.  */)
-  (Lisp_Object window, Lisp_Object minibuf, Lisp_Object all_frames)
+If you use consistent values for MINI and ALL-FRAMES, you can use
+`previous-window' to iterate through the entire cycle of acceptable
+windows, eventually ending up back at the window you started with.
+`next-window' traverses the same cycle, in the reverse order.  */)
+  (Lisp_Object window, Lisp_Object mini, Lisp_Object all_frames)
 {
-  return next_window (window, minibuf, all_frames, false);
+  return next_window (window, mini, all_frames, false);
 }


-/* Return a list of windows in cyclic ordering.  Arguments are like
-   for `next-window'.  */
-
+/**
+ * next_window:
+ *
+ * Return a list of windows in cyclic ordering.  Arguments are like
+ * for 'next-window'.  */
 static Lisp_Object
-window_list_1 (Lisp_Object window, Lisp_Object minibuf, Lisp_Object all_frames)
+window_list_1 (Lisp_Object window, Lisp_Object mini, Lisp_Object all_frames)
 {
   Lisp_Object tail, list, rest;

-  decode_next_window_args (&window, &minibuf, &all_frames);
+  decode_next_window_args (&window, &mini, &all_frames);
   list = Qnil;

   for (tail = window_list (); CONSP (tail); tail = XCDR (tail))
-    if (candidate_window_p (XCAR (tail), window, minibuf, all_frames))
+    if (candidate_window_p (XCAR (tail), window, mini, all_frames))
       list = Fcons (XCAR (tail), list);

   /* Rotate the list to start with WINDOW.  */
@@ -2742,11 +2969,14 @@ struct Lisp_Char_Table *
        doc: /* Return a list of windows on FRAME, starting with WINDOW.
 FRAME nil or omitted means use the selected frame.
 WINDOW nil or omitted means use the window selected within FRAME.
-MINIBUF t means include the minibuffer window, even if it isn't active.
-MINIBUF nil or omitted means include the minibuffer window only
-if it's active.
-MINIBUF neither nil nor t means never include the minibuffer window.  */)
-  (Lisp_Object frame, Lisp_Object minibuf, Lisp_Object window)
+
+MINI t means include any mini window (minibuffer or echo window).
+MINI nil or omitted means include a minibuffer window only if it's
+active and don't include any echo window.  MINI 'minibuffer' means
+include any minibuffer window but not an echo window.  MINI
+'echo-area' include any eco window window but not a minibuffer window.
+Any other value means to never include at mini window.  */)
+  (Lisp_Object frame, Lisp_Object mini, Lisp_Object window)
 {
   if (NILP (window))
     window = FRAMEP (frame) ? XFRAME (frame)->selected_window : selected_window;
@@ -2757,7 +2987,7 @@ struct Lisp_Char_Table *
   if (!EQ (frame, XWINDOW (window)->frame))
     error ("Window is on a different frame");

-  return window_list_1 (window, minibuf, frame);
+  return window_list_1 (window, mini, frame);
 }


@@ -2766,17 +2996,19 @@ struct Lisp_Char_Table *
 WINDOW specifies the first window to list and defaults to the selected
 window.

-Optional argument MINIBUF nil or omitted means consider the minibuffer
-window only if the minibuffer is active.  MINIBUF t means consider the
-minibuffer window even if the minibuffer is not active.  Any other value
-means do not consider the minibuffer window even if the minibuffer is
-active.
+Optional argument MINI t means include any mini window (minibuffer or
+echo window).  MINI nil or omitted means include a minibuffer window
+only if it's active and don't include any echo window.  MINI
+'minibuffer' means include any minibuffer window but not an echo
+window.  MINI 'echo-area' include any eco window window but not a
+minibuffer window.  Any other value means to never include at mini
+window.

 Optional argument ALL-FRAMES nil or omitted means consider all windows
-on WINDOW's frame, plus the minibuffer window if specified by the
-MINIBUF argument.  If the minibuffer counts, consider all windows on all
-frames that share that minibuffer too.  The following non-nil values of
-ALL-FRAMES have special meanings:
+on WINDOW's frame, plus the minibuffer window if specified by the MINI
+argument.  If the minibuffer counts, consider all windows on all
+frames that share that minibuffer too.  The following non-nil values
+of ALL-FRAMES have special meanings:

 - t means consider all windows on all existing frames.

@@ -2792,9 +3024,9 @@ struct Lisp_Char_Table *

 If WINDOW is not on the list of windows returned, some other window will
 be listed first but no error is signaled.  */)
-  (Lisp_Object window, Lisp_Object minibuf, Lisp_Object all_frames)
+  (Lisp_Object window, Lisp_Object mini, Lisp_Object all_frames)
 {
-  return window_list_1 (window, minibuf, all_frames);
+  return window_list_1 (window, mini, all_frames);
 }
 \f
 /* Look at all windows, performing an operation specified by TYPE
@@ -3071,11 +3303,8 @@ depends on the value of (window-start WINDOW), so if calling this
   if (EQ (window, root))
     /* A noop.  */
     return Qnil;
-  /* I don't understand the "top > 0" part below.  If we deal with a
-     standalone minibuffer it would have been caught by the preceding
-     test.  */
-  else if (MINI_WINDOW_P (w)) /* && top > 0) */
-    error ("Can't expand minibuffer to full frame");
+  else if (MINIBUFFER_WINDOW_P (w) && !MINIBUFFER_WINDOW_LOOSE_P (f))
+    error ("Can't expand minibuffer window to full frame");

   if (BUFFERP (w->contents))
     {
@@ -3460,10 +3689,10 @@ depends on the value of (window-start WINDOW), so if calling this

       window_set_before_size_change_sizes (r);

-      if (FRAME_HAS_MINIBUF_P (f) && !FRAME_MINIBUF_ONLY_P (f))
+      if (MINIBUFFER_WINDOW_FIXED_P (f))
 	/* Record size of FRAME's minibuffer window too.  */
 	window_set_before_size_change_sizes
-	  (XWINDOW (FRAME_MINIBUF_WINDOW (f)));
+	  (XWINDOW (MINIBUFFER_WINDOW (f)));

       FRAME_WINDOW_CONFIGURATION_CHANGED (f) = false;
     }
@@ -3611,12 +3840,6 @@ depends on the value of (window-start WINDOW), so if calling this
   return Qnil;
 }
 \f
-static Lisp_Object
-display_buffer (Lisp_Object buffer, Lisp_Object not_this_window_p, Lisp_Object override_frame)
-{
-  return call3 (Qdisplay_buffer, buffer, not_this_window_p, override_frame);
-}
-
 DEFUN ("force-window-update", Fforce_window_update, Sforce_window_update,
        0, 1, 0,
        doc: /* Force all windows to be updated on next redisplay.
@@ -3781,7 +4004,6 @@ depends on the value of (window-start WINDOW), so if calling this
      allocate_window.  */
   wset_prev_buffers (w, Qnil);
   wset_next_buffers (w, Qnil);
-
   /* Initialize non-Lisp data.  Note that allocate_window zeroes out all
      non-Lisp data, so do it only for slots which should not be zero.  */
   w->nrows_scale_factor = w->ncols_scale_factor = 1;
@@ -3972,13 +4194,17 @@ depends on the value of (window-start WINDOW), so if calling this
 }


-/* Set w->pixel_height (w->pixel_width if HORFLAG) to
-   w->new_pixel for window W and recursively all child windows of W.
-   Also calculate and assign the new vertical (horizontal) pixel start
-   positions of each of these windows.
+/**
+ * window_resize_apply:
+ *
+ * Set pixel height (pixel width if HORFLAG is true) to the new pixels
+ * sizes for window W and recursively all child windows of W.  Also
+ * calculate and assign the new vertical (horizontal) pixel start
+ * positions of each of these windows.

-   This function does not perform any error checks.  Make sure you have
-   run window_resize_check on W before applying this function.  */
+ * This function does not perform any error checks.  Make sure you
+ *  have run window_resize_check on W before running this function.
+ */
 static void
 window_resize_apply (struct window *w, bool horflag)
 {
@@ -4181,24 +4407,41 @@ depends on the value of (window-start WINDOW), so if calling this
   struct window *r = XWINDOW (FRAME_ROOT_WINDOW (f));

   block_input ();
-  /* Necessary when deleting the top-/or leftmost window.  */
-  r->left_col = 0;
-  r->top_line = FRAME_TOP_MARGIN (f);
-  window_resize_apply_total (r, !NILP (horizontal));
-  /* Handle the mini window.  */
-  if (FRAME_HAS_MINIBUF_P (f) && !FRAME_MINIBUF_ONLY_P (f))
+
+  if (NILP (horizontal))
     {
-      struct window *m = XWINDOW (f->minibuffer_window);
+      if (MINIBUFFER_WINDOW_TOP_P (f))
+	{
+	  struct window *m = XWINDOW (MINIBUFFER_WINDOW (f));

-      if (NILP (horizontal))
+	  m->top_line = FRAME_TOP_MARGIN (f);
+	  r->top_line = m->top_line + m->total_lines;
+	}
+      else if (MINIBUFFER_WINDOW_BOTTOM_P (f))
 	{
+	  struct window *m = XWINDOW (MINIBUFFER_WINDOW (f));
+
+	  r->top_line = FRAME_TOP_MARGIN (f);
 	  m->top_line = r->top_line + r->total_lines;
-	  m->total_lines = XFASTINT (m->new_total);
 	}
       else
-	m->total_cols = XFASTINT (m->new_total);
+	r->top_line = FRAME_TOP_MARGIN (f);
+    }
+  else
+    {
+      r->left_col = 0;
+
+      if (MINIBUFFER_WINDOW_FIXED_P (f))
+	{
+	  struct window *m = XWINDOW (MINIBUFFER_WINDOW (f));
+
+	  m->total_cols = XFASTINT (m->new_total);
+	  m->left_col = 0;
+	}
     }

+  window_resize_apply_total (r, !NILP (horizontal));
+
   unblock_input ();

   return Qt;
@@ -4215,7 +4458,7 @@ depends on the value of (window-start WINDOW), so if calling this
 {
   Lisp_Object root = f->root_window;
   struct window *r = XWINDOW (root);
-  Lisp_Object mini = f->minibuffer_window;
+  Lisp_Object mini = MINIBUFFER_WINDOW (f);
   struct window *m;
   /* old_size is the old size of the frame's root window.  */
   int old_size = horflag ? r->total_cols : r->total_lines;
@@ -4233,24 +4476,41 @@ depends on the value of (window-start WINDOW), so if calling this
 	 since these are not part of the frame's text area.  */
       new_pixel_size = max (horflag
 			    ? size
-			    : (size
-			       - ((FRAME_HAS_MINIBUF_P (f)
-				   && !FRAME_MINIBUF_ONLY_P (f))
-				  ? FRAME_LINE_HEIGHT (f) : 0)),
+			    : (size - (MINIBUFFER_WINDOW_FIXED_P (f)
+				       ? FRAME_LINE_HEIGHT (f) : 0)),
 			    unit);
       new_size = new_pixel_size / unit;
     }
   else
     {
-      new_size = max (size - (!horflag
-			      && FRAME_HAS_MINIBUF_P (f)
-			      && !FRAME_MINIBUF_ONLY_P (f)),
+      new_size = max (size - (!horflag && MINIBUFFER_WINDOW_FIXED_P (f)),
 		      1);
       new_pixel_size = new_size * unit;
     }

+  if (MINIBUFFER_WINDOW_TOP_P (f))
+    {
+      m = XWINDOW (mini);
+      if (horflag)
+	{
+	  m->total_cols = new_size;
+	  m->pixel_width = new_pixel_size;
+	}
+      else
+	{
+	  /* Are we sure we always want 1 line here?  */
+	  m->total_lines = 1;
+	  m->pixel_height = FRAME_LINE_HEIGHT (f);
+	  m->top_line = FRAME_TOP_MARGIN (f);
+	  m->pixel_top = FRAME_TOP_MARGIN_HEIGHT (f);
+	}
+    }
+
   if (new_pixel_size == old_pixel_size
-      && (horflag || r->pixel_top == FRAME_TOP_MARGIN_HEIGHT (f)))
+      && (horflag
+	  || (FRAME_TOP_MARGIN_HEIGHT (f)
+	      == (MINIBUFFER_WINDOW_TOP_P (f)
+		  ? m->pixel_top : r->pixel_top))))
     ;
   else if (WINDOW_LEAF_P (r))
     /* For a leaf root window just set the size.  */
@@ -4261,8 +4521,12 @@ depends on the value of (window-start WINDOW), so if calling this
       }
     else
       {
-	r->top_line = FRAME_TOP_MARGIN (f);
-	r->pixel_top = FRAME_TOP_MARGIN_HEIGHT (f);
+	r->top_line = (MINIBUFFER_WINDOW_TOP_P (f)
+		       ? m->top_line + m->total_lines
+		       : FRAME_TOP_MARGIN (f));
+	r->pixel_top = (MINIBUFFER_WINDOW_TOP_P (f)
+			? m->pixel_top + m->pixel_height
+			: FRAME_TOP_MARGIN_HEIGHT (f));

 	r->total_lines = new_size;
 	r->pixel_height = new_pixel_size;
@@ -4273,8 +4537,12 @@ depends on the value of (window-start WINDOW), so if calling this

       if (!horflag)
 	{
-	  r->top_line = FRAME_TOP_MARGIN (f);
-	  r->pixel_top = FRAME_TOP_MARGIN_HEIGHT (f);
+	  r->top_line = (MINIBUFFER_WINDOW_TOP_P (f)
+			 ? m->top_line + m->total_lines
+			 : FRAME_TOP_MARGIN (f));
+	  r->pixel_top = (MINIBUFFER_WINDOW_TOP_P (f)
+			  ? m->pixel_top + m->pixel_height
+			  : FRAME_TOP_MARGIN_HEIGHT (f));
 	}

       if (pixelwise)
@@ -4305,7 +4573,7 @@ depends on the value of (window-start WINDOW), so if calling this
 	}
     }

-  if (FRAME_HAS_MINIBUF_P (f) && !FRAME_MINIBUF_ONLY_P (f))
+  if (MINIBUFFER_WINDOW_BOTTOM_P (f))
     {
       m = XWINDOW (mini);
       if (horflag)
@@ -4395,7 +4663,7 @@ SIDE t (or `right') specifies that the new window shall be located on
   r = XWINDOW (reference);

   /* The following bugs are caught by `split-window'.  */
-  if (MINI_WINDOW_P (o))
+  if (MINIBUFFER_WINDOW_P (o))
     error ("Attempt to split minibuffer window");
   else if (total_size < (horflag ? 2 : 1))
     error ("Size of new window too small (after split)");
@@ -4709,146 +4977,182 @@ SIDE t (or `right') specifies that the new window shall be located on
 			Resizing Mini-Windows
  ***********************************************************************/

-/* Grow mini-window W by DELTA lines, DELTA >= 0, or as much as we
-   can.  */
-void
-grow_mini_window (struct window *w, int delta, bool pixelwise)
+/**
+ * fixed_minibuffer_window_resize_apply:
+ *
+ * Assign new window sizes after resizing a fixed minibuffer window W
+ * by DELTA pixels (a fixed minibuffer window is displayed invariably
+ * at the top or bottom of its frame).  No error checking performed.
+  */
+static void
+fixed_minibuffer_window_resize_apply (struct window *w, int delta)
 {
   struct frame *f = XFRAME (w->frame);
-  struct window *r;
-  Lisp_Object root, height;
-  int line_height, pixel_height;
+  Lisp_Object root = FRAME_ROOT_WINDOW (f);
+  struct window *r = XWINDOW (root);

-  eassert (MINI_WINDOW_P (w));
-  eassert (delta >= 0);
+  block_input ();
+  w->pixel_height = w->pixel_height + delta;
+  w->total_lines = w->pixel_height / FRAME_LINE_HEIGHT (f);

-  if (delta > 0)
+  if (MINIBUFFER_WINDOW_TOP_P (f))
     {
-      root = FRAME_ROOT_WINDOW (f);
-      r = XWINDOW (root);
-      height = call3 (Qwindow__resize_root_window_vertically,
-		      root, make_number (- delta), pixelwise ? Qt : Qnil);
-      if (INTEGERP (height) && window_resize_check (r, false))
-	{
-	  block_input ();
-	  window_resize_apply (r, false);
+      /* The new position of the root window is needed for
+	 window_resize_apply.  */
+      r->pixel_top = w->pixel_top + w->pixel_height;
+      r->top_line = w->top_line + w->total_lines;
+    }

-	  if (pixelwise)
-	    {
-	      pixel_height = min (-XINT (height), INT_MAX - w->pixel_height);
-	      line_height = pixel_height / FRAME_LINE_HEIGHT (f);
-	    }
-	  else
-	    {
-	      line_height = min (-XINT (height),
-				 ((INT_MAX - w->pixel_height)
-				  / FRAME_LINE_HEIGHT (f)));
-	      pixel_height = line_height * FRAME_LINE_HEIGHT (f);
-	    }
+  window_resize_apply (r, false);

-	  /* Grow the mini-window.  */
-	  w->pixel_top = r->pixel_top + r->pixel_height;
-	  w->top_line = r->top_line + r->total_lines;
-	  /* Make sure the mini-window has always at least one line.  */
-	  w->pixel_height = max (w->pixel_height + pixel_height,
-				 FRAME_LINE_HEIGHT (f));
-	  w->total_lines = max (w->total_lines + line_height, 1);
-
-	  /* Enforce full redisplay of the frame.  */
-	  /* FIXME: Shouldn't window--resize-root-window-vertically do it?  */
-	  fset_redisplay (f);
-	  adjust_frame_glyphs (f);
-	  unblock_input ();
-	}
-      else
-	error ("Failed to grow minibuffer window");
+  if (MINIBUFFER_WINDOW_BOTTOM_P (f))
+    {
+      /* The position of the root window is needed to assign
+	 the position of a minibuffer window at the bottom of
+	 the frame.  */
+      w->pixel_top = r->pixel_top + r->pixel_height;
+      w->top_line = r->top_line + r->total_lines;
+    }
+
+  /* Enforce full redisplay of the frame.  */
+  /* FIXME: Shouldn't some of the caller do it?  */
+  fset_redisplay (f);
+  adjust_frame_glyphs (f);
+  unblock_input ();
+}
+
+/**
+ * grow_mini_window:
+ *
+ * Grow mini-window W by DELTA pixels.  Return true if the height of W
+ * changed.  If DELTA is negative, this may shrink the minibuffer
+ * window to the minimum height to display one line of text plus an
+ * eventual divider height.
+ */
+bool
+grow_mini_window (struct window *w, int delta)
+{
+  struct frame *f = XFRAME (w->frame);
+  int old_height = WINDOW_PIXEL_HEIGHT (w);
+  int min_height = FRAME_LINE_HEIGHT (f) + WINDOW_BOTTOM_DIVIDER_WIDTH (w);
+
+  eassert (MINI_WINDOW_P (w));
+
+  if (old_height + delta < min_height)
+    /* Never shrink mini-window to less than its minimum
+       height.  */
+    delta = old_height > min_height ? min_height - old_height : 0;
+
+  if (delta == 0)
+    return false;
+
+  FRAME_WINDOWS_FROZEN (f) = true;
+
+  if (MINIBUFFER_WINDOW_FIXED_P (f))
+    /* Minibuffer window fixed at the top or bottom of its frame.  */
+    {
+      Lisp_Object root = FRAME_ROOT_WINDOW (f);
+      struct window *r = XWINDOW (root);
+      Lisp_Object grow = call3 (Qwindow__resize_root_window_vertically,
+				root, make_number (- delta), Qt);
+
+      if (INTEGERP (grow) && window_resize_check (r, false))
+	fixed_minibuffer_window_resize_apply (w, -XINT (grow));
+    }
+  else if (w == XWINDOW (FRAME_ROOT_WINDOW (f)))
+    /* A minibuffer only or echo only frame.  */
+    call2 (Qwindow__resize_minibuffer_frame,
+	   FRAME_ROOT_WINDOW (f), make_number (delta));
+  else
+    /* A loose minibuffer window or a separate echo window.  */
+    {
+      Lisp_Object window;

+      XSETWINDOW (window, w);
+      call2 (Qwindow__resize_mini_window, window, make_number (delta));
     }
+
+  return WINDOW_PIXEL_HEIGHT (w) != old_height;
 }

-/* Shrink mini-window W to one line.  */
-void
-shrink_mini_window (struct window *w, bool pixelwise)
+/**
+ * shrink_mini_window:
+ *
+ * Shrink mini-window W to the minimum height needed to display one
+ * line of text plus an eventual divider height.  Return true if the
+ * height of W changed.
+ */
+bool
+shrink_mini_window (struct window *w)
 {
   struct frame *f = XFRAME (w->frame);
-  struct window *r;
-  Lisp_Object root, delta;
-  EMACS_INT height, unit;
+  int old_height = WINDOW_PIXEL_HEIGHT (w);
+  int delta = (old_height
+	       - FRAME_LINE_HEIGHT (f) - WINDOW_BOTTOM_DIVIDER_WIDTH (w));

   eassert (MINI_WINDOW_P (w));

-  height = pixelwise ? w->pixel_height : w->total_lines;
-  unit = pixelwise ? FRAME_LINE_HEIGHT (f) : 1;
-  if (height > unit)
+  if (delta <= 0)
+    return false;
+
+  FRAME_WINDOWS_FROZEN (f) = false;
+
+  if (MINIBUFFER_WINDOW_FIXED_P (f))
+    /* Minibuffer window fixed at the top or bottom of its frame.  */
     {
-      root = FRAME_ROOT_WINDOW (f);
-      r = XWINDOW (root);
-      delta = call3 (Qwindow__resize_root_window_vertically,
-		     root, make_number (height - unit),
-		     pixelwise ? Qt : Qnil);
-      if (INTEGERP (delta) && window_resize_check (r, false))
-	{
-	  block_input ();
-	  window_resize_apply (r, false);
-
-	  /* Shrink the mini-window.  */
-	  w->top_line = r->top_line + r->total_lines;
-	  w->total_lines = 1;
-	  w->pixel_top = r->pixel_top + r->pixel_height;
-	  w->pixel_height = FRAME_LINE_HEIGHT (f);
-	  /* Enforce full redisplay of the frame.  */
-	  /* FIXME: Shouldn't window--resize-root-window-vertically do it?  */
-	  fset_redisplay (f);
-	  adjust_frame_glyphs (f);
-	  unblock_input ();
-	}
-      /* If the above failed for whatever strange reason we must make a
-	 one window frame here.  The same routine will be needed when
-	 shrinking the frame (and probably when making the initial
-	 *scratch* window).  For the moment leave things as they are.  */
-      else
-	error ("Failed to shrink minibuffer window");
+      Lisp_Object root = FRAME_ROOT_WINDOW (f);
+      struct window *r = XWINDOW (root);
+      Lisp_Object grow = call3 (Qwindow__resize_root_window_vertically,
+				root, make_number (delta), Qt);
+
+      if (INTEGERP (grow) && window_resize_check (r, false))
+	fixed_minibuffer_window_resize_apply (w, -XINT (grow));
     }
+  else if (w == XWINDOW (FRAME_ROOT_WINDOW (f)))
+    /* A minibuffer only or echo only frame.  */
+    call2 (Qwindow__resize_minibuffer_frame,
+	   FRAME_ROOT_WINDOW (f), make_number (- delta));
+  else
+    /* A loose minibuffer window or a separate echo window.  */
+    {
+      Lisp_Object window;
+
+      XSETWINDOW (window, w);
+      call2 (Qwindow__resize_mini_window, window, make_number (- delta));
+    }
+
+  return WINDOW_PIXEL_HEIGHT (w) != old_height;
 }

-DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, Sresize_mini_window_internal, 1, 1, 0,
+DEFUN ("resize-minibuffer-window-internal", Fresize_minibuffer_window_internal,
+       Sresize_minibuffer_window_internal, 1, 1, 0,
        doc: /* Resize minibuffer window WINDOW.  */)
      (Lisp_Object window)
 {
   struct window *w = XWINDOW (window);
   struct window *r;
   struct frame *f;
-  int height;
+  int old_height, delta;

-  CHECK_WINDOW (window);
+  CHECK_LIVE_WINDOW (window);
   f = XFRAME (w->frame);

-  if (!EQ (FRAME_MINIBUF_WINDOW (XFRAME (w->frame)), window))
-    error ("Not a valid minibuffer window");
-  else if (FRAME_MINIBUF_ONLY_P (f))
-    error ("Cannot resize a minibuffer-only frame");
+  if (!MINIBUFFER_WINDOW_FIXED_P (f))
+    error ("Not a fixed minibuffer window");

   r = XWINDOW (FRAME_ROOT_WINDOW (f));
-  height = r->pixel_height + w->pixel_height;
+  old_height = r->pixel_height + w->pixel_height;
+  delta = XINT (w->new_pixel) - w->pixel_height;
   if (window_resize_check (r, false)
       && XINT (w->new_pixel) > 0
-      && height == XINT (r->new_pixel) + XINT (w->new_pixel))
+      && old_height == XINT (r->new_pixel) + XINT (w->new_pixel))
     {
-      block_input ();
-      window_resize_apply (r, false);
+      fixed_minibuffer_window_resize_apply (w, delta);

-      w->pixel_height = XFASTINT (w->new_pixel);
-      w->total_lines = w->pixel_height / FRAME_LINE_HEIGHT (f);
-      w->pixel_top = r->pixel_top + r->pixel_height;
-      w->top_line = r->top_line + r->total_lines;
-
-      fset_redisplay (f);
-      adjust_frame_glyphs (f);
-      unblock_input ();
       return Qt;
     }
   else
-    error ("Failed to resize minibuffer window");
+    error ("Cannot resize minibuffer window");
 }
 \f
 /* Mark window cursors off for all windows in the window tree rooted
@@ -5730,7 +6034,7 @@ SIDE t (or `right') specifies that the new window shall be located on
 {
   Lisp_Object window;

-  if (MINI_WINDOW_P (XWINDOW (selected_window))
+  if (MINIBUFFER_WINDOW_P (XWINDOW (selected_window))
       && !NILP (Vminibuf_scroll_window))
     window = Vminibuf_scroll_window;
   /* If buffer is specified and live, scroll that buffer.  */
@@ -5846,7 +6150,7 @@ SIDE t (or `right') specifies that the new window shall be located on
   (void)
 {
   if (minibuf_level > 0
-      && MINI_WINDOW_P (XWINDOW (selected_window))
+      && MINIBUFFER_WINDOW_P (XWINDOW (selected_window))
       && WINDOW_LIVE_P (minibuf_selected_window))
     return minibuf_selected_window;

@@ -6740,7 +7044,7 @@ struct saved_window
 void
 delete_all_child_windows (Lisp_Object window)
 {
-  register struct window *w;
+  struct window *w;

   w = XWINDOW (window);

@@ -7145,6 +7449,8 @@ Value is a cons of the form (LEFT-WIDTH . RIGHT-WIDTH).
       w->right_fringe_width = right;
       w->fringes_outside_margins = outside;

+      SET_FRAME_GARBAGED (XFRAME (WINDOW_FRAME (w)));
+
       return w;
     }
   else
@@ -7262,6 +7568,9 @@ Value is a list of the form (LEFT-WIDTH RIGHT-WIDTH OUTSIDE-MARGINS).  */)
   wset_horizontal_scroll_bar_type (w, Qnil);
 #endif

+  if (changed)
+    SET_FRAME_GARBAGED (XFRAME (WINDOW_FRAME (w)));
+
   return changed ? w : NULL;
 }

@@ -7536,7 +7845,7 @@ Value is a list of the form (WIDTH COLUMNS VERTICAL-TYPE HEIGHT LINES
   struct frame *f = make_initial_frame ();
   XSETFRAME (selected_frame, f);
   Vterminal_frame = selected_frame;
-  minibuf_window = f->minibuffer_window;
+  minibuf_window = MINIBUFFER_WINDOW (f);
   selected_window = f->selected_window;
 }

@@ -7566,10 +7875,15 @@ Value is a list of the form (WIDTH COLUMNS VERTICAL-TYPE HEIGHT LINES
   DEFSYM (Qwindow__resize_root_window, "window--resize-root-window");
   DEFSYM (Qwindow__resize_root_window_vertically,
 	  "window--resize-root-window-vertically");
+  DEFSYM (Qwindow__resize_mini_window, "window--resize-mini-window");
+  DEFSYM (Qwindow__resize_minibuffer_frame, "window--resize-minibuffer-frame");
+  DEFSYM (Qvertically, "vertically");
   DEFSYM (Qwindow__sanitize_window_sizes, "window--sanitize-window-sizes");
   DEFSYM (Qwindow__pixel_to_total, "window--pixel-to-total");
   DEFSYM (Qsafe, "safe");
   DEFSYM (Qdisplay_buffer, "display-buffer");
+  DEFSYM (Qdisplay_minibuffer, "display-minibuffer");
+  DEFSYM (Qdisplay_echo, "display-echo");
   DEFSYM (Qreplace_buffer_in_windows, "replace-buffer-in-windows");
   DEFSYM (Qrecord_window_buffer, "record-window-buffer");
   DEFSYM (Qget_mru_window, "get-mru-window");
@@ -7770,13 +8084,16 @@ this value for parameters without read syntax (like windows or frames).
   Vfast_but_imprecise_scrolling = false;

   defsubr (&Sselected_window);
-  defsubr (&Sminibuffer_window);
-  defsubr (&Swindow_minibuffer_p);
   defsubr (&Swindowp);
   defsubr (&Swindow_valid_p);
   defsubr (&Swindow_live_p);
   defsubr (&Swindow_frame);
   defsubr (&Sframe_root_window);
+  defsubr (&Sminibuffer_window);
+  defsubr (&Secho_window);
+  defsubr (&Swindow_minibuffer_p);
+  defsubr (&Swindow_echo_p);
+  defsubr (&Swindow_mini_p);
   defsubr (&Sframe_first_window);
   defsubr (&Sframe_selected_window);
   defsubr (&Sset_frame_selected_window);
@@ -7840,7 +8157,7 @@ this value for parameters without read syntax (like windows or frames).
   defsubr (&Sget_buffer_window);
   defsubr (&Sdelete_other_windows_internal);
   defsubr (&Sdelete_window_internal);
-  defsubr (&Sresize_mini_window_internal);
+  defsubr (&Sresize_minibuffer_window_internal);
   defsubr (&Sset_window_buffer);
   defsubr (&Srun_window_configuration_change_hook);
   defsubr (&Srun_window_scroll_functions);
diff --git a/src/window.h b/src/window.h
index 013083e..4433f09 100644
--- a/src/window.h
+++ b/src/window.h
@@ -28,7 +28,8 @@
 Lisp data type is changed to Lisp_Window.  They are garbage
 collected along with the vectors.

-All windows in use are arranged into a tree, with pointers up and down.
+All windows of a frame are arranged into a tree, with pointers up and
+down.

 Windows that are leaves of the tree are actually displayed
 and show the contents of buffers.  Windows that are not leaves
@@ -60,18 +61,18 @@
 or horizontal combination windows.  The children of a horizontal
 combination window may be leaf windows or vertical combination windows.

-At the top of the tree are two windows which have nil as parent.
-The second of these is minibuf_window.  The first one manages all
-the frame area that is not minibuffer, and is called the root window.
-Different windows can be the root at different times;
-initially the root window is a leaf window, but if more windows
-are created then that leaf window ceases to be root and a newly
-made combination window becomes root instead.
-
-In any case, on screens which have an ordinary window and a
-minibuffer, prev of the minibuf window is the root window and next of
-the root window is the minibuf window.  On minibufferless screens or
-minibuffer-only screens, the root window and the minibuffer window are
+For a frame with its own minibuffer window, there are two windows
+which have nil as parent.  The second of these is the minibuffer
+window.  The first one manages all the frame area that is not
+minibuffer and is called the root window.  Different windows can be
+the root at different times; initially the root window is a leaf
+window, but if more windows are created then that leaf window ceases
+to be root and a newly made combination window becomes root instead.
+
+In any case, on frames which have an ordinary window and a minibuffer
+window, prev of the minibuffer window is the root window and next of
+the root window is the minibuffer window.  On minibuffer-less or
+minibuffer-only frames, the root window and the minibuffer window are
 one and the same, so its prev and next members are nil.

 A dead window has its contents field set to nil.  */
@@ -329,7 +330,10 @@ struct window
     int window_end_vpos;

     /* True if this window is a minibuffer window.  */
-    bool_bf mini : 1;
+    bool_bf minibuffer : 1;
+
+    /* True if this window is an echo window.  */
+    bool_bf echo : 1;

     /* Meaningful for internal windows only: true if this window is a
        horizontal combination, false if it is a vertical
@@ -490,16 +494,14 @@ struct window
 }

 /* True if W is a minibuffer window.  */
-#define MINI_WINDOW_P(W) ((W)->mini)
+#define MINIBUFFER_WINDOW_P(W) ((W)->minibuffer)

-/* True if W is a minibuffer window on a frame that contains at least
-   one other window.  */
-#define MINI_NON_ONLY_WINDOW_P(W)	 \
-  (MINI_WINDOW_P (W) && !NILP ((W)->prev))
+/* True if W is an echo window.  */
+#define ECHO_WINDOW_P(W) ((W)->echo)

-/* True if W is a minibuffer window that is alone on its frame.  */
-#define MINI_ONLY_WINDOW_P(W)		 \
-  (MINI_WINDOW_P (W) && NILP ((W)->prev))
+/* True if W is a mini window - a minibuffer or echo window.  */
+#define MINI_WINDOW_P(W)				\
+  (MINIBUFFER_WINDOW_P (W) || ECHO_WINDOW_P (W))

 /* General window layout:

@@ -529,21 +531,24 @@ struct window
 /* A handy macro.  */

 /* Non-nil if window W is leaf window (has a buffer).  */
-#define WINDOW_LEAF_P(W) \
+#define WINDOW_LEAF_P(W)			\
   (BUFFERP ((W)->contents))

 /* Non-nil if window W is internal (is a parent window).  */
-#define WINDOW_INTERNAL_P(W) \
+#define WINDOW_INTERNAL_P(W)			\
   (WINDOWP ((W)->contents))

 /* True if window W is a horizontal combination of windows.  */
-#define WINDOW_HORIZONTAL_COMBINATION_P(W) \
+#define WINDOW_HORIZONTAL_COMBINATION_P(W)	\
   (WINDOW_INTERNAL_P (W) && (W)->horizontal)

 /* True if window W is a vertical combination of windows.  */
-#define WINDOW_VERTICAL_COMBINATION_P(W) \
+#define WINDOW_VERTICAL_COMBINATION_P(W)	\
   (WINDOW_INTERNAL_P (W) && !(W)->horizontal)

+/* Window W's frame.  */
+#define WINDOW_FRAME(W) ((W)->frame)
+
 /* Window W's XFRAME.  */
 #define WINDOW_XFRAME(W) (XFRAME (WINDOW_FRAME ((W))))

@@ -554,14 +559,14 @@ struct window
 #define WINDOW_BUFFER(W)			\
   (WINDOW_LEAF_P(W)				\
    ? (W)->contents				\
-   : Qnil)					\
+   : Qnil)

 /* Return the canonical column width of the frame of window W.  */
-#define WINDOW_FRAME_COLUMN_WIDTH(W) \
+#define WINDOW_FRAME_COLUMN_WIDTH(W)		\
   (FRAME_COLUMN_WIDTH (WINDOW_XFRAME ((W))))

 /* Return the canonical line height of the frame of window W.  */
-#define WINDOW_FRAME_LINE_HEIGHT(W) \
+#define WINDOW_FRAME_LINE_HEIGHT(W)		\
   (FRAME_LINE_HEIGHT (WINDOW_XFRAME ((W))))

 /* Return the pixel width of window W.  This includes dividers, scroll
@@ -588,55 +593,62 @@ struct window
    might crash Emacs.  */
 #define MIN_SAFE_WINDOW_WIDTH (2)

-#define MIN_SAFE_WINDOW_PIXEL_WIDTH(W) \
+#define MIN_SAFE_WINDOW_PIXEL_WIDTH(W)		\
   (2 * WINDOW_FRAME_COLUMN_WIDTH (W))

 #define MIN_SAFE_WINDOW_HEIGHT (1)

-#define MIN_SAFE_WINDOW_PIXEL_HEIGHT(W) \
+#define MIN_SAFE_WINDOW_PIXEL_HEIGHT(W)		\
   (WINDOW_FRAME_LINE_HEIGHT (W))

 /* True if window W has no other windows to its left on its frame.  */
 #define WINDOW_LEFTMOST_P(W)			\
   (WINDOW_LEFT_PIXEL_EDGE (W) == 0)

-/* True if window W has no other windows above it on its frame.  */
-#define WINDOW_TOPMOST_P(W)			\
-  (WINDOW_TOP_PIXEL_EDGE (W) == 0)
+/* True if window W has no other windows above it on its frame (a
+   minibuffer window on the top of W's frame does not count in this
+   regard).  */
+#define WINDOW_TOPMOST_P(W)					\
+  (WINDOW_TOP_PIXEL_EDGE (W)					\
+   <= (WINDOW_TOP_PIXEL_EDGE					\
+       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))

 /* True if window W has no other windows to its right on its frame.  */
 #define WINDOW_RIGHTMOST_P(W)					\
   (WINDOW_RIGHT_PIXEL_EDGE (W)					\
    == (WINDOW_RIGHT_PIXEL_EDGE					\
-       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))	\
+       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))

 /* True if window W has no other windows below it on its frame (the
    minibuffer window is not counted in this respect unless W itself is a
    minibuffer window).  */
 #define WINDOW_BOTTOMMOST_P(W)					\
   (WINDOW_BOTTOM_PIXEL_EDGE (W)					\
-   == (WINDOW_BOTTOM_PIXEL_EDGE					\
-       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))	\
+   >= (WINDOW_BOTTOM_PIXEL_EDGE					\
+       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))

 /* True if window W takes up the full width of its frame.  */
 #define WINDOW_FULL_WIDTH_P(W)					\
   (WINDOW_PIXEL_WIDTH (W)					\
    == (WINDOW_PIXEL_WIDTH					\
-       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))	\
+       (XWINDOW (FRAME_ROOT_WINDOW (WINDOW_XFRAME (W))))))

 /* Width of right divider of window W.  */
 #define WINDOW_RIGHT_DIVIDER_WIDTH(W)				\
-  (WINDOW_RIGHTMOST_P (W)					\
-   ? 0 : FRAME_RIGHT_DIVIDER_WIDTH (WINDOW_XFRAME (W)))
+  ((WINDOW_RIGHTMOST_P (W) || (W)->pseudo_window_p)		\
+   ? 0								\
+   : FRAME_RIGHT_DIVIDER_WIDTH (WINDOW_XFRAME (W)))

 /* Width of bottom divider of window W.  */
 #define WINDOW_BOTTOM_DIVIDER_WIDTH(W)					\
-  (((WINDOW_BOTTOMMOST_P (W)						\
-     && NILP ((XWINDOW (FRAME_ROOT_WINDOW				\
-			(WINDOW_XFRAME (W))))->next))			\
-    || EQ ((W)->prev, FRAME_ROOT_WINDOW (WINDOW_XFRAME (W)))		\
-    || (W)->pseudo_window_p)						\
-   ? 0 : FRAME_BOTTOM_DIVIDER_WIDTH (WINDOW_XFRAME (W)))
+  (((!MINIBUFFER_WINDOW_P (W)						\
+     || MINIBUFFER_WINDOW_TOP_P (WINDOW_XFRAME (W))			\
+     || MINIBUFFER_WINDOW_LOOSE_P (WINDOW_XFRAME (W)))			\
+    && !((W)->pseudo_window_p)						\
+    && (!WINDOW_BOTTOMMOST_P (W)					\
+	|| MINIBUFFER_WINDOW_BOTTOM_P (WINDOW_XFRAME (W))))		\
+   ? FRAME_BOTTOM_DIVIDER_WIDTH (WINDOW_XFRAME (W))			\
+   : 0)

 /* Return the canonical frame column at which window W starts.
    This includes a left-hand scroll bar, if any.  */
@@ -644,7 +656,7 @@ struct window

 /* Return the canonical frame column before which window W ends.
    This includes a right-hand scroll bar, if any.  */
-#define WINDOW_RIGHT_EDGE_COL(W) \
+#define WINDOW_RIGHT_EDGE_COL(W)			\
   (WINDOW_LEFT_EDGE_COL (W) + WINDOW_TOTAL_COLS (W))

 /* Return the canonical frame line at which window W starts.
@@ -653,7 +665,7 @@ struct window

 /* Return the canonical frame line before which window W ends.
    This includes a mode line, if any.  */
-#define WINDOW_BOTTOM_EDGE_LINE(W) \
+#define WINDOW_BOTTOM_EDGE_LINE(W)			\
   (WINDOW_TOP_EDGE_LINE (W) + WINDOW_TOTAL_LINES (W))

 /* Return the left pixel edge at which window W starts.
@@ -662,7 +674,7 @@ struct window

 /* Return the right pixel edge before which window W ends.
    This includes a right-hand scroll bar, if any.  */
-#define WINDOW_RIGHT_PIXEL_EDGE(W) \
+#define WINDOW_RIGHT_PIXEL_EDGE(W)			\
   (WINDOW_LEFT_PIXEL_EDGE (W) + WINDOW_PIXEL_WIDTH (W))

 /* Return the top pixel edge at which window W starts.
@@ -671,25 +683,25 @@ struct window

 /* Return the bottom pixel edge before which window W ends.
    This includes a mode line, if any.  */
-#define WINDOW_BOTTOM_PIXEL_EDGE(W) \
+#define WINDOW_BOTTOM_PIXEL_EDGE(W)			\
   (WINDOW_TOP_PIXEL_EDGE (W) + WINDOW_PIXEL_HEIGHT (W))

 /* Return the frame x-position at which window W starts.
    This includes a left-hand scroll bar, if any.  */
-#define WINDOW_LEFT_EDGE_X(W) \
+#define WINDOW_LEFT_EDGE_X(W)			   \
   (FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W)) \
    + WINDOW_LEFT_PIXEL_EDGE (W))

 /* Return the frame x- position before which window W ends.
    This includes a right-hand scroll bar, if any.  */
-#define WINDOW_RIGHT_EDGE_X(W) \
+#define WINDOW_RIGHT_EDGE_X(W)			   \
   (FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W)) \
    + WINDOW_RIGHT_PIXEL_EDGE (W))

 /* True if W is a menu bar window.  */
 #if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
-#define WINDOW_MENU_BAR_P(W) \
-  (WINDOWP (WINDOW_XFRAME (W)->menu_bar_window) \
+#define WINDOW_MENU_BAR_P(W)					\
+  (WINDOWP (WINDOW_XFRAME (W)->menu_bar_window)			\
    && (W) == XWINDOW (WINDOW_XFRAME (W)->menu_bar_window))
 #else
 /* No menu bar windows if X toolkit is in use.  */
@@ -698,16 +710,16 @@ struct window

 /* True if W is a tool bar window.  */
 #if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS)
-#define WINDOW_TOOL_BAR_P(W) \
-  (WINDOWP (WINDOW_XFRAME (W)->tool_bar_window) \
+#define WINDOW_TOOL_BAR_P(W)					\
+  (WINDOWP (WINDOW_XFRAME (W)->tool_bar_window)			\
    && (W) == XWINDOW (WINDOW_XFRAME (W)->tool_bar_window))
 #else
 #define WINDOW_TOOL_BAR_P(W) false
 #endif

 /* Return the frame y-position at which window W starts.  */
-#define WINDOW_TOP_EDGE_Y(W) \
-  (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \
+#define WINDOW_TOP_EDGE_Y(W)				   \
+  (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W))	   \
     ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \
    + WINDOW_TOP_PIXEL_EDGE (W))

@@ -759,7 +771,7 @@ struct window
 #define WINDOW_LEFT_MARGIN_WIDTH(W)			\
   (W->left_margin_cols * WINDOW_FRAME_COLUMN_WIDTH (W))

-#define WINDOW_RIGHT_MARGIN_WIDTH(W)				\
+#define WINDOW_RIGHT_MARGIN_WIDTH(W)			\
   (W->right_margin_cols * WINDOW_FRAME_COLUMN_WIDTH (W))

 #define WINDOW_MARGINS_WIDTH(W)			\
@@ -777,7 +789,7 @@ struct window
    ? W->right_fringe_width				\
    : FRAME_RIGHT_FRINGE_WIDTH (WINDOW_XFRAME (W)))

-#define WINDOW_FRINGES_WIDTH(W)		\
+#define WINDOW_FRINGES_WIDTH(W)						\
   (WINDOW_LEFT_FRINGE_WIDTH (W) + WINDOW_RIGHT_FRINGE_WIDTH (W))

 /* Are fringes outside display margins in window W.  */
@@ -807,7 +819,7 @@ struct window
   (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (W)		\
    || WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_RIGHT (W))

-#if (defined (HAVE_WINDOW_SYSTEM)					\
+#if (defined (HAVE_WINDOW_SYSTEM)		\
      && ((defined (USE_TOOLKIT_SCROLL_BARS))	\
 	 || defined (HAVE_NTGUI)))
 # define USE_HORIZONTAL_SCROLL_BARS true
@@ -819,7 +831,7 @@ struct window
    W.  Horizontal scrollbars exist for toolkit versions only.  */
 #if USE_HORIZONTAL_SCROLL_BARS
 #define WINDOW_HAS_HORIZONTAL_SCROLL_BAR(W)			\
-  ((WINDOW_PSEUDO_P (W) || MINI_NON_ONLY_WINDOW_P (W))		\
+  ((WINDOW_PSEUDO_P (W) || MINI_WINDOW_P (W))			\
    ? false							\
    : EQ (W->horizontal_scroll_bar_type, Qt)			\
    ? FRAME_HAS_HORIZONTAL_SCROLL_BARS (WINDOW_XFRAME (W))	\
@@ -865,27 +877,27 @@ struct window
    : 0)

 /* Width of a scroll bar in window W, measured in columns.  */
-#define WINDOW_SCROLL_BAR_COLS(W)	       \
-  (WINDOW_HAS_VERTICAL_SCROLL_BAR (W)	       \
-   ? WINDOW_CONFIG_SCROLL_BAR_COLS (W)	       \
+#define WINDOW_SCROLL_BAR_COLS(W)      \
+  (WINDOW_HAS_VERTICAL_SCROLL_BAR (W)  \
+   ? WINDOW_CONFIG_SCROLL_BAR_COLS (W) \
    : 0)

 /* Width of a left scroll bar area in window W, measured in pixels.  */
-#define WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH(W)				\
-  (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (W)				\
-   ? WINDOW_CONFIG_SCROLL_BAR_WIDTH (W)					\
+#define WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH(W)  \
+  (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (W) \
+   ? WINDOW_CONFIG_SCROLL_BAR_WIDTH (W)	      \
    : 0)

 /* Width of a right scroll bar area in window W, measured in pixels.  */
-#define WINDOW_RIGHT_SCROLL_BAR_AREA_WIDTH(W)				\
-  (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_RIGHT (W)				\
-   ? WINDOW_CONFIG_SCROLL_BAR_WIDTH (W)					\
+#define WINDOW_RIGHT_SCROLL_BAR_AREA_WIDTH(W)  \
+  (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_RIGHT (W) \
+   ? WINDOW_CONFIG_SCROLL_BAR_WIDTH (W)	       \
    : 0)

 /* Width of scroll bar area in window W, measured in pixels.  */
-#define WINDOW_SCROLL_BAR_AREA_WIDTH(W)					\
-  (WINDOW_HAS_VERTICAL_SCROLL_BAR (W)					\
-   ? WINDOW_CONFIG_SCROLL_BAR_WIDTH (W)					\
+#define WINDOW_SCROLL_BAR_AREA_WIDTH(W) \
+  (WINDOW_HAS_VERTICAL_SCROLL_BAR (W)	\
+   ? WINDOW_CONFIG_SCROLL_BAR_WIDTH (W) \
    : 0)

 /* Return the frame position where the vertical scroll bar of window W
@@ -995,36 +1007,36 @@ struct window
    buffer is selected by the top level editing loop at the end of each command.

    This value is always the same as FRAME_SELECTED_WINDOW (selected_frame).  */
-
 extern Lisp_Object selected_window;

 /* This is a time stamp for window selection, so we can find the least
    recently used window.  Its only users are Fselect_window,
    init_window_once, and make_frame.  */
-
 extern EMACS_INT window_select_count;

-/* The minibuffer window of the selected frame.
-   Note that you cannot test for minibufferness of an arbitrary window
-   by comparing against this; use the MINI_WINDOW_P macro instead.  */
-
+/* The minibuffer window of the selected frame.  Note that you cannot
+   test for minibufferness of an arbitrary window by comparing against
+   this; use the MINIBUFFER_WINDOW_P macro instead.  */
 extern Lisp_Object minibuf_window;

 /* Non-nil means it is the window whose mode line should be
    shown as the selected window when the minibuffer is selected.  */
-
 extern Lisp_Object minibuf_selected_window;

 extern Lisp_Object make_window (void);
+extern void delete_deletable_window (Lisp_Object);
 extern Lisp_Object window_from_coordinates (struct frame *, int, int,
                                             enum window_part *, bool);
 extern void resize_frame_windows (struct frame *, int, bool, bool);
 extern void restore_window_configuration (Lisp_Object);
 extern void delete_all_child_windows (Lisp_Object);
-extern void grow_mini_window (struct window *, int, bool);
-extern void shrink_mini_window (struct window *, bool);
+extern bool grow_mini_window (struct window *, int);
+extern bool shrink_mini_window (struct window *);
 extern int window_relative_x_coord (struct window *, enum window_part, int);

+extern Lisp_Object minibuffer_window (struct frame *f);
+extern Lisp_Object echo_window (struct frame *f);
+
 void run_window_size_change_functions (Lisp_Object);

 /* Make WINDOW display BUFFER.  RUN_HOOKS_P means it's allowed
diff --git a/src/xdisp.c b/src/xdisp.c
index 316c12e..fb2a24c 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -519,7 +519,7 @@

 /* The buffers referenced from echo_area_buffer.  */

-static Lisp_Object echo_buffer[2];
+Lisp_Object echo_buffer[2];

 /* A vector saved used in with_area_buffer to reduce consing.  */

@@ -2740,7 +2740,7 @@ static Lisp_Object calc_line_height_property (struct it *, Lisp_Object,
 CHECK_WINDOW_END (struct window *w)
 {
 #if defined GLYPH_DEBUG && defined ENABLE_CHECKING
-  if (!MINI_WINDOW_P (w) && w->window_end_valid)
+  if (!MINIBUFFER_WINDOW_P (w) && w->window_end_valid)
     {
       struct glyph_row *row;
       eassert ((row = MATRIX_ROW (w->current_matrix, w->window_end_vpos),
@@ -10618,10 +10618,9 @@ struct overlay_entry
      toss it.  */
   else if (INTERACTIVE && sf->glyphs_initialized_p)
     {
-      /* Get the frame containing the mini-buffer
-	 that the selected frame is using.  */
-      Lisp_Object mini_window = FRAME_MINIBUF_WINDOW (sf);
-      Lisp_Object frame = XWINDOW (mini_window)->frame;
+      /* Get the frame containing the mini-buffer that the selected
+	 frame is using.  */
+      Lisp_Object frame = WINDOW_FRAME (XWINDOW (echo_window (sf)));
       struct frame *f = XFRAME (frame);

       if (FRAME_VISIBLE_P (sf) && !FRAME_VISIBLE_P (f))
@@ -10689,13 +10688,8 @@ struct overlay_entry
       /* The frame whose minibuffer we're going to display the message on.
 	 It may be larger than the selected frame, so we need
 	 to use its buffer, not the selected frame's buffer.  */
-      Lisp_Object mini_window;
-      struct frame *f, *sf = SELECTED_FRAME ();
-
-      /* Get the frame containing the minibuffer
-	 that the selected frame is using.  */
-      mini_window = FRAME_MINIBUF_WINDOW (sf);
-      f = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
+      struct frame *sf = SELECTED_FRAME ();
+      struct frame *f = WINDOW_XFRAME (XWINDOW (echo_window (sf)));

       /* Error messages get reported properly by cmd_error, so this must be
 	 just an informative message; if the frame hasn't really been
@@ -10756,13 +10750,11 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
       /* The frame whose mini-buffer we're going to display the message
 	 on.  It may be larger than the selected frame, so we need to
 	 use its buffer, not the selected frame's buffer.  */
-      Lisp_Object mini_window;
       struct frame *f, *sf = SELECTED_FRAME ();

       /* Get the frame containing the mini-buffer
 	 that the selected frame is using.  */
-      mini_window = FRAME_MINIBUF_WINDOW (sf);
-      f = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
+      f = WINDOW_XFRAME (XWINDOW (echo_window (sf)));

       /* Error messages get reported properly by cmd_error, so this must be
 	 just an informative message; if the frame hasn't really been
@@ -11011,15 +11003,15 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
       buffer = AREF (vector, 4);

       wset_buffer (w, buffer);
-      set_marker_both (w->pointm, buffer,
-		       XFASTINT (AREF (vector, 5)),
-		       XFASTINT (AREF (vector, 6)));
-      set_marker_both (w->old_pointm, buffer,
-		       XFASTINT (AREF (vector, 7)),
-		       XFASTINT (AREF (vector, 8)));
-      set_marker_both (w->start, buffer,
-		       XFASTINT (AREF (vector, 9)),
-		       XFASTINT (AREF (vector, 10)));
+      set_marker_restricted_both (w->pointm, buffer,
+				  XFASTINT (AREF (vector, 5)),
+				  XFASTINT (AREF (vector, 6)));
+      set_marker_restricted_both (w->old_pointm, buffer,
+				  XFASTINT (AREF (vector, 7)),
+				  XFASTINT (AREF (vector, 8)));
+      set_marker_restricted_both (w->start, buffer,
+				  XFASTINT (AREF (vector, 9)),
+				  XFASTINT (AREF (vector, 10)));
     }

   Vwith_echo_area_save_vector = vector;
@@ -11078,9 +11070,8 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
       if (minibuffer_auto_raise)
 	{
 	  struct frame *sf = SELECTED_FRAME ();
-	  Lisp_Object mini_window;
-	  mini_window = FRAME_MINIBUF_WINDOW (sf);
-	  Fraise_frame  (WINDOW_FRAME (XWINDOW (mini_window)));
+
+	  Fraise_frame (WINDOW_FRAME (XWINDOW (echo_window (sf))));
 	}

       message_log_maybe_newline ();
@@ -11187,21 +11178,33 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
    is active, don't shrink it.  */

 void
-resize_echo_area_exactly (void)
+resize_mini_windows_exactly (void)
 {
-  if (BUFFERP (echo_area_buffer[0])
-      && WINDOWP (echo_area_window))
+  bool resized_p;
+
+  if (BUFFERP (echo_area_buffer[0]) && WINDOW_LIVE_P (echo_area_window))
     {
       struct window *w = XWINDOW (echo_area_window);
-      Lisp_Object resize_exactly = (minibuf_level == 0 ? Qt : Qnil);
-      bool resized_p = with_echo_area_buffer (w, 0, resize_mini_window_1,
-					      (intptr_t) w, resize_exactly);
-      if (resized_p)
-	{
-	  windows_or_buffers_changed = 42;
-	  update_mode_lines = 30;
-	  redisplay_internal ();
-	}
+      Lisp_Object resize_exactly = ((minibuf_level == 0
+				     || !EQ (echo_area_window, minibuf_window))
+				    ? Qt : Qnil);
+
+      resized_p = with_echo_area_buffer (w, 0, resize_mini_window_1,
+					 (intptr_t) w, resize_exactly);
+    }
+
+  if (WINDOW_LIVE_P (minibuf_window) && !EQ (echo_area_window, minibuf_window))
+    {
+      struct window *w = XWINDOW (minibuf_window);
+
+      resized_p = resize_mini_window (w, minibuf_level == 0) || resized_p;
+    }
+
+  if (resized_p)
+    {
+      windows_or_buffers_changed = 42;
+      update_mode_lines = 30;
+      redisplay_internal ();
     }
 }

@@ -11258,16 +11261,49 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
       || (FRAME_X_P (f) && FRAME_X_OUTPUT (f) == NULL))
     return false;

-  if (!FRAME_MINIBUF_ONLY_P (f))
+  if (!MINIBUFFER_WINDOW_ONLY_P (f) || Vfit_minibuffer_only_frames)
     {
       struct it it;
-      int total_height = (WINDOW_PIXEL_HEIGHT (XWINDOW (FRAME_ROOT_WINDOW (f)))
-			  + WINDOW_PIXEL_HEIGHT (w));
+      int old_height = WINDOW_PIXEL_HEIGHT (w);
       int unit = FRAME_LINE_HEIGHT (f);
       int height, max_height;
       struct text_pos start;
       struct buffer *old_current_buffer = NULL;

+      if (Vfit_minibuffer_only_frames
+	  && w == XWINDOW (FRAME_ROOT_WINDOW (f)))
+	{
+	  /* if the minibuffer window is the root window of its frame
+	     and we fit minibuffer-only frames, we accept only integer
+	     values for Vmax_mini_window_height and use 4 as fallback
+	     otherwise.  */
+	  if (INTEGERP (Vmax_mini_window_height))
+	    {
+	      max_height = XINT (Vmax_mini_window_height) * unit;
+	      if (max_height < unit)
+		max_height = unit;
+	    }
+	  else
+	    /* 4 is arbitrary.  */
+	    max_height = 4 * unit;
+	}
+      else
+	{
+	  int windows_height = FRAME_WINDOWS_HEIGHT (f);
+
+	  /* Compute the max. number of lines specified by the user.  */
+	  if (FLOATP (Vmax_mini_window_height))
+	    max_height = XFLOAT_DATA (Vmax_mini_window_height) * windows_height;
+	  else if (INTEGERP (Vmax_mini_window_height))
+	    max_height = XINT (Vmax_mini_window_height) * unit;
+	  else
+	    max_height = windows_height / 4;
+
+	  /* Correct that max. height if it's bogus.  */
+	  max_height = clip_to_bounds (unit, max_height, windows_height);
+	}
+
+      /* Find out the height of the text in the window.  */
       if (current_buffer != XBUFFER (w->contents))
 	{
 	  old_current_buffer = current_buffer;
@@ -11275,19 +11311,6 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
 	}

       init_iterator (&it, w, BEGV, BEGV_BYTE, NULL, DEFAULT_FACE_ID);
-
-      /* Compute the max. number of lines specified by the user.  */
-      if (FLOATP (Vmax_mini_window_height))
-	max_height = XFLOAT_DATA (Vmax_mini_window_height) * total_height;
-      else if (INTEGERP (Vmax_mini_window_height))
-	max_height = XINT (Vmax_mini_window_height) * unit;
-      else
-	max_height = total_height / 4;
-
-      /* Correct that max. height if it's bogus.  */
-      max_height = clip_to_bounds (unit, max_height, total_height);
-
-      /* Find out the height of the text in the window.  */
       if (it.line_wrap == TRUNCATE)
 	height = unit;
       else
@@ -11301,6 +11324,16 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
 	  height -= min (it.extra_line_spacing, it.max_extra_line_spacing);
 	}

+/**       if (MINIBUFFER_WINDOW_TOP_P (f) **/
+/** 	  || (MINIBUFFER_WINDOW_P (w) && MINIBUFFER_WINDOW_LOOSE_P (f) **/
+/** 	      && !WINDOW_BOTTOMMOST_P (w)) **/
+/** 	  || (ECHO_WINDOW_P (w) && !WINDOW_BOTTOMMOST_P (w))) **/
+
+      if (ECHO_WINDOW_P (w) && !MINIBUFFER_WINDOW_P (w))
+	height += WINDOW_BOTTOM_DIVIDER_WIDTH (w);
+      else
+	height += WINDOW_BOTTOM_DIVIDER_WIDTH (w);
+
       /* Compute a suitable window start.  */
       if (height > max_height)
 	{
@@ -11317,51 +11350,16 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
 	{
 	  /* Let it grow only, until we display an empty message, in which
 	     case the window shrinks again.  */
-	  if (height > WINDOW_PIXEL_HEIGHT (w))
-	    {
-	      int old_height = WINDOW_PIXEL_HEIGHT (w);
-
-	      FRAME_WINDOWS_FROZEN (f) = true;
-	      grow_mini_window (w, height - WINDOW_PIXEL_HEIGHT (w), true);
-	      window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height;
-	    }
-	  else if (height < WINDOW_PIXEL_HEIGHT (w)
-		   && (exact_p || BEGV == ZV))
-	    {
-	      int old_height = WINDOW_PIXEL_HEIGHT (w);
-
-	      FRAME_WINDOWS_FROZEN (f) = false;
-	      shrink_mini_window (w, true);
-	      window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height;
-	    }
-	}
-      else
-	{
-	  /* Always resize to exact size needed.  */
-	  if (height > WINDOW_PIXEL_HEIGHT (w))
-	    {
-	      int old_height = WINDOW_PIXEL_HEIGHT (w);
-
-	      FRAME_WINDOWS_FROZEN (f) = true;
-	      grow_mini_window (w, height - WINDOW_PIXEL_HEIGHT (w), true);
-	      window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height;
-	    }
-	  else if (height < WINDOW_PIXEL_HEIGHT (w))
-	    {
-	      int old_height = WINDOW_PIXEL_HEIGHT (w);
-
-	      FRAME_WINDOWS_FROZEN (f) = false;
-	      shrink_mini_window (w, true);
-
-	      if (height)
-		{
-		  FRAME_WINDOWS_FROZEN (f) = true;
-		  grow_mini_window (w, height - WINDOW_PIXEL_HEIGHT (w), true);
-		}
-
-	      window_height_changed_p = WINDOW_PIXEL_HEIGHT (w) != old_height;
-	    }
+	  if (height > old_height)
+	    window_height_changed_p
+	      = grow_mini_window (w, height - old_height);
+	  else if (height < old_height && (exact_p || BEGV == ZV))
+	    window_height_changed_p = shrink_mini_window (w);
 	}
+      else if (height != old_height)
+	/* Always resize to exact size needed.  */
+	window_height_changed_p
+	  = grow_mini_window (w, height - old_height);

       if (old_current_buffer)
 	set_buffer_internal (old_current_buffer);
@@ -11619,16 +11617,19 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
 static void
 echo_area_display (bool update_frame_p)
 {
+  struct frame *sf = SELECTED_FRAME ();
   Lisp_Object mini_window;
   struct window *w;
   struct frame *f;
   bool window_height_changed_p = false;
-  struct frame *sf = SELECTED_FRAME ();

-  mini_window = FRAME_MINIBUF_WINDOW (sf);
-  if (NILP (mini_window))
+  /* When Emacs starts, selected_frame may be the initial terminal
+     frame.  If we let this through, a message would be displayed on
+     the terminal.  */
+  if (FRAME_INITIAL_P (sf))
     return;

+  mini_window = echo_window (sf);
   w = XWINDOW (mini_window);
   f = XFRAME (WINDOW_FRAME (w));

@@ -11636,14 +11637,6 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
   if (!FRAME_VISIBLE_P (f) || !f->glyphs_initialized_p)
     return;

-#ifdef HAVE_WINDOW_SYSTEM
-  /* When Emacs starts, selected_frame may be the initial terminal
-     frame.  If we let this through, a message would be displayed on
-     the terminal.  */
-  if (FRAME_INITIAL_P (XFRAME (selected_frame)))
-    return;
-#endif /* HAVE_WINDOW_SYSTEM */
-
   /* Redraw garbaged frames.  */
   clear_garbaged_frames ();

@@ -11771,7 +11764,7 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
 	return false;
       else if (EQ (window, selected_window))
 	return false;
-      else if (MINI_WINDOW_P (XWINDOW (selected_window))
+      else if (MINIBUFFER_WINDOW_P (XWINDOW (selected_window))
 	       && EQ (window, Vminibuf_scroll_window))
 	/* This special window can't be frozen too.  */
 	return false;
@@ -11980,7 +11973,7 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
   struct frame *f = XFRAME (frame);

   if ((FRAME_WINDOW_P (f)
-       || FRAME_MINIBUF_ONLY_P (f)
+       || MINIBUFFER_WINDOW_ONLY_P (f)
        || f->explicit_name)
       && !FRAME_TOOLTIP_P (f))
     {
@@ -11998,7 +11991,7 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0)

 	  if (tf != f
 	      && FRAME_KBOARD (tf) == FRAME_KBOARD (f)
-	      && !FRAME_MINIBUF_ONLY_P (tf)
+	      && !MINIBUFFER_WINDOW_ONLY_P (tf)
 	      && !FRAME_PARENT_FRAME (tf)
 	      && !FRAME_TOOLTIP_P (tf)
 	      && (FRAME_VISIBLE_P (tf) || FRAME_ICONIFIED_P (tf)))
@@ -13911,7 +13904,7 @@ static void debug_method_add (struct window *, char const *, ...)
   bool consider_all_windows_p;

   /* True means redisplay has to redisplay the miniwindow.  */
-  bool update_miniwindow_p = false;
+  bool update_echo_window_p = false;

   TRACE ((stderr, "redisplay_internal %d\n", redisplaying_p));

@@ -14071,7 +14064,7 @@ static void debug_method_add (struct window *, char const *, ...)
 	  && minibuf_level == 0
 	  /* If the mini-window is currently selected, this means the
 	     echo-area doesn't show through.  */
-	  && !MINI_WINDOW_P (XWINDOW (selected_window))))
+	  && !MINIBUFFER_WINDOW_P (XWINDOW (selected_window))))
     {
       echo_area_display (false);

@@ -14090,7 +14083,7 @@ static void debug_method_add (struct window *, char const *, ...)
 	}

       if (message_cleared_p)
-	update_miniwindow_p = true;
+	update_echo_window_p = true;

       must_finish = true;

@@ -14101,7 +14094,8 @@ static void debug_method_add (struct window *, char const *, ...)
       if (!display_last_displayed_message_p)
 	message_cleared_p = false;
     }
-  else if (EQ (selected_window, minibuf_window)
+  else if ((EQ (selected_window, minibuf_window)
+	    || EQ (selected_window, echo_area_window))
 	   && (current_buffer->clip_changed || window_outdated (w))
 	   && resize_mini_window (w, false))
     {
@@ -14184,7 +14178,7 @@ static void debug_method_add (struct window *, char const *, ...)
 	      || FETCH_BYTE (BYTEPOS (tlbufpos)) == '\n'))
 	/* Former continuation line has disappeared by becoming empty.  */
 	goto cancel;
-      else if (window_outdated (w) || MINI_WINDOW_P (w))
+      else if (window_outdated (w) || MINIBUFFER_WINDOW_P (w))
 	{
 	  /* We have to handle the case of continuation around a
 	     wide-column character (see the comment in indent.c around
@@ -14513,10 +14507,11 @@ static void debug_method_add (struct window *, char const *, ...)
       internal_condition_case_1 (redisplay_window_1, selected_window,
 				 list_of_error,
 				 redisplay_window_error);
-      if (update_miniwindow_p)
-	internal_condition_case_1 (redisplay_window_1,
-				   FRAME_MINIBUF_WINDOW (sf), list_of_error,
-				   redisplay_window_error);
+      if (update_echo_window_p)
+	  if (WINDOW_LIVE_P (ECHO_WINDOW (sf)))
+	    internal_condition_case_1 (redisplay_window_1,
+				       ECHO_WINDOW (sf), list_of_error,
+				       redisplay_window_error);

       /* Compare desired and current matrices, perform output.  */

@@ -14569,20 +14564,24 @@ static void debug_method_add (struct window *, char const *, ...)
 	 have put text on a frame other than the selected one, so the
 	 above call to update_frame would not have caught it.  Catch
 	 it here.  */
-      Lisp_Object mini_window = FRAME_MINIBUF_WINDOW (sf);
-      struct frame *mini_frame = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
+      Lisp_Object mini_window = MINIBUFFER_WINDOW (sf);

-      if (mini_frame != sf && FRAME_WINDOW_P (mini_frame))
+      if (WINDOW_LIVE_P (mini_window))
 	{
-	  XWINDOW (mini_window)->must_be_updated_p = true;
-	  pending |= update_frame (mini_frame, false, false);
-	  mini_frame->cursor_type_changed = false;
-          if (!pending && hscroll_retries <= MAX_HSCROLL_RETRIES
-              && hscroll_windows (mini_window))
-            {
-              hscroll_retries++;
-              goto retry;
-            }
+	  struct frame *mini_frame = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
+
+	  if (mini_frame != sf && FRAME_WINDOW_P (mini_frame))
+	    {
+	      XWINDOW (mini_window)->must_be_updated_p = true;
+	      pending |= update_frame (mini_frame, false, false);
+	      mini_frame->cursor_type_changed = false;
+	      if (!pending && hscroll_retries <= MAX_HSCROLL_RETRIES
+		  && hscroll_windows (mini_window))
+		{
+		  hscroll_retries++;
+		  goto retry;
+		}
+	    }
 	}
     }

@@ -16490,8 +16489,9 @@ enum
      visible region.

      Note that mini-buffers sometimes aren't displaying any text.  */
-  if (!MINI_WINDOW_P (w)
-      || (w == XWINDOW (minibuf_window)
+  if (!MINIBUFFER_WINDOW_P (w)
+      || (WINDOW_LIVE_P (minibuf_window)
+	  && w == XWINDOW (minibuf_window)
 	  && NILP (echo_area_buffer[0])))
     {
       struct buffer *buf = XBUFFER (w->contents);
@@ -16521,9 +16521,7 @@ enum
 {
   int start, end, whole, portion;

-  if (!MINI_WINDOW_P (w)
-      || (w == XWINDOW (minibuf_window)
-	  && NILP (echo_area_buffer[0])))
+  if (!MINI_WINDOW_P (w))
     {
       struct buffer *b = XBUFFER (w->contents);
       struct buffer *old_buffer = NULL;
@@ -16697,7 +16695,8 @@ enum

   if (MINI_WINDOW_P (w))
     {
-      if (w == XWINDOW (echo_area_window)
+      if (WINDOW_LIVE_P (echo_area_window)
+	  && w == XWINDOW (echo_area_window)
 	  && !NILP (echo_area_buffer[0]))
 	{
 	  if (update_mode_line)
@@ -16708,7 +16707,8 @@ enum
 	    /* We've already displayed the echo area glyphs in this window.  */
 	    goto finish_scroll_bars;
 	}
-      else if ((w != XWINDOW (minibuf_window)
+      else if (((WINDOW_LIVE_P (minibuf_window)
+		 && w != XWINDOW (minibuf_window))
 		|| minibuf_level == 0)
 	       /* When buffer is nonempty, redisplay window normally.  */
 	       && BUF_Z (XBUFFER (w->contents)) == BUF_BEG (XBUFFER (w->contents))
@@ -17132,7 +17132,7 @@ enum
 	 the mouse, resulting in an unwanted mouse-movement rather
 	 than a simple mouse-click.  */
       if (!w->start_at_line_beg
-	  && NILP (do_mouse_tracking)
+	  && NILP (track_mouse)
       	  && CHARPOS (startp) > BEGV
 	  && CHARPOS (startp) > BEG + beg_unchanged
 	  && CHARPOS (startp) <= Z - end_unchanged
@@ -17642,17 +17642,12 @@ enum
       block_input ();
       if (draw_window_fringes (w, true))
 	{
-	  if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
-	    x_draw_right_divider (w);
-	  else
+	  if (!WINDOW_RIGHT_DIVIDER_WIDTH (w))
 	    x_draw_vertical_border (w);
 	}
       unblock_input ();
       update_end (f);
     }
-
-  if (WINDOW_BOTTOM_DIVIDER_WIDTH (w))
-    x_draw_bottom_divider (w);
 #endif /* HAVE_WINDOW_SYSTEM */

   /* We go to this label, with fonts_changed set, if it is
@@ -17661,9 +17656,10 @@ enum
      because the loop in redisplay_internal expects that.  */
  need_larger_matrices:
   ;
+
  finish_scroll_bars:

-   if (WINDOW_HAS_VERTICAL_SCROLL_BAR (w) || WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w))
+  if (WINDOW_HAS_VERTICAL_SCROLL_BAR (w) || WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w))
     {
       if (WINDOW_HAS_VERTICAL_SCROLL_BAR (w))
 	/* Set the thumb's position and size.  */
@@ -17679,6 +17675,13 @@ enum
         (*FRAME_TERMINAL (f)->redeem_scroll_bar_hook) (w);
     }

+#ifdef HAVE_WINDOW_SYSTEM
+ if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
+   x_draw_right_divider (w);
+ if (WINDOW_BOTTOM_DIVIDER_WIDTH (w))
+   x_draw_bottom_divider (w);
+#endif /* HAVE_WINDOW_SYSTEM */
+
   /* Restore current_buffer and value of point in it.  The window
      update may have changed the buffer, so first make sure `opoint'
      is still valid (Bug#6177).  */
@@ -17743,8 +17746,7 @@ enum
   ptrdiff_t it_charpos = IT_CHARPOS (it);

   /* Don't let the cursor end in the scroll margins.  */
-  if ((flags & TRY_WINDOW_CHECK_MARGINS)
-      && !MINI_WINDOW_P (w))
+  if ((flags & TRY_WINDOW_CHECK_MARGINS) && !MINI_WINDOW_P (w))
     {
       int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
       start_display (&it, w, pos);
@@ -21538,7 +21540,7 @@ struct glyph_row *
 		row->displays_text_p = false;

 	      if (!NILP (BVAR (XBUFFER (it->w->contents), indicate_empty_lines))
-		  && (!MINI_WINDOW_P (it->w)))
+		  && !MINI_WINDOW_P (it->w))
 		row->indicate_empty_line_p = true;
 	    }

@@ -29193,11 +29195,19 @@ struct font *
   bool non_selected = false;

   *active_cursor = true;
+  non_selected = false;
+
+  /* Echo window.  */
+  if (ECHO_WINDOW_P (w) && !MINIBUFFER_WINDOW_P (w))
+    {
+      *active_cursor = false;

-  /* Echo area */
-  if (cursor_in_echo_area
-      && FRAME_HAS_MINIBUF_P (f)
-      && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window))
+      return NO_CURSOR;
+    }
+  /* Echo area.  */
+  else if (cursor_in_echo_area
+      && MINIBUFFER_WINDOW_LOCAL_P (f)
+      && EQ (MINIBUFFER_WINDOW (f), echo_area_window))
     {
       if (w == XWINDOW (echo_area_window))
 	{
@@ -29220,7 +29230,7 @@ struct font *
     {
       *active_cursor = false;

-      if (MINI_WINDOW_P (w) && minibuf_level == 0)
+      if ((MINIBUFFER_WINDOW_P (w) && minibuf_level == 0) || ECHO_WINDOW_P (w))
 	return NO_CURSOR;

       non_selected = true;
@@ -29935,7 +29945,7 @@ struct font *

 #ifdef HAVE_WINDOW_SYSTEM
   /* Change the mouse cursor.  */
-  if (FRAME_WINDOW_P (f) && NILP (do_mouse_tracking))
+  if (FRAME_WINDOW_P (f) && NILP (track_mouse))
     {
 #if ! defined (USE_GTK) && ! defined (HAVE_NS)
       if (draw == DRAW_NORMAL_TEXT
@@ -30882,7 +30892,7 @@ A polygon is a cons (poly . [x0 y0 x1 y1 ...]) where each pair in the
     return;

   /* Do not change cursor shape while dragging mouse.  */
-  if (EQ (do_mouse_tracking, Qdragging))
+  if (EQ (track_mouse, Qdragging) || EQ (track_mouse, Qdropping))
     return;

   if (!NILP (pointer))
@@ -31381,7 +31391,10 @@ A polygon is a cons (poly . [x0 y0 x1 y1 ...]) where each pair in the
       goto set_cursor;
     }
   else if (part == ON_BOTTOM_DIVIDER)
-    if (! WINDOW_BOTTOMMOST_P (w)
+    if ((!WINDOW_BOTTOMMOST_P (w)
+	 && (!MINIBUFFER_WINDOW_P (w)
+	     || minibuf_level
+	     || NILP (Vresize_mini_windows)))
 	|| minibuf_level
 	|| NILP (Vresize_mini_windows))
       {
@@ -32077,9 +32090,7 @@ A polygon is a cons (poly . [x0 y0 x1 y1 ...]) where each pair in the
 {
   struct frame *f = WINDOW_XFRAME (w);

-  if (w->mini || w->pseudo_window_p)
-    return;
-  else if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
+  if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
     {
       int x0 = WINDOW_RIGHT_EDGE_X (w) - WINDOW_RIGHT_DIVIDER_WIDTH (w);
       int x1 = WINDOW_RIGHT_EDGE_X (w);
@@ -32103,9 +32114,7 @@ A polygon is a cons (poly . [x0 y0 x1 y1 ...]) where each pair in the
 {
   struct frame *f = XFRAME (WINDOW_FRAME (w));

-  if (w->mini || w->pseudo_window_p)
-    return;
-  else if (WINDOW_BOTTOM_DIVIDER_WIDTH (w))
+  if (WINDOW_BOTTOM_DIVIDER_WIDTH (w))
     {
       int x0 = WINDOW_LEFT_EDGE_X (w);
       int x1 = WINDOW_RIGHT_EDGE_X (w);
@@ -32593,6 +32602,7 @@ A polygon is a cons (poly . [x0 y0 x1 y1 ...]) where each pair in the
   /* also Qtext */

   DEFSYM (Qdragging, "dragging");
+  DEFSYM (Qdropping, "dropping");

   DEFSYM (Qinhibit_free_realized_faces, "inhibit-free-realized-faces");

@@ -32950,8 +32960,9 @@ make sure that (1) your window manager has focus follow the mouse and

   DEFVAR_LISP ("max-mini-window-height", Vmax_mini_window_height,
     doc: /* Maximum height for resizing mini-windows (the minibuffer and the echo area).
-If a float, it specifies a fraction of the mini-window frame's height.
-If an integer, it specifies a number of lines.  */);
+If a float, it specifies a fraction of the inner height of the
+mini-window's frame.  If an integer, it specifies a number of
+lines.  */);
   Vmax_mini_window_height = make_float (0.25);

   DEFVAR_LISP ("resize-mini-windows", Vresize_mini_windows,
@@ -32968,6 +32979,10 @@ make sure that (1) your window manager has focus follow the mouse and
      during loadup.  */
   Vresize_mini_windows = Qnil;

+  DEFVAR_BOOL ("fit-minibuffer-only-frames", Vfit_minibuffer_only_frames,
+    doc: /* Non-nil means fit minibuffer-only frames to their buffers.  */);
+  Vfit_minibuffer_only_frames = false;
+
   DEFVAR_LISP ("blink-cursor-alist", Vblink_cursor_alist,
     doc: /* Alist specifying how to blink the cursor off.
 Each element has the form (ON-STATE . OFF-STATE).  Whenever the
@@ -33014,7 +33029,7 @@ Each element has the form (ON-STATE . OFF-STATE).  Whenever the
   Vhscroll_step = make_number (0);

   DEFVAR_BOOL ("message-truncate-lines", message_truncate_lines,
-    doc: /* If non-nil, messages are truncated instead of resizing the echo area.
+    doc: /* If non-nil, truncate messages instead of resizing the echo window.
 Bind this around calls to `message' to let it take effect.  */);
   message_truncate_lines = false;

diff --git a/src/xfns.c b/src/xfns.c
index 66e49df..dae526e 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1528,7 +1528,7 @@ struct mouse_cursor_data {
      most of the commands try to apply themselves to the minibuffer
      frame itself, and get an error because you can't switch buffers
      in or split the minibuffer window.  */
-  if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f))
+  if (MINIBUFFER_WINDOW_ONLY_P (f) || FRAME_PARENT_FRAME (f))
     return;

   if (TYPE_RANGED_INTEGERP (int, value))
@@ -1614,7 +1614,7 @@ struct mouse_cursor_data {
   int nlines;

   /* Treat tool bars like menu bars.  */
-  if (FRAME_MINIBUF_ONLY_P (f))
+  if (MINIBUFFER_WINDOW_ONLY_P (f))
     return;

   /* Use VALUE only if an int >= 0.  */
@@ -3629,10 +3629,24 @@ struct mouse_cursor_data {
       f = make_minibuffer_frame ();
       minibuffer_only = true;
     }
-  else if (WINDOWP (tem))
+  else if (WINDOWP (tem) || EQ (tem, Qloose))
     f = make_frame_without_minibuffer (tem, kb, display);
+  else if (EQ (tem, Qtop))
+    f = make_frame (Qtop);
   else
-    f = make_frame (true);
+    f = make_frame (Qbottom);
+
+  XSETFRAME (frame, f);
+
+  if (EQ (tem, Qloose))
+    {
+      /* Process 'echo-area' parameter here to make sure it gets
+	 applied when an echo window is needed.  */
+      tem = x_get_arg (dpyinfo, parms, Qecho_area, "echo-area", "Echo-area",
+		       RES_TYPE_SYMBOL);
+      if (!NILP (tem))
+	store_frame_param (f, Qecho_area, tem);
+    }

   parent_frame = x_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL,
 			    RES_TYPE_SYMBOL);
@@ -3664,8 +3678,6 @@ struct mouse_cursor_data {
   FRAME_OVERRIDE_REDIRECT (f) = override_redirect;
   store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil);

-  XSETFRAME (frame, f);
-
   f->terminal = dpyinfo->terminal;

   f->output_method = output_x_window;
@@ -4051,7 +4063,7 @@ struct mouse_cursor_data {

   /* Initialize `default-minibuffer-frame' in case this is the first
      frame on this terminal.  */
-  if (FRAME_HAS_MINIBUF_P (f)
+  if (MINIBUFFER_WINDOW_LOCAL_P (f)
       && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
           || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
     kset_default_minibuffer_frame (kb, frame);
@@ -6156,7 +6168,7 @@ static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object,
     error ("Invalid frame name--not a string or nil");

   frame = Qnil;
-  f = make_frame (false);
+  f = make_frame (Qnil);
   f->wants_modeline = false;
   XSETFRAME (frame, f);
   record_unwind_protect (unwind_create_tip_frame, frame);
@@ -6550,9 +6562,9 @@ static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object,
  * Hide currently visible tooltip and cancel its timer.
  *
  * If GTK+ system tooltips are used, this will try to hide the tooltip
- * referenced by the x_output structure of tooltip_last_frame.  For
- * Emacs tooltips this will try to make tooltip_frame invisible (if
- * DELETE is false) or delete tooltip_frame (if DELETE is true).
+ * referenced by the x_output structure of tip_last_frame.  For Emacs
+ * tooltips this will try to make tip_frame invisible (if DELETE is
+ * false) or delete tip_frame (if DELETE is true).
  *
  * Return Qt if the tooltip was either deleted or made invisible, Qnil
  * otherwise.
diff --git a/src/xmenu.c b/src/xmenu.c
index a99e8ab..96bdf67 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -2269,11 +2269,11 @@ struct pop_down_menu
       uly = dispheight - height;
     }
 #ifndef HAVE_X_WINDOWS
-  if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
+  if (MINIBUFFER_WINDOW_LOCAL_P (f) && uly+height > dispheight - 1)
     {
       /* Move the menu away of the echo area, to avoid overwriting the
 	 menu with help echo messages or vice versa.  */
-      if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
+      if (BUFFERP (echo_area_buffer[0]) && WINDOW_LIVE_P (echo_area_window))
 	{
 	  y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
 	  uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
diff --git a/src/xterm.c b/src/xterm.c
index af28dab..86a5214 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -5046,20 +5046,15 @@ struct frame *
       /* Figure out which root window we're on.  */
       XQueryPointer (FRAME_X_DISPLAY (*fp),
 		     DefaultRootWindow (FRAME_X_DISPLAY (*fp)),
-
 		     /* The root window which contains the pointer.  */
 		     &root,
-
 		     /* Trash which we can't trust if the pointer is on
 			a different screen.  */
 		     &dummy_window,
-
 		     /* The position on that root window.  */
 		     &root_x, &root_y,
-
 		     /* More trash we can't trust.  */
 		     &dummy, &dummy,
-
 		     /* Modifier keys and pointer buttons, about which
 			we don't care.  */
 		     (unsigned int *) &dummy);
@@ -5082,21 +5077,17 @@ struct frame *

 	x_catch_errors (FRAME_X_DISPLAY (*fp));

-	if (x_mouse_grabbed (dpyinfo))
+	if (x_mouse_grabbed (dpyinfo) && !EQ (track_mouse, Qdropping))
 	  {
 	    /* If mouse was grabbed on a frame, give coords for that frame
 	       even if the mouse is now outside it.  */
 	    XTranslateCoordinates (FRAME_X_DISPLAY (*fp),
-
 				   /* From-window.  */
 				   root,
-
 				   /* To-window.  */
 				   FRAME_X_WINDOW (dpyinfo->last_mouse_frame),
-
 				   /* From-position, to-position.  */
 				   root_x, root_y, &win_x, &win_y,
-
 				   /* Child of win.  */
 				   &child);
 	    f1 = dpyinfo->last_mouse_frame;
@@ -5106,16 +5097,12 @@ struct frame *
 	    while (true)
 	      {
 		XTranslateCoordinates (FRAME_X_DISPLAY (*fp),
-
 				       /* From-window, to-window.  */
 				       root, win,
-
 				       /* From-position, to-position.  */
 				       root_x, root_y, &win_x, &win_y,
-
 				       /* Child of win.  */
 				       &child);
-
 		if (child == None || child == win)
 		  {
 #ifdef USE_GTK
@@ -5148,16 +5135,16 @@ struct frame *
 	      win = first_win;
 #endif

-	    /* Now we know that:
-	       win is the innermost window containing the pointer
-	       (XTC says it has no child containing the pointer),
-	       win_x and win_y are the pointer's position in it
-	       (XTC did this the last time through), and
-	       parent_x and parent_y are the pointer's position in win's parent.
-	       (They are what win_x and win_y were when win was child.
-	       If win is the root window, it has no parent, and
-	       parent_{x,y} are invalid, but that's okay, because we'll
-	       never use them in that case.)  */
+	    /* Now we know that: win is the innermost window
+	       containing the pointer (XTC says it has no child
+	       containing the pointer), win_x and win_y are the
+	       pointer's position in it (XTC did this the last time
+	       through), and parent_x and parent_y are the pointer's
+	       position in win's parent.  (They are what win_x and
+	       win_y were when win was child.  If win is the root
+	       window, it has no parent, and parent_{x,y} are invalid,
+	       but that's okay, because we'll never use them in that
+	       case.)  */

 #ifdef USE_GTK
 	    /* We don't wan't to know the innermost window.  We
@@ -5178,13 +5165,39 @@ struct frame *
 #endif /* USE_X_TOOLKIT */
 	  }

+	if ((!f1 || FRAME_TOOLTIP_P (f1))
+	    && EQ (track_mouse, Qdropping)
+	    && x_mouse_grabbed (dpyinfo))
+	  {
+	    /* When dropping then if we didn't get a frame or only a
+	       tooltip frame and the mouse was grabbed on a frame,
+	       give coords for that frame even if the mouse is now
+	       outside it.  */
+	    XTranslateCoordinates (FRAME_X_DISPLAY (*fp),
+
+				   /* From-window.  */
+				   root,
+
+				   /* To-window.  */
+				   FRAME_X_WINDOW (dpyinfo->last_mouse_frame),
+
+				   /* From-position, to-position.  */
+				   root_x, root_y, &win_x, &win_y,
+
+				   /* Child of win.  */
+				   &child);
+	    f1 = dpyinfo->last_mouse_frame;
+	  }
+	else if (f1 && FRAME_TOOLTIP_P (f1))
+	  f1 = NULL;
+
 	if (x_had_errors_p (FRAME_X_DISPLAY (*fp)))
-	  f1 = 0;
+	  f1 = NULL;

 	x_uncatch_errors_after_check ();

 	/* If not, is it one of our scroll bars?  */
-	if (! f1)
+	if (!f1)
 	  {
 	    struct scroll_bar *bar;

@@ -5198,7 +5211,7 @@ struct frame *
 	      }
 	  }

-	if (f1 == 0 && insist > 0)
+	if (!f1 && insist > 0)
 	  f1 = SELECTED_FRAME ();

 	if (f1)
@@ -7667,6 +7680,42 @@ static void xembed_send_message (struct frame *f, Time,
   unblock_input ();
 }

+/**
+ * mouse_or_wdesc_frame:
+ *
+ * When not dropping and the mouse was grabbed for DPYINFO, return the
+ * frame where the mouse was seen last.  If there's no such frame,
+ * return the frame according to WDESC.
+ *
+ * When dropping, return the frame according to WDESC.  If there's no
+ * such frame and the mouse was grabbed for DPYINFO, return the frame
+ * where the mouse was seen last.
+ *
+ * In either case, never return a tooltip frame.
+ */
+static struct frame *
+mouse_or_wdesc_frame (struct x_display_info *dpyinfo, int wdesc)
+{
+  struct frame *lm_f = (x_mouse_grabbed (dpyinfo)
+			? dpyinfo->last_mouse_frame
+			: NULL);
+
+  if (lm_f && !EQ (track_mouse, Qdropping))
+    return lm_f;
+  else
+    {
+      struct frame *w_f = x_window_to_frame (dpyinfo, wdesc);
+
+      /* Do not return a tooltip frame.  */
+      if (!w_f || FRAME_TOOLTIP_P (w_f))
+	return EQ (track_mouse, Qdropping) ? lm_f : NULL;
+      else
+	/* When dropping it would be probably nice to raise w_f
+	   here.  */
+	return w_f;
+    }
+}
+
 /* Handles the XEvent EVENT on display DPYINFO.

    *FINISH is X_EVENT_GOTO_OUT if caller should stop reading events.
@@ -8596,16 +8645,14 @@ static void xembed_send_message (struct frame *f, Time,
         x_display_set_last_user_time (dpyinfo, event->xmotion.time);
         previous_help_echo_string = help_echo_string;
         help_echo_string = Qnil;
-
-	f = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame
-	     : x_window_to_frame (dpyinfo, event->xmotion.window));
-
         if (hlinfo->mouse_face_hidden)
           {
             hlinfo->mouse_face_hidden = false;
             clear_mouse_face (hlinfo);
           }

+	f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window);
+
 #ifdef USE_GTK
         if (f && xg_event_is_for_scrollbar (f, event))
           f = 0;
@@ -8622,7 +8669,7 @@ static void xembed_send_message (struct frame *f, Time,
 		   minibuffer gets unselected unexpectedly, and where
 		   you then have to move your mouse all the way down to
 		   the minibuffer to select it.  */
-		&& !MINI_WINDOW_P (XWINDOW (selected_window))
+		&& !MINIBUFFER_WINDOW_P (XWINDOW (selected_window))
 		/* With `focus-follows-mouse' non-nil create an event
 		   also when the target window is on another frame.  */
 		&& (f == XFRAME (selected_frame)
@@ -8844,33 +8891,27 @@ static void xembed_send_message (struct frame *f, Time,
 	dpyinfo->last_mouse_glyph_frame = NULL;
 	x_display_set_last_user_time (dpyinfo, event->xbutton.time);

-	if (x_mouse_grabbed (dpyinfo))
-	  f = dpyinfo->last_mouse_frame;
-	else
+	f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window);
+	if (f && event->xbutton.type == ButtonPress
+	    && !popup_activated ()
+	    && !x_window_to_scroll_bar (event->xbutton.display,
+					event->xbutton.window, 2)
+	    && !FRAME_NO_ACCEPT_FOCUS (f))
 	  {
-	    f = x_window_to_frame (dpyinfo, event->xbutton.window);
+	    /* When clicking into a child frame or when clicking
+	       into a parent frame with the child frame selected and
+	       `no-accept-focus' is not set, select the clicked
+	       frame.  */
+	    struct frame *hf = dpyinfo->x_highlight_frame;

-	    if (f && event->xbutton.type == ButtonPress
-		&& !popup_activated ()
-		&& !x_window_to_scroll_bar (event->xbutton.display,
-					    event->xbutton.window, 2)
-		&& !FRAME_NO_ACCEPT_FOCUS (f))
+	    if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
 	      {
-		/* When clicking into a child frame or when clicking
-		   into a parent frame with the child frame selected and
-		   `no-accept-focus' is not set, select the clicked
-		   frame.  */
-		struct frame *hf = dpyinfo->x_highlight_frame;
-
-		if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
-		  {
-		    block_input ();
-		    XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-				    RevertToParent, CurrentTime);
-		    if (FRAME_PARENT_FRAME (f))
-		      XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
-		    unblock_input ();
-		  }
+		block_input ();
+		XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+				RevertToParent, CurrentTime);
+		if (FRAME_PARENT_FRAME (f))
+		  XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
+		unblock_input ();
 	      }
 	  }

@@ -9786,18 +9827,22 @@ struct x_error_message_stack {
     }
   if (ioerror) dpyinfo->display = 0;

-  /* First delete frames whose mini-buffers are on frames
-     that are on the dead display.  */
+  /* First delete frames whose minibuffer window is on another frame
+     on the dead display.  */
   FOR_EACH_FRAME (tail, frame)
     {
-      Lisp_Object minibuf_frame;
-      minibuf_frame
-	= WINDOW_FRAME (XWINDOW (FRAME_MINIBUF_WINDOW (XFRAME (frame))));
-      if (FRAME_X_P (XFRAME (frame))
-	  && FRAME_X_P (XFRAME (minibuf_frame))
-	  && ! EQ (frame, minibuf_frame)
-	  && FRAME_DISPLAY_INFO (XFRAME (minibuf_frame)) == dpyinfo)
-	delete_frame (frame, Qnoelisp);
+      struct frame *f = XFRAME (frame);
+
+      if (FRAME_X_P (f) && WINDOW_LIVE_P (MINIBUFFER_WINDOW (f)))
+	{
+	  Lisp_Object minibuf_frame
+	    = WINDOW_FRAME (XWINDOW (MINIBUFFER_WINDOW (f)));
+
+	  if (FRAME_X_P (XFRAME (minibuf_frame))
+	      && !EQ (frame, minibuf_frame)
+	      && FRAME_DISPLAY_INFO (XFRAME (minibuf_frame)) == dpyinfo)
+	    delete_frame (frame, Qnoelisp);
+	}
     }

   /* Now delete all remaining frames on the dead display.

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01  5:29                   ` Eli Zaretskii
@ 2018-08-01 12:05                     ` Andy Moreton
  2019-05-22  0:52                       ` Noam Postavsky
  0 siblings, 1 reply; 27+ messages in thread
From: Andy Moreton @ 2018-08-01 12:05 UTC (permalink / raw)
  To: 32257

On Wed 01 Aug 2018, Eli Zaretskii wrote:

>> From: Andy Moreton <andrewjmoreton@gmail.com>
>> Date: Tue, 31 Jul 2018 20:29:31 +0100
>> 
>> Noam's original patch may bring some possibility of new problems,
>> but it does at least fix the original problem (on emacs-26 and
>> master).
>
> Fixing a problem and introducing another doesn't sound wise to me.
> We've been there several times during the last years, and I don't want
> to make the same mistake again.  Sorry.

Perhaps reverting the patches that introduced read-multiple-choice
is the right answer then.

    AndyM






^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01  8:44                     ` martin rudalics
@ 2018-08-01 12:30                       ` Noam Postavsky
  2018-08-01 12:57                       ` Robert Pluim
  2018-08-01 20:55                       ` Live System User
  2 siblings, 0 replies; 27+ messages in thread
From: Noam Postavsky @ 2018-08-01 12:30 UTC (permalink / raw)
  To: martin rudalics; +Cc: Andy Moreton, 32257

martin rudalics <rudalics@gmx.at> writes:

>> Yes please - prefeably in a new bug report for the enhancement patch,
>> so it can be tracked separately from this bug.
>
> It's not yet ready for broader discussion.  I attach it here so you
> can just test whether making the echo interference go away during
> the minibuffer dialog.

By the way, there is the existing Bug#19718 about the minibuffer
interference during read_filtered_event.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01  8:44                     ` martin rudalics
  2018-08-01 12:30                       ` Noam Postavsky
@ 2018-08-01 12:57                       ` Robert Pluim
  2018-08-02  7:09                         ` martin rudalics
  2018-08-01 20:55                       ` Live System User
  2 siblings, 1 reply; 27+ messages in thread
From: Robert Pluim @ 2018-08-01 12:57 UTC (permalink / raw)
  To: martin rudalics; +Cc: Andy Moreton, 32257

martin rudalics <rudalics@gmx.at> writes:

>>> Here I use separate echo (area) and minibuffer windows.  If you want
>>> to play around with a patch please tell me.
>>>
>>> martin
>>
>> Yes please - prefeably in a new bug report for the enhancement patch,
>> so it can be tracked separately from this bug.
>
> It's not yet ready for broader discussion.  I attach it here so you
> can just test whether making the echo interference go away during
> the minibuffer dialog.
>
> To put it into use run emacs with
>
> (setq default-frame-alist '((minibuffer . loose) (echo-area . t)))
>
> Looks better with window dividers enabled.
>
> Eventually I'll make a remote branch.  But this may take its time.

It looks interesting, but please use something other than 'loose' to
describe the behaviour. I guess 'dedicated' is already
overloaded. 'separate', 'no-echo'....?

Robert





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01  8:44                     ` martin rudalics
  2018-08-01 12:30                       ` Noam Postavsky
  2018-08-01 12:57                       ` Robert Pluim
@ 2018-08-01 20:55                       ` Live System User
  2018-08-02  7:09                         ` martin rudalics
  2 siblings, 1 reply; 27+ messages in thread
From: Live System User @ 2018-08-01 20:55 UTC (permalink / raw)
  To: 32257

[-- Attachment #1: Type: text/plain, Size: 606 bytes --]

martin rudalics <rudalics@gmx.at> writes:

>>> Here I use separate echo (area) and minibuffer windows.  If you want
>>> to play around with a patch please tell me.
>>>
>>> martin
>>
>> Yes please - prefeably in a new bug report for the enhancement patch,
>> so it can be tracked separately from this bug.
>
> It's not yet ready for broader discussion.  I attach it here so you
> can just test whether making the echo interference go away during
> the minibuffer dialog.

  I know this is not ready but I thought I'd report this error
  when using this patch with popwin.el (a third-party app).

  Thanks.


[-- Attachment #2: echomini error --]
[-- Type: text/plain, Size: 18808 bytes --]

Debugger entered--Lisp error: (wrong-number-of-arguments nil 1)
  signal(wrong-number-of-arguments (nil 1))
  (if (= (length --cl-rest--) 2) (car-safe (prog1 --cl-rest-- (setq --cl-rest-- (cdr --cl-rest--)))) (signal 'wrong-number-of-arguments (list nil (length --cl-rest--))))
  (let* ((--cl-rest-- (window-tree)) (root (if (= (length --cl-rest--) 2) (car-safe (prog1 --cl-rest-- (setq --cl-rest-- (cdr --cl-rest--)))) (signal 'wrong-number-of-arguments (list nil (length --cl-rest--))))) (mini (car --cl-rest--))) (list (popwin:window-config-tree-1 root) mini))
  popwin:window-config-tree()
  (car (popwin:window-config-tree))
  (let ((win-outline (car (popwin:window-config-tree)))) (let* ((--cl-rest-- (let ((size (if ... width height)) (adjust popwin:adjust-other-windows)) (popwin:create-popup-window size position adjust))) (master-win (if (= (length --cl-rest--) 3) (car-safe (prog1 --cl-rest-- (setq --cl-rest-- ...))) (signal 'wrong-number-of-arguments (list nil (length --cl-rest--))))) (popup-win (car-safe (prog1 --cl-rest-- (setq --cl-rest-- (cdr --cl-rest--))))) (win-map (car --cl-rest--))) (setq popwin:popup-window popup-win popwin:master-window master-win popwin:window-outline win-outline popwin:window-map win-map popwin:window-config nil popwin:selected-window (selected-window))) (popwin:update-window-reference 'popwin:context-stack :recursive t) (popwin:start-close-popup-window-timer))
  (if context (progn (popwin:use-context context) (setq popwin:context-stack context-stack)) (let ((win-outline (car (popwin:window-config-tree)))) (let* ((--cl-rest-- (let ((size ...) (adjust popwin:adjust-other-windows)) (popwin:create-popup-window size position adjust))) (master-win (if (= (length --cl-rest--) 3) (car-safe (prog1 --cl-rest-- ...)) (signal 'wrong-number-of-arguments (list nil ...)))) (popup-win (car-safe (prog1 --cl-rest-- (setq --cl-rest-- ...)))) (win-map (car --cl-rest--))) (setq popwin:popup-window popup-win popwin:master-window master-win popwin:window-outline win-outline popwin:window-map win-map popwin:window-config nil popwin:selected-window (selected-window))) (popwin:update-window-reference 'popwin:context-stack :recursive t) (popwin:start-close-popup-window-timer)) (let ((save-selected-window--state (internal--before-with-selected-window popwin:popup-window))) (save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) 'norecord) (popwin:switch-to-buffer buffer) (if tail (progn (set-window-point popwin:popup-window ...) (recenter -2)))) (internal--after-with-selected-window save-selected-window--state)))) (setq popwin:popup-buffer buffer popwin:popup-last-config (list buffer :width width :height height :position position :noselect noselect :dedicated dedicated :stick stick :tail tail) popwin:popup-window-dedicated-p dedicated popwin:popup-window-stuck-p stick))
  (let* ((--cl-var-- (popwin:find-context-for-buffer buffer :valid-only t)) (context (nth 0 --cl-var--)) (context-stack (nth 1 --cl-var--))) (if context (progn (popwin:use-context context) (setq popwin:context-stack context-stack)) (let ((win-outline (car (popwin:window-config-tree)))) (let* ((--cl-rest-- (let (... ...) (popwin:create-popup-window size position adjust))) (master-win (if (= ... 3) (car-safe ...) (signal ... ...))) (popup-win (car-safe (prog1 --cl-rest-- ...))) (win-map (car --cl-rest--))) (setq popwin:popup-window popup-win popwin:master-window master-win popwin:window-outline win-outline popwin:window-map win-map popwin:window-config nil popwin:selected-window (selected-window))) (popwin:update-window-reference 'popwin:context-stack :recursive t) (popwin:start-close-popup-window-timer)) (let ((save-selected-window--state (internal--before-with-selected-window popwin:popup-window))) (save-current-buffer (unwind-protect (progn (select-window (car save-selected-window--state) 'norecord) (popwin:switch-to-buffer buffer) (if tail (progn ... ...))) (internal--after-with-selected-window save-selected-window--state)))) (setq popwin:popup-buffer buffer popwin:popup-last-config (list buffer :width width :height height :position position :noselect noselect :dedicated dedicated :stick stick :tail tail) popwin:popup-window-dedicated-p dedicated popwin:popup-window-stuck-p stick)))
  (progn (setq buffer (get-buffer buffer)) (popwin:push-context) (run-hooks 'popwin:before-popup-hook) (let* ((--cl-var-- (popwin:find-context-for-buffer buffer :valid-only t)) (context (nth 0 --cl-var--)) (context-stack (nth 1 --cl-var--))) (if context (progn (popwin:use-context context) (setq popwin:context-stack context-stack)) (let ((win-outline (car (popwin:window-config-tree)))) (let* ((--cl-rest-- (let ... ...)) (master-win (if ... ... ...)) (popup-win (car-safe ...)) (win-map (car --cl-rest--))) (setq popwin:popup-window popup-win popwin:master-window master-win popwin:window-outline win-outline popwin:window-map win-map popwin:window-config nil popwin:selected-window (selected-window))) (popwin:update-window-reference 'popwin:context-stack :recursive t) (popwin:start-close-popup-window-timer)) (let ((save-selected-window--state (internal--before-with-selected-window popwin:popup-window))) (save-current-buffer (unwind-protect (progn (select-window ... ...) (popwin:switch-to-buffer buffer) (if tail ...)) (internal--after-with-selected-window save-selected-window--state)))) (setq popwin:popup-buffer buffer popwin:popup-last-config (list buffer :width width :height height :position position :noselect noselect :dedicated dedicated :stick stick :tail tail) popwin:popup-window-dedicated-p dedicated popwin:popup-window-stuck-p stick))) (if noselect (setq popwin:focus-window popwin:selected-window) (setq popwin:focus-window popwin:popup-window) (select-window popwin:popup-window)) (run-hooks 'popwin:after-popup-hook) popwin:popup-window)
  (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '(:width :height :position :noselect :dedicated :stick :tail :allow-other-keys)) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:width :height :position :noselect :dedicated :stick :tail)" (car --cl-keys--)))))) (progn (setq buffer (get-buffer buffer)) (popwin:push-context) (run-hooks 'popwin:before-popup-hook) (let* ((--cl-var-- (popwin:find-context-for-buffer buffer :valid-only t)) (context (nth 0 --cl-var--)) (context-stack (nth 1 --cl-var--))) (if context (progn (popwin:use-context context) (setq popwin:context-stack context-stack)) (let ((win-outline (car ...))) (let* ((--cl-rest-- ...) (master-win ...) (popup-win ...) (win-map ...)) (setq popwin:popup-window popup-win popwin:master-window master-win popwin:window-outline win-outline popwin:window-map win-map popwin:window-config nil popwin:selected-window (selected-window))) (popwin:update-window-reference 'popwin:context-stack :recursive t) (popwin:start-close-popup-window-timer)) (let ((save-selected-window--state (internal--before-with-selected-window popwin:popup-window))) (save-current-buffer (unwind-protect (progn ... ... ...) (internal--after-with-selected-window save-selected-window--state)))) (setq popwin:popup-buffer buffer popwin:popup-last-config (list buffer :width width :height height :position position :noselect noselect :dedicated dedicated :stick stick :tail tail) popwin:popup-window-dedicated-p dedicated popwin:popup-window-stuck-p stick))) (if noselect (setq popwin:focus-window popwin:selected-window) (setq popwin:focus-window popwin:popup-window) (select-window popwin:popup-window)) (run-hooks 'popwin:after-popup-hook) popwin:popup-window))
  (let* ((width (car (cdr (or (plist-member --cl-rest-- ':width) (list nil popwin:popup-window-width))))) (height (car (cdr (or (plist-member --cl-rest-- ':height) (list nil popwin:popup-window-height))))) (position (car (cdr (or (plist-member --cl-rest-- ':position) (list nil popwin:popup-window-position))))) (noselect (car (cdr (plist-member --cl-rest-- ':noselect)))) (dedicated (car (cdr (plist-member --cl-rest-- ':dedicated)))) (stick (car (cdr (plist-member --cl-rest-- ':stick)))) (tail (car (cdr (plist-member --cl-rest-- ':tail))))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '...) (setq --cl-keys-- (cdr ...))) ((car (cdr ...)) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:width :height :position :noselect :dedicated :stick :tail)" (car --cl-keys--)))))) (progn (setq buffer (get-buffer buffer)) (popwin:push-context) (run-hooks 'popwin:before-popup-hook) (let* ((--cl-var-- (popwin:find-context-for-buffer buffer :valid-only t)) (context (nth 0 --cl-var--)) (context-stack (nth 1 --cl-var--))) (if context (progn (popwin:use-context context) (setq popwin:context-stack context-stack)) (let ((win-outline ...)) (let* (... ... ... ...) (setq popwin:popup-window popup-win popwin:master-window master-win popwin:window-outline win-outline popwin:window-map win-map popwin:window-config nil popwin:selected-window ...)) (popwin:update-window-reference 'popwin:context-stack :recursive t) (popwin:start-close-popup-window-timer)) (let ((save-selected-window--state ...)) (save-current-buffer (unwind-protect ... ...))) (setq popwin:popup-buffer buffer popwin:popup-last-config (list buffer :width width :height height :position position :noselect noselect :dedicated dedicated :stick stick :tail tail) popwin:popup-window-dedicated-p dedicated popwin:popup-window-stuck-p stick))) (if noselect (setq popwin:focus-window popwin:selected-window) (setq popwin:focus-window popwin:popup-window) (select-window popwin:popup-window)) (run-hooks 'popwin:after-popup-hook) popwin:popup-window)))
  popwin:popup-buffer(#<buffer *Help*> :width 30 :height 15 :position bottom :noselect nil :dedicated nil :stick nil :tail nil)
  (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '(:regexp :width :height :position :noselect :dedicated :stick :tail :allow-other-keys)) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:regexp :width :height :position :noselect :dedicated :stick :tail)" (car --cl-keys--)))))) (popwin:popup-buffer buffer :width (or width popwin:popup-window-width) :height (or height popwin:popup-window-height) :position (or position popwin:popup-window-position) :noselect (or (popwin:minibuffer-window-selected-p) noselect) :dedicated dedicated :stick stick :tail tail))
  (let* ((--cl-rest-- (append (cdr pattern-and-keywords) default-config-keywords)) (regexp (car (cdr (plist-member --cl-rest-- ':regexp)))) (width (car (cdr (plist-member --cl-rest-- ':width)))) (height (car (cdr (plist-member --cl-rest-- ':height)))) (position (car (cdr (plist-member --cl-rest-- ':position)))) (noselect (car (cdr (plist-member --cl-rest-- ':noselect)))) (dedicated (car (cdr (plist-member --cl-rest-- ':dedicated)))) (stick (car (cdr (plist-member --cl-rest-- ':stick)))) (tail (car (cdr (plist-member --cl-rest-- ':tail))))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '...) (setq --cl-keys-- (cdr ...))) ((car (cdr ...)) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:regexp :width :height :position :noselect :dedicated :stick :tail)" (car --cl-keys--)))))) (popwin:popup-buffer buffer :width (or width popwin:popup-window-width) :height (or height popwin:popup-window-height) :position (or position popwin:popup-window-position) :noselect (or (popwin:minibuffer-window-selected-p) noselect) :dedicated dedicated :stick stick :tail tail)))
  (let* ((buffer (popwin:get-buffer buffer-or-name if-buffer-not-found)) (pattern-and-keywords (popwin:match-config buffer))) (if pattern-and-keywords nil (if if-config-not-found (throw '--cl-block-popwin:display-buffer-1-- (funcall if-config-not-found buffer)) (setq pattern-and-keywords '(t)))) (let* ((--cl-rest-- (append (cdr pattern-and-keywords) default-config-keywords)) (regexp (car (cdr (plist-member --cl-rest-- ':regexp)))) (width (car (cdr (plist-member --cl-rest-- ':width)))) (height (car (cdr (plist-member --cl-rest-- ':height)))) (position (car (cdr (plist-member --cl-rest-- ':position)))) (noselect (car (cdr (plist-member --cl-rest-- ':noselect)))) (dedicated (car (cdr (plist-member --cl-rest-- ':dedicated)))) (stick (car (cdr (plist-member --cl-rest-- ':stick)))) (tail (car (cdr (plist-member --cl-rest-- ':tail))))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq ... ...) (setq --cl-keys-- ...)) ((car ...) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:regexp :width :height :position :noselect :dedicated :stick :tail)" ...))))) (popwin:popup-buffer buffer :width (or width popwin:popup-window-width) :height (or height popwin:popup-window-height) :position (or position popwin:popup-window-position) :noselect (or (popwin:minibuffer-window-selected-p) noselect) :dedicated dedicated :stick stick :tail tail))))
  (catch '--cl-block-popwin:display-buffer-1-- (let* ((buffer (popwin:get-buffer buffer-or-name if-buffer-not-found)) (pattern-and-keywords (popwin:match-config buffer))) (if pattern-and-keywords nil (if if-config-not-found (throw '--cl-block-popwin:display-buffer-1-- (funcall if-config-not-found buffer)) (setq pattern-and-keywords '(t)))) (let* ((--cl-rest-- (append (cdr pattern-and-keywords) default-config-keywords)) (regexp (car (cdr (plist-member --cl-rest-- ...)))) (width (car (cdr (plist-member --cl-rest-- ...)))) (height (car (cdr (plist-member --cl-rest-- ...)))) (position (car (cdr (plist-member --cl-rest-- ...)))) (noselect (car (cdr (plist-member --cl-rest-- ...)))) (dedicated (car (cdr (plist-member --cl-rest-- ...)))) (stick (car (cdr (plist-member --cl-rest-- ...)))) (tail (car (cdr (plist-member --cl-rest-- ...))))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond (... ...) (... ...) (t ...)))) (popwin:popup-buffer buffer :width (or width popwin:popup-window-width) :height (or height popwin:popup-window-height) :position (or position popwin:popup-window-position) :noselect (or (popwin:minibuffer-window-selected-p) noselect) :dedicated dedicated :stick stick :tail tail)))))
  (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '(:default-config-keywords :if-buffer-not-found :if-config-not-found :allow-other-keys)) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:default-config-keywords :if-buffer-not-found :if-config-not-found)" (car --cl-keys--)))))) (catch '--cl-block-popwin:display-buffer-1-- (let* ((buffer (popwin:get-buffer buffer-or-name if-buffer-not-found)) (pattern-and-keywords (popwin:match-config buffer))) (if pattern-and-keywords nil (if if-config-not-found (throw '--cl-block-popwin:display-buffer-1-- (funcall if-config-not-found buffer)) (setq pattern-and-keywords '(t)))) (let* ((--cl-rest-- (append (cdr pattern-and-keywords) default-config-keywords)) (regexp (car (cdr ...))) (width (car (cdr ...))) (height (car (cdr ...))) (position (car (cdr ...))) (noselect (car (cdr ...))) (dedicated (car (cdr ...))) (stick (car (cdr ...))) (tail (car (cdr ...)))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ... ... ...))) (popwin:popup-buffer buffer :width (or width popwin:popup-window-width) :height (or height popwin:popup-window-height) :position (or position popwin:popup-window-position) :noselect (or (popwin:minibuffer-window-selected-p) noselect) :dedicated dedicated :stick stick :tail tail))))))
  (let* ((default-config-keywords (car (cdr (plist-member --cl-rest-- ':default-config-keywords)))) (if-buffer-not-found (car (cdr (or (plist-member --cl-rest-- ':if-buffer-not-found) (list nil :create))))) (if-config-not-found (car (cdr (plist-member --cl-rest-- ':if-config-not-found))))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '...) (setq --cl-keys-- (cdr ...))) ((car (cdr ...)) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:default-config-keywords :if-buffer-not-found :if-config-not-found)" (car --cl-keys--)))))) (catch '--cl-block-popwin:display-buffer-1-- (let* ((buffer (popwin:get-buffer buffer-or-name if-buffer-not-found)) (pattern-and-keywords (popwin:match-config buffer))) (if pattern-and-keywords nil (if if-config-not-found (throw '--cl-block-popwin:display-buffer-1-- (funcall if-config-not-found buffer)) (setq pattern-and-keywords '...))) (let* ((--cl-rest-- (append ... default-config-keywords)) (regexp (car ...)) (width (car ...)) (height (car ...)) (position (car ...)) (noselect (car ...)) (dedicated (car ...)) (stick (car ...)) (tail (car ...))) (progn (let (...) (while --cl-keys-- ...)) (popwin:popup-buffer buffer :width (or width popwin:popup-window-width) :height (or height popwin:popup-window-height) :position (or position popwin:popup-window-position) :noselect (or ... noselect) :dedicated dedicated :stick stick :tail tail)))))))
  popwin:display-buffer-1(#<buffer *Help*> :if-config-not-found (lambda (buffer) (popwin:original-display-buffer buffer not-this-window)))
  (if (popwin:reuse-window-p buffer-or-name not-this-window) (popwin:original-display-buffer buffer-or-name not-this-window) (popwin:display-buffer-1 buffer-or-name :if-config-not-found (if (with-no-warnings (called-interactively-p)) nil #'(lambda (buffer) (popwin:original-display-buffer buffer not-this-window)))))
  popwin:display-buffer(#<buffer *Help*> nil)
  (let ((not-this-window (plist-get 'inhibit-same-window alist))) (popwin:display-buffer buffer not-this-window))
  popwin:display-buffer-action(#<buffer *Help*> (reusable-frames quote visible (inhibit-same-window . t)))
  display-buffer(#<buffer *Help*> nil)
  temp-buffer-window-show(#<buffer *Help*> nil)
  describe-face(window-divider)
  #f(compiled-function (s _b _f) #<bytecode 0x3f2b9d>)(window-divider #<buffer *scratch*> #<frame  liveuser@localhost *scratch*  0x3438e30>)
  #f(compiled-function (arg0) #<bytecode 0x3ed405>)(("face" facep #f(compiled-function (s _b _f) #<bytecode 0x3f2b9d>)))
  mapcar(#f(compiled-function (arg0) #<bytecode 0x3ed405>) ((nil cl-find-class #f(compiled-function (s b f) #<bytecode 0x3b6119>)) (nil fboundp #f(compiled-function (s _b _f) #<bytecode 0x3f2b71>)) (nil #f(compiled-function (symbol) #<bytecode 0x3f2b89>) describe-variable) ("face" facep #f(compiled-function (s _b _f) #<bytecode 0x3f2b9d>))))
  describe-symbol(window-divider)
  funcall-interactively(describe-symbol window-divider)
  call-interactively(describe-symbol nil nil)
  command-execute(describe-symbol)

[-- Attachment #3: Type: text/plain, Size: 239 bytes --]


>
> To put it into use run emacs with
>
> (setq default-frame-alist '((minibuffer . loose) (echo-area . t)))
>
> Looks better with window dividers enabled.
>
> Eventually I'll make a remote branch.  But this may take its time.
>
> martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01 12:57                       ` Robert Pluim
@ 2018-08-02  7:09                         ` martin rudalics
  0 siblings, 0 replies; 27+ messages in thread
From: martin rudalics @ 2018-08-02  7:09 UTC (permalink / raw)
  To: Robert Pluim; +Cc: Andy Moreton, 32257

 > It looks interesting, but please use something other than 'loose' to
 > describe the behaviour. I guess 'dedicated' is already
 > overloaded. 'separate', 'no-echo'....?

The behavior is more complicated than that.  In escalating order a
user can:

(1) Put minibuffer and echo in a shared "fixed" window at the top of
the frame.  Together with the standard behavior, this is considered a
fixed minibuffer window because it never goes away.

(2) Put minibuffer and echo in a shared "loose" window anywhere on a
frame.  A loose window is not fixed, it can be deleted at any time and
will be recreated when a message shall be shown or a question asked.

(3) Put minibuffer and echo in separate, loose windows anywhere on a
frame.  Both windows can be deleted where the minibuffer window is
deleted automatically when the dialog with the user has terminated.

Hence, the term loose refers only to the fact that such a window can
be deleted and will be recreated on-the-fly.  Alternatively, we could
call such a window "volatile", "ephemeral" ...  And to indicate that
echos shall appear in a separate window you have to specify a non-nil
'echo-area' frame parameter on top of a 'loose' minibuffer parameter.

Eventually we would allow an arbitrary number of loose minibuffer and
echo windows on a frame, each possibly owned by a separate thread
allowing an arbitrary number of user interactions to go on in
parallel.  But I have not yet digged into all conventions for
minibuffers and echos (naming, synchronicity) and maybe never will.

martin





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01 20:55                       ` Live System User
@ 2018-08-02  7:09                         ` martin rudalics
  0 siblings, 0 replies; 27+ messages in thread
From: martin rudalics @ 2018-08-02  7:09 UTC (permalink / raw)
  To: Live System User, 32257

 >    I know this is not ready but I thought I'd report this error
 >    when using this patch with popwin.el (a third-party app).

FWIW this is a bug in popwin.el and should be fixed there.  The same
error is reported here with Emacs 26 invoked via

--eval "(setq default-frame-alist '((minibuffer . nil)))"

martin





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2018-08-01 12:05                     ` Andy Moreton
@ 2019-05-22  0:52                       ` Noam Postavsky
  2019-05-22 10:08                         ` Eli Zaretskii
  0 siblings, 1 reply; 27+ messages in thread
From: Noam Postavsky @ 2019-05-22  0:52 UTC (permalink / raw)
  To: Andy Moreton; +Cc: 32257

[-- Attachment #1: Type: text/plain, Size: 791 bytes --]

tags 32257 + patch
quit

Andy Moreton <andrewjmoreton@gmail.com> writes:

> On Wed 01 Aug 2018, Eli Zaretskii wrote:
>>> 
>>> Noam's original patch may bring some possibility of new problems,
>>> but it does at least fix the original problem (on emacs-26 and
>>> master).
>>
>> Fixing a problem and introducing another doesn't sound wise to me.
>> We've been there several times during the last years, and I don't want
>> to make the same mistake again.  Sorry.
>
> Perhaps reverting the patches that introduced read-multiple-choice
> is the right answer then.

How about for emacs-26, we call read-event only after receiving the
problematic error.  This works around the problem, and should be safe
enough, since it only triggers when we would otherwise be ending up in
an infloop anyway.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: patch --]
[-- Type: text/x-diff, Size: 1674 bytes --]

From 8545e36df343cc169d7ae1ab3a43c9805cd51015 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <npostavs@gmail.com>
Date: Tue, 21 May 2019 20:33:09 -0400
Subject: [PATCH] Avoid infloop in read-multiple-choice (Bug#32257)

* lisp/emacs-lisp/rmc.el (read-multiple-choice): When `read-char'
signals an error "Non-character input-event", call `read-event' to
take the non-character event out of the queue.  Don't merge to master,
we just use `read-event' directly there, rather than this solution
which relies a particular error message.
---
 lisp/emacs-lisp/rmc.el | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/lisp/emacs-lisp/rmc.el b/lisp/emacs-lisp/rmc.el
index 6d1adae974..5411f2ba77 100644
--- a/lisp/emacs-lisp/rmc.el
+++ b/lisp/emacs-lisp/rmc.el
@@ -116,10 +116,15 @@ read-multiple-choice
                               (cons (capitalize (cadr elem))
                                     (car elem)))
                             choices)))
-                  (condition-case nil
+                  (condition-case err
                       (let ((cursor-in-echo-area t))
                         (read-char))
-                    (error nil))))
+                    (error (when (equal (cadr err) "Non-character input-event")
+                             ;; Use up the non-character input-event.
+                             ;; Otherwise we'll just keep reading it
+                             ;; again and again (Bug#32257).
+                             (read-event))
+                           nil))))
           (setq answer (lookup-key query-replace-map (vector tchar) t))
           (setq tchar
                 (cond
-- 
2.11.0


[-- Attachment #3: Type: text/plain, Size: 38 bytes --]


And master can just use read-event.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: patch --]
[-- Type: text/x-diff, Size: 1030 bytes --]

From f1f32043f65dc19b6d63cf1ce52ef19b46c14e22 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <npostavs@gmail.com>
Date: Tue, 21 May 2019 20:38:00 -0400
Subject: [PATCH] Avoid infloop in read-multiple-choice (Bug#32257)

* lisp/emacs-lisp/rmc.el (read-multiple-choice): Use `read-event'
which won't get stuck (return the same event over and over again) for
non-character events, unlike `read-char'.
---
 lisp/emacs-lisp/rmc.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/rmc.el b/lisp/emacs-lisp/rmc.el
index 6d1adae974..47f3b8dc9c 100644
--- a/lisp/emacs-lisp/rmc.el
+++ b/lisp/emacs-lisp/rmc.el
@@ -118,7 +118,7 @@ read-multiple-choice
                             choices)))
                   (condition-case nil
                       (let ((cursor-in-echo-area t))
-                        (read-char))
+                        (read-event))
                     (error nil))))
           (setq answer (lookup-key query-replace-map (vector tchar) t))
           (setq tchar
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2019-05-22  0:52                       ` Noam Postavsky
@ 2019-05-22 10:08                         ` Eli Zaretskii
  2019-05-26 12:57                           ` Noam Postavsky
  0 siblings, 1 reply; 27+ messages in thread
From: Eli Zaretskii @ 2019-05-22 10:08 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: andrewjmoreton, 32257

> From: Noam Postavsky <npostavs@gmail.com>
> Date: Tue, 21 May 2019 20:52:34 -0400
> Cc: 32257@debbugs.gnu.org
> 
> >> Fixing a problem and introducing another doesn't sound wise to me.
> >> We've been there several times during the last years, and I don't want
> >> to make the same mistake again.  Sorry.
> >
> > Perhaps reverting the patches that introduced read-multiple-choice
> > is the right answer then.
> 
> How about for emacs-26, we call read-event only after receiving the
> problematic error.  This works around the problem, and should be safe
> enough, since it only triggers when we would otherwise be ending up in
> an infloop anyway.

SGTM, thanks.





^ permalink raw reply	[flat|nested] 27+ messages in thread

* bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks
  2019-05-22 10:08                         ` Eli Zaretskii
@ 2019-05-26 12:57                           ` Noam Postavsky
  0 siblings, 0 replies; 27+ messages in thread
From: Noam Postavsky @ 2019-05-26 12:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 32257, andrewjmoreton

tags 32257 fixed
close 32257 26.3
quit

Eli Zaretskii <eliz@gnu.org> writes:

>> How about for emacs-26, we call read-event only after receiving the
>> problematic error.  This works around the problem, and should be safe
>> enough, since it only triggers when we would otherwise be ending up in
>> an infloop anyway.
>
> SGTM, thanks.

Done.

c4d4dcf17e 2019-05-26T08:46:30-04:00 "Avoid infloop in read-multiple-choice (Bug#32257)"
https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=c4d4dcf17e407a3c68e150f22b9756ef6c943070

3f03f6284a 2019-05-26T08:16:23-04:00 "Avoid infloop in read-multiple-choice (Bug#32257)"
https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=3f03f6284a093d69086773226bc2273cf62f5e85






^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2019-05-26 12:57 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-24 12:05 bug#32257: 26.1; read-multiple-choice inf loops on mouse clicks Noam Postavsky
2018-07-26  1:25 ` Noam Postavsky
2018-07-27  9:08   ` Eli Zaretskii
2018-07-27 11:35     ` Andy Moreton
2018-07-27 12:30       ` Eli Zaretskii
2018-07-27 12:45         ` Noam Postavsky
2018-07-27 13:50           ` Eli Zaretskii
2018-07-31  1:52             ` Noam Postavsky
2018-07-31 11:59               ` Andy Moreton
2018-07-31 12:27                 ` martin rudalics
2018-07-31 12:38                   ` Andy Moreton
2018-08-01  8:44                     ` martin rudalics
2018-08-01 12:30                       ` Noam Postavsky
2018-08-01 12:57                       ` Robert Pluim
2018-08-02  7:09                         ` martin rudalics
2018-08-01 20:55                       ` Live System User
2018-08-02  7:09                         ` martin rudalics
2018-07-31 16:06                 ` Eli Zaretskii
2018-07-31 19:26                   ` Andy Moreton
2018-08-01  5:27                     ` Eli Zaretskii
2018-07-31 16:04               ` Eli Zaretskii
2018-07-31 19:29                 ` Andy Moreton
2018-08-01  5:29                   ` Eli Zaretskii
2018-08-01 12:05                     ` Andy Moreton
2019-05-22  0:52                       ` Noam Postavsky
2019-05-22 10:08                         ` Eli Zaretskii
2019-05-26 12:57                           ` Noam Postavsky

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).