type-break.el 10.8 KB
Newer Older
1
;;; type-break.el --- take breaks from typing at appropriate intervals
2 3 4 5 6 7 8

;;; Copyright (C) 1994 Roland McGrath
;;; Copyright (C) 1994 Noah S. Friedman

;;; Author: Noah Friedman <friedman@prep.ai.mit.edu>
;;;         Roland McGrath <roland@prep.ai.mit.edu>
;;; Maintainer: friedman@prep.ai.mit.edu
9 10
;;; Keywords: extensions, timers
;;; Status: works in GNU Emacs 19
11 12
;;; Created: 1994-07-13

13 14 15 16 17
;;; LCD Archive Entry:
;;; type-break|Noah Friedman|friedman@prep.ai.mit.edu|
;;; take breaks from typing at appropriate intervals|
;;; $Date$|$Revision$||

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
;;; $Id$

;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, you can either send email to this
;;; program's maintainer or write to: The Free Software Foundation,
;;; Inc.; 675 Massachusetts Avenue; Cambridge, MA 02139, USA.

;;; Commentary:
;;; Code:


Roland McGrath's avatar
Roland McGrath committed
39 40 41
(require 'timer)

;;;###autoload
42 43 44 45 46 47 48
(defvar type-break-mode t
  "*Non-`nil' means typing break mode is enabled.
See the docstring for the `type-break-mode' command for more information.")

;;;###autoload
(defvar type-break-interval (* 60 60)
  "*Number of seconds between scheduled typing breaks.")
49 50

;;;###autoload
51
(defvar type-break-query-interval 60
52 53 54 55 56
  "*Number of seconds between queries to take a break, if put off.
The user will continue to be prompted at this interval until he or she
finally submits to taking a typing break.")

;;;###autoload
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
(defvar type-break-keystroke-threshold
  ;; Assuming average typing speed is 45wpm and the average word length is
  ;; about 5 letters, default upper threshold to the average number of
  ;; keystrokes one is likely to type in a break interval.  That way if the
  ;; user goes through a furious burst of typing activity, cause a typing
  ;; break to be required sooner than originally scheduled.
  ;; Conversely, the minimum threshold should be about a quarter of this.
  (let* ((wpm 45)
         (avg-word-length 5)
         (upper (* wpm avg-word-length (/ type-break-interval 60)))
         (lower (/ upper 4)))
    (cons lower upper))
  "*Upper and lower bound on number of keystrokes for considering typing break.

This structure is a pair of numbers.  The first number is the minimum
number of keystrokes that must have been entered since the last typing
break before considering another one, even if the scheduled time has
elapsed; the break is simply rescheduled until later if the minimum
threshold hasn't been reached.

The second number is the maximum number of keystrokes that can be entered
before a typing break is requested immediately, pre-empting the originally
scheduled break.

Keys with bucky bits (shift, control, meta, etc) are counted as only one
keystroke even though they really require multiple keys to generate them.")
83 84
  
;;;###autoload
85 86 87 88 89
(defvar type-break-query-function 'yes-or-no-p
  "*Function to use for making query for a typing break.
It should take a string as an argument, the prompt.
Usually this should be set to `yes-or-no-p' or `y-or-n-p'.")

90
(defvar type-break-demo-function-vector
91
  [type-break-demo-life type-break-demo-hanoi]
92 93 94
  "*Vector consisting of functions to run as demos during typing breaks.
When a typing break begins, one of these functions is selected randomly
to have emacs do something interesting.
Roland McGrath's avatar
Roland McGrath committed
95

96 97
Any function in this vector should start a demo which ceases as soon as a
key is pressed.")
Roland McGrath's avatar
Roland McGrath committed
98

99
;; These are internal variables.  Do not set them yourself.
Roland McGrath's avatar
Roland McGrath committed
100

101 102
;; Number of commands (roughly # of keystrokes) recorded since last break.
(defvar type-break-keystroke-count 0)
Roland McGrath's avatar
Roland McGrath committed
103

104 105
;; Non-nil when a scheduled typing break is due.
(defvar type-break-alarm-p nil)
106 107


108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
;;;###autoload
(defun type-break-mode (&optional prefix)
  "Enable or disable typing-break mode.
This is a minor mode, but it is global to all buffers by default.

When this mode is enabled, the user is encouraged to take typing breaks at
appropriate intervals; either after a specified amount of time or when the
user has exceeded a keystroke threshold.  When the time arrives, the user
is asked to take a break.  If the user refuses at that time, emacs will ask
again in a short period of time.  The idea is to give the user enough time
to find a good breaking point in his or her work, but be sufficiently
annoying to discourage putting typing breaks off indefinitely.

Calling this command with no prefix argument toggles this mode.
A negative prefix argument disables this mode.
A non-negative prefix argument or any other non-`nil' argument enables it.

The user may enable or disable this mode by setting the variable of the
same name, though setting it in that way doesn't reschedule a break or
reset the keystroke counter.

When this function enables the mode, it schedules a break with
`type-break-schedule' to make sure one occurs (the user can call that
command to reschedule the break at any time).  It also initializes the
keystroke counter.

The variable `type-break-interval' specifies the number of seconds to
schedule between regular typing breaks.  This variable doesn't directly
affect the time schedule; it simply provides a default for the
`type-break-schedule' command.

The variable `type-break-query-interval' specifies the number of seconds to
schedule between repeated queries for breaks when the user answers \"no\"
to the previous query.

The variable `type-break-keystroke-theshold' is used to determine the
thresholds at which typing breaks should be considered.

The variable `type-break-query-function' should contain a function (or the
symbolic name of a function) to be used to query the user for typing
breaks."
  (interactive "P")
  ;; make sure it's there.
  (add-hook 'post-command-hook 'type-break-check 'append)

  (cond
   ((null prefix)
    (setq type-break-mode (not type-break-mode)))
   ((numberp (prefix-numeric-value prefix))
    (setq type-break-mode (>= (prefix-numeric-value prefix) 0)))
   (prefix
    (setq type-break-mode t))
   (t
    (setq type-break-mode nil)))

  (cond
   (type-break-mode
    (setq type-break-keystroke-count 0)
    (type-break-schedule)
    (and (interactive-p)
         (message "type-break-mode is enabled and reset")))
   ((interactive-p)
    (message "type-break-mode is disabled")))

  type-break-mode)

Roland McGrath's avatar
Roland McGrath committed
174
;;;###autoload
175 176 177
(defun type-break ()
  "Take a typing break.

178 179
During the break, a demo selected from the functions listed in
`type-break-demo-function-vector' is run.
180

181 182 183
After the typing break is finished, the next break is scheduled
as per the function `type-break-schedule', and the keystroke counter is
reset."
Roland McGrath's avatar
Roland McGrath committed
184 185
  (interactive)
  (save-window-excursion
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    ;; Eat the screen.
    (and (eq (selected-window) (minibuffer-window))
         (other-window 1))
    (delete-other-windows)
    (scroll-right (window-width))
    (message "Press any key to finish typing break.")

    (random t)
    (let* ((len (length type-break-demo-function-vector))
           (idx (random len))
           (fn (aref type-break-demo-function-vector idx)))
      (condition-case ()
          (funcall fn)
        (error nil)))

    (setq type-break-keystroke-count 0)
    (type-break-schedule)))
Roland McGrath's avatar
Roland McGrath committed
203

204

Roland McGrath's avatar
Roland McGrath committed
205
;;;###autoload
206
(defun type-break-schedule (&optional time)
207 208
  "Schedule a typing break for TIME seconds from now.
If time is not specified, default to `type-break-interval'."
Roland McGrath's avatar
Roland McGrath committed
209 210
  (interactive (list (and current-prefix-arg
			  (prefix-numeric-value current-prefix-arg))))
211 212
  (or time (setq time type-break-interval))
  ;; Remove any old scheduled break
213 214
  (type-break-cancel-schedule)
  (run-at-time time nil 'type-break-alarm))
Roland McGrath's avatar
Roland McGrath committed
215

216 217 218 219 220
(defun type-break-cancel-schedule ()
  "Cancel scheduled typing breaks.
This does not prevent queries for typing breaks when the keystroke
threshold has been reached; to turn off typing breaks altogether, turn off
type-break-mode."
Roland McGrath's avatar
Roland McGrath committed
221
  (interactive)
222
  (let ((timer-dont-exit t))
223 224
    (cancel-function-timers 'type-break-alarm))
  (setq type-break-alarm-p nil))
225

226 227 228
(defun type-break-alarm ()
  "This function is run when a scheduled typing break is due."
  (setq type-break-alarm-p t))
229 230

(defun type-break-check ()
231 232 233 234
  "Ask to take a typing break if appropriate.
This may be the case either because the scheduled time has come \(and the
minimum keystroke threshold has been reached\) or because the maximum
keystroke threshold has been exceeded."
235
  (cond
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
   (type-break-mode
    (let* ((pair (and (consp type-break-keystroke-threshold)
                      type-break-keystroke-threshold))
           (min-threshold (car pair))
           (max-threshold (cdr pair)))
      (and pair
           (stringp (this-command-keys))
           (setq type-break-keystroke-count
                 (+ type-break-keystroke-count (length (this-command-keys)))))
      (cond
       ((input-pending-p))
       ((and (numberp max-threshold)
             (> type-break-keystroke-count max-threshold))
        (setq type-break-keystroke-count 0)
        (type-break-query))
       (type-break-alarm-p
        (cond
         ((and (numberp min-threshold)
               (< type-break-keystroke-count min-threshold)))
         (t
          (setq type-break-keystroke-count 0)
          (type-break-query)))))))))

(defun type-break-query ()
  (condition-case ()
      (cond
       ((funcall type-break-query-function "Take a break from typing now? ")
        (type-break))
       (t
        (type-break-schedule type-break-query-interval)))
    (quit
     (type-break-schedule type-break-query-interval))))
268 269 270 271 272


;; This is a wrapper around hanoi that calls it with an arg large enough to
;; make the largest discs possible that will fit in the window.
;; Also, clean up the *Hanoi* buffer after we're done.
273
(defun type-break-demo-hanoi ()
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
  "Take a hanoiing typing break."
  (and (get-buffer "*Hanoi*")
       (kill-buffer "*Hanoi*"))
  (condition-case ()
      (progn
        (hanoi (/ (window-width) 8))
        ;; Wait for user to come back.
        (read-char)
        (kill-buffer "*Hanoi*"))
    (quit 
     (and (get-buffer "*Hanoi*")
          (kill-buffer "*Hanoi*")))))

;; This is a wrapper around life that calls it with a `sleep' arg to make
;; it run a little more leisurely.
;; Also, clean up the *Life* buffer after we're done.
290
(defun type-break-demo-life ()
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
  "Take a typing break and get a life."
  (and (get-buffer "*Life*")
       (kill-buffer "*Life*"))
  (condition-case ()
      (progn
        (life 3)
        ;; Wait for user to come back.
        (read-char)
        (kill-buffer "*Life*"))
    (quit 
     (and (get-buffer "*Life*")
          (kill-buffer "*Life*")))))


(provide 'type-break)

307
(type-break-mode t)
Roland McGrath's avatar
Roland McGrath committed
308

309
;;; type-break.el ends here