Subject: 30.0.50; Toggle password visibility

From: Michael Albinus <[email-masked]>
Date: Fri, 16 Feb 2024 14:33:02 +0100

We have `read-passwd', which reads a password in the minibuffer, hiding
the input. However, modern password input boxes outside Emacs allow to
show/hide the input while typing.

The appended patch tries to implement this feature. The minibuffer in
`read-passwd' has bound TAB to a function, which toggles visibility of
the typed input.

On graphic displays, the mode-line has an additional indicator for
whowing/hiding the typed password. Clicking on this indicator with
mouse-1 toggles the password visibility.

Comments?

Best regards, Michael.

In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.39, cairo version 1.18.0) of 2024-01-07 built on gandalf
Repository revision: 166b10e9f80dc78147601a87b6425f59860bcfe4
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12302004
System Description: Fedora Linux 39 (Workstation Edition)

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
LIBOTF LIBSELINUX LIBXML2 M17N_FLT MODULES NATIVE_COMP NOTIFY INOTIFY
PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS
TREE_SITTER X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8

Major mode: Group

Minor modes in effect:
  gnus-undo-mode: t
  display-time-mode: t
  delete-selection-mode: t
  icomplete-mode: t
  global-goto-address-mode: t
  goto-address-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/albinus/src/elpa/packages/debbugs/debbugs hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs
/home/albinus/src/elpa/packages/debbugs/debbugs-org hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-org
/home/albinus/src/elpa/packages/debbugs/debbugs-gnu hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-gnu
/home/albinus/src/elpa/packages/debbugs/debbugs-guix hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-guix
/home/albinus/src/elpa/packages/debbugs/debbugs-browse hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-browse
/home/albinus/src/elpa/packages/debbugs/debbugs-pkg hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-pkg
/home/albinus/src/elpa/packages/debbugs/debbugs-autoloads hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-autoloads
/home/albinus/src/elpa/packages/debbugs/debbugs-compat hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-compat
/home/albinus/.emacs.d/elpa/helm-3.9.7/helm-packages hides /home/albinus/.emacs.d/elpa/helm-core-3.9.7/helm-packages
~/lisp/telepathy hides /home/albinus/.emacs.d/elpa/telepathy-20131209.1258/telepathy
/home/albinus/src/elpa/packages/tramp-theme/tramp-theme-autoloads hides /home/albinus/.emacs.d/elpa/tramp-theme-0.2/tramp-theme-autoloads
/home/albinus/src/elpa/packages/tramp-theme/tramp-theme hides /home/albinus/.emacs.d/elpa/tramp-theme-0.2/tramp-theme
/home/albinus/src/elpa/packages/tramp-theme/tramp-theme-pkg hides /home/albinus/.emacs.d/elpa/tramp-theme-0.2/tramp-theme-pkg
/home/albinus/.emacs.d/elpa/hydra-0.15.0/lv hides /home/albinus/.emacs.d/elpa/lv-0.15.0/lv
/home/albinus/.emacs.d/elpa/transient-20240201.1100/transient hides /usr/local/share/emacs/30.0.50/lisp/transient
~/lisp/dbus hides /usr/local/share/emacs/30.0.50/lisp/net/dbus
/home/albinus/src/tramp/lisp/tramp-sh hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-sh
/home/albinus/src/tramp/lisp/tramp-fuse hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-fuse
/home/albinus/src/tramp/lisp/tramp-loaddefs hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-loaddefs
/home/albinus/src/tramp/lisp/tramp-ftp hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-ftp
/home/albinus/src/tramp/lisp/tramp hides /usr/local/share/emacs/30.0.50/lisp/net/tramp
/home/albinus/src/tramp/lisp/tramp-cache hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-cache
/home/albinus/src/tramp/lisp/tramp-uu hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-uu
/home/albinus/src/tramp/lisp/tramp-rclone hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-rclone
/home/albinus/src/tramp/lisp/tramp-integration hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-integration
/home/albinus/src/tramp/lisp/tramp-archive hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-archive
/home/albinus/src/tramp/lisp/tramp-adb hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-adb
/home/albinus/src/tramp/lisp/tramp-cmds hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-cmds
/home/albinus/src/tramp/lisp/tramp-compat hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-compat
/home/albinus/src/tramp/lisp/tramp-sudoedit hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-sudoedit
/home/albinus/src/tramp/lisp/tramp-container hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-container
/home/albinus/src/tramp/lisp/tramp-gvfs hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-gvfs
/home/albinus/src/tramp/lisp/tramp-crypt hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-crypt
/home/albinus/src/tramp/lisp/tramp-message hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-message
/home/albinus/src/tramp/lisp/tramp-smb hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-smb
/home/albinus/src/tramp/lisp/trampver hides /usr/local/share/emacs/30.0.50/lisp/net/trampver
/home/albinus/src/tramp/lisp/tramp-sshfs hides /usr/local/share/emacs/30.0.50/lisp/net/tramp-sshfs

Features:
(shadow emacsbug whitespace vc-dir ewoc conf-mode smerge-mode diff
org-element org-persist org-id org-refile avl-tree generator oc-basic
ol-eww ol-rmail ol-mhe ol-irc ol-info ol-gnus nnselect ol-docview
doc-view filenotify ol-bibtex bibtex ol-bbdb ol-w3m ol-doi org-link-doi
org org-macro org-pcomplete org-list org-footnote org-faces org-entities
noutline outline ob-emacs-lisp org-table org-loaddefs cal-menu calendar
cal-loaddefs ediff-vers ediff ediff-merg ediff-mult ediff-wind
ediff-diff ediff-help ediff-init ediff-util hyperspec nndoc gnus-dup
debbugs-gnu debbugs-compat debbugs soap-client warnings mailalias debug
backtrace cl-print timezone find-func shortdoc help-fns radix-tree eww
mm-url macrostep-c cmacexp macrostep cc-mode cc-fonts cc-guess cc-menus
cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs add-log vc-hg
vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs log-view pcvs-util pulse xref
project grep display-line-numbers url-queue gnus-fun shr-color color
flow-fill mm-archive url-http url-gw url-auth gnus-gravatar gravatar dns
url-cache smiley gnus-cite mail-extr gnus-bcklg textsec uni-scripts
idna-mapping ucs-normalize uni-confusable textsec-check gnus-async
cl-extra gnus-ml debbugs-browse bug-reference disp-table qp pop3 utf-7
nndraft nnmh nnml network-stream nsm gnus-agent gnus-srvr gnus-score
score-mode nnvirtual gnus-msg gnus-art mm-uu mml2015 mm-view mml-smime
smime gnutls dig gnus-cache gnus-sum shr pixel-fill kinsoku url-file svg
time-stamp nnnil smtpmail gnus-demon nntp gnus-group gnus-undo
gnus-start gnus-dbus dbus xml gnus-cloud nnimap nnmail mail-source utf7
nnoo gnus-spec gnus-int gnus-range message sendmail yank-media puny
rfc822 mml mml-sec epa derived epg rfc6068 epg-config mm-decode
mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045 ietf-drums
mailabbrev gmm-utils mailheader gnus-win gnus nnheader gnus-util
mail-utils range mm-util mail-prsvr magit-base magit-section
cursor-sensor crm dash compat mule-util sort find-dired vc-git diff-mode
easy-mmode misearch multi-isearch rng-xsd xsd-regexp rng-cmpct rng-nxml
rng-valid rng-loc rng-uri rng-parse nxml-parse rng-match rng-dt rng-util
rng-pttrn nxml-ns nxml-mode nxml-outln nxml-rap sgml-mode facemenu dom
nxml-util nxml-enc xmltok image-mode exif dired-aux compile
text-property-search comp-run comp-common face-remap ob-shell ob
ob-tangle ol org-src ob-ref ob-lob ob-table ob-exp ob-comint ob-core
org-cycle org-fold org-fold-core ob-eval org-keys oc org-compat
org-version org-macs vc vc-dispatcher time tramp-sh lxc-tramp lxd-tramp
tramp trampver tramp-integration files-x tramp-message help-mode
tramp-compat xdg shell pcomplete comint ansi-osc ring parse-time iso8601
time-date format-spec ansi-color tramp-loaddefs rx delsel ido jka-compr
icomplete cus-edit pp cus-load icons wid-edit dired dired-loaddefs
goto-addr thingatpt alert-autoloads android-mode-autoloads
auth-source-gopass-autoloads auth-source-keytar-autoloads
auth-source-kwallet-autoloads auth-source-xoauth2-autoloads
auto-sudoedit-autoloads auto-virtualenv-autoloads
auto-virtualenvwrapper-autoloads boxquote-autoloads
clang-format-autoloads company-shell-autoloads company-autoloads
counsel-toki-autoloads counsel-tramp-autoloads counsel-autoloads
dbus-codegen-autoloads debbugs-autoloads dired-du-autoloads
dired-rsync-autoloads dired-toggle-sudo-autoloads direnv-autoloads
disk-usage-autoloads dockerfile-mode-autoloads
editorconfig-charset-extras-autoloads
editorconfig-custom-majormode-autoloads
editorconfig-domain-specific-autoloads editorconfig-autoloads
editorconfig-generate-autoloads ednc-autoloads el-get-autoloads
envrc-autoloads etc-sudoers-mode-autoloads
exec-path-from-shell-autoloads fontaine-autoloads forge-autoloads
closql-autoloads emacsql-autoloads friendly-tramp-path-autoloads
fzf-autoloads ggtags-autoloads ghub-autoloads gited-autoloads
gitlab-ci-mode-flycheck-autoloads gitlab-ci-mode-autoloads
flycheck-autoloads gntp-autoloads gnus-desktop-notify-autoloads
helm-gitlab-autoloads helm-projectile-autoloads helm-autoloads
helm-core-autoloads async-autoloads ibuffer-tramp-autoloads
inheritenv-autoloads ivy-gitlab-autoloads gitlab-autoloads
journalctl-mode-autoloads keepass-mode-autoloads keytar-autoloads
kubernetes-autoloads log4e-autoloads lsp-java-autoloads
dap-mode-autoloads lsp-docker-autoloads bui-autoloads
lsp-latex-autoloads consult-autoloads lsp-treemacs-autoloads
lsp-mode-autoloads f-autoloads lxc-tramp-autoloads lxd-tramp-autoloads
magit-filenotify-autoloads magit-autoloads pcase git-commit-autoloads
magit-popup-autoloads magit-section-autoloads marcopolo-autoloads
mastodon-autoloads nexus-autoloads nnir-est-autoloads oauth2-autoloads
ob-restclient-autoloads orderless-autoloads persist-autoloads
pkg-info-autoloads epl-autoloads popup-autoloads projectile-autoloads
promise-autoloads pylint-autoloads python-environment-autoloads
deferred-autoloads pyvenv-autoloads
recentf-remove-sudo-tramp-prefix-autoloads request-autoloads
restclient-test-autoloads restclient-autoloads s3ed-autoloads finder-inf
slime-autoloads macrostep-autoloads spinner-autoloads
ssh-deploy-autoloads su-autoloads sudo-edit-autoloads sudo-ext-autoloads
sudo-utils-autoloads swiper-autoloads ivy-autoloads sx-autoloads
markdown-mode-autoloads telepathy-autoloads totp-autoloads
totp-auth-autoloads base32-autoloads tramp-theme-autoloads
transient-dwim-autoloads transient-autoloads treemacs-autoloads
cfrs-autoloads posframe-autoloads ht-autoloads pfuture-autoloads
ace-window-autoloads avy-autoloads treepy-autoloads uuid-autoloads
vdiff-autoloads hydra-autoloads lv-autoloads vertico-autoloads
virtualenv-autoloads virtualenvwrapper-autoloads s-autoloads
dash-autoloads web-server-autoloads wfnames-autoloads
with-editor-autoloads info compat-autoloads yaml-autoloads
yaml-mode-autoloads package browse-url url url-proxy url-privacy
url-expand url-methods url-history url-cookie generate-lisp-file
url-domsuf url-util mailcap url-handlers url-parse auth-source cl-seq
eieio eieio-core cl-macs password-cache json subr-x map byte-opt gv
bytecomp byte-compile url-vars cl-loaddefs cl-lib rmc iso-transl tooltip
cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type
elisp-mode mwheel term/x-win x-win term/common-win x-dnd touch-screen
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq
simple cl-generic indonesian philippine cham georgian utf-8-lang
misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms
cp51932 hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese composite emoji-zwj charscript charprop case-table
epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button
loaddefs theme-loaddefs faces cus-face macroexp files window
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget keymap hashtable-print-readable backquote threads dbusbind
inotify dynamic-setting system-font-setting font-render-setting cairo
gtk x-toolkit xinput2 x multi-tty move-toolbar make-network-process
native-compile emacs)

Memory information:
((conses 16 886899 163552) (symbols 48 39461 3)
 (strings 32 179866 14690) (string-bytes 1 5887530) (vectors 16 96047)
 (vector-slots 8 2459540 118160) (floats 8 849 18205)
 (intervals 56 34351 1182) (buffers 984 108))

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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+    <path d="m 1.53125 0.46875 l -1.0625 1.0625 l 14 14 l 1.0625 -1.0625 l -2.382812 -2.382812 c 1.265624 -1.0625 2.171874 -2.496094 2.589843 -4.097657 c -0.914062 -3.523437 -4.097656 -5.984375 -7.738281 -5.988281 c -1.367188 0.011719 -2.707031 0.371094 -3.894531 1.042969 z m 6.46875 3.53125 c 2.210938 0 4 1.789062 4 4 c -0.003906 0.800781 -0.246094 1.578125 -0.699219 2.238281 l -1.46875 -1.46875 c 0.105469 -0.242187 0.164063 -0.503906 0.167969 -0.769531 c 0 -1.105469 -0.894531 -2 -2 -2 c -0.265625 0.003906 -0.527344 0.0625 -0.769531 0.167969 l -1.46875 -1.46875 c 0.660156 -0.453125 1.4375 -0.695313 2.238281 -0.699219 z m -6.144531 0.917969 c -0.753907 0.898437 -1.296875 1.957031 -1.59375 3.09375 c 0.914062 3.523437 4.097656 5.984375 7.738281 5.988281 c 0.855469 -0.007812 1.703125 -0.152344 2.511719 -0.425781 l -1.667969 -1.667969 c -0.277344 0.058594 -0.5625 0.089844 -0.84375 0.09375 c -2.210938 0 -4 -1.789062 -4 -4 c 0.003906 -0.28125 0.035156 -0.566406 0.09375 -0.84375 z m 0 0" fill="#2e3436"/>
+</svg>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+    <path d="m 8 2 c -3.648438 0.003906 -6.832031 2.476562 -7.738281 6.007812 c 0.914062 3.527344 4.097656 5.988282 7.738281 5.992188 c 3.648438 -0.003906 6.832031 -2.476562 7.738281 -6.011719 c -0.914062 -3.523437 -4.097656 -5.984375 -7.738281 -5.988281 z m 0 2 c 2.210938 0 4 1.789062 4 4 s -1.789062 4 -4 4 s -4 -1.789062 -4 -4 s 1.789062 -4 4 -4 z m 0 2 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 s 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 0" fill="#2e3436"/>
+</svg>
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))))))))