diff options
| author | cashmere <cashmere@autistici.org> | 2026-04-30 10:41:50 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-04-30 10:41:50 +0200 |
| commit | 3a198ba6586562cbae33130de226ae5b73f3958c (patch) | |
| tree | 30d91a02cf24d29219044f40e244f268131ad7ae /kitty-graphics.el | |
| parent | 308fc78775058d583449b6ffcdc227cf7f1280af (diff) | |
| download | kitty-graphics-master.tar.gz | |
This reverts commit 308fc78775058d583449b6ffcdc227cf7f1280af, reversing
changes made to 632b33b6ecbc3e2a7c8277effcac9b2de0fff5a9.
Diffstat (limited to 'kitty-graphics.el')
| -rw-r--r-- | kitty-graphics.el | 525 |
1 files changed, 95 insertions, 430 deletions
diff --git a/kitty-graphics.el b/kitty-graphics.el index 7fafd62..7ae9cb7 100644 --- a/kitty-graphics.el +++ b/kitty-graphics.el @@ -895,14 +895,13 @@ Sixel has no placement IDs — erase by writing spaces over the region." (defun kitty-gfx--sixel-cleanup (file _image-id) "Cleanup Sixel resources for FILE." - (when file - (remhash file kitty-gfx--sixel-cache) - ;; Remove cached sixel encodings for this file - (dolist (temp-file kitty-gfx--sixel-temp-files) - (when (string-match-p (regexp-quote (md5 file)) temp-file) - (kitty-gfx--log "sixel-cleanup: deleting %s" temp-file) - (ignore-errors (delete-file temp-file)) - (setq kitty-gfx--sixel-temp-files (delete temp-file kitty-gfx--sixel-temp-files)))))) + (remhash file kitty-gfx--sixel-cache) + ;; Remove cached sixel encodings for this file + (dolist (temp-file kitty-gfx--sixel-temp-files) + (when (string-match-p (regexp-quote (md5 file)) temp-file) + (kitty-gfx--log "sixel-cleanup: deleting %s" temp-file) + (ignore-errors (delete-file temp-file)) + (setq kitty-gfx--sixel-temp-files (delete temp-file kitty-gfx--sixel-temp-files))))) (defun kitty-gfx--sixel-cleanup-all () "Cleanup all Sixel resources. @@ -1404,19 +1403,14 @@ Ignores cosmetic invisibility like hidden link brackets (`org-link')." (kitty-gfx--log "in-folded-region: pos=%d folded=%s" pos folded)) folded)) -(defun kitty-gfx--overlay-screen-pos (ov &optional win) - "Return (TERM-ROW . TERM-COL) for overlay OV in WIN, or nil if hidden. -Coordinates are 1-indexed terminal positions. WIN defaults to a window -showing OV's buffer, for interactive debug helpers. +(defun kitty-gfx--overlay-screen-pos (ov) + "Return (TERM-ROW . TERM-COL) for overlay OV, or nil if not visible. +Coordinates are 1-indexed terminal positions. Returns nil when the overlay position is outside the visible window range, inside a folded region, or not visible on screen." (let* ((buf (overlay-buffer ov)) (pos (overlay-start ov)) - (win (and buf - (or (and (window-live-p win) - (eq (window-buffer win) buf) - win) - (get-buffer-window buf))))) + (win (and buf (get-buffer-window buf)))) ;; Fast path: skip entirely if no window, no position, or ;; buffer position is outside the visible window range. ;; This avoids expensive posn-at-point and fold checks. @@ -1439,25 +1433,13 @@ range, inside a folded region, or not visible on screen." (let* ((col-row (posn-col-row win-pos)) (row (cdr col-row)) (posn-col (car col-row)) - (posn-xy (posn-x-y win-pos)) - ;; `posn-col-row' is derived from pixel coordinates and can - ;; report the position after an overlay's `display' string - ;; rather than the overlay's logical start. That is exactly - ;; the wrong edge for Sixel placement: the terminal graphic - ;; must be emitted at the top-left of the reserved cells. - ;; In terminal Emacs text cells are fixed-width, so the - ;; buffer column at POS is the reliable horizontal anchor. - (buffer-col (save-excursion - (goto-char pos) - (current-column))) - (visual-col (max 0 (- buffer-col (window-hscroll win))))) - (kitty-gfx--log "screen-pos-detail: pid=%s posn-col=%d buffer-col=%d visual-col=%d posn-row=%d posn-xy=%S body-left=%d body-top=%d" - (overlay-get ov 'kitty-gfx-pid) posn-col buffer-col - visual-col row posn-xy body-left body-top) + (posn-xy (posn-x-y win-pos))) + (kitty-gfx--log "screen-pos-detail: pid=%s posn-col=%d posn-row=%d posn-xy=%S body-left=%d body-top=%d" + (overlay-get ov 'kitty-gfx-pid) posn-col row posn-xy body-left body-top) (when col-row - (let ((result (cons (+ body-top row 1) (+ body-left visual-col 1)))) - (kitty-gfx--log "screen-pos: pid=%s pos=%d win=%s -> row=%d col=%d" - (overlay-get ov 'kitty-gfx-pid) pos win + (let ((result (cons (+ body-top row 1) (+ body-left posn-col 1)))) + (kitty-gfx--log "screen-pos: pid=%s pos=%d -> row=%d col=%d" + (overlay-get ov 'kitty-gfx-pid) pos (car result) (cdr result)) result)))))))) @@ -1529,19 +1511,6 @@ to prevent flicker." (lambda (win) (with-current-buffer (window-buffer win) (when kitty-gfx--overlays - ;; A single overlay can be visible in multiple windows showing - ;; the same buffer. Its legacy last-row/last-col properties - ;; mirror the placement for the window currently being - ;; refreshed; per-window placement tracking below keeps the - ;; individual terminal regions deletable when one window later - ;; disappears. - (dolist (ov kitty-gfx--overlays) - (let ((placement (kitty-gfx--image-placement ov win))) - (unless (overlay-get ov 'kitty-gfx-heading) - (overlay-put ov 'kitty-gfx-last-row - (plist-get (cdr placement) :row)) - (overlay-put ov 'kitty-gfx-last-col - (plist-get (cdr placement) :col))))) ;; Prune dead overlays (overlay-buffer returns nil) (let ((before (length kitty-gfx--overlays))) (setq kitty-gfx--overlays @@ -1557,7 +1526,7 @@ to prevent flicker." win (buffer-name) (length kitty-gfx--overlays) win-bottom) (dolist (ov kitty-gfx--overlays) (cl-incf total-overlays) - (kitty-gfx--refresh-overlay ov win win-bottom) + (kitty-gfx--refresh-overlay ov win-bottom) (if (overlay-get ov 'kitty-gfx-last-row) (cl-incf placed) (cl-incf hidden))))) @@ -1573,22 +1542,18 @@ to prevent flicker." (kitty-gfx--log "refresh: done total=%d placed=%d hidden=%d pruned=%d" total-overlays placed hidden pruned)))) -(defun kitty-gfx--refresh-overlay (ov win win-bottom) - "Refresh a single overlay OV in WIN. -WIN-BOTTOM is WIN's bottom edge. Dispatches to heading or image -refresh based on overlay type." +(defun kitty-gfx--refresh-overlay (ov win-bottom) + "Refresh a single overlay OV. WIN-BOTTOM is the window's bottom edge. +Dispatches to heading or image refresh based on overlay type." (if (overlay-get ov 'kitty-gfx-heading) ;; Heading overlay — phase 1: compute position + erase if moved. ;; OSC 66 emission happens in phase 2 (kitty-gfx--emit-heading-overlays). - (kitty-gfx--refresh-heading-overlay ov win win-bottom) + (kitty-gfx--refresh-heading-overlay ov win-bottom) ;; Image overlay refresh - (let* ((pos (kitty-gfx--overlay-screen-pos ov win)) - (rows (overlay-get ov 'kitty-gfx-rows)) - (cols (overlay-get ov 'kitty-gfx-cols)) - (placement (kitty-gfx--image-placement ov win)) - (placement-data (cdr placement)) - (last-row (plist-get placement-data :row)) - (last-col (plist-get placement-data :col))) + (let ((pos (kitty-gfx--overlay-screen-pos ov)) + (rows (overlay-get ov 'kitty-gfx-rows)) + (last-row (overlay-get ov 'kitty-gfx-last-row)) + (last-col (overlay-get ov 'kitty-gfx-last-col))) (let ((pid (overlay-get ov 'kitty-gfx-pid)) (id (overlay-get ov 'kitty-gfx-id))) (if (and pos @@ -1611,29 +1576,24 @@ refresh based on overlay type." new-row new-col) (overlay-put ov 'kitty-gfx-last-row new-row) (overlay-put ov 'kitty-gfx-last-col new-col) - (kitty-gfx--record-image-placement ov win new-row new-col cols rows pid) - (setq placement (kitty-gfx--image-placement ov win) - pid (plist-get (cdr placement) :pid)) (funcall (kitty-gfx--backend-fn 'place) - ov id pid cols rows new-row new-col))) - ;; Not visible or overflows — delete if was placed in this window - (when placement - (kitty-gfx--log "refresh-ov: pid=%d hiding in win=%s (was row=%d col=%d)" - pid win last-row last-col) - (kitty-gfx--delete-image-placement ov placement) - (kitty-gfx--forget-image-placement ov win) + ov id pid (overlay-get ov 'kitty-gfx-cols) rows new-row new-col))) + ;; Not visible or overflows — delete if was placed + (when last-row + (kitty-gfx--log "refresh-ov: pid=%d hiding (was row=%d col=%d)" + pid last-row last-col) (overlay-put ov 'kitty-gfx-last-row nil) - (overlay-put ov 'kitty-gfx-last-col nil))))))) + (overlay-put ov 'kitty-gfx-last-col nil) + (funcall (kitty-gfx--backend-fn 'delete) ov id pid))))))) -(defun kitty-gfx--refresh-heading-overlay (ov win win-bottom) - "Refresh heading overlay OV in WIN. -WIN-BOTTOM is WIN's bottom edge. Phase 1 of two-phase heading -refresh: computes screen position, erases old multicell block if -heading moved or became hidden, and caches the new position. Does -NOT emit OSC 66 — that happens in phase 2 -(`kitty-gfx--emit-heading-overlays') to avoid posn-at-point redraws -destroying freshly-placed blocks." - (let ((pos (kitty-gfx--overlay-screen-pos ov win)) +(defun kitty-gfx--refresh-heading-overlay (ov win-bottom) + "Refresh heading overlay OV. WIN-BOTTOM is the window's bottom edge. +Phase 1 of two-phase heading refresh: computes screen position, +erases old multicell block if heading moved or became hidden, +and caches the new position. Does NOT emit OSC 66 — that +happens in phase 2 (`kitty-gfx--emit-heading-overlays') to +avoid posn-at-point redraws destroying freshly-placed blocks." + (let ((pos (kitty-gfx--overlay-screen-pos ov)) (rows (overlay-get ov 'kitty-gfx-rows)) (last-row (overlay-get ov 'kitty-gfx-last-row)) (last-col (overlay-get ov 'kitty-gfx-last-col))) @@ -1718,9 +1678,15 @@ then invalidates position caches and schedules a refresh." (kitty-gfx--erase-heading ov) (overlay-put ov 'kitty-gfx-last-row nil) (overlay-put ov 'kitty-gfx-last-col nil)) - ;; Image overlay — delete all terminal placements, including - ;; multiple windows that were showing this buffer. - (kitty-gfx--delete-image-placements ov)))))))) + ;; Image overlay — delete terminal placement + (let ((id (overlay-get ov 'kitty-gfx-id)) + (pid (overlay-get ov 'kitty-gfx-pid))) + (if (overlay-get ov 'kitty-gfx-last-row) + (when (and id pid) + (kitty-gfx--delete-placement id pid)) + (kitty-gfx--log "on-buffer-change: ov pid=%s has no last-row (never placed or already cleaned)" pid))) + (overlay-put ov 'kitty-gfx-last-row nil) + (overlay-put ov 'kitty-gfx-last-col nil)))))))) ;; Reset cache for visible buffers so they re-place correctly. ;; Heading overlays preserve cache (same rationale as on-window-change). (dolist (buf (buffer-list)) @@ -1728,7 +1694,6 @@ then invalidates position caches and schedules a refresh." (dolist (ov kitty-gfx--overlays) (when (and (overlay-buffer ov) (not (overlay-get ov 'kitty-gfx-heading))) - (overlay-put ov 'kitty-gfx-placements nil) (overlay-put ov 'kitty-gfx-last-row nil) (overlay-put ov 'kitty-gfx-last-col nil))))) ;; Longer debounce: cancel any fast leading-edge cooldown and @@ -1744,36 +1709,27 @@ then invalidates position caches and schedules a refresh." (defun kitty-gfx--on-window-change (_frame) "Handle window configuration change for image refresh. -Invalidates cell pixel size, deletes stale image placements, then -clears image position caches so the refresh cycle re-places images -at their new positions. Uses a longer debounce than normal refresh -to let Emacs finish window layout transitions (e.g., when closing a -split, Emacs briefly shows two windows for the same buffer before -settling to one)." - (kitty-gfx--log "on-window-change: deleting stale placements and invalidating cell size") +Invalidates position caches and cell pixel size so the refresh +cycle re-places images correctly. Does NOT delete all placements +— that causes visible flicker. Uses a longer debounce than normal +refresh to let Emacs finish window layout transitions (e.g., when +closing a split, Emacs briefly shows two windows for the same +buffer before settling to one)." + (kitty-gfx--log "on-window-change: invalidating positions and cell size") (setq kitty-gfx--cell-pixel-width nil kitty-gfx--cell-pixel-height nil) - ;; Delete image placements before clearing their cached positions. - ;; Window splits/resizes can move an image from the middle of the old - ;; window to the center of the new pane(s). A buffer can also be shown - ;; in multiple windows, and closing one of those windows leaves terminal - ;; pixels that no remaining window can discover from `posn-at-point'. - ;; Therefore image placements are tracked and deleted per window before - ;; the cache is reset. Some terminals do not reliably erase an old - ;; direct placement when it is re-placed at a different geometry, and - ;; Sixel is stateless and must be explicitly overwritten. - ;; + ;; Reset position cache so images get re-placed at correct positions. ;; Heading overlays PRESERVE their cache — the refresh cycle needs ;; old→new position comparison to erase multicell blocks properly. - (kitty-gfx--sync-begin) - (unwind-protect - (dolist (buf (buffer-list)) - (with-current-buffer buf - (dolist (ov kitty-gfx--overlays) - (when (and (overlay-buffer ov) - (not (overlay-get ov 'kitty-gfx-heading))) - (kitty-gfx--delete-image-placements ov))))) - (kitty-gfx--sync-end)) + ;; Images can clear cache because re-placing with the same PID is + ;; idempotent (the terminal replaces the old placement). + (dolist (buf (buffer-list)) + (with-current-buffer buf + (dolist (ov kitty-gfx--overlays) + (when (and (overlay-buffer ov) + (not (overlay-get ov 'kitty-gfx-heading))) + (overlay-put ov 'kitty-gfx-last-row nil) + (overlay-put ov 'kitty-gfx-last-col nil))))) ;; Longer debounce: cancel any fast leading-edge cooldown and ;; schedule a 0.1s delayed refresh to let window layout settle. (when kitty-gfx--render-timer @@ -1965,82 +1921,6 @@ delete step, avoiding visual glitches in some terminals." image-id pid cols rows beg end (buffer-name) (length kitty-gfx--overlays)) ov)) -(defun kitty-gfx--image-placement (ov win) - "Return OV's recorded image placement for WIN, or nil." - (assq win (overlay-get ov 'kitty-gfx-placements))) - -(defun kitty-gfx--record-image-placement (ov win row col cols rows pid) - "Record that OV is placed in WIN at ROW COL with COLS ROWS and PID." - (unless (kitty-gfx--image-placement ov win) - (setq pid (kitty-gfx--alloc-placement-id))) - (let ((placements (assq-delete-all win (copy-sequence - (overlay-get ov 'kitty-gfx-placements))))) - (overlay-put ov 'kitty-gfx-placements - (cons (cons win (list :row row :col col - :cols cols :rows rows - :pid pid)) - placements)))) - -(defun kitty-gfx--forget-image-placement (ov win) - "Forget OV's recorded image placement for WIN." - (overlay-put ov 'kitty-gfx-placements - (assq-delete-all win (copy-sequence - (overlay-get ov 'kitty-gfx-placements))))) - -(defun kitty-gfx--delete-image-placement (ov placement) - "Delete one recorded image PLACEMENT for OV." - (let* ((data (cdr placement)) - (id (overlay-get ov 'kitty-gfx-id)) - (pid (plist-get data :pid)) - (row (plist-get data :row)) - (col (plist-get data :col)) - (cols (plist-get data :cols)) - (rows (plist-get data :rows)) - (old-row (overlay-get ov 'kitty-gfx-last-row)) - (old-col (overlay-get ov 'kitty-gfx-last-col)) - (old-cols (overlay-get ov 'kitty-gfx-cols)) - (old-rows (overlay-get ov 'kitty-gfx-rows))) - (when (and id pid row col cols rows kitty-gfx--active-backend) - ;; Sixel deletion is position-based and reads these properties from OV; - ;; Kitty deletion ignores them and deletes by PID. Temporarily bind the - ;; recorded geometry so both backends can share the same helper. - (unwind-protect - (progn - (overlay-put ov 'kitty-gfx-last-row row) - (overlay-put ov 'kitty-gfx-last-col col) - (overlay-put ov 'kitty-gfx-cols cols) - (overlay-put ov 'kitty-gfx-rows rows) - (funcall (kitty-gfx--backend-fn 'delete) ov id pid)) - (overlay-put ov 'kitty-gfx-last-row old-row) - (overlay-put ov 'kitty-gfx-last-col old-col) - (overlay-put ov 'kitty-gfx-cols old-cols) - (overlay-put ov 'kitty-gfx-rows old-rows))))) - -(defun kitty-gfx--delete-image-placements (ov) - "Delete all recorded terminal placements for image overlay OV." - (let ((placements (overlay-get ov 'kitty-gfx-placements))) - (if placements - (dolist (placement placements) - (condition-case err - (kitty-gfx--delete-image-placement ov placement) - (error - (kitty-gfx--log "delete-image-placements: error: %s" - (error-message-string err))))) - ;; Backward-compatible fallback for overlays created before per-window - ;; placement tracking or for callers that only populated last-row/col. - (let ((id (overlay-get ov 'kitty-gfx-id)) - (pid (overlay-get ov 'kitty-gfx-pid))) - (when (and id pid kitty-gfx--active-backend - (overlay-get ov 'kitty-gfx-last-row)) - (condition-case err - (funcall (kitty-gfx--backend-fn 'delete) ov id pid) - (error - (kitty-gfx--log "delete-image-placements: fallback error: %s" - (error-message-string err)))))))) - (overlay-put ov 'kitty-gfx-placements nil) - (overlay-put ov 'kitty-gfx-last-row nil) - (overlay-put ov 'kitty-gfx-last-col nil)) - (defun kitty-gfx--delete-placement (id pid) "Delete a specific placement PID of image ID from terminal. Uses d=i (lowercase) to remove the placement but keep stored image @@ -2056,14 +1936,17 @@ the placement ID can be reused by a subsequent overlay (avoids visual glitches from delete+re-place sequences in some terminals)." (let ((id (overlay-get ov 'kitty-gfx-id)) (pid (overlay-get ov 'kitty-gfx-pid))) - (when keep-placement - (overlay-put ov 'kitty-gfx-placements nil)) (kitty-gfx--log "remove-overlay: id=%s pid=%s keep=%s buf=%s" id pid keep-placement (when (overlay-buffer ov) (buffer-name (overlay-buffer ov)))) (when (overlay-buffer ov) (unless keep-placement - (kitty-gfx--delete-image-placements ov)) + (condition-case err + (when (and id pid kitty-gfx--active-backend) + (funcall (kitty-gfx--backend-fn 'delete) ov id pid)) + (error + (kitty-gfx--log "remove-overlay: error deleting placement: %s" + (error-message-string err))))) (delete-overlay ov)) (setq kitty-gfx--overlays (delq ov kitty-gfx--overlays)) (kitty-gfx--log "remove-overlay: done (remaining=%d)" (length kitty-gfx--overlays)))) @@ -2146,10 +2029,9 @@ The buffer should be writable (caller handles `inhibit-read-only')." (unless cached-id (when (funcall (kitty-gfx--backend-fn 'prepare) abs-file image-id) (kitty-gfx--cache-put abs-file image-id))) - ;; Create overlay at the scaled dimensions. + ;; Create overlay at the scaled dimensions (when (or cached-id (gethash abs-file kitty-gfx--image-cache)) - (kitty-gfx--make-overlay img-start (point) image-id - img-cols img-rows abs-file reuse-pid) + (kitty-gfx--make-overlay img-start (point) image-id img-cols img-rows abs-file reuse-pid) (kitty-gfx--schedule-refresh))))) (defun kitty-gfx-remove-images (&optional beg end) @@ -2779,13 +2661,8 @@ With an active region, restrict to it; otherwise clear the whole buffer." "Zoom scale factor for image-mode display. Values > 1.0 zoom in, < 1.0 zoom out.") -(defun kitty-gfx--image-mode-render (&optional reuse-placement) - "Render the current image file centered at current scale. -When REUSE-PLACEMENT is non-nil, reuse the old terminal placement -ID instead of deleting it first. This is useful for zoom commands -where the new placement immediately replaces the old one, but it is -intentionally not used for window size changes because stale pixels -can otherwise remain over newly-created window separators." +(defun kitty-gfx--image-mode-render () + "Render the current image file centered at current scale." (when-let* ((file (buffer-file-name))) (when (kitty-gfx--image-file-p file) (let* ((inhibit-read-only t) @@ -2793,18 +2670,15 @@ can otherwise remain over newly-created window separators." (win-h (- (window-body-height) 2)) (max-cols (min win-w kitty-gfx-max-width)) (max-rows (min win-h kitty-gfx-max-height)) - ;; Save the old placement ID only when the caller explicitly - ;; wants to reuse it. Reusing avoids delete+re-place glitches - ;; for zoom commands (WezTerm #5892), but window splits/resizes - ;; must delete first so stale terminal pixels are cleared. - (old-pid (when (and reuse-placement (car kitty-gfx--overlays)) + ;; Save the old placement ID before removing overlays. + ;; Reusing it avoids delete+re-place glitches (WezTerm #5892). + (old-pid (when (car kitty-gfx--overlays) (overlay-get (car kitty-gfx--overlays) 'kitty-gfx-pid)))) (kitty-gfx--log "image-mode-render: file=%s scale=%.2f win=%dx%d max=%dx%d reuse-pid=%s" (file-name-nondirectory file) kitty-gfx--image-scale win-w win-h max-cols max-rows old-pid) - ;; Remove overlays. When OLD-PID is non-nil, skip terminal-side - ;; delete so the new placement atomically replaces it; otherwise - ;; delete/erase the old placement before changing the buffer text. + ;; Remove overlays but skip terminal-side delete when we have + ;; a PID to reuse (the new placement will atomically replace it). (dolist (ov (overlays-in (point-min) (point-max))) (when (overlay-get ov 'kitty-gfx) (kitty-gfx--remove-overlay ov old-pid))) @@ -2819,19 +2693,19 @@ can otherwise remain over newly-created window separators." "Zoom in on the image in image-mode." (interactive) (setq kitty-gfx--image-scale (* kitty-gfx--image-scale 1.25)) - (kitty-gfx--image-mode-render t)) + (kitty-gfx--image-mode-render)) (defun kitty-gfx-image-decrease-size () "Zoom out on the image in image-mode." (interactive) (setq kitty-gfx--image-scale (max 0.1 (* kitty-gfx--image-scale 0.8))) - (kitty-gfx--image-mode-render t)) + (kitty-gfx--image-mode-render)) (defun kitty-gfx-image-reset-size () "Reset image zoom to default in image-mode." (interactive) (setq kitty-gfx--image-scale 1.0) - (kitty-gfx--image-mode-render t)) + (kitty-gfx--image-mode-render)) (defun kitty-gfx--image-mode-advice (orig-fn &rest args) "Around advice for `image-mode'." @@ -2941,160 +2815,6 @@ Values > 1.0 zoom in, < 1.0 zoom out.") "Path to the current doc-view page image file. Stored so zoom commands can re-render without querying `doc-view-current-image'.") -(defun kitty-gfx--doc-view-terminal-p () - "Non-nil when Kitty graphics is handling a doc-view buffer in a terminal." - (and kitty-graphics-mode - (not (display-graphic-p)) - (eq major-mode 'doc-view-mode))) - -(defun kitty-gfx--doc-view-image-cell-size () - "Return the current doc-view Kitty image size as (COLS . ROWS), or nil." - (when (overlayp kitty-gfx--doc-view-overlay) - (let ((cols (overlay-get kitty-gfx--doc-view-overlay 'kitty-gfx-cols)) - (rows (overlay-get kitty-gfx--doc-view-overlay 'kitty-gfx-rows))) - (when (and cols rows) - (cons cols rows))))) - -(defun kitty-gfx--doc-view-max-hscroll () - "Return the maximum horizontal scroll for the current Kitty doc-view page." - (let ((size (kitty-gfx--doc-view-image-cell-size))) - (max 0 (- (or (car size) 0) (window-body-width))))) - -(defun kitty-gfx--doc-view-max-vscroll () - "Return the maximum vertical pixel scroll for the current Kitty doc-view page." - (let ((size (kitty-gfx--doc-view-image-cell-size))) - (max 0 (- (* (or (cdr size) 0) (frame-char-height)) - (window-body-height nil t))))) - -(defun kitty-gfx--doc-view-set-hscroll (ncols) - "Set horizontal scroll to NCOLS for Kitty doc-view and refresh the page." - (let ((new (max 0 (min ncols (kitty-gfx--doc-view-max-hscroll))))) - (set-window-hscroll (selected-window) new) - (kitty-gfx--schedule-refresh) - new)) - -(defun kitty-gfx--doc-view-set-vscroll (pixels) - "Set vertical pixel scroll to PIXELS for Kitty doc-view and refresh the page." - (let ((new (max 0 (min pixels (kitty-gfx--doc-view-max-vscroll))))) - (set-window-vscroll (selected-window) new t) - (kitty-gfx--schedule-refresh) - new)) - -(defun kitty-gfx--doc-view-forward-hscroll (&optional n) - "Scroll the current Kitty doc-view page left by N columns." - (kitty-gfx--doc-view-set-hscroll (+ (window-hscroll) (or n 1)))) - -(defun kitty-gfx--doc-view-next-line (&optional n) - "Scroll the current Kitty doc-view page upward by N terminal rows." - (kitty-gfx--doc-view-set-vscroll - (+ (window-vscroll nil t) (* (or n 1) (frame-char-height))))) - -(defun kitty-gfx--doc-view-scroll-left (&optional n) - "Scroll the current Kitty doc-view page leftward by N columns." - (kitty-gfx--doc-view-forward-hscroll - (cond - ((null n) (max 0 (- (window-body-width) 2))) - ((eq n '-) (min 0 (- 2 (window-body-width)))) - (t (prefix-numeric-value n))))) - -(defun kitty-gfx--doc-view-scroll-up (&optional n) - "Scroll the current Kitty doc-view page upward by N rows." - (kitty-gfx--doc-view-next-line - (cond - ((null n) (max 0 (- (window-body-height) next-screen-context-lines))) - ((eq n '-) (min 0 (- next-screen-context-lines (window-body-height)))) - (t (prefix-numeric-value n))))) - -(defun kitty-gfx--doc-view-image-forward-hscroll-advice (orig-fn &optional n) - "Around advice for `image-forward-hscroll' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-forward-hscroll n) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-backward-hscroll-advice (orig-fn &optional n) - "Around advice for `image-backward-hscroll' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-forward-hscroll (- (or n 1))) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-next-line-advice (orig-fn &optional n) - "Around advice for `image-next-line' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-next-line n) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-previous-line-advice (orig-fn &optional n) - "Around advice for `image-previous-line' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-next-line (- (or n 1))) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-scroll-left-advice (orig-fn &optional n) - "Around advice for `image-scroll-left' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-scroll-left n) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-scroll-right-advice (orig-fn &optional n) - "Around advice for `image-scroll-right' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-scroll-left - (cond - ((null n) (min 0 (- 2 (window-body-width)))) - ((eq n '-) (max 0 (- (window-body-width) 2))) - (t (- (prefix-numeric-value n))))) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-scroll-up-advice (orig-fn &optional n) - "Around advice for `image-scroll-up' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-scroll-up n) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-scroll-down-advice (orig-fn &optional n) - "Around advice for `image-scroll-down' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (kitty-gfx--doc-view-scroll-up - (cond - ((null n) (min 0 (- next-screen-context-lines (window-body-height)))) - ((eq n '-) (max 0 (- (window-body-height) next-screen-context-lines))) - (t (- (prefix-numeric-value n))))) - (funcall orig-fn n))) - -(defun kitty-gfx--doc-view-image-bol-advice (orig-fn &optional arg) - "Around advice for `image-bol' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (progn - (when (and arg (/= (prefix-numeric-value arg) 1)) - (kitty-gfx--doc-view-next-line (- (prefix-numeric-value arg) 1))) - (kitty-gfx--doc-view-set-hscroll 0)) - (funcall orig-fn arg))) - -(defun kitty-gfx--doc-view-image-eol-advice (orig-fn &optional arg) - "Around advice for `image-eol' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (progn - (when (and arg (/= (prefix-numeric-value arg) 1)) - (kitty-gfx--doc-view-next-line (- (prefix-numeric-value arg) 1))) - (kitty-gfx--doc-view-set-hscroll (kitty-gfx--doc-view-max-hscroll))) - (funcall orig-fn arg))) - -(defun kitty-gfx--doc-view-image-bob-advice (orig-fn) - "Around advice for `image-bob' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (progn - (kitty-gfx--doc-view-set-hscroll 0) - (kitty-gfx--doc-view-set-vscroll 0)) - (funcall orig-fn))) - -(defun kitty-gfx--doc-view-image-eob-advice (orig-fn) - "Around advice for `image-eob' in Kitty doc-view buffers." - (if (kitty-gfx--doc-view-terminal-p) - (progn - (kitty-gfx--doc-view-set-hscroll (kitty-gfx--doc-view-max-hscroll)) - (kitty-gfx--doc-view-set-vscroll (kitty-gfx--doc-view-max-vscroll))) - (funcall orig-fn))) - (defun kitty-gfx--doc-view-insert-image-advice (orig-fn file &rest args) "Around advice for `doc-view-insert-image'. Displays the page image via Kitty graphics instead of an Emacs @@ -3111,34 +2831,15 @@ image spec. FILE is the path to the page PNG." (when kitty-gfx--doc-view-overlay (kitty-gfx--remove-overlay kitty-gfx--doc-view-overlay old-pid) (setq kitty-gfx--doc-view-overlay nil)) - ;; Display the rendered page using only an overlay. Do not erase - ;; or insert text here: doc-view buffers visit the original PDF, so - ;; mutating buffer text can corrupt the document if it is saved. - (let* ((win-w (- (window-body-width) 1)) - (win-h (- (window-body-height) 1)) - (abs-file (expand-file-name file)) - (px (kitty-gfx--image-pixel-size abs-file)) - (base-dims (if px - (kitty-gfx--compute-cell-dims - (car px) (cdr px) win-w win-h) - (cons (min 40 win-w) (min 15 win-h)))) - (img-cols (max 1 (round (* kitty-gfx--doc-view-scale - (car base-dims))))) - (img-rows (max 1 (round (* kitty-gfx--doc-view-scale - (cdr base-dims))))) - (cached-id (kitty-gfx--cache-get abs-file)) - (image-id (or cached-id (kitty-gfx--alloc-id)))) - (set-window-hscroll (selected-window) 0) - (set-window-vscroll (selected-window) 0 t) - (unless cached-id - (when (funcall (kitty-gfx--backend-fn 'prepare) abs-file image-id) - (kitty-gfx--cache-put abs-file image-id))) - (when (or cached-id (gethash abs-file kitty-gfx--image-cache)) - (setq kitty-gfx--doc-view-overlay - (kitty-gfx--make-overlay (point-min) (point-max) - image-id img-cols img-rows - abs-file old-pid)) - (kitty-gfx--schedule-refresh)))) + ;; Clear the buffer (removes "Welcome to DocView!" text) + (let* ((inhibit-read-only t) + (win-w (- (window-body-width) 1)) + (win-h (- (window-body-height) 1))) + (erase-buffer) + (kitty-gfx--display-image-centered + file win-w win-h win-w win-h + kitty-gfx--doc-view-scale old-pid) + (setq kitty-gfx--doc-view-overlay (car kitty-gfx--overlays)))) (goto-char (point-min))) (apply orig-fn file args))) @@ -3569,31 +3270,7 @@ Requires `kitty-gfx-enable-video' to be non-nil and mpv installed." (advice-add 'doc-view-enlarge :around #'kitty-gfx--doc-view-enlarge-advice) (advice-add 'doc-view-scale-reset :around - #'kitty-gfx--doc-view-scale-reset-advice) - (advice-add 'image-forward-hscroll :around - #'kitty-gfx--doc-view-image-forward-hscroll-advice) - (advice-add 'image-backward-hscroll :around - #'kitty-gfx--doc-view-image-backward-hscroll-advice) - (advice-add 'image-next-line :around - #'kitty-gfx--doc-view-image-next-line-advice) - (advice-add 'image-previous-line :around - #'kitty-gfx--doc-view-image-previous-line-advice) - (advice-add 'image-scroll-left :around - #'kitty-gfx--doc-view-image-scroll-left-advice) - (advice-add 'image-scroll-right :around - #'kitty-gfx--doc-view-image-scroll-right-advice) - (advice-add 'image-scroll-up :around - #'kitty-gfx--doc-view-image-scroll-up-advice) - (advice-add 'image-scroll-down :around - #'kitty-gfx--doc-view-image-scroll-down-advice) - (advice-add 'image-bol :around - #'kitty-gfx--doc-view-image-bol-advice) - (advice-add 'image-eol :around - #'kitty-gfx--doc-view-image-eol-advice) - (advice-add 'image-bob :around - #'kitty-gfx--doc-view-image-bob-advice) - (advice-add 'image-eob :around - #'kitty-gfx--doc-view-image-eob-advice)) + #'kitty-gfx--doc-view-scale-reset-advice)) (with-eval-after-load 'markdown-overlays (advice-add 'markdown-overlays--fontify-image :around #'kitty-gfx--markdown-overlays-fontify-image-advice) @@ -3618,18 +3295,6 @@ Requires `kitty-gfx-enable-video' to be non-nil and mpv installed." (advice-remove 'doc-view-insert-image #'kitty-gfx--doc-view-insert-image-advice) (advice-remove 'doc-view-enlarge #'kitty-gfx--doc-view-enlarge-advice) (advice-remove 'doc-view-scale-reset #'kitty-gfx--doc-view-scale-reset-advice) - (advice-remove 'image-forward-hscroll #'kitty-gfx--doc-view-image-forward-hscroll-advice) - (advice-remove 'image-backward-hscroll #'kitty-gfx--doc-view-image-backward-hscroll-advice) - (advice-remove 'image-next-line #'kitty-gfx--doc-view-image-next-line-advice) - (advice-remove 'image-previous-line #'kitty-gfx--doc-view-image-previous-line-advice) - (advice-remove 'image-scroll-left #'kitty-gfx--doc-view-image-scroll-left-advice) - (advice-remove 'image-scroll-right #'kitty-gfx--doc-view-image-scroll-right-advice) - (advice-remove 'image-scroll-up #'kitty-gfx--doc-view-image-scroll-up-advice) - (advice-remove 'image-scroll-down #'kitty-gfx--doc-view-image-scroll-down-advice) - (advice-remove 'image-bol #'kitty-gfx--doc-view-image-bol-advice) - (advice-remove 'image-eol #'kitty-gfx--doc-view-image-eol-advice) - (advice-remove 'image-bob #'kitty-gfx--doc-view-image-bob-advice) - (advice-remove 'image-eob #'kitty-gfx--doc-view-image-eob-advice) (advice-remove 'image-mode #'kitty-gfx--image-mode-advice) (advice-remove 'shr-put-image #'kitty-gfx--shr-put-image-advice) (advice-remove 'markdown-overlays--fontify-image #'kitty-gfx--markdown-overlays-fontify-image-advice) |
