| Added Xft support (in the same fashion as done in dmenu by Hiltjo Posthuma), Xi… | |
| git clone git://git.suckless.org/svkbd | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| commit c8ee269e7344f7e1deeaa8c4ce1b4a16c6f5b26b | |
| parent 915b95b03d33e960e477fbdbe9d108ffc90aaacf | |
| Author: Maarten van Gompel <[email protected]> | |
| Date: Sun, 2 Aug 2020 15:46:12 +0200 | |
| Added Xft support (in the same fashion as done in dmenu by Hiltjo Posthuma), Xi… | |
| Signed-off-by: Miles Alan <[email protected]> | |
| Diffstat: | |
| M LICENSE | 1 + | |
| M Makefile | 13 +++---------- | |
| M config.def.h | 18 ++++++++++-------- | |
| M config.mk | 12 ++++++++---- | |
| A drw.c | 417 +++++++++++++++++++++++++++++… | |
| A drw.h | 51 +++++++++++++++++++++++++++++… | |
| M layout.sxmo.h | 38 ++++++++++++++++-------------… | |
| M svkbd.c | 202 ++++++++++-------------------… | |
| A util.c | 35 +++++++++++++++++++++++++++++… | |
| A util.h | 8 ++++++++ | |
| 10 files changed, 613 insertions(+), 182 deletions(-) | |
| --- | |
| diff --git a/LICENSE b/LICENSE | |
| @@ -2,6 +2,7 @@ MIT/X Consortium License | |
| © 2011 Christoph Lohmann <[email protected]> | |
| © 2008-2011 Enno Boland <g # s01 ' de> | |
| +© 2020 Maarten van Gompel <[email protected]> | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| diff --git a/Makefile b/Makefile | |
| @@ -3,12 +3,12 @@ | |
| .POSIX: | |
| NAME = svkbd | |
| -VERSION = 0.1 | |
| +VERSION = 0.2 | |
| include config.mk | |
| BIN = ${NAME}-${LAYOUT} | |
| -SRC = ${NAME}.c | |
| +SRC = drw.c ${NAME}.c util.c | |
| OBJ = ${NAME}-${LAYOUT}.o | |
| MAN1 = ${NAME}.1 | |
| @@ -24,15 +24,8 @@ options: | |
| config.h: | |
| cp config.def.h $@ | |
| -${BIN}: config.h ${OBJ} | |
| - | |
| -${OBJ}: config.h | |
| - | |
| -${OBJ}: | |
| - ${CC} -o $@ -c ${SRC} ${SVKBD_CFLAGS} ${SVKBD_CPPFLAGS} | |
| - | |
| ${BIN}: | |
| - ${CC} -o ${BIN} ${OBJ} ${SVKBD_LDFLAGS} | |
| + ${CC} -o $@ ${SRC} ${SVKBD_CFLAGS} ${SVKBD_CPPFLAGS} ${SVKBD_LDFLAGS} | |
| clean: | |
| rm -f ${NAME}-?? ${NAME}-??.o ${OBJ} | |
| diff --git a/config.def.h b/config.def.h | |
| @@ -1,9 +1,11 @@ | |
| static const Bool wmborder = True; | |
| -static const char font[] = "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*"; | |
| -static const char normbgcolor[] = "#cccccc"; | |
| -static const char normfgcolor[] = "#000000"; | |
| -static const char pressbgcolor[] = "#0000cc"; | |
| -static const char pressfgcolor[] = "#ffffff"; | |
| -static const char highlightbgcolor[] = "#0000cc"; | |
| -static const char highlightfgcolor[] = "#ffffff"; | |
| - | |
| +static int fontsize = 16; | |
| +static const char *fonts[] = { | |
| + "monospace:size=16" | |
| +}; | |
| +static const char *colors[SchemeLast][2] = { | |
| + /* fg bg */ | |
| + [SchemeNorm] = { "#58a7c6", "#14313d" }, | |
| + [SchemePress] = { "#ffffff", "#005577" }, | |
| + [SchemeHighlight] = { "#58a7c6", "#005577" }, | |
| +}; | |
| diff --git a/config.mk b/config.mk | |
| @@ -1,4 +1,4 @@ | |
| -LAYOUT = en | |
| +LAYOUT = sxmo | |
| # paths | |
| PREFIX = /usr/local | |
| @@ -7,11 +7,15 @@ MANPREFIX = ${PREFIX}/share/man | |
| X11INC = /usr/X11R6/include | |
| X11LIB = /usr/X11R6/lib | |
| +# Xinerama, comment if you don't want it | |
| +XINERAMALIBS = -L${X11LIB} -lXinerama | |
| +XINERAMAFLAGS = -DXINERAMA | |
| + | |
| # includes and libs | |
| -INCS = -I. -I./layouts -I${X11INC} | |
| -LIBS = -L${X11LIB} -lX11 -lXtst | |
| +INCS = -I. -I./layouts -I${X11INC} -I/usr/include/freetype2 | |
| +LIBS = -L${X11LIB} -lX11 -lXtst -lfontconfig -lXft ${XINERAMALIBS} | |
| # use system flags | |
| SVKBD_CFLAGS = ${CFLAGS} | |
| SVKBD_LDFLAGS = ${LDFLAGS} ${LIBS} | |
| -SVKBD_CPPFLAGS = ${CPPFLAGS} ${INCS} -DVERSION=\"VERSION\" -DLAYOUT=\"layout.$… | |
| +SVKBD_CPPFLAGS = ${CPPFLAGS} ${INCS} -DVERSION=\"VERSION\" ${XINERAMAFLAGS} -D… | |
| diff --git a/drw.c b/drw.c | |
| @@ -0,0 +1,417 @@ | |
| +/* See LICENSE file for copyright and license details. */ | |
| +#include <stdio.h> | |
| +#include <stdlib.h> | |
| +#include <string.h> | |
| +#include <X11/Xlib.h> | |
| +#include <X11/Xft/Xft.h> | |
| + | |
| +#include "drw.h" | |
| +#include "util.h" | |
| + | |
| +#define UTF_INVALID 0xFFFD | |
| +#define UTF_SIZ 4 | |
| + | |
| +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF… | |
| +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF… | |
| +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x100… | |
| +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FF… | |
| + | |
| +static long | |
| +utf8decodebyte(const char c, size_t *i) | |
| +{ | |
| + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) | |
| + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) | |
| + return (unsigned char)c & ~utfmask[*i]; | |
| + return 0; | |
| +} | |
| + | |
| +static size_t | |
| +utf8validate(long *u, size_t i) | |
| +{ | |
| + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) | |
| + *u = UTF_INVALID; | |
| + for (i = 1; *u > utfmax[i]; ++i) | |
| + ; | |
| + return i; | |
| +} | |
| + | |
| +static size_t | |
| +utf8decode(const char *c, long *u, size_t clen) | |
| +{ | |
| + size_t i, j, len, type; | |
| + long udecoded; | |
| + | |
| + *u = UTF_INVALID; | |
| + if (!clen) | |
| + return 0; | |
| + udecoded = utf8decodebyte(c[0], &len); | |
| + if (!BETWEEN(len, 1, UTF_SIZ)) | |
| + return 1; | |
| + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { | |
| + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | |
| + if (type) | |
| + return j; | |
| + } | |
| + if (j < len) | |
| + return 0; | |
| + *u = udecoded; | |
| + utf8validate(u, len); | |
| + | |
| + return len; | |
| +} | |
| + | |
| +Drw * | |
| +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int… | |
| +{ | |
| + Drw *drw = ecalloc(1, sizeof(Drw)); | |
| + | |
| + drw->dpy = dpy; | |
| + drw->screen = screen; | |
| + drw->root = root; | |
| + drw->w = w; | |
| + drw->h = h; | |
| + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, scree… | |
| + drw->gc = XCreateGC(dpy, root, 0, NULL); | |
| + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | |
| + | |
| + return drw; | |
| +} | |
| + | |
| +void | |
| +drw_resize(Drw *drw, unsigned int w, unsigned int h) | |
| +{ | |
| + if (!drw) | |
| + return; | |
| + | |
| + drw->w = w; | |
| + drw->h = h; | |
| + if (drw->drawable) | |
| + XFreePixmap(drw->dpy, drw->drawable); | |
| + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(… | |
| +} | |
| + | |
| +void | |
| +drw_free(Drw *drw) | |
| +{ | |
| + XFreePixmap(drw->dpy, drw->drawable); | |
| + XFreeGC(drw->dpy, drw->gc); | |
| + drw_fontset_free(drw->fonts); | |
| + free(drw); | |
| +} | |
| + | |
| +/* This function is an implementation detail. Library users should use | |
| + * drw_fontset_create instead. | |
| + */ | |
| +static Fnt * | |
| +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) | |
| +{ | |
| + Fnt *font; | |
| + XftFont *xfont = NULL; | |
| + FcPattern *pattern = NULL; | |
| + | |
| + if (fontname) { | |
| + /* Using the pattern found at font->xfont->pattern does not yi… | |
| + * same substitution results as using the pattern returned by | |
| + * FcNameParse; using the latter results in the desired fallba… | |
| + * behaviour whereas the former just results in missing-charac… | |
| + * rectangles being drawn, at least with some fonts. */ | |
| + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)… | |
| + fprintf(stderr, "error, cannot load font from name: '%… | |
| + return NULL; | |
| + } | |
| + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { | |
| + fprintf(stderr, "error, cannot parse font name to patt… | |
| + XftFontClose(drw->dpy, xfont); | |
| + return NULL; | |
| + } | |
| + } else if (fontpattern) { | |
| + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { | |
| + fprintf(stderr, "error, cannot load font from pattern.… | |
| + return NULL; | |
| + } | |
| + } else { | |
| + 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.… | |
| + * 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) == FcResultMa… | |
| + XftFontClose(drw->dpy, xfont); | |
| + return NULL; | |
| + } | |
| + | |
| + font = ecalloc(1, sizeof(Fnt)); | |
| + font->xfont = xfont; | |
| + font->pattern = pattern; | |
| + font->h = xfont->ascent + xfont->descent; | |
| + font->dpy = drw->dpy; | |
| + | |
| + return font; | |
| +} | |
| + | |
| +static void | |
| +xfont_free(Fnt *font) | |
| +{ | |
| + if (!font) | |
| + return; | |
| + if (font->pattern) | |
| + FcPatternDestroy(font->pattern); | |
| + XftFontClose(font->dpy, font->xfont); | |
| + free(font); | |
| +} | |
| + | |
| +Fnt* | |
| +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) | |
| +{ | |
| + Fnt *cur, *ret = NULL; | |
| + size_t i; | |
| + | |
| + if (!drw || !fonts) | |
| + return NULL; | |
| + | |
| + for (i = 1; i <= fontcount; i++) { | |
| + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { | |
| + cur->next = ret; | |
| + ret = cur; | |
| + } | |
| + } | |
| + return (drw->fonts = ret); | |
| +} | |
| + | |
| +void | |
| +drw_fontset_free(Fnt *font) | |
| +{ | |
| + if (font) { | |
| + drw_fontset_free(font->next); | |
| + xfont_free(font); | |
| + } | |
| +} | |
| + | |
| +void | |
| +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) | |
| +{ | |
| + if (!drw || !dest || !clrname) | |
| + return; | |
| + | |
| + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), | |
| + DefaultColormap(drw->dpy, drw->screen), | |
| + clrname, dest)) | |
| + die("error, cannot allocate color '%s'", clrname); | |
| +} | |
| + | |
| +/* Wrapper to create color schemes. The caller has to call free(3) on the | |
| + * returned color scheme when done using it. */ | |
| +Clr * | |
| +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) | |
| +{ | |
| + size_t i; | |
| + Clr *ret; | |
| + | |
| + /* need at least two colors for a scheme */ | |
| + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, siz… | |
| + die("error, cannot create color scheme (drw=%d) (clrcount=%d)"… | |
| + | |
| + for (i = 0; i < clrcount; i++) | |
| + drw_clr_create(drw, &ret[i], clrnames[i]); | |
| + return ret; | |
| +} | |
| + | |
| +void | |
| +drw_setfontset(Drw *drw, Fnt *set) | |
| +{ | |
| + if (drw) | |
| + drw->fonts = set; | |
| +} | |
| + | |
| +void | |
| +drw_setscheme(Drw *drw, Clr *scm) | |
| +{ | |
| + if (drw) | |
| + drw->scheme = scm; | |
| +} | |
| + | |
| +void | |
| +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, i… | |
| +{ | |
| + if (!drw || !drw->scheme) | |
| + return; | |
| + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : … | |
| + if (filled) | |
| + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | |
| + else | |
| + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, … | |
| +} | |
| + | |
| +int | |
| +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int … | |
| +{ | |
| + char buf[1024]; | |
| + int ty; | |
| + unsigned int ew; | |
| + XftDraw *d = NULL; | |
| + Fnt *usedfont, *curfont, *nextfont; | |
| + size_t i, len; | |
| + int utf8strlen, utf8charlen, render = x || y || w || h; | |
| + long utf8codepoint = 0; | |
| + const char *utf8str; | |
| + FcCharSet *fccharset; | |
| + FcPattern *fcpattern; | |
| + FcPattern *match; | |
| + XftResult result; | |
| + int charexists = 0; | |
| + | |
| + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) | |
| + return 0; | |
| + | |
| + if (!render) { | |
| + w = ~w; | |
| + } else { | |
| + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg :… | |
| + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | |
| + d = XftDrawCreate(drw->dpy, drw->drawable, | |
| + DefaultVisual(drw->dpy, drw->screen), | |
| + DefaultColormap(drw->dpy, drw->screen)); | |
| + x += lpad; | |
| + w -= lpad; | |
| + } | |
| + | |
| + usedfont = drw->fonts; | |
| + while (1) { | |
| + utf8strlen = 0; | |
| + utf8str = text; | |
| + nextfont = NULL; | |
| + while (*text) { | |
| + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ… | |
| + for (curfont = drw->fonts; curfont; curfont = curfont-… | |
| + charexists = charexists || XftCharExists(drw->… | |
| + if (charexists) { | |
| + if (curfont == usedfont) { | |
| + utf8strlen += utf8charlen; | |
| + text += utf8charlen; | |
| + } else { | |
| + nextfont = curfont; | |
| + } | |
| + break; | |
| + } | |
| + } | |
| + | |
| + if (!charexists || nextfont) | |
| + break; | |
| + else | |
| + charexists = 0; | |
| + } | |
| + | |
| + if (utf8strlen) { | |
| + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, N… | |
| + /* shorten text if necessary */ | |
| + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew… | |
| + drw_font_getexts(usedfont, utf8str, len, &ew, … | |
| + | |
| + if (len) { | |
| + memcpy(buf, utf8str, len); | |
| + buf[len] = '\0'; | |
| + if (len < utf8strlen) | |
| + for (i = len; i && i > len - 3; buf[--… | |
| + ; /* NOP */ | |
| + | |
| + if (render) { | |
| + ty = y + (h - usedfont->h) / 2 + usedf… | |
| + XftDrawStringUtf8(d, &drw->scheme[inve… | |
| + usedfont->xfont, x, … | |
| + } | |
| + x += ew; | |
| + w -= ew; | |
| + } | |
| + } | |
| + | |
| + if (!*text) { | |
| + break; | |
| + } else if (nextfont) { | |
| + charexists = 0; | |
| + usedfont = nextfont; | |
| + } else { | |
| + /* Regardless of whether or not a fallback font is fou… | |
| + * character must be drawn. */ | |
| + charexists = 1; | |
| + | |
| + fccharset = FcCharSetCreate(); | |
| + FcCharSetAddChar(fccharset, utf8codepoint); | |
| + | |
| + if (!drw->fonts->pattern) { | |
| + /* Refer to the comment in xfont_create for mo… | |
| + die("the first font in the cache must be loade… | |
| + } | |
| + | |
| + fcpattern = FcPatternDuplicate(drw->fonts->pattern); | |
| + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); | |
| + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); | |
| + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); | |
| + | |
| + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); | |
| + FcDefaultSubstitute(fcpattern); | |
| + match = XftFontMatch(drw->dpy, drw->screen, fcpattern,… | |
| + | |
| + FcCharSetDestroy(fccharset); | |
| + FcPatternDestroy(fcpattern); | |
| + | |
| + if (match) { | |
| + usedfont = xfont_create(drw, NULL, match); | |
| + if (usedfont && XftCharExists(drw->dpy, usedfo… | |
| + for (curfont = drw->fonts; curfont->ne… | |
| + ; /* NOP */ | |
| + curfont->next = usedfont; | |
| + } else { | |
| + xfont_free(usedfont); | |
| + usedfont = drw->fonts; | |
| + } | |
| + } | |
| + } | |
| + } | |
| + if (d) | |
| + XftDrawDestroy(d); | |
| + | |
| + return x + (render ? w : 0); | |
| +} | |
| + | |
| +void | |
| +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) | |
| +{ | |
| + if (!drw) | |
| + return; | |
| + | |
| + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); | |
| +} | |
| + | |
| +void | |
| +drw_sync(Drw *drw) { | |
| + XSync(drw->dpy, False); | |
| +} | |
| + | |
| +unsigned int | |
| +drw_fontset_getwidth(Drw *drw, const char *text) | |
| +{ | |
| + if (!drw || !drw->fonts || !text) | |
| + return 0; | |
| + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); | |
| +} | |
| + | |
| +void | |
| +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *… | |
| +{ | |
| + XGlyphInfo ext; | |
| + | |
| + if (!font || !text) | |
| + return; | |
| + | |
| + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext… | |
| + if (w) | |
| + *w = ext.xOff; | |
| + if (h) | |
| + *h = font->h; | |
| +} | |
| diff --git a/drw.h b/drw.h | |
| @@ -0,0 +1,51 @@ | |
| +/* See LICENSE file for copyright and license details. */ | |
| + | |
| +typedef struct Fnt { | |
| + Display *dpy; | |
| + unsigned int h; | |
| + XftFont *xfont; | |
| + FcPattern *pattern; | |
| + struct Fnt *next; | |
| +} Fnt; | |
| + | |
| +enum { ColFg, ColBg }; /* Clr scheme index */ | |
| +typedef XftColor Clr; | |
| + | |
| +typedef struct { | |
| + unsigned int w, h; | |
| + Display *dpy; | |
| + int screen; | |
| + Window root; | |
| + Drawable drawable; | |
| + GC gc; | |
| + Clr *scheme; | |
| + Fnt *fonts; | |
| +} Drw; | |
| + | |
| +/* Drawable abstraction */ | |
| +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned… | |
| +void drw_resize(Drw *drw, unsigned int w, unsigned int h); | |
| +void drw_free(Drw *drw); | |
| + | |
| +/* Fnt abstraction */ | |
| +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); | |
| +void drw_fontset_free(Fnt* set); | |
| +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); | |
| +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned … | |
| + | |
| +/* Colorscheme abstraction */ | |
| +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); | |
| +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); | |
| + | |
| + | |
| +/* Drawing context manipulation */ | |
| +void drw_setfontset(Drw *drw, Fnt *set); | |
| +void drw_setscheme(Drw *drw, Clr *scm); | |
| + | |
| +/* Drawing functions */ | |
| +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int fill… | |
| +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned … | |
| + | |
| +/* Map functions */ | |
| +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int … | |
| +void drw_sync(Drw *drw); | |
| diff --git a/layout.sxmo.h b/layout.sxmo.h | |
| @@ -37,20 +37,20 @@ static Key keys_en[40] = { | |
| { 0, XK_m, 1 }, | |
| /*{ "/?", XK_slash, 1 },*/ | |
| { "Tab", XK_Tab, 1 }, | |
| - { "<-", XK_BackSpace, 2 }, | |
| + { "⇍ Bksp", XK_BackSpace, 2 }, | |
| { 0 }, /* New row */ | |
| - { "Layer 2", XK_Cancel, 1}, | |
| - { "Shift", XK_Shift_L, 1 }, | |
| + { "↺", XK_Cancel, 1}, | |
| + { "Shft", XK_Shift_L, 1 }, | |
| /*{ "L", XK_Left, 1 },*/ | |
| - { "D", XK_Down, 1 }, | |
| - { "U", XK_Up, 1 }, | |
| + { "↓", XK_Down, 1 }, | |
| + { "↑", XK_Up, 1 }, | |
| /*{ "R", XK_Right, 1 },*/ | |
| { "", XK_space, 2 }, | |
| { "Esc", XK_Escape, 1 }, | |
| { "Ctrl", XK_Control_L, 1 }, | |
| /*{ "Alt", XK_Alt_L, 1 },*/ | |
| - { "Enter", XK_Return, 2 }, | |
| + { "↲ Enter", XK_Return, 2 }, | |
| }; | |
| static Key keys_symbols[40] = { | |
| @@ -80,28 +80,28 @@ static Key keys_symbols[40] = { | |
| { 0 }, /* New row */ | |
| - { " ", XK_Shift_L|XK_bar, 1 }, | |
| - { " ", XK_Shift_L|XK_bar, 1 }, | |
| - { "L", XK_Left, 1 }, | |
| - { "R", XK_Right, 1 }, | |
| - { " ", XK_Shift_L|XK_bar, 1 }, | |
| - { " ", XK_Shift_L|XK_bar, 1 }, | |
| - { " ", XK_Shift_L|XK_bar, 1 }, | |
| + { "", XK_Shift_L|XK_bar, 1 }, | |
| + { "⇤", XK_Home, 1 }, | |
| + { "←", XK_Left, 1 }, | |
| + { "→", XK_Right, 1 }, | |
| + { "⇥", XK_End, 1 }, | |
| + { "⇊", XK_Next, 1 }, | |
| + { "⇈", XK_Prior, 1 }, | |
| { "Tab", XK_Tab, 1 }, | |
| - { "<-", XK_BackSpace, 2 }, | |
| + { "⇍ Bksp", XK_BackSpace, 2 }, | |
| { 0 }, /* New row */ | |
| - { "Layer 1", XK_Cancel, 1}, | |
| - { "Shift", XK_Shift_L, 1 }, | |
| + { "↺", XK_Cancel, 1}, | |
| + { "Shft", XK_Shift_L, 1 }, | |
| /*{ "L", XK_Left, 1 },*/ | |
| - { "D", XK_Down, 1 }, | |
| - { "U", XK_Up, 1 }, | |
| + { "↓", XK_Down, 1 }, | |
| + { "↑", XK_Up, 1 }, | |
| /*{ "R", XK_Right, 1 },*/ | |
| { "", XK_space, 2 }, | |
| { "Esc", XK_Escape, 1 }, | |
| { "Ctrl", XK_Control_L, 1 }, | |
| /*{ "Alt", XK_Alt_L, 1 },*/ | |
| - { "Enter", XK_Return, 2 }, | |
| + { "↲ Enter", XK_Return, 2 }, | |
| }; | |
| Buttonmod buttonmods[] = { | |
| diff --git a/svkbd.c b/svkbd.c | |
| @@ -13,16 +13,24 @@ | |
| #include <X11/Xutil.h> | |
| #include <X11/Xproto.h> | |
| #include <X11/extensions/XTest.h> | |
| +#include <X11/Xft/Xft.h> | |
| +#ifdef XINERAMA | |
| +#include <X11/extensions/Xinerama.h> | |
| +#endif | |
| #include <signal.h> | |
| #include <sys/select.h> | |
| +#include "drw.h" | |
| +#include "util.h" | |
| + | |
| + | |
| /* macros */ | |
| -#define MAX(a, b) ((a) > (b) ? (a) : (b)) | |
| #define LENGTH(x) (sizeof x / sizeof x[0]) | |
| +#define TEXTW(X) (drw_fontset_getwidth(drw, (X))) | |
| /* enums */ | |
| -enum { ColFG, ColBG, ColLast }; | |
| +enum { SchemeNorm, SchemePress, SchemeHighlight, SchemeLast }; | |
| enum { NetWMWindowType, NetLast }; | |
| /* typedefs */ | |
| @@ -30,22 +38,6 @@ typedef unsigned int uint; | |
| typedef unsigned long ulong; | |
| typedef struct { | |
| - ulong norm[ColLast]; | |
| - ulong press[ColLast]; | |
| - ulong high[ColLast]; | |
| - | |
| - Drawable drawable; | |
| - GC gc; | |
| - struct { | |
| - int ascent; | |
| - int descent; | |
| - int height; | |
| - XFontSet set; | |
| - XFontStruct *xfont; | |
| - } font; | |
| -} DC; /* draw context */ | |
| - | |
| -typedef struct { | |
| char *label; | |
| KeySym keysym; | |
| uint width; | |
| @@ -66,18 +58,15 @@ static void buttonrelease(XEvent *e); | |
| static void cleanup(void); | |
| static void configurenotify(XEvent *e); | |
| static void countrows(); | |
| -static void die(const char *errstr, ...); | |
| static void drawkeyboard(void); | |
| static void drawkey(Key *k); | |
| static void expose(XEvent *e); | |
| static Key *findkey(int x, int y); | |
| -static ulong getcolor(const char *colstr); | |
| -static void initfont(const char *fontstr); | |
| static void leavenotify(XEvent *e); | |
| static void press(Key *k, KeySym mod); | |
| static void run(void); | |
| static void setup(void); | |
| -static int textnw(const char *text, uint len); | |
| +static void togglelayer(); | |
| static void unpress(Key *k, KeySym mod); | |
| static void updatekeys(); | |
| @@ -93,8 +82,9 @@ static void (*handler[LASTEvent]) (XEvent *) = { | |
| }; | |
| static Atom netatom[NetLast]; | |
| static Display *dpy; | |
| -static DC dc; | |
| +static Drw *drw; | |
| static Window root, win; | |
| +static Clr* scheme[SchemeLast]; | |
| static Bool running = True, isdock = False; | |
| static KeySym pressedmod = 0; | |
| static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0; | |
| @@ -203,14 +193,12 @@ cleanup(void) { | |
| XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].k… | |
| } | |
| } | |
| - XSync(dpy, False); | |
| - if(dc.font.set) | |
| - XFreeFontSet(dpy, dc.font.set); | |
| - else | |
| - XFreeFont(dpy, dc.font.xfont); | |
| - XFreePixmap(dpy, dc.drawable); | |
| - XFreeGC(dpy, dc.gc); | |
| + for (i = 0; i < SchemeLast; i++) | |
| + free(scheme[i]); | |
| + drw_free(drw); | |
| + drw_sync(drw); | |
| + XSync(dpy, False); | |
| XDestroyWindow(dpy, win); | |
| XSync(dpy, False); | |
| XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); | |
| @@ -223,9 +211,7 @@ configurenotify(XEvent *e) { | |
| if(ev->window == win && (ev->width != ww || ev->height != wh)) { | |
| ww = ev->width; | |
| wh = ev->height; | |
| - XFreePixmap(dpy, dc.drawable); | |
| - dc.drawable = XCreatePixmap(dpy, root, ww, wh, | |
| - DefaultDepth(dpy, screen)); | |
| + drw_resize(drw, ww, wh); | |
| updatekeys(); | |
| } | |
| } | |
| @@ -240,15 +226,6 @@ countrows() { | |
| } | |
| } | |
| -void | |
| -die(const char *errstr, ...) { | |
| - va_list ap; | |
| - | |
| - va_start(ap, errstr); | |
| - vfprintf(stderr, errstr, ap); | |
| - va_end(ap); | |
| - exit(EXIT_FAILURE); | |
| -} | |
| void | |
| drawkeyboard(void) { | |
| @@ -262,42 +239,29 @@ drawkeyboard(void) { | |
| void | |
| drawkey(Key *k) { | |
| - int x, y, h, len; | |
| - XRectangle r = { k->x, k->y, k->w, k->h}; | |
| + int x, y, w, h; | |
| const char *l; | |
| - ulong *col; | |
| if(k->pressed) | |
| - col = dc.press; | |
| + drw_setscheme(drw, scheme[SchemePress]); | |
| else if(k->highlighted) | |
| - col = dc.high; | |
| + drw_setscheme(drw, scheme[SchemeHighlight]); | |
| else | |
| - col = dc.norm; | |
| - | |
| - XSetForeground(dpy, dc.gc, col[ColBG]); | |
| - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
| - XSetForeground(dpy, dc.gc, dc.norm[ColFG]); | |
| - r.height -= 1; | |
| - r.width -= 1; | |
| - XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
| - XSetForeground(dpy, dc.gc, col[ColFG]); | |
| + drw_setscheme(drw, scheme[SchemeNorm]); | |
| + drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1); | |
| + drw_rect(drw, k->x, k->y, k->w, k->h, 0, 0); | |
| + | |
| if(k->label) { | |
| l = k->label; | |
| } else { | |
| l = XKeysymToString(k->keysym); | |
| } | |
| - len = strlen(l); | |
| - h = dc.font.ascent + dc.font.descent; | |
| - y = k->y + (k->h / 2) - (h / 2) + dc.font.ascent; | |
| - x = k->x + (k->w / 2) - (textnw(l, len) / 2); | |
| - if(dc.font.set) { | |
| - XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, l, | |
| - len); | |
| - } else { | |
| - XDrawString(dpy, dc.drawable, dc.gc, x, y, l, len); | |
| - } | |
| - XCopyArea(dpy, dc.drawable, win, dc.gc, k->x, k->y, k->w, k->h, | |
| - k->x, k->y); | |
| + h = fontsize * 2; | |
| + y = k->y + (k->h / 2) - (h / 2); | |
| + w = TEXTW(l); | |
| + x = k->x + (k->w / 2) - (w / 2); | |
| + drw_text(drw, x, y, w, h, 0, l, 0); | |
| + drw_map(drw, win, k->x, k->y, k->w, k->h); | |
| } | |
| void | |
| @@ -322,52 +286,7 @@ findkey(int x, int y) { | |
| return NULL; | |
| } | |
| -ulong | |
| -getcolor(const char *colstr) { | |
| - Colormap cmap = DefaultColormap(dpy, screen); | |
| - XColor color; | |
| - if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) | |
| - die("error, cannot allocate color '%s'\n", colstr); | |
| - return color.pixel; | |
| -} | |
| - | |
| -void | |
| -initfont(const char *fontstr) { | |
| - char *def, **missing; | |
| - int i, n; | |
| - | |
| - missing = NULL; | |
| - if(dc.font.set) | |
| - XFreeFontSet(dpy, dc.font.set); | |
| - dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); | |
| - if(missing) { | |
| - while(n--) | |
| - fprintf(stderr, "svkbd: missing fontset: %s\n", missin… | |
| - XFreeStringList(missing); | |
| - } | |
| - if(dc.font.set) { | |
| - XFontStruct **xfonts; | |
| - char **font_names; | |
| - dc.font.ascent = dc.font.descent = 0; | |
| - n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); | |
| - for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++… | |
| - dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent… | |
| - dc.font.descent = MAX(dc.font.descent,(*xfonts)->desce… | |
| - xfonts++; | |
| - } | |
| - } else { | |
| - if(dc.font.xfont) | |
| - XFreeFont(dpy, dc.font.xfont); | |
| - dc.font.xfont = NULL; | |
| - if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) | |
| - && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) | |
| - die("error, cannot load font: '%s'\n", fontstr); | |
| - dc.font.ascent = dc.font.xfont->ascent; | |
| - dc.font.descent = dc.font.xfont->descent; | |
| - } | |
| - dc.font.height = dc.font.ascent + dc.font.descent; | |
| -} | |
| void | |
| leavenotify(XEvent *e) { | |
| @@ -487,15 +406,36 @@ setup(void) { | |
| XSizeHints *sizeh = NULL; | |
| XClassHint *ch; | |
| Atom atype = -1; | |
| - int i, sh, sw; | |
| + int i, j, sh, sw; | |
| XWMHints *wmh; | |
| + #if XINERAMA | |
| + XineramaScreenInfo *info = NULL; | |
| + #endif | |
| + | |
| /* init screen */ | |
| screen = DefaultScreen(dpy); | |
| root = RootWindow(dpy, screen); | |
| - sw = DisplayWidth(dpy, screen); | |
| - sh = DisplayHeight(dpy, screen); | |
| - initfont(font); | |
| + #if XINERAMA | |
| + if(XineramaIsActive(dpy)) { | |
| + info = XineramaQueryScreens(dpy, &i); | |
| + sw = info[0].width; | |
| + sh = info[0].height; | |
| + XFree(info); | |
| + } else | |
| + #endif | |
| + { | |
| + sw = DisplayWidth(dpy, screen); | |
| + sh = DisplayHeight(dpy, screen); | |
| + } | |
| + drw = drw_create(dpy, screen, root, sw, sh); | |
| + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) | |
| + die("no fonts could be loaded."); | |
| + drw_setscheme(drw, scheme[SchemeNorm]); | |
| + | |
| + /* init appearance */ | |
| + for (j = 0; j < SchemeLast; j++) | |
| + scheme[j] = drw_scm_create(drw, colors[j], 2); | |
| /* init atoms */ | |
| if(isdock) { | |
| @@ -520,23 +460,12 @@ setup(void) { | |
| if(wy < 0) | |
| wy = sh + wy - wh; | |
| - dc.norm[ColBG] = getcolor(normbgcolor); | |
| - dc.norm[ColFG] = getcolor(normfgcolor); | |
| - dc.press[ColBG] = getcolor(pressbgcolor); | |
| - dc.press[ColFG] = getcolor(pressfgcolor); | |
| - dc.high[ColBG] = getcolor(highlightbgcolor); | |
| - dc.high[ColFG] = getcolor(highlightfgcolor); | |
| - dc.drawable = XCreatePixmap(dpy, root, ww, wh, | |
| - DefaultDepth(dpy, screen)); | |
| - dc.gc = XCreateGC(dpy, root, 0, 0); | |
| - if(!dc.font.set) | |
| - XSetFont(dpy, dc.gc, dc.font.xfont->fid); | |
| for(i = 0; i < LENGTH(keys); i++) | |
| keys[i].pressed = 0; | |
| wa.override_redirect = !wmborder; | |
| - wa.border_pixel = dc.norm[ColFG]; | |
| - wa.background_pixel = dc.norm[ColBG]; | |
| + wa.border_pixel = scheme[SchemeNorm][ColFg].pixel; | |
| + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; | |
| win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0, | |
| CopyFromParent, CopyFromParent, CopyFromParent, | |
| CWOverrideRedirect | CWBorderPixel | | |
| @@ -575,20 +504,11 @@ setup(void) { | |
| } | |
| XMapRaised(dpy, win); | |
| + drw_resize(drw, ww, wh); | |
| updatekeys(); | |
| drawkeyboard(); | |
| } | |
| -int | |
| -textnw(const char *text, uint len) { | |
| - XRectangle r; | |
| - | |
| - if(dc.font.set) { | |
| - XmbTextExtents(dc.font.set, text, len, NULL, &r); | |
| - return r.width; | |
| - } | |
| - return XTextWidth(dc.font.xfont, text, len); | |
| -} | |
| void | |
| updatekeys() { | |
| @@ -642,7 +562,7 @@ main(int argc, char *argv[]) { | |
| signal(SIGTERM, sigterm); | |
| for (i = 1; argv[i]; i++) { | |
| if(!strcmp(argv[i], "-v")) { | |
| - die("svkbd-"VERSION", © 2006-2016 svkbd engineers," | |
| + die("svkbd-"VERSION", © 2006-2020 svkbd engineers," | |
| " see LICENSE for details\n"); | |
| } else if(!strcmp(argv[i], "-d")) { | |
| isdock = True; | |
| diff --git a/util.c b/util.c | |
| @@ -0,0 +1,35 @@ | |
| +/* See LICENSE file for copyright and license details. */ | |
| +#include <stdarg.h> | |
| +#include <stdio.h> | |
| +#include <stdlib.h> | |
| +#include <string.h> | |
| + | |
| +#include "util.h" | |
| + | |
| +void * | |
| +ecalloc(size_t nmemb, size_t size) | |
| +{ | |
| + void *p; | |
| + | |
| + if (!(p = calloc(nmemb, size))) | |
| + die("calloc:"); | |
| + return p; | |
| +} | |
| + | |
| +void | |
| +die(const char *fmt, ...) { | |
| + va_list ap; | |
| + | |
| + va_start(ap, fmt); | |
| + vfprintf(stderr, fmt, ap); | |
| + va_end(ap); | |
| + | |
| + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { | |
| + fputc(' ', stderr); | |
| + perror(NULL); | |
| + } else { | |
| + fputc('\n', stderr); | |
| + } | |
| + | |
| + exit(1); | |
| +} | |
| diff --git a/util.h b/util.h | |
| @@ -0,0 +1,8 @@ | |
| +/* See LICENSE file for copyright and license details. */ | |
| + | |
| +#define MAX(A, B) ((A) > (B) ? (A) : (B)) | |
| +#define MIN(A, B) ((A) < (B) ? (A) : (B)) | |
| +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) | |
| + | |
| +void die(const char *fmt, ...); | |
| +void *ecalloc(size_t nmemb, size_t size); |