+(defcustom type-break-good-break-interval nil
+ "*Number of seconds considered to be an adequate explicit typing rest.
+
+When this variable is non-nil, its value is considered to be a \"good\"
+length (in seconds) for a break initiated by the command `type-break',
+overriding `type-break-good-rest-interval'. This provides querying of
+break interruptions when `type-break-good-rest-interval' is nil."
+ :type 'integer
+ :group 'type-break)
+
+;;;###autoload
+(defcustom type-break-keystroke-threshold
+ ;; Assuming typing speed is 35wpm (on the average, do you really
+ ;; type more than that in a minute? I spend a lot of time reading mail
+ ;; and simply studying code in buffers) and average word length is
+ ;; about 5 letters, default upper threshold to the average number of
+ ;; keystrokes one is likely to type in a break interval. That way if the
+ ;; user goes through a furious burst of typing activity, cause a typing
+ ;; break to be required sooner than originally scheduled.
+ ;; Conversely, the minimum threshold should be about a fifth of this.
+ (let* ((wpm 35)
+ (avg-word-length 5)
+ (upper (* wpm avg-word-length (/ type-break-interval 60)))
+ (lower (/ upper 5)))
+ (cons lower upper))
+ "*Upper and lower bound on number of keystrokes for considering typing break.
+This structure is a pair of numbers (MIN . MAX).
+
+The first number is the minimum number of keystrokes that must have been
+entered since the last typing break before considering another one, even if
+the scheduled time has elapsed; the break is simply rescheduled until later
+if the minimum threshold hasn't been reached. If this first value is nil,
+then there is no minimum threshold; as soon as the scheduled time has
+elapsed, the user will always be queried.
+
+The second number is the maximum number of keystrokes that can be entered
+before a typing break is requested immediately, pre-empting the originally
+scheduled break. If this second value is nil, then no pre-emptive breaks
+will occur; only scheduled ones will.
+
+Keys with bucky bits (shift, control, meta, etc) are counted as only one
+keystroke even though they really require multiple keys to generate them.
+
+The command `type-break-guesstimate-keystroke-threshold' can be used to
+guess a reasonably good pair of values for this variable."
+ :type 'sexp
+ :group 'type-break)
+
+(defcustom type-break-query-mode t
+ "*Non-nil means ask whether or not to prompt user for breaks.
+If so, call the function specified in the value of the variable
+`type-break-query-function' to do the asking."
+ :type 'boolean
+ :group 'type-break)
+
+(defcustom type-break-query-function 'yes-or-no-p
+ "*Function to use for making query for a typing break.
+It should take a string as an argument, the prompt.
+Usually this should be set to `yes-or-no-p' or `y-or-n-p'.
+
+To avoid being queried at all, set `type-break-query-mode' to nil."
+ :type '(radio function
+ (function-item yes-or-no-p)
+ (function-item y-or-n-p))
+ :group 'type-break)
+
+(defcustom type-break-query-interval 60
+ "*Number of seconds between queries to take a break, if put off.
+The user will continue to be prompted at this interval until he or she
+finally submits to taking a typing break."
+ :type 'integer
+ :group 'type-break)
+
+(defcustom type-break-time-warning-intervals '(300 120 60 30)
+ "*List of time intervals for warnings about upcoming typing break.
+At each of the intervals (specified in seconds) away from a scheduled
+typing break, print a warning in the echo area."
+ :type '(repeat integer)
+ :group 'type-break)
+
+(defcustom type-break-keystroke-warning-intervals '(300 200 100 50)
+ "*List of keystroke measurements for warnings about upcoming typing break.
+At each of the intervals (specified in keystrokes) away from the upper
+keystroke threshold, print a warning in the echo area.
+If either this variable or the upper threshold is set, then no warnings
+will occur."
+ :type '(repeat integer)
+ :group 'type-break)
+
+(defcustom type-break-warning-repeat 40
+ "*Number of keystrokes for which warnings should be repeated.
+That is, for each of this many keystrokes the warning is redisplayed
+in the echo area to make sure it's really seen."
+ :type 'integer
+ :group 'type-break)
+
+(defcustom type-break-time-stamp-format "[%H:%M] "
+ "*Timestamp format used to prefix messages.
+Format specifiers are as used by `format-time-string'."
+ :type 'string
+ :group 'type-break)
+
+(defcustom type-break-demo-functions
+ '(type-break-demo-boring type-break-demo-life type-break-demo-hanoi)
+ "*List of functions to consider running as demos during typing breaks.
+When a typing break begins, one of these functions is selected randomly
+to have Emacs do something interesting.
+
+Any function in this list should start a demo which ceases as soon as a
+key is pressed."
+ :type '(repeat function)
+ :group 'type-break)
+
+(defcustom type-break-demo-boring-stats nil
+ "*Show word per minute and keystroke figures in the Boring demo."
+ :type 'boolean
+ :group 'type-break)
+
+(defcustom type-break-terse-messages nil
+ "*Use slightly terser messages."
+ :type 'boolean
+ :group 'type-break)
+
+(defcustom type-break-file-name (convert-standard-filename "~/.type-break")
+ "*Name of file used to save state across sessions.
+If this is nil, no data will be saved across sessions."
+ :type 'file
+ :group 'type-break)
+
+(defvar type-break-post-command-hook '(type-break-check)
+ "Hook run indirectly by `post-command-hook' for typing break functions.
+This is not really intended to be set by the user, but it's probably
+harmless to do so. Mainly it is used by various parts of the typing break
+program to delay actions until after the user has completed some command.
+It exists because `post-command-hook' itself is inaccessible while its
+functions are being run, and some type-break--related functions want to
+remove themselves after running.")
+
+\f
+;; Mode line frobs
+
+(defcustom type-break-mode-line-message-mode nil
+ "*Non-nil means put type-break related messages in the mode line.
+Otherwise, messages typically go in the echo area.
+
+See also `type-break-mode-line-format' and its members."
+ :type 'boolean
+ :group 'type-break)
+
+(defvar type-break-mode-line-format
+ '(type-break-mode-line-message-mode
+ (""
+ type-break-mode-line-break-message
+ type-break-mode-line-warning))
+ "*Format of messages in the mode line concerning typing breaks.")
+
+(defvar type-break-mode-line-break-message
+ '(type-break-mode-line-break-message-p
+ type-break-mode-line-break-string))
+
+(defvar type-break-mode-line-break-message-p nil)
+(defvar type-break-mode-line-break-string " *** TAKE A TYPING BREAK NOW ***")
+
+(defvar type-break-mode-line-warning
+ '(type-break-mode-line-break-message-p
+ ("")
+ (type-break-warning-countdown-string
+ (" *** "
+ "Break in "
+ type-break-warning-countdown-string
+ " "
+ type-break-warning-countdown-string-type
+ "***"))))
+
+(defvar type-break-warning-countdown-string nil
+ "If non-nil, this is a countdown for the next typing break.
+
+This variable, in conjunction with `type-break-warning-countdown-string-type'
+\(which indicates whether this value is a number of keystrokes or seconds)
+is installed in `mode-line-format' to notify of imminent typing breaks.")
+
+(defvar type-break-warning-countdown-string-type nil
+ "Indicates the unit type of `type-break-warning-countdown-string'.
+It will be either \"seconds\" or \"keystrokes\".")
+
+\f
+;; These are internal variables. Do not set them yourself.
+
+(defvar type-break-alarm-p nil)
+(defvar type-break-keystroke-count 0)
+(defvar type-break-time-last-break nil)
+(defvar type-break-time-next-break nil)
+(defvar type-break-time-last-command (current-time))
+(defvar type-break-current-time-warning-interval nil)
+(defvar type-break-current-keystroke-warning-interval nil)
+(defvar type-break-time-warning-count 0)
+(defvar type-break-keystroke-warning-count 0)
+(defvar type-break-interval-start nil)
+
+\f
+;;;###autoload
+(defun type-break-mode (&optional prefix)
+ "Enable or disable typing-break mode.
+This is a minor mode, but it is global to all buffers by default.
+
+When this mode is enabled, the user is encouraged to take typing breaks at
+appropriate intervals; either after a specified amount of time or when the
+user has exceeded a keystroke threshold. When the time arrives, the user
+is asked to take a break. If the user refuses at that time, Emacs will ask
+again in a short period of time. The idea is to give the user enough time
+to find a good breaking point in his or her work, but be sufficiently
+annoying to discourage putting typing breaks off indefinitely.
+
+A negative prefix argument disables this mode.
+No argument or any non-negative argument enables it.
+
+The user may enable or disable this mode by setting the variable of the
+same name, though setting it in that way doesn't reschedule a break or
+reset the keystroke counter.
+
+If the mode was previously disabled and is enabled as a consequence of
+calling this function, it schedules a break with `type-break-schedule' to
+make sure one occurs (the user can call that command to reschedule the
+break at any time). It also initializes the keystroke counter.
+
+The variable `type-break-interval' specifies the number of seconds to
+schedule between regular typing breaks. This variable doesn't directly
+affect the time schedule; it simply provides a default for the
+`type-break-schedule' command.
+
+If set, the variable `type-break-good-rest-interval' specifies the minimum
+amount of time which is considered a reasonable typing break. Whenever
+that time has elapsed, typing breaks are automatically rescheduled for
+later even if Emacs didn't prompt you to take one first. Also, if a break
+is ended before this much time has elapsed, the user will be asked whether
+or not to continue. A nil value for this variable prevents automatic
+break rescheduling, making `type-break-interval' an upper bound on the time
+between breaks. In this case breaks will be prompted for as usual before
+the upper bound if the keystroke threshold is reached.
+
+If `type-break-good-rest-interval' is nil and
+`type-break-good-break-interval' is set, then confirmation is required to
+interrupt a break before `type-break-good-break-interval' seconds
+have passed. This provides for an upper bound on the time between breaks
+together with confirmation of interruptions to these breaks.
+
+The variable `type-break-keystroke-threshold' is used to determine the
+thresholds at which typing breaks should be considered. You can use
+the command `type-break-guesstimate-keystroke-threshold' to try to
+approximate good values for this.
+
+There are several variables that affect how or when warning messages about
+imminent typing breaks are displayed. They include:
+
+ `type-break-mode-line-message-mode'
+ `type-break-time-warning-intervals'
+ `type-break-keystroke-warning-intervals'
+ `type-break-warning-repeat'
+ `type-break-warning-countdown-string'
+ `type-break-warning-countdown-string-type'
+
+There are several variables that affect if, how, and when queries to begin
+a typing break occur. They include:
+
+ `type-break-query-mode'
+ `type-break-query-function'
+ `type-break-query-interval'
+
+The command `type-break-statistics' prints interesting things.
+
+Finally, a file (named `type-break-file-name') is used to store information
+across Emacs sessions. This provides recovery of the break status between
+sessions and after a crash. Manual changes to the file may result in
+problems."
+ (interactive "P")
+ (type-break-check-post-command-hook)
+
+ (let ((already-enabled type-break-mode))
+ (setq type-break-mode (>= (prefix-numeric-value prefix) 0))
+
+ (cond
+ ((and already-enabled type-break-mode)
+ (and (interactive-p)
+ (message "Type Break mode is already enabled")))
+ (type-break-mode
+ (when type-break-file-name
+ (with-current-buffer (find-file-noselect type-break-file-name 'nowarn)
+ (setq buffer-save-without-query t)))
+
+ (or global-mode-string
+ (setq global-mode-string '("")))
+ (or (assq 'type-break-mode-line-message-mode
+ minor-mode-alist)
+ (setq minor-mode-alist
+ (cons type-break-mode-line-format
+ minor-mode-alist)))
+ (type-break-keystroke-reset)
+ (type-break-mode-line-countdown-or-break nil)
+
+ (setq type-break-time-last-break
+ (or (type-break-get-previous-time)
+ (current-time)))