As part of my custom mode-line, I've included a feature that displays a countdown timer (timers are created using the chronos package) when it recognises that there is one running.

Works great, but the problem I have is that the mode-line only refreshes when I enter keypresses into Emacs. It will not update in the background (say I work in the browser for a bit but Emacs is still visible).

Is there any good way to force the mode-line to redisplay each second, so that the timer display stays up to date?

Screenshot of mode-line Screenshot

Relevant code This is appended to the format-mode-line list:

(if (>= (length chronos--timers-list) 2)
    (list " timer|" (propertize (my-timer-display) 'face 'my-red-face) "| ")

The my-timer-display decides whether or not to show the timer. I think this might be the best place to get Emacs to start forcing redisplays.

(defun my-timer-display ()
  "Return the lowest timer, avoiding the --now-- time, in the chronos timers list"
  (if (string= (chronos--message (nth 0 (chronos--sort-by-expiry))) chronos-now-message)
      (my-print-timer 1)
    (my-print-timer 0)))

The my-print-timer function referenced just returns the xth timer in the chronos timers list. I won't include it as I don't think it's relevant to the question.

2 Answers 2


I try not to use standard timers that repeat because they can affect performance while typing or scrolling. An idle-timer only fires once each time an idle occurs. Therefore, I would suggest setting up a system that runs an idle timer while Emacs has focus and a standard timer when Emacs loses focus. Since that is outside the scope of the question, I will leave that to the O.P. to investigate ...

To try out the following example, start with emacs -Q, open two or more windows and evaluate the following two lines of code. E.g., place both lines of code in the *scratch* buffer and type M-x eval-buffer.

The extra set of parentheses used in the example as part of the mode-line-format is not necessary for this example, but is helpful to remind us that we can include additional components of the mode-line (as elements of the list) if so desired.

Note that this example uses the optional argument for force-mode-line-update to operate on all visible windows.

(setq-default mode-line-format '((:eval (format-time-string "%m/%d/%Y @ %1I:%M:%S %p"))))

(run-with-timer 0 1 #'(lambda () (force-mode-line-update t)))
  • After learning about timer.el from this answer decided to implement a solution like so: ` (progn` ` (cancel-function-timers 'force-mode-line-update)` ` (run-at-time 0.5 nil '(lambda () (force-mode-line-update t)))` ` (list " timer|" (propertize (my-timer-display) 'face 'my-red-face) "| "))` This fits in the true condition for the mode-line-format list.
    – jamesmaj
    Commented Jan 2, 2019 at 4:58
  • Unsure on stackoverflow ettiquite. Should I mark this as the answer, or should I create my own detailed answer and give credit to this answer?
    – jamesmaj
    Commented Jan 2, 2019 at 5:06
  • I will not be saddened if you write-up your own alternative answer. If you think that the question title and/or body need to be clarified so that this thread helps future forum searches, then please feel free to do so.
    – lawlist
    Commented Jan 2, 2019 at 5:07

Thanks to @lawlist for directing me to timer.el. A solution I implemented uses timer.el's run-at-time.

I replaced the format-line-list item with:

(if (< (length chronos--timers-list) 2)
  (cancel-function-timers 'force-mode-line-update)
  (run-at-time 0.5 nil '(lambda () (force-mode-line-update t)))
  (list " timer|" (propertize (my-timer-display) 'face 'my-red-face) "| ")))

This means that if a timer is running, I ask Emacs to update the mode-line in another 0.5s. This sets a timer. To prevent timers building up if I trigger multiple mode-line refreshes within the 0.5s interval I delete any previous timers first (cancel-function-timers).

I found that if I used a 1s interval, it would sometimes skip a second if I was typing in Emacs. Reducing to 0.5s makes it act/feel smoother.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.