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