X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=dmenu.c;h=8c0ea41432fb6f08961154c024a9bc6ba9d900ec;hb=9f3b0c6ea843340b87a045ea0afd2d1b33425eee;hp=ec6260222c3a103944d9797d828970641bd54476;hpb=97797d90a6396c2fc0109a7bd47bb260d1143c8a;p=dmenu.git diff --git a/dmenu.c b/dmenu.c index ec62602..8c0ea41 100644 --- a/dmenu.c +++ b/dmenu.c @@ -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; @@ -53,24 +34,19 @@ 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 dinput(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 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" +#include "draw.h" /* variables */ static char *maxname = NULL; @@ -80,22 +56,23 @@ static int cmdw = 0; static int promptw = 0; static int ret = 0; static int screen; -static unsigned int mw, mh; -static unsigned int cursor = 0; +static unsigned int lines = 0; static unsigned int numlockmask = 0; +static unsigned int mw, mh; +static unsigned long normcol[ColLast]; +static unsigned long selcol[ColLast]; static Bool running = True; -static Display *dpy; static DC dc; +static Display *dpy; static Item *allitems = NULL; /* first of all items */ static Item *item = NULL; /* first of pattern matching items */ static Item *sel = NULL; static Item *next = NULL; static Item *prev = NULL; static Item *curr = NULL; -static Window parent, win; +static Window win, parent; static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; static char *(*fstrstr)(const char *, const char *) = strstr; -static unsigned int lines = 0; static void (*calcoffsets)(void) = calcoffsetsh; void @@ -113,28 +90,25 @@ void calcoffsetsh(void) { unsigned int w; - if(!curr) - return; - w = promptw + cmdw + 2 * spaceitem; - for(next = curr; next && w < mw; next=next->right) - w += MIN(textw(next->text), mw / 3); - w = promptw + cmdw + 2 * spaceitem; - for(prev = curr; prev && prev->left && w < mw; prev=prev->left) - w += MIN(textw(prev->left->text), mw / 3); + w = promptw + cmdw + (2 * spaceitem); + for(next = curr; next; next = next->right) + if((w += MIN(textw(&dc, next->text), mw / 3)) > mw) + break; + w = promptw + cmdw + (2 * spaceitem); + for(prev = curr; prev && prev->left; prev = prev->left) + if((w += MIN(textw(&dc, prev->left->text), mw / 3)) > mw) + break; } void calcoffsetsv(void) { - unsigned int h; + unsigned int i; - if(!curr) - return; - h = (dc.font.height + 2) * lines; - for(next = curr; next && h > 0; next = next->right) - h -= dc.font.height + 2; - h = (dc.font.height + 2) * lines; - for(prev = curr; prev && prev->left && h > 0; prev = prev->left) - h -= dc.font.height + 2; + next = prev = curr; + for(i = 0; i < lines && next; i++) + next = next->right; + for(i = 0; i < lines && prev && prev->left; i++) + prev = prev->left; } char * @@ -161,24 +135,24 @@ 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); + Item *itm; + + while(allitems) { + itm = allitems->next; + free(allitems->text); + free(allitems); + allitems = itm; + } + cleanupdraw(&dc); 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); +dinput(void) { + cleanup(); + execlp("dinput", "dinput", text, NULL); /* todo: argv */ + eprint("cannot exec dinput\n"); } void @@ -187,19 +161,18 @@ drawmenu(void) { dc.y = 0; dc.w = mw; dc.h = mh; - drawtext(NULL, dc.norm); + drawtext(&dc, NULL, normcol); /* print prompt? */ if(prompt) { dc.w = promptw; - drawtext(prompt, dc.sel); + drawtext(&dc, prompt, selcol); dc.x += dc.w; } dc.w = mw - dc.x; /* print command */ if(cmdw && item && lines == 0) dc.w = cmdw; - drawtext(*text ? text : NULL, dc.norm); - drawcursor(); + drawtext(&dc, *text ? text : NULL, normcol); if(curr) { if(lines > 0) drawmenuv(); @@ -216,16 +189,16 @@ drawmenuh(void) { dc.x += cmdw; dc.w = spaceitem; - drawtext(curr->left ? "<" : NULL, dc.norm); + drawtext(&dc, curr->left ? "<" : NULL, normcol); dc.x += dc.w; - for(i = curr; i != next; i=i->right) { - dc.w = MIN(textw(i->text), mw / 3); - drawtext(i->text, (sel == i) ? dc.sel : dc.norm); + for(i = curr; i != next; i = i->right) { + dc.w = MIN(textw(&dc, i->text), mw / 3); + drawtext(&dc, i->text, (sel == i) ? selcol : normcol); dc.x += dc.w; } dc.w = spaceitem; dc.x = mw - dc.w; - drawtext(next ? ">" : NULL, dc.norm); + drawtext(&dc, next ? ">" : NULL, normcol); } void @@ -235,60 +208,12 @@ drawmenuv(void) { dc.w = mw - dc.x; dc.h = dc.font.height + 2; dc.y = dc.h; - for(i = curr; i != next; i=i->right) { - drawtext(i->text, (sel == i) ? dc.sel : dc.norm); + for(i = curr; i != next; i = i->right) { + drawtext(&dc, i->text, (sel == i) ? selcol : normcol); dc.y += dc.h; } dc.h = mh - dc.y; - drawtext(NULL, dc.norm); -} - -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; - - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - 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; + drawtext(&dc, NULL, normcol); } Bool @@ -305,38 +230,7 @@ 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) { +kpress(XKeyEvent *e) { char buf[sizeof text]; int num; unsigned int i, len; @@ -355,17 +249,23 @@ kpress(XKeyEvent * e) { /* first check if a control mask is omitted */ if(e->state & ControlMask) { switch(tolower(ksym)) { - default: /* ignore other control sequences */ + 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; @@ -373,95 +273,51 @@ kpress(XKeyEvent * e) { ksym = XK_Tab; break; case XK_j: + case XK_m: ksym = XK_Return; break; - case XK_k: - text[cursor] = '\0'; + case XK_n: + ksym = XK_Down; + break; + case XK_p: + 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); - } - break; - } - } - if(CLEANMASK(e->state) & Mod1Mask) { - switch(ksym) { - default: - return; - case XK_h: - ksym = XK_Left; - break; - case XK_l: - ksym = XK_Right; - break; - case XK_j: - ksym = XK_Next; - break; - case XK_k: - ksym = XK_Prior; - break; - case XK_g: - ksym = XK_Home; - break; - case XK_G: - ksym = XK_End; + if(len == 0) + return; + i = len; + while(i-- > 0 && text[i] == ' '); + while(i-- > 0 && text[i] != ' '); + text[++i] = '\0'; + match(text); break; - case XK_p: - { - 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: + 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: - 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(); @@ -474,26 +330,18 @@ kpress(XKeyEvent * e) { running = False; return; case XK_Home: - if(sel == item) { - cursor = 0; - break; - } sel = curr = item; calcoffsets(); break; case XK_Left: case XK_Up: - if(sel && sel->left){ - sel = sel->left; - if(sel->right == curr) { - curr = prev; - calcoffsets(); - } - } - else if(cursor > 0) - while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); - else + if(!sel || !sel->left) return; + sel = sel->left; + if(sel->right == curr) { + curr = prev; + calcoffsets(); + } break; case XK_Next: if(!next) @@ -517,24 +365,18 @@ kpress(XKeyEvent * e) { return; case XK_Right: case XK_Down: - if(cursor < len) - while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); - else if(sel && sel->right) { - sel = sel->right; - if(sel == next) { - curr = next; - calcoffsets(); - } - } - else + if(!sel || !sel->right) return; + sel = sel->right; + if(sel == next) { + curr = next; + calcoffsets(); + } break; case XK_Tab: - if(!sel) - return; - strncpy(text, sel->text, sizeof text); - cursor = strlen(text); - match(text); + if(sel) + strncpy(text, sel->text, sizeof text); + dinput(); break; } drawmenu(); @@ -593,11 +435,11 @@ readstdin(void) { if(buf[len-1] == '\n') buf[--len] = '\0'; if(!(p = strdup(buf))) - eprint("dmenu: cannot strdup %u bytes\n", len); + eprint("cannot strdup %u bytes\n", len); if((max = MAX(max, len)) == len) maxname = p; if(!(new = malloc(sizeof *new))) - eprint("dmenu: cannot malloc %u bytes\n", sizeof *new); + eprint("cannot malloc %u bytes\n", sizeof *new); new->next = new->left = new->right = NULL; new->text = p; if(!i) @@ -650,17 +492,17 @@ setup(Bool topbar) { } 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); + dc.dpy = dpy; + normcol[ColBG] = getcolor(&dc, normbgcolor); + normcol[ColFG] = getcolor(&dc, normfgcolor); + selcol[ColBG] = getcolor(&dc, selbgcolor); + selcol[ColFG] = getcolor(&dc, selfgcolor); + initfont(&dc, font); /* menu window */ wa.override_redirect = True; wa.background_pixmap = ParentRelative; - wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask; + wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; /* menu window geometry */ mh = (dc.font.height + 2) * (lines + 1); @@ -695,43 +537,23 @@ setup(Bool topbar) { DefaultVisual(dpy, screen), 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); + setupdraw(&dc, win); if(maxname) - cmdw = MIN(textw(maxname), mw / 3); + cmdw = MIN(textw(&dc, maxname), mw / 3); if(prompt) - promptw = MIN(textw(prompt), mw / 5); + promptw = MIN(textw(&dc, prompt), mw / 5); text[0] = '\0'; match(text); XMapRaised(dpy, win); } -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; /* command line args */ + progname = argv[0]; for(i = 1; i < argc; i++) if(!strcmp(argv[i], "-i")) { fstrncmp = strncasecmp; @@ -773,7 +595,7 @@ main(int argc, char *argv[]) { if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fprintf(stderr, "dmenu: warning: no locale support\n"); if(!(dpy = XOpenDisplay(NULL))) - eprint("dmenu: cannot open display\n"); + eprint("cannot open display\n"); screen = DefaultScreen(dpy); if(!parent) parent = RootWindow(dpy, screen);