diff --git a/LICENSE b/LICENSE index d80eb47..3cbf420 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT/X Consortium License -© 2014-2020 Hiltjo Posthuma +© 2014-2022 Hiltjo Posthuma © 2018 Devin J. Pohly © 2014-2017 Quentin Rameau © 2009-2012 Aurélien APTEL diff --git a/PKGBUILD b/PKGBUILD index 03d6f9f..66dd05e 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -13,7 +13,7 @@ options=('zipman') depends=('libxft') makedepends=('ncurses' 'libxext' 'git') optdepends=('dmenu: feed urls to dmenu') -source=('git://github.com/LukeSmithxyz/st') +source=(git+https://github.com/LukeSmithxyz/st) sha1sums=('SKIP') provides=("${_pkgname}") diff --git a/README.md b/README.md index c410e4b..0fe88d2 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ features that make it literally the best terminal emulator ever: + Boxdraw + Ligatures + font2 -+ updated to latest version 0.8.4 ++ updated to latest version 0.8.5 ## Installation for newbs diff --git a/config.h b/config.h index d6eeade..c86daa3 100644 --- a/config.h +++ b/config.h @@ -6,7 +6,7 @@ * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ static char *font = "mono:pixelsize=12:antialias=true:autohint=true"; -static char *font2[] = { "JoyPixels:pixelsize=10:antialias=true:autohint=true" }; +static char *font2[] = { "NotoColorEmoji:pixelsize=10:antialias=true:autohint=true" }; static int borderpx = 2; /* @@ -246,7 +246,6 @@ static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -o", "externalpipe static char *copyurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -c", "externalpipe", NULL }; static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL }; - static Shortcut shortcuts[] = { /* mask keysym function argument */ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, @@ -555,3 +554,4 @@ static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~"; + diff --git a/config.mk b/config.mk index 1741840..ef6de39 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.8.4 +VERSION = 0.8.5 # Customize below to fit your system diff --git a/st-urlhandler b/st-urlhandler index e2a62f4..0eb4586 100755 --- a/st-urlhandler +++ b/st-urlhandler @@ -1,6 +1,6 @@ #!/bin/sh -urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:]?[a-zA-Z0-9./@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)" +urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:;a-zA-Z0-9./+@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)" urls="$(sed 's/.*│//g' | tr -d '\n' | # First remove linebreaks and mutt sidebars: grep -aEo "$urlregex" | # grep only urls as defined above. diff --git a/st.1 b/st.1 index 37f7e84..1728e24 100644 --- a/st.1 +++ b/st.1 @@ -176,6 +176,24 @@ Print the full screen to the .B Print Screen Print the selection to the .I iofile. +.TP +.B Ctrl-Shift-Page Up +Increase font size. +.TP +.B Ctrl-Shift-Page Down +Decrease font size. +.TP +.B Ctrl-Shift-Home +Reset to default font size. +.TP +.B Ctrl-Shift-y +Paste from primary selection (middle mouse button). +.TP +.B Ctrl-Shift-c +Copy the selected text to the clipboard selection. +.TP +.B Ctrl-Shift-v +Paste from the clipboard selection. .SH CUSTOMIZATION .B st can be customized by creating a custom config.h and (re)compiling the source diff --git a/st.c b/st.c index ceabf85..78cb9b7 100644 --- a/st.c +++ b/st.c @@ -170,6 +170,7 @@ static void csidump(void); static void csihandle(void); static void csiparse(void); static void csireset(void); +static void osc_color_response(int, int, int); static int eschandle(uchar); static void strdump(void); static void strhandle(void); @@ -195,17 +196,17 @@ static void tputc(Rune); static void treset(void); static void tscrollup(int, int, int); static void tscrolldown(int, int, int); -static void tsetattr(int *, int); -static void tsetchar(Rune, Glyph *, 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 tsetmode(int, int, int *, int); +static void tsetmode(int, int, const int *, int); static int twrite(const char *, int, int); static void tcontrolcode(uchar ); static void tdectest(char ); static void tdefutf8(char); -static int32_t tdefcolor(int *, int *, int); +static int32_t tdefcolor(const int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); @@ -234,10 +235,10 @@ static int iofd = 1; static int cmdfd; static pid_t pid; -static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; -static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; -static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; -static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; ssize_t xwrite(int fd, const char *s, size_t len) @@ -277,12 +278,16 @@ xrealloc(void *p, size_t len) } char * -xstrdup(char *s) +xstrdup(const char *s) { if ((s = strdup(s)) == NULL) die("strdup: %s\n", strerror(errno)); + char *p; - return s; + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; } size_t @@ -355,25 +360,10 @@ utf8validate(Rune *u, size_t i) return i; } -static const char base64_digits[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - char base64dec_getc(const char **src) { - while (**src && !isprint(**src)) + while (**src && !isprint((unsigned char)**src)) (*src)++; return **src ? *((*src)++) : '='; /* emulate padding if string ends */ } @@ -383,6 +373,13 @@ base64dec(const char *src) { size_t in_len = strlen(src); char *result, *dst; + static const char base64_digits[256] = { + [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; if (in_len % 4) in_len += 4 - (in_len % 4); @@ -491,6 +488,7 @@ selextend(int col, int row, int type, int done) sel.mode = done ? SEL_IDLE : SEL_READY; } + void selnormalize(void) { @@ -540,7 +538,7 @@ selsnap(int *x, int *y, int direction) { int newx, newy, xt, yt; int delim, prevdelim; - Glyph *gp, *prevgp; + const Glyph *gp, *prevgp; switch (sel.snap) { case SNAP_WORD: @@ -613,7 +611,7 @@ getsel(void) { char *str, *ptr; int y, bufsize, lastx, linelen; - Glyph *gp, *last; + const Glyph *gp, *last; if (sel.ob.x == -1) return NULL; @@ -780,7 +778,7 @@ stty(char **args) } int -ttynew(char *line, char *cmd, char *out, char **args) +ttynew(const char *line, char *cmd, const char *out, char **args) { int m, s; @@ -813,14 +811,15 @@ ttynew(char *line, char *cmd, char *out, char **args) break; case 0: close(iofd); + close(m); setsid(); /* create a new process group */ dup2(s, 0); dup2(s, 1); dup2(s, 2); if (ioctl(s, TIOCSCTTY, NULL) < 0) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); - close(s); - close(m); + if (s > 2) + close(s); #ifdef __OpenBSD__ if (pledge("stdio getpw proc exec", NULL) == -1) die("pledge\n"); @@ -1263,9 +1262,9 @@ tmoveto(int x, int y) } void -tsetchar(Rune u, Glyph *attr, int x, int y) +tsetchar(Rune u, const Glyph *attr, int x, int y) { - static char *vt100_0[62] = { /* 0x41 - 0x7e */ + 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 */ @@ -1380,7 +1379,7 @@ tdeleteline(int n) } int32_t -tdefcolor(int *attr, int *npar, int l) +tdefcolor(const int *attr, int *npar, int l) { int32_t idx = -1; uint r, g, b; @@ -1430,7 +1429,7 @@ tdefcolor(int *attr, int *npar, int l) } void -tsetattr(int *attr, int l) +tsetattr(const int *attr, int l) { int i; int32_t idx; @@ -1548,9 +1547,9 @@ tsetscroll(int t, int b) } void -tsetmode(int priv, int set, int *args, int narg) +tsetmode(int priv, int set, const int *args, int narg) { - int alt, *lim; + int alt; const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -1919,11 +1918,41 @@ csireset(void) memset(&csiescseq, 0, sizeof(csiescseq)); } +void +osc_color_response(int num, int index, int is_osc4) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); + } +} + void strhandle(void) { char *p = NULL, *dec; int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); @@ -1957,14 +1986,35 @@ strhandle(void) } } return; + case 10: + case 11: + case 12: + if (narg < 2) + break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ + + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { + tfulldirt(); + } + return; case 4: /* color set */ if (narg < 3) break; p = strescseq.args[2]; /* FALLTHROUGH */ - case 104: /* color reset, here p = NULL */ + case 104: /* color reset */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (xsetcolorname(j, p)) { + + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { if (par == 104 && narg <= 1) return; /* color reset without parameter */ fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", @@ -1974,7 +2024,7 @@ strhandle(void) * TODO if defaultbg color is changed, borders * are dirty */ - redraw(); + tfulldirt(); } return; } @@ -2155,7 +2205,7 @@ void tdumpline(int n) { char buf[UTF_SIZ]; - Glyph *bp, *end; + const Glyph *bp, *end; bp = &term.line[n][0]; end = &bp[MIN(tlinelen(n), term.col) - 1]; @@ -2561,6 +2611,10 @@ check_control_code: if (width == 2) { gp->mode |= ATTR_WIDE; if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } gp[1].u = '\0'; gp[1].mode = ATTR_WDUMMY; } @@ -2759,3 +2813,4 @@ redraw(void) tfulldirt(); draw(); } + diff --git a/st.h b/st.h index 3ddfadd..60d973a 100644 --- a/st.h +++ b/st.h @@ -88,6 +88,7 @@ void draw(void); void externalpipe(const Arg *); void kscrolldown(const Arg *); void kscrollup(const Arg *); + void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); @@ -98,7 +99,7 @@ void tnew(int, int); void tresize(int, int); void tsetdirtattr(int); void ttyhangup(void); -int ttynew(char *, char *, char *, char **); +int ttynew(const char *, char *, const char *, char **); size_t ttyread(void); void ttyresize(int, int); void ttywrite(const char *, size_t, int); @@ -116,7 +117,7 @@ size_t utf8encode(Rune, char *); void *xmalloc(size_t); void *xrealloc(void *, size_t); -char *xstrdup(char *); +char *xstrdup(const char *); int isboxdraw(Rune); ushort boxdrawindex(const Glyph *); @@ -141,3 +142,5 @@ extern unsigned int defaultbg; extern float alpha; extern float alphaUnfocus; extern const int boxdraw, boxdraw_bold, boxdraw_braille; +extern unsigned int defaultcs; + diff --git a/win.h b/win.h index 8b5b618..8839e31 100644 --- a/win.h +++ b/win.h @@ -30,6 +30,7 @@ void xdrawline(Line, int, int, int); void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); +int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *); void xseticontitle(char *); void xsettitle(char *); int xsetcursor(int); @@ -38,3 +39,4 @@ void xsetpointermotion(int); void xsetsel(char *); int xstartdraw(void); void xximspot(int, int); + diff --git a/x.c b/x.c index 2580d2a..017238f 100644 --- a/x.c +++ b/x.c @@ -63,7 +63,7 @@ typedef struct { /* X modifiers */ #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13) +#define XK_SWITCH_MOD (1<<13|1<<14) /* function definitions used in config.h */ static void clipcopy(const Arg *); @@ -173,9 +173,9 @@ static void xresize(int, int); static void xhints(void); static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); -static void xloadfonts(char *, double); static int xloadsparefont(FcPattern *, int); static void xloadsparefonts(void); +static void xloadfonts(const char *, double); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); @@ -276,6 +276,7 @@ static char *opt_title = NULL; static int focused = 0; static int oldbutton = 3; /* button event on startup: 3 = release */ +static uint buttons; /* bit field of pressed buttons */ void clipcopy(const Arg *dummy) @@ -409,6 +410,7 @@ mousesel(XEvent *e, int done) void mousereport(XEvent *e) { + int code; int len, x = evcol(e), y = evrow(e), button = e->xbutton.button, state = e->xbutton.state; char buf[40]; @@ -457,11 +459,11 @@ mousereport(XEvent *e) if (IS_SET(MODE_MOUSESGR)) { len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", - button, x+1, y+1, - e->xbutton.type == ButtonRelease ? 'm' : 'M'); + code, x+1, y+1, + e->type == ButtonRelease ? 'm' : 'M'); } else if (x < 223 && y < 223) { len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - 32+button, 32+x+1, 32+y+1); + 32+code, 32+x+1, 32+y+1); } else { return; } @@ -504,9 +506,13 @@ mouseaction(XEvent *e, uint release) void bpress(XEvent *e) { + int btn = e->xbutton.button; struct timespec now; int snap; + if (1 <= btn && btn <= 11) + buttons |= 1 << (btn-1); + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; @@ -515,7 +521,7 @@ bpress(XEvent *e) if (mouseaction(e, 0)) return; - if (e->xbutton.button == Button1) { + if (btn == Button1) { /* * If the user clicks below predefined timeouts specific * snapping behaviour is exposed. @@ -729,6 +735,11 @@ xsetsel(char *str) void brelease(XEvent *e) { + int btn = e->xbutton.button; + + if (1 <= btn && btn <= 11) + buttons &= ~(1 << (btn-1)); + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; @@ -736,7 +747,7 @@ brelease(XEvent *e) if (mouseaction(e, 1)) return; - if (e->xbutton.button == Button1) + if (btn == Button1) mousesel(e, 1); } @@ -854,6 +865,19 @@ xloadcols(void) loaded = 1; } +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + int xsetcolorname(int x, const char *name) { @@ -1007,7 +1031,7 @@ xloadfont(Font *f, FcPattern *pattern) } void -xloadfonts(char *fontstr, double fontsize) +xloadfonts(const char *fontstr, double fontsize) { FcPattern *pattern; double fontval; @@ -1015,7 +1039,7 @@ xloadfonts(char *fontstr, double fontsize) if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); else - pattern = FcNameParse((FcChar8 *)fontstr); + pattern = FcNameParse((const FcChar8 *)fontstr); if (!pattern) die("can't open font %s\n", fontstr); @@ -1641,11 +1665,11 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, - width, 1); + width, 1); } if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, width, 1); } @@ -1767,8 +1791,9 @@ xseticontitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, - &prop); + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; XSetWMIconName(xw.dpy, xw.win, &prop); XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); XFree(prop.value); @@ -1780,8 +1805,9 @@ xsettitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, - &prop); + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; XSetWMName(xw.dpy, xw.win, &prop); XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); XFree(prop.value); @@ -2315,3 +2341,4 @@ run: return 0; } +