Use line-end-position rather than end-of-line, etc.
[bpt/emacs.git] / lisp / type-break.el
CommitLineData
be8d412c 1;;; type-break.el --- encourage rests from typing at appropriate intervals
458401b6 2
5ed619e0
GM
3;; Copyright (C) 1994, 1995, 1997, 2000, 2001, 2002, 2003, 2004, 2005,
4;; 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
99c0333b 5
f6cafb3e
NF
6;; Author: Noah Friedman
7;; Maintainer: Noah Friedman <friedman@splode.com>
99c0333b 8;; Keywords: extensions, timers
846f6dd9 9;; Status: Works in GNU Emacs 19.25 or later, some versions of XEmacs
99c0333b 10;; Created: 1994-07-13
846f6dd9 11
99c0333b
NF
12;; This file is part of GNU Emacs.
13
eb3fa2cf 14;; GNU Emacs is free software: you can redistribute it and/or modify
99c0333b 15;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
99c0333b
NF
18
19;; GNU Emacs is distributed in the hope that it will be useful,
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
23
24;; You should have received a copy of the GNU General Public License
eb3fa2cf 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
458401b6
NF
26
27;;; Commentary:
be8d412c 28
b4dc9e6a
NF
29;; The docstring for the function `type-break-mode' summarizes most of the
30;; details of the interface.
be8d412c 31
27cd478d 32;; This package relies on the assumption that you live entirely in Emacs,
b4dc9e6a 33;; as the author does. If that's not the case for you (e.g. you often
27cd478d
EZ
34;; suspend Emacs or work in other windows) then this won't help very much;
35;; it will depend on just how often you switch back to Emacs. At the very
b4dc9e6a
NF
36;; least, you will want to turn off the keystroke thresholds and rest
37;; interval tracking.
be8d412c 38
846f6dd9
NF
39;; If you prefer not to be queried about taking breaks, but instead just
40;; want to be reminded, do the following:
41;;
42;; (setq type-break-query-mode nil)
43;;
44;; Or call the command `type-break-query-mode' with a negative prefix
45;; argument.
46
47;; If you find echo area messages annoying and would prefer to see messages
48;; in the mode line instead, do M-x type-break-mode-line-message-mode
49;; or set the variable of the same name to `t'.
b4dc9e6a
NF
50
51;; This program can truly cons up a storm because of all the calls to
52;; `current-time' (which always returns 3 fresh conses). I'm dismayed by
53;; this, but I think the health of my hands is far more important than a
54;; few pages of virtual memory.
55
846f6dd9
NF
56;; This program has no hope of working in Emacs 18.
57
b4dc9e6a
NF
58;; This package was inspired by Roland McGrath's hanoi-break.el.
59;; Several people contributed feedback and ideas, including
5762abec 60;; Roland McGrath <roland@gnu.org>
ad953485 61;; Kleanthes Koniaris <kgk@koniaris.com>
5762abec 62;; Mark Ashton <mpashton@gnu.org>
b4dc9e6a 63;; Matt Wilding <wilding@cli.com>
846f6dd9 64;; Robert S. Boyer <boyer@cs.utexas.edu>
be8d412c 65
458401b6
NF
66;;; Code:
67
68\f
104221a0
SE
69(defgroup type-break nil
70 "Encourage the user to take a rest from typing at suitable intervals."
71 :prefix "type-break"
72 :group 'keyboard)
73
622aca7c 74;;;###autoload
104221a0 75(defcustom type-break-mode nil
511a0719 76 "Toggle typing break mode.
104221a0 77See the docstring for the `type-break-mode' command for more information.
4de26885
DL
78Setting this variable directly does not take effect;
79use either \\[customize] or the function `type-break-mode'."
104221a0
SE
80 :set (lambda (symbol value)
81 (type-break-mode (if value 1 -1)))
e18b0c51 82 :initialize 'custom-initialize-default
104221a0
SE
83 :type 'boolean
84 :group 'type-break
e18b0c51 85 :require 'type-break)
4cf64c15
NF
86
87;;;###autoload
104221a0 88(defcustom type-break-interval (* 60 60)
9201cc28 89 "Number of seconds between scheduled typing breaks."
104221a0
SE
90 :type 'integer
91 :group 'type-break)
458401b6 92
be8d412c 93;;;###autoload
104221a0 94(defcustom type-break-good-rest-interval (/ type-break-interval 6)
9201cc28 95 "Number of seconds of idle time considered to be an adequate typing rest.
be8d412c 96
27cd478d 97When this variable is non-nil, Emacs checks the idle time between
cc669dd8 98keystrokes. If this idle time is long enough to be considered a \"good\"
be8d412c
NF
99rest from typing, then the next typing break is simply rescheduled for later.
100
cc669dd8 101If a break is interrupted before this much time elapses, the user will be
104221a0
SE
102asked whether or not really to interrupt the break."
103 :type 'integer
104 :group 'type-break)
be8d412c 105
27cd478d
EZ
106;;;###autoload
107(defcustom type-break-good-break-interval nil
9201cc28 108 "Number of seconds considered to be an adequate explicit typing rest.
27cd478d
EZ
109
110When this variable is non-nil, its value is considered to be a \"good\"
111length (in seconds) for a break initiated by the command `type-break',
112overriding `type-break-good-rest-interval'. This provides querying of
113break interruptions when `type-break-good-rest-interval' is nil."
114 :type 'integer
115 :group 'type-break)
116
458401b6 117;;;###autoload
104221a0 118(defcustom type-break-keystroke-threshold
cc669dd8 119 ;; Assuming typing speed is 35wpm (on the average, do you really
f486195c
NF
120 ;; type more than that in a minute? I spend a lot of time reading mail
121 ;; and simply studying code in buffers) and average word length is
4cf64c15
NF
122 ;; about 5 letters, default upper threshold to the average number of
123 ;; keystrokes one is likely to type in a break interval. That way if the
124 ;; user goes through a furious burst of typing activity, cause a typing
125 ;; break to be required sooner than originally scheduled.
f486195c 126 ;; Conversely, the minimum threshold should be about a fifth of this.
cc669dd8 127 (let* ((wpm 35)
4cf64c15
NF
128 (avg-word-length 5)
129 (upper (* wpm avg-word-length (/ type-break-interval 60)))
f486195c 130 (lower (/ upper 5)))
4cf64c15 131 (cons lower upper))
9201cc28 132 "Upper and lower bound on number of keystrokes for considering typing break.
104221a0 133This structure is a pair of numbers (MIN . MAX).
4cf64c15 134
be8d412c
NF
135The first number is the minimum number of keystrokes that must have been
136entered since the last typing break before considering another one, even if
137the scheduled time has elapsed; the break is simply rescheduled until later
138if the minimum threshold hasn't been reached. If this first value is nil,
139then there is no minimum threshold; as soon as the scheduled time has
140elapsed, the user will always be queried.
4cf64c15
NF
141
142The second number is the maximum number of keystrokes that can be entered
143before a typing break is requested immediately, pre-empting the originally
be8d412c
NF
144scheduled break. If this second value is nil, then no pre-emptive breaks
145will occur; only scheduled ones will.
4cf64c15
NF
146
147Keys with bucky bits (shift, control, meta, etc) are counted as only one
b4dc9e6a
NF
148keystroke even though they really require multiple keys to generate them.
149
150The command `type-break-guesstimate-keystroke-threshold' can be used to
104221a0
SE
151guess a reasonably good pair of values for this variable."
152 :type 'sexp
153 :group 'type-break)
b4dc9e6a 154
ad953485 155(defcustom type-break-query-function 'yes-or-no-p
9201cc28 156 "Function to use for making query for a typing break.
b4dc9e6a
NF
157It should take a string as an argument, the prompt.
158Usually this should be set to `yes-or-no-p' or `y-or-n-p'.
159
ee6258ff 160To avoid being queried at all, set `type-break-query-mode' to nil."
ad953485
NF
161 :type '(radio function
162 (function-item yes-or-no-p)
163 (function-item y-or-n-p))
164 :group 'type-break)
b4dc9e6a 165
104221a0 166(defcustom type-break-query-interval 60
9201cc28 167 "Number of seconds between queries to take a break, if put off.
b4dc9e6a 168The user will continue to be prompted at this interval until he or she
104221a0
SE
169finally submits to taking a typing break."
170 :type 'integer
171 :group 'type-break)
be8d412c 172
104221a0 173(defcustom type-break-time-warning-intervals '(300 120 60 30)
9201cc28 174 "List of time intervals for warnings about upcoming typing break.
e7b20417 175At each of the intervals (specified in seconds) away from a scheduled
104221a0
SE
176typing break, print a warning in the echo area."
177 :type '(repeat integer)
178 :group 'type-break)
e7b20417 179
104221a0 180(defcustom type-break-keystroke-warning-intervals '(300 200 100 50)
9201cc28 181 "List of keystroke measurements for warnings about upcoming typing break.
e7b20417
NF
182At each of the intervals (specified in keystrokes) away from the upper
183keystroke threshold, print a warning in the echo area.
184If either this variable or the upper threshold is set, then no warnings
104221a0
SE
185will occur."
186 :type '(repeat integer)
187 :group 'type-break)
188
104221a0 189(defcustom type-break-warning-repeat 40
9201cc28 190 "Number of keystrokes for which warnings should be repeated.
e7b20417 191That is, for each of this many keystrokes the warning is redisplayed
104221a0
SE
192in the echo area to make sure it's really seen."
193 :type 'integer
194 :group 'type-break)
e7b20417 195
ad953485 196(defcustom type-break-time-stamp-format "[%H:%M] "
9201cc28 197 "Timestamp format used to prefix messages.
ad953485
NF
198Format specifiers are as used by `format-time-string'."
199 :type 'string
200 :group 'type-break)
201
104221a0 202(defcustom type-break-demo-functions
e7b20417 203 '(type-break-demo-boring type-break-demo-life type-break-demo-hanoi)
9201cc28 204 "List of functions to consider running as demos during typing breaks.
458401b6 205When a typing break begins, one of these functions is selected randomly
27cd478d 206to have Emacs do something interesting.
622aca7c 207
cc669dd8 208Any function in this list should start a demo which ceases as soon as a
104221a0
SE
209key is pressed."
210 :type '(repeat function)
211 :group 'type-break)
622aca7c 212
27cd478d 213(defcustom type-break-demo-boring-stats nil
9201cc28 214 "Show word per minute and keystroke figures in the Boring demo."
27cd478d
EZ
215 :type 'boolean
216 :group 'type-break)
217
218(defcustom type-break-terse-messages nil
9201cc28 219 "Use slightly terser messages."
27cd478d
EZ
220 :type 'boolean
221 :group 'type-break)
222
223(defcustom type-break-file-name (convert-standard-filename "~/.type-break")
9201cc28 224 "Name of file used to save state across sessions.
a7ed85f0 225If this is nil, no data will be saved across sessions."
27cd478d
EZ
226 :type 'file
227 :group 'type-break)
228
846f6dd9 229(defvar type-break-post-command-hook '(type-break-check)
27cd478d 230 "Hook run indirectly by `post-command-hook' for typing break functions.
b4dc9e6a
NF
231This is not really intended to be set by the user, but it's probably
232harmless to do so. Mainly it is used by various parts of the typing break
233program to delay actions until after the user has completed some command.
234It exists because `post-command-hook' itself is inaccessible while its
235functions are being run, and some type-break--related functions want to
236remove themselves after running.")
e7b20417 237
846f6dd9
NF
238\f
239;; Mode line frobs
240
846f6dd9
NF
241(defvar type-break-mode-line-format
242 '(type-break-mode-line-message-mode
243 (""
244 type-break-mode-line-break-message
245 type-break-mode-line-warning))
246 "*Format of messages in the mode line concerning typing breaks.")
247
248(defvar type-break-mode-line-break-message
249 '(type-break-mode-line-break-message-p
250 type-break-mode-line-break-string))
251
252(defvar type-break-mode-line-break-message-p nil)
ad953485 253(defvar type-break-mode-line-break-string " *** TAKE A TYPING BREAK NOW ***")
846f6dd9
NF
254
255(defvar type-break-mode-line-warning
256 '(type-break-mode-line-break-message-p
257 ("")
258 (type-break-warning-countdown-string
ad953485
NF
259 (" *** "
260 "Break in "
846f6dd9
NF
261 type-break-warning-countdown-string
262 " "
263 type-break-warning-countdown-string-type
264 "***"))))
265
266(defvar type-break-warning-countdown-string nil
267 "If non-nil, this is a countdown for the next typing break.
268
269This variable, in conjunction with `type-break-warning-countdown-string-type'
b688ed44 270\(which indicates whether this value is a number of keystrokes or seconds)
27cd478d 271is installed in `mode-line-format' to notify of imminent typing breaks.")
846f6dd9
NF
272
273(defvar type-break-warning-countdown-string-type nil
274 "Indicates the unit type of `type-break-warning-countdown-string'.
275It will be either \"seconds\" or \"keystrokes\".")
276
277\f
4cf64c15 278;; These are internal variables. Do not set them yourself.
622aca7c 279
defa7346 280(defvar type-break-alarm-p nil)
be8d412c 281(defvar type-break-keystroke-count 0)
be8d412c
NF
282(defvar type-break-time-last-break nil)
283(defvar type-break-time-next-break nil)
284(defvar type-break-time-last-command (current-time))
e7b20417
NF
285(defvar type-break-current-time-warning-interval nil)
286(defvar type-break-current-keystroke-warning-interval nil)
287(defvar type-break-time-warning-count 0)
288(defvar type-break-keystroke-warning-count 0)
27cd478d
EZ
289(defvar type-break-interval-start nil)
290
cc669dd8 291\f
4cf64c15
NF
292;;;###autoload
293(defun type-break-mode (&optional prefix)
294 "Enable or disable typing-break mode.
295This is a minor mode, but it is global to all buffers by default.
296
297When this mode is enabled, the user is encouraged to take typing breaks at
298appropriate intervals; either after a specified amount of time or when the
299user has exceeded a keystroke threshold. When the time arrives, the user
27cd478d 300is asked to take a break. If the user refuses at that time, Emacs will ask
4cf64c15
NF
301again in a short period of time. The idea is to give the user enough time
302to find a good breaking point in his or her work, but be sufficiently
303annoying to discourage putting typing breaks off indefinitely.
304
4cf64c15 305A negative prefix argument disables this mode.
cc669dd8 306No argument or any non-negative argument enables it.
4cf64c15
NF
307
308The user may enable or disable this mode by setting the variable of the
309same name, though setting it in that way doesn't reschedule a break or
310reset the keystroke counter.
311
be8d412c
NF
312If the mode was previously disabled and is enabled as a consequence of
313calling this function, it schedules a break with `type-break-schedule' to
314make sure one occurs (the user can call that command to reschedule the
315break at any time). It also initializes the keystroke counter.
4cf64c15
NF
316
317The variable `type-break-interval' specifies the number of seconds to
318schedule between regular typing breaks. This variable doesn't directly
319affect the time schedule; it simply provides a default for the
320`type-break-schedule' command.
321
cc669dd8
NF
322If set, the variable `type-break-good-rest-interval' specifies the minimum
323amount of time which is considered a reasonable typing break. Whenever
324that time has elapsed, typing breaks are automatically rescheduled for
27cd478d 325later even if Emacs didn't prompt you to take one first. Also, if a break
cc669dd8 326is ended before this much time has elapsed, the user will be asked whether
27cd478d
EZ
327or not to continue. A nil value for this variable prevents automatic
328break rescheduling, making `type-break-interval' an upper bound on the time
329between breaks. In this case breaks will be prompted for as usual before
330the upper bound if the keystroke threshold is reached.
331
332If `type-break-good-rest-interval' is nil and
333`type-break-good-break-interval' is set, then confirmation is required to
334interrupt a break before `type-break-good-break-interval' seconds
335have passed. This provides for an upper bound on the time between breaks
336together with confirmation of interruptions to these breaks.
be8d412c
NF
337
338The variable `type-break-keystroke-threshold' is used to determine the
339thresholds at which typing breaks should be considered. You can use
b4dc9e6a 340the command `type-break-guesstimate-keystroke-threshold' to try to
be8d412c 341approximate good values for this.
4cf64c15 342
b4dc9e6a
NF
343There are several variables that affect how or when warning messages about
344imminent typing breaks are displayed. They include:
345
2157be68
RS
346 `type-break-mode-line-message-mode'
347 `type-break-time-warning-intervals'
348 `type-break-keystroke-warning-intervals'
349 `type-break-warning-repeat'
350 `type-break-warning-countdown-string'
351 `type-break-warning-countdown-string-type'
b4dc9e6a 352
846f6dd9
NF
353There are several variables that affect if, how, and when queries to begin
354a typing break occur. They include:
b4dc9e6a 355
2157be68
RS
356 `type-break-query-mode'
357 `type-break-query-function'
358 `type-break-query-interval'
b4dc9e6a 359
27cd478d
EZ
360The command `type-break-statistics' prints interesting things.
361
362Finally, a file (named `type-break-file-name') is used to store information
363across Emacs sessions. This provides recovery of the break status between
364sessions and after a crash. Manual changes to the file may result in
365problems."
4cf64c15 366 (interactive "P")
846f6dd9 367 (type-break-check-post-command-hook)
4cf64c15 368
be8d412c 369 (let ((already-enabled type-break-mode))
cc669dd8 370 (setq type-break-mode (>= (prefix-numeric-value prefix) 0))
be8d412c
NF
371
372 (cond
373 ((and already-enabled type-break-mode)
32226619 374 (and (called-interactively-p 'interactive)
2157be68 375 (message "Type Break mode is already enabled")))
be8d412c 376 (type-break-mode
cc7fe910
EZ
377 (when type-break-file-name
378 (with-current-buffer (find-file-noselect type-break-file-name 'nowarn)
379 (setq buffer-save-without-query t)))
1b8d0755 380
846f6dd9
NF
381 (or global-mode-string
382 (setq global-mode-string '("")))
7cd3279a
RS
383 (or (assq 'type-break-mode-line-message-mode
384 minor-mode-alist)
385 (setq minor-mode-alist
386 (cons type-break-mode-line-format
387 minor-mode-alist)))
e7b20417 388 (type-break-keystroke-reset)
846f6dd9 389 (type-break-mode-line-countdown-or-break nil)
27cd478d 390
a7ed85f0
EZ
391 (setq type-break-time-last-break
392 (or (type-break-get-previous-time)
393 (current-time)))
27cd478d
EZ
394
395 ;; schedule according to break time from session file
396 (type-break-schedule
397 (let (diff)
398 (if (and type-break-time-last-break
399 (< (setq diff (type-break-time-difference
400 type-break-time-last-break
401 (current-time)))
402 type-break-interval))
403 ;; use the file's value
404 (progn
405 (setq type-break-keystroke-count
406 (type-break-get-previous-count))
407 ;; file the time, in case it was read from the auto-save file
408 (type-break-file-time type-break-interval-start)
409 (setq type-break-interval-start type-break-time-last-break)
410 (- type-break-interval diff))
411 ;; schedule from now
412 (setq type-break-interval-start (current-time))
413 (type-break-file-time type-break-interval-start)
414 type-break-interval))
415 type-break-interval-start
416 type-break-interval)
417
32226619 418 (and (called-interactively-p 'interactive)
27cd478d 419 (message "Type Break mode is enabled and set")))
846f6dd9
NF
420 (t
421 (type-break-keystroke-reset)
422 (type-break-mode-line-countdown-or-break nil)
423 (type-break-cancel-schedule)
27cd478d 424 (do-auto-save)
a7ed85f0
EZ
425 (when type-break-file-name
426 (with-current-buffer (find-file-noselect type-break-file-name
427 'nowarn)
428 (set-buffer-modified-p nil)
429 (unlock-buffer)
430 (kill-this-buffer)))
32226619 431 (and (called-interactively-p 'interactive)
2157be68 432 (message "Type Break mode is disabled")))))
4cf64c15
NF
433 type-break-mode)
434
56eb0904 435(define-minor-mode type-break-mode-line-message-mode
846f6dd9
NF
436 "Enable or disable warnings in the mode line about typing breaks.
437
27cd478d 438A negative PREFIX argument disables this mode.
846f6dd9
NF
439No argument or any non-negative argument enables it.
440
441The user may also enable or disable this mode simply by setting the
442variable of the same name.
443
444Variables controlling the display of messages in the mode line include:
445
2157be68
RS
446 `mode-line-format'
447 `global-mode-string'
448 `type-break-mode-line-break-message'
449 `type-break-mode-line-warning'"
56eb0904
SM
450 :global t)
451
452(define-minor-mode type-break-query-mode
846f6dd9
NF
453 "Enable or disable warnings in the mode line about typing breaks.
454
455When enabled, the user is periodically queried about whether to take a
456typing break at that moment. The function which does this query is
457specified by the variable `type-break-query-function'.
b4dc9e6a 458
27cd478d 459A negative PREFIX argument disables this mode.
b4dc9e6a
NF
460No argument or any non-negative argument enables it.
461
462The user may also enable or disable this mode simply by setting the
463variable of the same name."
56eb0904 464 :global t)
b4dc9e6a 465
846f6dd9 466\f
27cd478d
EZ
467;;; session file functions
468
469(defvar type-break-auto-save-file-name
470 (let ((buffer-file-name type-break-file-name))
471 (make-auto-save-file-name))
472 "Auto-save name of `type-break-file-name'.")
473
474(defun type-break-file-time (&optional time)
475 "File break time in `type-break-file-name', unless the file is locked."
a7ed85f0
EZ
476 (if (and type-break-file-name
477 (not (stringp (file-locked-p type-break-file-name))))
27cd478d
EZ
478 (with-current-buffer (find-file-noselect type-break-file-name
479 'nowarn)
480 (let ((inhibit-read-only t))
481 (erase-buffer)
482 (insert (format "%s\n\n" (or time type-break-interval-start)))
483 ;; file saving is left to auto-save
484 ))))
485
486(defun type-break-file-keystroke-count ()
487 "File keystroke count in `type-break-file-name', unless the file is locked."
a7ed85f0
EZ
488 (if (and type-break-file-name
489 (not (stringp (file-locked-p type-break-file-name))))
cf8a2dae
RS
490 ;; Prevent deactivation of the mark in some other buffer.
491 (let (deactivate-mark)
492 (with-current-buffer (find-file-noselect type-break-file-name
493 'nowarn)
494 (save-excursion
495 (let ((inhibit-read-only t))
496 (goto-char (point-min))
497 (forward-line)
5ed619e0 498 (delete-region (point) (line-end-position))
cf8a2dae
RS
499 (insert (format "%s" type-break-keystroke-count))
500 ;; file saving is left to auto-save
501 ))))))
27cd478d
EZ
502
503(defun timep (time)
504 "If TIME is in the format returned by `current-time' then
505return TIME, else return nil."
506 (and (listp time)
507 (eq (length time) 3)
508 (integerp (car time))
509 (integerp (nth 1 time))
510 (integerp (nth 2 time))
511 time))
512
513(defun type-break-choose-file ()
514 "Return file to read from."
515 (cond
a7ed85f0
EZ
516 ((not type-break-file-name)
517 nil)
27cd478d
EZ
518 ((and (file-exists-p type-break-auto-save-file-name)
519 (file-readable-p type-break-auto-save-file-name))
520 type-break-auto-save-file-name)
521 ((and (file-exists-p type-break-file-name)
522 (file-readable-p type-break-file-name))
523 type-break-file-name)
524 (t nil)))
525
526(defun type-break-get-previous-time ()
527 "Get previous break time from `type-break-file-name'.
528Returns nil if the file is missing or if the time breaks with the
529`current-time' format."
530 (let ((file (type-break-choose-file)))
531 (if file
532 (timep ;; returns expected format, else nil
533 (with-current-buffer (find-file-noselect file 'nowarn)
dad757bc
RS
534 (condition-case nil
535 (save-excursion
536 (goto-char (point-min))
537 (read (current-buffer)))
538 (end-of-file
539 (error "End of file in `%s'" file))))))))
27cd478d
EZ
540
541(defun type-break-get-previous-count ()
542 "Get previous keystroke count from `type-break-file-name'.
543Return 0 if the file is missing or if the form read is not an
544integer."
545 (let ((file (type-break-choose-file)))
546 (if (and file
547 (integerp
548 (setq file
549 (with-current-buffer
550 (find-file-noselect file 'nowarn)
7ab2e82f
RS
551 (condition-case nil
552 (save-excursion
553 (goto-char (point-min))
554 (forward-line 1)
555 (read (current-buffer)))
556 (end-of-file
557 (error "End of file in `%s'" file)))))))
27cd478d
EZ
558 file
559 0)))
560
561\f
622aca7c 562;;;###autoload
458401b6
NF
563(defun type-break ()
564 "Take a typing break.
565
4cf64c15 566During the break, a demo selected from the functions listed in
cc669dd8 567`type-break-demo-functions' is run.
458401b6 568
4cf64c15 569After the typing break is finished, the next break is scheduled
cc669dd8 570as per the function `type-break-schedule'."
622aca7c 571 (interactive)
ad953485 572 (do-auto-save)
e7b20417 573 (type-break-cancel-schedule)
27cd478d
EZ
574 ;; remove any query scheduled during interactive invocation
575 (remove-hook 'type-break-post-command-hook 'type-break-do-query)
cc669dd8
NF
576 (let ((continue t)
577 (start-time (current-time)))
578 (setq type-break-time-last-break start-time)
579 (while continue
580 (save-window-excursion
581 ;; Eat the screen.
582 (and (eq (selected-window) (minibuffer-window))
583 (other-window 1))
584 (delete-other-windows)
585 (scroll-right (window-width))
27cd478d
EZ
586 (unless type-break-terse-messages
587 (message "Press any key to resume from typing break."))
cc669dd8
NF
588
589 (random t)
590 (let* ((len (length type-break-demo-functions))
591 (idx (random len))
592 (fn (nth idx type-break-demo-functions)))
593 (condition-case ()
594 (funcall fn)
595 (error nil))))
be8d412c 596
27cd478d
EZ
597 (let ((good-interval (or type-break-good-rest-interval
598 type-break-good-break-interval)))
599 (cond
600 (good-interval
601 (let ((break-secs (type-break-time-difference
602 start-time (current-time))))
603 (cond
604 ((>= break-secs good-interval)
605 (setq continue nil))
606 ;; 60 seconds may be too much leeway if the break is only 3
607 ;; minutes to begin with. You can just say "no" to the query
608 ;; below if you're in that much of a hurry.
609 ;;((> 60 (abs (- break-secs good-interval)))
610 ;; (setq continue nil))
611 ((funcall
612 type-break-query-function
613 (format
614 (if type-break-terse-messages
615 "%s%s remaining. Continue break? "
616 "%sYou really ought to rest %s more. Continue break? ")
617 (type-break-time-stamp)
618 (type-break-format-time (- good-interval
619 break-secs)))))
620 (t
621 (setq continue nil)))))
622 (t (setq continue nil))))))
4cf64c15 623
e7b20417 624 (type-break-keystroke-reset)
27cd478d 625 (type-break-file-time)
846f6dd9 626 (type-break-mode-line-countdown-or-break nil)
be8d412c 627 (type-break-schedule))
622aca7c 628
458401b6 629\f
27cd478d 630(defun type-break-schedule (&optional time start interval)
defa7346 631 "Schedule a typing break for TIME seconds from now.
27cd478d
EZ
632If time is not specified it defaults to `type-break-interval'.
633START and INTERVAL are used when recovering a break.
634START is the start of the break (defaults to now).
635INTERVAL is the full length of an interval (defaults to TIME)."
defa7346
NF
636 (interactive (list (and current-prefix-arg
637 (prefix-numeric-value current-prefix-arg))))
458401b6 638 (or time (setq time type-break-interval))
846f6dd9 639 (type-break-check-post-command-hook)
4cf64c15 640 (type-break-cancel-schedule)
e7b20417 641 (type-break-time-warning-schedule time 'reset)
846f6dd9 642 (type-break-run-at-time (max 1 time) nil 'type-break-alarm)
defa7346 643 (setq type-break-time-next-break
27cd478d
EZ
644 (type-break-time-sum (or start (current-time))
645 (or interval time))))
622aca7c 646
4cf64c15 647(defun type-break-cancel-schedule ()
e7b20417 648 (type-break-cancel-time-warning-schedule)
846f6dd9 649 (type-break-cancel-function-timers 'type-break-alarm)
be8d412c
NF
650 (setq type-break-alarm-p nil)
651 (setq type-break-time-next-break nil))
458401b6 652
e7b20417 653(defun type-break-time-warning-schedule (&optional time resetp)
b4dc9e6a 654 (let ((type-break-current-time-warning-interval nil))
e7b20417 655 (type-break-cancel-time-warning-schedule))
846f6dd9 656 (add-hook 'type-break-post-command-hook 'type-break-time-warning 'append)
e7b20417
NF
657 (cond
658 (type-break-time-warning-intervals
659 (and resetp
660 (setq type-break-current-time-warning-interval
661 type-break-time-warning-intervals))
662
663 (or time
664 (setq time (type-break-time-difference (current-time)
665 type-break-time-next-break)))
666
667 (while (and type-break-current-time-warning-interval
668 (> (car type-break-current-time-warning-interval) time))
669 (setq type-break-current-time-warning-interval
670 (cdr type-break-current-time-warning-interval)))
671
672 (cond
673 (type-break-current-time-warning-interval
674 (setq time (- time (car type-break-current-time-warning-interval)))
675 (setq type-break-current-time-warning-interval
676 (cdr type-break-current-time-warning-interval))
677
b4dc9e6a
NF
678 ;(let (type-break-current-time-warning-interval)
679 ; (type-break-cancel-time-warning-schedule))
846f6dd9 680 (type-break-run-at-time (max 1 time) nil 'type-break-time-warning-alarm)
b4dc9e6a
NF
681
682 (cond
683 (resetp
684 (setq type-break-warning-countdown-string nil))
685 (t
686 (setq type-break-warning-countdown-string (number-to-string time))
687 (setq type-break-warning-countdown-string-type "seconds"))))))))
e7b20417
NF
688
689(defun type-break-cancel-time-warning-schedule ()
846f6dd9 690 (type-break-cancel-function-timers 'type-break-time-warning-alarm)
e7b20417
NF
691 (remove-hook 'type-break-post-command-hook 'type-break-time-warning)
692 (setq type-break-current-time-warning-interval
b4dc9e6a 693 type-break-time-warning-intervals)
27cd478d 694 (setq type-break-time-warning-count 0) ; avoid warnings after break
b4dc9e6a 695 (setq type-break-warning-countdown-string nil))
e7b20417 696
4cf64c15 697(defun type-break-alarm ()
846f6dd9
NF
698 (type-break-check-post-command-hook)
699 (setq type-break-alarm-p t)
700 (type-break-mode-line-countdown-or-break 'break))
458401b6 701
e7b20417 702(defun type-break-time-warning-alarm ()
846f6dd9 703 (type-break-check-post-command-hook)
e7b20417
NF
704 (type-break-time-warning-schedule)
705 (setq type-break-time-warning-count type-break-warning-repeat)
846f6dd9
NF
706 (type-break-time-warning)
707 (type-break-mode-line-countdown-or-break 'countdown))
e7b20417
NF
708
709\f
710(defun type-break-run-tb-post-command-hook ()
711 (and type-break-mode
712 (run-hooks 'type-break-post-command-hook)))
713
458401b6 714(defun type-break-check ()
4cf64c15
NF
715 "Ask to take a typing break if appropriate.
716This may be the case either because the scheduled time has come \(and the
717minimum keystroke threshold has been reached\) or because the maximum
718keystroke threshold has been exceeded."
27cd478d 719 (type-break-file-keystroke-count)
e7b20417
NF
720 (let* ((min-threshold (car type-break-keystroke-threshold))
721 (max-threshold (cdr type-break-keystroke-threshold)))
722 (and type-break-good-rest-interval
723 (progn
724 (and (> (type-break-time-difference
725 type-break-time-last-command (current-time))
726 type-break-good-rest-interval)
727 (progn
728 (type-break-keystroke-reset)
846f6dd9 729 (type-break-mode-line-countdown-or-break nil)
e7b20417
NF
730 (setq type-break-time-last-break (current-time))
731 (type-break-schedule)))
732 (setq type-break-time-last-command (current-time))))
733
734 (and type-break-keystroke-threshold
b4dc9e6a
NF
735 (let ((keys (this-command-keys)))
736 (cond
737 ;; Ignore mouse motion
738 ((and (vectorp keys)
739 (consp (aref keys 0))
740 (memq (car (aref keys 0)) '(mouse-movement))))
741 (t
742 (setq type-break-keystroke-count
743 (+ type-break-keystroke-count (length keys)))))))
e7b20417 744
e7b20417
NF
745 (cond
746 (type-break-alarm-p
747 (cond
748 ((input-pending-p))
749 ((eq (selected-window) (minibuffer-window)))
750 ((and min-threshold
751 (< type-break-keystroke-count min-threshold))
752 (type-break-schedule))
753 (t
754 ;; If keystroke count is within min-threshold of
b4dc9e6a 755 ;; max-threshold, lower it to reduce the likelihood of an
e7b20417
NF
756 ;; immediate subsequent query.
757 (and max-threshold
758 min-threshold
759 (< (- max-threshold type-break-keystroke-count) min-threshold)
760 (progn
761 (type-break-keystroke-reset)
762 (setq type-break-keystroke-count min-threshold)))
763 (type-break-query))))
764 ((and type-break-keystroke-warning-intervals
765 max-threshold
766 (= type-break-keystroke-warning-count 0)
767 (type-break-check-keystroke-warning)))
768 ((and max-threshold
769 (> type-break-keystroke-count max-threshold)
770 (not (input-pending-p))
771 (not (eq (selected-window) (minibuffer-window))))
772 (type-break-keystroke-reset)
773 (setq type-break-keystroke-count (or min-threshold 0))
774 (type-break-query)))))
775
776;; This should return t if warnings were enabled, nil otherwise.
846f6dd9 777(defun type-break-check-keystroke-warning ()
b4dc9e6a
NF
778 ;; This is safe because the caller should have checked that the cdr was
779 ;; non-nil already.
e7b20417
NF
780 (let ((left (- (cdr type-break-keystroke-threshold)
781 type-break-keystroke-count)))
782 (cond
783 ((null (car type-break-current-keystroke-warning-interval))
784 nil)
785 ((> left (car type-break-current-keystroke-warning-interval))
786 nil)
787 (t
788 (while (and (car type-break-current-keystroke-warning-interval)
789 (< left (car type-break-current-keystroke-warning-interval)))
790 (setq type-break-current-keystroke-warning-interval
791 (cdr type-break-current-keystroke-warning-interval)))
792 (setq type-break-keystroke-warning-count type-break-warning-repeat)
793 (add-hook 'type-break-post-command-hook 'type-break-keystroke-warning)
b4dc9e6a
NF
794 (setq type-break-warning-countdown-string (number-to-string left))
795 (setq type-break-warning-countdown-string-type "keystrokes")
846f6dd9 796 (type-break-mode-line-countdown-or-break 'countdown)
e7b20417 797 t))))
4cf64c15 798
b4dc9e6a 799;; Arrange for a break query to be made, when the user stops typing furiously.
4cf64c15 800(defun type-break-query ()
b4dc9e6a
NF
801 (add-hook 'type-break-post-command-hook 'type-break-do-query))
802
b4dc9e6a
NF
803(defun type-break-do-query ()
804 (cond
846f6dd9
NF
805 ((not type-break-query-mode)
806 (type-break-noninteractive-query)
807 (type-break-schedule type-break-query-interval)
808 (remove-hook 'type-break-post-command-hook 'type-break-do-query))
809 ((sit-for 2)
b4dc9e6a
NF
810 (condition-case ()
811 (cond
812 ((let ((type-break-mode nil)
813 ;; yes-or-no-p sets this-command to exit-minibuffer,
814 ;; which hoses undo or yank-pop (if you happened to be
815 ;; yanking just when the query occurred).
816 (this-command this-command))
ad953485
NF
817 ;; Cancel schedule to prevent possibility of a second query
818 ;; from taking place before this one has even returned.
819 ;; The condition-case wrapper will reschedule on quit.
820 (type-break-cancel-schedule)
27cd478d
EZ
821 ;; Also prevent a second query when the break is interrupted.
822 (remove-hook 'type-break-post-command-hook 'type-break-do-query)
b4dc9e6a 823 (funcall type-break-query-function
ad953485
NF
824 (format "%s%s"
825 (type-break-time-stamp)
27cd478d
EZ
826 (if type-break-terse-messages
827 "Break now? "
828 "Take a break from typing now? "))))
b4dc9e6a
NF
829 (type-break))
830 (t
831 (type-break-schedule type-break-query-interval)))
832 (quit
27cd478d 833 (type-break-schedule type-break-query-interval))))))
458401b6 834
846f6dd9
NF
835(defun type-break-noninteractive-query (&optional ignored-args)
836 "Null query function which doesn't interrupt user and assumes `no'.
837It prints a reminder in the echo area to take a break, but doesn't enforce
838this or ask the user to start one right now."
839 (cond
840 (type-break-mode-line-message-mode)
841 (t
842 (beep t)
ad953485
NF
843 (message "%sYou should take a typing break now. Do `M-x type-break'."
844 (type-break-time-stamp))
846f6dd9
NF
845 (sit-for 1)
846 (beep t)
847 ;; return nil so query caller knows to reset reminder, as if user
848 ;; said "no" in response to yes-or-no-p.
849 nil)))
850
e7b20417
NF
851(defun type-break-time-warning ()
852 (cond
853 ((and (car type-break-keystroke-threshold)
854 (< type-break-keystroke-count (car type-break-keystroke-threshold))))
855 ((> type-break-time-warning-count 0)
b4dc9e6a
NF
856 (let ((timeleft (type-break-time-difference (current-time)
857 type-break-time-next-break)))
858 (setq type-break-warning-countdown-string (number-to-string timeleft))
859 (cond
860 ((eq (selected-window) (minibuffer-window)))
861 ;; Do nothing if the command was just a prefix arg, since that will
862 ;; immediately be followed by some other interactive command.
846f6dd9
NF
863 ;; Otherwise, it is particularly annoying for the sit-for below to
864 ;; delay redisplay when one types sequences like `C-u -1 C-l'.
b4dc9e6a 865 ((memq this-command '(digit-argument universal-argument)))
846f6dd9 866 ((not type-break-mode-line-message-mode)
b4dc9e6a
NF
867 ;; Pause for a moment so any previous message can be seen.
868 (sit-for 2)
ad953485
NF
869 (message "%sWarning: typing break due in %s."
870 (type-break-time-stamp)
b4dc9e6a
NF
871 (type-break-format-time timeleft))
872 (setq type-break-time-warning-count
873 (1- type-break-time-warning-count))))))
e7b20417 874 (t
b4dc9e6a
NF
875 (remove-hook 'type-break-post-command-hook 'type-break-time-warning)
876 (setq type-break-warning-countdown-string nil))))
e7b20417
NF
877
878(defun type-break-keystroke-warning ()
879 (cond
880 ((> type-break-keystroke-warning-count 0)
b4dc9e6a
NF
881 (setq type-break-warning-countdown-string
882 (number-to-string (- (cdr type-break-keystroke-threshold)
883 type-break-keystroke-count)))
e7b20417
NF
884 (cond
885 ((eq (selected-window) (minibuffer-window)))
846f6dd9
NF
886 ;; Do nothing if the command was just a prefix arg, since that will
887 ;; immediately be followed by some other interactive command.
888 ;; Otherwise, it is particularly annoying for the sit-for below to
889 ;; delay redisplay when one types sequences like `C-u -1 C-l'.
890 ((memq this-command '(digit-argument universal-argument)))
891 ((not type-break-mode-line-message-mode)
e7b20417 892 (sit-for 2)
ad953485
NF
893 (message "%sWarning: typing break due in %s keystrokes."
894 (type-break-time-stamp)
e7b20417
NF
895 (- (cdr type-break-keystroke-threshold)
896 type-break-keystroke-count))
897 (setq type-break-keystroke-warning-count
898 (1- type-break-keystroke-warning-count)))))
899 (t
900 (remove-hook 'type-break-post-command-hook
b4dc9e6a
NF
901 'type-break-keystroke-warning)
902 (setq type-break-warning-countdown-string nil))))
458401b6 903
846f6dd9
NF
904(defun type-break-mode-line-countdown-or-break (&optional type)
905 (cond
906 ((not type-break-mode-line-message-mode))
907 ((eq type 'countdown)
908 ;(setq type-break-mode-line-break-message-p nil)
909 (add-hook 'type-break-post-command-hook
910 'type-break-force-mode-line-update 'append))
911 ((eq type 'break)
912 ;; Alternate
913 (setq type-break-mode-line-break-message-p
914 (not type-break-mode-line-break-message-p))
915 (remove-hook 'type-break-post-command-hook
916 'type-break-force-mode-line-update))
917 (t
918 (setq type-break-mode-line-break-message-p nil)
919 (setq type-break-warning-countdown-string nil)
920 (remove-hook 'type-break-post-command-hook
921 'type-break-force-mode-line-update)))
922 (type-break-force-mode-line-update))
923
458401b6 924\f
be8d412c
NF
925;;;###autoload
926(defun type-break-statistics ()
927 "Print statistics about typing breaks in a temporary buffer.
928This includes the last time a typing break was taken, when the next one is
929scheduled, the keystroke thresholds and the current keystroke count, etc."
930 (interactive)
931 (with-output-to-temp-buffer "*Typing Break Statistics*"
932 (princ (format "Typing break statistics\n-----------------------\n
b4dc9e6a 933Typing break mode is currently %s.
846f6dd9
NF
934Interactive query for breaks is %s.
935Warnings of imminent typing breaks in mode line is %s.
b4dc9e6a
NF
936
937Last typing break ended : %s
be8d412c
NF
938Next scheduled typing break : %s\n
939Minimum keystroke threshold : %s
940Maximum keystroke threshold : %s
941Current keystroke count : %s"
b4dc9e6a 942 (if type-break-mode "enabled" "disabled")
846f6dd9
NF
943 (if type-break-query-mode "enabled" "disabled")
944 (if type-break-mode-line-message-mode "enabled" "disabled")
be8d412c
NF
945 (if type-break-time-last-break
946 (current-time-string type-break-time-last-break)
947 "never")
948 (if (and type-break-mode type-break-time-next-break)
6b62b567 949 (format "%s\t(%s from now)"
be8d412c 950 (current-time-string type-break-time-next-break)
e7b20417 951 (type-break-format-time
cc669dd8 952 (type-break-time-difference
e7b20417 953 (current-time)
cc669dd8 954 type-break-time-next-break)))
be8d412c
NF
955 "none scheduled")
956 (or (car type-break-keystroke-threshold) "none")
957 (or (cdr type-break-keystroke-threshold) "none")
958 type-break-keystroke-count))))
959
960;;;###autoload
b4dc9e6a 961(defun type-break-guesstimate-keystroke-threshold (wpm &optional wordlen frac)
be8d412c 962 "Guess values for the minimum/maximum keystroke threshold for typing breaks.
b4dc9e6a 963
be8d412c 964If called interactively, the user is prompted for their guess as to how
b4dc9e6a
NF
965many words per minute they usually type. This value should not be your
966maximum WPM, but your average. Of course, this is harder to gauge since it
967can vary considerably depending on what you are doing. For example, one
846f6dd9 968tends to type less when debugging a program as opposed to writing
b4dc9e6a
NF
969documentation. (Perhaps a separate program should be written to estimate
970average typing speed.)
971
972From that, this command sets the values in `type-break-keystroke-threshold'
973based on a fairly simple algorithm involving assumptions about the average
974length of words (5). For the minimum threshold, it uses about a fifth of
975the computed maximum threshold.
be8d412c 976
27cd478d 977When called from Lisp programs, the optional args WORDLEN and FRAC can be
be8d412c
NF
978used to override the default assumption about average word length and the
979fraction of the maximum threshold to which to set the minimum threshold.
980FRAC should be the inverse of the fractional value; for example, a value of
9812 would mean to use one half, a value of 4 would mean to use one quarter, etc."
b4dc9e6a 982 (interactive "NOn average, how many words per minute do you type? ")
be8d412c 983 (let* ((upper (* wpm (or wordlen 5) (/ type-break-interval 60)))
f486195c 984 (lower (/ upper (or frac 5))))
be8d412c
NF
985 (or type-break-keystroke-threshold
986 (setq type-break-keystroke-threshold (cons nil nil)))
987 (setcar type-break-keystroke-threshold lower)
988 (setcdr type-break-keystroke-threshold upper)
32226619 989 (if (called-interactively-p 'interactive)
5d610140
RS
990 (message "min threshold: %d\tmax threshold: %d" lower upper))
991 type-break-keystroke-threshold))
be8d412c
NF
992
993\f
e7b20417
NF
994;;; misc functions
995
996;; Compute the difference, in seconds, between a and b, two structures
997;; similar to those returned by `current-time'.
defa7346
NF
998;; Use addition rather than logand since that is more robust; the low 16
999;; bits of the seconds might have been incremented, making it more than 16
1000;; bits wide.
846f6dd9 1001(defun type-break-time-difference (a b)
e7b20417
NF
1002 (+ (lsh (- (car b) (car a)) 16)
1003 (- (car (cdr b)) (car (cdr a)))))
1004
defa7346
NF
1005;; Return (in a new list the same in structure to that returned by
1006;; `current-time') the sum of the arguments. Each argument may be a time
1007;; list or a single integer, a number of seconds.
1008;; This function keeps the high and low 16 bits of the seconds properly
1009;; balanced so that the lower value never exceeds 16 bits. Otherwise, when
1010;; the result is passed to `current-time-string' it will toss some of the
846f6dd9 1011;; "low" bits and format the time incorrectly.
defa7346
NF
1012(defun type-break-time-sum (&rest tmlist)
1013 (let ((high 0)
1014 (low 0)
1015 (micro 0)
1016 tem)
1017 (while tmlist
1018 (setq tem (car tmlist))
1019 (setq tmlist (cdr tmlist))
1020 (cond
1021 ((numberp tem)
1022 (setq low (+ low tem)))
1023 (t
1024 (setq high (+ high (or (car tem) 0)))
1025 (setq low (+ low (or (car (cdr tem)) 0)))
1026 (setq micro (+ micro (or (car (cdr (cdr tem))) 0))))))
1027
1028 (and (>= micro 1000000)
1029 (progn
1030 (setq tem (/ micro 1000000))
1031 (setq low (+ low tem))
1032 (setq micro (- micro (* tem 1000000)))))
1033
1034 (setq tem (lsh low -16))
1035 (and (> tem 0)
1036 (progn
1037 (setq low (logand low 65535))
1038 (setq high (+ high tem))))
1039
1040 (list high low micro)))
1041
ad953485
NF
1042(defun type-break-time-stamp (&optional when)
1043 (if (fboundp 'format-time-string)
1044 (format-time-string type-break-time-stamp-format when)
1045 ;; Emacs 19.28 and prior do not have format-time-string.
1046 ;; In that case, result is not customizable. Upgrade today!
1047 (format "[%s] " (substring (current-time-string when) 11 16))))
1048
846f6dd9 1049(defun type-break-format-time (secs)
e7b20417
NF
1050 (let ((mins (/ secs 60)))
1051 (cond
1052 ((= mins 1) (format "%d minute" mins))
1053 ((> mins 0) (format "%d minutes" mins))
1054 ((= secs 1) (format "%d second" secs))
1055 (t (format "%d seconds" secs)))))
1056
1057(defun type-break-keystroke-reset ()
27cd478d 1058 (setq type-break-interval-start (current-time)) ; not a keystroke
e7b20417
NF
1059 (setq type-break-keystroke-count 0)
1060 (setq type-break-keystroke-warning-count 0)
1061 (setq type-break-current-keystroke-warning-interval
1062 type-break-keystroke-warning-intervals)
1063 (remove-hook 'type-break-post-command-hook 'type-break-keystroke-warning))
1064
846f6dd9
NF
1065(defun type-break-force-mode-line-update (&optional all)
1066 "Force the mode-line of the current buffer to be redisplayed.
1067With optional non-nil ALL, force redisplay of all mode-lines."
7fdbcd83 1068 (and all (with-current-buffer (other-buffer)))
846f6dd9
NF
1069 (set-buffer-modified-p (buffer-modified-p)))
1070
27cd478d 1071;; If an exception occurs in Emacs while running the post command hook, the
846f6dd9
NF
1072;; value of that hook is clobbered. This is because the value of the
1073;; variable is temporarily set to nil while it's running to prevent
1074;; recursive application, but it also means an exception aborts the routine
1075;; of restoring it. This function is called from the timers to restore it,
1076;; just in case.
1077(defun type-break-check-post-command-hook ()
1078 (add-hook 'post-command-hook 'type-break-run-tb-post-command-hook 'append))
1079
1080\f
1081;;; Timer wrapper functions
7fdbcd83
SM
1082;;
1083;; These shield type-break from variations in the interval timer packages
1084;; for different versions of Emacs.
846f6dd9
NF
1085
1086(defun type-break-run-at-time (time repeat function)
2c8155f7 1087 (condition-case nil (or (require 'timer) (require 'itimer)) (error nil))
5d610140 1088 (run-at-time time repeat function))
846f6dd9 1089
2c8155f7 1090(defvar timer-dont-exit)
846f6dd9 1091(defun type-break-cancel-function-timers (function)
5d610140
RS
1092 (let ((timer-dont-exit t))
1093 (cancel-function-timers function)))
846f6dd9 1094
e7b20417
NF
1095\f
1096;;; Demo wrappers
1097
a5b5e31e
CY
1098(defun type-break-catch-up-event ()
1099 ;; If the last input event is a down-event, read and discard the
1100 ;; corresponding up-event too, to avoid triggering another prompt.
1101 (and (eventp last-input-event)
1102 (memq 'down (event-modifiers last-input-event))
1103 (read-event)))
1104
e7b20417
NF
1105;; This is a wrapper around hanoi that calls it with an arg large enough to
1106;; make the largest discs possible that will fit in the window.
1107;; Also, clean up the *Hanoi* buffer after we're done.
1108(defun type-break-demo-hanoi ()
1109 "Take a hanoiing typing break."
1110 (and (get-buffer "*Hanoi*")
1111 (kill-buffer "*Hanoi*"))
1112 (condition-case ()
1113 (progn
1114 (hanoi (/ (window-width) 8))
1115 ;; Wait for user to come back.
1dfdb0c0 1116 (read-event)
a5b5e31e 1117 (type-break-catch-up-event)
e7b20417
NF
1118 (kill-buffer "*Hanoi*"))
1119 (quit
1dfdb0c0 1120 (read-event)
a5b5e31e 1121 (type-break-catch-up-event)
e7b20417
NF
1122 (and (get-buffer "*Hanoi*")
1123 (kill-buffer "*Hanoi*")))))
1124
1125;; This is a wrapper around life that calls it with a `sleep' arg to make
1126;; it run a little more leisurely.
1127;; Also, clean up the *Life* buffer after we're done.
1128(defun type-break-demo-life ()
1129 "Take a typing break and get a life."
1130 (let ((continue t))
1131 (while continue
1132 (setq continue nil)
1133 (and (get-buffer "*Life*")
1134 (kill-buffer "*Life*"))
1135 (condition-case ()
1136 (progn
1137 (life 3)
1138 ;; wait for user to return
1dfdb0c0 1139 (read-event)
a5b5e31e 1140 (type-break-catch-up-event)
e7b20417
NF
1141 (kill-buffer "*Life*"))
1142 (life-extinct
b4dc9e6a 1143 (message "%s" (get 'life-extinct 'error-message))
e7b20417
NF
1144 ;; restart demo
1145 (setq continue t))
1146 (quit
a5b5e31e 1147 (type-break-catch-up-event)
e7b20417
NF
1148 (and (get-buffer "*Life*")
1149 (kill-buffer "*Life*")))))))
1150
defa7346 1151;; Boring demo, but doesn't use many cycles
e7b20417
NF
1152(defun type-break-demo-boring ()
1153 "Boring typing break demo."
27cd478d
EZ
1154 (let ((rmsg (if type-break-terse-messages
1155 ""
1156 "Press any key to resume from typing break"))
e7b20417 1157 (buffer-name "*Typing Break Buffer*")
27cd478d 1158 lines elapsed timeleft tmsg)
e7b20417
NF
1159 (condition-case ()
1160 (progn
1161 (switch-to-buffer (get-buffer-create buffer-name))
1162 (buffer-disable-undo (current-buffer))
27cd478d
EZ
1163 (setq lines (/ (window-body-height) 2))
1164 (unless type-break-terse-messages (setq lines (1- lines)))
1165 (if type-break-demo-boring-stats
1166 (setq lines (- lines 2)))
1167 (setq lines (make-string lines ?\C-j))
defa7346 1168 (while (not (input-pending-p))
27cd478d 1169 (erase-buffer)
defa7346
NF
1170 (setq elapsed (type-break-time-difference
1171 type-break-time-last-break
1172 (current-time)))
27cd478d
EZ
1173 (let ((good-interval (or type-break-good-rest-interval
1174 type-break-good-break-interval)))
1175 (cond
1176 (good-interval
1177 (setq timeleft (- good-interval elapsed))
1178 (if (> timeleft 0)
1179 (setq tmsg
1180 (format (if type-break-terse-messages
1181 "Break remaining: %s"
1182 "You should rest for %s more")
1183 (type-break-format-time timeleft)))
1184 (setq tmsg
1185 (format (if type-break-terse-messages
1186 "Break complete (%s elapsed in total)"
1187 "Typing break has lasted %s")
1188 (type-break-format-time elapsed)))))
1189 (t
1190 (setq tmsg
1191 (format (if type-break-terse-messages
1192 "Break has lasted %s"
1193 "Typing break has lasted %s")
1194 (type-break-format-time elapsed))))))
1195 (insert lines
1196 (make-string (/ (- (window-width) (length tmsg)) 2) ?\ )
1197 tmsg)
1198 (if (> (length rmsg) 0)
1199 (insert "\n"
1200 (make-string (/ (- (window-width) (length rmsg)) 2)
1201 ?\ )
1202 rmsg))
1203 (if type-break-demo-boring-stats
1204 (let*
1205 ((message
1206 (format
1207 (if type-break-terse-messages
1208 "Since last break: %s keystrokes\n"
1209 "Since your last break you've typed %s keystrokes\n")
1210 type-break-keystroke-count))
1211 (column-spaces
1212 (make-string (/ (- (window-width) (length message)) 2)
1213 ?\ ))
1214 (wpm (/ (/ (float type-break-keystroke-count) 5)
1215 (/ (type-break-time-difference
1216 type-break-interval-start
1217 type-break-time-last-break)
1218 60.0))))
1219 (insert "\n\n" column-spaces message)
1220 (if type-break-terse-messages
1221 (insert (format " %s%.2f wpm"
1222 column-spaces
1223 wpm))
1224 (setq message
1225 (format "at an average of %.2f words per minute"
1226 wpm))
1227 (insert
1228 (make-string (/ (- (window-width) (length message)) 2)
1229 ?\ )
1230 message))))
defa7346
NF
1231 (goto-char (point-min))
1232 (sit-for 60))
a5b5e31e
CY
1233 (read-event)
1234 (type-break-catch-up-event)
e7b20417
NF
1235 (kill-buffer buffer-name))
1236 (quit
1237 (and (get-buffer buffer-name)
1238 (kill-buffer buffer-name))))))
1239
1240\f
458401b6
NF
1241(provide 'type-break)
1242
104221a0
SE
1243(if type-break-mode
1244 (type-break-mode 1))
ad953485 1245
458401b6 1246;;; type-break.el ends here