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