Subject: Re: bug#11939: 24.1; `save-buffers-kill-emacs' loses minibuffer focuswhenit calls `list-processes'

From: martin rudalics <[email-masked]>
Date: Fri, 03 Aug 2012 12:40:48 +0200

> My contention from the beginning (i.e., before this thread, in other
 > discussions) is that we need an explicit notion of a minibuffer dialog that
 > should keep the focus in the minibuffer, so that code can ensure that popped up
 > frames do not grab the focus away from the minibuffer during the dialog.  Or if
 > they do, other than via Emacs code or a user action - e.g. via MS Windows, then
 > they are properly redirected back.
 >
 > In terms of implementation it could perhaps be a `with-*<something>'
 > encapsulation.  A given code context DOES know when it is using a popup window
 > (which could thus be a popup frame) only to provide information while asking a
 > minibuffer question, and it can wrap the minibuffer interaction and frame
 > creation in such an encapsulation.
 >
 > The encapsulation would redirect any new frames created to the minibuffer for
 > the duration.  But it would need to allow Emacs code within it the possibility
 > of redirecting the focus (even using a recursive minibuffer invocation that is,
 > itself, not so encapsulated?).  And it would of course need to allow the user to
 > explicitly change the focus.
 >
 > IOW, it would handle new informational frames the way a standalong *Completions*
 > frame is handled: when it is displayed its focus is redirected to the minibuffer
 > frame, but that does not prevent Emacs code or the user from switching the focus
 > to it during minibuffer activity.
 >
 > See the code I sent for my *Completions* frame for an example.  It does this
 > when it displays *Completions* (the selected-frame):
 > (redirect-frame-focus (selected-frame) 1on1-minibuffer-frame)

What you request here is trivial.  read_minibuf has this statement

   if (!EQ (mini_frame, selected_frame))
     Fredirect_frame_focus (selected_frame, mini_frame);

so if the new frame is already selected when this statement is executed,
reading from the minibuffer works out of the box as in my
`with-temp-buffer-window.el' and your *Completions* code examples.

The problem happens in the following scenario:

(1) The application requests (implicitly) to make a new frame.

(2) The application ask to read from the minibuffer with some old frame
     selected.  In this case the statement above does not redirect focus
     because either the old frame is the minibuffer frame or the old
     frame's focus is already redirected to the minibuffer frame.

(3) Emacs eventually selects the new frame in `handle-switch-frame'.

After that, the new frame gets the keystrokes for reading from the
minibuffer but Emacs won't find a redirection in kbd_buffer_get_event.

 >>  > Finally, knowing that the selected frame is the minibuffer
 >>  > frame, but it does not have the focus, I tried this:
 >>  >
 >>  > (add-hook 'after-make-frame-functions
 >>  >           (lambda (frame)
 >>  >             (when (eq (selected-frame)
 >>  >                       (window-frame (minibuffer-window)))
 >>  >               (redirect-frame-focus frame
 >>  >                 (window-frame (minibuffer-window))))))
 >>  >
 >>  > And that solves the problem.  IOW, that does just as much
 >>  > good as the systematic redirection (i.e., without the
 >>  > `when') did, but it does not have the drawback
 >>  > that each time a frame is popped up it loses the focus to
 >>  > the minibuffer frame.
 >>
 >> Using `selected-frame' within `after-make-frame-functions'
 >> seems awfully fragile to me.  IMHO this can't ever work reliably.
 >
 > I cannot speak to that; you're the expert here.  But do you have a
 > counter-example, just for the sake of concreteness?  (Not important, just
 > wondering.)

No and I am no expert here.  In any case the idea of doing

 >>  >             (when (eq (selected-frame)
 >>  >                       (window-frame (minibuffer-window)))

means that when you pop up a new frame when a non-minibuffer frame is
selected you never redirect, while you always redirect when the
minibuffer frame is selected.  This conditioning cannot always work
correctly IMHO.

 >>  > That's the best thing I've come up with, but perhaps you
 >>  > have a suggestion.  `after-make-frame-functions' seems
 >>  > like the right place to do the deed, because it knows
 >>  > about the new frame, which is the one whose focus needs
 >>  > to be redirected.
 >
 > I want to say "ONLY IT knows..." (among existing hooks), but I am not sure of
 > that.  IOW, of the hooks I am aware of, this one seems the most pertinent here.

It knows that a new frame will be constructed.  But it does not know
whether focus shall be redirected.

 >>  > Again, this all seems to underline the need for a
 >>  > notion/mechanism of defining or detecting a user dialog
 >
 > I think you are probably right that "detecting" might be a pipe dream.  What I
 > really have had in mind is mentioned above: the code would encapsulate a
 > minibuffer reading that might pop up an informational window, redirecting focus
 > for any new frames to the minibuffer.

And what you have in mind here should be distinguishable from other
things that pop up frames but do not want the redirection.

 >>  > that uses the minibuffer while popping up an informational
 >>  > frame only for the duration of the minibuffer interaction
 >>  > (input).
 >
 > The important point here is "informational...only".
 >
 > And this is where I need to mention an example of why it is not a solution to do
 > this redirection systematically, testing only
 > (when (eq (selected-frame) (window-frame (minibuffer-window))).
 >
 > A case in point is the debugger.  In my setup *Backtrace* pops up in a
 > special-display frame.  It is not the case that this buffer is for information
 > only.  It is truly necessary that *Backtrace* receive the focus.

Is it?  I never gave it a thought.  But I perfectly understand that when
*Backtrace* pops up you don't want to redirect focus to the minibuffer
frame.

 > So this is a
 > good case where my redirection "fix" does not do the right thing.

If you hardwire redirection in `after-make-frame-functions', you get bad
results in the case where the redirection is not wanted.  What you want
is some clairvoyance in step (1) of my description above whether step
(2) will be performed.  `with-temp-buffer-window' avoids that by asking
the `yes-or-no-p' question in the new frame.  Older application don't
and that's our trouble.

 >>  > Anyway, I will use that code (the last above) for a while,
 >>  > to see how it goes.
 >
 > See previous.  I was mistaken in supposing that doing this systematically would
 > DTRT.  There are clearly some cases where a frame is popped up during minibuffer
 > input, and that frame is NOT only for informational purposes but should in fact
 > receive the input focus.
 >
 > It is only the code that invokes reading from the minibuffer and pops up the
 > other window/frame that can know whether the focus should be in that
 > window/frame or in the minibuffer.

It's not that simple.  The invoking code doesn't care about focus and in
particular not about some frame whose eventually popping up will cause
problems.  It expects the reading from the minibuffer mechanism DTRT.

 > But here's the thing: in the case of windows instead of frames, Emacs DTRT, no?
 > Emacs distinguishes the case of window *Process List* from window *Backtrace*,
 > giving the focus to the latter and not to the former.  Why can't we make Emacs
 > DTRT for frames, just as it does for windows?

For windows on the same frame we can differ between `display-buffer' and
`pop-to-buffer'.  For windows on different frames we have the
complication that the `handle-switch-frame' event triggered by the OS
causes Emacs to select that frame, that is, implicitly replace
`display-buffer' by `pop-to-buffer'.

 > I know that MS Windows altering the focus throws a monkey wrench into the mix,
 > but surely we can find some way to KEEP the focus (i.e. re-focus if necessary)
 > where Emacs put it (correctly).

As far as old code is concerned you probably are overly optimistic.

martin