Subject: Re: bug#73768: 'read-passwd-mode' can clobber user defined mode lines

From: martin rudalics <[email-masked]>
Date: Sun, 13 Oct 2024 11:58:52 +0200

When in my Emacs with a stand alone minibuffer frame I create a shell
buffer and there do ssh-add, the following happens:

In the lines below 'wid' stands for the window in direction (nil in the
rows below because a stand alone minibuffer frame has no window above),
'msw' the minibuffer selected window, 'glw' the largest window (nil in
the first row below because a stand alone minibuffer frame has no
largest window) and 'sw' the selected window.  'add' and 'remove' stand
for adding and removing the icon. 'before' and 'after' stand for whether
we are before or after doing that.  These are followed by the value of
'read-passwd--mode-line-buffer' and the first two elements of that
buffer's mode line string.

wid .. nil .. msw .. #<window 11 on *shell*> .. glw .. nil .. sw .. #<window 8 on  *Minibuf-1*>
add before .. *shell* .. %e .. mode-line-version
add after .. *shell* .. (:eval read-passwd--mode-line-icon) .. %e
wid .. nil .. msw .. nil .. glw .. #<window 6 on .emacs> .. sw .. #<window 11 on *shell*>
remove before .. .emacs .. %e .. mode-line-version
remove after .. .emacs .. mode-line-version ..

So the window chosen for adding the icon is the value of calling
'minibuffer-selected-window' and everything is OK ('mode-line-version'
is a private variable of mine, ignore it).  The icon's buffer is *shell*
and the icon appears correctly.

But when the icon shall be removed, 'minibuffer-selected-window' returns
nil and the largest window is chosen instead which now happens to show
my init file .emacs.  ‘read-passwd’ leaves the icon in *shell* and kills
the mode line of my .emacs file.

The crucial fact is that the selected window changes from the minibuffer
window to the *shell* window because 'minibuffer-selected-window' is
specified as

   if (minibuf_level > 0
       && MINI_WINDOW_P (XWINDOW (selected_window))
       && WINDOW_LIVE_P (minibuf_selected_window))
     return minibuf_selected_window;

and *shell* is not a mini window.  So 'minibuffer-selected-window'
returns nil in the scenario at hand and the largest window of the
selected window's frame is chosen.  And the selected window obviously
changes because 'read-string' (and in further consequence read_minibuf)
restores the window selected before the minibuffer window was selected.

Concludingly, the current version works when 'window-in-direction'
returns a suitable window and may fail miserably otherwise.

The attached patch fixes the problem here.  But I still think that the
icon should appear in the prompt of 'read-passwd' itself.

martin
diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index 90b58f560c0..5353a7a55fe 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -2534,13 +2534,15 @@ read-passwd-mode
   :keymap read-passwd-map
   :version "30.1"
 
-  (setq read-passwd--hide-password nil
-        ;; Stolen from `eldoc-minibuffer-message'.
-        read-passwd--mode-line-buffer
-        (window-buffer
-         (or (window-in-direction 'above (minibuffer-window))
-	     (minibuffer-selected-window)
-	     (get-largest-window))))
+  (setq read-passwd--hide-password nil)
+  ;; Stolen from `eldoc-minibuffer-message'.
+  (unless (and (buffer-live-p read-passwd--mode-line-buffer)
+	       (get-buffer-window read-passwd--mode-line-buffer))
+    (setq read-passwd--mode-line-buffer
+          (window-buffer
+           (or (window-in-direction 'above (minibuffer-window))
+	       (minibuffer-selected-window)
+	       (get-largest-window)))))
 
   (if read-passwd-mode
       (with-current-buffer read-passwd--mode-line-buffer
@@ -2552,7 +2554,8 @@ read-passwd-mode
     (with-current-buffer read-passwd--mode-line-buffer
       ;; Remove `read-passwd--mode-line-icon'.
       (when (listp mode-line-format)
-        (setq mode-line-format (cdr mode-line-format)))))
+        (setq mode-line-format (cdr mode-line-format)))
+      (setq read-passwd--mode-line-buffer nil)))
 
   (when read-passwd-mode
     (read-passwd-toggle-visibility)))