Subject: bug#77715: [PATCH] Add ring-bell functions for mode line and header line.

From: Elijah Gabe Pérez <[email-masked]>
Date: Fri, 11 Apr 2025 12:18:14 -0600

Ship Mints <[email-masked]> writes:
> I think the term duration is better than length when describing time intervals.

I agree.

> I still prefer the unwind-protected sit-for method that guarantees the remaps are removed to the one with
> the timer where I've experienced errors intervening with a race condition creating new cookies that are
> not removed.  Try mashing C-g with your timer implementation and you'll see it.

The problem with using sit-for is that it delays the message displayed
(usually "Quit") until timer stops (or if there is an input), it look
like Emacs froze.

Anyway, it changed it for use sit-for instead.

> Is a new face really necessary just to implement a flash?  The implementation I proposed
> allows the user to specify any face attributes to define what they want for a flash.  I wouldn't use this
> feature without it being more reliable and flexible.

I think that adding a new face for this would be better, it makes custom
themes set it and change it dynamically; of course, the user can change
face attributes freely.

>From 00d44767ac13b9c8420319884fbb6dccb0c3bf73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= <[email-masked]>
Date: Thu, 10 Apr 2025 11:38:21 -0600
Subject: [PATCH] Add ring-bell function for flash faces.  (bug#77715)

New functions for flash any face or echo area once whether
`ring-bell-function' is called.
* lisp/faces.el (flash-face): New face.
* lisp/simple.el (flash-line-duration, flash-face-faces): New
user option.
(flash-face-bell-function, flash-echo-area-bell-function): New
functions.
---
 etc/NEWS       | 24 ++++++++++++++++++++++++
 lisp/faces.el  | 11 +++++++++++
 lisp/simple.el | 31 +++++++++++++++++++++++++++++++
 3 files changed, 66 insertions(+)

diff --git a/etc/NEWS b/etc/NEWS
index c08b0052639..dcf3608fcf5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1716,6 +1716,25 @@ change their face if the current line exceeds the 'fill-column'.  The
 new face 'display-fill-column-indicator-warning-face' is used to
 highlight the fill-column indicators.  By default this is disabled.
 
+---
+*** New function 'flash-face-bell-function'.
+This function when called will flash a face briefly.
+Intended to be used in 'ring-bell-function'.
+
+---
+*** New function 'flash-echo-area-bell-function'.
+This function when called will flash current echo area briefly.
+Intended to be used in 'ring-bell-function'.
+
+---
+*** New user option 'flash-face-duration'.
+This option controls duration for 'flash-face-bell-function' and
+'flash-echo-area-bell-function'.
+
+---
+*** New user option 'flash-face-faces'.
+This option tells 'flash-face-bell-function' which faces should flash.
+
 
 * New Modes and Packages in Emacs 31.1
 
@@ -1753,6 +1772,11 @@ modes that support it, such as: c-mode, c++-mode, java-mode, js-mode,
 css-mode, and derived: html-mode, mhtml-mode, xml-mode and nxml-mode,
 pascal-mode, lua-ts-mode, lisp-mode and common-lisp-mode
 
+** New face 'flash-face'.
+This face is used for flash a face briefly.
+Mainly used for 'flash-echo-area-bell-function' and
+'flash-face-bell-function'.
+
 
 * Incompatible Lisp Changes in Emacs 31.1
 
diff --git a/lisp/faces.el b/lisp/faces.el
index dbc0a2e04f6..b749627254c 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2825,6 +2825,17 @@ header-line-inactive
   :group 'mode-line-faces
   :group 'basic-faces)
 
+(defface flash-face
+  '((((class color) (min-colors 88))
+     :background "red3" :foreground "white")
+    (((class color grayscale) (min-colors 88))
+     :background "grey10" :foreground "white")
+    (t
+     :inherit error :inverse-video t))
+  "Basic face used for flash a face briefly."
+  :version "31.1"
+  :group 'basic-faces)
+
 (defface vertical-border
   '((((type tty)) :inherit mode-line-inactive))
   "Face used for vertical window dividers on ttys."
diff --git a/lisp/simple.el b/lisp/simple.el
index 7037158df8d..f2b94160cbd 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -9383,6 +9383,37 @@ auto-save-mode
   (and (< buffer-saved-size 0)
        (setq buffer-saved-size 0)))
 
+(defcustom flash-face-duration 0.05
+  "Time (in seconds) used for flash face duration."
+  :type 'float
+  :version "31.1")
+
+(defcustom flash-face-faces
+  '(mode-line-active)
+  "A list of faces which `flash-face-bell-function' will flash them."
+  :type 'float
+  :version "31.1")
+
+(defun flash-face-bell-function ()
+  "Flash a face or faces as ring a bell.
+Intended to be used in `ring-bell-function'."
+  (when-let* (flash-face-duration ; Ensure time is non-nil
+              (cookies (mapcar (lambda (f) (face-remap-add-relative f 'flash-face))
+                               flash-face-faces)))
+    (unwind-protect
+        (sit-for flash-face-duration)
+      (mapc #'face-remap-remove-relative cookies))))
+
+(defun flash-echo-area-bell-function ()
+  "Flash echo area as ring a bell.
+Intended to be used in `ring-bell-function'."
+  (message
+   (propertize "ring!"
+               'display `(space . (:align-to right))
+               'face 'flash-face))
+  (sit-for flash-face-duration)
+  (message ""))
+
 (defgroup paren-blinking nil
   "Blinking matching of parens and expressions."
   :prefix "blink-matching-"
-- 
2.49.0

-- 
                                          - E.G via GNU Emacs and Org.