X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=st.c;h=979fdc8ee5d082e9c5a20ca48a8ec79ed0cd6bb8;hb=0e232a41150d67539df05e60c7e829f0bfb431d0;hp=42061b9809a3e4221fb6ac9564cf35057e6d4d60;hpb=2696f8187509eb94644bbac5b05348e37c391bea;p=st.git diff --git a/st.c b/st.c index 42061b9..979fdc8 100644 --- a/st.c +++ b/st.c @@ -53,12 +53,12 @@ #define XEMBED_FOCUS_OUT 5 /* Arbitrary sizes */ -#define ESC_BUF_SIZ 256 +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 -#define STR_BUF_SIZ 256 -#define STR_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ #define DRAW_BUF_SIZ 20*1024 -#define UTF_SIZ 4 #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 @@ -168,7 +168,7 @@ typedef struct { int len; /* raw string length */ char priv; int arg[ESC_ARG_SIZ]; - int narg; /* nb of args */ + int narg; /* nb of args */ char mode; } CSIEscape; @@ -203,7 +203,7 @@ typedef struct { Display *dpy; Colourmap cmap; Window win; - XdbeBackBuffer buf; + Drawable buf; Atom xembed, wmdeletewin; XIM xim; XIC xic; @@ -275,7 +275,9 @@ typedef struct { int descent; short lbearing; short rbearing; - XftFont *set; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; } Font; /* Drawing Context */ @@ -338,10 +340,13 @@ static void xclear(int, int, int, int); static void xdrawcursor(void); static void xinit(void); static void xloadcols(void); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(char *, int); static void xresettitle(void); static void xseturgency(int); static void xsetsel(char*); static void xtermclear(int, int, int, int); +static void xunloadfonts(void); static void xresize(int, int); static void expose(XEvent *); @@ -350,7 +355,7 @@ static void unmap(XEvent *); static char *kmap(KeySym, uint); static void kpress(XEvent *); static void cmessage(XEvent *); -static void cresize(int width, int height); +static void cresize(int, int); static void resize(XEvent *); static void focus(XEvent *); static void brelease(XEvent *); @@ -373,7 +378,7 @@ static int isfullutf8(char *, int); static ssize_t xwrite(int, char *, size_t); static void *xmalloc(size_t); static void *xrealloc(void *, size_t); -static void *xcalloc(size_t nmemb, size_t size); +static void *xcalloc(size_t, size_t); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, @@ -409,9 +414,33 @@ static char *opt_embed = NULL; static char *opt_class = NULL; static char *opt_font = NULL; +bool usedbe = False; + static char *usedfont = NULL; static int usedfontsize = 0; +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + long c; + int flags; +} Fontcache; + +/* + * Fontcache is a ring buffer, with frccur as current position and frclen as + * the current length of used elements. + */ + +static Fontcache frc[1024]; +static int frccur = -1, frclen = 0; + ssize_t xwrite(int fd, char *s, size_t len) { size_t aux = len; @@ -1363,7 +1392,7 @@ tsetattr(int *attr, int l) { case 1: term.c.attr.mode |= ATTR_BOLD; break; - case 3: /* enter standout (highlight) */ + case 3: term.c.attr.mode |= ATTR_ITALIC; break; case 4: @@ -1379,7 +1408,7 @@ tsetattr(int *attr, int l) { case 22: term.c.attr.mode &= ~ATTR_BOLD; break; - case 23: /* leave standout (highlight) mode */ + case 23: term.c.attr.mode &= ~ATTR_ITALIC; break; case 24: @@ -1884,12 +1913,13 @@ tputc(char *c, int len) { if(iofd != -1) { if (xwrite(iofd, c, len) < 0) { - fprintf(stderr, "Error writting in %s:%s\n", + fprintf(stderr, "Error writing in %s:%s\n", opt_io, strerror(errno)); close(iofd); iofd = -1; } } + /* * STR sequences must be checked before anything else * because it can use some control codes as part of the sequence. @@ -1904,10 +1934,23 @@ tputc(char *c, int len) { strhandle(); break; default: - strescseq.buf[strescseq.len++] = ascii; - if(strescseq.len+1 >= STR_BUF_SIZ) { - term.esc = 0; - strhandle(); + if(strescseq.len + len < sizeof(strescseq.buf)) { + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + } 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(); + */ } } return; @@ -2248,7 +2291,7 @@ xtermclear(int col1, int row1, int col2, int row2) { void xclear(int x1, int y1, int x2, int y2) { XftDrawRect(xw.draw, - &dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg], + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], x1, y1, x2-x1, y2-y1); } @@ -2282,20 +2325,28 @@ xloadfont(Font *f, FcPattern *pattern) { FcPattern *match; FcResult result; - match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); + match = FcFontMatch(NULL, pattern, &result); if(!match) return 1; - if(!(f->set = XftFontOpenPattern(xw.dpy, match))) { + + if(!(f->set = FcFontSort(0, match, FcTrue, 0, &result))) { + FcPatternDestroy(match); + return 1; + } + + if(!(f->match = XftFontOpenPattern(xw.dpy, match))) { FcPatternDestroy(match); return 1; } - f->ascent = f->set->ascent; - f->descent = f->set->descent; + f->pattern = FcPatternDuplicate(pattern); + + f->ascent = f->match->ascent; + f->descent = f->match->descent; f->lbearing = 0; - f->rbearing = f->set->max_advance_width; + f->rbearing = f->match->max_advance_width; - f->height = f->set->height; + f->height = f->match->height; f->width = f->lbearing + f->rbearing; return 0; @@ -2334,6 +2385,9 @@ xloadfonts(char *fontstr, int fontsize) { } } + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + if(xloadfont(&dc.font, pattern)) die("st: can't open font %s\n", fontstr); @@ -2358,9 +2412,41 @@ xloadfonts(char *fontstr, int fontsize) { FcPatternDestroy(pattern); } +void +xunloadfonts(void) +{ + int i, ip; + + /* + * Free the loaded fonts in the font cache. This is done backwards + * from the frccur. + */ + for (i = 0, ip = frccur; i < frclen; i++, ip--) { + if (ip < 0) + ip = LEN(frc) - 1; + XftFontClose(xw.dpy, frc[ip].font); + } + frccur = -1; + frclen = 0; + + XftFontClose(xw.dpy, dc.font.match); + FcPatternDestroy(dc.font.pattern); + FcFontSetDestroy(dc.font.set); + XftFontClose(xw.dpy, dc.bfont.match); + FcPatternDestroy(dc.bfont.pattern); + FcFontSetDestroy(dc.bfont.set); + XftFontClose(xw.dpy, dc.ifont.match); + FcPatternDestroy(dc.ifont.pattern); + FcFontSetDestroy(dc.ifont.set); + XftFontClose(xw.dpy, dc.ibfont.match); + FcPatternDestroy(dc.ibfont.pattern); + FcFontSetDestroy(dc.ibfont.set); +} + void xzoom(const Arg *arg) { + xunloadfonts(); xloadfonts(usedfont, usedfontsize + arg->i); cresize(0, 0); draw(); @@ -2379,6 +2465,9 @@ xinit(void) { xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ + if (!FcInit()) + die("Could not init fontconfig.\n"); + usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); @@ -2413,7 +2502,8 @@ xinit(void) { | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; attrs.colormap = xw.cmap; - parent = opt_embed ? strtol(opt_embed, NULL, 0) : XRootWindow(xw.dpy, xw.scr); + parent = opt_embed ? strtol(opt_embed, NULL, 0) : \ + XRootWindow(xw.dpy, xw.scr); xw.win = XCreateWindow(xw.dpy, parent, xw.fx, xw.fy, xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, xw.vis, @@ -2422,18 +2512,34 @@ xinit(void) { &attrs); /* double buffering */ - if(!XdbeQueryExtension(xw.dpy, &major, &minor)) - die("Xdbe extension is not present\n"); - xw.buf = XdbeAllocateBackBufferName(xw.dpy, xw.win, XdbeCopied); + if(XdbeQueryExtension(xw.dpy, &major, &minor)) { + xw.buf = XdbeAllocateBackBufferName(xw.dpy, xw.win, + XdbeBackground); + usedbe = True; + } else { + xw.buf = xw.win; + } /* Xft rendering context */ - xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + xw.draw = XftDrawCreate(xw.dpy, xw.win, xw.vis, xw.cmap); /* input methods */ - xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if((xw.xim = XOpenIM(xw.dpy, + NULL, NULL, NULL)) == NULL) { + die("XOpenIM failed. Could not open input" + " device.\n"); + } + } + } xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + if(xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, XC_xterm); @@ -2455,13 +2561,22 @@ xinit(void) { void xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, - width = charlen * xw.cw; + width = charlen * xw.cw, xp, i; + int frp, frcflags; + int u8fl, u8fblen, u8cblen, doesexist; + char *u8c, *u8fs; + long u8char; Font *font = &dc.font; - XGlyphInfo extents; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; Colour *fg = &dc.col[base.fg], *bg = &dc.col[base.bg], *temp, revfg, revbg; XRenderColor colfg, colbg; + frcflags = FRC_NORMAL; + if(base.mode & ATTR_BOLD) { if(BETWEEN(base.fg, 0, 7)) { /* basic system colors */ @@ -2480,12 +2595,17 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { * 252 - 255 – brightest colors in greyscale */ font = &dc.bfont; + frcflags = FRC_BOLD; } - if(base.mode & ATTR_ITALIC) + if(base.mode & ATTR_ITALIC) { font = &dc.ifont; - if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) + frcflags = FRC_ITALIC; + } + if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) { font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } if(IS_SET(MODE_REVERSE)) { if(fg == &dc.col[defaultfg]) { @@ -2511,30 +2631,142 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { } } - if(base.mode & ATTR_REVERSE) - temp = fg, fg = bg, bg = temp; - - XftTextExtentsUtf8(xw.dpy, font->set, (FcChar8 *)s, bytelen, - &extents); - width = extents.xOff; + if(base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } /* Intelligent cleaning up of the borders. */ if(x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, - winy + xw.ch + (y == term.row-1)? xw.h : 0); + winy + xw.ch + ((y >= term.row-1)? xw.h : 0)); } - if(x + charlen >= term.col-1) { + if(x + charlen >= term.col) { xclear(winx + width, (y == 0)? 0 : winy, xw.w, - (y == term.row-1)? xw.h : (winy + xw.ch)); + ((y >= term.row-1)? xw.h : (winy + xw.ch))); } if(y == 0) xclear(winx, 0, winx + width, borderpx); if(y == term.row-1) xclear(winx, winy + xw.ch, winx + width, xw.h); + /* Clean up the region we want to draw to. */ XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch); + + fcsets[0] = font->set; + for (xp = winx; bytelen > 0;) { + /* + * Search for the range in the to be printed string of glyphs + * that are in the main font. Then print that range. If + * some glyph is found that is not in the font, do the + * fallback dance. + */ + u8fs = s; + u8fblen = 0; + u8fl = 0; + for (;;) { + u8c = s; + u8cblen = utf8decode(s, &u8char); + s += u8cblen; + bytelen -= u8cblen; + + doesexist = XftCharIndex(xw.dpy, font->match, u8char); + if (!doesexist || bytelen <= 0) { + if (bytelen <= 0) { + if (doesexist) { + u8fl++; + u8fblen += u8cblen; + } + } + + if (u8fl > 0) { + XftDrawStringUtf8(xw.draw, fg, + font->match, xp, + winy + font->ascent, + (FcChar8 *)u8fs, + u8fblen); + xp += font->width * u8fl; + } + break; + } + + u8fl++; + u8fblen += u8cblen; + } + if (doesexist) + break; + + frp = frccur; + /* Search the font cache. */ + for (i = 0; i < frclen; i++, frp--) { + if (frp <= 0) + frp = LEN(frc) - 1; + + if (frc[frp].c == u8char + && frc[frp].flags == frcflags) { + break; + } + } + + /* Nothing was found. */ + if (i >= frclen) { + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, u8char); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, + FcTrue); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, + FcTrue, fcpattern, &fcres); + + /* + * Overwrite or create the new cache entry + * entry. + */ + frccur++; + frclen++; + if (frccur >= LEN(frc)) + frccur = 0; + if (frclen > LEN(frc)) { + frclen = LEN(frc); + XftFontClose(xw.dpy, frc[frccur].font); + } + + frc[frccur].font = XftFontOpenPattern(xw.dpy, + fontpattern); + frc[frccur].c = u8char; + frc[frccur].flags = frcflags; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + + frp = frccur; + } + + XftDrawStringUtf8(xw.draw, fg, frc[frp].font, + xp, winy + frc[frp].font->ascent, + (FcChar8 *)u8c, u8cblen); + + xp += font->width; + } + + /* XftDrawStringUtf8(xw.draw, fg, font->set, winx, winy + font->ascent, (FcChar8 *)s, bytelen); + */ if(base.mode & ATTR_UNDERLINE) { XftDrawRect(xw.draw, fg, winx, winy + font->ascent + 1, @@ -2597,7 +2829,8 @@ draw(void) { XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}}; drawregion(0, 0, term.col, term.row); - XdbeSwapBuffers(xw.dpy, swpinfo, 1); + if(usedbe) + XdbeSwapBuffers(xw.dpy, swpinfo, 1); } void @@ -2724,7 +2957,7 @@ kmap(KeySym k, uint state) { break; } if(i == LEN(mappedkeys)) { - if((k & 0xFFFF) < 0xFF00) + if((k & 0xFFFF) < 0xFD00) return NULL; }