X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=st.c;h=e50e8848c50ebabc5e64f451123e001dc2fcc68a;hb=740ada1447a0bf9eb7db327d9433fa0b96e0a4d8;hp=6736464f7ecc2819cc9c22622149eb6ef6701c91;hpb=60aeb37edb8c5280d31b6b3c801d09c7a5fdca76;p=st.git diff --git a/st.c b/st.c index 6736464..e50e884 100644 --- a/st.c +++ b/st.c @@ -66,6 +66,7 @@ char *argv0; #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) #define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) @@ -87,6 +88,8 @@ char *argv0; #define TRUEGREEN(x) (((x) & 0xff00)) #define TRUEBLUE(x) (((x) & 0xff) << 8) +/* constants */ +#define ISO14755CMD "dmenu -w %lu -p codepoint: 0) + memmove(buf, ptr, buflen); return ret; } @@ -1554,15 +1577,26 @@ void ttysend(char *s, size_t n) { int len; + char *t, *lim; Rune u; ttywrite(s, n); - if (IS_SET(MODE_ECHO)) - while ((len = utf8decode(s, &u, n)) > 0) { - techo(u); - n -= len; - s += len; + if (!IS_SET(MODE_ECHO)) + return; + + lim = &s[n]; + for (t = s; t < lim; t += len) { + if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + len = utf8decode(t, &u, n); + } else { + u = *t & 0xFF; + len = 1; } + if (len <= 0) + break; + techo(u); + n -= len; + } } void @@ -1656,7 +1690,7 @@ treset(void) term.tabs[i] = 1; term.top = 0; term.bot = term.row - 1; - term.mode = MODE_WRAP; + term.mode = MODE_WRAP|MODE_UTF8; memset(term.trantbl, CS_USA, sizeof(term.trantbl)); term.charset = 0; @@ -2456,22 +2490,22 @@ csidump(void) int i; uint c; - printf("ESC["); + fprintf(stderr, "ESC["); for (i = 0; i < csiescseq.len; i++) { c = csiescseq.buf[i] & 0xff; if (isprint(c)) { - putchar(c); + putc(c, stderr); } else if (c == '\n') { - printf("(\\n)"); + fprintf(stderr, "(\\n)"); } else if (c == '\r') { - printf("(\\r)"); + fprintf(stderr, "(\\r)"); } else if (c == 0x1b) { - printf("(\\e)"); + fprintf(stderr, "(\\e)"); } else { - printf("(%02x)", c); + fprintf(stderr, "(%02x)", c); } } - putchar('\n'); + putc('\n', stderr); } void @@ -2522,6 +2556,7 @@ strhandle(void) xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ + term.mode |= ESC_DCS; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2559,24 +2594,25 @@ strdump(void) int i; uint c; - printf("ESC%c", strescseq.type); + fprintf(stderr, "ESC%c", strescseq.type); for (i = 0; i < strescseq.len; i++) { c = strescseq.buf[i] & 0xff; if (c == '\0') { + putc('\n', stderr); return; } else if (isprint(c)) { - putchar(c); + putc(c, stderr); } else if (c == '\n') { - printf("(\\n)"); + fprintf(stderr, "(\\n)"); } else if (c == '\r') { - printf("(\\r)"); + fprintf(stderr, "(\\r)"); } else if (c == 0x1b) { - printf("(\\e)"); + fprintf(stderr, "(\\e)"); } else { - printf("(%02x)", c); + fprintf(stderr, "(%02x)", c); } } - printf("ESC\\\n"); + fprintf(stderr, "ESC\\\n"); } void @@ -2603,6 +2639,30 @@ tprinter(char *s, size_t len) } } +void +iso14755(const Arg *arg) +{ + char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)]; + FILE *p; + char *us, *e, codepoint[9], uc[UTF_SIZ]; + unsigned long utf32; + + snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win); + if (!(p = popen(cmd, "r"))) + return; + + us = fgets(codepoint, sizeof(codepoint), p); + pclose(p); + + if (!us || *us == '\0' || *us == '-' || strlen(us) > 7) + return; + if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX || + (*e != '\n' && *e != '\0')) + return; + + ttysend(uc, utf8encode(utf32, uc)); +} + void toggleprinter(const Arg *arg) { @@ -2689,6 +2749,15 @@ techo(Rune u) tputc(u); } +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + void tdeftran(char ascii) { @@ -2719,9 +2788,12 @@ tdectest(char c) void tstrsequence(uchar c) { + strreset(); + switch (c) { case 0x90: /* DCS -- Device Control String */ c = 'P'; + term.esc |= ESC_DCS; break; case 0x9f: /* APC -- Application Program Command */ c = '_'; @@ -2733,7 +2805,6 @@ tstrsequence(uchar c) c = ']'; break; } - strreset(); strescseq.type = c; term.esc |= ESC_STR; } @@ -2851,6 +2922,9 @@ eschandle(uchar ascii) case '#': term.esc |= ESC_TEST; return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ @@ -2930,10 +3004,15 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - len = utf8encode(u, c); - if (!control && (width = wcwidth(u)) == -1) { - memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ - width = 1; + if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) { + memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ + width = 1; + } } if (IS_SET(MODE_PRINT)) @@ -2948,30 +3027,47 @@ tputc(Rune u) if (term.esc & ESC_STR) { if (u == '\a' || u == 030 || u == 032 || u == 033 || ISCONTROLC1(u)) { - term.esc &= ~(ESC_START|ESC_STR); + term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); + if (IS_SET(MODE_SIXEL)) { + /* TODO: render sixel */; + term.mode &= ~MODE_SIXEL; + return; + } term.esc |= ESC_STR_END; - } else if (strescseq.len + len < sizeof(strescseq.buf) - 1) { - memmove(&strescseq.buf[strescseq.len], c, len); - strescseq.len += len; + goto check_control_code; + } + + + if (IS_SET(MODE_SIXEL)) { + /* TODO: implement sixel mode */ return; - } else { - /* - * Here is a bug in terminals. If the user never sends - * some code to stop the str or esc command, then st - * will stop responding. But this is better than - * silently failing with unknown characters. At least - * then users will report back. - * - * In the case users ever get fixed, here is the code: - */ - /* - * term.esc = 0; - * strhandle(); - */ + } + if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') + term.mode |= MODE_SIXEL; + + if (strescseq.len+len >= sizeof(strescseq.buf)-1) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ return; } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; } +check_control_code: /* * Actions of control codes must be performed as soon they arrive * because they can be embedded inside a control sequence, and @@ -2994,6 +3090,8 @@ tputc(Rune u) csihandle(); } return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); } else if (term.esc & ESC_ALTCHARSET) { tdeftran(u); } else if (term.esc & ESC_TEST) { @@ -3278,8 +3376,9 @@ xloadfont(Font *f, FcPattern *pattern) FcPattern *match; FcResult result; XGlyphInfo extents; + int wantattr, haveattr; - match = FcFontMatch(NULL, pattern, &result); + match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); if (!match) return 1; @@ -3288,6 +3387,28 @@ xloadfont(Font *f, FcPattern *pattern) return 1; } + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("st: font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("st: font weight does not match\n", stderr); + } + } + XftTextExtentsUtf8(xw.dpy, f->match, (const FcChar8 *) ascii_printable, strlen(ascii_printable), &extents); @@ -3345,15 +3466,15 @@ xloadfonts(char *fontstr, double fontsize) defaultfontsize = usedfontsize; } - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - if (xloadfont(&dc.font, pattern)) die("st: can't open font %s\n", fontstr); if (usedfontsize < 0) { FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); + if (xloadfont(&dc.font, pattern)) + die("st: can't open font %s\n", fontstr); usedfontsize = fontval; if (fontsize == 0) defaultfontsize = fontval; @@ -3688,14 +3809,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i XRenderColor colfg, colbg; XRectangle r; - /* Determine foreground and background colors based on mode. */ - if (base.fg == defaultfg) { - if (base.mode & ATTR_ITALIC) - base.fg = defaultitalic; - else if ((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) - base.fg = defaultitalic; - else if (base.mode & ATTR_UNDERLINE) - base.fg = defaultunderline; + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; } if (IS_TRUECOL(base.fg)) {