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