;;; speed-reader-mode.el --- Speed reading mode ;; Copyright (C) 2006 Ben Hyde ;; Author: Ben Hyde ;; Maintainer: Ben Hyde ;; Keywords: reading, frf, dyslexia, speed-reading, rsvp ;; Comentary: ;; Instructions ;; Load this into your your xemacs, invoke M-x sr, in a buffer ;; you wish to read. The text in the buffer will largely disappear ;; with the exception of a window showing a few words begining at ;; the current point. This window with proceed to sweep across ;; the buffer text. This simulates how presumably your attention ;; is focused during traditional reading. By masking out the test ;; your not currently reading it helps to keep you focused. ;; While this technique is analagous to techniques used in some approaches ;; to speed reading. This technique is based on a therapy describe ;; here[1] designed to help dyslexics improve their skill at focusing ;; their visual field. ;; [1] http://cbcl.mit.edu/projects/cbcl/people/geiger/memo_complete.pdf ;;; Customization (defgroup sr nil "Focus Reading Frame Mode" :prefix "sr-" :group 'wp) (defcustom sr-window-size 35 "*Minimum number of characters revealed in the attention window." :type 'integer :group 'sr) (defcustom sr-words-per-minute 150 "*Number of words per minute to sweep the attention window thru." :type 'integer :group 'sr) (defcustom sr-initial-pause 1.0 "*How many seconds to pause so your eyes can focus when starting." :type 'float :group 'sr) (defcustom sr-focus-face-name 'sr-attention-face "*Face to use in the region where out attetion is focused." :type 'face :group 'sr) (defcustom sr-mask-face-name 'sr-mask-face "*Face to mask out everything else." :type 'face :group 'sr) (defcustom sr-pause-mask-face-name 'sr-pause-mask-face "*Face for the mask when we are paused." :type 'face :group 'sr) (defcustom sr-word-length 5 "*Estimated characters per word." :type 'integer :group 'sr) (defcustom sr-show-whole-words nil "*Resize attention window as we run to show entire words" :type 'boolean :group 'sr) (defcustom sr-show-viewer nil "*Toggle controling the display of a seperate viewer" :type 'boolean :group 'sr) (defcustom sr-viewer-face-name 'sr-viewer-face "*Face used in the seperate viewer" :type 'face :group 'sr) ;;;; Faces (defface sr-attention-face '((((type x pm mswindows) (class color grayscale) (background light)) (:foreground "black")) (((type x pm mswindows) (class color grayscale) (background dark)) (:foreground "black")) (((type tty) (class color) (background dark)) (:foreground "black" :dim t)) (((type tty) (class color) (background light)) (:foreground "black"))) "Face used for the masked out text." :group 'sr) (defface sr-mask-face '((((type x pm mswindows) (class color grayscale) (background light)) (:foreground "lightgray")) (((type x pm mswindows) (class color grayscale) (background dark)) (:foreground "lightgray")) (((type tty) (class color) (background dark)) (:foreground "lightgray" :dim t)) (((type tty) (class color) (background light)) (:foreground "lightgray"))) "Face used for the masked out text." :group 'sr) (defface sr-pause-mask-face '((((type x pm mswindows) (class color grayscale) (background light)) (:foreground "firebrick")) (((type x pm mswindows) (class color grayscale) (background dark)) (:foreground "firebrick")) (((type tty) (class color) (background dark)) (:foreground "firebrick" :dim t)) (((type tty) (class color) (background light)) (:foreground "firebrick"))) "Face used for the masked out text, when we are paused." :group 'sr) (defface sr-viewer-face '((((type x pm mswindows) (class color grayscale) (background light)) (:foreground "blue" :family "helvetica" :size 20)) (((type x pm mswindows) (class color grayscale) (background dark)) (:foreground "red" :family "helvetica" :size 20)) (((type tty) (class color) (background dark)) (:foreground "green" :dim t)) (((type tty) (class color) (background light)) (:foreground "pink"))) "Face used in the seperate viewer." :group 'sr) ;;;; Internal state (defvar sr-read-point nil) (defvar sr-background-extent nil) (defvar sr-attention-extent nil) (defvar sr-viewer-extent nil) (defvar sr-itimer nil) (defvar sr-saved-window-configuration nil) ;;;; Infrastructure (defun sr-interval () (/ 1 (/ (* sr-word-length sr-words-per-minute) 60.0))) (defvar sr-viewer-name " *Speed Reader Viewer*") (defun sr-get-or-make-viewer () (or (get-buffer sr-viewer-name) (with-current-buffer (get-buffer-create sr-viewer-name) (setf sr-viewer-extent (make-extent (point-min) (point-max))) (set-extent-face sr-viewer-extent sr-viewer-face-name) (current-buffer)))) (defun sr-update-viewer (extent) (when sr-show-viewer (let* ((viewer (sr-get-or-make-viewer)) (viewer-name (buffer-name viewer))) (with-output-to-temp-buffer viewer-name (with-current-buffer (get-buffer viewer-name) (let ((end (point-max))) (goto-char end) (insert-buffer-substring (extent-object extent) (extent-start-position extent) (extent-end-position extent)) (setf (extent-end-position sr-viewer-extent) (point-max)) (delete-region (point-min) end) (goto-char (point-min)) (replace-string " " " " nil) )))))) (defun sr-make-extents () (setf sr-attention-extent (let ((extent (make-extent (point-marker) (point-marker)))) (set-extent-property extent 'id 'sr-attention) (set-extent-priority extent 10) ; ? try to highlighting (set-extent-face extent sr-focus-face-name) (sr-size-extent extent))) (setf sr-background-extent (let ((extent (make-extent (point-min) (point-max)))) (set-extent-property extent 'id 'sr-background) (set-extent-priority extent 10) ; ? try to highlighting (set-extent-face extent sr-mask-face-name) extent))) (defun sr-get-or-make-itimer () (unless sr-itimer (setf sr-itimer (make-itimer)) ; (setf (itimer-name sr-itimer) "SR stepper") (set-itimer-restart sr-itimer (sr-interval)) (set-itimer-function sr-itimer 'sr-advance)) sr-itimer) (defun sr-size-extent (extent) (save-excursion (goto-char sr-read-point) (when sr-show-whole-words (forward-word 1) (forward-word -1)) (setf (extent-start-position extent) (point)) (forward-char sr-window-size) (when sr-show-whole-words (forward-word 1)) (setf (extent-end-position extent) (point-marker))) (sr-update-viewer extent) extent) (defun sr-advance () (interactive) (with-current-buffer (extent-object sr-attention-extent) (cond ((= (point-max) (extent-end-position sr-attention-extent)) (sr-done)) (t (incf sr-read-point) (sr-size-extent sr-attention-extent) (set-itimer-restart sr-itimer (sr-interval)) (sr-scroll-if-necessary))))) (defun sr-scroll-if-necessary () ;; TBD More work needed (unless (pos-visible-in-window-p (extent-end-position sr-attention-extent) (selected-window)) (goto-char (extent-start-position sr-attention-extent)) (beginning-of-line) (set-window-start (selected-window) (extent-start-position sr-attention-extent)) t)) (defvar sr-mode-map (let ((map (make-keymap))) (set-keymap-name map 'sr-mode-map) ;; Bind all printing characters to `sr-quit'. (let ((i 32) (str (make-string 1 0))) (while (< i 127) (aset str 0 i) (define-key map str 'sr-quit) (setq i (1+ i)))) ;; Speed Control (define-key map "a" 'sr-backup) (define-key map "f" 'sr-faster) (define-key map "d" 'sr-go-or-pause) (define-key map "s" 'sr-slower) ;; Help (define-key map '(control h) 'sr-help) (define-key map 'f1 'sr-help) (define-key map 'help 'sr-help) map) "Keymap for sr-mode.") ;;;; User Interface (defun speed-reader () (interactive) (sr)) (defun sr () (interactive) (sr-mode) (display-message 'command "Enter SR mode: s/d/f/h = slower/pause/faster/help, otherkeys exit")) (defun sr-quit () (interactive) (sr-done) (display-message 'command "Exit SR mode")) (defun sr-adjust-interval (description num dom) (setf sr-words-per-minute (/ (* num sr-words-per-minute) dom)) (set-itimer-restart sr-itimer (sr-interval)) (display-message 'command (format "SR: %s %d cpm" description sr-words-per-minute))) (defun sr-faster () (interactive) (sr-adjust-interval 'faster 110 100)) (defun sr-backup () (interactive) (decf sr-read-point sr-window-size) ; (set-itimer-value sr-itimer sr-initial-pause) ;; ? (sr-size-extent sr-attention-extent)) (defun sr-go-or-pause () (interactive) (display-message 'command (cond ((itimer-live-p sr-itimer) (delete-itimer sr-itimer) (setf (extent-face sr-background-extent) sr-pause-mask-face-name) "sr: paused") (t (setf (extent-face sr-background-extent) sr-mask-face-name) (activate-itimer sr-itimer) "sr: resume")))) (defun sr-slower () (interactive) (sr-adjust-interval 'slower 100 110)) (defun sr-help () (interactive) (display-message 'command "tbd - sr-help")) (defun sr-mode () "Start sr minor mode. \\{sr-mode-map}" (setf sr-saved-window-configuration (current-window-configuration)) (setf sr-read-point (point)) (sr-make-extents) (sr-get-or-make-itimer) (set-itimer-value sr-itimer (or sr-initial-pause (sr-interval))) (activate-itimer sr-itimer) (setq sr-mode (gettext " SR") overriding-local-map sr-mode-map) (redraw-modeline) ; (run-hooks 'sr-mode-hook) t) (defun sr-done () (interactive) (setq overriding-local-map nil sr-mode nil) (redraw-modeline) (let ((itimer? (shiftf sr-itimer nil))) (when itimer? (delete-itimer itimer?))) (let ((extent? (shiftf sr-attention-extent nil))) (when extent? (delete-extent extent?))) (let ((extent? (shiftf sr-background-extent nil))) (when extent? (delete-extent extent?))) (shiftf (current-window-configuration) sr-saved-window-configuration nil) ; (run-hooks 'sr-mode-end-hook) )