Subject: Re: bug#24500: 25.1.50; Can't other-window from minibuffer if Ediff control panel frame present

From: martin rudalics <[email-masked]>
Date: Sat, 24 Sep 2016 21:04:49 +0200

A simpler scenario with emacs -Q is evaluating

(make-frame '((minibuffer . nil)))

followed by M-x and C-x o in the original frame.

 > "C-x o" switches to "some other window in the cyclic ordering of
 > windows".  It does so by calling next-window, which is called in a way
 > that doesn't limit it to return windows only on the current frame.

It this particular case it does so because the minibuffer is (1) active
and (2) shared by the minibuffer-less frame.

 > So what happens here is that next-window returns the window on the
 > Ediff control frame, and Emacs then selects it, and also makes the
 > control frame the selected frame.  But because the original selected
 > frame, when you typed "M-x", was the main frame (and we are still
 > reading from its minibuffer), Emacs switches back to the main frame
 > right away.  And that's what you see: the single window of the Ediff
 > control frame becomes the selected window, but its frame doesn't
 > become the selected frame.

That would be fatal ;-)

At the command prompt the frame of the selected window must be
invariantly the selected frame.  Try the following snippet:

(defvar frame (make-frame '((minibuffer . nil))))

(define-key ctl-x-map "o" 'my-other-window)

(defun my-other-window ()
   (interactive)
   (other-window 1)
   (with-current-buffer "*scratch*"
     (goto-char (point-max))
     (insert
      (format "sw: %s ... wfsw: %s ... sf: %s ... fff: %s\n"
	     (selected-window)
	     (window-frame (selected-window))
	     (selected-frame)
	     (frame-focus frame)))))

It's easy to see, trying with M-x and C-x o, once from the original and
once from the minibuffer-less frame, that the difference is with the
focus of the minibuffer-less frame.  The selected window and the
selected frame's selected window are always the same.

 > Not sure what, if anything, could or should be done about this.

The annoying aspect is that once the minibuffer-less frame is selected,
another C-x o doesn't get you back to the frame with the minibuffer for
the simple reason that the minibuffer-less frame has its focus currently
_not_ redirected.  As a consequence, no combinations of C-g and C-x o
will get you out of this mess.

I attached two patches that seem to work, but without any warranty (I do
not fully understand the intentions of frame-focus/focus_frame and
x_get_focus_frame yet).  The purpose of these patches is to keep the
‘next-window’ and ‘other-window’ mechanisms symmetric whenever a frame
shares its minibuffer with other frames:

(1) The frame.c patch changes the behavior of ‘do_switch_frame’ by
redirecting focus to another frame that shares this frame's minibuffer
even when that other frame has no pending minibuffer activity.

(2) The window.c patch simply inhibits ‘next-window’ to select a window
on a frame that has no pending minibuffer activity.

Please try these patches (only one at a time because the window.c patch
makes the frame.c patch moot) and tell me whether they have any bad
effects.

Thanks, martin
diff --git a/src/frame.c b/src/frame.c
index 00f25f7..b729249 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -1157,7 +1157,10 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor
       if (FRAMEP (xfocus))
 	{
 	  focus = FRAME_FOCUS_FRAME (XFRAME (xfocus));
-	  if (FRAMEP (focus) && XFRAME (focus) == SELECTED_FRAME ())
+	  if ((FRAMEP (focus) && XFRAME (focus) == SELECTED_FRAME ())
+	      || (NILP (focus)
+		  && EQ (FRAME_MINIBUF_WINDOW (XFRAME (frame)),
+			 sf->selected_window)))
 	    Fredirect_frame_focus (xfocus, frame);
 	}
     }
diff --git a/src/window.c b/src/window.c
index 733cf75..428d572 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2346,8 +2346,7 @@ candidate_window_p (Lisp_Object window, Lisp_Object owindow,
 	    == FRAME_TERMINAL (XFRAME (selected_frame)));
     }
   else if (WINDOWP (all_frames))
-    candidate_p = (EQ (FRAME_MINIBUF_WINDOW (f), all_frames)
-		   || EQ (XWINDOW (all_frames)->frame, w->frame)
+    candidate_p = (EQ (XWINDOW (all_frames)->frame, w->frame)
 		   || EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f)));
   else if (FRAMEP (all_frames))
     candidate_p = EQ (all_frames, w->frame);