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