kitty: add kitty terminal graphics protocol support
Adds kitty.c/kitty.h implementing the kitty graphics protocol behind KITTY_GRAPHICS_PATCH, enabled in patches.h. Supports transmission (direct, file, temp file, POSIX shared memory), the RGB/RGBA/PNG formats with a self-contained PNG decoder, zlib compression, chunked transfers, placements with source rects, scaling, cell offsets, z-index ordering with alpha blending via XRender, relative and Unicode-placeholder virtual placements, the full set of delete selectors, queries, image numbers and animation (frame transfer, composition and playback).
This commit is contained in:
parent
e8e4ba8995
commit
cb1f41c9ae
8
Makefile
8
Makefile
@ -4,7 +4,7 @@
|
||||
|
||||
include config.mk
|
||||
|
||||
SRC = st.c x.c $(LIGATURES_C) $(SIXEL_C)
|
||||
SRC = st.c x.c $(LIGATURES_C) $(SIXEL_C) $(KITTY_C)
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
all: st
|
||||
@ -18,8 +18,8 @@ patches.h:
|
||||
.c.o:
|
||||
$(CC) $(STCFLAGS) -c $<
|
||||
|
||||
st.o: config.h st.h win.h
|
||||
x.o: arg.h config.h st.h win.h $(LIGATURES_H)
|
||||
st.o: config.h st.h win.h $(KITTY_H)
|
||||
x.o: arg.h config.h st.h win.h $(LIGATURES_H) $(KITTY_H)
|
||||
|
||||
$(OBJ): config.h config.mk patches.h
|
||||
|
||||
@ -32,7 +32,7 @@ clean:
|
||||
dist: clean
|
||||
mkdir -p st-$(VERSION)
|
||||
cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
|
||||
config.def.h st.info st.1 arg.h st.h win.h $(LIGATURES_H) $(SRC)\
|
||||
config.def.h st.info st.1 arg.h st.h win.h $(LIGATURES_H) $(KITTY_H) $(SRC)\
|
||||
st-$(VERSION)
|
||||
tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
|
||||
rm -rf st-$(VERSION)
|
||||
|
||||
@ -30,6 +30,11 @@ XRENDER = `$(PKG_CONFIG) --libs xrender`
|
||||
SIXEL_C = sixel.c sixel_hls.c
|
||||
SIXEL_LIBS = `$(PKG_CONFIG) --libs imlib2`
|
||||
|
||||
# Uncomment this for the kitty graphics patch / KITTY_GRAPHICS_PATCH
|
||||
KITTY_C = kitty.c
|
||||
KITTY_H = kitty.h
|
||||
KITTY_LIBS = `$(PKG_CONFIG) --libs zlib xrender`
|
||||
|
||||
# Uncomment for the netwmicon patch / NETWMICON_PATCH
|
||||
#NETWMICON_LIBS = `$(PKG_CONFIG) --libs gdlib`
|
||||
|
||||
@ -38,7 +43,7 @@ INCS = -I$(X11INC) \
|
||||
`$(PKG_CONFIG) --cflags fontconfig` \
|
||||
`$(PKG_CONFIG) --cflags freetype2` \
|
||||
$(LIGATURES_INC)
|
||||
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${SIXEL_LIBS} ${XRENDER} ${XCURSOR}\
|
||||
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${SIXEL_LIBS} ${KITTY_LIBS} ${XRENDER} ${XCURSOR}\
|
||||
`$(PKG_CONFIG) --libs fontconfig` \
|
||||
`$(PKG_CONFIG) --libs freetype2` \
|
||||
$(LIGATURES_LIBS) \
|
||||
|
||||
91
kitty.h
Normal file
91
kitty.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* kitty.h - kitty terminal graphics protocol for st
|
||||
*
|
||||
* See https://sw.kovidgoyal.net/kitty/graphics-protocol/
|
||||
*/
|
||||
#ifndef KITTY_H
|
||||
#define KITTY_H
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* the Unicode placeholder character */
|
||||
#define KITTY_PLACEHOLDER 0x10EEEE
|
||||
|
||||
/* placeholder glyph bits packed into Glyph.kitty */
|
||||
#define KPH_HAS_ROW (1u << 0)
|
||||
#define KPH_HAS_COL (1u << 1)
|
||||
#define KPH_HAS_MSB (1u << 2)
|
||||
#define KPH_ROW_SHIFT 8
|
||||
#define KPH_COL_SHIFT 16
|
||||
#define KPH_MSB_SHIFT 24
|
||||
|
||||
/* limits */
|
||||
#define KITTY_MAX_DIM 16384
|
||||
#define KITTY_STORAGE_QUOTA (320u * 1024u * 1024u)
|
||||
#define KITTY_MAX_REL_DEPTH 8
|
||||
|
||||
/* a decoded RGBA image (or animation frame) */
|
||||
typedef struct KittyFrame {
|
||||
struct KittyFrame *next;
|
||||
unsigned char *pixels; /* RGBA, width*height*4, may be NULL for lazy frames */
|
||||
int width, height;
|
||||
int gap; /* ms until next frame; <0 == gapless */
|
||||
int transient;
|
||||
} KittyFrame;
|
||||
|
||||
typedef struct KittyImage {
|
||||
struct KittyImage *next, *prev;
|
||||
uint32_t id; /* image id (i key) */
|
||||
uint32_t number; /* image number (I key) */
|
||||
int width, height; /* dimensions of the root frame */
|
||||
unsigned char *pixels; /* root frame RGBA data */
|
||||
size_t datasize;
|
||||
KittyFrame *frames; /* animation frames, frame 1 == root (pixels) */
|
||||
int nframes;
|
||||
int current_frame; /* 1-based */
|
||||
int anim_state; /* 0 stopped, 2 loading, 3 running */
|
||||
int loops; /* remaining loops, -1 infinite */
|
||||
int transient;
|
||||
uint64_t last_frame_time;/* ms of last frame advance */
|
||||
unsigned refs; /* number of placements */
|
||||
} KittyImage;
|
||||
|
||||
typedef struct KittyPlacement {
|
||||
struct KittyPlacement *next, *prev;
|
||||
KittyImage *image;
|
||||
uint32_t id; /* placement id (p key) */
|
||||
int virt; /* U=1 virtual placement (Unicode placeholder) */
|
||||
/* source rect in image pixels */
|
||||
int sx, sy, sw, sh;
|
||||
/* screen position */
|
||||
int x, y; /* cell coordinates; y is in scrollback space */
|
||||
int xoff, yoff; /* pixel offset within the first cell */
|
||||
int cols, rows; /* size on screen in cells */
|
||||
int z;
|
||||
int alt; /* belongs to the alternate screen */
|
||||
/* relative placement */
|
||||
uint32_t parent_img, parent_plc;
|
||||
int hoff, voff;
|
||||
/* scaled pixmap cache */
|
||||
void *pixmap;
|
||||
int pm_w, pm_h; /* pixel size the pixmap was rendered at */
|
||||
int pm_frame; /* frame the pixmap was rendered from */
|
||||
} KittyPlacement;
|
||||
|
||||
/* entry points used by st.c / x.c */
|
||||
void kitty_apc(const char *buf, size_t len);
|
||||
void kitty_draw(void);
|
||||
void kitty_scroll(int top, int bot, int n, int save);
|
||||
void kitty_deleteplacements(int alt);
|
||||
void kitty_clearregion(int x1, int y1, int x2, int y2);
|
||||
void kitty_reset(void);
|
||||
void kitty_altscreen(int alt);
|
||||
void kitty_resize(void);
|
||||
void kitty_trimhistory(int histsize);
|
||||
int kitty_animating(double *timeout_ms);
|
||||
int kitty_is_diacritic(Rune u);
|
||||
int kitty_absorb_diacritic(Glyph *gp, Rune u);
|
||||
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
#endif // KITTY_H
|
||||
@ -9,6 +9,9 @@ tloaddefscreen(int clear, int loadcursor)
|
||||
#if SIXEL_PATCH
|
||||
tdeleteimages();
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_deleteplacements(1);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
}
|
||||
col = term.col, row = term.row;
|
||||
tswapscreen();
|
||||
@ -37,6 +40,9 @@ tloadaltscreen(int clear, int savecursor)
|
||||
#if SIXEL_PATCH
|
||||
tdeleteimages();
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_deleteplacements(1);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +65,9 @@ tclearglyph(Glyph *gp, int usecurattr)
|
||||
}
|
||||
gp->mode = ATTR_NULL;
|
||||
gp->u = ' ';
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
gp->kitty = 0;
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
}
|
||||
|
||||
#if SIXEL_PATCH
|
||||
@ -268,6 +277,9 @@ rscrolldown(int n)
|
||||
#if SIXEL_PATCH
|
||||
scroll_images(n - term.scr);
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_trimhistory(n - term.scr);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
term.scr = 0;
|
||||
if (sel.ob.x != -1 && !sel.alt)
|
||||
selmove(-i);
|
||||
@ -340,6 +352,9 @@ tresizealt(int col, int row)
|
||||
#if SIXEL_PATCH
|
||||
scroll_images(-i);
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_trimhistory(-i);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
term.c.y = row - 1;
|
||||
}
|
||||
for (i += row; i < term.row; i++)
|
||||
@ -412,6 +427,9 @@ kscrolldown(const Arg* a)
|
||||
#if SIXEL_PATCH
|
||||
scroll_images(-1*n);
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_trimhistory(-1*n);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
if (n > 0)
|
||||
@ -444,6 +462,9 @@ kscrollup(const Arg* a)
|
||||
#if SIXEL_PATCH
|
||||
scroll_images(n);
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_trimhistory(n);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
if (n > 0)
|
||||
@ -501,6 +522,10 @@ tscrollup(int top, int bot, int n, int mode)
|
||||
term.line[i+n] = temp;
|
||||
}
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_scroll(top, bot, n, savehist);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if SIXEL_PATCH
|
||||
if (alt || !savehist) {
|
||||
/* move images, if they are inside the scrolling region */
|
||||
@ -571,6 +596,10 @@ tscrolldown(int top, int n)
|
||||
term.line[i-n] = temp;
|
||||
}
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_scroll(top, bot, -n, 0);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if SIXEL_PATCH
|
||||
/* move images, if they are inside the scrolling region */
|
||||
for (im = term.images; im; im = next) {
|
||||
|
||||
@ -343,6 +343,20 @@
|
||||
*/
|
||||
#define SINGLE_DRAWABLE_BUFFER_PATCH 0
|
||||
|
||||
/* This patch adds support for the kitty terminal graphics protocol, which lets
|
||||
* clients display raster images in the terminal. It supports the RGB, RGBA and
|
||||
* PNG formats, zlib compression, direct/file/temp-file/shared-memory
|
||||
* transmission, placements (including relative and Unicode-placeholder based
|
||||
* virtual placements), z-index ordering with alpha blending, image deletion,
|
||||
* queries and animations.
|
||||
*
|
||||
* Note that you need to uncomment the corresponding lines in config.mk when
|
||||
* including this patch (it needs zlib and Xrender).
|
||||
*
|
||||
* https://sw.kovidgoyal.net/kitty/graphics-protocol/
|
||||
*/
|
||||
#define KITTY_GRAPHICS_PATCH 0
|
||||
|
||||
/* This patch adds SIXEL graphics support for st.
|
||||
* Note that patch/sixel.c/sixel_hls.c come from mintty, licensed under GPL.
|
||||
* Known issues:
|
||||
|
||||
14
patches.h
14
patches.h
@ -343,6 +343,20 @@
|
||||
*/
|
||||
#define SINGLE_DRAWABLE_BUFFER_PATCH 0
|
||||
|
||||
/* This patch adds support for the kitty terminal graphics protocol, which lets
|
||||
* clients display raster images in the terminal. It supports the RGB, RGBA and
|
||||
* PNG formats, zlib compression, direct/file/temp-file/shared-memory
|
||||
* transmission, placements (including relative and Unicode-placeholder based
|
||||
* virtual placements), z-index ordering with alpha blending, image deletion,
|
||||
* queries and animations.
|
||||
*
|
||||
* Note that you need to uncomment the corresponding lines in config.mk when
|
||||
* including this patch (it needs zlib and Xrender).
|
||||
*
|
||||
* https://sw.kovidgoyal.net/kitty/graphics-protocol/
|
||||
*/
|
||||
#define KITTY_GRAPHICS_PATCH 1
|
||||
|
||||
/* This patch adds SIXEL graphics support for st.
|
||||
* Note that patch/sixel.c/sixel_hls.c come from mintty, licensed under GPL.
|
||||
* Known issues:
|
||||
|
||||
42
st.c
42
st.c
@ -30,6 +30,10 @@
|
||||
#include "sixel.h"
|
||||
#endif // SIXEL_PATCH
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
#include "kitty.h"
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if defined(__linux)
|
||||
#include <pty.h>
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
|
||||
@ -1227,6 +1231,9 @@ treset(void)
|
||||
#if SIXEL_PATCH
|
||||
tdeleteimages();
|
||||
#endif // SIXEL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_deleteplacements(i);
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
tswapscreen();
|
||||
}
|
||||
#if REFLOW_PATCH
|
||||
@ -1595,6 +1602,14 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
|
||||
if (isboxdraw(u))
|
||||
term.line[y][x].mode |= ATTR_BOXDRAW;
|
||||
#endif // BOXDRAW_PATCH
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
term.line[y][x].kitty = 0;
|
||||
if (u == KITTY_PLACEHOLDER)
|
||||
term.line[y][x].mode |= ATTR_KITTYPH;
|
||||
else
|
||||
term.line[y][x].mode &= ~ATTR_KITTYPH;
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
}
|
||||
|
||||
#if !REFLOW_PATCH
|
||||
@ -2219,6 +2234,10 @@ csihandle(void)
|
||||
#endif // REFLOW_PATCH
|
||||
break;
|
||||
case 2: /* screen */
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
/* the clear screen escape also clears all images */
|
||||
kitty_deleteplacements(IS_SET(MODE_ALTSCREEN));
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
#if REFLOW_PATCH
|
||||
if (IS_SET(MODE_ALTSCREEN)) {
|
||||
tclearregion(0, 0, term.col-1, term.row-1, 1);
|
||||
@ -2603,6 +2622,16 @@ strhandle(void)
|
||||
#endif // SIXEL_PATCH
|
||||
|
||||
term.esc &= ~(ESC_STR_END|ESC_STR);
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
/* handled before strparse(), which destroys the ';' separators */
|
||||
if (strescseq.type == '_') {
|
||||
strescseq.buf[strescseq.len] = '\0';
|
||||
kitty_apc(strescseq.buf, strescseq.len);
|
||||
return;
|
||||
}
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
strparse();
|
||||
par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;
|
||||
|
||||
@ -3381,6 +3410,19 @@ check_control_code:
|
||||
return;
|
||||
}
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
/* absorb row/column diacritics into the preceding placeholder cell */
|
||||
if (width == 0 && kitty_is_diacritic(u)) {
|
||||
int px = term.c.x - ((term.c.state & CURSOR_WRAPNEXT) ? 0 : 1);
|
||||
if (px >= 0 && px < term.col &&
|
||||
(term.line[term.c.y][px].mode & ATTR_KITTYPH) &&
|
||||
kitty_absorb_diacritic(&term.line[term.c.y][px], u)) {
|
||||
term.dirty[term.c.y] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if REFLOW_PATCH
|
||||
/* selected() takes relative coordinates */
|
||||
if (selected(term.c.x + term.scr, term.c.y + term.scr))
|
||||
|
||||
6
st.h
6
st.h
@ -69,6 +69,9 @@ enum glyph_attribute {
|
||||
#if KEYBOARDSELECT_PATCH && REFLOW_PATCH
|
||||
ATTR_HIGHLIGHT = 1 << 17,
|
||||
#endif // KEYBOARDSELECT_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
ATTR_KITTYPH = 1 << 18,
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
|
||||
};
|
||||
|
||||
@ -144,6 +147,9 @@ typedef struct {
|
||||
int ustyle; /* underline style */
|
||||
int ucolor[3]; /* underline color */
|
||||
#endif // UNDERCURL_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
uint32_t kitty; /* kitty Unicode placeholder row/column/msb */
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
} Glyph;
|
||||
|
||||
typedef Glyph *Line;
|
||||
|
||||
19
x.c
19
x.c
@ -60,6 +60,10 @@ static void zoomreset(const Arg *);
|
||||
#include "patch/st_include.h"
|
||||
#include "patch/x_include.h"
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
#include "kitty.h"
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
/* config.h for applying patches and the configuration. */
|
||||
#include "config.h"
|
||||
|
||||
@ -332,6 +336,10 @@ zoomabs(const Arg *arg)
|
||||
}
|
||||
#endif // SIXEL_PATCH
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_resize();
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
cresize(0, 0);
|
||||
redraw();
|
||||
xhints();
|
||||
@ -3265,6 +3273,10 @@ xfinishdraw(void)
|
||||
}
|
||||
#endif // SIXEL_PATCH
|
||||
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
kitty_draw();
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
#if !SINGLE_DRAWABLE_BUFFER_PATCH
|
||||
XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0);
|
||||
#endif // SINGLE_DRAWABLE_BUFFER_PATCH
|
||||
@ -3770,6 +3782,13 @@ run(void)
|
||||
|
||||
/* idle detected or maxlatency exhausted -> draw */
|
||||
timeout = -1;
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
{
|
||||
double animto = -1;
|
||||
if (kitty_animating(&animto) && animto >= 0)
|
||||
timeout = animto;
|
||||
}
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
#if BLINKING_CURSOR_PATCH
|
||||
if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK)))
|
||||
#else
|
||||
|
||||
Loading…
Reference in New Issue
Block a user