[CONTACT]

[ABOUT]

[POLICY]

git clone dmenu dmenu Log gph

Found at: gopher.r-36.net:70/scm/dmenu/commit/b4e63454e5768d38682405d252a81b1149273c0b.gph

tinitial dmenu / dinput separation - dmenu - Dmenu fork with xft fonts.

git clone git://r-36.net/dmenu

Log

Files

Refs

README

LICENSE

---

commit b4e63454e5768d38682405d252a81b1149273c0b

parent bff1526d31faaf289804174f6477d61736e82443

Author: Connor Lane Smith

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

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

diff --git a/dinput.c b/dinput.c

t@@ -0,0 +1,387 @@
+/* See LICENSE file for copyright and license details. */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifdef XINERAMA
+#include 
+#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 ] [-fn ] [-nb ] [-nf ]\n"
+                               "              [-p ] [-sb ] [-sf ] [-v] []\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;
+}

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;

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;
+}
.


AD: