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