diff --git a/config.h b/config.h index fa37f97..492d528 100644 --- a/config.h +++ b/config.h @@ -1,12 +1,13 @@ /* See LICENSE file for copyright and license details. */ -static Bool topbar = True; -static const char *fonts[]={ - "Noto Sans UI:size=13" +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; +static const char *asterisk = "*"; +static const char *colors[SchemeLast][2] = { + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" } }; -static const char *secstring = "● ● "; -/*static char *description = NULL;*/ -static const char *prompt = "🔑 Pinentry "; -static const char *normbgcolor = "#000000"; -static const char *normfgcolor = "#ffffff"; -static const char *selbgcolor = "#d9904a"; -static const char *selfgcolor = "#ffffff"; diff --git a/config.mk b/config.mk index e8cf5ef..8c28fe6 100644 --- a/config.mk +++ b/config.mk @@ -24,7 +24,7 @@ LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} -CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} LDFLAGS = -s ${LIBS} # compiler and linker diff --git a/drw.c b/drw.c index 1d911e4..c1582e7 100644 --- a/drw.c +++ b/drw.c @@ -9,7 +9,7 @@ #include "util.h" #define UTF_INVALID 0xFFFD -#define UTF_SIZ 4 +#define UTF_SIZ 4 static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; @@ -17,50 +17,54 @@ static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000 static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 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]) +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)) +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) + for (i = 1; *u > utfmax[i]; ++i) ; return i; } static size_t -utf8decode(const char *c, long *u, size_t clen) { +utf8decode(const char *c, long *u, size_t clen) +{ size_t i, j, len, type; long udecoded; *u = UTF_INVALID; - if(!clen) + if (!clen) return 0; udecoded = utf8decodebyte(c[0], &len); - if(!BETWEEN(len, 1, UTF_SIZ)) + if (!BETWEEN(len, 1, UTF_SIZ)) return 1; - for(i = 1, j = 1; i < clen && j < len; ++i, ++j) { + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); - if(type != 0) + if (type) return j; } - if(j < len) + 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 h) { - Drw *drw = (Drw *)calloc(1, sizeof(Drw)); - if(!drw) - return NULL; +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + drw->dpy = dpy; drw->screen = screen; drw->root = root; @@ -68,166 +72,178 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h drw->h = h; drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); drw->gc = XCreateGC(dpy, root, 0, NULL); - drw->fontcount = 0; XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + return drw; } void -drw_resize(Drw *drw, unsigned int w, unsigned int h) { - if(!drw) +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) return; + drw->w = w; drw->h = h; - if(drw->drawable != 0) + if (drw->drawable) XFreePixmap(drw->dpy, drw->drawable); drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); } void -drw_free(Drw *drw) { - size_t i; - - /* This was breaking the program when I tried to bring window up second time - Too lazy to dig deeper right now,*\ - \*or (i = 0; i < drw->fontcount; i++) { - drw_font_free(drw->fonts[i]); - }*/ +drw_free(Drw *drw) +{ XFreePixmap(drw->dpy, drw->drawable); XFreeGC(drw->dpy, drw->gc); free(drw); } /* This function is an implementation detail. Library users should use - * drw_font_create instead. + * drw_fontset_create instead. */ static Fnt * -drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) { +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ Fnt *font; - - if (!(fontname || fontpattern)) - die("No font specified.\n"); - - if (!(font = (Fnt *)calloc(1, sizeof(Fnt)))) - return NULL; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; if (fontname) { - /* Using the pattern found at font->xfont->pattern does not yield same - * the same substitution results as using the pattern returned by + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by * FcNameParse; using the latter results in the desired fallback - * behaviour whereas the former just results in - * missing-character-rectangles being drawn, at least with some fonts. - */ - if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) || - !(font->pattern = FcNameParse((FcChar8 *) fontname))) { - if (font->xfont) { - XftFontClose(drw->dpy, font->xfont); - font->xfont = NULL; - } - fprintf(stderr, "error, cannot load font: '%s'\n", fontname); + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; } } else if (fontpattern) { - if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { - fprintf(stderr, "error, cannot load font pattern.\n"); - } else { - font->pattern = NULL; + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; } + } else { + die("no font specified."); } - if (!font->xfont) { - free(font); - return NULL; - } - - font->ascent = font->xfont->ascent; - font->descent = font->xfont->descent; - font->h = font->ascent + font->descent; + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; font->dpy = drw->dpy; + return font; } -Fnt* -drw_font_create(Drw *drw, const char *fontname) { - return drw_font_xcreate(drw, fontname, NULL); +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); } -void -drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) { +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; size_t i; - Fnt *font; - for (i = 0; i < fontcount; i++) { - if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { - die("Font cache exhausted.\n"); - } else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) { - drw->fonts[drw->fontcount++] = font; + 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_font_free(Fnt *font) { - if(!font) +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) return; - if(font->pattern) - FcPatternDestroy(font->pattern); - XftFontClose(font->dpy, font->xfont); - free(font); + + 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_clr_create(Drw *drw, const char *clrname) { - Clr *clr; - Colormap cmap; - Visual *vis; +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; - if(!drw) - return NULL; - clr = (Clr *)calloc(1, sizeof(Clr)); - if(!clr) + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) return NULL; - cmap = DefaultColormap(drw->dpy, drw->screen); - vis = DefaultVisual(drw->dpy, drw->screen); - if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb)) - die("error, cannot allocate color '%s'\n", clrname); - clr->pix = clr->rgb.pixel; - return clr; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; } void -drw_clr_free(Clr *clr) { - free(clr); +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; } void -drw_setscheme(Drw *drw, ClrScheme *scheme) { - if(!drw) - return; - drw->scheme = scheme; +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, int empty, int invert) { - if(!drw || !drw->scheme) +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) return; - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix); - if(filled) - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1); - else if(empty) - XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].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, h - 1); } int -drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) { +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ char buf[1024]; - int tx, ty, th; - Extnts tex; - Colormap cmap; - Visual *vis; - XftDraw *d; - Fnt *curfont, *nextfont; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; size_t i, len; - int utf8strlen, utf8charlen, render; + int utf8strlen, utf8charlen, render = x || y || w || h; long utf8codepoint = 0; const char *utf8str; FcCharSet *fccharset; @@ -236,73 +252,67 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex XftResult result; int charexists = 0; - if (!(render = x || y || w || h)) { - w = ~w; - } - - if (!drw || !drw->scheme) { + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) return 0; - } else if (render) { - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix); - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); - } - if (!text || !drw->fontcount) { - return 0; - } else if (render) { - cmap = DefaultColormap(drw->dpy, drw->screen); - vis = DefaultVisual(drw->dpy, drw->screen); - d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap); + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + 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; } - curfont = drw->fonts[0]; + usedfont = drw->fonts; while (1) { utf8strlen = 0; utf8str = text; nextfont = NULL; while (*text) { utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); - for (i = 0; i < drw->fontcount; i++) { - charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { - if (drw->fonts[i] == curfont) { + if (curfont == usedfont) { utf8strlen += utf8charlen; text += utf8charlen; } else { - nextfont = drw->fonts[i]; + nextfont = curfont; } break; } } - if (!charexists || (nextfont && nextfont != curfont)) { + if (!charexists || nextfont) break; - } else { + else charexists = 0; - } } if (utf8strlen) { - drw_font_getexts(curfont, utf8str, utf8strlen, &tex); + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); /* shorten text if necessary */ - for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--) - drw_font_getexts(curfont, utf8str, len, &tex); + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); if (len) { memcpy(buf, utf8str, len); buf[len] = '\0'; - if(len < utf8strlen) - for(i = len; i && i > len - 3; buf[--i] = '.'); + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ if (render) { - th = curfont->ascent + curfont->descent; - ty = y + (h / 2) - (th / 2) + curfont->ascent; - tx = x + (h / 2); - XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); } - - x += tex.w; - w -= tex.w; + x += ew; + w -= ew; } } @@ -310,28 +320,21 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex break; } else if (nextfont) { charexists = 0; - curfont = nextfont; + usedfont = nextfont; } else { /* Regardless of whether or not a fallback font is found, the - * character must be drawn. - */ + * character must be drawn. */ charexists = 1; - if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { - continue; - } - fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, utf8codepoint); - if (!drw->fonts[0]->pattern) { - /* Refer to the comment in drw_font_xcreate for more - * information. - */ - die("The first font in the cache must be loaded from a font string.\n"); + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); } - fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); + fcpattern = FcPatternDuplicate(drw->fonts->pattern); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); @@ -343,73 +346,76 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex FcPatternDestroy(fcpattern); if (match) { - curfont = drw_font_xcreate(drw, NULL, match); - if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { - drw->fonts[drw->fontcount++] = curfont; + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; } else { - if (curfont) { - drw_font_free(curfont); - } - curfont = drw->fonts[0]; + xfont_free(usedfont); + usedfont = drw->fonts; } } } } - - if (render) { + if (d) XftDrawDestroy(d); - } - return x; + return x + (render ? w : 0); } void -drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { - if(!drw) +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); 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, Extnts *tex) { +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ XGlyphInfo ext; - if(!font || !text) + if (!font || !text) return; - XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); - tex->h = font->h; - tex->w = ext.xOff; -} - -unsigned int -drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) { - Extnts tex; - if(!font) - return -1; - drw_font_getexts(font, text, len, &tex); - return tex.w; + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; } Cur * -drw_cur_create(Drw *drw, int shape) { +drw_cur_create(Drw *drw, int shape) +{ Cur *cur; - if(!drw) - return NULL; - cur = (Cur *)calloc(1, sizeof(Cur)); - if (!cur) + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) return NULL; + cur->cursor = XCreateFontCursor(drw->dpy, shape); + return cur; } void -drw_cur_free(Drw *drw, Cur *cursor) { - if(!drw || !cursor) +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) return; + XFreeCursor(drw->dpy, cursor->cursor); free(cursor); } diff --git a/drw.h b/drw.h index 536171b..4c67419 100644 --- a/drw.h +++ b/drw.h @@ -1,29 +1,19 @@ /* See LICENSE file for copyright and license details. */ -#define DRW_FONT_CACHE_SIZE 32 - -typedef struct { - unsigned long pix; - XftColor rgb; -} Clr; typedef struct { Cursor cursor; } Cur; -typedef struct { +typedef struct Fnt { Display *dpy; - int ascent; - int descent; unsigned int h; XftFont *xfont; FcPattern *pattern; + struct Fnt *next; } Fnt; -typedef struct { - Clr *fg; - Clr *bg; - Clr *border; -} ClrScheme; +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; typedef struct { unsigned int w, h; @@ -32,43 +22,36 @@ typedef struct { Window root; Drawable drawable; GC gc; - ClrScheme *scheme; - size_t fontcount; - Fnt *fonts[DRW_FONT_CACHE_SIZE]; + Clr *scheme; + Fnt *fonts; } Drw; -typedef struct { - unsigned int w; - unsigned int h; -} Extnts; - /* Drawable abstraction */ Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); void drw_resize(Drw *drw, unsigned int w, unsigned int h); void drw_free(Drw *drw); /* Fnt abstraction */ -Fnt *drw_font_create(Drw *drw, const char *fontname); -void drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount); -void drw_font_free(Fnt *font); -void drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *extnts); -unsigned int drw_font_getexts_width(Fnt *font, const char *text, unsigned int len); +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 int *w, unsigned int *h); -/* Colour abstraction */ -Clr *drw_clr_create(Drw *drw, const char *clrname); -void drw_clr_free(Clr *clr); +/* 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); /* Cursor abstraction */ Cur *drw_cur_create(Drw *drw, int shape); void drw_cur_free(Drw *drw, Cur *cursor); /* Drawing context manipulation */ -void drw_setfont(Drw *drw, Fnt *font); -void drw_setscheme(Drw *drw, ClrScheme *scheme); +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 filled, int empty, int invert); -int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert); +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); /* Map functions */ void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/pinentry-dmenu.c b/pinentry-dmenu.c index 9579918..e58c2e9 100644 --- a/pinentry-dmenu.c +++ b/pinentry-dmenu.c @@ -1,12 +1,14 @@ -#include -#include +/* See LICENSE file for copyright and license details. */ #include #include +#include #include #include -#include #include +#include +#include #include + #include #include #include @@ -22,50 +24,54 @@ #include "pinentry/memory.h" /* macros */ -#define INTERSECT(x, y, w, h, r) \ - (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ - && MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) -#define LENGTH(X) (sizeof X / sizeof X[0]) -#define TEXTNW(X, N) (drw_font_getexts_width(drw->fonts[0], (X), (N))) -#define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h) - -const char *str_OK = "OK\n"; -const char *str_ERRUNPARS = "ERR1337 dunno what to do with it\n"; -const char *str_ERRNOTIMP = "ERR4100 not implemented yet\n"; +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + && MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) /* enums */ enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */ enum { WinPin, WinConfirm }; /* window modes */ enum { Ok, NotOk, Cancel }; /* return status */ -static char text[2048] = ""; +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char text[BUFSIZ] = ""; +static char *embed; static int bh, mw, mh; -static int inputw, promptw; -static size_t cursor = 0; +static int inputw = 0, promptw, ppromptw; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + static Atom clip, utf8; -static Window win; +static Display *dpy; +static Window root, parentwin, win; static XIC xic; -static int mon = 0; -static ClrScheme scheme[SchemeLast]; -static Display *dpy; -static int screen; -static Window root; static Drw *drw; +static Clr *scheme[SchemeLast]; static int timed_out; - static int confirmed; static int winmode; - pinentry_t pinentry; #include "config.h" -void +static void grabkeyboard(void) { int i; + if (embed) { + return; + } /* try to grab keyboard, * we may have to wait for another process to ungrab */ for (i = 0; i < 1000; i++) { @@ -75,10 +81,11 @@ grabkeyboard(void) { } usleep(1000); } - die("cannot grab keyboard\n"); + + die("cannot grab keyboard"); } -size_t +static size_t nextrune(int cursor, int inc) { ssize_t n; @@ -88,8 +95,7 @@ nextrune(int cursor, int inc) { return n; } - -void +static void insert(const char *str, ssize_t n) { if (strlen(text) + n > sizeof text - 1) { return; @@ -97,91 +103,123 @@ insert(const char *str, ssize_t n) { /* move existing text out of the way, insert new text, and update cursor */ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); - + if (n > 0) { memcpy(&text[cursor], str, n); } cursor += n; } -void +static void drawwin(void) { - int curpos; - int x = 0, y = 0, w; - char *sectext; - int i; + unsigned int curpos; + int x = 0, y = 0, w, i; + size_t asterlen = strlen(asterisk); + char* censort = ecalloc(1, asterlen * sizeof(text)); +#if 0 + /* TODO: Code from first pintenry-demnu version */ + char *sectext = malloc(sizeof (char) * 2048); int seccursor = 0; - ssize_t n; + int n; +#endif - drw_setscheme(drw, &scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, True, 1, 1); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); - if ((pinentry->description) && *(pinentry->description)) { - drw_setscheme(drw, &scheme[SchemeSel]); - drw_text(drw, 0, 0, mw, bh, pinentry->description, 0); - y += bh; + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); } - if ((pinentry->prompt) && *(pinentry->prompt)) { - drw_setscheme(drw, &scheme[SchemeSel]); - drw_text(drw, x, y, promptw, bh, pinentry->prompt, 0); - x += promptw; + if (pinentry->prompt && *pinentry->prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + drw_text(drw, x, y, ppromptw, bh, lrpad / 2, pinentry->prompt, 0); + x += ppromptw; } + /* Draw input field */ w = inputw; - drw_setscheme(drw, &scheme[SchemeNorm]); + drw_setscheme(drw, scheme[SchemeNorm]); - sectext = malloc (sizeof (char) * 2048); +#if 0 + /* TODO: Code from first pintenry-demnu version */ sectext[0] = '\0'; - for (i=0; text[i] != '\0'; i = nextrune(i, +1)) { - strcat(sectext, secstring); + for (i = 0; text[i] != '\0'; i = nextrune(i, +1)) { + strcat(sectext, asterisk); if (i < cursor) { - for (n = seccursor + 1; n + 1 >= 0 && (sectext[n] & 0xc0) == 0x80; - n ++); + for (n = seccursor + 1; n > 0 && (sectext[n] & 0xc0) == 0x80; n++); seccursor = n; } } +#endif if (winmode == WinPin) { - drw_text(drw, x, y, mw, bh, sectext, 0); +#if 0 + /* TODO: Code from first pintenry-demnu version */ + drw_text(drw, x, y, mw, bh, lrpad / 2, censort, 0); - if ((curpos = TEXTNW(sectext, seccursor) + bh/2 - 2) < w) { - drw_rect(drw, x + curpos + 2, y + 2, 1 , bh - 4 , True, 1, 0); + drw_font_getexts(drw->fonts, sectext, seccursor, &curpos, NULL); + curpos += bh / 2 - 2; + if (curpos < w) { + drw_rect(drw, x + curpos + 2, y + 2, 1, bh - 4, 1, 0); + } +#else + for (i = 0; i < asterlen * strlen(text); i += asterlen) { + memcpy(&censort[i], asterisk, asterlen); } + + censort[i+1] = '\n'; + drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); + drw_font_getexts(drw->fonts, censort, cursor * asterlen, &curpos, NULL); +#endif + free(censort); } else { - drw_text(drw, x, y, mw, bh, "(y/n)", 0); + // TODO: Do this with a list view? 3 entries: startentry/neutral, YES and NO + drw_text(drw, x, y, mw, bh, lrpad / 2, "(y/n)", 0); } +#if 0 + /* This is code from the first pinentry-dmenu version */ + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } +#endif + drw_map(drw, win, 0, 0, mw, mh); } -void -setup(void){ - int x, y; - Window pw; - XWindowAttributes wa; -#ifdef XINERAMA +static void +setup(void) { + int x, y, i = 0; unsigned int du; - int a, i, di, n, j, area = 0; + const char* pprompt = pinentry->prompt; + XSetWindowAttributes swa; + XIM xim; Window w, dw, *dws; + XWindowAttributes wa; +#ifdef XINERAMA XineramaScreenInfo *info; + Window pw; + int a, j, di, n, area = 0; #endif - XSetWindowAttributes swa; - XIM xim; - scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor); - scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor); - scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor); - scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor); + + /* Init appearance */ + scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2); + scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2); + clip = XInternAtom(dpy, "CLIPBOARD", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False); - bh = drw->fonts[0]->h + 2; + + /* Calculate menu geometry */ + bh = drw->fonts->h + 2; mh = bh; #ifdef XINERAMA info = XineramaQueryScreens(dpy, &n); - - if (info) { + + if (parentwin == root && info) { XGetInputFocus(dpy, &w, &di); if (mon >= 0 && mon < n) { i = mon; @@ -212,6 +250,7 @@ setup(void){ } } } + x = info[i].x_org; y = info[i].y_org + (topbar ? 0 : info[i].height - mh); mw = info[i].width; @@ -219,45 +258,54 @@ setup(void){ } else #endif { - if (!XGetWindowAttributes(dpy, root, &wa)) { - die("could not get embedding window attributes: 0x%lx", root); + if (!XGetWindowAttributes(dpy, parentwin, &wa)) { + die("could not get embedding window attributes: 0x%lx", parentwin); } x = 0; y = topbar ? 0 : wa.height - mh; mw = wa.width; } - /* FIXME */ - if (pinentry->prompt && *(pinentry->prompt)) { - promptw = TEXTW(pinentry->prompt); - } else { - promptw = 0; - } - inputw = mw-promptw; + + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + ppromptw = (pprompt && *pprompt) ? TEXTW(pprompt) : 0; + inputw = MIN(inputw, mw / 3); + + /* create menu window */ swa.override_redirect = True; - swa.background_pixel = scheme[SchemeNorm].bg->pix; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; - win = XCreateWindow(dpy, root, x, y, mw, mh, 0, - DefaultDepth(dpy, screen), CopyFromParent, - DefaultVisual(dpy, screen), + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + /* Open input methods */ xim = XOpenIM(dpy, NULL, NULL, NULL); xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, XNFocusWindow, win, NULL); - XMapRaised(dpy, win); - drw_resize(drw, mw, mh); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask); + + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) { + XSelectInput(dpy, dws[i], FocusChangeMask); + } + + XFree(dws); + } + grabfocus(); + } + + drw_resize(drw, mw, mh); drawwin(); } -void +static void cleanup(void) { XUngrabKey(dpy, AnyKey, AnyModifier, root); - drw_clr_free(scheme[SchemeNorm].bg); - drw_clr_free(scheme[SchemeNorm].fg); - drw_clr_free(scheme[SchemeSel].fg); - drw_clr_free(scheme[SchemeSel].bg); + free(scheme[SchemeNorm]); + free(scheme[SchemeSel]); drw_free(drw); XSync(dpy, False); XCloseDisplay(dpy); @@ -277,14 +325,17 @@ keypress(XKeyEvent *ev) { } if (winmode == WinConfirm) { - switch(ksym){ + switch(ksym) { case XK_KP_Enter: case XK_Return: + case XK_y: + case XK_Y: confirmed = 1; return 1; break; case XK_n: + case XK_N: confirmed = 0; return 1; break; @@ -295,7 +346,7 @@ keypress(XKeyEvent *ev) { break; } } else { - switch(ksym){ + switch(ksym) { default: if (!iscntrl(*buf)) { insert(buf, len); @@ -335,21 +386,23 @@ keypress(XKeyEvent *ev) { break; } } + drawwin(); + return 0; } -void +static void paste(void) { char *p, *q; int di; unsigned long dl; 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, utf8, &da, &di, &dl, &dl, (unsigned char **)&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); drawwin(); } @@ -414,7 +467,7 @@ password (void) { buf = secmem_malloc(strlen(text)); strcpy(buf, text); - pinentry_setbuffer_use (pinentry, buf, 0); + pinentry_setbuffer_use(pinentry, buf, 0); return 1; } @@ -429,31 +482,34 @@ confirm(void) { } static int -cmdhandler (pinentry_t recieved_pinentry) { +cmdhandler(pinentry_t received_pinentry) { struct sigaction sa; XWindowAttributes wa; text[0]='\0'; cursor = 0; - pinentry = recieved_pinentry; + pinentry = received_pinentry; if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) { fputs("warning: no locale support\n", stderr); } - if (!(dpy = XOpenDisplay(pinentry->display))) { /* NULL was here */ - die("dmenu: cannot open display\n"); + if (!(dpy = XOpenDisplay(pinentry->display))) { + die("cannot open display"); } screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); - if (!XGetWindowAttributes(dpy, root, &wa)) { - die("could not get embedding window attributes: 0x%lx", root); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) { + parentwin = root; + } + if (!XGetWindowAttributes(dpy, parentwin, &wa)) { + die("could not get embedding window attributes: 0x%lx", parentwin); } drw = drw_create(dpy, screen, root, wa.width, wa.height); - drw_load_fonts(drw, fonts, LENGTH(fonts)); - if (!drw->fontcount) { - die("No fonts could be loaded.\n"); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) { + die("no fonts could be loaded."); } - drw_setscheme(drw, &scheme[SchemeNorm]); + lrpad = drw->fonts->h; + drw_setscheme(drw, scheme[SchemeNorm]); if (pinentry->timeout) { memset(&sa, 0, sizeof(sa)); diff --git a/util.c b/util.c index 51acd1a..fe044fc 100644 --- a/util.c +++ b/util.c @@ -2,16 +2,34 @@ #include #include #include +#include #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 *errstr, ...) { +die(const char *fmt, ...) { va_list ap; - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); va_end(ap); - exit(EXIT_FAILURE); -} + 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 index f7ce721..f633b51 100644 --- a/util.h +++ b/util.h @@ -4,4 +4,5 @@ #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) -void die(const char *errstr, ...); +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size);