diff --git a/etc/images/README b/etc/images/README
index a778d9ce6c3..77377d36b5a 100644
--- a/etc/images/README
+++ b/etc/images/README
@@ -125,7 +125,7 @@ For more information see the adwaita-icon-theme repository at:
https://gitlab.gnome.org/GNOME/adwaita-icon-theme
-Emacs images and their source in the Adwaita/scalable directory:
+Emacs images and their source in the Adwaita/symbolic directory:
checked.svg ui/checkbox-checked-symbolic.svg
unchecked.svg ui/checkbox-symbolic.svg
@@ -137,3 +137,5 @@ Emacs images and their source in the Adwaita/scalable directory:
left.svg ui/pan-start-symbolic.svg
right.svg ui/pan-end-symbolic.svg
up.svg ui/pan-up-symbolic.svg
+ conceal.svg actions/view-conceal-symbolic.svg
+ reveal.svg actions/view-reveal-symbolic.svg
diff --git a/etc/images/conceal.svg b/etc/images/conceal.svg
new file mode 100644
index 00000000000..172b73ed3d3
--- /dev/null
+++ b/etc/images/conceal.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/etc/images/reveal.svg b/etc/images/reveal.svg
new file mode 100644
index 00000000000..41ae3733a53
--- /dev/null
+++ b/etc/images/reveal.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/lisp/simple.el b/lisp/simple.el
index 9a33049f4ca..667b6c84a66 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10858,6 +10858,80 @@ visible-mode
(setq-local vis-mode-saved-buffer-invisibility-spec
buffer-invisibility-spec)
(setq buffer-invisibility-spec nil)))
+
+
+;; It would be preferable to use "👁" ("\N{EYE}"). However, there is
+;; no corresponding Unicode char with a slash. Therefore, we use images.
+(defvar read-passwd-show-password-image "reveal.svg"
+ "Mode-line image to show a hidden password")
+
+(defvar read-passwd-hide-password-image "conceal.svg"
+ "Mode-line image to hide a visible password")
+
+(defvar read-passwd-mode-line-buffer nil
+ "Buffer to modify `mode-line-format' for showing/hiding passwords.")
+
+(defvar read-passwd-mode-line-string nil
+ "Propertized mode line indicator for showing/hiding passwords.")
+
+(defvar read-passwd-mode-line-display nil
+ "Display properties for `read-passwd-mode-line-string'.")
+
+(defun read-passwd--toggle-visibility ()
+ "Toggle minibuffer contents visibility.
+Adapt also mode line."
+ (interactive)
+ (with-current-buffer read-passwd-mode-line-buffer
+ (setq read-passwd--hide-password (not read-passwd--hide-password))
+ (when (display-graphic-p)
+ (setq read-passwd-mode-line-display
+ (find-image
+ `((:type svg
+ :file ,(if read-passwd--hide-password
+ read-passwd-hide-password-image
+ read-passwd-show-password-image))
+ :ascent center))
+ read-passwd-mode-line-string
+ `(:propertize " "
+ display ,read-passwd-mode-line-display
+ help-echo "mouse-1: Toggle password visibility"
+ mouse-face mode-line-highlight
+ local-map
+ (keymap
+ (mode-line keymap (mouse-1 . read-passwd--toggle-visibility)))))
+ (force-mode-line-update)))
+ (read-passwd--hide-password))
+
+(define-minor-mode read-passwd-mode
+ "Toggle visibility of password in minibuffer."
+ :group 'mode-line
+ :group 'minibuffer
+ :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))))
+
+ (when (display-graphic-p)
+ (if read-passwd-mode
+ (with-current-buffer read-passwd-mode-line-buffer
+ ;; Add `read-passwd-mode-line-string'.
+ (when (listp mode-line-format)
+ (setq mode-line-format
+ (cons '(:eval read-passwd-mode-line-string)
+ mode-line-format))))
+ (with-current-buffer read-passwd-mode-line-buffer
+ ;; Remove `read-passwd-mode-line-string'.
+ (when (listp mode-line-format)
+ (setq mode-line-format (cdr mode-line-format))))))
+
+ (when read-passwd-mode
+ (read-passwd--toggle-visibility)))
+
(defvar messages-buffer-mode-map
(let ((map (make-sparse-keymap)))
diff --git a/lisp/subr.el b/lisp/subr.el
index c317d558e24..2f475891df9 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3375,14 +3375,23 @@ read-passwd-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map minibuffer-local-map)
(define-key map "\C-u" #'delete-minibuffer-contents) ;bug#12570
+ (define-key map "\t" #'read-passwd--toggle-visibility)
map)
"Keymap used while reading passwords.")
-(defun read-password--hide-password ()
+(defvar read-passwd--hide-password t)
+
+(defun read-passwd--hide-password ()
+ "Make minibuffer contents hidden or visible."
(let ((beg (minibuffer-prompt-end)))
(dotimes (i (1+ (- (buffer-size) beg)))
- (put-text-property (+ i beg) (+ 1 i beg)
- 'display (string (or read-hide-char ?*))))))
+ (if read-passwd--hide-password
+ (put-text-property
+ (+ i beg) (+ 1 i beg) 'display (string (or read-hide-char ?*)))
+ (remove-list-of-text-properties (+ i beg) (+ 1 i beg) '(display)))
+ (put-text-property
+ (+ i beg) (+ 1 i beg)
+ 'help-echo "C-u: Clear password\nTAB: Toggle password visibility"))))
(defun read-passwd (prompt &optional confirm default)
"Read a password, prompting with PROMPT, and return it.
@@ -3420,18 +3429,20 @@ read-passwd
(setq-local inhibit-modification-hooks nil) ;bug#15501.
(setq-local show-paren-mode nil) ;bug#16091.
(setq-local inhibit--record-char t)
- (add-hook 'post-command-hook #'read-password--hide-password nil t))
+ (read-passwd-mode 1)
+ (add-hook 'post-command-hook #'read-passwd--hide-password nil t))
(unwind-protect
(let ((enable-recursive-minibuffers t)
(read-hide-char (or read-hide-char ?*)))
(read-string prompt nil t default)) ; t = "no history"
+ (read-passwd-mode -1)
(when (buffer-live-p minibuf)
(with-current-buffer minibuf
;; Not sure why but it seems that there might be cases where the
;; minibuffer is not always properly reset later on, so undo
;; whatever we've done here (bug#11392).
(remove-hook 'after-change-functions
- #'read-password--hide-password 'local)
+ #'read-passwd--hide-password 'local)
(kill-local-variable 'post-self-insert-hook)
;; And of course, don't keep the sensitive data around.
(erase-buffer))))))))