+ XFreePixmap(xw.dpy, xw.buf);
+ xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
+ DefaultDepth(xw.dpy, xw.scr));
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, xw.w, xw.h);
+}
+
+static inline ushort
+sixd_to_16bit(int x) {
+ return x == 0 ? 0 : 0x3737 + 0x2828 * x;
+}
+
+void
+xloadcols(void) {
+ int i;
+ XRenderColor color = { .alpha = 0xffff };
+ static bool loaded;
+ Color *cp;
+
+ if(loaded) {
+ for (cp = dc.col; cp < dc.col + LEN(dc.col); ++cp)
+ XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
+ }
+
+ /* load colors [0-15] and [256-LEN(colorname)] (config.h) */
+ for(i = 0; i < LEN(colorname); i++) {
+ if(!colorname[i])
+ continue;
+ if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, colorname[i], &dc.col[i])) {
+ die("Could not allocate color '%s'\n", colorname[i]);
+ }
+ }
+
+ /* load colors [16-231] ; same colors as xterm */
+ for(i = 16; i < 6*6*6+16; i++) {
+ color.red = sixd_to_16bit( ((i-16)/36)%6 );
+ color.green = sixd_to_16bit( ((i-16)/6) %6 );
+ color.blue = sixd_to_16bit( ((i-16)/1) %6 );
+ if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i]))
+ die("Could not allocate color %d\n", i);
+ }
+
+ /* load colors [232-255] ; grayscale */
+ for(; i < 256; i++) {
+ color.red = color.green = color.blue = 0x0808 + 0x0a0a * (i-(6*6*6+16));
+ if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i]))
+ die("Could not allocate color %d\n", i);
+ }
+ loaded = true;
+}
+
+int
+xsetcolorname(int x, const char *name) {
+ XRenderColor color = { .alpha = 0xffff };
+ Color ncolor;
+
+ if(!BETWEEN(x, 0, LEN(colorname)))
+ return 1;
+
+ if(!name) {
+ if(BETWEEN(x, 16, 16 + 215)) { /* 256 color */
+ color.red = sixd_to_16bit( ((x-16)/36)%6 );
+ color.green = sixd_to_16bit( ((x-16)/6) %6 );
+ color.blue = sixd_to_16bit( ((x-16)/1) %6 );
+ if(!XftColorAllocValue(xw.dpy, xw.vis,
+ xw.cmap, &color, &ncolor)) {
+ return 1;
+ }
+
+ XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
+ dc.col[x] = ncolor;
+ return 0;
+ } else if(BETWEEN(x, 16 + 216, 255)) { /* greyscale */
+ color.red = color.green = color.blue = \
+ 0x0808 + 0x0a0a * (x - (16 + 216));
+ if(!XftColorAllocValue(xw.dpy, xw.vis,
+ xw.cmap, &color, &ncolor)) {
+ return 1;
+ }
+
+ XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
+ dc.col[x] = ncolor;
+ return 0;
+ } else { /* system colors */
+ name = colorname[x];
+ }
+ }
+ if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, &ncolor))
+ return 1;
+
+ XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
+ dc.col[x] = ncolor;
+ return 0;
+}
+
+void
+xtermclear(int col1, int row1, int col2, int row2) {
+ XftDrawRect(xw.draw,
+ &dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg],
+ borderpx + col1 * xw.cw,
+ borderpx + row1 * xw.ch,
+ (col2-col1+1) * xw.cw,
+ (row2-row1+1) * xw.ch);
+}
+
+/*
+ * Absolute coordinates.
+ */
+void
+xclear(int x1, int y1, int x2, int y2) {
+ XftDrawRect(xw.draw,
+ &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
+ x1, y1, x2-x1, y2-y1);
+}
+
+void
+xhints(void) {
+ XClassHint class = {opt_class ? opt_class : termname, termname};
+ XWMHints wm = {.flags = InputHint, .input = 1};
+ XSizeHints *sizeh = NULL;
+
+ sizeh = XAllocSizeHints();
+
+ sizeh->flags = PSize | PResizeInc | PBaseSize;
+ sizeh->height = xw.h;
+ sizeh->width = xw.w;
+ sizeh->height_inc = xw.ch;
+ sizeh->width_inc = xw.cw;
+ sizeh->base_height = 2 * borderpx;
+ sizeh->base_width = 2 * borderpx;
+ if(xw.isfixed == True) {
+ sizeh->flags |= PMaxSize | PMinSize;
+ sizeh->min_width = sizeh->max_width = xw.w;
+ sizeh->min_height = sizeh->max_height = xw.h;
+ }
+ if(xw.gm & (XValue|YValue)) {
+ sizeh->flags |= USPosition | PWinGravity;
+ sizeh->x = xw.l;
+ sizeh->y = xw.t;
+ sizeh->win_gravity = xgeommasktogravity(xw.gm);
+ }
+
+ XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
+ &class);
+ XFree(sizeh);
+}
+
+int
+xgeommasktogravity(int mask) {
+ switch(mask & (XNegative|YNegative)) {
+ case 0:
+ return NorthWestGravity;
+ case XNegative:
+ return NorthEastGravity;
+ case YNegative:
+ return SouthWestGravity;
+ }
+ return SouthEastGravity;
+}
+
+int
+xloadfont(Font *f, FcPattern *pattern) {
+ FcPattern *match;
+ FcResult result;
+
+ match = FcFontMatch(NULL, pattern, &result);
+ if(!match)
+ return 1;
+
+ if(!(f->match = XftFontOpenPattern(xw.dpy, match))) {
+ FcPatternDestroy(match);
+ return 1;
+ }
+
+ f->set = NULL;
+ f->pattern = FcPatternDuplicate(pattern);
+
+ f->ascent = f->match->ascent;
+ f->descent = f->match->descent;
+ f->lbearing = 0;
+ f->rbearing = f->match->max_advance_width;
+
+ f->height = f->ascent + f->descent;
+ f->width = f->lbearing + f->rbearing;
+
+ return 0;
+}
+
+void
+xloadfonts(char *fontstr, double fontsize) {
+ FcPattern *pattern;
+ FcResult r_sz, r_psz;
+ double fontval;
+ float ceilf(float);
+
+ if(fontstr[0] == '-') {
+ pattern = XftXlfdParse(fontstr, False, False);
+ } else {
+ pattern = FcNameParse((FcChar8 *)fontstr);
+ }
+
+ if(!pattern)
+ die("st: can't open font %s\n", fontstr);
+
+ if(fontsize > 0) {
+ FcPatternDel(pattern, FC_PIXEL_SIZE);
+ FcPatternDel(pattern, FC_SIZE);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
+ usedfontsize = fontsize;
+ } else {
+ r_psz = FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval);
+ r_sz = FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval);
+ if(r_psz == FcResultMatch) {
+ usedfontsize = fontval;
+ } else if(r_sz == FcResultMatch) {
+ usedfontsize = -1;
+ } else {
+ /*
+ * Default font size is 12, if none given. This is to
+ * have a known usedfontsize value.
+ */
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
+ usedfontsize = 12;
+ }
+ 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);
+ usedfontsize = fontval;
+ if(fontsize == 0)
+ defaultfontsize = fontval;
+ }
+
+ /* Setting character width and height. */
+ xw.cw = ceilf(dc.font.width * cwscale);
+ xw.ch = ceilf(dc.font.height * chscale);
+
+ FcPatternDel(pattern, FC_SLANT);
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ if(xloadfont(&dc.ifont, pattern))
+ die("st: can't open font %s\n", fontstr);
+
+ FcPatternDel(pattern, FC_WEIGHT);
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ if(xloadfont(&dc.ibfont, pattern))
+ die("st: can't open font %s\n", fontstr);
+
+ FcPatternDel(pattern, FC_SLANT);
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
+ if(xloadfont(&dc.bfont, pattern))
+ die("st: can't open font %s\n", fontstr);
+
+ FcPatternDestroy(pattern);
+}
+
+int
+xloadfontset(Font *f) {
+ FcResult result;
+
+ if(!(f->set = FcFontSort(0, f->pattern, FcTrue, 0, &result)))
+ return 1;
+ return 0;
+}
+
+void
+xunloadfont(Font *f) {
+ XftFontClose(xw.dpy, f->match);
+ FcPatternDestroy(f->pattern);
+ if(f->set)
+ FcFontSetDestroy(f->set);
+}
+
+void
+xunloadfonts(void) {
+ /* Free the loaded fonts in the font cache. */
+ while(frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+ xunloadfont(&dc.font);
+ xunloadfont(&dc.bfont);
+ xunloadfont(&dc.ifont);
+ xunloadfont(&dc.ibfont);
+}
+
+void
+xzoom(const Arg *arg) {
+ Arg larg;
+
+ larg.i = usedfontsize + arg->i;
+ xzoomabs(&larg);
+}
+
+void
+xzoomabs(const Arg *arg) {
+ xunloadfonts();
+ xloadfonts(usedfont, arg->i);
+ cresize(0, 0);
+ redraw(0);
+ xhints();
+}
+
+void
+xzoomreset(const Arg *arg) {
+ Arg larg;
+
+ if(defaultfontsize > 0) {
+ larg.i = defaultfontsize;
+ xzoomabs(&larg);
+ }