;;; kj-timer.el -- countdown timer -*- lexical-binding: t -*- (defvar kj/current-timer nil) (defvar kj/roster nil) (defvar kj/timer-seconds) (defun kj/display-timer (seconds current &optional next) (with-current-buffer (get-buffer-create "*timer*") (let* ((stopped (< seconds 0)) (seconds (if stopped 0 (truncate seconds)))) (erase-buffer) (insert (propertize (format "%02d:%02d" (/ seconds 60) (% seconds 60)) 'face (append '(:height 1000) (when stopped '(:inherit 'org-imminent-deadline)))) "\n" (propertize (concat current " ยท " (if next next "done")) 'face '(:foreground "gray" :height 500))) (set-left-margin (point-min) (point-max) 10)))) (defun kj/run-single-timer (seconds current next) (let* ((stop-time (+ (float-time) seconds)) (display-fn (lambda () (kj/display-timer (- stop-time (float-time)) current next)))) (setq kj/current-timer (run-at-time t 0.5 display-fn)))) (defun kj/timer (seconds &rest roster) (with-current-buffer (get-buffer-create "*timer*") (display-buffer (current-buffer)) (setq cursor-type nil) (use-local-map (define-keymap "SPC" 'kj/timer-next)) (setq kj/roster roster) (setq kj/timer-seconds seconds) (kj/timer-next))) (defun kj/timer-next () (interactive) (unless kj/roster (error "The timers are done.")) (when kj/current-timer (cancel-timer kj/current-timer)) (kj/run-single-timer kj/timer-seconds (nth 0 kj/roster) (nth 1 kj/roster)) (setq kj/roster (cdr kj/roster))) (provide 'kj-timer)