\f
* Incompatible Lisp Changes in Emacs 28.1
+---
+** 'run-at-time' now tries harder to implement the t TIME parameter.
+If TIME is t, the timer runs at an integral multiple of REPEAT.
+(I.e., if given a REPEAT of 60, it'll run at 08:11:00, 08:12:00,
+08:13:00.) However, when a machine goes to sleep (or otherwise didn't
+get a time slot to run when the timer was scheduled), the timer would
+then fire every 60 seconds after the time the timer was fired. This
+has now changed, and the timer code now recomputes the integral
+multiple every time it runs, which means that if the laptop wakes at
+08:16:43, it'll fire at that time, but then at 08:17:00, 08:18:00...
+
---
** 'parse-partial-sexp' now signals an error if TO is smaller than FROM.
Previously this would lead to the function interpreting FROM as TO and
(eval-when-compile (require 'cl-lib))
+;; If you change this structure, you also have to change `timerp'
+;; (below) and decode_timer in keyboard.c.
(cl-defstruct (timer
(:constructor nil)
(:copier nil)
repeat-delay
function args ;What to do when triggered.
idle-delay ;If non-nil, this is an idle-timer.
- psecs)
+ psecs
+ ;; A timer may be created with `t' as the TIME, which means that we
+ ;; want to run at specific integral multiples of `repeat-delay'. We
+ ;; then have to recompute this (because the machine may have gone to
+ ;; sleep, etc).
+ integral-multiple)
(defun timerp (object)
"Return t if OBJECT is a timer."
- (and (vectorp object) (= (length object) 9)))
+ (and (vectorp object) (= (length object) 10)))
(defsubst timer--check (timer)
(or (timerp timer) (signal 'wrong-type-argument (list #'timerp timer))))
(if (> repeats timer-max-repeats)
(timer-inc-time timer (* (timer--repeat-delay timer)
repeats)))))
+ ;; If we want integral multiples, we have to recompute
+ ;; the repetition.
+ (when (and (timer--integral-multiple timer)
+ (not (timer--idle-delay timer)))
+ (setf (timer--time timer)
+ (timer-next-integral-multiple-of-time
+ (current-time) (timer--repeat-delay timer))))
;; Place it back on the timer-list before running
;; timer--function, so it can cancel-timer itself.
(timer-activate timer t cell)
`cancel-timer'."
(interactive "sRun at time: \nNRepeat interval: \naFunction: ")
- (or (null repeat)
- (and (numberp repeat) (< 0 repeat))
- (error "Invalid repetition interval"))
+ (when (and repeat
+ (numberp repeat)
+ (< repeat 0))
+ (error "Invalid repetition interval"))
- ;; Special case: nil means "now" and is useful when repeating.
- (if (null time)
+ (let ((timer (timer-create)))
+ ;; Special case: nil means "now" and is useful when repeating.
+ (unless time
(setq time (current-time)))
- ;; Special case: t means the next integral multiple of REPEAT.
- (if (and (eq time t) repeat)
- (setq time (timer-next-integral-multiple-of-time (current-time) repeat)))
+ ;; Special case: t means the next integral multiple of REPEAT.
+ (when (and (eq time t) repeat)
+ (setq time (timer-next-integral-multiple-of-time (current-time) repeat))
+ (setf (timer--integral-multiple timer) t))
- ;; Handle numbers as relative times in seconds.
- (if (numberp time)
+ ;; Handle numbers as relative times in seconds.
+ (when (numberp time)
(setq time (timer-relative-time nil time)))
- ;; Handle relative times like "2 hours 35 minutes"
- (if (stringp time)
- (let ((secs (timer-duration time)))
- (if secs
- (setq time (timer-relative-time nil secs)))))
-
- ;; Handle "11:23pm" and the like. Interpret it as meaning today
- ;; which admittedly is rather stupid if we have passed that time
- ;; already. (Though only Emacs hackers hack Emacs at that time.)
- (if (stringp time)
- (progn
- (require 'diary-lib)
- (let ((hhmm (diary-entry-time time))
- (now (decode-time)))
- (if (>= hhmm 0)
- (setq time
- (encode-time 0 (% hhmm 100) (/ hhmm 100)
- (decoded-time-day now)
- (decoded-time-month now)
- (decoded-time-year now)
- (decoded-time-zone now)))))))
+ ;; Handle relative times like "2 hours 35 minutes".
+ (when (stringp time)
+ (when-let ((secs (timer-duration time)))
+ (setq time (timer-relative-time nil secs))))
+
+ ;; Handle "11:23pm" and the like. Interpret it as meaning today
+ ;; which admittedly is rather stupid if we have passed that time
+ ;; already. (Though only Emacs hackers hack Emacs at that time.)
+ (when (stringp time)
+ (require 'diary-lib)
+ (let ((hhmm (diary-entry-time time))
+ (now (decode-time)))
+ (when (>= hhmm 0)
+ (setq time (encode-time 0 (% hhmm 100) (/ hhmm 100)
+ (decoded-time-day now)
+ (decoded-time-month now)
+ (decoded-time-year now)
+ (decoded-time-zone now))))))
- (let ((timer (timer-create)))
(timer-set-time timer time repeat)
(timer-set-function timer function args)
(timer-activate timer)