[vim/vim] Render images inside popup windows via sixel and GDI (PR #20136)

65 views
Skip to first unread message

mattn

unread,
May 4, 2026, 12:51:25 PMMay 4
to vim/vim, Subscribed

Add an image attribute to popup_create() / popup_setoptions() that renders a pixel buffer inside the popup window. Terminal backends emit the buffer as DEC sixel DCS sequences (FEAT_IMAGE_SIXEL); on the MS-Windows GUI the same data is BitBlt'd through a GDI device bitmap (FEAT_IMAGE_GDI). The popup auto-sizes its cell box from the image's pixel dimensions, so the caller does not have to compute minwidth/maxwidth/minheight/maxheight by hand.

image.png (view on web)

Vim itself does not link against libpng, libjpeg, libwebp or any image-decoding library. The image attribute takes an already-decoded raw RGB / RGBA pixel buffer; format decoding is left to the script caller, who can pipe the file through any external tool (GraphicsMagick, ImageMagick, ffmpeg, custom converter, ...) and pass the resulting bytes via a Blob. This keeps Vim's build footprint unchanged while letting users render PNG, JPEG, GIF, WebP, AVIF or proprietary formats with whatever decoder they already have.

The image dict accepts data (a Blob of width*height*3 for RGB or width*height*4 for RGBA), width and height. RGBA buffers are composited over the popup's background. A companion script function getbgcolor() returns the current background colour as [r, g, b] (from the terminal's OSC 11 response, or the GUI's Normal highlight) so scripts that want to pre-composite their pixels can match the popup's actual backdrop -- handy when generating an image with anti-aliased edges that need to blend cleanly into the editor.

Minimal RGB example — a 4×4 red square popped at the cursor:

let pixels = repeat([0xff, 0x00, 0x00], 4 * 4)->list2blob()
call popup_create('', #{
      \ image: #{ data: pixels, width: 4, height: 4 },
      \ line: 'cursor+1', col: 'cursor',
      \ })

Real-world example — pipe a PNG through GraphicsMagick and pop the resulting raw RGB bytes:

let png = 'cat.png'
let dim = split(system('gm identify -format "%w %h" ' .. shellescape(png)))
let [w, h] = [str2nr(dim[0]), str2nr(dim[1])]
call system('gm convert ' .. shellescape(png) .. ' -depth 8 rgb:/tmp/cat.rgb')
let blob = readblob('/tmp/cat.rgb')
call popup_create('', #{
      \ image: #{ data: blob, width: w, height: h },
      \ line: 1, col: 1, border: [], padding: [0, 0, 0, 0],
      \ })

Sixel encoding is deferred to popup_adjust_position() so the encoder knows the final winrow and can crop the bottom row of the image to one cell above the screen edge, avoiding sixel-induced terminal scrolling. popup_getoptions() returns the same dict back -- data is a fresh blob copy independent of the popup's internal buffer.

Other refinements:

  • Hidden popups skip image re-emission so the cached sixel is not re-blitted at its old position after the textprop scrolls out of view.
  • A few small redraw/cursor fixes around sixel emit, so terminal cells previously covered by the image are re-rendered cleanly when the popup hides on scroll.
  • has('image'), has('image_sixel'), has('image_gdi') are registered as known feature names so the test framework's CheckFeature works and has('image') is no longer a runtime no-op.

Tests in test_popupwin.vim cover image acceptance for both RGB and RGBA, blob round-trip through popup_getoptions, the validation paths (wrong data length / missing keys), and the existing dimension-update case (whose blob lengths were corrected -- the previous test silently no-op'd because has('image') returned 0).

With this in place, Vim's expressive range grows considerably: anything that can be rendered to an RGB/RGBA buffer can now sit next to your text, anchored to a textprop and tracking the buffer as it scrolls. A few demos of what this enables straight from a markdown buffer:

PlantUML diagram preview, anchored to a fenced ```plantuml block:

https://github.com/user-attachments/assets/8c6e3973-ff16-4605-91be-51bfe77e4308

LaTeX math rendered through the CodeCogs API and pinned next to the formula:

image.png (view on web)

QR code generated for the URL under the cursor, ready to scan with a phone:

https://github.com/user-attachments/assets/352c8398-78b0-4331-8aca-a8d6c010b85e

And -- because the popup just consumes a raw pixel buffer -- you can generate each frame yourself in Vim script and feed it back via popup_setoptions() on a timer. That alone is enough to build an xeyes-style dynamic application, drawn pixel by pixel and updated in place, running entirely inside Vim.

https://github.com/user-attachments/assets/de5ca08a-b0f0-47db-9ac3-ad9ae9d7c89a


You can view, comment on, or merge this pull request online at:

  https://github.com/vim/vim/pull/20136

Commit Summary

  • 8fbd8d3 feat: support image attribute in popup_create() via sixel
  • 2152944 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • c8f5c5a fix popup image redraw after paint and cursor updates
  • a063970 defer sixel encoding and crop to fit screen
  • 2bd224f force full redraw when textprop popup hides on scroll
  • 021672e add RGBA alpha support for popup images
  • 305d90f fix cursor flicker after popup sixel emit
  • 89eb20e skip image emit for hidden popups
  • 89e4f00 expose popup image via popup_getoptions and add tests

File Changes

(24 files)

Patch Links:


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136@github.com>

mattn

unread,
May 4, 2026, 12:58:29 PMMay 4
to vim/vim, Push

@mattn pushed 1 commit.

  • 6e9501f fix CI: list sixel files and avoid -Wcomment on term.c


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/89e4f00ebdb290e866e7c0f5ac66bb98a95e18f0/after/6e9501f7d14135713bb27f1b8b2bf270e72139e4@github.com>

mattn

unread,
May 4, 2026, 1:03:57 PMMay 4
to vim/vim, Push

@mattn pushed 1 commit.

  • 4bf7c8b fix CI: scope GDI-only locals inside FEAT_IMAGE_GDI block in gui_undraw_cursor


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/6e9501f7d14135713bb27f1b8b2bf270e72139e4/after/4bf7c8b6cef4dc5f8af8f7fb3655cc4ef1d20552@github.com>

mattn

unread,
May 4, 2026, 1:16:02 PMMay 4
to vim/vim, Push

@mattn pushed 1 commit.

  • 791dd69 fix CI: list getbgcolor() in :help function-list


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/4bf7c8b6cef4dc5f8af8f7fb3655cc4ef1d20552/after/791dd69691eca154f6aed87178cd52c58481ccbb@github.com>

mattn

unread,
May 4, 2026, 1:33:55 PMMay 4
to vim/vim, Push

@mattn pushed 1 commit.


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/791dd69691eca154f6aed87178cd52c58481ccbb/after/4a9a053dc102f4acb42abaff81cdeb82c90bda99@github.com>

mattn

unread,
May 4, 2026, 2:04:57 PMMay 4
to vim/vim, Push

@mattn pushed 1 commit.

  • 34e8959 fix CI: accept empty list in Test_getcellpixels_for_unix


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/4a9a053dc102f4acb42abaff81cdeb82c90bda99/after/34e8959560b575ad1605f82f69dbb5f4d32ab717@github.com>

Foxe Chen

unread,
May 4, 2026, 4:55:54 PMMay 4
to vim/vim, Subscribed
64-bitman left a comment (vim/vim#20136)

Wow, this is amazing work

do you plan on supporting the kitty graphics protocol? Thanks


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4374415506@github.com>

mattn

unread,
May 5, 2026, 8:23:47 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • a5cbf63 Revert "fix popup image redraw after paint and cursor updates"


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/34e8959560b575ad1605f82f69dbb5f4d32ab717/after/a5cbf63df501207c1b15d4f0d9c1b0d0972caffa@github.com>

mattn

unread,
May 5, 2026, 8:45:09 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • e3a0d49 Revert "fix cursor flicker after popup sixel emit"


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/a5cbf63df501207c1b15d4f0d9c1b0d0972caffa/after/e3a0d49dcd05a25a68f5426dabd2fe62b1df0c2c@github.com>

mattn

unread,
May 5, 2026, 9:02:51 AMMay 5
to vim/vim, Push

@mattn pushed 2 commits.

  • 7a6d99d Revert "skip image emit for hidden popups"
  • 4bb2959 Revert "force full redraw when textprop popup hides on scroll"


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/e3a0d49dcd05a25a68f5426dabd2fe62b1df0c2c/after/4bb29599d45f1bf631d7aa304bb67b7177f123f9@github.com>

mattn

unread,
May 5, 2026, 9:27:22 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 1de6211 Revert "expose popup image via popup_getoptions and add tests"


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/4bb29599d45f1bf631d7aa304bb67b7177f123f9/after/1de6211c69fb27a5f611889aac36f109f132a939@github.com>

mattn

unread,
May 5, 2026, 10:11:45 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 7cabcc6 fix CI: do not call mch_calc_cell_size() from mch_report_winsize


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/1de6211c69fb27a5f611889aac36f109f132a939/after/7cabcc68aa9726b2aec54661b806fff5a128c253@github.com>

mattn

unread,
May 5, 2026, 10:32:39 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 5f80ff7 add kitty graphics protocol backend for popup images


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/7cabcc68aa9726b2aec54661b806fff5a128c253/after/5f80ff72f53b0a9d821419d3b8831cfeb048b667@github.com>

mattn

unread,
May 5, 2026, 11:02:47 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 4868064 broaden kitty backend auto-detection


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/5f80ff72f53b0a9d821419d3b8831cfeb048b667/after/486806495a8b71baa8d2f3d9a24c2cc726e8fd9f@github.com>

mattn

unread,
May 5, 2026, 11:09:36 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 72fcda4 delete kitty placement when popup hides or closes


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/486806495a8b71baa8d2f3d9a24c2cc726e8fd9f/after/72fcda48620bd58ffe445a5455d75fb10cca6026@github.com>

mattn

unread,
May 5, 2026, 11:13:29 AMMay 5
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

@64-bitman Now supported kitty renderer.

https://github.com/user-attachments/assets/fe2b6354-d13c-4b78-883d-aecaee368cef


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4380582687@github.com>

mattn

unread,
May 5, 2026, 11:16:30 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 337c766 fix CI: indent IMAGE_BACKEND_* defines under outer #ifdef


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/72fcda48620bd58ffe445a5455d75fb10cca6026/after/337c766553b05c1f5e92c35fde8188a0ec075d0d@github.com>

Foxe Chen

unread,
May 5, 2026, 11:23:25 AMMay 5
to vim/vim, Subscribed
64-bitman left a comment (vim/vim#20136)

I just took a quick look at the commits, but it seems that detection for the kitty graphics protocol is done via environment variables. However support for it can be queried from the terminal: https://sw.kovidgoyal.net/kitty/graphics-protocol/#querying-support-and-available-transmission-mediums. Not sure if that is feasible though


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4380666838@github.com>

mattn

unread,
May 5, 2026, 11:31:50 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • fe94dc0 add active probe for kitty graphics support


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/337c766553b05c1f5e92c35fde8188a0ec075d0d/after/fe94dc0327631bb4730c95258d25923065d63d28@github.com>

mattn

unread,
May 5, 2026, 11:38:45 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • abd67d8 implement mch_calc_cell_size() on Windows console


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/fe94dc0327631bb4730c95258d25923065d63d28/after/abd67d8a959eb265e1cbb185ddd672deedd81281@github.com>

mattn

unread,
May 5, 2026, 11:44:24 AMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 5f41d73 add CSI 14 t fallback to mch_calc_cell_size() on Windows


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/abd67d8a959eb265e1cbb185ddd672deedd81281/after/5f41d7385debaf204c5e131a2ed76f7bdfac5f3c@github.com>

mattn

unread,
May 5, 2026, 12:18:22 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/5f41d7385debaf204c5e131a2ed76f7bdfac5f3c/after/f6004a4c09d3a935c2b9cdeb31e3817eddbf287d@github.com>

mattn

unread,
May 5, 2026, 12:58:45 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 14d939e fix CI: indent kitty probe preprocessor directives under FEAT_IMAGE_SIXEL


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/f6004a4c09d3a935c2b9cdeb31e3817eddbf287d/after/14d939e5ddf71819ff20f4f18c82d0357094a185@github.com>

mattn

unread,
May 5, 2026, 1:24:59 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • daf69b0 add Cairo image backend for popup_image (GTK2/3)


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/14d939e5ddf71819ff20f4f18c82d0357094a185/after/daf69b03cc42317c049279a3c95e3382f5be2d47@github.com>

mattn

unread,
May 5, 2026, 1:28:15 PMMay 5
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

Now supported cairo renderer (gtk3)
image.png (view on web)


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4381535028@github.com>

mattn

unread,
May 5, 2026, 1:43:13 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/daf69b03cc42317c049279a3c95e3382f5be2d47/after/68f81ef8555b7210acf7919e479780c74b526e19@github.com>

mattn

unread,
May 5, 2026, 1:59:59 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/68f81ef8555b7210acf7919e479780c74b526e19/after/68ce9240f69a8c480224014cbe9b3546c486f4d2@github.com>

mattn

unread,
May 5, 2026, 3:09:41 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 8d83f5c document popup_create() image attribute and image_* features


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/68ce9240f69a8c480224014cbe9b3546c486f4d2/after/8d83f5ced3c928550b771c272c11fa564144af40@github.com>

mattn

unread,
May 5, 2026, 9:09:04 PMMay 5
to vim/vim, Push

@mattn pushed 28 commits.

  • 1f5174d feat: support image attribute in popup_create() via sixel
  • 57756a7 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 229eacc fix popup image redraw after paint and cursor updates
  • a6c31f4 defer sixel encoding and crop to fit screen
  • 812c37e force full redraw when textprop popup hides on scroll
  • 37606da add RGBA alpha support for popup images
  • a634a58 fix cursor flicker after popup sixel emit
  • 128af88 skip image emit for hidden popups
  • 0fe8685 expose popup image via popup_getoptions and add tests
  • bf7b06b fix CI: list sixel files and avoid -Wcomment on term.c
  • dcd1da7 fix CI: scope GDI-only locals inside FEAT_IMAGE_GDI block in gui_undraw_cursor
  • 518bae1 fix CI: list getbgcolor() in :help function-list
  • 307439f fix CI: regenerate proto/popupwin.pro and proto/gui_w32.pro
  • 1c0c6c3 fix CI: accept empty list in Test_getcellpixels_for_unix
  • 1c00439 fix CI: do not call mch_calc_cell_size() from mch_report_winsize
  • f5a3315 add kitty graphics protocol backend for popup images
  • 79f7155 broaden kitty backend auto-detection
  • 9e2623a delete kitty placement when popup hides or closes
  • e29c212 fix CI: indent IMAGE_BACKEND_* defines under outer #ifdef
  • 9be1d3d add active probe for kitty graphics support
  • 0f8d363 implement mch_calc_cell_size() on Windows console
  • d111a64 add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • c863beb fix CI: regenerate proto/os_win32.pro for Windows mch_calc_cell_size
  • fab8e94 fix CI: indent kitty probe preprocessor directives under FEAT_IMAGE_SIXEL
  • 11ddbf1 add Cairo image backend for popup_image (GTK2/3)
  • 4cde41e fix CI: add popup_image wrappers to proto/gui_gtk_x11.pro
  • 4b228af fix CI: match cproto order in proto/gui_gtk_x11.pro
  • 845d639 document popup_create() image attribute and image_* features


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/8d83f5ced3c928550b771c272c11fa564144af40/after/845d6397fc26742f117154a07bb3b5a12b153277@github.com>

mattn

unread,
May 5, 2026, 10:55:49 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 2ac7849 fix CI: don't wrap external API names in |...| in popup.txt


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/845d6397fc26742f117154a07bb3b5a12b153277/after/2ac7849c4c3f94b3bacbcebada8a8f192f964684@github.com>

Foxe Chen

unread,
May 5, 2026, 11:12:37 PMMay 5
to vim/vim, Subscribed

@64-bitman commented on this pull request.


In src/os_unix.c:

> @@ -4527,6 +4527,76 @@ mch_get_shellsize(void)
     return OK;
 }
 
+/*
+ * Synchronously ask the terminal for its window pixel dimensions via the
+ * xterm CSI 14 t query and parse the CSI 4 ; H ; W t response.  Returns OK
+ * and fills *win_w and *win_h on success.  Returns FAIL on any error
+ * (non-tty, no response within ~200ms, malformed reply).
+ */
+    static int
+query_terminal_pixel_size(int *win_w, int *win_h)

Is it possible to do this asynchronously? Although I'm guessing it will be very complicated


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4232915688@github.com>

Foxe Chen

unread,
May 5, 2026, 11:15:51 PMMay 5
to vim/vim, Subscribed

@64-bitman commented on this pull request.


In src/os_unix.c:

> +    }
+    buf[n] = 0;
+    tcsetattr(fd_in, TCSANOW, &old_t);
+
+    // expected: ESC [ 4 ; H ; W t
+    p = (char *)vim_strchr((char_u *)buf, '\033');
+    if (p == NULL || p[1] != '[' || p[2] != '4' || p[3] != ';')
+	return FAIL;
+    p += 4;
+    semi = (char *)vim_strchr((char_u *)p, ';');
+    if (semi == NULL)
+	return FAIL;
+    end = (char *)vim_strchr((char_u *)semi, 't');
+    if (end == NULL)
+	return FAIL;
+    hpx = atoi(p);

maybe use strtol instead of atoi?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4232923174@github.com>

Foxe Chen

unread,
May 5, 2026, 11:20:41 PMMay 5
to vim/vim, Subscribed

@64-bitman commented on this pull request.


In src/popupwin.c:

> +	return FALSE;
+    new_t = old_t;
+    new_t.c_lflag &= ~(ICANON | ECHO);
+    new_t.c_cc[VMIN]  = 0;
+    new_t.c_cc[VTIME] = 3;	    // 300ms grace per read()
+    if (tcsetattr(fd_in, TCSANOW, &new_t) != 0)
+	return FALSE;
+
+    if (write(fd_out, query, sizeof(query) - 1) != sizeof(query) - 1)
+    {
+	tcsetattr(fd_in, TCSANOW, &old_t);
+	return FALSE;
+    }
+
+    // Read until the DA1 reply (`\e[?...c`) lands or we time out.
+    while (n < (int)sizeof(buf) - 1)

Is it possible to check for the response later when it is received, and during the meantime just make it so that kitty graphics protocol isn't available?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4232935619@github.com>

Foxe Chen

unread,
May 5, 2026, 11:30:41 PMMay 5
to vim/vim, Subscribed

@64-bitman commented on this pull request.


In src/popupwin.c:

> +    return ok;
+}
+# endif
+
+/*
+ * Pick a terminal image backend.  Cached on first call.  Returns
+ * IMAGE_BACKEND_KITTY when the host terminal advertises kitty graphics
+ * support, otherwise IMAGE_BACKEND_SIXEL.
+ *
+ * Detection order:
+ *   1. $VIM_IMAGE_BACKEND override ("kitty" | "sixel")
+ *   2. $KITTY_WINDOW_ID                (kitty itself)
+ *   3. $GHOSTTY_BIN_DIR / $GHOSTTY_RESOURCES_DIR  (Ghostty)
+ *   4. $WEZTERM_EXECUTABLE / $WEZTERM_PANE        (WezTerm)
+ *   5. $TERM contains "kitty" / "ghostty" / "wezterm"
+ *   6. $TERM_PROGRAM equals one of the above

I don't think detecting backends based on environment variables is a good idea. Either we can add an option like 'keyprotocol' or just rely on querying the terminal instead. It also seems like Konsole supports the kitty graphics protocol as well.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4232968025@github.com>

mattn

unread,
May 5, 2026, 11:57:57 PMMay 5
to vim/vim, Push

@mattn pushed 1 commit.

  • 7b8b08a use strtol for CSI 14 t pixel size response


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/9330c4f84016e657463d18984839b8cd06347f7c/after/7b8b08a9681f7e4dfe3ea3f1b8fa6e3147236fc8@github.com>

mattn

unread,
May 6, 2026, 12:01:06 AMMay 6
to vim/vim, Push

@mattn pushed 1 commit.

  • bbbdfc3 rely on terminal probe for kitty backend detection


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/7b8b08a9681f7e4dfe3ea3f1b8fa6e3147236fc8/after/bbbdfc3e5f0868fe90dcc61d7829725ec994394b@github.com>

mattn

unread,
May 6, 2026, 12:07:42 AMMay 6
to vim/vim, Subscribed

@mattn commented on this pull request.


In src/os_unix.c:

> @@ -4527,6 +4527,76 @@ mch_get_shellsize(void)
     return OK;
 }
 
+/*
+ * Synchronously ask the terminal for its window pixel dimensions via the
+ * xterm CSI 14 t query and parse the CSI 4 ; H ; W t response.  Returns OK
+ * and fills *win_w and *win_h on success.  Returns FAIL on any error
+ * (non-tty, no response within ~200ms, malformed reply).
+ */
+    static int
+query_terminal_pixel_size(int *win_w, int *win_h)

The query is lazy and cached — it only runs the first time something asks for cell pixel size, and the 200 ms timeout bounds the worst case, so it's a one-time cost on the first image popup.


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4233095198@github.com>

mattn

unread,
May 6, 2026, 12:08:30 AMMay 6
to vim/vim, Subscribed

@mattn commented on this pull request.


In src/popupwin.c:

> +	return FALSE;
+    new_t = old_t;
+    new_t.c_lflag &= ~(ICANON | ECHO);
+    new_t.c_cc[VMIN]  = 0;
+    new_t.c_cc[VTIME] = 3;	    // 300ms grace per read()
+    if (tcsetattr(fd_in, TCSANOW, &new_t) != 0)
+	return FALSE;
+
+    if (write(fd_out, query, sizeof(query) - 1) != sizeof(query) - 1)
+    {
+	tcsetattr(fd_in, TCSANOW, &old_t);
+	return FALSE;
+    }
+
+    // Read until the DA1 reply (`\e[?...c`) lands or we time out.
+    while (n < (int)sizeof(buf) - 1)

Same answer as the CSI 14 t one — the probe is lazy and one-shot per session, cached on first call to popup_image_backend(). Async would be a worthwhile follow-up if the cost actually shows up.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4233097429@github.com>

mattn

unread,
May 10, 2026, 9:26:37 AMMay 10
to vim/vim, Push

@mattn pushed 19 commits.

  • bfb6921 feat: support image attribute in popup_create() via sixel
  • c3f9140 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 74b4f37 fix popup image redraw after paint and cursor updates
  • 86822ad defer sixel encoding and crop to fit screen
  • e806cab force full redraw when textprop popup hides on scroll
  • 6d725ff add RGBA alpha support for popup images
  • 7e2f531 fix cursor flicker after popup sixel emit
  • e41d02f skip image emit for hidden popups
  • 2d2fe68 expose popup image via popup_getoptions and add tests
  • 5980570 add kitty graphics protocol backend for popup images
  • de71e2a broaden kitty backend auto-detection
  • 9e5cad1 delete kitty placement when popup hides or closes
  • f0d7d9a add active probe for kitty graphics support
  • e985b55 implement mch_calc_cell_size() on Windows console
  • f1fe523 add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • 1b328ba add Cairo image backend for popup_image (GTK2/3)
  • 058a68b document popup_create() image attribute and image_* features
  • 4aaa201 use strtol for CSI 14 t pixel size response
  • 9f1c28f rely on terminal probe for kitty backend detection


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/bbbdfc3e5f0868fe90dcc61d7829725ec994394b/after/9f1c28fec617a23363a72ece89f862aa5ef38556@github.com>

mattn

unread,
May 10, 2026, 11:56:31 AMMay 10
to vim/vim, Push

@mattn pushed 1 commit.

  • 403c959 cache sixel encoder buffers across calls


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/9f1c28fec617a23363a72ece89f862aa5ef38556/after/403c959eedd62593fa805f7f1ecd5e6f6a45aac9@github.com>

mattn

unread,
May 10, 2026, 7:25:06 PMMay 10
to vim/vim, Push

@mattn pushed 20 commits.

  • 0f18c5f feat: support image attribute in popup_create() via sixel
  • 39c95d2 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 4cd0532 fix popup image redraw after paint and cursor updates
  • 949656d defer sixel encoding and crop to fit screen
  • 48f2456 force full redraw when textprop popup hides on scroll
  • 953461b add RGBA alpha support for popup images
  • 2264d35 fix cursor flicker after popup sixel emit
  • b3b2b9f skip image emit for hidden popups
  • 0550ff7 expose popup image via popup_getoptions and add tests
  • 51cfce4 add kitty graphics protocol backend for popup images
  • 91693a8 broaden kitty backend auto-detection
  • 431b2b8 delete kitty placement when popup hides or closes
  • 8d4b5b2 add active probe for kitty graphics support
  • 3b0678c implement mch_calc_cell_size() on Windows console
  • a7664be add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • e14daa5 add Cairo image backend for popup_image (GTK2/3)
  • 1b5841e document popup_create() image attribute and image_* features
  • 0c4fa81 use strtol for CSI 14 t pixel size response
  • c501598 rely on terminal probe for kitty backend detection
  • 80c5034 cache sixel encoder buffers across calls


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/403c959eedd62593fa805f7f1ecd5e6f6a45aac9/after/80c503487af817bcc5991ea7db8888b616d10387@github.com>

mattn

unread,
May 10, 2026, 9:16:41 PMMay 10
to vim/vim, Push

@mattn pushed 5 commits.

  • 4048696 crop popup image to clipwindow visible region
  • 03f168f reuse popup_compute_clip() for popup image clipping
  • d4f1a9b emit clipped popup image when origin is above host top
  • 09dfa1a emit popup image after topoff shift is restored
  • dcf27c3 keep popup sixel image off the host status line


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/80c503487af817bcc5991ea7db8888b616d10387/after/dcf27c3f8c9bb9e2efbca76f56ce71326ad168e7@github.com>

mattn

unread,
May 10, 2026, 9:23:17 PMMay 10
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

Now popup image support clipwindow

https://github.com/user-attachments/assets/2dbe7168-52de-41ff-9a25-d269b1d2f7e5


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4416917707@github.com>

github-advanced-security[bot]

unread,
May 10, 2026, 9:28:46 PMMay 10
to vim/vim, Subscribed

@github-advanced-security[bot] commented on this pull request.


In src/popupwin.c:

> +
+    VIM_CLEAR(wp->w_popup_image_seq);
+
+    // The sixel/kitty encoders read data tightly packed as width*height
+    // pixels.  When the source row width changes (left or right clipped),
+    // or when rows must be skipped from the top, build a contiguous cropped
+    // buffer.  A pure bottom crop (target_h reduced, full source width) can
+    // reuse the source buffer as-is.
+    if (crop_left_px > 0 || crop_right_px > 0 || crop_top_px > 0)
+    {
+	int bpp = wp->w_popup_image_alpha ? 4 : 3;
+	int src_stride = wp->w_popup_image_w * bpp;
+	int dst_stride = target_w * bpp;
+	int y;
+
+	crop_buf = alloc(target_h * dst_stride);

CodeQL / Multiplication result converted to larger type

Multiplication result may overflow 'int' before it is converted to 'size_t'.

Show more details


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4260271822@github.com>

mattn

unread,
May 12, 2026, 12:39:27 AMMay 12
to vim/vim, Push

@mattn pushed 25 commits.

  • 9afecba feat: support image attribute in popup_create() via sixel
  • 759ccc0 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • ef319ff fix popup image redraw after paint and cursor updates
  • ae5cd9d defer sixel encoding and crop to fit screen
  • 94ab18a force full redraw when textprop popup hides on scroll
  • 29c4d71 add RGBA alpha support for popup images
  • e5802eb fix cursor flicker after popup sixel emit
  • 9a38687 skip image emit for hidden popups
  • 1b4d2b3 expose popup image via popup_getoptions and add tests
  • f5c539c add kitty graphics protocol backend for popup images
  • 0534c49 broaden kitty backend auto-detection
  • 3358df7 delete kitty placement when popup hides or closes
  • ad37c7f add active probe for kitty graphics support
  • f911520 implement mch_calc_cell_size() on Windows console
  • 2f778db add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • a010931 add Cairo image backend for popup_image (GTK2/3)
  • 7215554 document popup_create() image attribute and image_* features
  • 8a6cabe use strtol for CSI 14 t pixel size response
  • 351e715 rely on terminal probe for kitty backend detection
  • a421068 cache sixel encoder buffers across calls
  • 0a8c5d9 crop popup image to clipwindow visible region
  • f4eb682 reuse popup_compute_clip() for popup image clipping
  • f300026 emit clipped popup image when origin is above host top
  • c5879d3 emit popup image after topoff shift is restored
  • e0e512f keep popup sixel image off the host status line


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/dcf27c3f8c9bb9e2efbca76f56ce71326ad168e7/after/e0e512f677500f1a734306f000fca4c4c0c9abfa@github.com>

mattn

unread,
May 12, 2026, 12:48:02 AMMay 12
to vim/vim, Push

@mattn pushed 1 commit.

  • 9b926c6 drop $VIM_IMAGE_BACKEND override for popup image backend


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/e0e512f677500f1a734306f000fca4c4c0c9abfa/after/9b926c6f484ec00647871fc39928c6b562d95ed7@github.com>

mattn

unread,
May 12, 2026, 12:49:24 AMMay 12
to vim/vim, Push

@mattn pushed 1 commit.

  • 1caba8a avoid int overflow when sizing popup image crop buffer


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/9b926c6f484ec00647871fc39928c6b562d95ed7/after/1caba8aaea470dd80a1e6d9d5dbab141d616a2a9@github.com>

mattn

unread,
May 12, 2026, 2:17:46 AMMay 12
to vim/vim, Push

@mattn pushed 1 commit.

  • 8383bbb clip popup image to host content rect in GUI backends


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/1caba8aaea470dd80a1e6d9d5dbab141d616a2a9/after/8383bbb679027cc1dada366fdfee66476b7cc38d@github.com>

mattn

unread,
May 12, 2026, 2:22:33 AMMay 12
to vim/vim, Push

@mattn pushed 1 commit.

  • d8c91f9 clip kitty popup image to host content rect


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/8383bbb679027cc1dada366fdfee66476b7cc38d/after/d8c91f9fd5a4dcf96b1e0eb16d81268ff1521478@github.com>

mattn

unread,
May 15, 2026, 1:16:59 PMMay 15
to vim/vim, Push

@mattn pushed 29 commits.

  • 29961ff feat: support image attribute in popup_create() via sixel
  • 33f0cc7 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 79483ae fix popup image redraw after paint and cursor updates
  • c97c6e0 defer sixel encoding and crop to fit screen
  • 35b6d2a force full redraw when textprop popup hides on scroll
  • 39aaf13 add RGBA alpha support for popup images
  • 368cf01 fix cursor flicker after popup sixel emit
  • bd3b3b2 skip image emit for hidden popups
  • cab4d98 expose popup image via popup_getoptions and add tests
  • 193381f add kitty graphics protocol backend for popup images
  • 9e2319a broaden kitty backend auto-detection
  • 49a34ee delete kitty placement when popup hides or closes
  • 7b819ce add active probe for kitty graphics support
  • 9a91470 implement mch_calc_cell_size() on Windows console
  • 1f61f40 add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • a3ab9c4 add Cairo image backend for popup_image (GTK2/3)
  • 7e1729b document popup_create() image attribute and image_* features
  • 7895b4d use strtol for CSI 14 t pixel size response
  • 8d0d76f rely on terminal probe for kitty backend detection
  • 7511ae9 cache sixel encoder buffers across calls
  • 5142468 crop popup image to clipwindow visible region
  • 6918c47 reuse popup_compute_clip() for popup image clipping
  • 38fb641 emit clipped popup image when origin is above host top
  • db03863 emit popup image after topoff shift is restored
  • 080c40e keep popup sixel image off the host status line
  • 0edc8e0 drop $VIM_IMAGE_BACKEND override for popup image backend
  • 9661843 avoid int overflow when sizing popup image crop buffer
  • 8f61563 clip popup image to host content rect in GUI backends
  • 61956d1 clip kitty popup image to host content rect


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/d8c91f9fd5a4dcf96b1e0eb16d81268ff1521478/after/61956d1a81f20e6df6d9d908f8eca964b873481f@github.com>

mattn

unread,
May 17, 2026, 7:49:25 PMMay 17
to vim/vim, Push

@mattn pushed 29 commits.

  • 45a4b7c feat: support image attribute in popup_create() via sixel
  • 84e51b7 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 0904934 fix popup image redraw after paint and cursor updates
  • b04ea85 defer sixel encoding and crop to fit screen
  • 564b3e4 force full redraw when textprop popup hides on scroll
  • 431f250 add RGBA alpha support for popup images
  • 25f0eeb fix cursor flicker after popup sixel emit
  • 7fbb4b6 skip image emit for hidden popups
  • 76b0667 expose popup image via popup_getoptions and add tests
  • 24818c8 add kitty graphics protocol backend for popup images
  • 7da81df broaden kitty backend auto-detection
  • 7ae0977 delete kitty placement when popup hides or closes
  • 2547a23 add active probe for kitty graphics support
  • e07208b implement mch_calc_cell_size() on Windows console
  • 1afa106 add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • 86af76c add Cairo image backend for popup_image (GTK2/3)
  • e0f0023 document popup_create() image attribute and image_* features
  • 4bc46a7 use strtol for CSI 14 t pixel size response
  • 7bb1f47 rely on terminal probe for kitty backend detection
  • 6f2e466 cache sixel encoder buffers across calls
  • b2c6ea1 crop popup image to clipwindow visible region
  • 9440a4e reuse popup_compute_clip() for popup image clipping
  • 0585d70 emit clipped popup image when origin is above host top
  • 42938e6 emit popup image after topoff shift is restored
  • ad6db10 keep popup sixel image off the host status line
  • 09c88d3 drop $VIM_IMAGE_BACKEND override for popup image backend
  • 2445898 avoid int overflow when sizing popup image crop buffer
  • 19fdfb0 clip popup image to host content rect in GUI backends
  • cf6eff6 clip kitty popup image to host content rect


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/61956d1a81f20e6df6d9d908f8eca964b873481f/after/cf6eff6327c1642a8112a871818af240afff7b1b@github.com>

Christian Brabandt

unread,
May 18, 2026, 4:29:25 PMMay 18
to vim/vim, Subscribed

@chrisbra commented on this pull request.


In src/testdir/test_popupwin.vim:

> @@ -5482,4 +5482,124 @@ func Test_popupwin_close_copen_redraw()
   delfunc s:PopupFilter
 endfunc
 
+func Test_popup_image_update()
+  if !has('image')
⬇️ Suggested change
-  if !has('image')
+  CheckFeature image

In src/cairo.c:

> +		p[x] = (0xffu << 24)
+		     | ((unsigned int)r << 16)
+		     | ((unsigned int)g << 8)
+		     |  (unsigned int)b;
+	    }
+	}
+    }
+    cairo_surface_mark_dirty(surf);
+}
+
+/*
+ * Build the cached cairo_image_surface_t for "wp" from its RGB / RGBA
+ * pixel buffer.  Existing cache is freed first.  Returns TRUE on
+ * success, FALSE on bad input or out-of-memory.
+ */
+    int

bool?


In src/cairo.c:

> +    if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS)
+    {
+	cairo_surface_destroy(surf);
+	return FALSE;
+    }
+    cairo_popup_image_fill_surface(wp, surf);
+    wp->w_popup_image_surface = surf;
+    return TRUE;
+}
+
+/*
+ * Same-size pixel swap: refill the existing cached surface in place
+ * with new bytes from wp->w_popup_image_data.  Returns FALSE when
+ * there is no cache yet (caller should fall back to a full rebuild).
+ */
+    int

bool?


In src/evalfunc.c:

> + *
+ * Returns a list [r, g, b] (0..255) describing the current background
+ * colour: in GUI mode the "Normal" highlight group's bg, in terminal
+ * mode the value reported by the OSC 11 response (see v:termrbgresp).
+ * Returns an empty list when no value is available, e.g. before the
+ * terminal has answered the query, or when "Normal" has no bg colour.
+ */
+    static void
+f_getbgcolor(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    if (rettv_list_alloc(rettv) == FAIL)
+	return;
+
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+    {
+	int try_hl = FALSE;

bool


In src/gui_w32.c:

> @@ -2728,6 +2732,207 @@ gui_mch_clear_block(
     clear_rect(&rc);
 }
 
+#ifdef FEAT_IMAGE_GDI
+/*
+ * Convert the popup's RGB(A) pixel buffer into a 32-bit BGRX buffer.
+ * "dst" must be large enough for iw * ih * 4 bytes.  When has_alpha is
+ * TRUE, the source has 4 bytes per pixel and the alpha channel is
+ * flattened onto the GUI window background colour (pre-multiplied blend).
+ */
+    static int

bool?


In src/gui_w32.c:

> +		d[1] = s[1];	// G
+		d[2] = s[0];	// R
+	    }
+	    d[3] = 0;
+	    s += src_bpp;
+	    d += 4;
+	}
+    }
+    return TRUE;
+}
+
+/*
+ * Build a top-down 32-bit DIB section for the popup's RGB buffer and cache
+ * both the bitmap and a memory DC on the window for fast BitBlt redraws.
+ */
+    static int

bool?


In src/gui_w32.c:

> +gui_mch_free_popup_image(win_T *wp)
+{
+    if (wp->w_popup_image_hdc != NULL)
+    {
+	DeleteDC((HDC)wp->w_popup_image_hdc);
+	wp->w_popup_image_hdc = NULL;
+    }
+    if (wp->w_popup_image_hbitmap != NULL)
+    {
+	DeleteObject((HBITMAP)wp->w_popup_image_hbitmap);
+	wp->w_popup_image_hbitmap = NULL;
+    }
+    wp->w_popup_image_bits = NULL;
+}
+
+    int

same here


In src/os_mswin.c:

> @@ -579,6 +579,14 @@ mch_new_shellsize(void)
     // never used
 }
 
+    void
+mch_calc_cell_size(struct cellsize *cs_out)
+{
+    // never used
+    cs_out->cs_xpixel = -1;
+    cs_out->cs_ypixel = -1;
+}
+
    void
mch_calc_cell_size(struct cellsize *cs_out UNUSED)
{
    // never used
}

In src/os_win32.c:

> +    if (restored)
+	SetConsoleMode(hIn, mode_in_old);
+
+    // expected: ESC [ 4 ; H ; W t
+    p = (char *)vim_strchr((char_u *)buf, '\033');
+    if (p == NULL || p[1] != '[' || p[2] != '4' || p[3] != ';')
+	return FAIL;
+    p += 4;
+    semi = (char *)vim_strchr((char_u *)p, ';');
+    if (semi == NULL)
+	return FAIL;
+    end = (char *)vim_strchr((char_u *)semi, 't');
+    if (end == NULL)
+	return FAIL;
+    hpx = atoi(p);
+    wpx = atoi(semi + 1);

Can you use vim_str2nr() here ?


In src/popupwin.c:

> + *
+ * Returns TRUE on a positive `OK` response.  Network/permission errors
+ * and timeouts are treated as "not supported".
+ *
+ * [1] https://sw.kovidgoyal.net/kitty/graphics-protocol/
+ *	   #querying-support-and-available-transmission-mediums
+ */
+    static int
+popup_kitty_probe(void)
+{
+    struct termios	old_t, new_t;
+    int			fd_in = read_cmd_fd;
+    int			fd_out = 1;
+    char		buf[256];
+    int			n = 0;
+    int			ok = FALSE;

drop it


In src/popupwin.c:

> + *	  ^^^^ kitty query ^^^^                       ^^^^ DA1 sentinel
+ *
+ * The kitty spec [1] says the terminal will respond with
+ *	`\e_Gi=31;OK\e\\` (or an `_Gi=31;<error>\e\\`)
+ * to the kitty query, followed by its own `\e[?...c` answer to DA1.
+ * We send DA1 as a sentinel: any conforming terminal must reply, so
+ * we have a guaranteed point at which to stop reading; if the kitty
+ * reply did not arrive before then, kitty is not supported.
+ *
+ * Returns TRUE on a positive `OK` response.  Network/permission errors
+ * and timeouts are treated as "not supported".
+ *
+ * [1] https://sw.kovidgoyal.net/kitty/graphics-protocol/
+ *	   #querying-support-and-available-transmission-mediums
+ */
+    static int

bool


In src/popupwin.c:

> +	if (c == 'c' && n >= 3 && buf[n - 2] != '\033')
+	{
+	    int i;
+	    for (i = n - 2; i > 0; --i)
+		if (buf[i] == '?' && buf[i - 1] == '[')
+		    break;
+	    if (i > 0)
+		break;
+	}
+    }
+    buf[n] = NUL;
+    tcsetattr(fd_in, TCSANOW, &old_t);
+
+    // A positive kitty reply contains the literal "_Gi=31;OK".
+    if (strstr(buf, "_Gi=31;OK") != NULL)
+	ok = TRUE;

return true;


In src/sixel.c:

> +    *npal_out = used;
+    *idx_out = idx;
+    return OK;
+
+too_many:
+    return FAIL;
+}
+
+/*
+ * Fallback for RGBA when the unique-colour count exceeds the dynamic
+ * palette: quantize to the same fixed 6x6x6 + 24-grayscale palette as
+ * rgb_to_paletted_fixed, but assign palette index 0 to alpha==0 pixels.
+ * Always succeeds; produces 240 palette entries.
+ */
+    static int
+rgba_to_paletted_fixed(

can this be merged with rgb_to_paletted_fixed() ?


In src/popupwin.c:

> +
+# if defined(FEAT_IMAGE_GDI) || defined(FEAT_IMAGE_CAIRO)
+    if (gui.in_use)
+    {
+	int src_x, src_y, draw_w, draw_h;
+
+	popup_image_gui_clip(wp, &row, &col,
+				    &src_x, &src_y, &draw_w, &draw_h);
+	if (row < 0 || col < 0 || draw_w <= 0 || draw_h <= 0)
+	    return;
+	gui_mch_draw_popup_image(wp, row, col,
+					    src_x, src_y, draw_w, draw_h);
+	return;
+    }
+# endif
+# if defined(FEAT_IMAGE_SIXEL) || defined(FEAT_IMAGE_KITTY)

do we need the if (gui.in_use) here as well? I am thinking of e.g. the Motif Gui.


In src/popupwin.c:

> +				    cy = cs.cs_ypixel;
+				}
+			    }
+			    cw = (iw + cx - 1) / cx;
+			    ch = (ih + cy - 1) / cy;
+			    wp->w_minwidth = cw;
+			    wp->w_maxwidth = cw;
+			    wp->w_minheight = ch;
+			    wp->w_maxheight = ch;
+			}
+# endif
+		    }
+		}
+	    }
+	    else
+		semsg(_(e_invalid_value_for_argument_str_str), "image",

according to the docs, an empty image key should clear the image.


In src/testdir/test_functions.vim:

> @@ -4384,7 +4387,7 @@ func Test_getcellpixels_for_unix()
   call term_sendkeys(buf, ":redi END\<CR>")
   call term_sendkeys(buf, "P")
 
-  call WaitForAssert({-> assert_match("\[\d+, \d+\]", term_getline(buf, 3))}, 1000)
+  call WaitForAssert({-> assert_match('\[\(\d\+,\s*\d\+\)\?\]', term_getline(buf, 3))}, 1000)

Hm, this allows now to return an empty list, doesn't this brake getcellpixels()?


In src/testdir/test_functions.vim:

> @@ -4371,7 +4371,10 @@ endfunc
 

I am missing a test for getbgcolor()


In src/popupwin.c:

> +				cs.cs_ypixel = -1;
+#  if defined(UNIX) || defined(MSWIN)
+				mch_calc_cell_size(&cs);
+#  endif
+				if (cs.cs_xpixel > 0 && cs.cs_ypixel > 0)
+				{
+				    cx = cs.cs_xpixel;
+				    cy = cs.cs_ypixel;
+				}
+			    }
+			    cw = (iw + cx - 1) / cx;
+			    ch = (ih + cy - 1) / cy;
+			    wp->w_minwidth = cw;
+			    wp->w_maxwidth = cw;
+			    wp->w_minheight = ch;
+			    wp->w_maxheight = ch;

If I read this correctly, this overwrites user specified minwidth/maxwidth/minheight/maxheight values?


In src/popupwin.c:

> +		// (the image will be re-blitted on top by popup_emit_image),
+		// so a UPD_VALID redraw is enough -- no need to repaint every
+		// cell under the image like UPD_NOT_VALID would.
+		int same_size_update = (wp->w_popup_image_data != NULL
+			&& wp->w_popup_image_w == iw
+			&& wp->w_popup_image_h == ih
+			&& wp->w_popup_image_alpha == has_alpha);
+
+		if (same_size_update)
+		    mch_memmove(wp->w_popup_image_data, b->bv_ga.ga_data,
+							    (size_t)blen);
+		else
+		{
+		    VIM_CLEAR(wp->w_popup_image_data);
+		    wp->w_popup_image_data = vim_memsave(b->bv_ga.ga_data,
+							    (size_t)blen);

this could return NULL, so we need to check the that w_popup_image_data is not NULL.


In src/popupwin.c:

> @@ -6595,6 +7351,18 @@ update_popups(void (*win_update)(win_T *wp))
 	// Undo the topoff shift applied before drawing the borders so the
 	// next iteration sees the popup's logical winrow.
 	wp->w_winrow -= wp->w_popup_topoff;
+
+#ifdef FEAT_IMAGE
+	// Emit the popup image after the topoff shift is undone so
+	// popup_emit_image() sees the popup's logical winrow.  Otherwise the
+	// clip-top adjustment overshoots by topoff and the image lands below
+	// its correct row, on top of (or beside) the late re-emit done from
+	// update_popup_images().
+# ifdef FEAT_GUI
+	if (!gui.in_use)
+# endif
+	    popup_emit_image(wp);

Won't this draw the popup images twice? once from here and once from update_popup_images()?


In src/popupwin.c:

> +		{
+#ifdef FEAT_IMAGE_KITTY
+		    // Kitty placements need to be deleted explicitly before
+		    // the popup goes hidden -- see popup_hide().
+		    popup_image_clear_kitty(wp);
+#endif
+		    popup_hide_for_textprop(wp);
+		    // Force a full redraw of every window plus a popup mask
+		    // rebuild so the cells previously covered by the popup
+		    // are re-emitted.  Without this, screen_line() may skip
+		    // cells whose ScreenLines content is unchanged, leaving
+		    // stale drawing on the terminal -- in particular sixel
+		    // image pixels persist until the cell is rewritten.
+		    if (wp->w_winrow + popup_height(wp) >= cmdline_row)
+			clear_cmdline = TRUE;
+		    redraw_all_later(UPD_NOT_VALID);

should those be done conditionally for image popups only like this:

if (wp->w_popup_image_data != NULL)
{
            redraw_all_later(UPD_NOT_VALID);
		    status_redraw_all();
		    popup_mask_refresh = TRUE;
}

In src/sixel.c:

> +typedef struct
+{
+    unsigned int    *keys;	    // hash keys for dynamic palette lookup
+    char_u	    *vals;	    // hash values for dynamic palette lookup
+    int		     hash_cap;	    // allocated slots in keys/vals
+    char_u	    *idx;	    // width * height paletted image
+    size_t	     idx_len;	    // allocated bytes in idx
+    char_u	    *pal;	    // dynamic palette storage (RGB triples)
+    int		     pal_len;	    // allocated bytes in pal
+    sixel_band_T     band;	    // reusable band scratch
+} sixel_state_T;
+
+static char_u	sixel_fixed_palette[240 * 3];
+static char_u	sixel_rgb_cube_idx[256];
+static int	sixel_fixed_tables_ready = FALSE;
+static sixel_state_T sixel_state;

can you add a section to free_all_mem() that frees all the static allocated sixel structs states on exit (EXITFREE)


In src/os_unix.c:

> @@ -4527,6 +4527,76 @@ mch_get_shellsize(void)
     return OK;
 }
 
+/*
+ * Synchronously ask the terminal for its window pixel dimensions via the
+ * xterm CSI 14 t query and parse the CSI 4 ; H ; W t response.  Returns OK
+ * and fills *win_w and *win_h on success.  Returns FAIL on any error
+ * (non-tty, no response within ~200ms, malformed reply).
+ */
+    static int
+query_terminal_pixel_size(int *win_w, int *win_h)

But I think it can swallow characters coming the input buffer (e.g. something the user has typed, while Vim is processing the terminal responses) (similar thing in popup_kitty_probe())


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4313084174@github.com>

Copilot

unread,
May 18, 2026, 4:36:53 PMMay 18
to vim/vim, Subscribed

@Copilot commented on this pull request.

Pull request overview

This PR adds support for rendering raw RGB/RGBA pixel buffers inside popup windows via a new image option on popup_create() / popup_setoptions(), with terminal backends (DEC sixel, kitty graphics protocol) and GUI backends (Windows GDI, GTK/Cairo). It also adds getbgcolor() for scripts to query the current background color and updates docs/features/tests accordingly.

Changes:

  • Add popup image option plumbing, screen redraw integration, and backend selection/encoding for sixel/kitty and GDI/Cairo.
  • Add getbgcolor() and extend has() / :version feature reporting for +image* features.
  • Add tests and documentation for popup images and background-color querying.

Reviewed changes

Copilot reviewed 40 out of 40 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/window.c Frees popup image buffers/caches during popup teardown.
src/version.c Registers +image* feature strings for :version.
src/testdir/test_popupwin.vim Adds tests for popup image set/update/getoptions/validation.
src/testdir/test_functions.vim Makes getcellpixels() terminal test accept empty results.
src/term.c Tracks terminal background color (OSC 11) and exposes it via term_get_bg_color().
src/structs.h Adds popup image fields to win_T and introduces image_rgb_T.
src/sixel.c Adds a self-contained RGB(A) → sixel encoder and resize helper.
src/proto/term.pro Updates term_get_bg_color() prototype to return status.
src/proto/sixel.pro Adds prototypes for sixel helpers.
src/proto/popupwin.pro Adds prototypes for popup image repaint helpers.
src/proto/os_win32.pro Adds mch_calc_cell_size() prototype on Win32.
src/proto/os_mswin.pro Adds mch_calc_cell_size() prototype on MS-Windows (stub impl).
src/proto/kitty.pro Adds prototypes for kitty encoder/delete helpers.
src/proto/gui_w32.pro Adds GUI popup image cache/update/draw prototypes for Win32 GUI.
src/proto/gui_gtk_x11.pro Adds GUI popup image cache/update/draw prototypes for GTK GUI.
src/proto/cairo.pro Adds prototypes for shared Cairo popup image backend.
src/proto.h Includes new proto headers based on FEAT_IMAGE_*.
src/popupwin.c Implements image option parsing, backend selection/probing, encoding/cropping, and repaint scheduling.
src/os_win32.c Adds Win32 mch_calc_cell_size() with CSI 14t fallback and caching.
src/os_unix.c Adds CSI 14t fallback/caching for cell pixel size; avoids blocking in mch_report_winsize().
src/os_mswin.c Adds a stub mch_calc_cell_size() for platforms where it’s unused.
src/Makefile Adds new source/object/proto entries for sixel/kitty/cairo backends.
src/Make_vms.mms Adds sixel/kitty/cairo sources/objects for VMS builds.
src/Make_mvc.mak Adds sixel/kitty/cairo objects for MSVC builds.
src/Make_cyg_ming.mak Adds sixel/kitty/cairo objects for Cygwin/MinGW builds.
src/Make_ami.mak Adds sixel/kitty/cairo sources for Amiga build.
src/kitty.c Adds RGB(A) → kitty graphics protocol APC encoder and delete sequence builder.
src/gui.c Repairs GDI GUI redraw damage by repainting overlapping popup images.
src/gui_w32.c Implements Windows GUI popup image cache/update/draw using DIBSection + BitBlt.
src/gui_gtk_x11.c Adds GTK wrappers calling shared Cairo popup image backend.
src/feature.h Introduces FEAT_IMAGE* feature flags and documents backends.
src/evalfunc.c Adds getbgcolor() and registers has('image*') feature names.
src/drawscreen.c Re-emits popup images at the end of screen redraw.
src/cairo.c Implements shared Cairo surface cache/update/composite for popup images.
runtime/doc/various.txt Documents new +image* features.
runtime/doc/usr_41.txt Lists getbgcolor() under GUI functions.
runtime/doc/tags Adds tags for +image*, getbgcolor(), and popup-image.
runtime/doc/popup.txt Documents popup_create()/popup_setoptions() image option and popup-image section.
runtime/doc/builtin.txt Documents getbgcolor() and has('image*') feature names.
Filelist Adds new source/proto files to distribution file list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


In src/popupwin.c:

> +#ifdef FEAT_IMAGE
+    di = dict_find(dict, (char_u *)"image", -1);
+    if (di != NULL && di->di_tv.v_type == VAR_DICT
+				    && di->di_tv.vval.v_dict != NULL)
+    {
+	dict_T	    *idict = di->di_tv.vval.v_dict;
+	dictitem_T  *id = dict_find(idict, (char_u *)"data", -1);
+	int	     iw = (int)dict_get_number(idict, "width");
+	int	     ih = (int)dict_get_number(idict, "height");
+
+	if (id != NULL && id->di_tv.v_type == VAR_BLOB
+				&& id->di_tv.vval.v_blob != NULL
+				&& iw > 0 && ih > 0)
+	{
+	    blob_T  *b = id->di_tv.vval.v_blob;
+	    long     blen = blob_len(b);
+	    int	     has_alpha = (blen == (long)iw * ih * 4);
+
+	    if (has_alpha || blen == (long)iw * ih * 3)
+	    {
+		// Detect "same-size image swap": replacing the pixel buffer
+		// without changing the popup's pixel dimensions or pixel
+		// format.  In that case the cached device bitmap can be
+		// refreshed in place and the popup's screen cells stay valid
+		// (the image will be re-blitted on top by popup_emit_image),
+		// so a UPD_VALID redraw is enough -- no need to repaint every
+		// cell under the image like UPD_NOT_VALID would.
+		int same_size_update = (wp->w_popup_image_data != NULL

popup_setoptions(..., #{image: {}}) is documented as removing a previously set image, but this code path never clears w_popup_image_data unless a valid {data,width,height} blob is provided. With an empty dict (or dict missing required keys) the existing image will remain attached to the popup. Consider explicitly treating an empty dict as "clear image": free w_popup_image_data/w_popup_image_seq, reset dimensions/alpha, free any GUI caches, and schedule a redraw so the underlying cells are repainted.


In src/window.c:

> +# ifdef FEAT_IMAGE
+    vim_free(win->w_popup_image_data);
+#  ifdef FEAT_IMAGE_SIXEL
+    vim_free(win->w_popup_image_seq);
+#  endif
+#  ifdef FEAT_IMAGE_GDI
+    gui_mch_free_popup_image(win);
+#  endif

win_free_popup() frees GUI image caches only under FEAT_IMAGE_GDI, but GTK/Cairo builds also allocate a cached surface (w_popup_image_surface) via gui_mch_*_popup_image() and will leak it here. Use a guard that also covers the Cairo backend (or just #ifdef FEAT_GUI if gui_mch_free_popup_image() is available there) so all GUI image caches are released when a popup is freed.


In src/sixel.c:

> +    if (npal + 1 > band->color_len)
+    {
+	int *seen = ALLOC_MULT(int, npal + 1);
+	int *used = ALLOC_MULT(int, npal + 1);
+	int *xmin = ALLOC_MULT(int, npal + 1);
+	int *xmax = ALLOC_MULT(int, npal + 1);
+
+	if (seen == NULL || used == NULL || xmin == NULL || xmax == NULL)
+	{
+	    vim_free(seen);
+	    vim_free(used);
+	    vim_free(xmin);
+	    vim_free(xmax);
+	    return FAIL;
+	}
+	if (band->color_len > 0)
+	{
+	    mch_memmove(seen, band->seen,
+			(size_t)band->color_len * sizeof(*seen));
+	    mch_memmove(used, band->used,
+			(size_t)band->color_len * sizeof(*used));
+	    mch_memmove(xmin, band->xmin,
+			(size_t)band->color_len * sizeof(*xmin));
+	    mch_memmove(xmax, band->xmax,
+			(size_t)band->color_len * sizeof(*xmax));
+	}
+	vim_free(band->seen);
+	vim_free(band->used);
+	vim_free(band->xmin);
+	vim_free(band->xmax);
+	band->seen = seen;
+	band->used = used;
+	band->xmin = xmin;
+	band->xmax = xmax;
+	band->color_len = npal + 1;
+    }
+    return OK;

sixel_ensure_band_capacity() allocates/extends the per-color arrays (seen, xmin, xmax, etc.) with ALLOC_MULT(), which does not zero-initialize memory. Newly added elements can contain garbage, and if a garbage seen[pix] happens to equal the current generation, the band loop will treat that color as already-initialized and use uninitialized xmin/xmax and bitmasks. Ensure the newly allocated portion is initialized (or allocate with alloc_clear() / explicitly vim_memset() the arrays) and reset seen_gen as needed to make the generation check reliable.


In src/kitty.c:

> +    for (i = 0; i < len; i += 3)
+    {
+	unsigned a = src[i];
+	unsigned b = (i + 1 < len) ? src[i + 1] : 0;
+	unsigned c = (i + 2 < len) ? src[i + 2] : 0;
+	unsigned triple = (a << 16) | (b << 8) | c;
+
+	ga_append(ga, kitty_b64_table[(triple >> 18) & 0x3f]);
+	ga_append(ga, kitty_b64_table[(triple >> 12) & 0x3f]);
+	ga_append(ga, (i + 1 < len)
+			    ? kitty_b64_table[(triple >> 6) & 0x3f] : '=');
+	ga_append(ga, (i + 2 < len)
+			    ? kitty_b64_table[triple & 0x3f]	    : '=');
+    }
+}
+
+/*
+ * Encode an RGB(A) image into a kitty graphics protocol APC sequence.
+ * Returns a malloced char_u* containing the full sequence
+ * (one or more `\e_G...\e\\` envelopes), or NULL on OOM.
+ *
+ * The sequence is emitted with `a=T` (transmit + display), `q=2` (no
+ * status responses), `f=24` for RGB or `f=32` for RGBA, and chunked
+ * via `m=1`/`m=0` so the per-envelope payload stays under kitty's
+ * 4096-byte limit.  When "id" is non-zero it is sent as `i=<id>` so
+ * the resulting placement can later be removed via kitty_delete().
+ */
+    char_u *
+kitty_encode(image_rgb_T *img, int id)
+{
+    garray_T	ga;
+    long	pix_bytes;
+    long	payload_len;
+    long	b64_total;
+    long	offset = 0;
+    int		fmt;
+    int		first = TRUE;
+    char_u	hdr[80];
+
+    if (img == NULL || img->data == NULL || img->width <= 0 || img->height <= 0)
+	return NULL;
+
+    pix_bytes = img->has_alpha ? 4 : 3;
+    payload_len = (long)img->width * img->height * pix_bytes;
+    b64_total = ((payload_len + 2) / 3) * 4;
+    fmt = img->has_alpha ? 32 : 24;
+
+    ga_init2(&ga, 1, (int)b64_total + 256);
+
+    // Emit one envelope per 4096 base64 chars.  The first envelope
+    // carries the full geometry/format header; later envelopes only
+    // need the chunk-continuation marker `m=`.
+    while (offset < b64_total)
+    {
+	long	this_chunk = b64_total - offset;
+	int	more;
+
+	if (this_chunk > 4096)
+	    this_chunk = 4096;
+	more = (offset + this_chunk < b64_total);
+
+	if (first)
+	{
+	    if (id != 0)
+		vim_snprintf((char *)hdr, sizeof(hdr),
+			"\033_Ga=T,f=%d,s=%d,v=%d,i=%d,q=2,m=%d;",
+			fmt, img->width, img->height, id, more ? 1 : 0);
+	    else
+		vim_snprintf((char *)hdr, sizeof(hdr),
+			"\033_Ga=T,f=%d,s=%d,v=%d,q=2,m=%d;",
+			fmt, img->width, img->height, more ? 1 : 0);
+	    first = FALSE;
+	}
+	else
+	{
+	    vim_snprintf((char *)hdr, sizeof(hdr),
+		    "\033_Gm=%d;", more ? 1 : 0);
+	}
+	ga_concat(&ga, hdr);
+
+	// Encode the matching slice of the source bytes.  Each base64
+	// chunk consumes (this_chunk / 4) base64 quartets, which means
+	// (this_chunk * 3 / 4) source bytes.
+	{
+	    long	src_offset = offset * 3 / 4;
+	    long	src_len = this_chunk * 3 / 4;
+
+	    if (src_offset + src_len > payload_len)
+		src_len = payload_len - src_offset;
+	    kitty_b64_append(&ga, img->data + src_offset, src_len);
+	}
+
+	ga_concat(&ga, (char_u *)"\033\\");
+
+	offset += this_chunk;
+    }
+
+    ga_append(&ga, NUL);
+    return (char_u *)ga.ga_data;
+}

kitty_encode()/kitty_b64_append() claim to return NULL on OOM, but they build the sequence using ga_concat() (void, silently no-ops on allocation failure) and ignore ga_append() return values. In low-memory situations this can return a truncated/invalid kitty APC sequence instead of NULL. To match the function contract (and avoid emitting malformed escape sequences), track allocation failures by using helpers that propagate ga_grow() errors (similar to ga_concat_bytes() in sixel.c) and abort/return NULL when any append fails.


In src/feature.h:

> @@ -1086,6 +1086,38 @@
 # define FEAT_PROP_POPUP
 #endif
 
+/*
+ * +image		RGB image rendering inside popup windows.
+ * +image_sixel		terminal backend: emit DEC sixel DCS sequences.
+ * +image_kitty		terminal backend: emit kitty graphics protocol APC
+ *			sequences.  Selected at runtime when the host
+ *			terminal advertises kitty graphics support and
+ *			falls back to sixel otherwise.
+ * +image_gdi		Windows GUI backend: StretchDIBits onto the GUI canvas.

The +image_gdi feature comment says the backend uses StretchDIBits, but the implementation builds a DIB section and draws it with BitBlt(). Updating the comment would avoid confusion when tracing rendering behavior/perf characteristics.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/review/4313683994@github.com>

mattn

unread,
May 18, 2026, 9:39:05 PMMay 18
to vim/vim, Push

@mattn pushed 34 commits.

  • a6e57fe feat: support image attribute in popup_create() via sixel
  • 0252945 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 7ff49b0 fix popup image redraw after paint and cursor updates
  • a598ed5 defer sixel encoding and crop to fit screen
  • bb4780e force full redraw when textprop popup hides on scroll
  • f14f194 add RGBA alpha support for popup images
  • c73f1af fix cursor flicker after popup sixel emit
  • 52ba7d1 skip image emit for hidden popups
  • 78e1d27 expose popup image via popup_getoptions and add tests
  • 350de69 add kitty graphics protocol backend for popup images
  • f22ea23 broaden kitty backend auto-detection
  • b2fea95 delete kitty placement when popup hides or closes
  • 709d32a add active probe for kitty graphics support
  • fc08007 implement mch_calc_cell_size() on Windows console
  • 024ef39 add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • b5beb32 add Cairo image backend for popup_image (GTK2/3)
  • fc5264a document popup_create() image attribute and image_* features
  • caccbb1 use strtol for CSI 14 t pixel size response
  • d4c3f48 rely on terminal probe for kitty backend detection
  • 9fdde6a cache sixel encoder buffers across calls
  • da2459e crop popup image to clipwindow visible region
  • bbf0c41 reuse popup_compute_clip() for popup image clipping
  • 7887891 emit clipped popup image when origin is above host top
  • 61bd248 emit popup image after topoff shift is restored
  • 77ede48 keep popup sixel image off the host status line
  • c033cb8 drop $VIM_IMAGE_BACKEND override for popup image backend
  • e031e34 avoid int overflow when sizing popup image crop buffer
  • df6b385 clip popup image to host content rect in GUI backends
  • 732e824 clip kitty popup image to host content rect
  • 02f5ef0 fix +image_gdi backend comment
  • d8b2d91 free cached cairo popup image in win_free_popup()
  • d28a42f zero new tail when growing sixel band arrays
  • 3513480 propagate allocation failures from kitty_encode()
  • 0255c66 clear popup image when image dict is empty


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/cf6eff6327c1642a8112a871818af240afff7b1b/after/0255c66043428c4c61cd268fabf449ce0eb8172b@github.com>

mattn

unread,
May 18, 2026, 9:56:27 PMMay 18
to vim/vim, Push

@mattn pushed 1 commit.

  • 022472a clear sixel residue in popup padding when popup moves


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/0255c66043428c4c61cd268fabf449ce0eb8172b/after/022472a83dcd66b006356358a742881a02ee9cfa@github.com>

mattn

unread,
May 19, 2026, 12:17:40 AMMay 19
to vim/vim, Push

@mattn pushed 9 commits.

  • c281f15 use bool/true/false for popup image helpers
  • cdc2d80 address chrisbra review for popup image
  • 6cab1d9 terminal probes: forward stray bytes to input buffer
  • fcddb38 drop body of mch_calc_cell_size stub on FEAT_GUI_MSWIN
  • bc0ee4d parse CSI 14 t pixel size with vim_str2nr()
  • 2ebea39 sixel: merge rgba_to_paletted_fixed into rgb_to_paletted_fixed
  • 0a3cb4d sixel: free encoder state from free_all_mem()
  • 3d63d3d test_popupwin: use CheckFeature image
  • 60d1f9f add Test_getbgcolor()


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/022472a83dcd66b006356358a742881a02ee9cfa/after/60d1f9fe44e0ddc6f6b345146cfc6d07fb71abc4@github.com>

mattn

unread,
May 19, 2026, 12:40:43 AMMay 19
to vim/vim, Push

@mattn pushed 2 commits.

  • 9542ffa fix CI: vim_str2nr() decimal flag is 0, not STR2NR_DEC
  • 7118d5a fix CI: drop nested # prefix from sixel_free_all() guards


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/60d1f9fe44e0ddc6f6b345146cfc6d07fb71abc4/after/7118d5a2b38d9d9cf274991d1d233faa9abe909c@github.com>

mattn

unread,
May 19, 2026, 1:16:34 AMMay 19
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

Addressed your review comments. CI is green; the remaining CodeQL alerts are pre-existing sprintf warnings in src/strings.c which this PR doesn't touch.


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4484635346@github.com>

mattn

unread,
May 22, 2026, 9:26:58 PMMay 22
to vim/vim, Push

@mattn pushed 46 commits.

  • b809477 feat: support image attribute in popup_create() via sixel
  • c7ef9df feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 126a0c5 fix popup image redraw after paint and cursor updates
  • 7204572 defer sixel encoding and crop to fit screen
  • ad85321 force full redraw when textprop popup hides on scroll
  • ae02aa7 add RGBA alpha support for popup images
  • 82ed23e fix cursor flicker after popup sixel emit
  • 5aa7e36 skip image emit for hidden popups
  • 8b86a05 expose popup image via popup_getoptions and add tests
  • c12594d add kitty graphics protocol backend for popup images
  • 9f48881 broaden kitty backend auto-detection
  • 94dd1c8 delete kitty placement when popup hides or closes
  • e42e174 add active probe for kitty graphics support
  • 779ed64 implement mch_calc_cell_size() on Windows console
  • e1dd7d7 add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • 0cdf3dd add Cairo image backend for popup_image (GTK2/3)
  • 42c490e document popup_create() image attribute and image_* features
  • f5cd874 use strtol for CSI 14 t pixel size response
  • b2624c8 rely on terminal probe for kitty backend detection
  • 7155b96 cache sixel encoder buffers across calls
  • cdc1780 crop popup image to clipwindow visible region
  • e812727 reuse popup_compute_clip() for popup image clipping
  • 57e8acf emit clipped popup image when origin is above host top
  • bd861ab emit popup image after topoff shift is restored
  • ec858ad keep popup sixel image off the host status line
  • 9957aef drop $VIM_IMAGE_BACKEND override for popup image backend
  • 9f0ba83 avoid int overflow when sizing popup image crop buffer
  • a3cf42c clip popup image to host content rect in GUI backends
  • eef498f clip kitty popup image to host content rect
  • f378629 fix +image_gdi backend comment
  • b7cdbcb free cached cairo popup image in win_free_popup()
  • 5021e6b zero new tail when growing sixel band arrays
  • c55f77d propagate allocation failures from kitty_encode()
  • a4bab58 clear popup image when image dict is empty
  • bc199c5 clear sixel residue in popup padding when popup moves
  • c90a669 use bool/true/false for popup image helpers
  • 8d44e4c address chrisbra review for popup image
  • c674fdc terminal probes: forward stray bytes to input buffer
  • b283048 drop body of mch_calc_cell_size stub on FEAT_GUI_MSWIN
  • 8865b1f parse CSI 14 t pixel size with vim_str2nr()
  • a04ae31 sixel: merge rgba_to_paletted_fixed into rgb_to_paletted_fixed
  • 5cb7cec sixel: free encoder state from free_all_mem()
  • f8635dc test_popupwin: use CheckFeature image
  • 9a6573a add Test_getbgcolor()
  • c07b7eb fix CI: vim_str2nr() decimal flag is 0, not STR2NR_DEC
  • 2efba06 fix CI: drop nested # prefix from sixel_free_all() guards


View it on GitHub or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/7118d5a2b38d9d9cf274991d1d233faa9abe909c/after/2efba069d750c19379dce296df80a4b0c003e562@github.com>

mattn

unread,
May 22, 2026, 9:42:36 PMMay 22
to vim/vim, Push

@mattn pushed 1 commit.

  • ed1dbbc clear popup image residue in top padding on GTK4/Win32 GUI


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/2efba069d750c19379dce296df80a4b0c003e562/after/ed1dbbc439fb53b96079eb0cf7ec19afe37904e9@github.com>

mattn

unread,
May 23, 2026, 2:04:06 AMMay 23
to vim/vim, Push

@mattn pushed 1 commit.

  • 68e0a19 add popup image GTK4 wrappers to fix link error


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/ed1dbbc439fb53b96079eb0cf7ec19afe37904e9/after/68e0a19149cb1b95da7f9c31cb82c540692de2c3@github.com>

mattn

unread,
May 24, 2026, 7:50:08 AMMay 24
to vim/vim, Push

@mattn pushed 48 commits.

  • 01c7a80 feat: support image attribute in popup_create() via sixel
  • 16d4c23 feat: add GDI backend for popup image, generalize FEAT_SIXEL
  • 2e3402c fix popup image redraw after paint and cursor updates
  • e4e90b9 defer sixel encoding and crop to fit screen
  • f4d3c0b force full redraw when textprop popup hides on scroll
  • 6f1133d add RGBA alpha support for popup images
  • bc487b3 fix cursor flicker after popup sixel emit
  • 02ddcd4 skip image emit for hidden popups
  • ce38ce9 expose popup image via popup_getoptions and add tests
  • 23def4e add kitty graphics protocol backend for popup images
  • ba7890d broaden kitty backend auto-detection
  • 17b53f2 delete kitty placement when popup hides or closes
  • 62dc493 add active probe for kitty graphics support
  • 809c89d implement mch_calc_cell_size() on Windows console
  • 8bc1ace add CSI 14 t fallback to mch_calc_cell_size() on Windows
  • 6d8dd54 add Cairo image backend for popup_image (GTK2/3)
  • 533f751 document popup_create() image attribute and image_* features
  • 4aa86c0 use strtol for CSI 14 t pixel size response
  • 2f1b7ab rely on terminal probe for kitty backend detection
  • ca9df2d cache sixel encoder buffers across calls
  • 4a6b879 crop popup image to clipwindow visible region
  • ee06fa1 reuse popup_compute_clip() for popup image clipping
  • ae1b634 emit clipped popup image when origin is above host top
  • 21959e5 emit popup image after topoff shift is restored
  • 4f45074 keep popup sixel image off the host status line
  • 0f4a5b4 drop $VIM_IMAGE_BACKEND override for popup image backend
  • a335e41 avoid int overflow when sizing popup image crop buffer
  • d474731 clip popup image to host content rect in GUI backends
  • 0521cc9 clip kitty popup image to host content rect
  • fc25ea4 fix +image_gdi backend comment
  • 7861072 free cached cairo popup image in win_free_popup()
  • 624860d zero new tail when growing sixel band arrays
  • a49d2f6 propagate allocation failures from kitty_encode()
  • 01a5882 clear popup image when image dict is empty
  • f538da2 clear sixel residue in popup padding when popup moves
  • 408bbcb use bool/true/false for popup image helpers
  • 68e760d address chrisbra review for popup image
  • 643a6ad terminal probes: forward stray bytes to input buffer
  • f369a64 drop body of mch_calc_cell_size stub on FEAT_GUI_MSWIN
  • 8f7ce22 parse CSI 14 t pixel size with vim_str2nr()
  • c6c4db9 sixel: merge rgba_to_paletted_fixed into rgb_to_paletted_fixed
  • 74110ac sixel: free encoder state from free_all_mem()
  • 5022ea6 test_popupwin: use CheckFeature image
  • 681b49e add Test_getbgcolor()
  • c5c24ce fix CI: vim_str2nr() decimal flag is 0, not STR2NR_DEC
  • 02a3828 fix CI: drop nested # prefix from sixel_free_all() guards
  • e2980bd clear popup image residue in top padding on GTK4/Win32 GUI
  • bd84bdb add popup image GTK4 wrappers to fix link error


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/68e0a19149cb1b95da7f9c31cb82c540692de2c3/after/bd84bdb671018813a03f4b625716fa92001f0db0@github.com>

mattn

unread,
May 24, 2026, 9:57:46 AMMay 24
to vim/vim, Push

@mattn pushed 1 commit.

  • 515ea7d merge rgba_to_paletted_fast into rgb_to_paletted_fast


View it on GitHub or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/before/bd84bdb671018813a03f4b625716fa92001f0db0/after/515ea7d94f444e5e347fa9388f2a145ea1db948a@github.com>

mattn

unread,
May 24, 2026, 10:02:01 AMMay 24
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

All review comments addressed. The last one, merging rgba_to_paletted_fast() into the RGB fast path via a has_alpha parameter, is in 515ea7d.


Reply to this email directly, view it on GitHub, or unsubscribe.


Triage notifications on the go with GitHub Mobile for iOS or Android.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4528948499@github.com>

arkissa

unread,
Jun 8, 2026, 3:50:40 AM (21 hours ago) Jun 8
to vim/vim, Subscribed
Arkissa left a comment (vim/vim#20136)

Will be supported in gvim?


Reply to this email directly, view it on GitHub, or unsubscribe.

Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4646506345@github.com>

mattn

unread,
Jun 8, 2026, 4:25:56 AM (21 hours ago) Jun 8
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

Yes, gvim is already supported. The GTK GUI (gvim built with GTK) renders the image through Cairo (FEAT_IMAGE_CAIRO), and the MS-Windows GUI through GDI (FEAT_IMAGE_GDI). See the Cairo screenshot above.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4646744941@github.com>

Christian Brabandt

unread,
Jun 8, 2026, 3:57:51 PM (9 hours ago) Jun 8
to vim/vim, Subscribed
chrisbra left a comment (vim/vim#20136)

Okay, thank you. Can you please squash it into a single commit?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4652922384@github.com>

mattn

unread,
Jun 8, 2026, 8:13:46 PM (5 hours ago) Jun 8
to vim/vim, Subscribed
mattn left a comment (vim/vim#20136)

Squashed into a single commit. Thanks!


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/20136/c4654707745@github.com>

Reply all
Reply to author
Forward
0 new messages