patch: ligatures-scrollback-ringbuffer

This commit is contained in:
2026-01-01 13:39:47 -05:00
parent d9fa845fc4
commit ab0590b48a
14 changed files with 1223 additions and 399 deletions

261
x.c
View File

@@ -19,6 +19,7 @@ char *argv0;
#include "arg.h"
#include "st.h"
#include "win.h"
#include "hb.h"
/* types used in config.h */
typedef struct {
@@ -143,8 +144,9 @@ typedef struct {
} DC;
static inline ushort sixd_to_16bit(int);
static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
static void xdrawglyph(Glyph, int, int);
static void xclear(int, int, int, int);
static int xgeommasktogravity(int);
@@ -759,7 +761,7 @@ xresize(int col, int row)
xclear(0, 0, win.w, win.h);
/* resize to new width */
xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
}
ushort
@@ -1064,6 +1066,9 @@ xunloadfont(Font *f)
void
xunloadfonts(void)
{
/* Clear Harfbuzz font cache. */
hbunloadfonts();
/* Free the loaded fonts in the font cache. */
while (frclen > 0)
XftFontClose(xw.dpy, frc[--frclen].font);
@@ -1190,7 +1195,7 @@ xinit(int cols, int rows)
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
/* font spec buffer */
xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
/* Xft rendering context */
xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
@@ -1244,142 +1249,153 @@ xinit(int cols, int rows)
xsel.xtarget = XA_STRING;
}
void
xresetfontsettings(ushort mode, Font **font, int *frcflags)
{
*font = &dc.font;
if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
*font = &dc.ibfont;
*frcflags = FRC_ITALICBOLD;
} else if (mode & ATTR_ITALIC) {
*font = &dc.ifont;
*frcflags = FRC_ITALIC;
} else if (mode & ATTR_BOLD) {
*font = &dc.bfont;
*frcflags = FRC_BOLD;
}
}
int
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
{
float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
ushort mode, prevmode = USHRT_MAX;
ushort mode = glyphs[0].mode & ~ATTR_WRAP;
Font *font = &dc.font;
int frcflags = FRC_NORMAL;
float runewidth = win.cw;
float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f);
Rune rune;
FT_UInt glyphidx;
FcResult fcres;
FcPattern *fcpattern, *fontpattern;
FcFontSet *fcsets[] = { NULL };
FcCharSet *fccharset;
int i, f, numspecs = 0;
int f, code_idx, numspecs = 0;
float cluster_xp = xp, cluster_yp = yp;
HbTransformData shaped = { 0 };
for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
/* Fetch rune and mode for current glyph. */
rune = glyphs[i].u;
mode = glyphs[i].mode;
/* Initial values. */
xresetfontsettings(mode, &font, &frcflags);
/* Skip dummy wide-character spacing. */
if (mode == ATTR_WDUMMY)
/* Shape the segment. */
hbtransform(&shaped, font->match, glyphs, 0, len);
xp = winx; yp = winy + font->ascent;
cluster_xp = xp; cluster_yp = yp;
for (code_idx = 0; code_idx < shaped.count; code_idx++) {
int idx = shaped.glyphs[code_idx].cluster;
if (glyphs[idx].mode & ATTR_WDUMMY)
continue;
/* Determine font for glyph if different from previous glyph. */
if (prevmode != mode) {
prevmode = mode;
font = &dc.font;
frcflags = FRC_NORMAL;
runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
font = &dc.ibfont;
frcflags = FRC_ITALICBOLD;
} else if (mode & ATTR_ITALIC) {
font = &dc.ifont;
frcflags = FRC_ITALIC;
} else if (mode & ATTR_BOLD) {
font = &dc.bfont;
frcflags = FRC_BOLD;
}
yp = winy + font->ascent;
/* Advance the drawing cursor if we've moved to a new cluster */
if (code_idx > 0 && (idx != shaped.glyphs[code_idx - 1].cluster)) {
xp += runewidth * (idx - shaped.glyphs[code_idx - 1].cluster);
cluster_xp = xp;
cluster_yp = yp;
}
/* Lookup character index with default font. */
glyphidx = XftCharIndex(xw.dpy, font->match, rune);
if (glyphidx) {
if (shaped.glyphs[code_idx].codepoint != 0) {
/* If symbol is found, put it into the specs. */
specs[numspecs].font = font->match;
specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
cluster_xp += shaped.positions[code_idx].x_advance / 64.;
cluster_yp += shaped.positions[code_idx].y_advance / 64.;
numspecs++;
} else {
/* If it's not found, try to fetch it through the font cache. */
rune = glyphs[idx].u;
for (f = 0; f < frclen; f++) {
glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
/* Everything correct. */
if (glyphidx && frc[f].flags == frcflags)
break;
/* We got a default font for a not found glyph. */
if (!glyphidx && frc[f].flags == frcflags
&& frc[f].unicodep == rune) {
break;
}
}
/* Nothing was found. Use fontconfig to find matching font. */
if (f >= frclen) {
if (!font->set)
font->set = FcFontSort(0, font->pattern,
1, 0, &fcres);
fcsets[0] = font->set;
/*
* Nothing was found in the cache. Now use
* some dozen of Fontconfig calls to get the
* font for one single character.
*
* Xft and fontconfig are design failures.
*/
fcpattern = FcPatternDuplicate(font->pattern);
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, rune);
FcPatternAddCharSet(fcpattern, FC_CHARSET,
fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
FcConfigSubstitute(0, fcpattern,
FcMatchPattern);
FcDefaultSubstitute(fcpattern);
fontpattern = FcFontSetMatch(0, fcsets, 1,
fcpattern, &fcres);
/* Allocate memory for the new cache entry. */
if (frclen >= frccap) {
frccap += 16;
frc = xrealloc(frc, frccap * sizeof(Fontcache));
}
frc[frclen].font = XftFontOpenPattern(xw.dpy,
fontpattern);
if (!frc[frclen].font)
die("XftFontOpenPattern failed seeking fallback font: %s\n",
strerror(errno));
frc[frclen].flags = frcflags;
frc[frclen].unicodep = rune;
glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
f = frclen;
frclen++;
FcPatternDestroy(fcpattern);
FcCharSetDestroy(fccharset);
}
specs[numspecs].font = frc[f].font;
specs[numspecs].glyph = glyphidx;
specs[numspecs].x = (short)xp;
specs[numspecs].y = (short)yp;
xp += runewidth;
numspecs++;
continue;
}
/* Fallback on font cache, search the font cache for match. */
for (f = 0; f < frclen; f++) {
glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
/* Everything correct. */
if (glyphidx && frc[f].flags == frcflags)
break;
/* We got a default font for a not found glyph. */
if (!glyphidx && frc[f].flags == frcflags
&& frc[f].unicodep == rune) {
break;
}
}
/* Nothing was found. Use fontconfig to find matching font. */
if (f >= frclen) {
if (!font->set)
font->set = FcFontSort(0, font->pattern,
1, 0, &fcres);
fcsets[0] = font->set;
/*
* Nothing was found in the cache. Now use
* some dozen of Fontconfig calls to get the
* font for one single character.
*
* Xft and fontconfig are design failures.
*/
fcpattern = FcPatternDuplicate(font->pattern);
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, rune);
FcPatternAddCharSet(fcpattern, FC_CHARSET,
fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
FcConfigSubstitute(0, fcpattern,
FcMatchPattern);
FcDefaultSubstitute(fcpattern);
fontpattern = FcFontSetMatch(0, fcsets, 1,
fcpattern, &fcres);
/* Allocate memory for the new cache entry. */
if (frclen >= frccap) {
frccap += 16;
frc = xrealloc(frc, frccap * sizeof(Fontcache));
}
frc[frclen].font = XftFontOpenPattern(xw.dpy,
fontpattern);
if (!frc[frclen].font)
die("XftFontOpenPattern failed seeking fallback font: %s\n",
strerror(errno));
frc[frclen].flags = frcflags;
frc[frclen].unicodep = rune;
glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
f = frclen;
frclen++;
FcPatternDestroy(fcpattern);
FcCharSetDestroy(fccharset);
}
specs[numspecs].font = frc[f].font;
specs[numspecs].glyph = glyphidx;
specs[numspecs].x = (short)xp;
specs[numspecs].y = (short)yp;
xp += runewidth;
numspecs++;
}
/* Cleanup and get ready for next segment. */
hbcleanup(&shaped);
return numspecs;
}
void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen)
{
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
width = charlen * win.cw;
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
@@ -1511,21 +1527,24 @@ void
xdrawglyph(Glyph g, int x, int y)
{
int numspecs;
XftGlyphFontSpec spec;
XftGlyphFontSpec *specs = xw.specbuf;
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
xdrawglyphfontspecs(&spec, g, numspecs, x, y);
numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1);
}
void
xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
{
Color drawcol;
/* remove the old cursor */
if (selected(ox, oy))
og.mode ^= ATTR_REVERSE;
xdrawglyph(og, ox, oy);
/* Redraw the line where cursor was previously.
* It will restore the ligatures broken by the cursor. */
xdrawline(line, 0, oy, len);
if (IS_SET(MODE_HIDE))
return;
@@ -1659,18 +1678,16 @@ xdrawline(Line line, int x1, int y1, int x2)
Glyph base, new;
XftGlyphFontSpec *specs = xw.specbuf;
numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
i = ox = 0;
for (x = x1; x < x2 && i < numspecs; x++) {
for (x = x1; x < x2; x++) {
new = line[x];
if (new.mode == ATTR_WDUMMY)
continue;
if (selected(x, y1))
new.mode ^= ATTR_REVERSE;
if (i > 0 && ATTRCMP(base, new)) {
xdrawglyphfontspecs(specs, base, i, ox, y1);
specs += i;
numspecs -= i;
if ((i > 0) && ATTRCMP(base, new)) {
numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox);
i = 0;
}
if (i == 0) {
@@ -1679,8 +1696,10 @@ xdrawline(Line line, int x1, int y1, int x2)
}
i++;
}
if (i > 0)
xdrawglyphfontspecs(specs, base, i, ox, y1);
if (i > 0) {
numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
}
}
void