tinitial dmenu / dinput separation - dmenu - Dmenu fork with xft fonts.
 (HTM) git clone git://r-36.net/dmenu
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit b4e63454e5768d38682405d252a81b1149273c0b
 (DIR) parent bff1526d31faaf289804174f6477d61736e82443
 (HTM) Author: Connor Lane Smith <cls@lubutu.com>
       Date:   Wed, 23 Jun 2010 12:04:54 +0100
       
       initial dmenu / dinput separation
       Diffstat:
         Makefile                            |      17 +++++++++--------
         dinput.c                            |     387 +++++++++++++++++++++++++++++++
         dmenu.c                             |     219 ++++---------------------------
         draw.c                              |     143 +++++++++++++++++++++++++++++++
       
       4 files changed, 561 insertions(+), 205 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       t@@ -3,10 +3,10 @@
        
        include config.mk
        
       -SRC = dmenu.c
       +SRC = dinput.c dmenu.c draw.c
        OBJ = ${SRC:.c=.o}
        
       -all: options dmenu
       +all: options dinput dmenu
        
        options:
                @echo dmenu build options:
       t@@ -18,19 +18,19 @@ options:
                @echo CC $<
                @${CC} -c ${CFLAGS} $<
        
       -${OBJ}: config.h config.mk
       +${OBJ}: config.h config.mk draw.c
        
        config.h:
                @echo creating $@ from config.def.h
                @cp config.def.h $@
        
       -dmenu: ${OBJ}
       +.o:
                @echo CC -o $@
       -        @${CC} -o $@ ${OBJ} ${LDFLAGS}
       +        @${CC} -o $@ $< ${LDFLAGS}
        
        clean:
                @echo cleaning
       -        @rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz
       +        @rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz
        
        dist: clean
                @echo creating dist tarball
       t@@ -43,7 +43,8 @@ dist: clean
        install: all
                @echo installing executable file to ${DESTDIR}${PREFIX}/bin
                @mkdir -p ${DESTDIR}${PREFIX}/bin
       -        @cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin
       +        @cp -f dinput dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin
       +        @chmod 755 ${DESTDIR}${PREFIX}/bin/dinput
                @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
                @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
                @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
       t@@ -55,7 +56,7 @@ install: all
        uninstall:
                @echo removing executable file from ${DESTDIR}${PREFIX}/bin
                @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path
       -        @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_run
       +        @rm -f ${DESTDIR}${PREFIX}/bin/dinput ${DESTDIR}${PREFIX}/bin/dmenu_run
                @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
                @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
        
 (DIR) diff --git a/dinput.c b/dinput.c
       t@@ -0,0 +1,387 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <ctype.h>
       +#include <locale.h>
       +#include <stdarg.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <strings.h>
       +#include <unistd.h>
       +#include <X11/keysym.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#ifdef XINERAMA
       +#include <X11/extensions/Xinerama.h>
       +#endif
       +
       +/* macros */
       +#define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
       +#define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH))
       +#define MIN(a, b)               ((a) < (b) ? (a) : (b))
       +#define MAX(a, b)               ((a) > (b) ? (a) : (b))
       +#define IS_UTF8_1ST_CHAR(c)     ((((c) & 0xc0) == 0xc0) || !((c) & 0x80))
       +
       +/* forward declarations */
       +static void cleanup(void);
       +static void drawcursor(void);
       +static void drawinput(void);
       +static void eprint(const char *errstr, ...);
       +static Bool grabkeyboard(void);
       +static void kpress(XKeyEvent * e);
       +static void run(void);
       +static void setup(Bool topbar);
       +
       +#include "config.h"
       +
       +/* variables */
       +static char *prompt = NULL;
       +static char text[4096];
       +static int promptw = 0;
       +static int ret = 0;
       +static int screen;
       +static unsigned int mw, mh;
       +static unsigned int cursor = 0;
       +static unsigned int numlockmask = 0;
       +static Bool running = True;
       +static Display *dpy;
       +static Window parent, win;
       +
       +#include "draw.c"
       +
       +void
       +cleanup(void) {
       +        dccleanup();
       +        XDestroyWindow(dpy, win);
       +        XUngrabKeyboard(dpy, CurrentTime);
       +}
       +
       +void
       +drawcursor(void) {
       +        XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 };
       +
       +        r.x += textnw(text, cursor) + dc.font.height / 2;
       +
       +        XSetForeground(dpy, dc.gc, dc.norm[ColFG]);
       +        XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
       +}
       +
       +void
       +drawinput(void)
       +{
       +        dc.x = 0;
       +        dc.y = 0;
       +        dc.w = mw;
       +        dc.h = mh;
       +        drawtext(NULL, dc.norm);
       +        /* print prompt? */
       +        if(prompt) {
       +                dc.w = promptw;
       +                drawtext(prompt, dc.sel);
       +                dc.x += dc.w;
       +        }
       +        dc.w = mw - dc.x;
       +        drawtext(*text ? text : NULL, dc.norm);
       +        drawcursor();
       +        XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0);
       +        XFlush(dpy);
       +}
       +
       +void
       +eprint(const char *errstr, ...) {
       +        va_list ap;
       +
       +        va_start(ap, errstr);
       +        vfprintf(stderr, errstr, ap);
       +        va_end(ap);
       +        exit(EXIT_FAILURE);
       +}
       +
       +Bool
       +grabkeyboard(void) {
       +        unsigned int len;
       +
       +        for(len = 1000; len; len--) {
       +                if(XGrabKeyboard(dpy, parent, True, GrabModeAsync, GrabModeAsync, CurrentTime)
       +                == GrabSuccess)
       +                        break;
       +                usleep(1000);
       +        }
       +        return len > 0;
       +}
       +
       +void
       +kpress(XKeyEvent * e) {
       +        char buf[sizeof text];
       +        int num;
       +        unsigned int i, len;
       +        KeySym ksym;
       +
       +        len = strlen(text);
       +        num = XLookupString(e, buf, sizeof buf, &ksym, NULL);
       +        if(ksym == XK_KP_Enter)
       +                ksym = XK_Return;
       +        else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
       +                ksym = (ksym - XK_KP_0) + XK_0;
       +        else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
       +        || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
       +        || IsPrivateKeypadKey(ksym))
       +                return;
       +        /* first check if a control mask is omitted */
       +        if(e->state & ControlMask) {
       +                switch(tolower(ksym)) {
       +                default:
       +                        return;
       +                case XK_a:
       +                        ksym = XK_Home;
       +                        break;
       +                case XK_b:
       +                        ksym = XK_Left;
       +                        break;
       +                case XK_c:
       +                        ksym = XK_Escape;
       +                        break;
       +                case XK_e:
       +                        ksym = XK_End;
       +                        break;
       +                case XK_f:
       +                        ksym = XK_Right;
       +                        break;
       +                case XK_h:
       +                        ksym = XK_BackSpace;
       +                        break;
       +                case XK_j:
       +                        ksym = XK_Return;
       +                        break;
       +                case XK_k:
       +                        text[cursor] = '\0';
       +                        break;
       +                case XK_u:
       +                        memmove(text, text + cursor, sizeof text - cursor + 1);
       +                        cursor = 0;
       +                        break;
       +                case XK_w:
       +                        if(cursor > 0) {
       +                                i = cursor;
       +                                while(i-- > 0 && text[i] == ' ');
       +                                while(i-- > 0 && text[i] != ' ');
       +                                memmove(text + i + 1, text + cursor, sizeof text - cursor + 1);
       +                                cursor = i + 1;
       +                        }
       +                        break;
       +                case XK_y:
       +                        {
       +                                FILE *fp;
       +                                char *s;
       +                                if(!(fp = popen("sselp", "r")))
       +                                        eprint("dinput: cannot popen sselp\n");
       +                                s = fgets(buf, sizeof buf, fp);
       +                                pclose(fp);
       +                                if(s == NULL)
       +                                        return;
       +                        }
       +                        num = strlen(buf);
       +                        if(num && buf[num-1] == '\n')
       +                                buf[--num] = '\0';
       +                        break;
       +                }
       +        }
       +        switch(ksym) {
       +        default:
       +                num = MIN(num, sizeof text - cursor);
       +                if(num && !iscntrl((int) buf[0])) {
       +                        memmove(text + cursor + num, text + cursor, sizeof text - cursor - num);
       +                        memcpy(text + cursor, buf, num);
       +                        cursor += num;
       +                }
       +                break;
       +        case XK_BackSpace:
       +                if(cursor == 0)
       +                        return;
       +                for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++);
       +                memmove(text + cursor - i, text + cursor, sizeof text - cursor + i);
       +                cursor -= i;
       +                break;
       +        case XK_Delete:
       +                if(cursor == len)
       +                        return;
       +                for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++);
       +                memmove(text + cursor, text + cursor + i, sizeof text - cursor);
       +                break;
       +        case XK_End:
       +                cursor = len;
       +                break;
       +        case XK_Escape:
       +                ret = 1;
       +                running = False;
       +                return;
       +        case XK_Home:
       +                cursor = 0;
       +                break;
       +        case XK_Left:
       +                if(cursor == 0)
       +                        return;
       +                while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor]));
       +                break;
       +        case XK_Return:
       +                fprintf(stdout, "%s", text);
       +                fflush(stdout);
       +                running = False;
       +                return;
       +        case XK_Right:
       +                if(cursor == len)
       +                        return;
       +                while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor]));
       +                break;
       +        }
       +        drawinput();
       +}
       +
       +void
       +run(void) {
       +        XEvent ev;
       +
       +        /* main event loop */
       +        while(running && !XNextEvent(dpy, &ev))
       +                switch (ev.type) {
       +                case KeyPress:
       +                        kpress(&ev.xkey);
       +                        break;
       +                case Expose:
       +                        if(ev.xexpose.count == 0)
       +                                drawinput();
       +                        break;
       +                case VisibilityNotify:
       +                        if (ev.xvisibility.state != VisibilityUnobscured)
       +                                XRaiseWindow(dpy, win);
       +                        break;
       +                }
       +}
       +
       +void
       +setup(Bool topbar) {
       +        int i, j, x, y;
       +#if XINERAMA
       +        int n;
       +        XineramaScreenInfo *info = NULL;
       +#endif
       +        XModifierKeymap *modmap;
       +        XSetWindowAttributes wa;
       +        XWindowAttributes pwa;
       +
       +        /* init modifier map */
       +        modmap = XGetModifierMapping(dpy);
       +        for(i = 0; i < 8; i++)
       +                for(j = 0; j < modmap->max_keypermod; j++) {
       +                        if(modmap->modifiermap[i * modmap->max_keypermod + j]
       +                        == XKeysymToKeycode(dpy, XK_Num_Lock))
       +                                numlockmask = (1 << i);
       +                }
       +        XFreeModifiermap(modmap);
       +
       +        /* style */
       +        dc.norm[ColBG] = getcolor(normbgcolor);
       +        dc.norm[ColFG] = getcolor(normfgcolor);
       +        dc.sel[ColBG] = getcolor(selbgcolor);
       +        dc.sel[ColFG] = getcolor(selfgcolor);
       +        initfont(font);
       +
       +        /* menu window */
       +        wa.override_redirect = True;
       +        wa.background_pixmap = ParentRelative;
       +        wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask;
       +
       +        /* menu window geometry */
       +        mh = (dc.font.height + 2);
       +#if XINERAMA
       +        if(parent == RootWindow(dpy, screen) && XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) {
       +                i = 0;
       +                if(n > 1) {
       +                        int di;
       +                        unsigned int dui;
       +                        Window dummy;
       +                        if(XQueryPointer(dpy, parent, &dummy, &dummy, &x, &y, &di, &di, &dui))
       +                                for(i = 0; i < n; i++)
       +                                        if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
       +                                                break;
       +                }
       +                x = info[i].x_org;
       +                y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh;
       +                mw = info[i].width;
       +                XFree(info);
       +        }
       +        else
       +#endif
       +        {
       +                XGetWindowAttributes(dpy, parent, &pwa);
       +                x = 0;
       +                y = topbar ? 0 : pwa.height - mh;
       +                mw = pwa.width;
       +        }
       +
       +        win = XCreateWindow(dpy, parent, x, y, mw, mh, 0,
       +                        DefaultDepth(dpy, screen), CopyFromParent,
       +                        DefaultVisual(dpy, screen),
       +                        CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
       +
       +        /* pixmap */
       +        dcsetup();
       +        if(prompt)
       +                promptw = MIN(textw(prompt), mw / 5);
       +        cursor = strlen(text);
       +        XMapRaised(dpy, win);
       +}
       +
       +int
       +main(int argc, char *argv[]) {
       +        unsigned int i;
       +        Bool topbar = True;
       +
       +        /* command line args */
       +        for(i = 1; i < argc; i++)
       +                if(!strcmp(argv[i], "-b"))
       +                        topbar = False;
       +                else if(!strcmp(argv[i], "-e")) {
       +                        if(++i < argc) parent = atoi(argv[i]);
       +                }
       +                else if(!strcmp(argv[i], "-fn")) {
       +                        if(++i < argc) font = argv[i];
       +                }
       +                else if(!strcmp(argv[i], "-nb")) {
       +                        if(++i < argc) normbgcolor = argv[i];
       +                }
       +                else if(!strcmp(argv[i], "-nf")) {
       +                        if(++i < argc) normfgcolor = argv[i];
       +                }
       +                else if(!strcmp(argv[i], "-p")) {
       +                        if(++i < argc) prompt = argv[i];
       +                }
       +                else if(!strcmp(argv[i], "-sb")) {
       +                        if(++i < argc) selbgcolor = argv[i];
       +                }
       +                else if(!strcmp(argv[i], "-sf")) {
       +                        if(++i < argc) selfgcolor = argv[i];
       +                }
       +                else if(!strcmp(argv[i], "-v"))
       +                        eprint("dinput-"VERSION", © 2006-2010 dinput engineers, see LICENSE for details\n");
       +                else if(!*text)
       +                        strncpy(text, argv[i], sizeof text);
       +                else
       +                        eprint("usage: dinput [-b] [-e <xid>] [-fn <font>] [-nb <color>] [-nf <color>]\n"
       +                               "              [-p <prompt>] [-sb <color>] [-sf <color>] [-v] [<text>]\n");
       +        if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
       +                fprintf(stderr, "dinput: warning: no locale support\n");
       +        if(!(dpy = XOpenDisplay(NULL)))
       +                eprint("dinput: cannot open display\n");
       +        screen = DefaultScreen(dpy);
       +        if(!parent)
       +                parent = RootWindow(dpy, screen);
       +
       +        running = grabkeyboard();
       +        setup(topbar);
       +        drawinput();
       +        XSync(dpy, False);
       +        run();
       +        cleanup();
       +        XCloseDisplay(dpy);
       +        return ret;
       +}
 (DIR) diff --git a/dmenu.c b/dmenu.c
       t@@ -21,25 +21,6 @@
        #define MAX(a, b)               ((a) > (b) ? (a) : (b))
        #define IS_UTF8_1ST_CHAR(c)     ((((c) & 0xc0) == 0xc0) || !((c) & 0x80))
        
       -/* enums */
       -enum { ColFG, ColBG, ColLast };
       -
       -/* typedefs */
       -typedef struct {
       -        int x, y, w, h;
       -        unsigned long norm[ColLast];
       -        unsigned long sel[ColLast];
       -        Drawable drawable;
       -        GC gc;
       -        struct {
       -                XFontStruct *xfont;
       -                XFontSet set;
       -                int ascent;
       -                int descent;
       -                int height;
       -        } font;
       -} DC; /* draw context */
       -
        typedef struct Item Item;
        struct Item {
                char *text;
       t@@ -53,22 +34,16 @@ static void calcoffsetsh(void);
        static void calcoffsetsv(void);
        static char *cistrstr(const char *s, const char *sub);
        static void cleanup(void);
       -static void drawcursor(void);
        static void drawmenu(void);
        static void drawmenuh(void);
        static void drawmenuv(void);
       -static void drawtext(const char *text, unsigned long col[ColLast]);
        static void eprint(const char *errstr, ...);
       -static unsigned long getcolor(const char *colstr);
        static Bool grabkeyboard(void);
       -static void initfont(const char *fontstr);
        static void kpress(XKeyEvent * e);
        static void match(char *pattern);
        static void readstdin(void);
        static void run(void);
        static void setup(Bool topbar);
       -static int textnw(const char *text, unsigned int len);
       -static int textw(const char *text);
        
        #include "config.h"
        
       t@@ -81,11 +56,9 @@ static int promptw = 0;
        static int ret = 0;
        static int screen;
        static unsigned int mw, mh;
       -static unsigned int cursor = 0;
        static unsigned int numlockmask = 0;
        static Bool running = True;
        static Display *dpy;
       -static DC dc;
        static Item *allitems = NULL;  /* first of all items */
        static Item *item = NULL;      /* first of pattern matching items */
        static Item *sel = NULL;
       t@@ -98,6 +71,8 @@ static char *(*fstrstr)(const char *, const char *) = strstr;
        static unsigned int lines = 0;
        static void (*calcoffsets)(void) = calcoffsetsh;
        
       +#include "draw.c"
       +
        void
        appenditem(Item *i, Item **list, Item **last) {
                if(!(*last))
       t@@ -161,27 +136,12 @@ cistrstr(const char *s, const char *sub) {
        
        void
        cleanup(void) {
       -        if(dc.font.set)
       -                XFreeFontSet(dpy, dc.font.set);
       -        else
       -                XFreeFont(dpy, dc.font.xfont);
       -        XFreePixmap(dpy, dc.drawable);
       -        XFreeGC(dpy, dc.gc);
       +        dccleanup();
                XDestroyWindow(dpy, win);
                XUngrabKeyboard(dpy, CurrentTime);
        }
        
        void
       -drawcursor(void) {
       -        XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 };
       -
       -        r.x += textnw(text, cursor) + dc.font.height / 2;
       -
       -        XSetForeground(dpy, dc.gc, dc.norm[ColFG]);
       -        XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
       -}
       -
       -void
        drawmenu(void) {
                dc.x = 0;
                dc.y = 0;
       t@@ -199,7 +159,6 @@ drawmenu(void) {
                if(cmdw && item && lines == 0)
                        dc.w = cmdw;
                drawtext(*text ? text : NULL, dc.norm);
       -        drawcursor();
                if(curr) {
                        if(lines > 0)
                                drawmenuv();
       t@@ -244,34 +203,6 @@ drawmenuv(void) {
        }
        
        void
       -drawtext(const char *text, unsigned long col[ColLast]) {
       -        char buf[256];
       -        int i, x, y, h, len, olen;
       -        XRectangle r = { dc.x, dc.y, dc.w, dc.h };
       -
       -        XSetForeground(dpy, dc.gc, col[ColBG]);
       -        XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
       -        if(!text)
       -                return;
       -        olen = strlen(text);
       -        h = dc.font.height;
       -        y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent;
       -        x = dc.x + (h / 2);
       -        /* shorten text if necessary */
       -        for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
       -        if(!len)
       -                return;
       -        memcpy(buf, text, len);
       -        if(len < olen)
       -                for(i = len; i && i > len - 3; buf[--i] = '.');
       -        XSetForeground(dpy, dc.gc, col[ColFG]);
       -        if(dc.font.set)
       -                XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
       -        else
       -                XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
       -}
       -
       -void
        eprint(const char *errstr, ...) {
                va_list ap;
        
       t@@ -281,16 +212,6 @@ eprint(const char *errstr, ...) {
                exit(EXIT_FAILURE);
        }
        
       -unsigned long
       -getcolor(const char *colstr) {
       -        Colormap cmap = DefaultColormap(dpy, screen);
       -        XColor color;
       -
       -        if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
       -                eprint("dmenu: cannot allocate color '%s'\n", colstr);
       -        return color.pixel;
       -}
       -
        Bool
        grabkeyboard(void) {
                unsigned int len;
       t@@ -305,37 +226,6 @@ grabkeyboard(void) {
        }
        
        void
       -initfont(const char *fontstr) {
       -        char *def, **missing = NULL;
       -        int i, n;
       -
       -        if(!fontstr || fontstr[0] == '\0')
       -                eprint("dmenu: cannot load font: '%s'\n", fontstr);
       -        dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
       -        if(missing)
       -                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; i < n; i++) {
       -                        dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
       -                        dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent);
       -                        xfonts++;
       -                }
       -        }
       -        else {
       -                if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
       -                && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
       -                        eprint("dmenu: 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
        kpress(XKeyEvent * e) {
                char buf[sizeof text];
                int num;
       t@@ -381,9 +271,6 @@ kpress(XKeyEvent * e) {
                        case XK_j:
                                ksym = XK_Return;
                                break;
       -                case XK_k:
       -                        text[cursor] = '\0';
       -                        break;
                        case XK_n:
                                ksym = XK_Down;
                                break;
       t@@ -391,67 +278,42 @@ kpress(XKeyEvent * e) {
                                ksym = XK_Up;
                                break;
                        case XK_u:
       -                        memmove(text, text + cursor, sizeof text - cursor + 1);
       -                        cursor = 0;
       +                        text[0] = '\0';
                                match(text);
                                break;
                        case XK_w:
       -                        if(cursor > 0) {
       -                                i = cursor;
       -                                while(i-- > 0 && text[i] == ' ');
       -                                while(i-- > 0 && text[i] != ' ');
       -                                memmove(text + i + 1, text + cursor, sizeof text - cursor + 1);
       -                                cursor = i + 1;
       -                                match(text);
       -                        }
       +                        if(len == 0)
       +                                return;
       +                        i = len;
       +                        while(i-- > 0 && text[i] == ' ');
       +                        while(i-- > 0 && text[i] != ' ');
       +                        text[++i] = '\0';
       +                        match(text);
                                break;
       -                case XK_y:
       -                        {
       -                                FILE *fp;
       -                                char *s;
       -                                if(!(fp = popen("sselp", "r")))
       -                                        eprint("dmenu: cannot popen sselp\n");
       -                                s = fgets(buf, sizeof buf, fp);
       -                                pclose(fp);
       -                                if(s == NULL)
       -                                        return;
       -                        }
       -                        num = strlen(buf);
       -                        if(num && buf[num-1] == '\n')
       -                                buf[--num] = '\0';
       +                case XK_x:
       +                        execlp("dinput", "dinput", text, NULL); /* todo: argv */
       +                        eprint("dmenu: cannot exec dinput:");
                                break;
                        }
                }
                switch(ksym) {
                default:
       -                num = MIN(num, sizeof text - cursor);
       +                num = MIN(num, sizeof text);
                        if(num && !iscntrl((int) buf[0])) {
       -                        memmove(text + cursor + num, text + cursor, sizeof text - cursor - num);
       -                        memcpy(text + cursor, buf, num);
       -                        cursor += num;
       +                        memcpy(text + len, buf, num + 1);
       +                        len += num;
                                match(text);
                        }
                        break;
                case XK_BackSpace:
       -                if(cursor == 0)
       +                if(len == 0)
                                return;
       -                for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++);
       -                memmove(text + cursor - i, text + cursor, sizeof text - cursor + i);
       -                cursor -= i;
       -                match(text);
       -                break;
       -        case XK_Delete:
       -                if(cursor == len)
       -                        return;
       -                for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++);
       -                memmove(text + cursor, text + cursor + i, sizeof text - cursor);
       +                for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++);
       +                len -= i;
       +                text[len] = '\0';
                        match(text);
                        break;
                case XK_End:
       -                if(cursor < len) {
       -                        cursor = len;
       -                        break;
       -                }
                        while(next) {
                                sel = curr = next;
                                calcoffsets();
       t@@ -464,20 +326,10 @@ kpress(XKeyEvent * e) {
                        running = False;
                        return;
                case XK_Home:
       -                if(sel == item) {
       -                        cursor = 0;
       -                        break;
       -                }
                        sel = curr = item;
                        calcoffsets();
                        break;
                case XK_Left:
       -                if(cursor > 0 && (!sel || !sel->left || lines > 0)) {
       -                        while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor]));
       -                        break;
       -                }
       -                if(lines > 0)
       -                        return;
                case XK_Up:
                        if(!sel || !sel->left)
                                return;
       t@@ -508,12 +360,6 @@ kpress(XKeyEvent * e) {
                        running = False;
                        return;
                case XK_Right:
       -                if(cursor < len) {
       -                        while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor]));
       -                        break;
       -                }
       -                if(lines > 0)
       -                        return;
                case XK_Down:
                        if(!sel || !sel->right)
                                return;
       t@@ -527,7 +373,6 @@ kpress(XKeyEvent * e) {
                        if(!sel)
                                return;
                        strncpy(text, sel->text, sizeof text);
       -                cursor = strlen(text);
                        match(text);
                        break;
                }
       t@@ -690,11 +535,7 @@ setup(Bool topbar) {
                                CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
        
                /* pixmap */
       -        dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen));
       -        dc.gc = XCreateGC(dpy, parent, 0, NULL);
       -        XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
       -        if(!dc.font.set)
       -                XSetFont(dpy, dc.gc, dc.font.xfont->fid);
       +        dcsetup();
                if(maxname)
                        cmdw = MIN(textw(maxname), mw / 3);
                if(prompt)
       t@@ -705,22 +546,6 @@ setup(Bool topbar) {
        }
        
        int
       -textnw(const char *text, unsigned int 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);
       -}
       -
       -int
       -textw(const char *text) {
       -        return textnw(text, strlen(text)) + dc.font.height;
       -}
       -
       -int
        main(int argc, char *argv[]) {
                unsigned int i;
                Bool topbar = True;
 (DIR) diff --git a/draw.c b/draw.c
       t@@ -0,0 +1,143 @@
       +/* See LICENSE file for copyright and license details. */
       +
       +/* enums */
       +enum { ColFG, ColBG, ColLast };
       +
       +/* typedefs */
       +typedef struct {
       +        int x, y, w, h;
       +        unsigned long norm[ColLast];
       +        unsigned long sel[ColLast];
       +        Drawable drawable;
       +        GC gc;
       +        struct {
       +                XFontStruct *xfont;
       +                XFontSet set;
       +                int ascent;
       +                int descent;
       +                int height;
       +        } font;
       +} DC; /* draw context */
       +
       +/* forward declarations */
       +static void dccleanup(void);
       +static void dcsetup(void);
       +static void drawtext(const char *text, unsigned long col[ColLast]);
       +static unsigned long getcolor(const char *colstr);
       +static void initfont(const char *fontstr);
       +static int textnw(const char *text, unsigned int len);
       +static int textw(const char *text);
       +
       +static DC dc;
       +
       +void
       +dccleanup(void) {
       +        if(dc.font.set)
       +                XFreeFontSet(dpy, dc.font.set);
       +        else
       +                XFreeFont(dpy, dc.font.xfont);
       +        XFreePixmap(dpy, dc.drawable);
       +        XFreeGC(dpy, dc.gc);
       +}
       +
       +void
       +dcsetup() {
       +        /* style */
       +        dc.norm[ColBG] = getcolor(normbgcolor);
       +        dc.norm[ColFG] = getcolor(normfgcolor);
       +        dc.sel[ColBG] = getcolor(selbgcolor);
       +        dc.sel[ColFG] = getcolor(selfgcolor);
       +        initfont(font);
       +
       +        /* pixmap */
       +        dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen));
       +        dc.gc = XCreateGC(dpy, parent, 0, NULL);
       +        XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
       +        if(!dc.font.set)
       +                XSetFont(dpy, dc.gc, dc.font.xfont->fid);
       +}
       +
       +void
       +drawtext(const char *text, unsigned long col[ColLast]) {
       +        char buf[256];
       +        int i, x, y, h, len, olen;
       +        XRectangle r = { dc.x, dc.y, dc.w, dc.h };
       +
       +        XSetForeground(dpy, dc.gc, col[ColBG]);
       +        XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
       +        if(!text)
       +                return;
       +        olen = strlen(text);
       +        h = dc.font.height;
       +        y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent;
       +        x = dc.x + (h / 2);
       +        /* shorten text if necessary */
       +        for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
       +        if(!len)
       +                return;
       +        memcpy(buf, text, len);
       +        if(len < olen)
       +                for(i = len; i && i > len - 3; buf[--i] = '.');
       +        XSetForeground(dpy, dc.gc, col[ColFG]);
       +        if(dc.font.set)
       +                XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
       +        else
       +                XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
       +}
       +
       +unsigned long
       +getcolor(const char *colstr) {
       +        Colormap cmap = DefaultColormap(dpy, screen);
       +        XColor color;
       +
       +        if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
       +                eprint("drawtext: cannot allocate color '%s'\n", colstr);
       +        return color.pixel;
       +}
       +
       +void
       +initfont(const char *fontstr) {
       +        char *def, **missing = NULL;
       +        int i, n;
       +
       +        if(!fontstr || fontstr[0] == '\0')
       +                eprint("drawtext: cannot load font: '%s'\n", fontstr);
       +        dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
       +        if(missing)
       +                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; i < n; i++) {
       +                        dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
       +                        dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent);
       +                        xfonts++;
       +                }
       +        }
       +        else {
       +                if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
       +                && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
       +                        eprint("drawtext: 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;
       +}
       +
       +int
       +textnw(const char *text, unsigned int 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);
       +}
       +
       +int
       +textw(const char *text) {
       +        return textnw(text, strlen(text)) + dc.font.height;
       +}