From b672675ae6cbbd38136b662a7c1862fd856c6370 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Sat, 23 May 2020 12:36:29 +0200 Subject: [PATCH] Added support for fallback fonts --- config.def.h | 6 + x.c | 362 +++++++++++++-------------------------------------- 2 files changed, 97 insertions(+), 271 deletions(-) diff --git a/config.def.h b/config.def.h index 293e00c..d80110b 100644 --- a/config.def.h +++ b/config.def.h @@ -6,6 +6,12 @@ * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +/* Spare fonts */ +static char *font2[] = { +/* "DejaVuSansMono Nerd Font Mono:pixelsize=11:antialias=true:autohint=true", */ +/* "Inconsolata for Powerline:pixelsize=12:antialias=true:autohint=true", */ +}; + static int borderpx = 2; /* diff --git a/x.c b/x.c index 1dc44d6..5c247fb 100644 --- a/x.c +++ b/x.c @@ -14,12 +14,10 @@ #include #include #include - char *argv0; #include "arg.h" #include "st.h" #include "win.h" - /* types used in config.h */ typedef struct { uint mod; @@ -27,7 +25,6 @@ typedef struct { void (*func)(const Arg *); const Arg arg; } Shortcut; - typedef struct { uint mod; uint button; @@ -35,7 +32,6 @@ typedef struct { const Arg arg; uint release; } MouseShortcut; - typedef struct { KeySym k; uint mask; @@ -44,12 +40,10 @@ typedef struct { signed char appkey; /* application keypad */ signed char appcursor; /* application cursor */ } Key; - /* X modifiers */ #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 #define XK_SWITCH_MOD (1<<13) - /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); @@ -59,24 +53,19 @@ static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); static void ttysend(const Arg *); - /* config.h for applying patches and the configuration. */ #include "config.h" - /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 - /* macros */ #define IS_SET(flag) ((win.mode & (flag)) != 0) #define TRUERED(x) (((x) & 0xff0000) >> 8) #define TRUEGREEN(x) (((x) & 0xff00)) #define TRUEBLUE(x) (((x) & 0xff) << 8) - typedef XftDraw *Draw; typedef XftColor Color; typedef XftGlyphFontSpec GlyphFontSpec; - /* Purely graphic info */ typedef struct { int tw, th; /* tty width and height */ @@ -86,7 +75,6 @@ typedef struct { int mode; /* window state/mode flags */ int cursor; /* cursor style */ } TermWindow; - typedef struct { Display *dpy; Colormap cmap; @@ -108,14 +96,12 @@ typedef struct { int l, t; /* left and top offset */ int gm; /* geometry mask */ } XWindow; - typedef struct { Atom xtarget; char *primary, *clipboard; struct timespec tclick1; struct timespec tclick2; } XSelection; - /* Font structure */ #define Font Font_ typedef struct { @@ -131,7 +117,6 @@ typedef struct { FcFontSet *set; FcPattern *pattern; } Font; - /* Drawing Context */ typedef struct { Color *col; @@ -139,7 +124,6 @@ typedef struct { Font font, bfont, ifont, ibfont; GC gc; } DC; - static inline ushort sixd_to_16bit(int); static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); @@ -157,13 +141,14 @@ 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 xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); static int evcol(XEvent *); static int evrow(XEvent *); - static void expose(XEvent *); static void visibility(XEvent *); static void unmap(XEvent *); @@ -185,10 +170,8 @@ static void mousesel(XEvent *, int); static void mousereport(XEvent *); static char *kmap(KeySym, uint); static int match(uint, uint); - static void run(void); static void usage(void); - static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, [ClientMessage] = cmessage, @@ -214,13 +197,11 @@ static void (*handler[LASTEvent])(XEvent *) = { [PropertyNotify] = propnotify, [SelectionRequest] = selrequest, }; - /* Globals */ static DC dc; static XWindow xw; static XSelection xsel; static TermWindow win; - /* Font Ring Cache */ enum { FRC_NORMAL, @@ -228,13 +209,11 @@ enum { FRC_BOLD, FRC_ITALICBOLD }; - typedef struct { XftFont *font; int flags; Rune unicodep; } Fontcache; - /* Fontcache is an array now. A new font will be appended to the array. */ static Fontcache *frc = NULL; static int frclen = 0; @@ -242,7 +221,6 @@ static int frccap = 0; static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; - static char *opt_class = NULL; static char **opt_cmd = NULL; static char *opt_embed = NULL; @@ -251,83 +229,69 @@ static char *opt_io = NULL; static char *opt_line = NULL; static char *opt_name = NULL; static char *opt_title = NULL; - static int oldbutton = 3; /* button event on startup: 3 = release */ - void clipcopy(const Arg *dummy) { Atom clipboard; - free(xsel.clipboard); xsel.clipboard = NULL; - if (xsel.primary != NULL) { xsel.clipboard = xstrdup(xsel.primary); clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); } } - void clippaste(const Arg *dummy) { Atom clipboard; - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, xw.win, CurrentTime); } - void selpaste(const Arg *dummy) { XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, xw.win, CurrentTime); } - void numlock(const Arg *dummy) { win.mode ^= MODE_NUMLOCK; } - void zoom(const Arg *arg) { Arg larg; - larg.f = usedfontsize + arg->f; zoomabs(&larg); } - void zoomabs(const Arg *arg) { xunloadfonts(); xloadfonts(usedfont, arg->f); + xloadsparefonts(); cresize(0, 0); redraw(); xhints(); } - void zoomreset(const Arg *arg) { Arg larg; - if (defaultfontsize > 0) { larg.f = defaultfontsize; zoomabs(&larg); } } - void ttysend(const Arg *arg) { ttywrite(arg->s, strlen(arg->s), 1); } - int evcol(XEvent *e) { @@ -335,7 +299,6 @@ evcol(XEvent *e) LIMIT(x, 0, win.tw - 1); return x / win.cw; } - int evrow(XEvent *e) { @@ -343,13 +306,11 @@ evrow(XEvent *e) LIMIT(y, 0, win.th - 1); return y / win.ch; } - void mousesel(XEvent *e, int done) { int type, seltype = SEL_REGULAR; uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); - for (type = 1; type < LEN(selmasks); ++type) { if (match(selmasks[type], state)) { seltype = type; @@ -360,7 +321,6 @@ mousesel(XEvent *e, int done) if (done) setsel(getsel(), e->xbutton.time); } - void mousereport(XEvent *e) { @@ -368,7 +328,6 @@ mousereport(XEvent *e) button = e->xbutton.button, state = e->xbutton.state; char buf[40]; static int ox, oy; - /* from urxvt */ if (e->xbutton.type == MotionNotify) { if (x == ox && y == oy) @@ -378,7 +337,6 @@ mousereport(XEvent *e) /* MOUSE_MOTION: no reporting if no button is pressed */ if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) return; - button = oldbutton + 32; ox = x; oy = y; @@ -403,13 +361,11 @@ mousereport(XEvent *e) return; } } - if (!IS_SET(MODE_MOUSEX10)) { button += ((state & ShiftMask ) ? 4 : 0) + ((state & Mod4Mask ) ? 8 : 0) + ((state & ControlMask) ? 16 : 0); } - if (IS_SET(MODE_MOUSESGR)) { len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", button, x+1, y+1, @@ -420,10 +376,8 @@ mousereport(XEvent *e) } else { return; } - ttywrite(buf, len, 0); } - uint buttonmask(uint button) { @@ -434,15 +388,12 @@ buttonmask(uint button) : button == Button5 ? Button5Mask : 0; } - int mouseaction(XEvent *e, uint release) { MouseShortcut *ms; - /* ignore Buttonmask for Button - it's set on release */ uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (ms->release == release && ms->button == e->xbutton.button && @@ -452,24 +403,19 @@ mouseaction(XEvent *e, uint release) return 1; } } - return 0; } - void bpress(XEvent *e) { struct timespec now; int snap; - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } - if (mouseaction(e, 0)) return; - if (e->xbutton.button == Button1) { /* * If the user clicks below predefined timeouts specific @@ -485,17 +431,14 @@ bpress(XEvent *e) } xsel.tclick2 = xsel.tclick1; xsel.tclick1 = now; - selstart(evcol(e), evrow(e), snap); } } - void propnotify(XEvent *e) { XPropertyEvent *xpev; Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - xpev = &e->xproperty; if (xpev->state == PropertyNewValue && (xpev->atom == XA_PRIMARY || @@ -503,7 +446,6 @@ propnotify(XEvent *e) selnotify(e); } } - void selnotify(XEvent *e) { @@ -511,18 +453,14 @@ selnotify(XEvent *e) int format; uchar *data, *last, *repl; Atom type, incratom, property = None; - incratom = XInternAtom(xw.dpy, "INCR", 0); - ofs = 0; if (e->type == SelectionNotify) property = e->xselection.property; else if (e->type == PropertyNotify) property = e->xproperty.atom; - if (property == None) return; - do { if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, BUFSIZ/4, False, AnyPropertyType, @@ -531,7 +469,6 @@ selnotify(XEvent *e) fprintf(stderr, "Clipboard allocation failed\n"); return; } - if (e->type == PropertyNotify && nitems == 0 && rem == 0) { /* * If there is some PropertyNotify with no data, then @@ -543,7 +480,6 @@ selnotify(XEvent *e) XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } - if (type == incratom) { /* * Activate the PropertyNotify events so we receive @@ -553,14 +489,12 @@ selnotify(XEvent *e) MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); - /* * Deleting the property is the transfer start signal. */ XDeleteProperty(xw.dpy, xw.win, (int)property); continue; } - /* * As seen in getsel: * Line endings are inconsistent in the terminal and GUI world @@ -573,7 +507,6 @@ selnotify(XEvent *e) while ((repl = memchr(repl, '\n', last - repl))) { *repl++ = '\r'; } - if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) ttywrite("\033[200~", 6, 0); ttywrite((char *)data, nitems * format / 8, 1); @@ -583,26 +516,22 @@ selnotify(XEvent *e) /* number of 32-bit chunks returned */ ofs += nitems * format / 32; } while (rem > 0); - /* * Deleting the property again tells the selection owner to send the * next data chunk in the property. */ XDeleteProperty(xw.dpy, xw.win, (int)property); } - void xclipcopy(void) { clipcopy(NULL); } - void selclear_(XEvent *e) { selclear(); } - void selrequest(XEvent *e) { @@ -610,7 +539,6 @@ selrequest(XEvent *e) XSelectionEvent xev; Atom xa_targets, string, clipboard; char *seltext; - xsre = (XSelectionRequestEvent *) e; xev.type = SelectionNotify; xev.requestor = xsre->requestor; @@ -619,10 +547,8 @@ selrequest(XEvent *e) xev.time = xsre->time; if (xsre->property == None) xsre->property = xsre->target; - /* reject */ xev.property = None; - xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); if (xsre->target == xa_targets) { /* respond with the supported type */ @@ -655,32 +581,26 @@ selrequest(XEvent *e) xev.property = xsre->property; } } - /* all done, send a notification to the listener */ if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) fprintf(stderr, "Error sending SelectionNotify event\n"); } - void setsel(char *str, Time t) { if (!str) return; - free(xsel.primary); xsel.primary = str; - XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) selclear(); } - void xsetsel(char *str) { setsel(str, CurrentTime); } - void brelease(XEvent *e) { @@ -688,13 +608,11 @@ brelease(XEvent *e) mousereport(e); return; } - if (mouseaction(e, 1)) return; if (e->xbutton.button == Button1) mousesel(e, 1); } - void bmotion(XEvent *e) { @@ -702,57 +620,46 @@ bmotion(XEvent *e) mousereport(e); return; } - mousesel(e, 0); } - void cresize(int width, int height) { int col, row; - if (width != 0) win.w = width; if (height != 0) win.h = height; - col = (win.w - 2 * borderpx) / win.cw; row = (win.h - 2 * borderpx) / win.ch; col = MAX(1, col); row = MAX(1, row); - tresize(col, row); xresize(col, row); ttyresize(win.tw, win.th); } - void xresize(int col, int row) { win.tw = col * win.cw; win.th = row * win.ch; - XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, DefaultDepth(xw.dpy, xw.scr)); XftDrawChange(xw.draw, xw.buf); xclear(0, 0, win.w, win.h); - /* resize to new width */ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); } - ushort sixd_to_16bit(int x) { return x == 0 ? 0 : 0x3737 + 0x2828 * x; } - int xloadcolor(int i, const char *name, Color *ncolor) { XRenderColor color = { .alpha = 0xffff }; - if (!name) { if (BETWEEN(i, 16, 255)) { /* 256 color */ if (i < 6*6*6+16) { /* same colors as xterm */ @@ -768,17 +675,14 @@ xloadcolor(int i, const char *name, Color *ncolor) } else name = colorname[i]; } - return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); } - void xloadcols(void) { int i; static int loaded; Color *cp; - if (loaded) { for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); @@ -786,7 +690,6 @@ xloadcols(void) dc.collen = MAX(LEN(colorname), 256); dc.col = xmalloc(dc.collen * sizeof(Color)); } - for (i = 0; i < dc.collen; i++) if (!xloadcolor(i, NULL, &dc.col[i])) { if (colorname[i]) @@ -796,24 +699,18 @@ xloadcols(void) } loaded = 1; } - int xsetcolorname(int x, const char *name) { Color ncolor; - if (!BETWEEN(x, 0, dc.collen)) return 1; - if (!xloadcolor(x, name, &ncolor)) return 1; - XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); dc.col[x] = ncolor; - return 0; } - /* * Absolute coordinates. */ @@ -824,7 +721,6 @@ xclear(int x1, int y1, int x2, int y2) &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], x1, y1, x2-x1, y2-y1); } - void xhints(void) { @@ -832,9 +728,7 @@ xhints(void) opt_class ? opt_class : termname}; XWMHints wm = {.flags = InputHint, .input = 1}; XSizeHints *sizeh; - sizeh = XAllocSizeHints(); - sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; @@ -855,12 +749,10 @@ xhints(void) 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) { @@ -872,10 +764,8 @@ xgeommasktogravity(int mask) case YNegative: return SouthWestGravity; } - return SouthEastGravity; } - int xloadfont(Font *f, FcPattern *pattern) { @@ -884,7 +774,6 @@ xloadfont(Font *f, FcPattern *pattern) FcResult result; XGlyphInfo extents; int wantattr, haveattr; - /* * Manually configure instead of calling XftMatchFont * so that we can use the configured pattern for @@ -893,22 +782,18 @@ xloadfont(Font *f, FcPattern *pattern) configured = FcPatternDuplicate(pattern); if (!configured) return 1; - FcConfigSubstitute(NULL, configured, FcMatchPattern); XftDefaultSubstitute(xw.dpy, xw.scr, configured); - match = FcFontMatch(NULL, configured, &result); if (!match) { FcPatternDestroy(configured); return 1; } - if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { FcPatternDestroy(configured); FcPatternDestroy(match); return 1; } - if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == XftResultMatch)) { /* @@ -921,7 +806,6 @@ xloadfont(Font *f, FcPattern *pattern) fputs("font slant does not match\n", stderr); } } - if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == XftResultMatch)) { if ((XftPatternGetInteger(f->match->pattern, "weight", 0, @@ -930,39 +814,30 @@ xloadfont(Font *f, FcPattern *pattern) fputs("font weight does not match\n", stderr); } } - XftTextExtentsUtf8(xw.dpy, f->match, (const FcChar8 *) ascii_printable, strlen(ascii_printable), &extents); - f->set = NULL; f->pattern = configured; - 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 = DIVCEIL(extents.xOff, strlen(ascii_printable)); - return 0; } - void xloadfonts(char *fontstr, double fontsize) { FcPattern *pattern; double fontval; - if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); else pattern = FcNameParse((FcChar8 *)fontstr); - if (!pattern) die("can't open font %s\n", fontstr); - if (fontsize > 1) { FcPatternDel(pattern, FC_PIXEL_SIZE); FcPatternDel(pattern, FC_SIZE); @@ -985,10 +860,8 @@ xloadfonts(char *fontstr, double fontsize) } defaultfontsize = usedfontsize; } - if (xloadfont(&dc.font, pattern)) die("can't open font %s\n", fontstr); - if (usedfontsize < 0) { FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval); @@ -996,29 +869,109 @@ xloadfonts(char *fontstr, double fontsize) if (fontsize == 0) defaultfontsize = fontval; } - /* Setting character width and height. */ win.cw = ceilf(dc.font.width * cwscale); win.ch = ceilf(dc.font.height * chscale); - FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadfont(&dc.ifont, pattern)) die("can't open font %s\n", fontstr); - FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadfont(&dc.ibfont, pattern)) die("can't open font %s\n", fontstr); - FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadfont(&dc.bfont, pattern)) die("can't open font %s\n", fontstr); - FcPatternDestroy(pattern); } - +int +xloadsparefont(FcPattern *pattern, int flags) +{ + FcPattern *match; + FcResult result; + + match = FcFontMatch(NULL, pattern, &result); + if (!match) { + return 1; + } + if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(match); + return 1; + } + frc[frclen].flags = flags; + /* Believe U+0000 glyph will present in each default font */ + frc[frclen].unicodep = 0; + frclen++; + return 0; +} +void +xloadsparefonts(void) +{ + FcPattern *pattern; + double sizeshift, fontval; + int fc; + char **fp; + if (frclen != 0) + die("can't embed spare fonts. cache isn't empty"); + /* Calculate count of spare fonts */ + fc = sizeof(font2) / sizeof(*font2); + if (fc == 0) + return; + /* Allocate memory for cache entries. */ + if (frccap < 4 * fc) { + frccap += 4 * fc - frccap; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + for (fp = font2; fp - font2 < fc; ++fp) { + + if (**fp == '-') + pattern = XftXlfdParse(*fp, False, False); + else + pattern = FcNameParse((FcChar8 *)*fp); + + if (!pattern) + die("can't open spare font %s\n", *fp); + + if (defaultfontsize > 0) { + sizeshift = usedfontsize - defaultfontsize; + if (sizeshift != 0 && + FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + fontval += sizeshift; + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); + } + } + + FcPatternAddBool(pattern, FC_SCALABLE, 1); + + FcConfigSubstitute(NULL, pattern, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, pattern); + + if (xloadsparefont(pattern, FRC_NORMAL)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadsparefont(pattern, FRC_ITALIC)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadsparefont(pattern, FRC_ITALICBOLD)) + die("can't open spare font %s\n", *fp); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadsparefont(pattern, FRC_BOLD)) + die("can't open spare font %s\n", *fp); + + FcPatternDestroy(pattern); + } +} void xunloadfont(Font *f) { @@ -1027,37 +980,30 @@ xunloadfont(Font *f) 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); } - int ximopen(Display *dpy) { XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; - xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); if (xw.ime.xim == NULL) return 0; - if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) fprintf(stderr, "XSetIMValues: " "Could not set XNDestroyCallback.\n"); - xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, NULL); - if (xw.ime.xic == NULL) { xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, @@ -1067,10 +1013,8 @@ ximopen(Display *dpy) } if (xw.ime.xic == NULL) fprintf(stderr, "XCreateIC: Could not create input context.\n"); - return 1; } - void ximinstantiate(Display *dpy, XPointer client, XPointer call) { @@ -1078,7 +1022,6 @@ ximinstantiate(Display *dpy, XPointer client, XPointer call) XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, NULL); } - void ximdestroy(XIM xim, XPointer client, XPointer call) { @@ -1087,14 +1030,12 @@ ximdestroy(XIM xim, XPointer client, XPointer call) ximinstantiate, NULL); XFree(xw.ime.spotlist); } - int xicdestroy(XIC xim, XPointer client, XPointer call) { xw.ime.xic = NULL; return 1; } - void xinit(int cols, int rows) { @@ -1103,23 +1044,20 @@ xinit(int cols, int rows) Window parent; pid_t thispid = getpid(); XColor xmousefg, xmousebg; - if (!(xw.dpy = XOpenDisplay(NULL))) die("can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); 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); - + /* spare fonts */ + xloadsparefonts(); /* colors */ xw.cmap = XDefaultColormap(xw.dpy, xw.scr); xloadcols(); - /* adjust fixed window geometry */ win.w = 2 * borderpx + cols * win.cw; win.h = 2 * borderpx + rows * win.ch; @@ -1127,7 +1065,6 @@ xinit(int cols, int rows) xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; if (xw.gm & YNegative) xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; - /* Events */ xw.attrs.background_pixel = dc.col[defaultbg].pixel; xw.attrs.border_pixel = dc.col[defaultbg].pixel; @@ -1136,14 +1073,12 @@ xinit(int cols, int rows) | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; xw.attrs.colormap = xw.cmap; - if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) parent = XRootWindow(xw.dpy, xw.scr); xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &xw.attrs); - memset(&gcvalues, 0, sizeof(gcvalues)); gcvalues.graphics_exposures = False; dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, @@ -1152,52 +1087,41 @@ xinit(int cols, int rows) DefaultDepth(xw.dpy, xw.scr)); XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); - /* font spec buffer */ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); - /* Xft rendering context */ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); - /* input methods */ if (!ximopen(xw.dpy)) { XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, NULL); } - /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, mouseshape); XDefineCursor(xw.dpy, xw.win, cursor); - if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { xmousefg.red = 0xffff; xmousefg.green = 0xffff; xmousefg.blue = 0xffff; } - if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { xmousebg.red = 0x0000; xmousebg.green = 0x0000; xmousebg.blue = 0x0000; } - XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); - xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); - xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, PropModeReplace, (uchar *)&thispid, 1); - win.mode = MODE_NUMLOCK; resettitle(); xhints(); XMapWindow(xw.dpy, xw.win); XSync(xw.dpy, False); - clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); xsel.primary = NULL; @@ -1206,7 +1130,6 @@ xinit(int cols, int rows) if (xsel.xtarget == None) xsel.xtarget = XA_STRING; } - int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) { @@ -1222,16 +1145,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x FcFontSet *fcsets[] = { NULL }; FcCharSet *fccharset; int i, f, numspecs = 0; - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { /* Fetch rune and mode for current glyph. */ rune = glyphs[i].u; mode = glyphs[i].mode; - /* Skip dummy wide-character spacing. */ if (mode == ATTR_WDUMMY) continue; - /* Determine font for glyph if different from previous glyph. */ if (prevmode != mode) { prevmode = mode; @@ -1250,7 +1170,6 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x } yp = winy + font->ascent; } - /* Lookup character index with default font. */ glyphidx = XftCharIndex(xw.dpy, font->match, rune); if (glyphidx) { @@ -1262,7 +1181,6 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x numspecs++; continue; } - /* Fallback on font cache, search the font cache for match. */ for (f = 0; f < frclen; f++) { glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); @@ -1275,14 +1193,12 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x break; } } - /* Nothing was found. Use fontconfig to find matching font. */ if (f >= frclen) { if (!font->set) font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); fcsets[0] = font->set; - /* * Nothing was found in the cache. Now use * some dozen of Fontconfig calls to get the @@ -1292,25 +1208,20 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x */ fcpattern = FcPatternDuplicate(font->pattern); fccharset = FcCharSetCreate(); - FcCharSetAddChar(fccharset, rune); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, 1); - FcConfigSubstitute(0, fcpattern, FcMatchPattern); FcDefaultSubstitute(fcpattern); - fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - /* Allocate memory for the new cache entry. */ if (frclen >= frccap) { frccap += 16; frc = xrealloc(frc, frccap * sizeof(Fontcache)); } - frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); if (!frc[frclen].font) @@ -1318,16 +1229,12 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x strerror(errno)); frc[frclen].flags = frcflags; frc[frclen].unicodep = rune; - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); - f = frclen; frclen++; - FcPatternDestroy(fcpattern); FcCharSetDestroy(fccharset); } - specs[numspecs].font = frc[f].font; specs[numspecs].glyph = glyphidx; specs[numspecs].x = (short)xp; @@ -1335,10 +1242,8 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x xp += runewidth; numspecs++; } - return numspecs; } - void xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) { @@ -1348,7 +1253,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; XRenderColor colfg, colbg; XRectangle r; - /* 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) @@ -1357,7 +1261,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i (base.mode & ATTR_BOLD && dc.bfont.badweight)) { base.fg = defaultattr; } - if (IS_TRUECOL(base.fg)) { colfg.alpha = 0xffff; colfg.red = TRUERED(base.fg); @@ -1368,7 +1271,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i } else { fg = &dc.col[base.fg]; } - if (IS_TRUECOL(base.bg)) { colbg.alpha = 0xffff; colbg.green = TRUEGREEN(base.bg); @@ -1379,11 +1281,9 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i } else { bg = &dc.col[base.bg]; } - /* Change basic system colors [0-7] to bright system colors [8-15] */ if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) fg = &dc.col[base.fg + 8]; - if (IS_SET(MODE_REVERSE)) { if (fg == &dc.col[defaultfg]) { fg = &dc.col[defaultbg]; @@ -1396,7 +1296,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i &revfg); fg = &revfg; } - if (bg == &dc.col[defaultbg]) { bg = &dc.col[defaultfg]; } else { @@ -1409,7 +1308,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i bg = &revbg; } } - if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { colfg.red = fg->color.red / 2; colfg.green = fg->color.green / 2; @@ -1418,19 +1316,15 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); fg = &revfg; } - if (base.mode & ATTR_REVERSE) { temp = fg; fg = bg; bg = temp; } - if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) fg = bg; - if (base.mode & ATTR_INVISIBLE) fg = bg; - /* Intelligent cleaning up of the borders. */ if (x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, @@ -1445,63 +1339,50 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i xclear(winx, 0, winx + width, borderpx); if (winy + win.ch >= borderpx + win.th) xclear(winx, winy + win.ch, winx + width, win.h); - /* Clean up the region we want to draw to. */ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); - /* Set the clip region because Xft is sometimes dirty. */ r.x = 0; r.y = 0; r.height = win.ch; r.width = width; XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - /* 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 + 1, width, 1); } - if (base.mode & ATTR_STRUCK) { XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, width, 1); } - /* Reset clip to none. */ XftDrawSetClip(xw.draw, 0); } - void xdrawglyph(Glyph g, int x, int y) { int numspecs; XftGlyphFontSpec spec; - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); xdrawglyphfontspecs(&spec, g, numspecs, x, y); } - void xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { Color drawcol; - /* remove the old cursor */ if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; xdrawglyph(og, ox, oy); - if (IS_SET(MODE_HIDE)) return; - /* * Select the right color for the right mode. */ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; - if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; g.bg = defaultfg; @@ -1522,7 +1403,6 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) } drawcol = dc.col[g.bg]; } - /* draw the new one */ if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { @@ -1569,42 +1449,35 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) win.cw, 1); } } - void xsetenv(void) { char buf[sizeof(long) * 8 + 1]; - snprintf(buf, sizeof(buf), "%lu", xw.win); setenv("WINDOWID", buf, 1); } - void xsettitle(char *p) { XTextProperty prop; DEFAULT(p, opt_title); - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop); XSetWMName(xw.dpy, xw.win, &prop); XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); XFree(prop.value); } - int xstartdraw(void) { return IS_SET(MODE_VISIBLE); } - void xdrawline(Line line, int x1, int y1, int x2) { int i, x, ox, numspecs; Glyph base, new; XftGlyphFontSpec *specs = xw.specbuf; - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); i = ox = 0; for (x = x1; x < x2 && i < numspecs; x++) { @@ -1628,7 +1501,6 @@ xdrawline(Line line, int x1, int y1, int x2) if (i > 0) xdrawglyphfontspecs(specs, base, i, ox, y1); } - void xfinishdraw(void) { @@ -1638,46 +1510,37 @@ xfinishdraw(void) dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg].pixel); } - void xximspot(int x, int y) { if (xw.ime.xic == NULL) return; - xw.ime.spot.x = borderpx + x * win.cw; xw.ime.spot.y = borderpx + (y + 1) * win.ch; - XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); } - void expose(XEvent *ev) { redraw(); } - void visibility(XEvent *ev) { XVisibilityEvent *e = &ev->xvisibility; - MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); } - void unmap(XEvent *ev) { win.mode &= ~MODE_VISIBLE; } - void xsetpointermotion(int set) { MODBIT(xw.attrs.event_mask, set, PointerMotionMask); XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } - void xsetmode(int set, unsigned int flags) { @@ -1686,7 +1549,6 @@ xsetmode(int set, unsigned int flags) if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) redraw(); } - int xsetcursor(int cursor) { @@ -1696,17 +1558,14 @@ xsetcursor(int cursor) win.cursor = cursor; return 0; } - void xseturgency(int add) { XWMHints *h = XGetWMHints(xw.dpy, xw.win); - MODBIT(h->flags, add, XUrgencyHint); XSetWMHints(xw.dpy, xw.win, h); XFree(h); } - void xbell(void) { @@ -1715,15 +1574,12 @@ xbell(void) if (bellvolume) XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); } - void focus(XEvent *ev) { XFocusChangeEvent *e = &ev->xfocus; - if (e->mode == NotifyGrab) return; - if (ev->type == FocusIn) { if (xw.ime.xic) XSetICFocus(xw.ime.xic); @@ -1739,19 +1595,16 @@ focus(XEvent *ev) ttywrite("\033[O", 3, 0); } } - int match(uint mask, uint state) { return mask == XK_ANY_MOD || mask == (state & ~ignoremod); } - char* kmap(KeySym k, uint state) { Key *kp; int i; - /* Check for mapped keys out of X11 function keys. */ for (i = 0; i < LEN(mappedkeys); i++) { if (mappedkeys[i] == k) @@ -1761,28 +1614,21 @@ kmap(KeySym k, uint state) if ((k & 0xFFFF) < 0xFD00) return NULL; } - for (kp = key; kp < key + LEN(key); kp++) { if (kp->k != k) continue; - if (!match(kp->mask, state)) continue; - if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) continue; if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) continue; - if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) continue; - return kp->s; } - return NULL; } - void kpress(XEvent *ev) { @@ -1793,10 +1639,8 @@ kpress(XEvent *ev) Rune c; Status status; Shortcut *bp; - if (IS_SET(MODE_KBDLOCK)) return; - if (xw.ime.xic) len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); else @@ -1808,13 +1652,11 @@ kpress(XEvent *ev) return; } } - /* 2. custom keys from config.h */ if ((customkey = kmap(ksym, e->state))) { ttywrite(customkey, strlen(customkey), 1); return; } - /* 3. composed string from input method */ if (len == 0) return; @@ -1832,7 +1674,6 @@ kpress(XEvent *ev) } ttywrite(buf, len, 1); } - void cmessage(XEvent *e) { @@ -1852,16 +1693,13 @@ cmessage(XEvent *e) exit(0); } } - void resize(XEvent *e) { if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) return; - cresize(e->xconfigure.width, e->xconfigure.height); } - void run(void) { @@ -1871,7 +1709,6 @@ run(void) int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; struct timespec seltv, *tv, now, lastblink, trigger; double timeout; - /* Waiting for window mapping */ do { XNextEvent(xw.dpy, &ev); @@ -1887,32 +1724,25 @@ run(void) h = ev.xconfigure.height; } } while (ev.type != MapNotify); - ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); cresize(w, h); - for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { FD_ZERO(&rfd); FD_SET(ttyfd, &rfd); FD_SET(xfd, &rfd); - if (XPending(xw.dpy)) timeout = 0; /* existing events might not set xfd */ - seltv.tv_sec = timeout / 1E3; seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); tv = timeout >= 0 ? &seltv : NULL; - if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if (errno == EINTR) continue; die("select failed: %s\n", strerror(errno)); } clock_gettime(CLOCK_MONOTONIC, &now); - if (FD_ISSET(ttyfd, &rfd)) ttyread(); - xev = 0; while (XPending(xw.dpy)) { xev = 1; @@ -1922,7 +1752,6 @@ run(void) if (handler[ev.type]) (handler[ev.type])(&ev); } - /* * To reduce flicker and tearing, when new content or event * triggers drawing, we first wait a bit to ensure we got @@ -1944,7 +1773,6 @@ run(void) if (timeout > 0) continue; /* we have time, try to find idle */ } - /* idle detected or maxlatency exhausted -> draw */ timeout = -1; if (blinktimeout && tattrset(ATTR_BLINK)) { @@ -1958,13 +1786,11 @@ run(void) timeout = blinktimeout; } } - draw(); XFlush(xw.dpy); drawing = 0; } } - void usage(void) { @@ -1977,14 +1803,12 @@ usage(void) " [-T title] [-t title] [-w windowid] -l line" " [stty_args ...]\n", argv0, argv0); } - int main(int argc, char *argv[]) { xw.l = xw.t = 0; xw.isfixed = False; win.cursor = cursorshape; - ARGBEGIN { case 'a': allowaltscreen = 0; @@ -2028,14 +1852,11 @@ main(int argc, char *argv[]) default: usage(); } ARGEND; - run: if (argc > 0) /* eat all remaining arguments */ opt_cmd = argv; - if (!opt_title) opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; - setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); cols = MAX(cols, 1); @@ -2045,6 +1866,5 @@ run: xsetenv(); selinit(); run(); - return 0; }