aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile6
-rw-r--r--PATCHES8
-rw-r--r--boxdraw.c194
-rw-r--r--boxdraw_data.h214
-rw-r--r--config.def.h97
-rw-r--r--config.mk1
-rw-r--r--st.c1176
-rw-r--r--st.desktop12
-rw-r--r--st.h40
-rw-r--r--st.info5
-rw-r--r--win.h4
-rw-r--r--x.c504
13 files changed, 1889 insertions, 375 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..51447da
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.o
+config.h
+st
diff --git a/Makefile b/Makefile
index 15db421..5132a07 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
include config.mk
-SRC = st.c x.c
+SRC = st.c x.c boxdraw.c
OBJ = $(SRC:.c=.o)
all: st
@@ -17,6 +17,7 @@ config.h:
st.o: config.h st.h win.h
x.o: arg.h config.h st.h win.h
+boxdraw.o: config.h st.h boxdraw_data.h
$(OBJ): config.h config.mk
@@ -43,9 +44,12 @@ install: st
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1
tic -sx st.info
@echo Please see the README file regarding the terminfo entry of st.
+ mkdir -p $(DESTDIR)$(APPPREFIX)
+ cp -f st.desktop $(DESTDIR)$(APPPREFIX)
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/st
+ rm -f $(DESTDIR)$(APPPREFIX)/st.desktop
rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
.PHONY: all clean dist install uninstall
diff --git a/PATCHES b/PATCHES
new file mode 100644
index 0000000..a2aaf4d
--- /dev/null
+++ b/PATCHES
@@ -0,0 +1,8 @@
+st-anygeometry-0.8.1.diff
+st-boxdraw_v2-0.8.5.diff
+st-clipboard-20180309-c5ba9c0.diff
+st-csi_22_23-0.8.5.diff
+st-desktopentry-0.8.5.diff
+st-scrollback-0.9.2.diff
+st-scrollback-reflow-0.9.2.diff
+st-undercurl-0.9-20240103.diff
diff --git a/boxdraw.c b/boxdraw.c
new file mode 100644
index 0000000..28a92d0
--- /dev/null
+++ b/boxdraw.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih
+ * MIT/X Consortium License
+ */
+
+#include <X11/Xft/Xft.h>
+#include "st.h"
+#include "boxdraw_data.h"
+
+/* Rounded non-negative integers division of n / d */
+#define DIV(n, d) (((n) + (d) / 2) / (d))
+
+static Display *xdpy;
+static Colormap xcmap;
+static XftDraw *xd;
+static Visual *xvis;
+
+static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort);
+static void drawboxlines(int, int, int, int, XftColor *, ushort);
+
+/* public API */
+
+void
+boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis)
+{
+ xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis;
+}
+
+int
+isboxdraw(Rune u)
+{
+ Rune block = u & ~0xff;
+ return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) ||
+ (boxdraw_braille && block == 0x2800);
+}
+
+/* the "index" is actually the entire shape data encoded as ushort */
+ushort
+boxdrawindex(const Glyph *g)
+{
+ if (boxdraw_braille && (g->u & ~0xff) == 0x2800)
+ return BRL | (uint8_t)g->u;
+ if (boxdraw_bold && (g->mode & ATTR_BOLD))
+ return BDB | boxdata[(uint8_t)g->u];
+ return boxdata[(uint8_t)g->u];
+}
+
+void
+drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg,
+ const XftGlyphFontSpec *specs, int len)
+{
+ for ( ; len-- > 0; x += cw, specs++)
+ drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph);
+}
+
+/* implementation */
+
+void
+drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd)
+{
+ ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */
+ if (bd & (BDL | BDA)) {
+ /* lines (light/double/heavy/arcs) */
+ drawboxlines(x, y, w, h, fg, bd);
+
+ } else if (cat == BBD) {
+ /* lower (8-X)/8 block */
+ int d = DIV((uint8_t)bd * h, 8);
+ XftDrawRect(xd, fg, x, y + d, w, h - d);
+
+ } else if (cat == BBU) {
+ /* upper X/8 block */
+ XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8));
+
+ } else if (cat == BBL) {
+ /* left X/8 block */
+ XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h);
+
+ } else if (cat == BBR) {
+ /* right (8-X)/8 block */
+ int d = DIV((uint8_t)bd * w, 8);
+ XftDrawRect(xd, fg, x + d, y, w - d, h);
+
+ } else if (cat == BBQ) {
+ /* Quadrants */
+ int w2 = DIV(w, 2), h2 = DIV(h, 2);
+ if (bd & TL)
+ XftDrawRect(xd, fg, x, y, w2, h2);
+ if (bd & TR)
+ XftDrawRect(xd, fg, x + w2, y, w - w2, h2);
+ if (bd & BL)
+ XftDrawRect(xd, fg, x, y + h2, w2, h - h2);
+ if (bd & BR)
+ XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2);
+
+ } else if (bd & BBS) {
+ /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */
+ int d = (uint8_t)bd;
+ XftColor xfc;
+ XRenderColor xrc = { .alpha = 0xffff };
+
+ xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4);
+ xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4);
+ xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4);
+
+ XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc);
+ XftDrawRect(xd, &xfc, x, y, w, h);
+ XftColorFree(xdpy, xvis, xcmap, &xfc);
+
+ } else if (cat == BRL) {
+ /* braille, each data bit corresponds to one dot at 2x4 grid */
+ int w1 = DIV(w, 2);
+ int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4);
+
+ if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1);
+ if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1);
+ if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2);
+ if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1);
+ if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1);
+ if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2);
+ if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3);
+ if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3);
+
+ }
+}
+
+void
+drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd)
+{
+ /* s: stem thickness. width/8 roughly matches underscore thickness. */
+ /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */
+ /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */
+ int mwh = MIN(w, h);
+ int base_s = MAX(1, DIV(mwh, 8));
+ int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */
+ int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s;
+ int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2);
+ /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */
+ /* The base length (per direction till edge) includes this square. */
+
+ int light = bd & (LL | LU | LR | LD);
+ int double_ = bd & (DL | DU | DR | DD);
+
+ if (light) {
+ /* d: additional (negative) length to not-draw the center */
+ /* texel - at arcs and avoid drawing inside (some) doubles */
+ int arc = bd & BDA;
+ int multi_light = light & (light - 1);
+ int multi_double = double_ & (double_ - 1);
+ /* light crosses double only at DH+LV, DV+LH (ref. shapes) */
+ int d = arc || (multi_double && !multi_light) ? -s : 0;
+
+ if (bd & LL)
+ XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s);
+ if (bd & LU)
+ XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d);
+ if (bd & LR)
+ XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s);
+ if (bd & LD)
+ XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d);
+ }
+
+ /* double lines - also align with light to form heavy when combined */
+ if (double_) {
+ /*
+ * going clockwise, for each double-ray: p is additional length
+ * to the single-ray nearer to the previous direction, and n to
+ * the next. p and n adjust from the base length to lengths
+ * which consider other doubles - shorter to avoid intersections
+ * (p, n), or longer to draw the far-corner texel (n).
+ */
+ int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD;
+ if (dl) {
+ int p = dd ? -s : 0, n = du ? -s : dd ? s : 0;
+ XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s);
+ XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s);
+ }
+ if (du) {
+ int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0;
+ XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p);
+ XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n);
+ }
+ if (dr) {
+ int p = du ? -s : 0, n = dd ? -s : du ? s : 0;
+ XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s);
+ XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s);
+ }
+ if (dd) {
+ int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0;
+ XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p);
+ XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n);
+ }
+ }
+}
diff --git a/boxdraw_data.h b/boxdraw_data.h
new file mode 100644
index 0000000..7890500
--- /dev/null
+++ b/boxdraw_data.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih
+ * MIT/X Consortium License
+ */
+
+/*
+ * U+25XX codepoints data
+ *
+ * References:
+ * http://www.unicode.org/charts/PDF/U2500.pdf
+ * http://www.unicode.org/charts/PDF/U2580.pdf
+ *
+ * Test page:
+ * https://github.com/GNOME/vte/blob/master/doc/boxes.txt
+ */
+
+/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */
+/* Categories (mutually exclusive except BDB): */
+/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */
+#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */
+#define BDA (1<<9) /* Box Draw Arc (light) */
+
+#define BBD (1<<10) /* Box Block Down (lower) X/8 */
+#define BBL (2<<10) /* Box Block Left X/8 */
+#define BBU (3<<10) /* Box Block Upper X/8 */
+#define BBR (4<<10) /* Box Block Right X/8 */
+#define BBQ (5<<10) /* Box Block Quadrants */
+#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */
+
+#define BBS (1<<14) /* Box Block Shades */
+#define BDB (1<<15) /* Box Draw is Bold */
+
+/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */
+/* Heavy is light+double (literally drawing light+double align to form heavy) */
+#define LL (1<<0)
+#define LU (1<<1)
+#define LR (1<<2)
+#define LD (1<<3)
+#define LH (LL+LR)
+#define LV (LU+LD)
+
+#define DL (1<<4)
+#define DU (1<<5)
+#define DR (1<<6)
+#define DD (1<<7)
+#define DH (DL+DR)
+#define DV (DU+DD)
+
+#define HL (LL+DL)
+#define HU (LU+DU)
+#define HR (LR+DR)
+#define HD (LD+DD)
+#define HH (HL+HR)
+#define HV (HU+HD)
+
+/* (BBQ) Quadrants Top/Bottom x Left/Right */
+#define TL (1<<0)
+#define TR (1<<1)
+#define BL (1<<2)
+#define BR (1<<3)
+
+/* Data for U+2500 - U+259F except dashes/diagonals */
+static const unsigned short boxdata[256] = {
+ /* light lines */
+ [0x00] = BDL + LH, /* light horizontal */
+ [0x02] = BDL + LV, /* light vertical */
+ [0x0c] = BDL + LD + LR, /* light down and right */
+ [0x10] = BDL + LD + LL, /* light down and left */
+ [0x14] = BDL + LU + LR, /* light up and right */
+ [0x18] = BDL + LU + LL, /* light up and left */
+ [0x1c] = BDL + LV + LR, /* light vertical and right */
+ [0x24] = BDL + LV + LL, /* light vertical and left */
+ [0x2c] = BDL + LH + LD, /* light horizontal and down */
+ [0x34] = BDL + LH + LU, /* light horizontal and up */
+ [0x3c] = BDL + LV + LH, /* light vertical and horizontal */
+ [0x74] = BDL + LL, /* light left */
+ [0x75] = BDL + LU, /* light up */
+ [0x76] = BDL + LR, /* light right */
+ [0x77] = BDL + LD, /* light down */
+
+ /* heavy [+light] lines */
+ [0x01] = BDL + HH,
+ [0x03] = BDL + HV,
+ [0x0d] = BDL + HR + LD,
+ [0x0e] = BDL + HD + LR,
+ [0x0f] = BDL + HD + HR,
+ [0x11] = BDL + HL + LD,
+ [0x12] = BDL + HD + LL,
+ [0x13] = BDL + HD + HL,
+ [0x15] = BDL + HR + LU,
+ [0x16] = BDL + HU + LR,
+ [0x17] = BDL + HU + HR,
+ [0x19] = BDL + HL + LU,
+ [0x1a] = BDL + HU + LL,
+ [0x1b] = BDL + HU + HL,
+ [0x1d] = BDL + HR + LV,
+ [0x1e] = BDL + HU + LD + LR,
+ [0x1f] = BDL + HD + LR + LU,
+ [0x20] = BDL + HV + LR,
+ [0x21] = BDL + HU + HR + LD,
+ [0x22] = BDL + HD + HR + LU,
+ [0x23] = BDL + HV + HR,
+ [0x25] = BDL + HL + LV,
+ [0x26] = BDL + HU + LD + LL,
+ [0x27] = BDL + HD + LU + LL,
+ [0x28] = BDL + HV + LL,
+ [0x29] = BDL + HU + HL + LD,
+ [0x2a] = BDL + HD + HL + LU,
+ [0x2b] = BDL + HV + HL,
+ [0x2d] = BDL + HL + LD + LR,
+ [0x2e] = BDL + HR + LL + LD,
+ [0x2f] = BDL + HH + LD,
+ [0x30] = BDL + HD + LH,
+ [0x31] = BDL + HD + HL + LR,
+ [0x32] = BDL + HR + HD + LL,
+ [0x33] = BDL + HH + HD,
+ [0x35] = BDL + HL + LU + LR,
+ [0x36] = BDL + HR + LU + LL,
+ [0x37] = BDL + HH + LU,
+ [0x38] = BDL + HU + LH,
+ [0x39] = BDL + HU + HL + LR,
+ [0x3a] = BDL + HU + HR + LL,
+ [0x3b] = BDL + HH + HU,
+ [0x3d] = BDL + HL + LV + LR,
+ [0x3e] = BDL + HR + LV + LL,
+ [0x3f] = BDL + HH + LV,
+ [0x40] = BDL + HU + LH + LD,
+ [0x41] = BDL + HD + LH + LU,
+ [0x42] = BDL + HV + LH,
+ [0x43] = BDL + HU + HL + LD + LR,
+ [0x44] = BDL + HU + HR + LD + LL,
+ [0x45] = BDL + HD + HL + LU + LR,
+ [0x46] = BDL + HD + HR + LU + LL,
+ [0x47] = BDL + HH + HU + LD,
+ [0x48] = BDL + HH + HD + LU,
+ [0x49] = BDL + HV + HL + LR,
+ [0x4a] = BDL + HV + HR + LL,
+ [0x4b] = BDL + HV + HH,
+ [0x78] = BDL + HL,
+ [0x79] = BDL + HU,
+ [0x7a] = BDL + HR,
+ [0x7b] = BDL + HD,
+ [0x7c] = BDL + HR + LL,
+ [0x7d] = BDL + HD + LU,
+ [0x7e] = BDL + HL + LR,
+ [0x7f] = BDL + HU + LD,
+
+ /* double [+light] lines */
+ [0x50] = BDL + DH,
+ [0x51] = BDL + DV,
+ [0x52] = BDL + DR + LD,
+ [0x53] = BDL + DD + LR,
+ [0x54] = BDL + DR + DD,
+ [0x55] = BDL + DL + LD,
+ [0x56] = BDL + DD + LL,
+ [0x57] = BDL + DL + DD,
+ [0x58] = BDL + DR + LU,
+ [0x59] = BDL + DU + LR,
+ [0x5a] = BDL + DU + DR,
+ [0x5b] = BDL + DL + LU,
+ [0x5c] = BDL + DU + LL,
+ [0x5d] = BDL + DL + DU,
+ [0x5e] = BDL + DR + LV,
+ [0x5f] = BDL + DV + LR,
+ [0x60] = BDL + DV + DR,
+ [0x61] = BDL + DL + LV,
+ [0x62] = BDL + DV + LL,
+ [0x63] = BDL + DV + DL,
+ [0x64] = BDL + DH + LD,
+ [0x65] = BDL + DD + LH,
+ [0x66] = BDL + DD + DH,
+ [0x67] = BDL + DH + LU,
+ [0x68] = BDL + DU + LH,
+ [0x69] = BDL + DH + DU,
+ [0x6a] = BDL + DH + LV,
+ [0x6b] = BDL + DV + LH,
+ [0x6c] = BDL + DH + DV,
+
+ /* (light) arcs */
+ [0x6d] = BDA + LD + LR,
+ [0x6e] = BDA + LD + LL,
+ [0x6f] = BDA + LU + LL,
+ [0x70] = BDA + LU + LR,
+
+ /* Lower (Down) X/8 block (data is 8 - X) */
+ [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4,
+ [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0,
+
+ /* Left X/8 block (data is X) */
+ [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4,
+ [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1,
+
+ /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */
+ [0x80] = BBU + 4, [0x94] = BBU + 1,
+ [0x90] = BBR + 4, [0x95] = BBR + 7,
+
+ /* Quadrants */
+ [0x96] = BBQ + BL,
+ [0x97] = BBQ + BR,
+ [0x98] = BBQ + TL,
+ [0x99] = BBQ + TL + BL + BR,
+ [0x9a] = BBQ + TL + BR,
+ [0x9b] = BBQ + TL + TR + BL,
+ [0x9c] = BBQ + TL + TR + BR,
+ [0x9d] = BBQ + TR,
+ [0x9e] = BBQ + BL + TR,
+ [0x9f] = BBQ + BL + TR + BR,
+
+ /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */
+ [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3,
+
+ /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */
+ /* U+2571 - U+2573: unsupported (diagonals) */
+};
diff --git a/config.def.h b/config.def.h
index 2cd740a..c535ee0 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,7 +5,7 @@
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
-static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
+static char *font = "JetBrainsMonoNL Nerd Font Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
/*
@@ -68,6 +68,18 @@ static unsigned int blinktimeout = 800;
static unsigned int cursorthickness = 2;
/*
+ * 1: render most of the lines/blocks characters without using the font for
+ * perfect alignment between cells (U2500 - U259F except dashes/diagonals).
+ * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored.
+ * 0: disable (render all U25XX glyphs normally from the font).
+ */
+const int boxdraw = 1;
+const int boxdraw_bold = 1;
+
+/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */
+const int boxdraw_braille = 1;
+
+/*
* bell volume. It must be a value between -100 and 100. Use 0 for disabling
* it
*/
@@ -91,37 +103,35 @@ char *termname = "st-256color";
*
* stty tabs
*/
-unsigned int tabspaces = 8;
+unsigned int tabspaces = 8; // TODO change to 4
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
- "black",
- "red3",
- "green3",
- "yellow3",
- "blue2",
- "magenta3",
- "cyan3",
- "gray90",
+ "#101010",
+ "#ed1207",
+ "#2bb500",
+ "#fc9700",
+ "#1578c1",
+ "#b14ff7",
+ "#00c4a3",
+ "white",
/* 8 bright colors */
"gray50",
- "red",
- "green",
- "yellow",
- "#5c5cff",
- "magenta",
- "cyan",
+ "#ed1207",
+ "#2bb500",
+ "#fc9700",
+ "#1578c1",
+ "#b14ff7",
+ "#00c4a3",
"white",
[255] = 0,
- /* more colors can be added after 255 to use with DefaultXX */
+ /* more colors can be added after 255 to use with defaultxx */
"#cccccc",
"#555555",
- "gray90", /* default foreground colour */
- "black", /* default background colour */
};
@@ -129,8 +139,8 @@ static const char *colorname[] = {
* Default colors (colorname index)
* foreground, background, cursor, reverse cursor
*/
-unsigned int defaultfg = 258;
-unsigned int defaultbg = 259;
+unsigned int defaultfg = 7;
+unsigned int defaultbg = 0;
unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
@@ -144,6 +154,12 @@ static unsigned int defaultrcs = 257;
static unsigned int cursorshape = 2;
/*
+ * Whether to use pixel geometry or cell geometry
+ */
+
+static Geometry geometry = CellGeometry;
+
+/*
* Default columns and rows numbers
*/
@@ -151,6 +167,13 @@ static unsigned int cols = 80;
static unsigned int rows = 24;
/*
+ * Default width and height (including borders!)
+ */
+
+static unsigned int width = 564;
+static unsigned int height = 364;
+
+/*
* Default colour and shape of the mouse cursor
*/
static unsigned int mouseshape = XC_xterm;
@@ -176,6 +199,10 @@ static uint forcemousemod = ShiftMask;
*/
static MouseShortcut mshortcuts[] = {
/* mask button function argument release */
+ { ControlMask, Button4, zoom, {.f = +1}, 0 },
+ { ControlMask, Button5, zoom, {.f = -1}, 0 },
+ { XK_ANY_MOD, Button4, kscrollup, {.i = 3}, 0 },
+ { XK_ANY_MOD, Button5, kscrolldown, {.i = 3}, 0 },
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
@@ -195,12 +222,16 @@ static Shortcut shortcuts[] = {
{ XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
{ TERMMOD, XK_Prior, zoom, {.f = +1} },
{ TERMMOD, XK_Next, zoom, {.f = -1} },
+ { ControlMask, XK_equal, zoom, {.f = +1} },
+ { ControlMask, XK_minus, zoom, {.f = -1} },
{ TERMMOD, XK_Home, zoomreset, {.f = 0} },
{ TERMMOD, XK_C, clipcopy, {.i = 0} },
{ TERMMOD, XK_V, clippaste, {.i = 0} },
{ TERMMOD, XK_Y, selpaste, {.i = 0} },
{ ShiftMask, XK_Insert, selpaste, {.i = 0} },
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
+ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
+ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
};
/*
@@ -472,3 +503,27 @@ static char ascii_printable[] =
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~";
+
+/**
+ * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
+ *
+ * Curly: Dunno how to draw it *shrug*
+ * _ _ _ _
+ * ( ) ( ) ( ) ( )
+ * (_) (_) (_) (_)
+ *
+ * Spiky:
+ * /\ /\ /\ /\
+ * \/ \/ \/
+ *
+ * Capped:
+ * _ _ _
+ * / \ / \ / \
+ * \_/ \_/
+ */
+// Available styles
+#define UNDERCURL_CURLY 0
+#define UNDERCURL_SPIKY 1
+#define UNDERCURL_CAPPED 2
+// Active style
+#define UNDERCURL_STYLE UNDERCURL_SPIKY
diff --git a/config.mk b/config.mk
index fdc29a7..a6c3829 100644
--- a/config.mk
+++ b/config.mk
@@ -5,6 +5,7 @@ VERSION = 0.9.2
# paths
PREFIX = /usr/local
+APPPREFIX = $(PREFIX)/share/applications
MANPREFIX = $(PREFIX)/share/man
X11INC = /usr/X11R6/include
diff --git a/st.c b/st.c
index 2e3800e..f1c7b76 100644
--- a/st.c
+++ b/st.c
@@ -33,8 +33,11 @@
#define UTF_SIZ 4
#define ESC_BUF_SIZ (128*UTF_SIZ)
#define ESC_ARG_SIZ 16
+#define CAR_PER_ARG 4
#define STR_BUF_SIZ ESC_BUF_SIZ
#define STR_ARG_SIZ ESC_ARG_SIZ
+#define HISTSIZE 2000
+#define RESIZEBUFFER 1000
/* macros */
#define IS_SET(flag) ((term.mode & (flag)) != 0)
@@ -42,6 +45,21 @@
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
+#define TLINE(y) ( \
+ (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \
+ : term.line[(y) - term.scr] \
+)
+
+#define TLINEABS(y) ( \
+ (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
+)
+
+#define UPDATEWRAPNEXT(alt, col) do { \
+ if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \
+ term.c.x += term.wrapcwidth[alt]; \
+ term.c.state &= ~CURSOR_WRAPNEXT; \
+ } \
+} while (0);
enum term_mode {
MODE_WRAP = 1 << 0,
@@ -53,6 +71,12 @@ enum term_mode {
MODE_UTF8 = 1 << 6,
};
+enum scroll_mode {
+ SCROLL_RESIZE = -1,
+ SCROLL_NOSAVEHIST = 0,
+ SCROLL_SAVEHIST = 1
+};
+
enum cursor_movement {
CURSOR_SAVE,
CURSOR_LOAD
@@ -114,7 +138,11 @@ typedef struct {
int row; /* nb row */
int col; /* nb col */
Line *line; /* screen */
- Line *alt; /* alternate screen */
+ Line hist[HISTSIZE]; /* history buffer */
+ int histi; /* history index */
+ int histf; /* nb history available */
+ int scr; /* scroll back */
+ int wrapcwidth[2]; /* used in updating WRAPNEXT when resizing */
int *dirty; /* dirtyness of lines */
TCursor c; /* cursor */
int ocx; /* old cursor col */
@@ -139,6 +167,7 @@ typedef struct {
int arg[ESC_ARG_SIZ];
int narg; /* nb of args */
char mode[2];
+ int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
} CSIEscape;
/* STR Escape sequence structs */
@@ -159,6 +188,7 @@ static void ttywriteraw(const char *, size_t);
static void csidump(void);
static void csihandle(void);
+static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
static void csiparse(void);
static void csireset(void);
static void osc_color_response(int, int, int);
@@ -172,26 +202,37 @@ static void tprinter(char *, size_t);
static void tdumpsel(void);
static void tdumpline(int);
static void tdump(void);
-static void tclearregion(int, int, int, int);
+static void tclearregion(int, int, int, int, int);
static void tcursor(int);
+static void tclearglyph(Glyph *, int);
+static void tresetcursor(void);
static void tdeletechar(int);
static void tdeleteline(int);
static void tinsertblank(int);
static void tinsertblankline(int);
-static int tlinelen(int);
+static int tlinelen(Line len);
+static int tiswrapped(Line line);
+static char *tgetglyphs(char *, const Glyph *, const Glyph *);
+static size_t tgetline(char *, const Glyph *);
static void tmoveto(int, int);
static void tmoveato(int, int);
static void tnewline(int);
static void tputtab(int);
static void tputc(Rune);
static void treset(void);
-static void tscrollup(int, int);
+static void tscrollup(int, int, int, int);
static void tscrolldown(int, int);
+static void treflow(int, int);
+static void rscrolldown(int);
+static void tresizedef(int, int);
+static void tresizealt(int, int);
static void tsetattr(const int *, int);
static void tsetchar(Rune, const Glyph *, int, int);
static void tsetdirt(int, int);
static void tsetscroll(int, int);
static void tswapscreen(void);
+static void tloaddefscreen(int, int);
+static void tloadaltscreen(int, int);
static void tsetmode(int, int, const int *, int);
static int twrite(const char *, int, int);
static void tfulldirt(void);
@@ -205,7 +246,10 @@ static void tstrsequence(uchar);
static void drawregion(int, int, int, int);
static void selnormalize(void);
-static void selscroll(int, int);
+static void selscroll(int, int, int);
+static void selmove(int);
+static void selremove(void);
+static int regionselected(int, int, int, int);
static void selsnap(int *, int *, int);
static size_t utf8decode(const char *, Rune *, size_t);
@@ -405,17 +449,46 @@ selinit(void)
}
int
-tlinelen(int y)
+tlinelen(Line line)
{
- int i = term.col;
+ int i = term.col - 1;
- if (term.line[y][i - 1].mode & ATTR_WRAP)
- return i;
+ for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--);
+ return i + 1;
+}
- while (i > 0 && term.line[y][i - 1].u == ' ')
- --i;
+int
+tiswrapped(Line line)
+{
+ int len = tlinelen(line);
- return i;
+ return len > 0 && (line[len - 1].mode & ATTR_WRAP);
+}
+
+char *
+tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp)
+{
+ while (gp <= lgp)
+ if (gp->mode & ATTR_WDUMMY) {
+ gp++;
+ } else {
+ buf += utf8encode((gp++)->u, buf);
+ }
+ return buf;
+}
+
+size_t
+tgetline(char *buf, const Glyph *fgp)
+{
+ char *ptr;
+ const Glyph *lgp = &fgp[term.col - 1];
+
+ while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP)))
+ lgp--;
+ ptr = tgetglyphs(buf, fgp, lgp);
+ if (!(lgp->mode & ATTR_WRAP))
+ *(ptr++) = '\n';
+ return ptr - buf;
}
void
@@ -455,10 +528,11 @@ selextend(int col, int row, int type, int done)
sel.oe.x = col;
sel.oe.y = row;
- selnormalize();
sel.type = type;
+ selnormalize();
- if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
+ if (oldey != sel.oe.y || oldex != sel.oe.x ||
+ oldtype != sel.type || sel.mode == SEL_EMPTY)
tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
sel.mode = done ? SEL_IDLE : SEL_READY;
@@ -467,61 +541,69 @@ selextend(int col, int row, int type, int done)
void
selnormalize(void)
{
- int i;
+ int i;
- if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
- sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
- sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
- } else {
- sel.nb.x = MIN(sel.ob.x, sel.oe.x);
- sel.ne.x = MAX(sel.ob.x, sel.oe.x);
- }
- sel.nb.y = MIN(sel.ob.y, sel.oe.y);
- sel.ne.y = MAX(sel.ob.y, sel.oe.y);
+ if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
+ sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
+ sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
+ } else {
+ sel.nb.x = MIN(sel.ob.x, sel.oe.x);
+ sel.ne.x = MAX(sel.ob.x, sel.oe.x);
+ }
+ sel.nb.y = MIN(sel.ob.y, sel.oe.y);
+ sel.ne.y = MAX(sel.ob.y, sel.oe.y);
- selsnap(&sel.nb.x, &sel.nb.y, -1);
- selsnap(&sel.ne.x, &sel.ne.y, +1);
+ selsnap(&sel.nb.x, &sel.nb.y, -1);
+ selsnap(&sel.ne.x, &sel.ne.y, +1);
- /* expand selection over line breaks */
- if (sel.type == SEL_RECTANGULAR)
- return;
- i = tlinelen(sel.nb.y);
- if (i < sel.nb.x)
- sel.nb.x = i;
- if (tlinelen(sel.ne.y) <= sel.ne.x)
- sel.ne.x = term.col - 1;
+ /* expand selection over line breaks */
+ if (sel.type == SEL_RECTANGULAR)
+ return;
+
+ i = tlinelen(TLINE(sel.nb.y));
+ if (sel.nb.x > i)
+ sel.nb.x = i;
+ if (sel.ne.x >= tlinelen(TLINE(sel.ne.y)))
+ sel.ne.x = term.col - 1;
}
-int
-selected(int x, int y)
+ int
+regionselected(int x1, int y1, int x2, int y2)
{
- if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
- sel.alt != IS_SET(MODE_ALTSCREEN))
- return 0;
+ if (sel.ob.x == -1 || sel.mode == SEL_EMPTY ||
+ sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1)
+ return 0;
- if (sel.type == SEL_RECTANGULAR)
- return BETWEEN(y, sel.nb.y, sel.ne.y)
- && BETWEEN(x, sel.nb.x, sel.ne.x);
+ return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1
+ : (sel.nb.y != y2 || sel.nb.x <= x2) &&
+ (sel.ne.y != y1 || sel.ne.x >= x1);
+}
- return BETWEEN(y, sel.nb.y, sel.ne.y)
- && (y != sel.nb.y || x >= sel.nb.x)
- && (y != sel.ne.y || x <= sel.ne.x);
+ int
+selected(int x, int y)
+{
+ return regionselected(x, y, x, y);
}
+
void
selsnap(int *x, int *y, int direction)
{
int newx, newy, xt, yt;
+ int rtop = 0, rbot = term.row - 1;
int delim, prevdelim;
const Glyph *gp, *prevgp;
+ if (!IS_SET(MODE_ALTSCREEN))
+ rtop += -term.histf + term.scr, rbot += term.scr;
+
switch (sel.snap) {
case SNAP_WORD:
/*
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
- prevgp = &term.line[*y][*x];
+ prevgp = &TLINE(*y)[*x];
prevdelim = ISDELIM(prevgp->u);
for (;;) {
newx = *x + direction;
@@ -529,24 +611,24 @@ selsnap(int *x, int *y, int direction)
if (!BETWEEN(newx, 0, term.col - 1)) {
newy += direction;
newx = (newx + term.col) % term.col;
- if (!BETWEEN(newy, 0, term.row - 1))
+ if (!BETWEEN(newy, rtop, rbot))
break;
if (direction > 0)
yt = *y, xt = *x;
else
yt = newy, xt = newx;
- if (!(term.line[yt][xt].mode & ATTR_WRAP))
+ if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
break;
}
- if (newx >= tlinelen(newy))
+ if (newx >= tlinelen(TLINE(newy)))
break;
- gp = &term.line[newy][newx];
+ gp = &TLINE(newy)[newx];
delim = ISDELIM(gp->u);
- if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
- || (delim && gp->u != prevgp->u)))
+ if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim ||
+ (delim && !(gp->u == ' ' && prevgp->u == ' '))))
break;
*x = newx;
@@ -561,20 +643,16 @@ selsnap(int *x, int *y, int direction)
* has set ATTR_WRAP at its end. Then the whole next or
* previous line will be selected.
*/
- *x = (direction < 0) ? 0 : term.col - 1;
- if (direction < 0) {
- for (; *y > 0; *y += direction) {
- if (!(term.line[*y-1][term.col-1].mode
- & ATTR_WRAP)) {
- break;
- }
+ *x = (direction < 0) ? 0 : term.col - 1;
+ if (direction < 0) {
+ for (; *y > rtop; *y -= 1) {
+ if (!tiswrapped(TLINE(*y-1)))
+ break;
}
} else if (direction > 0) {
- for (; *y < term.row-1; *y += direction) {
- if (!(term.line[*y][term.col-1].mode
- & ATTR_WRAP)) {
+ for (; *y < rbot; *y += 1) {
+ if (!tiswrapped(TLINE(*y)))
break;
- }
}
}
break;
@@ -585,39 +663,34 @@ char *
getsel(void)
{
char *str, *ptr;
- int y, bufsize, lastx, linelen;
- const Glyph *gp, *last;
+ int y, lastx, linelen;
+ const Glyph *gp, *lgp;
- if (sel.ob.x == -1)
+ if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
return NULL;
- bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
- ptr = str = xmalloc(bufsize);
+ str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ);
+ ptr = str;
/* append every set & selected glyph to the selection */
for (y = sel.nb.y; y <= sel.ne.y; y++) {
- if ((linelen = tlinelen(y)) == 0) {
+ Line line = TLINE(y);
+
+ if ((linelen = tlinelen(line)) == 0) {
*ptr++ = '\n';
continue;
}
if (sel.type == SEL_RECTANGULAR) {
- gp = &term.line[y][sel.nb.x];
+ gp = &line[sel.nb.x];
lastx = sel.ne.x;
} else {
- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+ gp = &line[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
- last = &term.line[y][MIN(lastx, linelen-1)];
- while (last >= gp && last->u == ' ')
- --last;
-
- for ( ; gp <= last; ++gp) {
- if (gp->mode & ATTR_WDUMMY)
- continue;
+ lgp = &line[MIN(lastx, linelen-1)];
- ptr += utf8encode(gp->u, ptr);
- }
+ ptr = tgetglyphs(ptr, gp, lgp);
/*
* Copy and pasting of line endings is inconsistent
@@ -629,10 +702,10 @@ getsel(void)
* FIXME: Fix the computer world.
*/
if ((y < sel.ne.y || lastx >= linelen) &&
- (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
+ (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
*ptr++ = '\n';
}
- *ptr = 0;
+ *ptr = '\0';
return str;
}
@@ -641,9 +714,15 @@ selclear(void)
{
if (sel.ob.x == -1)
return;
+ selremove();
+ tsetdirt(sel.nb.y, sel.ne.y);
+}
+
+void
+selremove(void)
+{
sel.mode = SEL_IDLE;
sel.ob.x = -1;
- tsetdirt(sel.nb.y, sel.ne.y);
}
void
@@ -845,6 +924,8 @@ ttywrite(const char *s, size_t n, int may_echo)
{
const char *next;
+ kscrolldown(&((Arg){ .i = term.scr }));
+
if (may_echo && IS_SET(MODE_ECHO))
twrite(s, n, 1);
@@ -980,7 +1061,7 @@ tsetdirtattr(int attr)
for (i = 0; i < term.row-1; i++) {
for (j = 0; j < term.col-1; j++) {
if (term.line[i][j].mode & attr) {
- tsetdirt(i, i);
+ term.dirty[i] = 1;
break;
}
}
@@ -990,7 +1071,8 @@ tsetdirtattr(int attr)
void
tfulldirt(void)
{
- tsetdirt(0, term.row-1);
+ for (int i = 0; i < term.row; i++)
+ term.dirty[i] = 1;
}
void
@@ -1008,109 +1090,260 @@ tcursor(int mode)
}
void
+tresetcursor(void)
+{
+ term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg },
+ .x = 0, .y = 0, .state = CURSOR_DEFAULT };
+}
+
+void
treset(void)
{
uint i;
+ int x, y;
- term.c = (TCursor){{
- .mode = ATTR_NULL,
- .fg = defaultfg,
- .bg = defaultbg
- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
+ tresetcursor();
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
for (i = tabspaces; i < term.col; i += tabspaces)
term.tabs[i] = 1;
term.top = 0;
+ term.histf = 0;
+ term.scr = 0;
term.bot = term.row - 1;
term.mode = MODE_WRAP|MODE_UTF8;
memset(term.trantbl, CS_USA, sizeof(term.trantbl));
term.charset = 0;
+ selremove();
for (i = 0; i < 2; i++) {
- tmoveto(0, 0);
- tcursor(CURSOR_SAVE);
- tclearregion(0, 0, term.col-1, term.row-1);
- tswapscreen();
+ tcursor(CURSOR_SAVE); /* reset saved cursor */
+ for (y = 0; y < term.row; y++)
+ for (x = 0; x < term.col; x++)
+ tclearglyph(&term.line[y][x], 0);
+ tswapscreen();
}
+ tfulldirt();
}
void
tnew(int col, int row)
{
- term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
- tresize(col, row);
- treset();
+ int i, j;
+
+ for (i = 0; i < 2; i++) {
+ term.line = xmalloc(row * sizeof(Line));
+ for (j = 0; j < row; j++)
+ term.line[j] = xmalloc(col * sizeof(Glyph));
+ term.col = col, term.row = row;
+ tswapscreen();
+ }
+ term.dirty = xmalloc(row * sizeof(*term.dirty));
+ term.tabs = xmalloc(col * sizeof(*term.tabs));
+ for (i = 0; i < HISTSIZE; i++)
+ term.hist[i] = xmalloc(col * sizeof(Glyph));
+ treset();
}
+/* handle it with care */
void
tswapscreen(void)
{
- Line *tmp = term.line;
+ static Line *altline;
+ static int altcol, altrow;
+ Line *tmpline = term.line;
+ int tmpcol = term.col, tmprow = term.row;
- term.line = term.alt;
- term.alt = tmp;
+ term.line = altline;
+ term.col = altcol, term.row = altrow;
+ altline = tmpline;
+ altcol = tmpcol, altrow = tmprow;
term.mode ^= MODE_ALTSCREEN;
- tfulldirt();
}
void
-tscrolldown(int orig, int n)
+tloaddefscreen(int clear, int loadcursor)
{
- int i;
- Line temp;
+ int col, row, alt = IS_SET(MODE_ALTSCREEN);
- LIMIT(n, 0, term.bot-orig+1);
+ if (alt) {
+ if (clear)
+ tclearregion(0, 0, term.col-1, term.row-1, 1);
+ col = term.col, row = term.row;
+ tswapscreen();
+ }
+ if (loadcursor)
+ tcursor(CURSOR_LOAD);
+ if (alt)
+ tresizedef(col, row);
+}
- tsetdirt(orig, term.bot-n);
- tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+void
+tloadaltscreen(int clear, int savecursor)
+{
+ int col, row, def = !IS_SET(MODE_ALTSCREEN);
- for (i = term.bot; i >= orig+n; i--) {
- temp = term.line[i];
- term.line[i] = term.line[i-n];
- term.line[i-n] = temp;
+ if (savecursor)
+ tcursor(CURSOR_SAVE);
+ if (def) {
+ col = term.col, row = term.row;
+ tswapscreen();
+ term.scr = 0;
+ tresizealt(col, row);
}
+ if (clear)
+ tclearregion(0, 0, term.col-1, term.row-1, 1);
+}
- selscroll(orig, n);
+int
+tisaltscreen(void)
+{
+ return IS_SET(MODE_ALTSCREEN);
}
+
void
-tscrollup(int orig, int n)
+kscrolldown(const Arg* a)
{
- int i;
- Line temp;
+ int n = a->i;
- LIMIT(n, 0, term.bot-orig+1);
+ if (!term.scr || IS_SET(MODE_ALTSCREEN))
+ return;
- tclearregion(0, orig, term.col-1, orig+n-1);
- tsetdirt(orig+n, term.bot);
+ if (n < 0)
+ n = MAX(term.row / -n, 1);
- for (i = orig; i <= term.bot-n; i++) {
- temp = term.line[i];
- term.line[i] = term.line[i+n];
- term.line[i+n] = temp;
- }
+ if (n <= term.scr) {
+ term.scr -= n;
+ } else {
+ n = term.scr;
+ term.scr = 0;
+ }
+ if (sel.ob.x != -1 && !sel.alt)
+ selmove(-n); /* negate change in term.scr */
+ tfulldirt();
+}
+
+
+
+void
+kscrollup(const Arg* a)
+{
+ int n = a->i;
+
+ if (!term.histf || IS_SET(MODE_ALTSCREEN))
+ return;
+
+ if (n < 0)
+ n = MAX(term.row / -n, 1);
+
+ if (term.scr + n <= term.histf) {
+ term.scr += n;
+ } else {
+ n = term.histf - term.scr;
+ term.scr = term.histf;
+ }
+
+ if (sel.ob.x != -1 && !sel.alt)
+ selmove(n); /* negate change in term.scr */
+ tfulldirt();
- selscroll(orig, -n);
}
void
-selscroll(int orig, int n)
+tscrolldown(int top, int n)
{
- if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
- return;
+ int i, bot = term.bot;
+ Line temp;
- if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
- selclear();
- } else if (BETWEEN(sel.nb.y, orig, term.bot)) {
- sel.ob.y += n;
- sel.oe.y += n;
- if (sel.ob.y < term.top || sel.ob.y > term.bot ||
- sel.oe.y < term.top || sel.oe.y > term.bot) {
- selclear();
- } else {
- selnormalize();
- }
+ if (n <= 0)
+ return;
+ n = MIN(n, bot-top+1);
+
+ tsetdirt(top, bot-n);
+ tclearregion(0, bot-n+1, term.col-1, bot, 1);
+
+ for (i = bot; i >= top+n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
+ }
+
+ if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN))
+ selscroll(top, bot, n);
+}
+
+void
+tscrollup(int top, int bot, int n, int mode)
+{
+ int i, j, s;
+ int alt = IS_SET(MODE_ALTSCREEN);
+ int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST;
+ Line temp;
+
+ if (n <= 0)
+ return;
+ n = MIN(n, bot-top+1);
+
+ if (savehist) {
+ for (i = 0; i < n; i++) {
+ term.histi = (term.histi + 1) % HISTSIZE;
+ temp = term.hist[term.histi];
+ for (j = 0; j < term.col; j++)
+ tclearglyph(&temp[j], 1);
+ term.hist[term.histi] = term.line[i];
+ term.line[i] = temp;
+ }
+ term.histf = MIN(term.histf + n, HISTSIZE);
+ s = n;
+ if (term.scr) {
+ j = term.scr;
+ term.scr = MIN(j + n, HISTSIZE);
+ s = j + n - term.scr;
+ }
+ if (mode != SCROLL_RESIZE)
+ tfulldirt();
+ } else {
+ tclearregion(0, top, term.col-1, top+n-1, 1);
+ tsetdirt(top+n, bot);
+ }
+
+ for (i = top; i <= bot-n; i++) {
+ temp = term.line[i];
+ term.line[i] = term.line[i+n];
+ term.line[i+n] = temp;
+ }
+
+ if (sel.ob.x != -1 && sel.alt == alt) {
+ if (!savehist) {
+ selscroll(top, bot, -n);
+ } else if (s > 0) {
+ selmove(-s);
+ if (-term.scr + sel.nb.y < -term.histf)
+ selremove();
+ }
+ }
+}
+
+void
+selmove(int n)
+ {
+ sel.ob.y += n, sel.nb.y += n;
+ sel.oe.y += n, sel.ne.y += n;
+}
+
+void
+selscroll(int top, int bot, int n)
+{
+ /* turn absolute coordinates into relative */
+ top += term.scr, bot += term.scr;
+
+ if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) {
+ selclear();
+ } else if (BETWEEN(sel.nb.y, top, bot)) {
+ selmove(n);
+ if (sel.nb.y < top || sel.ne.y > bot)
+ selclear();
}
}
@@ -1120,7 +1353,7 @@ tnewline(int first_col)
int y = term.c.y;
if (y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
} else {
y++;
}
@@ -1128,6 +1361,29 @@ tnewline(int first_col)
}
void
+readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
+{
+ int i = 0;
+ for (; i < CAR_PER_ARG; i++)
+ params[cursor][i] = -1;
+
+ if (**p != ':')
+ return;
+
+ char *np = NULL;
+ i = 0;
+
+ while (**p == ':' && i < CAR_PER_ARG) {
+ while (**p == ':')
+ (*p)++;
+ params[cursor][i] = strtol(*p, &np, 10);
+ *p = np;
+ i++;
+ }
+}
+
+
+void
csiparse(void)
{
char *p = csiescseq.buf, *np;
@@ -1152,6 +1408,7 @@ csiparse(void)
p = np;
if (sep == ';' && *p == ':')
sep = ':'; /* allow override to colon once */
+ readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); // TJK here dont know if to put before or after above
if (*p != sep || csiescseq.narg == ESC_ARG_SIZ)
break;
p++;
@@ -1187,101 +1444,115 @@ tmoveto(int x, int y)
void
tsetchar(Rune u, const Glyph *attr, int x, int y)
{
- static const char *vt100_0[62] = { /* 0x41 - 0x7e */
- "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
- 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
- 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
- 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
- "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
- "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
- "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
- "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
- };
+ static const char *vt100_0[62] = { /* 0x41 - 0x7e */
+ "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
+ 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
+ "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
+ "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
+ "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
+ "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
+ };
- /*
- * The table is proudly stolen from rxvt.
- */
- if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
- BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
- utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
-
- if (term.line[y][x].mode & ATTR_WIDE) {
- if (x+1 < term.col) {
- term.line[y][x+1].u = ' ';
- term.line[y][x+1].mode &= ~ATTR_WDUMMY;
- }
- } else if (term.line[y][x].mode & ATTR_WDUMMY) {
- term.line[y][x-1].u = ' ';
- term.line[y][x-1].mode &= ~ATTR_WIDE;
- }
+ /*
+ * The table is proudly stolen from rxvt.
+ */
+ if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
+ BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
+ utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
+
+ if (term.line[y][x].mode & ATTR_WIDE) {
+ if (x+1 < term.col) {
+ term.line[y][x+1].u = ' ';
+ term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+ }
+ } else if (term.line[y][x].mode & ATTR_WDUMMY) {
+ term.line[y][x-1].u = ' ';
+ term.line[y][x-1].mode &= ~ATTR_WIDE;
+ }
- term.dirty[y] = 1;
- term.line[y][x] = *attr;
- term.line[y][x].u = u;
+ term.dirty[y] = 1;
+ term.line[y][x] = *attr;
+ term.line[y][x].u = u;
+ term.line[y][x].mode |= ATTR_SET;
+
+ if (isboxdraw(u))
+ term.line[y][x].mode |= ATTR_BOXDRAW;
}
+
+
void
-tclearregion(int x1, int y1, int x2, int y2)
+tclearglyph(Glyph *gp, int usecurattr)
{
- int x, y, temp;
- Glyph *gp;
+ if (usecurattr) {
+ gp->fg = term.c.attr.fg;
+ gp->bg = term.c.attr.bg;
+ } else {
+ gp->fg = defaultfg;
+ gp->bg = defaultbg;
+ }
+ gp->mode = ATTR_NULL;
+ gp->u = ' ';
+}
- if (x1 > x2)
- temp = x1, x1 = x2, x2 = temp;
- if (y1 > y2)
- temp = y1, y1 = y2, y2 = temp;
- LIMIT(x1, 0, term.col-1);
- LIMIT(x2, 0, term.col-1);
- LIMIT(y1, 0, term.row-1);
- LIMIT(y2, 0, term.row-1);
- for (y = y1; y <= y2; y++) {
+void
+tclearregion(int x1, int y1, int x2, int y2, int usecurattr)
+{
+ int x, y;
+ /* regionselected() takes relative coordinates */
+ if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr))
+ selremove();
+
+ for (y = y1; y <= y2; y++) {
term.dirty[y] = 1;
- for (x = x1; x <= x2; x++) {
- gp = &term.line[y][x];
- if (selected(x, y))
- selclear();
- gp->fg = term.c.attr.fg;
- gp->bg = term.c.attr.bg;
- gp->mode = 0;
- gp->u = ' ';
- }
+ for (x = x1; x <= x2; x++)
+ tclearglyph(&term.line[y][x], usecurattr);
}
}
void
tdeletechar(int n)
{
- int dst, src, size;
- Glyph *line;
-
- LIMIT(n, 0, term.col - term.c.x);
+ int src, dst, size;
+ Line line;
- dst = term.c.x;
- src = term.c.x + n;
- size = term.col - src;
- line = term.line[term.c.y];
+ if (n <= 0)
+ return;
- memmove(&line[dst], &line[src], size * sizeof(Glyph));
- tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
+ dst = term.c.x;
+ src = MIN(term.c.x + n, term.col);
+ size = term.col - src;
+ if (size > 0) {
+ /*
+ * otherwise src would point beyond the array
+ * https://stackoverflow.com/questions/29844298
+ */
+ line = term.line[term.c.y];
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ }
+ tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1);
}
void
tinsertblank(int n)
{
- int dst, src, size;
- Glyph *line;
+ int src, dst, size;
+ Line line;
- LIMIT(n, 0, term.col - term.c.x);
-
- dst = term.c.x + n;
- src = term.c.x;
- size = term.col - dst;
- line = term.line[term.c.y];
-
- memmove(&line[dst], &line[src], size * sizeof(Glyph));
- tclearregion(src, term.c.y, dst - 1, term.c.y);
+ if (n <= 0)
+ return;
+ dst = MIN(term.c.x + n, term.col);
+ src = term.c.x;
+ size = term.col - dst;
+ if (size > 0) { /* otherwise dst would point beyond the array */
+ line = term.line[term.c.y];
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ }
+ tclearregion(src, term.c.y, dst - 1, term.c.y, 1);
}
void
@@ -1295,7 +1566,7 @@ void
tdeleteline(int n)
{
if (BETWEEN(term.c.y, term.top, term.bot))
- tscrollup(term.c.y, n);
+ tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);
}
int32_t
@@ -1368,6 +1639,10 @@ tsetattr(const int *attr, int l)
ATTR_STRUCK );
term.c.attr.fg = defaultfg;
term.c.attr.bg = defaultbg;
+ term.c.attr.ustyle = -1;
+ term.c.attr.ucolor[0] = -1;
+ term.c.attr.ucolor[1] = -1;
+ term.c.attr.ucolor[2] = -1;
break;
case 1:
term.c.attr.mode |= ATTR_BOLD;
@@ -1379,7 +1654,14 @@ tsetattr(const int *attr, int l)
term.c.attr.mode |= ATTR_ITALIC;
break;
case 4:
- term.c.attr.mode |= ATTR_UNDERLINE;
+ term.c.attr.ustyle = csiescseq.carg[i][0];
+
+ if (term.c.attr.ustyle != 0)
+ term.c.attr.mode |= ATTR_UNDERLINE;
+ else
+ term.c.attr.mode &= ~ATTR_UNDERLINE;
+
+ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
break;
case 5: /* slow blink */
/* FALLTHROUGH */
@@ -1430,6 +1712,18 @@ tsetattr(const int *attr, int l)
case 49:
term.c.attr.bg = defaultbg;
break;
+ case 58:
+ term.c.attr.ucolor[0] = csiescseq.carg[i][1];
+ term.c.attr.ucolor[1] = csiescseq.carg[i][2];
+ term.c.attr.ucolor[2] = csiescseq.carg[i][3];
+ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
+ break;
+ case 59:
+ term.c.attr.ucolor[0] = -1;
+ term.c.attr.ucolor[1] = -1;
+ term.c.attr.ucolor[2] = -1;
+ term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
+ break;
default:
if (BETWEEN(attr[i], 30, 37)) {
term.c.attr.fg = attr[i] - 30;
@@ -1469,7 +1763,7 @@ tsetscroll(int t, int b)
void
tsetmode(int priv, int set, const int *args, int narg)
{
- int alt; const int *lim;
+ const int *lim;
for (lim = args + narg; args < lim; ++args) {
if (priv) {
@@ -1530,26 +1824,20 @@ tsetmode(int priv, int set, const int *args, int narg)
xsetmode(set, MODE_8BIT);
break;
case 1049: /* swap screen & set/restore cursor as xterm */
- if (!allowaltscreen)
- break;
- tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
- /* FALLTHROUGH */
case 47: /* swap screen */
- case 1047:
+ case 1047: /*swap screen, clearing alternate screen */
if (!allowaltscreen)
break;
- alt = IS_SET(MODE_ALTSCREEN);
- if (alt) {
- tclearregion(0, 0, term.col-1,
- term.row-1);
- }
- if (set ^ alt) /* set is always 1 or 0 */
- tswapscreen();
- if (*args != 1049)
- break;
+ if (set)
+ tloadaltscreen(*args == 1049, *args == 1049);
+ else
+ tloaddefscreen(*args == 1047, *args == 1049);
+ break;
/* FALLTHROUGH */
- case 1048:
- tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+ case 1048:
+ if (!allowaltscreen)
+ break;
+ tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
break;
case 2004: /* 2004: bracketed paste mode */
xsetmode(set, MODE_BRCKTPASTE);
@@ -1600,7 +1888,7 @@ void
csihandle(void)
{
char buf[40];
- int len;
+ int n, x;
switch (csiescseq.mode[0]) {
default:
@@ -1698,19 +1986,29 @@ csihandle(void)
case 'J': /* ED -- Clear screen */
switch (csiescseq.arg[0]) {
case 0: /* below */
- tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
+ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
if (term.c.y < term.row-1) {
- tclearregion(0, term.c.y+1, term.col-1,
- term.row-1);
+ tclearregion(0, term.c.y+1, term.col-1, term.row-1, 1);
}
break;
case 1: /* above */
- if (term.c.y > 0)
- tclearregion(0, 0, term.col-1, term.c.y-1);
- tclearregion(0, term.c.y, term.c.x, term.c.y);
+ if (term.c.y >= 1)
+ tclearregion(0, 0, term.col-1, term.c.y-1, 1);
+ tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
break;
case 2: /* all */
- tclearregion(0, 0, term.col-1, term.row-1);
+ if (IS_SET(MODE_ALTSCREEN)) {
+ tclearregion(0, 0, term.col-1, term.row-1, 1);
+ break;
+ }
+ /* vte does this:
+ tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */
+
+ /* alacritty does this: */
+ for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--);
+ if (n >= 0)
+ tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST);
+ tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);
break;
default:
goto unknown;
@@ -1719,21 +2017,21 @@ csihandle(void)
case 'K': /* EL -- Clear line */
switch (csiescseq.arg[0]) {
case 0: /* right */
- tclearregion(term.c.x, term.c.y, term.col-1,
- term.c.y);
+ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
break;
case 1: /* left */
- tclearregion(0, term.c.y, term.c.x, term.c.y);
+ tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
break;
case 2: /* all */
- tclearregion(0, term.c.y, term.col-1, term.c.y);
+ tclearregion(0, term.c.y, term.col-1, term.c.y, 1);
break;
}
break;
case 'S': /* SU -- Scroll <n> line up */
if (csiescseq.priv) break;
DEFAULT(csiescseq.arg[0], 1);
- tscrollup(term.top, csiescseq.arg[0]);
+ /* xterm, urxvt, alacritty save this in history */
+ tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST);
break;
case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1);
@@ -1751,9 +2049,11 @@ csihandle(void)
tdeleteline(csiescseq.arg[0]);
break;
case 'X': /* ECH -- Erase <n> char */
+ if (csiescseq.arg[0] < 0)
+ return;
DEFAULT(csiescseq.arg[0], 1);
- tclearregion(term.c.x, term.c.y,
- term.c.x + csiescseq.arg[0] - 1, term.c.y);
+ x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1;
+ tclearregion(term.c.x, term.c.y, x, term.c.y, 1);
break;
case 'P': /* DCH -- Delete <n> char */
DEFAULT(csiescseq.arg[0], 1);
@@ -1779,9 +2079,9 @@ csihandle(void)
ttywrite("\033[0n", sizeof("\033[0n") - 1, 0);
break;
case 6: /* Report Cursor Position (CPR) "<row>;<column>R" */
- len = snprintf(buf, sizeof(buf), "\033[%i;%iR",
+ n = snprintf(buf, sizeof(buf), "\033[%i;%iR",
term.c.y+1, term.c.x+1);
- ttywrite(buf, len, 0);
+ ttywrite(buf, n, 0);
break;
default:
goto unknown;
@@ -1813,6 +2113,33 @@ csihandle(void)
goto unknown;
}
break;
+ case 't': /* title stack operations */
+ switch (csiescseq.arg[0]) {
+ case 22: /* pust current title on stack */
+ switch (csiescseq.arg[1]) {
+ case 0:
+ case 1:
+ case 2:
+ xpushtitle();
+ break;
+ default:
+ goto unknown;
+ }
+ break;
+ case 23: /* pop last title from stack */
+ switch (csiescseq.arg[1]) {
+ case 0:
+ case 1:
+ case 2:
+ xsettitle(NULL, 1);
+ break;
+ default:
+ goto unknown;
+ }
+ break;
+ default:
+ goto unknown;
+ }
}
}
@@ -1891,7 +2218,7 @@ strhandle(void)
switch (par) {
case 0:
if (narg > 1) {
- xsettitle(strescseq.args[1]);
+ xsettitle(strescseq.args[1], 0);
xseticontitle(strescseq.args[1]);
}
return;
@@ -1901,7 +2228,7 @@ strhandle(void)
return;
case 2:
if (narg > 1)
- xsettitle(strescseq.args[1]);
+ xsettitle(strescseq.args[1], 0);
return;
case 52:
if (narg > 2 && allowwindowops) {
@@ -1960,7 +2287,7 @@ strhandle(void)
}
break;
case 'k': /* old title set compatibility */
- xsettitle(strescseq.args[0]);
+ xsettitle(strescseq.args[0], 0);
return;
case 'P': /* DCS -- Device Control String */
case '_': /* APC -- Application Program Command */
@@ -2079,16 +2406,8 @@ tdumpsel(void)
void
tdumpline(int n)
{
- char buf[UTF_SIZ];
- const Glyph *bp, *end;
-
- bp = &term.line[n][0];
- end = &bp[MIN(tlinelen(n), term.col) - 1];
- if (bp != end || bp->u != ' ') {
- for ( ; bp <= end; ++bp)
- tprinter(buf, utf8encode(bp->u, buf));
- }
- tprinter("\n", 1);
+ char str[(term.col + 1) * UTF_SIZ];
+ tprinter(str, tgetline(str, &term.line[n][0]));
}
void
@@ -2309,7 +2628,7 @@ eschandle(uchar ascii)
return 0;
case 'D': /* IND -- Linefeed */
if (term.c.y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
} else {
tmoveto(term.c.x, term.c.y+1);
}
@@ -2332,6 +2651,7 @@ eschandle(uchar ascii)
break;
case 'c': /* RIS -- Reset to initial state */
treset();
+ xfreetitlestack();
resettitle();
xloadcols();
xsetmode(0, MODE_HIDE);
@@ -2466,7 +2786,9 @@ check_control_code:
*/
return;
}
- if (selected(term.c.x, term.c.y))
+
+ /* selected() takes relative coordinates */
+ if (selected(term.c.x + term.scr, term.c.y + term.scr))
selclear();
gp = &term.line[term.c.y][term.c.x];
@@ -2506,6 +2828,7 @@ check_control_code:
if (term.c.x+width < term.col) {
tmoveto(term.c.x+width, term.c.y);
} else {
+ term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;
term.c.state |= CURSOR_WRAPNEXT;
}
}
@@ -2543,91 +2866,290 @@ twrite(const char *buf, int buflen, int show_ctrl)
}
void
+rscrolldown(int n)
+{
+ int i;
+ Line temp;
+
+ /* can never be true as of now
+ if (IS_SET(MODE_ALTSCREEN))
+ return; */
+
+ if ((n = MIN(n, term.histf)) <= 0)
+ return;
+
+ for (i = term.c.y + n; i >= n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
+ }
+ for (/*i = n - 1 */; i >= 0; i--) {
+ temp = term.line[i];
+ term.line[i] = term.hist[term.histi];
+ term.hist[term.histi] = temp;
+ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
+ }
+ term.c.y += n;
+ term.histf -= n;
+ if ((i = term.scr - n) >= 0) {
+ term.scr = i;
+ } else {
+ term.scr = 0;
+ if (sel.ob.x != -1 && !sel.alt)
+ selmove(-i);
+ }
+}
+
+
+
+void
tresize(int col, int row)
{
- int i;
- int minrow = MIN(row, term.row);
- int mincol = MIN(col, term.col);
int *bp;
- TCursor c;
+ /* col and row are always MAX(_, 1)
if (col < 1 || row < 1) {
- fprintf(stderr,
- "tresize: error resizing to %dx%d\n", col, row);
+ fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row);
return;
- }
-
- /*
- * slide screen to keep cursor where we expect it -
- * tscrollup would work here, but we can optimize to
- * memmove because we're freeing the earlier lines
- */
- for (i = 0; i <= term.c.y - row; i++) {
- free(term.line[i]);
- free(term.alt[i]);
- }
- /* ensure that both src and dst are not NULL */
- if (i > 0) {
- memmove(term.line, term.line + i, row * sizeof(Line));
- memmove(term.alt, term.alt + i, row * sizeof(Line));
- }
- for (i += row; i < term.row; i++) {
- free(term.line[i]);
- free(term.alt[i]);
- }
+ } */
- /* resize to new height */
- term.line = xrealloc(term.line, row * sizeof(Line));
- term.alt = xrealloc(term.alt, row * sizeof(Line));
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+ if (col > term.col) {
+ bp = term.tabs + term.col;
+ memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
+ while (--bp > term.tabs && !*bp)
+ /* nothing */ ;
+ for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
+ *bp = 1;
+ }
- /* resize each row to new width, zero-pad if needed */
- for (i = 0; i < minrow; i++) {
- term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
- term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
- }
+ if (IS_SET(MODE_ALTSCREEN))
+ tresizealt(col, row);
+ else
+ tresizedef(col, row);
+}
- /* allocate any new rows */
- for (/* i = minrow */; i < row; i++) {
- term.line[i] = xmalloc(col * sizeof(Glyph));
- term.alt[i] = xmalloc(col * sizeof(Glyph));
+
+void
+tresizedef(int col, int row)
+{
+ int i, j;
+
+ /* return if dimensions haven't changed */
+ if (term.col == col && term.row == row) {
+ tfulldirt();
+ return;
}
- if (col > term.col) {
- bp = term.tabs + term.col;
-
- memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
- while (--bp > term.tabs && !*bp)
- /* nothing */ ;
- for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
- *bp = 1;
+ if (col != term.col) {
+ if (!sel.alt)
+ selremove();
+ treflow(col, row);
+ } else {
+ /* slide screen up if otherwise cursor would get out of the screen */
+ if (term.c.y >= row) {
+ tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE);
+ term.c.y = row - 1;
+ }
+ for (i = row; i < term.row; i++)
+ free(term.line[i]);
+
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+ /* allocate any new rows */
+ for (i = term.row; i < row; i++) {
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ for (j = 0; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* scroll down as much as height has increased */
+ rscrolldown(row - term.row);
}
/* update terminal size */
- term.col = col;
- term.row = row;
+ term.col = col, term.row = row;
/* reset scrolling region */
- tsetscroll(0, row-1);
- /* make use of the LIMIT in tmoveto */
- tmoveto(term.c.x, term.c.y);
- /* Clearing both screens (it makes dirty all lines) */
- c = term.c;
- for (i = 0; i < 2; i++) {
- if (mincol < col && 0 < minrow) {
- tclearregion(mincol, 0, col - 1, minrow - 1);
- }
- if (0 < col && minrow < row) {
- tclearregion(0, minrow, col - 1, row - 1);
- }
- tswapscreen();
- tcursor(CURSOR_LOAD);
- }
- term.c = c;
+ term.top = 0, term.bot = row - 1;
+ /* dirty all lines */
+ tfulldirt();
+}
+
+
+
+void
+tresizealt(int col, int row)
+{
+ int i, j;
+
+ /* return if dimensions haven't changed */
+ if (term.col == col && term.row == row) {
+ tfulldirt();
+ return;
+ }
+ if (sel.alt)
+ selremove();
+ /* slide screen up if otherwise cursor would get out of the screen */
+ for (i = 0; i <= term.c.y - row; i++)
+ free(term.line[i]);
+ if (i > 0) {
+ /* ensure that both src and dst are not NULL */
+ memmove(term.line, term.line + i, row * sizeof(Line));
+ term.c.y = row - 1;
+ }
+ for (i += row; i < term.row; i++)
+ free(term.line[i]);
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+ /* resize to new width */
+ for (i = 0; i < MIN(row, term.row); i++) {
+ term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+ for (j = term.col; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* allocate any new rows */
+ for (/*i = MIN(row, term.row) */; i < row; i++) {
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ for (j = 0; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* update cursor */
+ if (term.c.x >= col) {
+ term.c.state &= ~CURSOR_WRAPNEXT;
+ term.c.x = col - 1;
+ } else {
+ UPDATEWRAPNEXT(1, col);
+ }
+ /* update terminal size */
+ term.col = col, term.row = row;
+ /* reset scrolling region */
+ term.top = 0, term.bot = row - 1;
+ /* dirty all lines */
+ tfulldirt();
+ }
+
+
+
+
+
+void
+treflow(int col, int row)
+{
+ int i, j;
+ int oce, nce, bot, scr;
+ int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
+ int cy = -1; /* proxy for new y coordinate of cursor */
+ int nlines;
+ Line *buf, line;
+
+ /* y coordinate of cursor line end */
+ for (oce = term.c.y; oce < term.row - 1 &&
+ tiswrapped(term.line[oce]); oce++);
+
+ nlines = term.histf + oce + 1;
+ if (col < term.col) {
+ /* each line can take this many lines after reflow */
+ j = (term.col + col - 1) / col;
+ nlines = j * nlines;
+ if (nlines > HISTSIZE + RESIZEBUFFER + row) {
+ nlines = HISTSIZE + RESIZEBUFFER + row;
+ oy = -(nlines / j - oce - 1);
+ }
+ }
+ buf = xmalloc(nlines * sizeof(Line));
+ do {
+ if (!nx)
+ buf[++ny] = xmalloc(col * sizeof(Glyph));
+ if (!ox) {
+ line = TLINEABS(oy);
+ len = tlinelen(line);
+ }
+ if (oy == term.c.y) {
+ if (!ox)
+ len = MAX(len, term.c.x + 1);
+ /* update cursor */
+ if (cy < 0 && term.c.x - ox < col - nx) {
+ term.c.x = nx + term.c.x - ox, cy = ny;
+ UPDATEWRAPNEXT(0, col);
+ }
+ }
+ /* get reflowed lines in buf */
+ if (col - nx > len - ox) {
+ memcpy(&buf[ny][nx], &line[ox], (len-ox) * sizeof(Glyph));
+ nx += len - ox;
+ if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) {
+ for (j = nx; j < col; j++)
+ tclearglyph(&buf[ny][j], 0);
+ nx = 0;
+ } else if (nx > 0) {
+ buf[ny][nx - 1].mode &= ~ATTR_WRAP;
+ }
+ ox = 0, oy++;
+ } else if (col - nx == len - ox) {
+ memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
+ ox = 0, oy++, nx = 0;
+ } else/* if (col - nx < len - ox) */ {
+ memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
+ ox += col - nx;
+ buf[ny][col - 1].mode |= ATTR_WRAP;
+ nx = 0;
+ }
+ } while (oy <= oce);
+ if (nx)
+ for (j = nx; j < col; j++)
+ tclearglyph(&buf[ny][j], 0);
+
+ /* free extra lines */
+ for (i = row; i < term.row; i++)
+ free(term.line[i]);
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+
+ bot = MIN(ny, row - 1);
+ scr = MAX(row - term.row, 0);
+ /* update y coordinate of cursor line end */
+ nce = MIN(oce + scr, bot);
+ /* update cursor y coordinate */
+ term.c.y = nce - (ny - cy);
+ if (term.c.y < 0) {
+ j = nce, nce = MIN(nce + -term.c.y, bot);
+ term.c.y += nce - j;
+ while (term.c.y < 0) {
+ free(buf[ny--]);
+ term.c.y++;
+ }
+ }
+ /* allocate new rows */
+ for (i = row - 1; i > nce; i--) {
+ term.line[i] = xmalloc(col * sizeof(Glyph));
+ for (j = 0; j < col; j++)
+ tclearglyph(&term.line[i][j], 0);
+ }
+ /* fill visible area */
+ for (/*i = nce */; i >= term.row; i--, ny--)
+ term.line[i] = buf[ny];
+ for (/*i = term.row - 1 */; i >= 0; i--, ny--) {
+ free(term.line[i]);
+ term.line[i] = buf[ny];
+ }
+ /* fill lines in history buffer and update term.histf */
+ for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) {
+ j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
+ free(term.hist[j]);
+ term.hist[j] = buf[ny];
+ }
+ term.histf = -i - 1;
+ term.scr = MIN(term.scr, term.histf);
+ /* resize rest of the history lines */
+ for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) {
+ j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
+ term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph));
+ }
+ free(buf);
}
void
resettitle(void)
{
- xsettitle(NULL);
+ xsettitle(NULL, 0);
}
void
@@ -2640,7 +3162,7 @@ drawregion(int x1, int y1, int x2, int y2)
continue;
term.dirty[y] = 0;
- xdrawline(term.line[y], x1, y, x2);
+ xdrawline(TLINE(y), x1, y, x2);
}
}
diff --git a/st.desktop b/st.desktop
new file mode 100644
index 0000000..2d2b76f
--- /dev/null
+++ b/st.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Type=Application
+Exec=st
+TryExec=st
+Icon=utilities-terminal
+Terminal=false
+Categories=System;TerminalEmulator;
+
+Name=st
+GenericName=Terminal
+Comment=st is a simple terminal implementation for X
+StartupWMClass=st-256color
diff --git a/st.h b/st.h
index fd3b0d8..46c33f6 100644
--- a/st.h
+++ b/st.h
@@ -22,18 +22,22 @@
enum glyph_attribute {
ATTR_NULL = 0,
- ATTR_BOLD = 1 << 0,
- ATTR_FAINT = 1 << 1,
- ATTR_ITALIC = 1 << 2,
- ATTR_UNDERLINE = 1 << 3,
- ATTR_BLINK = 1 << 4,
- ATTR_REVERSE = 1 << 5,
- ATTR_INVISIBLE = 1 << 6,
- ATTR_STRUCK = 1 << 7,
- ATTR_WRAP = 1 << 8,
- ATTR_WIDE = 1 << 9,
- ATTR_WDUMMY = 1 << 10,
+ ATTR_SET = 1 << 0,
+ ATTR_BOLD = 1 << 1,
+ ATTR_FAINT = 1 << 2,
+ ATTR_ITALIC = 1 << 3,
+ ATTR_UNDERLINE = 1 << 4,
+ ATTR_BLINK = 1 << 5,
+ ATTR_REVERSE = 1 << 6,
+ ATTR_INVISIBLE = 1 << 7,
+ ATTR_STRUCK = 1 << 8,
+ ATTR_WRAP = 1 << 9,
+ ATTR_WIDE = 1 << 10,
+ ATTR_WDUMMY = 1 << 11,
+ ATTR_SELECTED = 1 << 12,
+ ATTR_BOXDRAW = 1 << 11,
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
+ ATTR_DIRTYUNDERLINE = 1 << 15,
};
enum selection_mode {
@@ -65,6 +69,8 @@ typedef struct {
ushort mode; /* attribute flags */
uint32_t fg; /* foreground */
uint32_t bg; /* background */
+ int ustyle; /* underline style */
+ int ucolor[3]; /* underline color */
} Glyph;
typedef Glyph *Line;
@@ -81,6 +87,8 @@ void die(const char *, ...);
void redraw(void);
void draw(void);
+void kscrolldown(const Arg *);
+void kscrollup(const Arg *);
void printscreen(const Arg *);
void printsel(const Arg *);
void sendbreak(const Arg *);
@@ -88,6 +96,7 @@ void toggleprinter(const Arg *);
int tattrset(int);
void tnew(int, int);
+int tisaltscreen(void);
void tresize(int, int);
void tsetdirtattr(int);
void ttyhangup(void);
@@ -111,6 +120,14 @@ void *xmalloc(size_t);
void *xrealloc(void *, size_t);
char *xstrdup(const char *);
+int isboxdraw(Rune);
+ushort boxdrawindex(const Glyph *);
+#ifdef XFT_VERSION
+/* only exposed to x.c, otherwise we'll need Xft.h for the types */
+void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *);
+void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int);
+#endif
+
/* config.h globals */
extern char *utmp;
extern char *scroll;
@@ -124,3 +141,4 @@ extern unsigned int tabspaces;
extern unsigned int defaultfg;
extern unsigned int defaultbg;
extern unsigned int defaultcs;
+extern const int boxdraw, boxdraw_bold, boxdraw_braille;
diff --git a/st.info b/st.info
index efab2cf..caef745 100644
--- a/st.info
+++ b/st.info
@@ -1,4 +1,5 @@
st-mono| simpleterm monocolor,
+ Su,
acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
am,
bce,
@@ -161,7 +162,7 @@ st-mono| simpleterm monocolor,
rin=\E[%p1%dT,
ritm=\E[23m,
rmacs=\E(B,
- rmcup=\E[?1049l,
+ rmcup=\E[?1049l\E[23;0;0t,
rmir=\E[4l,
rmkx=\E[?1l\E>,
rmso=\E[27m,
@@ -172,7 +173,7 @@ st-mono| simpleterm monocolor,
sitm=\E[3m,
sgr0=\E[0m,
smacs=\E(0,
- smcup=\E[?1049h,
+ smcup=\E[?1049h\E[22;0;0t,
smir=\E[4h,
smkx=\E[?1h\E=,
smso=\E[7m,
diff --git a/win.h b/win.h
index 6de960d..2a40aa0 100644
--- a/win.h
+++ b/win.h
@@ -32,7 +32,9 @@ void xloadcols(void);
int xsetcolorname(int, const char *);
int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *);
void xseticontitle(char *);
-void xsettitle(char *);
+void xfreetitlestack(void);
+void xsettitle(char *, int);
+void xpushtitle(void);
int xsetcursor(int);
void xsetmode(int, unsigned int);
void xsetpointermotion(int);
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();