First Rough Draft (July 30, 2014): This example creates an invisible overlay spacer at the beginning and ending of the visible window. The mode-line, however, remains at the very bottom. Some modes that use overlays may not be compatible with this example. To see this example in action, just type M-x screencast-mode
.
(defun screencast-function
(&optional
screencast-old-window-start
screencast-old-window-end
screencast-old-window-end-forced
screencast-new-window-start
screencast-new-window-end)
(save-excursion
(let* (
(window-start
(cond
(screencast-old-window-start
screencast-old-window-start)
(screencast-new-window-start
screencast-new-window-start)
(t (window-start))))
(window-end
(cond
((and
screencast-old-window-end
screencast-old-window-end-forced
(= screencast-old-window-end screencast-old-window-end-forced))
screencast-old-window-end)
((and
screencast-old-window-end
screencast-old-window-end-forced
(> screencast-old-window-end-forced screencast-old-window-end))
screencast-old-window-end-forced)
(screencast-new-window-end
screencast-new-window-end)
(t (window-end (selected-window) t))))
(top-margin 5)
(bottom-margin 5)
end-point
(top-margin-overlay
(propertize (char-to-string ?\uE001)
'display `((space :align-to 0 :height ,top-margin))))
(bottom-margin-overlay
(propertize (char-to-string ?\uE001)
'display `((space :align-to 0 :height ,bottom-margin)))) )
(screencast-delete-overlays (point-min) (point-max))
(setq top-margin-overlay-string top-margin-overlay)
(setq bottom-margin-overlay-string bottom-margin-overlay)
(overlay-put (make-overlay window-start window-start)
'before-string top-margin-overlay)
(setq window-end (window-end nil t))
(goto-char window-end)
(vertical-motion (- 2 bottom-margin))
(setq end-point (point))
(overlay-put (make-overlay end-point end-point)
'before-string bottom-margin-overlay)
(setq screencast-overlays-exist-p t) )))
(defun screencast-remove-overlays (beg end name val)
"Remove the overlays."
;; DEBUGGING
;; (unless (and beg end name val)
;; (message "ERROR -- beg: %s | end: %s | name: %s | val: %s" beg end name val))
(when (and beg end name val)
(overlay-recenter end)
(dolist (o (overlays-in beg end))
(when (eq (overlay-get o name) val)
(delete-overlay o)))))
(defun screencast-delete-overlays (start end)
"Delete overlays from `start` to `end`."
(when screencast-overlays-exist-p
(dolist (ov `(
,top-margin-overlay-string
,bottom-margin-overlay-string ))
(screencast-remove-overlays start end 'before-string ov))
(setq screencast-overlays-exist-p nil)))
(defvar top-margin-overlay-string nil
"This local variable stores the `top-margin-overlay-string`.")
(make-variable-buffer-local 'top-margin-overlay-string)
(defvar bottom-margin-overlay-string nil
"This local variable stores the `bottom-margin-overlay-string`.")
(make-variable-buffer-local 'bottom-margin-overlay-string)
(defvar screencast-overlays-exist-p nil
"A simple test for the existence of screencast overlays.")
(make-variable-buffer-local 'screencast-overlays-exist-p)
(defvar screencast-old-window-start nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'screencast-old-window-start)
(defvar screencast-old-window-end nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'screencast-old-window-end)
(defvar screencast-old-window-end-forced nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'screencast-old-window-end-forced)
(defvar screencast-new-window-start nil
"This local variable is set within the `window-scroll-functions`.")
(make-variable-buffer-local 'screencast-new-window-start)
(defvar screencast-new-window-end nil
"This local variable is set within the `window-scroll-functions`.")
(make-variable-buffer-local 'screencast-new-window-end)
(defun screencast-post-command-hook ()
"NOT good for things like: `beginning-of-buffer`; `end-of-buffer`; `yank`; etc.
NOTE: When using `this-command` in conjunction with the `post-command-hook`,
the `window-scroll-functions` hook would need to use `last-command`."
(when
(and
(not (minibufferp))
(window-live-p (get-buffer-window (current-buffer))))
(setq screencast-old-window-start (window-start))
(setq screencast-old-window-end (window-end))
(setq screencast-old-window-end-forced (window-end nil t))
(when
(or
(and
(not (< (point) screencast-old-window-start))
(not (> (point) screencast-old-window-end))
(not (> (point) screencast-old-window-end-forced)))
;; special situation when deleting region greater than size of window.
(and
(region-active-p)
(< screencast-old-window-end 0))
;; special situation when deleting region greater than size of window.
(and
(region-active-p)
(> (point) screencast-old-window-start)
(> (point) screencast-old-window-end)
(< (point) screencast-old-window-end-forced)) )
(screencast-function screencast-old-window-start screencast-old-window-end screencast-old-window-end-forced nil nil) )))
(defun screencast-window-scroll-functions (win _start)
"Good for things like: `beginning-of-buffer`; `end-of-buffer`; `yank`; etc.
NOTE: When using `this-command` in conjunction with the `post-command-hook`,
the `window-scroll-functions` hook would need to use `last-command`."
(when
(and
screencast-old-window-start
screencast-old-window-end
screencast-old-window-end-forced
(not (minibufferp))
(window-live-p (get-buffer-window (current-buffer))))
(when
(or
(not (= _start screencast-old-window-start))
(< (point) screencast-old-window-start)
(> (point) screencast-old-window-end)
(> (point) screencast-old-window-end-forced))
(setq screencast-new-window-start _start)
(setq screencast-new-window-end (window-end win t))
(screencast-function nil nil nil screencast-new-window-start screencast-new-window-end)
(setq screencast-old-window-start nil)
(setq screencast-old-window-end nil)
(setq screencast-old-window-end-forced nil))))
(define-minor-mode screencast-mode
"A minor-mode for screencasts."
:init-value nil
:lighter " screencast"
:keymap nil
:global nil
:group nil
(cond
(screencast-mode
(condition-case error
(progn
(setq scroll-conservatively 101)
(add-hook 'post-command-hook 'screencast-post-command-hook nil t)
(add-hook 'window-scroll-functions 'screencast-window-scroll-functions nil t)
(message "Turned ON `screencast-mode`."))
(error
(screencast-mode 0)
(signal (car error) (cdr error)))))
((not screencast-mode)
(remove-hook 'post-command-hook 'screencast-post-command-hook t)
(remove-hook 'window-scroll-functions 'screencast-window-scroll-functions t)
(screencast-delete-overlays (point-min) (point-max))
(message "Turned OFF `screencast-mode`.") )))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;