aboutsummaryrefslogtreecommitdiff
path: root/x.c
diff options
context:
space:
mode:
authorTim Keller <tjkeller.xyz>2025-01-08 21:41:30 -0600
committerTim Keller <tjkeller.xyz>2025-01-08 21:41:30 -0600
commitd8e0ba487eca7164aed1d1fde1258558a6e44368 (patch)
treef9bc22b98f3450252a52c79a6bdeca2fe5b814b8 /x.c
parent6009e6e25bdff9548f085e9ae562b1ca305d3a0b (diff)
downloadst-d8e0ba487eca7164aed1d1fde1258558a6e44368.tar.xz
st-d8e0ba487eca7164aed1d1fde1258558a6e44368.zip
add patches and configtj-0.9.2.00.9.2.0
Diffstat (limited to 'x.c')
-rw-r--r--x.c504
1 files changed, 492 insertions, 12 deletions
diff --git a/x.c b/x.c
index d73152b..41aebdf 100644
--- a/x.c
+++ b/x.c
@@ -45,6 +45,19 @@ typedef struct {
signed char appcursor; /* application cursor */
} Key;
+/* Undercurl slope types */
+enum undercurl_slope_type {
+ UNDERCURL_SLOPE_ASCENDING = 0,
+ UNDERCURL_SLOPE_TOP_CAP = 1,
+ UNDERCURL_SLOPE_DESCENDING = 2,
+ UNDERCURL_SLOPE_BOTTOM_CAP = 3
+};
+
+typedef enum {
+ PixelGeometry,
+ CellGeometry
+} Geometry;
+
/* X modifiers */
#define XK_ANY_MOD UINT_MAX
#define XK_NO_MOD 0
@@ -63,6 +76,9 @@ static void ttysend(const Arg *);
/* config.h for applying patches and the configuration. */
#include "config.h"
+/* size of title stack */
+#define TITLESTACKSIZE 8
+
/* XEMBED messages */
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
@@ -220,6 +236,8 @@ static DC dc;
static XWindow xw;
static XSelection xsel;
static TermWindow win;
+static int tstki; /* title stack index */
+static char *titlestack[TITLESTACKSIZE]; /* title stack */
/* Font Ring Cache */
enum {
@@ -686,6 +704,8 @@ setsel(char *str, Time t)
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
selclear();
+
+ xclipcopy();
}
void
@@ -1127,7 +1147,7 @@ xicdestroy(XIC xim, XPointer client, XPointer call)
}
void
-xinit(int cols, int rows)
+xinit(int w, int h)
{
XGCValues gcvalues;
Cursor cursor;
@@ -1152,8 +1172,16 @@ xinit(int cols, int rows)
xloadcols();
/* adjust fixed window geometry */
- win.w = 2 * borderpx + cols * win.cw;
- win.h = 2 * borderpx + rows * win.ch;
+ switch (geometry) {
+ case CellGeometry:
+ win.w = 2 * borderpx + w * win.cw;
+ win.h = 2 * borderpx + h * win.ch;
+ break;
+ case PixelGeometry:
+ win.w = w;
+ win.h = h;
+ break;
+ }
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
if (xw.gm & YNegative)
@@ -1240,6 +1268,8 @@ xinit(int cols, int rows)
xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
if (xsel.xtarget == None)
xsel.xtarget = XA_STRING;
+
+ boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
}
int
@@ -1287,7 +1317,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
}
/* Lookup character index with default font. */
- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+ if (mode & ATTR_BOXDRAW) {
+ /* minor shoehorning: boxdraw uses only this ushort */
+ glyphidx = boxdrawindex(&glyphs[i]);
+ } else {
+ /* Lookup character index with default font. */
+ glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+ }
if (glyphidx) {
specs[numspecs].font = font->match;
specs[numspecs].glyph = glyphidx;
@@ -1374,6 +1410,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
return numspecs;
}
+static int isSlopeRising (int x, int iPoint, int waveWidth)
+{
+ // . . . .
+ // / \ / \ / \ / \
+ // / \ / \ / \ / \
+ // . . . . .
+
+ // Find absolute `x` of point
+ x += iPoint * (waveWidth/2);
+
+ // Find index of absolute wave
+ int absSlope = x / ((float)waveWidth/2);
+
+ return (absSlope % 2);
+}
+
+static int getSlope (int x, int iPoint, int waveWidth)
+{
+ // Sizes: Caps are half width of slopes
+ // 1_2 1_2 1_2 1_2
+ // / \ / \ / \ / \
+ // / \ / \ / \ / \
+ // 0 3_0 3_0 3_0 3_
+ // <2-> <1> <---6---->
+
+ // Find type of first point
+ int firstType;
+ x -= (x / waveWidth) * waveWidth;
+ if (x < (waveWidth * (2.f/6.f)))
+ firstType = UNDERCURL_SLOPE_ASCENDING;
+ else if (x < (waveWidth * (3.f/6.f)))
+ firstType = UNDERCURL_SLOPE_TOP_CAP;
+ else if (x < (waveWidth * (5.f/6.f)))
+ firstType = UNDERCURL_SLOPE_DESCENDING;
+ else
+ firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
+
+ // Find type of given point
+ int pointType = (iPoint % 4);
+ pointType += firstType;
+ pointType %= 4;
+
+ return pointType;
+}
+
void
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
{
@@ -1492,12 +1573,366 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
/* Render the glyphs. */
- XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
+ if (base.mode & ATTR_BOXDRAW) {
+ drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len);
+ } else {
+ /* Render the glyphs. */
+ XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
+ }
/* Render underline and strikethrough. */
if (base.mode & ATTR_UNDERLINE) {
- XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
- width, 1);
+ // Underline Color
+ const int widthThreshold = 28; // +1 width every widthThreshold px of font
+ int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
+ int linecolor;
+ if ((base.ucolor[0] >= 0) &&
+ !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
+ !(base.mode & ATTR_INVISIBLE)
+ ) {
+ // Special color for underline
+ // Index
+ if (base.ucolor[1] < 0) {
+ linecolor = dc.col[base.ucolor[0]].pixel;
+ }
+ // RGB
+ else {
+ XColor lcolor;
+ lcolor.red = base.ucolor[0] * 257;
+ lcolor.green = base.ucolor[1] * 257;
+ lcolor.blue = base.ucolor[2] * 257;
+ lcolor.flags = DoRed | DoGreen | DoBlue;
+ XAllocColor(xw.dpy, xw.cmap, &lcolor);
+ linecolor = lcolor.pixel;
+ }
+ } else {
+ // Foreground color for underline
+ linecolor = fg->pixel;
+ }
+
+ XGCValues ugcv = {
+ .foreground = linecolor,
+ .line_width = wlw,
+ .line_style = LineSolid,
+ .cap_style = CapNotLast
+ };
+
+ GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
+ GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
+ &ugcv);
+
+ // Underline Style
+ if (base.ustyle != 3) {
+ //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1);
+ XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx,
+ winy + dc.font.ascent + 1, width, wlw);
+ } else if (base.ustyle == 3) {
+ int ww = win.cw;//width;
+ int wh = dc.font.descent - wlw/2 - 1;//r.height/7;
+ int wx = winx;
+ int wy = winy + win.ch - dc.font.descent;
+
+#if UNDERCURL_STYLE == UNDERCURL_CURLY
+ // Draw waves
+ int narcs = charlen * 2 + 1;
+ XArc *arcs = xmalloc(sizeof(XArc) * narcs);
+
+ int i = 0;
+ for (i = 0; i < charlen-1; i++) {
+ arcs[i*2] = (XArc) {
+ .x = wx + win.cw * i + ww / 4,
+ .y = wy,
+ .width = win.cw / 2,
+ .height = wh,
+ .angle1 = 0,
+ .angle2 = 180 * 64
+ };
+ arcs[i*2+1] = (XArc) {
+ .x = wx + win.cw * i + ww * 0.75,
+ .y = wy,
+ .width = win.cw/2,
+ .height = wh,
+ .angle1 = 180 * 64,
+ .angle2 = 180 * 64
+ };
+ }
+ // Last wave
+ arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
+ 0, 180 * 64 };
+ // Last wave tail
+ arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
+ wh, 180 * 64, 90 * 64};
+ // First wave tail
+ i++;
+ arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
+ 90 * 64 };
+
+ XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
+
+ free(arcs);
+#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
+ // Make the underline corridor larger
+ /*
+ wy -= wh;
+ */
+ wh *= 2;
+
+ // Set the angle of the slope to 45°
+ ww = wh;
+
+ // Position of wave is independent of word, it's absolute
+ wx = (wx / (ww/2)) * (ww/2);
+
+ int marginStart = winx - wx;
+
+ // Calculate number of points with floating precision
+ float n = width; // Width of word in pixels
+ n = (n / ww) * 2; // Number of slopes (/ or \)
+ n += 2; // Add two last points
+ int npoints = n; // Convert to int
+
+ // Total length of underline
+ float waveLength = 0;
+
+ if (npoints >= 3) {
+ // We add an aditional slot in case we use a bonus point
+ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
+
+ // First point (Starts with the word bounds)
+ points[0] = (XPoint) {
+ .x = wx + marginStart,
+ .y = (isSlopeRising(wx, 0, ww))
+ ? (wy - marginStart + ww/2.f)
+ : (wy + marginStart)
+ };
+
+ // Second point (Goes back to the absolute point coordinates)
+ points[1] = (XPoint) {
+ .x = (ww/2.f) - marginStart,
+ .y = (isSlopeRising(wx, 1, ww))
+ ? (ww/2.f - marginStart)
+ : (-ww/2.f + marginStart)
+ };
+ waveLength += (ww/2.f) - marginStart;
+
+ // The rest of the points
+ for (int i = 2; i < npoints-1; i++) {
+ points[i] = (XPoint) {
+ .x = ww/2,
+ .y = (isSlopeRising(wx, i, ww))
+ ? wh/2
+ : -wh/2
+ };
+ waveLength += ww/2;
+ }
+
+ // Last point
+ points[npoints-1] = (XPoint) {
+ .x = ww/2,
+ .y = (isSlopeRising(wx, npoints-1, ww))
+ ? wh/2
+ : -wh/2
+ };
+ waveLength += ww/2;
+
+ // End
+ if (waveLength < width) { // Add a bonus point?
+ int marginEnd = width - waveLength;
+ points[npoints] = (XPoint) {
+ .x = marginEnd,
+ .y = (isSlopeRising(wx, npoints, ww))
+ ? (marginEnd)
+ : (-marginEnd)
+ };
+
+ npoints++;
+ } else if (waveLength > width) { // Is last point too far?
+ int marginEnd = waveLength - width;
+ points[npoints-1].x -= marginEnd;
+ if (isSlopeRising(wx, npoints-1, ww))
+ points[npoints-1].y -= (marginEnd);
+ else
+ points[npoints-1].y += (marginEnd);
+ }
+
+ // Draw the lines
+ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
+ CoordModePrevious);
+
+ // Draw a second underline with an offset of 1 pixel
+ if ( ((win.ch / (widthThreshold/2)) % 2)) {
+ points[0].x++;
+
+ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
+ npoints, CoordModePrevious);
+ }
+
+ // Free resources
+ free(points);
+ }
+#else // UNDERCURL_CAPPED
+ // Cap is half of wave width
+ float capRatio = 0.5f;
+
+ // Make the underline corridor larger
+ wh *= 2;
+
+ // Set the angle of the slope to 45°
+ ww = wh;
+ ww *= 1 + capRatio; // Add a bit of width for the cap
+
+ // Position of wave is independent of word, it's absolute
+ wx = (wx / ww) * ww;
+
+ float marginStart;
+ switch(getSlope(winx, 0, ww)) {
+ case UNDERCURL_SLOPE_ASCENDING:
+ marginStart = winx - wx;
+ break;
+ case UNDERCURL_SLOPE_TOP_CAP:
+ marginStart = winx - (wx + (ww * (2.f/6.f)));
+ break;
+ case UNDERCURL_SLOPE_DESCENDING:
+ marginStart = winx - (wx + (ww * (3.f/6.f)));
+ break;
+ case UNDERCURL_SLOPE_BOTTOM_CAP:
+ marginStart = winx - (wx + (ww * (5.f/6.f)));
+ break;
+ }
+
+ // Calculate number of points with floating precision
+ float n = width; // Width of word in pixels
+ // ._.
+ n = (n / ww) * 4; // Number of points (./ \.)
+ n += 2; // Add two last points
+ int npoints = n; // Convert to int
+
+ // Position of the pen to draw the lines
+ float penX = 0;
+ float penY = 0;
+
+ if (npoints >= 3) {
+ XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
+
+ // First point (Starts with the word bounds)
+ penX = winx;
+ switch (getSlope(winx, 0, ww)) {
+ case UNDERCURL_SLOPE_ASCENDING:
+ penY = wy + wh/2.f - marginStart;
+ break;
+ case UNDERCURL_SLOPE_TOP_CAP:
+ penY = wy;
+ break;
+ case UNDERCURL_SLOPE_DESCENDING:
+ penY = wy + marginStart;
+ break;
+ case UNDERCURL_SLOPE_BOTTOM_CAP:
+ penY = wy + wh/2.f;
+ break;
+ }
+ points[0].x = penX;
+ points[0].y = penY;
+
+ // Second point (Goes back to the absolute point coordinates)
+ switch (getSlope(winx, 1, ww)) {
+ case UNDERCURL_SLOPE_ASCENDING:
+ penX += ww * (1.f/6.f) - marginStart;
+ penY += 0;
+ break;
+ case UNDERCURL_SLOPE_TOP_CAP:
+ penX += ww * (2.f/6.f) - marginStart;
+ penY += -wh/2.f + marginStart;
+ break;
+ case UNDERCURL_SLOPE_DESCENDING:
+ penX += ww * (1.f/6.f) - marginStart;
+ penY += 0;
+ break;
+ case UNDERCURL_SLOPE_BOTTOM_CAP:
+ penX += ww * (2.f/6.f) - marginStart;
+ penY += -marginStart + wh/2.f;
+ break;
+ }
+ points[1].x = penX;
+ points[1].y = penY;
+
+ // The rest of the points
+ for (int i = 2; i < npoints; i++) {
+ switch (getSlope(winx, i, ww)) {
+ case UNDERCURL_SLOPE_ASCENDING:
+ case UNDERCURL_SLOPE_DESCENDING:
+ penX += ww * (1.f/6.f);
+ penY += 0;
+ break;
+ case UNDERCURL_SLOPE_TOP_CAP:
+ penX += ww * (2.f/6.f);
+ penY += -wh / 2.f;
+ break;
+ case UNDERCURL_SLOPE_BOTTOM_CAP:
+ penX += ww * (2.f/6.f);
+ penY += wh / 2.f;
+ break;
+ }
+ points[i].x = penX;
+ points[i].y = penY;
+ }
+
+ // End
+ float waveLength = penX - winx;
+ if (waveLength < width) { // Add a bonus point?
+ int marginEnd = width - waveLength;
+ penX += marginEnd;
+ switch(getSlope(winx, npoints, ww)) {
+ case UNDERCURL_SLOPE_ASCENDING:
+ case UNDERCURL_SLOPE_DESCENDING:
+ //penY += 0;
+ break;
+ case UNDERCURL_SLOPE_TOP_CAP:
+ penY += -marginEnd;
+ break;
+ case UNDERCURL_SLOPE_BOTTOM_CAP:
+ penY += marginEnd;
+ break;
+ }
+
+ points[npoints].x = penX;
+ points[npoints].y = penY;
+
+ npoints++;
+ } else if (waveLength > width) { // Is last point too far?
+ int marginEnd = waveLength - width;
+ points[npoints-1].x -= marginEnd;
+ switch(getSlope(winx, npoints-1, ww)) {
+ case UNDERCURL_SLOPE_TOP_CAP:
+ points[npoints-1].y += marginEnd;
+ break;
+ case UNDERCURL_SLOPE_BOTTOM_CAP:
+ points[npoints-1].y -= marginEnd;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Draw the lines
+ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
+ CoordModeOrigin);
+
+ // Draw a second underline with an offset of 1 pixel
+ if ( ((win.ch / (widthThreshold/2)) % 2)) {
+ for (int i = 0; i < npoints; i++)
+ points[i].x++;
+
+ XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
+ npoints, CoordModeOrigin);
+ }
+
+ // Free resources
+ free(points);
+ }
+#endif
+ }
+
+ XFreeGC(xw.dpy, ugc);
}
if (base.mode & ATTR_STRUCK) {
@@ -1535,7 +1970,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
/*
* Select the right color for the right mode.
*/
- g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE;
+ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW;
if (IS_SET(MODE_REVERSE)) {
g.mode |= ATTR_REVERSE;
@@ -1632,10 +2067,30 @@ xseticontitle(char *p)
}
void
-xsettitle(char *p)
+xfreetitlestack(void)
{
- XTextProperty prop;
- DEFAULT(p, opt_title);
+ for (int i = 0; i < LEN(titlestack); i++) {
+ free(titlestack[i]);
+ titlestack[i] = NULL;
+ }
+}
+
+void
+xsettitle(char *p, int pop)
+{
+ XTextProperty prop;
+
+ free(titlestack[tstki]);
+ if (pop) {
+ titlestack[tstki] = NULL;
+ tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE;
+ p = titlestack[tstki] ? titlestack[tstki] : opt_title;
+ } else if (p) {
+ titlestack[tstki] = xstrdup(p);
+ } else {
+ titlestack[tstki] = NULL;
+ p = opt_title;
+ }
if (p[0] == '\0')
p = opt_title;
@@ -1648,6 +2103,16 @@ xsettitle(char *p)
XFree(prop.value);
}
+void
+xpushtitle(void)
+{
+ int tstkin = (tstki + 1) % TITLESTACKSIZE;
+
+ free(titlestack[tstkin]);
+ titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL;
+ tstki = tstkin;
+}
+
int
xstartdraw(void)
{
@@ -2060,6 +2525,12 @@ main(int argc, char *argv[])
case 'g':
xw.gm = XParseGeometry(EARGF(usage()),
&xw.l, &xw.t, &cols, &rows);
+ geometry = CellGeometry;
+ break;
+ case 'G':
+ xw.gm = XParseGeometry(EARGF(usage()),
+ &xw.l, &xw.t, &width, &height);
+ geometry = PixelGeometry;
break;
case 'i':
xw.isfixed = 1;
@@ -2096,10 +2567,19 @@ run:
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
+ switch (geometry) {
+ case CellGeometry:
+ xinit(cols, rows);
+ break;
+ case PixelGeometry:
+ xinit(width, height);
+ cols = (win.w - 2 * borderpx) / win.cw;
+ rows = (win.h - 2 * borderpx) / win.ch;
+ break;
+ }
cols = MAX(cols, 1);
rows = MAX(rows, 1);
tnew(cols, rows);
- xinit(cols, rows);
xsetenv();
selinit();
run();