summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-04-26 21:14:50 +0200
committercashmere <cashmere@autistici.org>2026-04-26 21:14:50 +0200
commitb4e12512a0db4ce8823e2265faf020bb222875a7 (patch)
tree34330b69a718dfd2e4035462235fac5b78db49c7
parentbcf54dda11f333b217443747f865c803dd7e489d (diff)
downloadkitty-graphics-b4e12512a0db4ce8823e2265faf020bb222875a7.tar.gz
feat(tmux): unlock Sixel backend inside tmux >= 3.4
Sixel detection used to hard-disable the moment `$TMUX' was set. That guard predates tmux 3.4 (released 2024-02-13), which ships native Sixel rendering, so we were rejecting a working configuration. Drop the hard guard, gate on a parsed `tmux -V' >= 3.4, and let users opt out via a new custom var. - `kitty-gfx-tmux-allow-sixel' (default t). Set to nil if your tmux build lacks `--enable-sixel' or if you want to avoid the upstream scroll artifact (tmux's cell buffer is not pixel-aware, so images may persist after scrolling). - `kitty-gfx--tmux-version' parses `tmux -V' once per session and caches the result in `kitty-gfx--tmux-version-cache' (with an `unset' sentinel so we can distinguish "not probed" from "no tmux"). - `kitty-gfx--tmux-sixel-supported-p' centralises the (in-tmux + opt-in + version >= 3.4) check. - `kitty-gfx--sixel-detect' now uses the helper instead of `(not in-tmux)'. Debug log gained `tmux-ver=' and `tmux-ok='. - AGENTS.md / README.org / CHANGELOG.org: limitation reworded from "no tmux support" to a partial entry that explains exactly what works and what the remaining caveats are (Kitty APC passthrough is still unimplemented; scroll artifacts are upstream). - justfile: new `test-sixel-tmux' recipe wraps `test-sixel-image' in `tmux new-session' for one-command interactive verification, and the sibling `test-sixel-image' recipe was switched from a fragile shell conditional to an elisp `when' so passing an encoder argument no longer breaks the parse. Refs #2.
-rw-r--r--AGENTS.md6
-rw-r--r--CHANGELOG.org6
-rw-r--r--README.org2
-rw-r--r--justfile12
-rw-r--r--kitty-graphics.el60
5 files changed, 77 insertions, 9 deletions
diff --git a/AGENTS.md b/AGENTS.md
index ccd185f..b18f76c 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -244,7 +244,11 @@ Key design differences from images:
## Known Issues & Planned Work
- **Fixed**: Overlays remain visible when org headings are collapsed (#1)
-- **Limitation**: Does not work inside tmux (neither Kitty passthrough nor Sixel) (#2)
+- **Partial**: tmux support (#2) -- Sixel works inside tmux >= 3.4 (native
+ rendering, foot/Konsole/etc as outer terminal); guarded by
+ `kitty-gfx-tmux-allow-sixel'. Known caveat: images may persist after
+ scrolling because tmux's cell buffer is not pixel-aware (upstream limit).
+ Kitty graphics passthrough is still unimplemented -- tracked separately.
- **Limitation**: Each mode needs explicit `:around` advice integration
- **Limitation**: GIF files are not supported (multi-frame conversion issues,
no animation support in terminal)
diff --git a/CHANGELOG.org b/CHANGELOG.org
index 837968e..82e8c5b 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -51,9 +51,11 @@ I will keep on improving this feature. I think it is actually great eyecandy, an
**** tmux support
-tmux support is next. It already supports sixel, so the sixel backend will work there out of the box.
+The Sixel side is now unlocked: starting in =0.5.0=, =kitty-graphics= no longer hard-disables the Sixel backend when =$TMUX= is set. tmux 3.4 (released 2024-02-13) ships native Sixel rendering, so running Emacs inside =tmux new-session= under foot/Konsole/mintty/mlterm/WezTerm just works. The detector probes =tmux -V= once per session and only engages on tmux >= 3.4. A new option =kitty-gfx-tmux-allow-sixel= (default =t=) lets you opt out if your tmux build lacks =--enable-sixel=.
-There is also an in-progress branch adding [[https://github.com/tmux/tmux/tree/ta/kitty-img][kitty image protocol support to tmux]]. If you already use Kitty directly, though, I would stick with it. The kitty protocol gives you stateful image management, truecolor rendering, and a smoother overall experience.
+Caveat: tmux's cell buffer is not pixel-aware, so images may stick to their old position after scrolling until the affected cells are overwritten. That is an upstream tmux limitation, not something this package can fix.
+
+The Kitty graphics protocol still does not work inside tmux because we do not yet wrap APC sequences with the =\ePtmux;...\e\\= passthrough envelope. There is also an in-progress branch adding [[https://github.com/tmux/tmux/tree/ta/kitty-img][native Kitty image protocol support to tmux]]. Until either lands, if you already use Kitty directly I would stick with it -- the Kitty protocol gives you stateful image management, truecolor rendering, and a smoother overall experience.
**** Package renaming?
diff --git a/README.org b/README.org
index eb0ab62..6b95ac4 100644
--- a/README.org
+++ b/README.org
@@ -143,7 +143,7 @@ TIFF, BMP) are converted to PNG via ImageMagick before transmission.
* Known Limitations
-- No tmux support (neither Kitty passthrough nor Sixel)
+- Partial tmux support: Sixel works inside tmux >= 3.4 (native sixel rendering, foot/Konsole/mintty/mlterm/WezTerm as outer terminal); set ~kitty-gfx-tmux-allow-sixel~ to ~nil~ to opt out. Images may stick to old positions after scrolling because tmux's cell buffer is not pixel-aware (upstream tmux limitation). Kitty graphics passthrough inside tmux is still unimplemented
- Animated GIFs display only the first frame
- Sixel is limited to 256 colors (vs truecolor on Kitty) and is stateless --- images are re-emitted on every scroll, which may be slower on large images
- Without ImageMagick, only PNG files work on the Kitty backend; the Sixel backend requires ImageMagick unconditionally
diff --git a/justfile b/justfile
index 22fe756..008f4ad 100644
--- a/justfile
+++ b/justfile
@@ -141,10 +141,20 @@ test-sixel-image encoder="":
@echo ">> Run inside foot, Konsole, mintty, mlterm, or WezTerm."
TERM={{TERM_}} {{EMACS}} -nw -Q -l {{SRC}} \
--eval "(setq kitty-gfx-debug t kitty-gfx-preferred-protocol 'sixel)" \
- $(if [ -n "{{encoder}}" ]; then echo "--eval (setq\\ kitty-gfx-sixel-encoder-program\\ \"{{encoder}}\")"; fi) \
+ --eval '(when (> (length "{{encoder}}") 0) (setq kitty-gfx-sixel-encoder-program "{{encoder}}"))' \
--eval "(kitty-graphics-mode 1)" \
tests/test-image.png
+# Open test-image.png inside `tmux new-session', sixel backend forced
+# (run on a sixel-capable outer terminal, requires tmux >= 3.4)
+test-sixel-tmux encoder="":
+ @echo ">> Outer terminal must be sixel-capable (foot, Konsole, mintty, ...)."
+ @echo ">> tmux >= 3.4 with --enable-sixel required."
+ tmux new-session -As kgfx-sixel-test "TERM={{TERM_}} {{EMACS}} -nw -Q -l {{SRC}} \
+ --eval '(setq kitty-gfx-debug t kitty-gfx-preferred-protocol (quote sixel))' \
+ --eval '(when (> (length \"{{encoder}}\") 0) (setq kitty-gfx-sixel-encoder-program \"{{encoder}}\"))' \
+ --eval '(kitty-graphics-mode 1)' tests/test-image.png"
+
# --- Logs -------------------------------------------------------------------
# Tail the kitty-gfx debug log (set kitty-gfx-debug to t to populate)
diff --git a/kitty-graphics.el b/kitty-graphics.el
index 640454b..c5a1e9d 100644
--- a/kitty-graphics.el
+++ b/kitty-graphics.el
@@ -207,6 +207,17 @@ will otherwise block Emacs."
:type '(choice (const :tag "No timeout" nil) number)
:group 'kitty-graphics)
+(defcustom kitty-gfx-tmux-allow-sixel t
+ "When non-nil, allow the Sixel backend to engage inside tmux >= 3.4.
+tmux 3.4 (released 2024-02-13) ships native Sixel rendering, so this
+package no longer hard-disables Sixel under tmux. Set to nil if your
+tmux is built without `--enable-sixel' or if you prefer to avoid the
+upstream scroll artifact: tmux's cell buffer is not pixel-aware, so
+images may persist after scrolling until the affected cells are
+overwritten."
+ :type 'boolean
+ :group 'kitty-graphics)
+
(defcustom kitty-gfx-heading-scales '((1 . 2.0) (2 . 1.5) (3 . 1.2))
"Alist mapping org heading level to visual scale factor.
Headings at levels not listed use scale 1.0 (normal size).
@@ -588,19 +599,60 @@ OV is the overlay (unused by Kitty backend but part of the interface)."
(defvar kitty-gfx--sixel-cache (make-hash-table :test 'equal)
"Maps (file . dims-string) to temp sixel file paths.")
+(defvar kitty-gfx--tmux-version-cache 'unset
+ "Cached `kitty-gfx--tmux-version' result.
+Sentinel `unset' means the probe has not run yet; nil means tmux was
+absent or its version could not be parsed; otherwise a list (MAJOR MINOR).")
+
+(defun kitty-gfx--tmux-version ()
+ "Return tmux version as (MAJOR MINOR) integers, or nil when unavailable.
+Memoised in `kitty-gfx--tmux-version-cache'."
+ (when (eq kitty-gfx--tmux-version-cache 'unset)
+ (setq kitty-gfx--tmux-version-cache
+ (when (executable-find "tmux")
+ (with-temp-buffer
+ (when (zerop (ignore-errors
+ (call-process "tmux" nil t nil "-V")))
+ (goto-char (point-min))
+ (when (re-search-forward
+ "tmux\\(?:[[:space:]]+next-\\)?[[:space:]]+\\([0-9]+\\)\\.\\([0-9]+\\)"
+ nil t)
+ (list (string-to-number (match-string 1))
+ (string-to-number (match-string 2)))))))))
+ kitty-gfx--tmux-version-cache)
+
+(defun kitty-gfx--tmux-sixel-supported-p ()
+ "Return non-nil when running under tmux >= 3.4 with Sixel allowed.
+Returns nil outside tmux, nil when `kitty-gfx-tmux-allow-sixel' is off,
+nil when tmux's version cannot be determined, and nil for tmux < 3.4."
+ (and (getenv "TMUX")
+ kitty-gfx-tmux-allow-sixel
+ (let ((ver (kitty-gfx--tmux-version)))
+ (and ver
+ (or (> (car ver) 3)
+ (and (= (car ver) 3) (>= (cadr ver) 4)))))))
+
(defun kitty-gfx--sixel-detect ()
- "Return non-nil if the terminal likely supports Sixel protocol."
+ "Return non-nil if the terminal likely supports Sixel protocol.
+Inside tmux, requires tmux >= 3.4 (native Sixel rendering, 2024-02-13)
+and `kitty-gfx-tmux-allow-sixel'. Older tmux versions still disable
+Sixel because they drop the DCS payload."
(let* ((term (getenv "TERM"))
(term-prog (getenv "TERM_PROGRAM"))
(in-tmux (getenv "TMUX"))
+ (tmux-ver (and in-tmux (kitty-gfx--tmux-version)))
+ (tmux-ok (kitty-gfx--tmux-sixel-supported-p))
(supported (and term
- (not in-tmux) ; Sixel doesn't work in tmux
+ (or (not in-tmux) tmux-ok)
(or (string-match-p "xterm\\|vt[0-9]\\|foot\\|contour" term)
(member term-prog '("foot" "Konsole" "mintty" "mlterm"
"contour" "WezTerm")))
t)))
- (kitty-gfx--log "sixel-detect: %s (TERM=%s TERM_PROGRAM=%s TMUX=%s)"
- supported term term-prog (if in-tmux "yes" "no"))
+ (kitty-gfx--log
+ "sixel-detect: %s (TERM=%s TERM_PROGRAM=%s TMUX=%s tmux-ver=%s tmux-ok=%s)"
+ supported term term-prog (if in-tmux "yes" "no")
+ (if tmux-ver (format "%d.%d" (car tmux-ver) (cadr tmux-ver)) "n/a")
+ (if tmux-ok "yes" "no"))
supported))
(defun kitty-gfx--sixel-cache-path (file cols rows)