]> code.delx.au - gnu-emacs-elpa/blob - packages/on-screen/on-screen.el
new package: on-screen
[gnu-emacs-elpa] / packages / on-screen / on-screen.el
1 ;;; on-screen.el --- guide your eyes while scrolling -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2015 Free Software Foundation, Inc
4
5 ;; Author: Michael Heerdegen <michael_heerdegen@web.de>
6 ;; Maintainer: Michael Heerdegen <michael_heerdegen@web.de>
7 ;; Created: 24 Jan 2013
8 ;; Keywords: convenience
9 ;; Version: 1.3
10 ;; Package-Requires: ((cl-lib "0"))
11
12
13 ;; This file is not 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 3 of the License, or
18 ;; (at your option) 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. If not, see <http://www.gnu.org/licenses/>.
27
28
29 ;;; Commentary:
30
31 ;; Scrolling can be distracting because your eyes may lose
32 ;; orientation. This library implements a minor mode that highlights
33 ;; the previously visible buffer part after each scroll.
34 ;;
35 ;; Installation: Put this library somewhere in your load-path, or
36 ;; install via M-x package-list-packages. Then add to your init-file:
37 ;;
38 ;; (require 'on-screen)
39 ;;
40 ;; To invoke on-screen globally for all buffers, also add
41 ;;
42 ;; (on-screen-global-mode +1)
43 ;;
44 ;; Alternatively you can use the buffer local version `on-screen-mode'.
45 ;; For example, add this line to your init file:
46 ;;
47 ;; (add-hook 'Info-mode-hook 'on-screen-mode)
48 ;;
49 ;; to enable it in all Info buffers.
50 ;;
51 ;; By default, fringe markers are used for highlighting - see
52 ;; `on-screen-highlight-method' to change that. Type M-x
53 ;; customize-group RET on-screen RET to see what else can be
54 ;; configured. If you use a configuration file (.emacs), you may also
55 ;; want to define mode specific settings by using buffer local
56 ;; variables. For example, to use non intrusive fringe markers by
57 ;; default, but transparent overlays in w3m, you would add
58 ;;
59 ;; (add-hook
60 ;; 'w3m-mode-hook
61 ;; (defun my-w3m-setup-on-screen ()
62 ;; (setq-local on-screen-highlight-method 'shadow)))
63 ;;
64 ;; to your .emacs.
65 ;;
66 ;; If you use transparent overlays for highlighting and there is the
67 ;; library "hexrgb.el" in your `load-path', it will be used to compute
68 ;; highlighting colors dynamically instead of using constant faces.
69 ;; I.e. if you use non-default background colors (e.g. from custom
70 ;; themes), on-screen will try to perform highlighting with a
71 ;; suitable, slightly different color. See
72 ;; `on-screen-highlighting-to-background-delta' to control this.
73 ;;
74 ;;
75 ;; Implementation notes (mainly for myself):
76 ;;
77 ;; Implementing this functionality is not as straightforward as one
78 ;; might think. There are commands that scroll other windows than the
79 ;; current one. Not only scrolling commands can scroll text - also
80 ;; editing or even redisplay can cause windows to scroll. There is
81 ;; weird stuff such as folding and narrowing, influencing the visible
82 ;; buffer part. And although highlighting is realized in the
83 ;; displayed buffers (with overlays), it must be organized on a
84 ;; per-window basis, because different buffer parts may be displayed
85 ;; in different windows, and their highlightings must not interfere.
86 ;;
87 ;; That all makes it necessary to observe windows via hacks in
88 ;; different hooks, and to manage information about buffers, visible
89 ;; parts and timers in a data structure (`on-screen-data'). It is
90 ;; realized as an association list whose keys are windows. There are
91 ;; some pitfalls - e.g. the data can be out of date if the window
92 ;; configuration has changed and windows display different buffers
93 ;; now. The data must be updated, but not simply be thrown away,
94 ;; because the highlightings in the old buffers must be removed
95 ;; nonetheless.
96 ;;
97 ;;
98 ;; Acknowledgments:
99 ;;
100 ;; This library was inspired by a similar feature of the "Conqueror"
101 ;; web browser.
102 ;;
103 ;; Thanks for Drew Adams for testing and contributions.
104
105
106
107
108 ;;; Code:
109
110 ;;;; Requirements
111
112 (require 'cl-lib)
113 (require 'timer)
114 (require 'hexrgb nil t)
115
116 (declare-function hexrgb-saturation "hexrgb")
117 (declare-function hexrgb-approx-equal "hexrgb")
118 (declare-function hexrgb-increment-value "hexrgb")
119 (declare-function hexrgb-increment-hue "hexrgb")
120
121
122 ;;;; Configuration stuff
123
124 (defgroup on-screen nil
125 "Guide your eyes while scrolling."
126 :group 'convenience
127 :prefix "on-screen")
128
129 (defcustom on-screen-inverse-flag nil
130 "What area to highlight.
131 When nil, highlight the previously visible screenful. Else
132 highlight the previously off-screen parts."
133 :group 'on-screen :type 'boolean)
134
135 (defcustom on-screen-highlight-method 'fringe
136 "Type of highlighting used by `on-screen-mode'.
137 The following values are valid:
138
139 fringe - graphical markers in the fringe
140 shadow - transparent overlay on the text
141 line - transparent overlay on the confining text lines
142 narrow-line - narrow horizontal lines
143
144 The fringe and the narrow-line methods only work on graphical
145 displays. narrow-line only works with Emacs 24 or higher.
146
147 `on-screen-inverse-flag' defines which part(s) of the buffers are
148 highlighted.
149
150 The face used for \"shadow\" and \"line\" may be computed
151 dynamically to support different background colors (color themes)
152 - see `on-screen-highlighting-to-background-delta'."
153 :type '(choice
154 (const :tag "Fringe markers" fringe)
155 (const :tag "Transparent overlay" shadow)
156 (const :tag "Overlay on confining text lines" line)
157 (const :tag "Narrow horizontal line" narrow-line))
158 :group 'on-screen)
159
160 (defcustom on-screen-fringe-marker-position t
161 "Where to display fringe markers.
162 Ignored if highlighting doesn't use the fringe."
163 :type '(choice
164 (const :tag "Left fringe only" left)
165 (const :tag "Right fringe only" right)
166 (const :tag "Both sides" t))
167 :group 'on-screen)
168
169 (defface on-screen-shadow
170 '((((class color) (min-colors 88) (background light))
171 :background "#f2efcb" ; alternative: "#f5f4ff" is a bit less intrusive
172 )
173 (((class color) (min-colors 88) (background dark))
174 :background "#272620")
175 (((class color) (min-colors 8) (background light))
176 :background "green")
177 (((class color) (min-colors 8) (background dark))
178 :background "blue"))
179 "Face used for displaying a transparent overlay."
180 :group 'on-screen)
181
182 (defface on-screen-hl-line
183 '((((background light)) :background "#ffa0a0")
184 (((background dark)) :background "#300000"))
185 "Face used for displaying the \"line\" style overlay."
186 :group 'on-screen)
187
188 (defcustom on-screen-highlighting-to-background-delta .05
189 "How much shadow and line highlighting should differ from background.
190 This should be a positive floating point number less than 1.
191 Smaller values will lead to a highlighting color being more
192 similar to the frame background. A value of nil means to use use
193 just face `on-screen-shadow'.
194
195 This variable is ignored if the library \"hexrgb\" is not
196 available."
197 :group 'on-screen
198 :type '(choice (const :tag "Use standard face" nil)
199 (float :tag "Delta")))
200
201 (defface on-screen-fringe '((t (:inherit shadow)))
202 "Face used for fringe markers."
203 :group 'on-screen)
204
205 (defface on-screen-narrow-line
206 '((((background dark)) (:width extra-expanded :underline (:color "gray30" :style wave)))
207 (((background light)) (:width extra-expanded :underline (:color "gray70" :style wave))))
208 "Face used by the narrow-line highlighting method."
209 :group 'on-screen)
210
211 (defcustom on-screen-delay 5
212 "How long `on-screen-mode' should display optical aids."
213 :group 'on-screen :type 'number)
214
215 (defcustom on-screen-auto-update t
216 "Whether to update highlighting for successive scrolls.
217 When non-nil, every scroll action will cause a highlighting
218 according to the previously visible screenful. When nil, a once
219 drawn highlighting will remain fixed relative to the text even
220 if you scroll further until `on-screen-delay' is over."
221 :group 'on-screen :type 'boolean)
222
223 (defcustom on-screen-remove-when-edit nil
224 "Whether to instantly remove highlighting when editing.
225
226 In those situations where a single command causes multiple
227 changes to a buffer highlighting is always removed to avoid
228 confusion."
229 :group 'on-screen :type 'boolean)
230
231 (defvar on-screen-treat-cut-lines--default-fraction .3)
232
233 (defcustom on-screen-treat-cut-lines nil
234 "Whether to care about vertically cut lines.
235 If nil, always count lines at the window start or end that are
236 only partially visible as part of the visible area. Else, a
237 number between 0 and 1, meaning that lines will count as visible
238 when the hidden part of them is less than this number. Note that
239 a non-nil value may make scrolling stuttering on slow computers."
240 :group 'on-screen
241 :type `(choice (const :tag "Count partially visible lines as visible" nil)
242 (const :tag "Count partially visible lines as not visible" t)
243 (float
244 :tag "Count lines with hidden part less than this as visible"
245 :value ,on-screen-treat-cut-lines--default-fraction)))
246
247 (defcustom on-screen-drawing-threshold 2
248 "If set, highlight only when scrolled at least that many lines."
249 :group 'on-screen
250 :type '(choice (const :tag "Off" nil)
251 (integer :value 2)))
252
253 (defvar on-screen-inhibit-highlighting nil
254 "Disable highlighting if non-nil.
255 This variable is checked before highlighting is actually being
256 performed, with the according buffer being current.
257
258 If a function, it will be called with zero arguments.
259 Highlighting will be inhibited if the result is non-nil.")
260
261
262 ;;;; Other variables
263
264 (defvar on-screen-overlay-priority 30 ; > stripe buffer, < ediff, isearch
265 "Priority for all on-screen overlays.")
266
267 (defvar on-screen-initialized-p nil
268 "Whether we have already added stuff to the hooks.")
269
270 (defvar on-screen-data nil
271 "Association list holding internal data.")
272
273 (defvar on-screen-command-counter 0)
274 (defvar on-screen-last-change 0)
275
276
277 ;;;; User Commands
278
279 ;;;###autoload
280 (define-minor-mode on-screen-mode
281 "Buffer local minor mode guiding your eyes while scrolling.
282 With a prefix argument ARG, enable the mode if ARG is positive,
283 and disable it otherwise. If called from Lisp, enable the mode
284 if ARG is omitted or nil.
285 Type M-x customize-group on-screen RET for configuration."
286 :group 'on-screen
287 (when on-screen-mode
288 (unless on-screen-initialized-p
289 (on-screen-initialize))))
290
291 ;;;###autoload
292 (define-minor-mode on-screen-global-mode
293 "Global minor mode guiding your eyes while scrolling.
294 With a prefix argument ARG, enable the mode if ARG is positive,
295 and disable it otherwise. If called from Lisp, enable the mode
296 if ARG is omitted or nil.
297
298 You can make use of `on-screen-inhibit-highlighting' to prevent
299 highlighting on a per-buffer basis.
300
301 Type M-x customize-group on-screen RET for configuration."
302 :group 'on-screen :global t
303 (when on-screen-global-mode
304 (unless on-screen-initialized-p
305 (on-screen-initialize))))
306
307 ;;;###autoload
308 (defalias 'global-on-screen-mode 'on-screen-global-mode)
309
310
311 ;;;; Internal functions
312
313 (defun on-screen--treat-cut-lines-get-fraction ()
314 (if (floatp on-screen-treat-cut-lines)
315 on-screen-treat-cut-lines
316 on-screen-treat-cut-lines--default-fraction))
317
318 (defun on-screen-window-start (&optional window)
319 "Like `window-start', but exclude partially visible lines."
320 (let* ((start (window-start window))
321 (vis (and on-screen-treat-cut-lines (pos-visible-in-window-p start window t))))
322 (if (not (cddr vis))
323 start
324 (cl-destructuring-bind (_x _y rtop _rbot rowh _vpos) vis
325 (if (< (/ (float rtop) (+ rtop rowh))
326 (on-screen--treat-cut-lines-get-fraction)) ; count as visible
327 start
328 (with-current-buffer (window-buffer window)
329 (save-excursion
330 (goto-char start)
331 (on-screen-beginning-of-line +2)
332 (point))))))))
333
334 (defun on-screen-window-end (&optional window)
335 "Like `window-end', but exclude partially visible lines."
336 (let* ((end (window-end window))
337 (vis (and on-screen-treat-cut-lines (pos-visible-in-window-p (1- end) window t))))
338 (if (not (cddr vis))
339 end
340 (cl-destructuring-bind (_x _y _rtop rbot rowh _vpos) vis
341 (if (< (/ (float rbot) (+ rbot rowh))
342 (on-screen--treat-cut-lines-get-fraction)) ; count as visible
343 end
344 (with-current-buffer (window-buffer window)
345 (save-excursion
346 (goto-char end)
347 (on-screen-beginning-of-line 0)
348 (point))))))))
349
350 (defun on-screen-beginning-of-line (&optional n)
351 (cl-callf or n 1)
352 (forward-visible-line (- n 1)))
353
354 (defun on-screen-end-of-line (&optional n)
355 (cl-callf or n 1)
356 (forward-visible-line (- n 1))
357 (end-of-visible-line))
358
359 (defun on-screen-record-data (win area &optional timer overlays)
360 ;; The collected data has the form ((beg end) timer overlays), and
361 ;; this is what `on-screen-get-data' returns. Internally, this
362 ;; function also remembers the window-buffer of the window, to
363 ;; enable the mode to check if remembered data still belongs to the
364 ;; same buffer.
365 "Store information for window WIN in `on-screen-data'.
366 AREA is a list (beg end). TIMER is the currently active timer
367 object. OVERLAYS are the on-screen overlays currently visible in
368 WIN.
369
370 A nil value for AREA, TIMER or OVERLAYS means that the remembered
371 values should not be changed. If TIMER is the symbol `finished',
372 remember nil for the timer."
373 (let* ((entry (assoc win on-screen-data))
374 (data (cdr entry))
375 (same-buffer-p (eq (car data) (window-buffer win))))
376 (setq area (or area (and same-buffer-p (cadr data)))
377 timer (cond ((timerp timer) timer)
378 (timer nil)
379 (t (and same-buffer-p (cl-caddr data))))
380 overlays (or overlays (and same-buffer-p (cl-cadddr data)))
381 data `(,(window-buffer win) ,area ,timer ,overlays))
382 (if entry
383 (setcdr entry data)
384 (push (cons win data) on-screen-data))))
385
386 (defun on-screen-get-data (win)
387 "Return stored data for WIN if existent and up-to-date."
388 (let ((data (cdr (assoc win on-screen-data))))
389 (if (eq (car data) (window-buffer win))
390 (cdr data)
391 nil)))
392
393 (defun on-screen-cleanup-data ()
394 "Delete information stored for deleted windows."
395 (setq on-screen-data
396 (delq nil (mapcar (lambda (entry) (if (window-live-p (car entry)) entry nil))
397 on-screen-data))))
398
399 (defun on-screen-derive-from-frame-bg
400 (win delta-brightness-dark-bg delta-brightness-light-bg delta-hue)
401 "Helper calculating a suitable background color for highlighting."
402 (let ((frame (window-frame win)))
403 (and (display-graphic-p frame) (featurep 'hexrgb)
404 (let* ((bg (or (let ((frame-bg (cdr (assq 'background-color (frame-parameters frame)))))
405 (when (member frame-bg '(nil unspecified "unspecified-bg"))
406 (setq frame-bg (if (eq (frame-parameter frame 'background-mode) 'dark)
407 "Black"
408 "White")))
409 (and frame-bg (x-color-defined-p frame-bg) frame-bg))))
410 (sat (condition-case nil (hexrgb-saturation bg) (error nil))))
411 (and sat
412 (if (hexrgb-approx-equal sat 0.0)
413 ;; Grayscale - change bg value slightly.
414 (hexrgb-increment-value
415 bg (if (eq (frame-parameter frame 'background-mode) 'dark)
416 delta-brightness-dark-bg
417 delta-brightness-light-bg))
418 (hexrgb-increment-hue bg delta-hue)) ; Color - change bg hue slightly.
419 )))))
420
421 (defun on-screen-get-shadow-face (win)
422 "Return face for the transparent overlay in WIN."
423 (if (eq on-screen-highlight-method 'shadow)
424 (or (and on-screen-highlighting-to-background-delta
425 (let ((bg-col (apply #'on-screen-derive-from-frame-bg win
426 (mapcar (lambda (x) (* x on-screen-highlighting-to-background-delta))
427 (list 1 -1 1)))))
428 (and bg-col `((t (:background ,bg-col))))))
429 'on-screen-shadow)
430 'on-screen-hl-line))
431
432 (defun on-screen-make-fringe-overlays (pos topp &optional inversep)
433 "Create and return list of fringe overlays."
434 (let (ov1 ov2)
435 (unless (eq on-screen-fringe-marker-position 'left)
436 (setq ov1 (save-excursion (make-overlay (progn (goto-char pos)
437 (on-screen-beginning-of-line
438 (cond ((not inversep) +1)
439 (topp +2)
440 (t 0)))
441 (point))
442 (1+ (point)))))
443 (overlay-put ov1 'before-string (on-screen-fringe-string topp nil inversep)))
444 (unless (eq on-screen-fringe-marker-position 'right)
445 (setq ov2 (save-excursion (make-overlay (progn (goto-char pos)
446 (on-screen-beginning-of-line
447 (cond ((not inversep) +1)
448 (topp +2)
449 (t 0)))
450 (point))
451 (1+ (point)))))
452 (overlay-put ov2 'before-string (on-screen-fringe-string topp t inversep)))
453 (delq nil (list ov1 ov2))))
454
455 (defun on-screen-fringe-string (topp leftp &optional inversep)
456 "Return a string suitable for displaying fringe markers."
457 (let ((xor (lambda (x y) (if x (not y) y))))
458 (propertize (purecopy " ")
459 'display (list (if leftp 'left-fringe 'right-fringe)
460 (if (funcall xor topp (not inversep))
461 (if leftp 'top-left-angle 'top-right-angle)
462 (if leftp 'bottom-left-angle 'bottom-right-angle))
463 'on-screen-fringe))))
464
465 (defun on-screen-make-line-overlay (pos)
466 "Create an overlay around POS for the line method."
467 (save-excursion
468 (make-overlay (progn (goto-char pos) (on-screen-beginning-of-line) (point))
469 (progn (goto-char pos) (on-screen-end-of-line) (1+ (point))))))
470
471 (defun on-screen-make-narrow-line-overlay (win pos)
472 "Create an overlay around POS for the narrow-line method."
473 (let ((ov (save-excursion
474 (make-overlay (progn (goto-char pos) (on-screen-beginning-of-line) (point))
475 (progn (goto-char pos) (on-screen-end-of-line) (point))))))
476 (overlay-put ov 'face 'on-screen-narrow-line)
477 ;; The following is necessary to get a line spanning the entire
478 ;; window width, because underlining is only applied to text - a
479 ;; problem especially for empty lines. However this hides any
480 ;; other highlighting there, e.g. from stripe-buffer or
481 ;; hl-line-mode. I think there's nothing I can do about that.
482 (overlay-put ov 'after-string (propertize "foo"
483 'face 'on-screen-narrow-line
484 'display `(space :align-to ,(window-width win))
485 'cursor 0))
486 ov))
487
488 (defun on-screen-get-windows (&optional all-frames)
489 "Return a list of all windows.
490 With ALL-FRAMES non-nil, include all windows of all frames, else
491 only the windows of the selected frame."
492 (apply #'nconc
493 (mapcar (lambda (frame) (window-list frame))
494 (if all-frames (frame-list) (list (selected-frame))))))
495
496 (defun on-screen-pre-command ()
497 "Remember visible buffer parts in the selected frame."
498 ;; This normally goes to `pre-command-hook'.
499 (cl-incf on-screen-command-counter)
500 (add-hook 'after-change-functions #'on-screen-after-change) ;$$$$ bug#16796
501 (condition-case nil
502 (mapc (lambda (win) (with-current-buffer (window-buffer win)
503 (when (on-screen-enabled-p)
504 (on-screen-record-data win (list (on-screen-window-start win)
505 (on-screen-window-end win))))))
506 (on-screen-get-windows))
507 ((debug error) nil)))
508
509 (defun on-screen-after-scroll (win display-start)
510 "DTRT after scrolling.
511 This should normally go to `window-scroll-functions'."
512 (condition-case nil
513 (with-current-buffer (window-buffer win)
514 (when (on-screen-enabled-p)
515 (let* ((win-data (on-screen-get-data win))
516 (area (car win-data))
517 (timer (cadr win-data))
518 (overlays (cl-caddr win-data))
519 (s1 (car area))
520 (s2 (cadr area)))
521 (when (and
522 on-screen-auto-update
523 (timerp timer)
524 ;; avoid removing highlighting when `window-scroll-functions' is
525 ;; called multiple times in succession (follow-mode does that)
526 (not (eq (car-safe area) (on-screen-window-start win))))
527 ;; do what the timer would do, and cancel timer
528 (on-screen-remove-highlighting win)
529 (cancel-timer timer)
530 (on-screen-record-data win area 'finished)
531 (setq timer nil))
532 (cond
533 ((timerp timer)
534 (timer-set-time timer (timer-relative-time (current-time) on-screen-delay)))
535 ((or (not area)
536 (= display-start s1)))
537 ((and (numberp on-screen-drawing-threshold)
538 (< (abs (apply #'count-lines (sort (list display-start s1) #'<)))
539 on-screen-drawing-threshold)))
540 (t
541 (setq
542 overlays
543 (let ((method `(,on-screen-highlight-method . ,on-screen-inverse-flag)))
544
545 ;; prevent highlighting in certain situations
546 ;; note that `window-end' must not be used here!
547
548 (when (and s1 s2
549 (pos-visible-in-window-p (point-min) win)
550 (pos-visible-in-window-p (point-max) win))
551 ;; after narrow
552 (setq s1 nil s2 nil))
553
554 (when (and s1 s2
555 (>= s2 (point-max))
556 (< s1 (on-screen-window-start win))
557 (pos-visible-in-window-p (point-max) win))
558 ;;scrolling down near buffer end
559 (setq s2 nil))
560
561 (cond
562 ((equal method '(shadow . nil))
563 (if (and s1 s2) (list (make-overlay s1 s2)) ()))
564 ((eq (car method) 'shadow)
565 (list (and s1 (make-overlay (point-min) s1))
566 (and s2 (make-overlay s2 (point-max)))))
567 ((eq (car method) 'fringe)
568 (append (and s1 (on-screen-make-fringe-overlays s1 nil (cdr method)))
569 (and s2 (on-screen-make-fringe-overlays (1- s2) t (cdr method)))))
570 ((equal method '(line . nil))
571 (list (and s1 (on-screen-make-line-overlay s1))
572 (and s2 (on-screen-make-line-overlay (1- s2)))))
573 ((eq (car method) 'line)
574 (list (and s1 (on-screen-make-line-overlay (1- s1)))
575 (and s2 (on-screen-make-line-overlay s2))))
576 ((eq (car method) 'narrow-line)
577 (list (and s1 (on-screen-make-narrow-line-overlay win (1- s1)))
578 (and s2 (on-screen-make-narrow-line-overlay win (1- s2)))))))
579 overlays (delq nil overlays))
580 (dolist (ov overlays)
581 (overlay-put ov 'window win) ; display only in selected window
582 (overlay-put ov 'priority on-screen-overlay-priority))
583 (when (memq on-screen-highlight-method '(shadow line))
584 (dolist (ov overlays)
585 (overlay-put ov 'face (on-screen-get-shadow-face win))))
586 (on-screen-record-data
587 win nil
588 (run-at-time (time-add (current-time) (seconds-to-time on-screen-delay)) nil
589 (lambda (win)
590 (condition-case nil
591 (progn
592 (when (window-live-p win)
593 (with-current-buffer (window-buffer win)
594 (on-screen-remove-highlighting win)
595 (on-screen-record-data
596 win (list (on-screen-window-start win)
597 (on-screen-window-end win))
598 'finished)))
599 (on-screen-cleanup-data))
600 ((debug error) nil)))
601 win)
602 overlays))))))
603 ((debug error) nil)))
604
605 (defun on-screen-remove-highlighting (win)
606 "Delete all on-screen overlays in window WIN.
607 This has to be done for a previously buffer if the window-buffer
608 had changed."
609 (let* ((entry (assoc win on-screen-data))
610 (data (cdr entry))
611 (buffer (car data)))
612 (when (buffer-live-p buffer)
613 (with-current-buffer buffer
614 (let* ((data (cdr data))
615 (timer (cadr data))
616 (overlays (cl-caddr data)))
617 (dolist (ov overlays) (delete-overlay ov))
618 (when (timerp timer) (cancel-timer timer))))
619 (setq on-screen-data (delq entry on-screen-data)))))
620
621 (defun on-screen-after-change (&rest _)
622 "Reset highligting for current buffer after it was changed.
623 This has to be done for all its windows. Goes to
624 `after-change-functions'."
625 (when (or on-screen-remove-when-edit
626 (= on-screen-last-change on-screen-command-counter))
627 (let ((buf (current-buffer)))
628 (when (on-screen-enabled-p buf)
629 (dolist (win (on-screen-get-windows t))
630 (when (eq (window-buffer win) buf)
631 (on-screen-remove-highlighting win))))))
632 (setq on-screen-last-change on-screen-command-counter))
633
634 (defun on-screen-after-wconf-change ()
635 "Clean up after the window configuration has changed.
636 I.e., for all windows of the selected frame, remove all
637 highlightings and clear all associated data."
638 (let ((wins (on-screen-get-windows)))
639 (dolist (win wins)
640 (on-screen-remove-highlighting win))))
641
642 (defun on-screen-enabled-p (&optional buffer)
643 "Return non-nil if on-screen is enabled in BUFFER."
644 (with-current-buffer (or buffer (current-buffer))
645 (and
646 (if on-screen-global-mode t on-screen-mode)
647 (cond
648 ((not on-screen-inhibit-highlighting) t)
649 ((functionp on-screen-inhibit-highlighting)
650 (not (funcall on-screen-inhibit-highlighting)))
651 (t nil)))))
652
653 (defun on-screen-initialize ()
654 "Prepare for using on-screen."
655 (add-hook 'pre-command-hook #'on-screen-pre-command)
656 (add-hook 'window-scroll-functions #'on-screen-after-scroll)
657 (add-hook 'after-change-functions #'on-screen-after-change)
658 (add-hook 'window-configuration-change-hook #'on-screen-after-wconf-change)
659 (setq on-screen-initialized-p t))
660
661 (defun on-screen-unload-function ()
662 "Function to run when unloading on-screen."
663 (remove-hook 'pre-command-hook #'on-screen-pre-command)
664 (remove-hook 'window-scroll-functions #'on-screen-after-scroll)
665 (remove-hook 'after-change-functions #'on-screen-after-change)
666 (remove-hook 'window-configuration-change-hook #'on-screen-after-wconf-change)
667 nil)
668
669
670 (provide 'on-screen)
671
672 ;;; on-screen.el ends here