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); |