4.9 update

This commit is contained in:
Luke Smith 2019-02-08 16:25:44 -05:00
parent e60466c0a1
commit 274d4f9a7b
No known key found for this signature in database
GPG key ID: 4C50B54A911F6252
7 changed files with 179 additions and 108 deletions

12
LICENSE
View file

@ -1,13 +1,15 @@
MIT/X Consortium License MIT/X Consortium License
© 2006-2014 Anselm R Garbe <anselm@garbe.us> © 2006-2019 Anselm R Garbe <anselm@garbe.ca>
© 2010-2012 Connor Lane Smith <cls@lubutu.com> © 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com>
© 2006-2007 Michał Janeczek <janeczek@gmail.com>
© 2007 Kris Maglione <jg@suckless.org>
© 2009 Gottox <gottox@s01.de> © 2009 Gottox <gottox@s01.de>
© 2009 Markus Schnalke <meillo@marmaro.de> © 2009 Markus Schnalke <meillo@marmaro.de>
© 2009 Evan Gates <evan.gates@gmail.com> © 2009 Evan Gates <evan.gates@gmail.com>
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com> © 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2006-2007 Michał Janeczek <janeczek at gmail dot com> © 2014-2019 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org> © 2015-2018 Quentin Rameau <quinq@fifth.space>
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),

View file

@ -4,71 +4,61 @@
include config.mk include config.mk
SRC = drw.c dmenu.c stest.c util.c SRC = drw.c dmenu.c stest.c util.c
OBJ = ${SRC:.c=.o} OBJ = $(SRC:.c=.o)
all: options dmenu stest all: options dmenu stest
options: options:
@echo dmenu build options: @echo dmenu build options:
@echo "CFLAGS = ${CFLAGS}" @echo "CFLAGS = $(CFLAGS)"
@echo "LDFLAGS = ${LDFLAGS}" @echo "LDFLAGS = $(LDFLAGS)"
@echo "CC = ${CC}" @echo "CC = $(CC)"
.c.o: .c.o:
@echo CC $< $(CC) -c $(CFLAGS) $<
@${CC} -c ${CFLAGS} $<
config.h: config.h:
@echo creating $@ from config.def.h cp config.def.h $@
@cp config.def.h $@
${OBJ}: arg.h config.h config.mk drw.h $(OBJ): arg.h config.h config.mk drw.h
dmenu: dmenu.o drw.o util.o dmenu: dmenu.o drw.o util.o
@echo CC -o $@ $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS)
@${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS}
stest: stest.o stest: stest.o
@echo CC -o $@ $(CC) -o $@ stest.o $(LDFLAGS)
@${CC} -o $@ stest.o ${LDFLAGS}
clean: clean:
@echo cleaning rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz
@rm -f dmenu stest ${OBJ} dmenu-${VERSION}.tar.gz
dist: clean dist: clean
@echo creating dist tarball mkdir -p dmenu-$(VERSION)
@mkdir -p dmenu-${VERSION} cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\
@cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1 \ drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\
drw.h util.h dmenu_path dmenu_run stest.1 ${SRC} \ dmenu-$(VERSION)
dmenu-${VERSION} tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION)
@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} gzip dmenu-$(VERSION).tar
@gzip dmenu-${VERSION}.tar rm -rf dmenu-$(VERSION)
@rm -rf dmenu-${VERSION}
install: all install: all
@echo installing executables to ${DESTDIR}${PREFIX}/bin mkdir -p $(DESTDIR)$(PREFIX)/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin
@cp -f dmenu dmenu_path dmenu_run stest ${DESTDIR}${PREFIX}/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run chmod 755 $(DESTDIR)$(PREFIX)/bin/stest
@chmod 755 ${DESTDIR}${PREFIX}/bin/stest mkdir -p $(DESTDIR)$(MANPREFIX)/man1
@echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1 sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1
@sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
@sed "s/VERSION/${VERSION}/g" < stest.1 > ${DESTDIR}${MANPREFIX}/man1/stest.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/stest.1
uninstall: uninstall:
@echo removing executables from ${DESTDIR}${PREFIX}/bin rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu $(DESTDIR)$(PREFIX)/bin/dmenu_path\
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path $(DESTDIR)$(PREFIX)/bin/dmenu_run\
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run $(DESTDIR)$(PREFIX)/bin/stest\
@rm -f ${DESTDIR}${PREFIX}/bin/stest $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 $(DESTDIR)$(MANPREFIX)/man1/stest.1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@rm -f ${DESTDIR}${MANPREFIX}/man1/stest.1
.PHONY: all options clean dist install uninstall .PHONY: all options clean dist install uninstall

View file

@ -1,9 +1,9 @@
# dmenu version # dmenu version
VERSION = 4.7 VERSION = 4.9
# paths # paths
PREFIX = /usr/local PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man MANPREFIX = $(PREFIX)/share/man
X11INC = /usr/X11R6/include X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib X11LIB = /usr/X11R6/lib
@ -16,16 +16,16 @@ XINERAMAFLAGS = -DXINERAMA
FREETYPELIBS = -lfontconfig -lXft FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2 FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment) # OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2 #FREETYPEINC = $(X11INC)/freetype2
# includes and libs # includes and libs
INCS = -I${X11INC} -I${FREETYPEINC} INCS = -I$(X11INC) -I$(FREETYPEINC)
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
# flags # flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS)
LDFLAGS = -s ${LIBS} LDFLAGS = $(LIBS)
# compiler and linker # compiler and linker
CC = cc CC = cc

68
dmenu.1
View file

@ -41,8 +41,8 @@ which lists programs in the user's $PATH and runs the result in their $SHELL.
dmenu appears at the bottom of the screen. dmenu appears at the bottom of the screen.
.TP .TP
.B \-f .B \-f
dmenu grabs the keyboard before reading stdin. This is faster, but will lock up dmenu grabs the keyboard before reading stdin if not reading from a tty. This
X until stdin reaches end\-of\-file. is faster, but will lock up X until stdin reaches end\-of\-file.
.TP .TP
.B \-i .B \-i
dmenu matches menu items case insensitively. dmenu matches menu items case insensitively.
@ -100,82 +100,94 @@ Confirm input. Prints the input text to stdout and exits, returning success.
.B Escape .B Escape
Exit without selecting an item, returning failure. Exit without selecting an item, returning failure.
.TP .TP
C\-a .B Ctrl-Left
Move cursor to the start of the current word
.TP
.B Ctrl-Right
Move cursor to the end of the current word
.TP
.B C\-a
Home Home
.TP .TP
C\-b .B C\-b
Left Left
.TP .TP
C\-c .B C\-c
Escape Escape
.TP .TP
C\-d .B C\-d
Delete Delete
.TP .TP
C\-e .B C\-e
End End
.TP .TP
C\-f .B C\-f
Right Right
.TP .TP
C\-g .B C\-g
Escape Escape
.TP .TP
C\-h .B C\-h
Backspace Backspace
.TP .TP
C\-i .B C\-i
Tab Tab
.TP .TP
C\-j .B C\-j
Return Return
.TP .TP
C\-J .B C\-J
Shift-Return Shift-Return
.TP .TP
C\-k .B C\-k
Delete line right Delete line right
.TP .TP
C\-m .B C\-m
Return Return
.TP .TP
C\-M .B C\-M
Shift-Return Shift-Return
.TP .TP
C\-n .B C\-n
Down Down
.TP .TP
C\-p .B C\-p
Up Up
.TP .TP
C\-u .B C\-u
Delete line left Delete line left
.TP .TP
C\-w .B C\-w
Delete word left Delete word left
.TP .TP
C\-y .B C\-y
Paste from primary X selection Paste from primary X selection
.TP .TP
C\-Y .B C\-Y
Paste from X clipboard Paste from X clipboard
.TP .TP
M\-g .B M\-b
Move cursor to the start of the current word
.TP
.B M\-f
Move cursor to the end of the current word
.TP
.B M\-g
Home Home
.TP .TP
M\-G .B M\-G
End End
.TP .TP
M\-h .B M\-h
Up Up
.TP .TP
M\-j .B M\-j
Page down Page down
.TP .TP
M\-k .B M\-k
Page up Page up
.TP .TP
M\-l .B M\-l
Down Down
.SH SEE ALSO .SH SEE ALSO
.IR dwm (1), .IR dwm (1),

83
dmenu.c
View file

@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -144,7 +145,7 @@ drawmenu(void)
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL); curpos = TEXTW(text) - TEXTW(&text[cursor]);
if ((curpos += lrpad / 2 - 1) < w) { if ((curpos += lrpad / 2 - 1) < w) {
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
@ -287,18 +288,42 @@ nextrune(int inc)
return n; return n;
} }
static void
movewordedge(int dir)
{
if (dir < 0) { /* move cursor to the start of the word*/
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
} else { /* move cursor to the end of the word */
while (text[cursor] && strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
while (text[cursor] && !strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
}
}
static void static void
keypress(XKeyEvent *ev) keypress(XKeyEvent *ev)
{ {
char buf[32]; char buf[32];
int len; int len;
KeySym ksym = NoSymbol; KeySym ksym;
Status status; Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
if (status == XBufferOverflow) switch (status) {
default: /* XLookupNone, XBufferOverflow */
return; return;
if (ev->state & ControlMask) case XLookupChars:
goto insert;
case XLookupKeySym:
case XLookupBoth:
break;
}
if (ev->state & ControlMask) {
switch(ksym) { switch(ksym) {
case XK_a: ksym = XK_Home; break; case XK_a: ksym = XK_Home; break;
case XK_b: ksym = XK_Left; break; case XK_b: ksym = XK_Left; break;
@ -334,6 +359,12 @@ keypress(XKeyEvent *ev)
XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime); utf8, utf8, win, CurrentTime);
return; return;
case XK_Left:
movewordedge(-1);
goto draw;
case XK_Right:
movewordedge(+1);
goto draw;
case XK_Return: case XK_Return:
case XK_KP_Enter: case XK_KP_Enter:
break; break;
@ -343,8 +374,14 @@ keypress(XKeyEvent *ev)
default: default:
return; return;
} }
else if (ev->state & Mod1Mask) } else if (ev->state & Mod1Mask) {
switch(ksym) { switch(ksym) {
case XK_b:
movewordedge(-1);
goto draw;
case XK_f:
movewordedge(+1);
goto draw;
case XK_g: ksym = XK_Home; break; case XK_g: ksym = XK_Home; break;
case XK_G: ksym = XK_End; break; case XK_G: ksym = XK_End; break;
case XK_h: ksym = XK_Up; break; case XK_h: ksym = XK_Up; break;
@ -354,8 +391,11 @@ keypress(XKeyEvent *ev)
default: default:
return; return;
} }
}
switch(ksym) { switch(ksym) {
default: default:
insert:
if (!iscntrl(*buf)) if (!iscntrl(*buf))
insert(buf, len); insert(buf, len);
break; break;
@ -455,6 +495,8 @@ keypress(XKeyEvent *ev)
match(); match();
break; break;
} }
draw:
drawmenu(); drawmenu();
} }
@ -467,10 +509,12 @@ paste(void)
Atom da; Atom da;
/* we have been given the current selection, now insert it into input */ /* we have been given the current selection, now insert it into input */
XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
utf8, &da, &di, &dl, &dl, (unsigned char **)&p); utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
== Success && p) {
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
XFree(p); XFree(p);
}
drawmenu(); drawmenu();
} }
@ -509,7 +553,7 @@ run(void)
XEvent ev; XEvent ev;
while (!XNextEvent(dpy, &ev)) { while (!XNextEvent(dpy, &ev)) {
if (XFilterEvent(&ev, win)) if (XFilterEvent(&ev, None))
continue; continue;
switch(ev.type) { switch(ev.type) {
case Expose: case Expose:
@ -539,22 +583,21 @@ run(void)
static void static void
setup(void) setup(void)
{ {
int x, y, i = 0; int x, y, i, j;
unsigned int du; unsigned int du;
XSetWindowAttributes swa; XSetWindowAttributes swa;
XIM xim; XIM xim;
Window w, dw, *dws; Window w, dw, *dws;
XWindowAttributes wa; XWindowAttributes wa;
XClassHint ch = {"dmenu", "dmenu"};
#ifdef XINERAMA #ifdef XINERAMA
XineramaScreenInfo *info; XineramaScreenInfo *info;
Window pw; Window pw;
int a, j, di, n, area = 0; int a, di, n, area = 0;
#endif #endif
/* init appearance */ /* init appearance */
scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2); for (j = 0; j < SchemeLast; j++)
scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2); scheme[j] = drw_scm_create(drw, colors[j], 2);
scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2);
clip = XInternAtom(dpy, "CLIPBOARD", False); clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False);
@ -564,6 +607,7 @@ setup(void)
lines = MAX(lines, 0); lines = MAX(lines, 0);
mh = (lines + 1) * bh; mh = (lines + 1) * bh;
#ifdef XINERAMA #ifdef XINERAMA
i = 0;
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
XGetInputFocus(dpy, &w, &di); XGetInputFocus(dpy, &w, &di);
if (mon >= 0 && mon < n) if (mon >= 0 && mon < n)
@ -613,6 +657,7 @@ setup(void)
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
XSetClassHint(dpy, win, &ch);
/* open input methods */ /* open input methods */
xim = XOpenIM(dpy, NULL, NULL, NULL); xim = XOpenIM(dpy, NULL, NULL, NULL);
@ -620,6 +665,7 @@ setup(void)
XNClientWindow, win, XNFocusWindow, win, NULL); XNClientWindow, win, XNFocusWindow, win, NULL);
XMapRaised(dpy, win); XMapRaised(dpy, win);
XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
if (embed) { if (embed) {
XSelectInput(dpy, parentwin, FocusChangeMask); XSelectInput(dpy, parentwin, FocusChangeMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
@ -685,6 +731,8 @@ main(int argc, char *argv[])
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("warning: no locale support\n", stderr); fputs("warning: no locale support\n", stderr);
if (!XSetLocaleModifiers(""))
fputs("warning: no locale modifiers support\n", stderr);
if (!(dpy = XOpenDisplay(NULL))) if (!(dpy = XOpenDisplay(NULL)))
die("cannot open display"); die("cannot open display");
screen = DefaultScreen(dpy); screen = DefaultScreen(dpy);
@ -699,7 +747,12 @@ main(int argc, char *argv[])
die("no fonts could be loaded."); die("no fonts could be loaded.");
lrpad = drw->fonts->h; lrpad = drw->fonts->h;
if (fast) { #ifdef __OpenBSD__
if (pledge("stdio rpath", NULL) == -1)
die("pledge");
#endif
if (fast && !isatty(0)) {
grabkeyboard(); grabkeyboard();
readstdin(); readstdin();
} else { } else {

View file

@ -1,10 +1,10 @@
#!/bin/sh #!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}"
cache=$cachedir/dmenu_run cache="$cachedir/dmenu_run"
else
cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~ [ ! -e "$cachedir" ] && mkdir -p "$cachedir"
fi
IFS=: IFS=:
if stest -dqr -n "$cache" $PATH; then if stest -dqr -n "$cache" $PATH; then
stest -flx $PATH | sort -u | tee "$cache" stest -flx $PATH | sort -u | tee "$cache"

14
drw.c
View file

@ -132,6 +132,19 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
die("no font specified."); die("no font specified.");
} }
/* Do not allow using color fonts. This is a workaround for a BadLength
* error from Xft with color glyphs. Modelled on the Xterm workaround. See
* https://bugzilla.redhat.com/show_bug.cgi?id=1498269
* https://lists.suckless.org/dev/1701/30932.html
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
* and lots more all over the internet.
*/
FcBool iscol;
if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
XftFontClose(drw->dpy, xfont);
return NULL;
}
font = ecalloc(1, sizeof(Fnt)); font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont; font->xfont = xfont;
font->pattern = pattern; font->pattern = pattern;
@ -337,6 +350,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
fcpattern = FcPatternDuplicate(drw->fonts->pattern); fcpattern = FcPatternDuplicate(drw->fonts->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
FcDefaultSubstitute(fcpattern); FcDefaultSubstitute(fcpattern);