@@ -3,7 +3,7 @@ | |||||
include config.mk | include config.mk | ||||
all: options dmenu dmenu_path | |||||
all: options dmenu dmenu_path config.mk | |||||
options: | options: | ||||
@echo dmenu build options: | @echo dmenu build options: | ||||
@@ -11,16 +11,20 @@ options: | |||||
@echo "LDFLAGS = ${LDFLAGS}" | @echo "LDFLAGS = ${LDFLAGS}" | ||||
@echo "CC = ${CC}" | @echo "CC = ${CC}" | ||||
dmenu: dmenu.c config.mk | |||||
dmenu_path: dmenu_path.c | |||||
dmenu: dmenu.o draw.o | |||||
dmenu_path: dmenu_path.o | |||||
.c.o: | |||||
@echo CC -c $< | |||||
@${CC} -c $< ${CFLAGS} | |||||
dmenu dmenu_path: | dmenu dmenu_path: | ||||
@echo CC -o $@ | @echo CC -o $@ | ||||
@${CC} -o $@ $< ${CFLAGS} ${LDFLAGS} | |||||
@${CC} -o $@ $+ ${LDFLAGS} | |||||
clean: | clean: | ||||
@echo cleaning | @echo cleaning | ||||
@rm -f dmenu dmenu_path dmenu-${VERSION}.tar.gz | |||||
@rm -f dmenu dmenu.o draw.o dmenu_path dmenu_path.o dmenu-${VERSION}.tar.gz | |||||
dist: clean | dist: clean | ||||
@echo creating dist tarball | @echo creating dist tarball | ||||
@@ -7,8 +7,6 @@ Requirements | |||||
------------ | ------------ | ||||
In order to build dmenu you need the Xlib header files. | In order to build dmenu you need the Xlib header files. | ||||
You also need libdc, available from http://hg.suckless.org/libdraw | |||||
Installation | Installation | ||||
------------ | ------------ | ||||
@@ -16,7 +16,7 @@ XINERAMAFLAGS = -DXINERAMA | |||||
# includes and libs | # includes and libs | ||||
INCS = -I${X11INC} | INCS = -I${X11INC} | ||||
LIBS = -L${X11LIB} -ldc -lX11 ${XINERAMALIBS} | |||||
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} | |||||
# flags | # flags | ||||
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | ||||
@@ -10,7 +10,7 @@ | |||||
#ifdef XINERAMA | #ifdef XINERAMA | ||||
#include <X11/extensions/Xinerama.h> | #include <X11/extensions/Xinerama.h> | ||||
#endif | #endif | ||||
#include <dc.h> | |||||
#include "draw.h" | |||||
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) | #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 MIN(a,b) ((a) < (b) ? (a) : (b)) | ||||
@@ -81,13 +81,13 @@ calcoffsets(void) { | |||||
if(lines > 0) | if(lines > 0) | ||||
n = lines * bh; | n = lines * bh; | ||||
else | else | ||||
n = mw - (promptw + inputw + dc_textw(dc, "<") + dc_textw(dc, ">")); | |||||
n = mw - (promptw + inputw + textw(dc, "<") + textw(dc, ">")); | |||||
for(i = 0, next = curr; next; next = next->right) | for(i = 0, next = curr; next; next = next->right) | ||||
if((i += (lines > 0) ? bh : MIN(dc_textw(dc, next->text), n)) > n) | |||||
if((i += (lines > 0) ? bh : MIN(textw(dc, next->text), n)) > n) | |||||
break; | break; | ||||
for(i = 0, prev = curr; prev && prev->left; prev = prev->left) | for(i = 0, prev = curr; prev && prev->left; prev = prev->left) | ||||
if((i += (lines > 0) ? bh : MIN(dc_textw(dc, prev->left->text), n)) > n) | |||||
if((i += (lines > 0) ? bh : MIN(textw(dc, prev->left->text), n)) > n) | |||||
break; | break; | ||||
} | } | ||||
@@ -99,41 +99,41 @@ drawmenu(void) { | |||||
dc->x = 0; | dc->x = 0; | ||||
dc->y = 0; | dc->y = 0; | ||||
dc->h = bh; | dc->h = bh; | ||||
dc_drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol)); | |||||
drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol)); | |||||
if(prompt) { | if(prompt) { | ||||
dc->w = promptw; | dc->w = promptw; | ||||
dc_drawtext(dc, prompt, selcol); | |||||
drawtext(dc, prompt, selcol); | |||||
dc->x = dc->w; | dc->x = dc->w; | ||||
} | } | ||||
dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw; | dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw; | ||||
dc_drawtext(dc, text, normcol); | |||||
if((curpos = dc_textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w) | |||||
dc_drawrect(dc, curpos, 2, 1, dc->h - 4, True, FG(dc, normcol)); | |||||
drawtext(dc, text, normcol); | |||||
if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w) | |||||
drawrect(dc, curpos, 2, 1, dc->h - 4, True, FG(dc, normcol)); | |||||
if(lines > 0) { | if(lines > 0) { | ||||
dc->w = mw - dc->x; | dc->w = mw - dc->x; | ||||
for(item = curr; item != next; item = item->right) { | for(item = curr; item != next; item = item->right) { | ||||
dc->y += dc->h; | dc->y += dc->h; | ||||
dc_drawtext(dc, item->text, (item == sel) ? selcol : normcol); | |||||
drawtext(dc, item->text, (item == sel) ? selcol : normcol); | |||||
} | } | ||||
} | } | ||||
else if(matches) { | else if(matches) { | ||||
dc->x += inputw; | dc->x += inputw; | ||||
dc->w = dc_textw(dc, "<"); | |||||
dc->w = textw(dc, "<"); | |||||
if(curr->left) | if(curr->left) | ||||
dc_drawtext(dc, "<", normcol); | |||||
drawtext(dc, "<", normcol); | |||||
for(item = curr; item != next; item = item->right) { | for(item = curr; item != next; item = item->right) { | ||||
dc->x += dc->w; | dc->x += dc->w; | ||||
dc->w = MIN(dc_textw(dc, item->text), mw - dc->x - dc_textw(dc, ">")); | |||||
dc_drawtext(dc, item->text, (item == sel) ? selcol : normcol); | |||||
dc->w = MIN(textw(dc, item->text), mw - dc->x - textw(dc, ">")); | |||||
drawtext(dc, item->text, (item == sel) ? selcol : normcol); | |||||
} | } | ||||
dc->w = dc_textw(dc, ">"); | |||||
dc->w = textw(dc, ">"); | |||||
dc->x = mw - dc->w; | dc->x = mw - dc->w; | ||||
if(next) | if(next) | ||||
dc_drawtext(dc, ">", normcol); | |||||
drawtext(dc, ">", normcol); | |||||
} | } | ||||
dc_map(dc, win, mw, mh); | |||||
mapdc(dc, win, mw, mh); | |||||
} | } | ||||
char * | char * | ||||
@@ -398,7 +398,7 @@ readstdin(void) { | |||||
if(!(item->text = strdup(buf))) | if(!(item->text = strdup(buf))) | ||||
eprintf("cannot strdup %u bytes\n", strlen(buf)+1); | eprintf("cannot strdup %u bytes\n", strlen(buf)+1); | ||||
item->next = item->left = item->right = NULL; | item->next = item->left = item->right = NULL; | ||||
inputw = MAX(inputw, dc_textw(dc, item->text)); | |||||
inputw = MAX(inputw, textw(dc, item->text)); | |||||
} | } | ||||
} | } | ||||
@@ -439,10 +439,10 @@ setup(void) { | |||||
root = RootWindow(dc->dpy, screen); | root = RootWindow(dc->dpy, screen); | ||||
utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); | utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); | ||||
normcol[ColBG] = dc_color(dc, normbgcolor); | |||||
normcol[ColFG] = dc_color(dc, normfgcolor); | |||||
selcol[ColBG] = dc_color(dc, selbgcolor); | |||||
selcol[ColFG] = dc_color(dc, selfgcolor); | |||||
normcol[ColBG] = getcolor(dc, normbgcolor); | |||||
normcol[ColFG] = getcolor(dc, normfgcolor); | |||||
selcol[ColBG] = getcolor(dc, selbgcolor); | |||||
selcol[ColFG] = getcolor(dc, selfgcolor); | |||||
/* menu geometry */ | /* menu geometry */ | ||||
bh = dc->font.height + 2; | bh = dc->font.height + 2; | ||||
@@ -481,9 +481,9 @@ setup(void) { | |||||
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||||
grabkeyboard(); | grabkeyboard(); | ||||
dc_resize(dc, mw, mh); | |||||
resizedc(dc, mw, mh); | |||||
inputw = MIN(inputw, mw/3); | inputw = MIN(inputw, mw/3); | ||||
promptw = prompt ? dc_textw(dc, prompt) : 0; | |||||
promptw = prompt ? textw(dc, prompt) : 0; | |||||
XMapRaised(dc->dpy, win); | XMapRaised(dc->dpy, win); | ||||
text[0] = '\0'; | text[0] = '\0'; | ||||
match(); | match(); | ||||
@@ -533,8 +533,8 @@ main(int argc, char *argv[]) { | |||||
else | else | ||||
usage(); | usage(); | ||||
dc = dc_init(); | |||||
dc_font(dc, font); | |||||
dc = initdc(); | |||||
initfont(dc, font); | |||||
readstdin(); | readstdin(); | ||||
setup(); | setup(); | ||||
run(); | run(); | ||||
@@ -0,0 +1,196 @@ | |||||
/* See LICENSE file for copyright and license details. */ | |||||
#include <locale.h> | |||||
#include <stdarg.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <X11/Xlib.h> | |||||
#include "draw.h" | |||||
#define MAX(a, b) ((a) > (b) ? (a) : (b)) | |||||
#define MIN(a, b) ((a) < (b) ? (a) : (b)) | |||||
#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG]) | |||||
#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG]) | |||||
#define DEFFONT "fixed" | |||||
static Bool loadfont(DC *dc, const char *fontstr); | |||||
void | |||||
drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) { | |||||
XRectangle r = { dc->x + x, dc->y + y, w, h }; | |||||
if(!fill) { | |||||
r.width -= 1; | |||||
r.height -= 1; | |||||
} | |||||
XSetForeground(dc->dpy, dc->gc, color); | |||||
(fill ? XFillRectangles : XDrawRectangles)(dc->dpy, dc->canvas, dc->gc, &r, 1); | |||||
} | |||||
void | |||||
drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { | |||||
char buf[256]; | |||||
size_t n, mn; | |||||
/* shorten text if necessary */ | |||||
n = strlen(text); | |||||
for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) > dc->w - dc->font.height/2; mn--) | |||||
if(mn == 0) | |||||
return; | |||||
memcpy(buf, text, mn); | |||||
if(mn < n) | |||||
for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.'); | |||||
drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col)); | |||||
drawtextn(dc, buf, mn, col); | |||||
} | |||||
void | |||||
drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) { | |||||
int x, y; | |||||
x = dc->x + dc->font.height/2; | |||||
y = dc->y + dc->font.ascent+1; | |||||
XSetForeground(dc->dpy, dc->gc, FG(dc, col)); | |||||
if(dc->font.set) | |||||
XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n); | |||||
else { | |||||
XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); | |||||
XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n); | |||||
} | |||||
} | |||||
void | |||||
eprintf(const char *fmt, ...) { | |||||
va_list ap; | |||||
fprintf(stderr, "%s: ", progname); | |||||
va_start(ap, fmt); | |||||
vfprintf(stderr, fmt, ap); | |||||
va_end(ap); | |||||
exit(EXIT_FAILURE); | |||||
} | |||||
void | |||||
freedc(DC *dc) { | |||||
if(dc->font.set) | |||||
XFreeFontSet(dc->dpy, dc->font.set); | |||||
if(dc->font.xfont) | |||||
XFreeFont(dc->dpy, dc->font.xfont); | |||||
if(dc->canvas) | |||||
XFreePixmap(dc->dpy, dc->canvas); | |||||
XFreeGC(dc->dpy, dc->gc); | |||||
XCloseDisplay(dc->dpy); | |||||
free(dc); | |||||
} | |||||
unsigned long | |||||
getcolor(DC *dc, const char *colstr) { | |||||
Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)); | |||||
XColor color; | |||||
if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color)) | |||||
eprintf("cannot allocate color '%s'\n", colstr); | |||||
return color.pixel; | |||||
} | |||||
DC * | |||||
initdc(void) { | |||||
DC *dc; | |||||
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | |||||
weprintf("no locale support\n"); | |||||
if(!(dc = malloc(sizeof *dc))) | |||||
eprintf("cannot malloc %u bytes\n", sizeof *dc); | |||||
if(!(dc->dpy = XOpenDisplay(NULL))) | |||||
eprintf("cannot open display\n"); | |||||
dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL); | |||||
XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter); | |||||
dc->font.xfont = NULL; | |||||
dc->font.set = NULL; | |||||
dc->canvas = None; | |||||
return dc; | |||||
} | |||||
void | |||||
initfont(DC *dc, const char *fontstr) { | |||||
if(!loadfont(dc, fontstr ? fontstr : DEFFONT)) { | |||||
if(fontstr != NULL) | |||||
weprintf("cannot load font '%s'\n", fontstr); | |||||
if(fontstr == NULL || !loadfont(dc, DEFFONT)) | |||||
eprintf("cannot load font '%s'\n", DEFFONT); | |||||
} | |||||
dc->font.height = dc->font.ascent + dc->font.descent; | |||||
} | |||||
Bool | |||||
loadfont(DC *dc, const char *fontstr) { | |||||
char *def, **missing; | |||||
int i, n; | |||||
if(!*fontstr) | |||||
return False; | |||||
if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { | |||||
char **names; | |||||
XFontStruct **xfonts; | |||||
n = XFontsOfFontSet(dc->font.set, &xfonts, &names); | |||||
for(i = dc->font.ascent = dc->font.descent = 0; i < n; i++) { | |||||
dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); | |||||
dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); | |||||
} | |||||
} | |||||
else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { | |||||
dc->font.ascent = dc->font.xfont->ascent; | |||||
dc->font.descent = dc->font.xfont->descent; | |||||
} | |||||
if(missing) | |||||
XFreeStringList(missing); | |||||
return (dc->font.set || dc->font.xfont); | |||||
} | |||||
void | |||||
mapdc(DC *dc, Window win, unsigned int w, unsigned int h) { | |||||
XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0); | |||||
} | |||||
void | |||||
resizedc(DC *dc, unsigned int w, unsigned int h) { | |||||
if(dc->canvas) | |||||
XFreePixmap(dc->dpy, dc->canvas); | |||||
dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h, | |||||
DefaultDepth(dc->dpy, DefaultScreen(dc->dpy))); | |||||
dc->x = dc->y = 0; | |||||
dc->w = w; | |||||
dc->h = h; | |||||
dc->invert = False; | |||||
} | |||||
int | |||||
textnw(DC *dc, const char *text, size_t len) { | |||||
if(dc->font.set) { | |||||
XRectangle r; | |||||
XmbTextExtents(dc->font.set, text, len, NULL, &r); | |||||
return r.width; | |||||
} | |||||
return XTextWidth(dc->font.xfont, text, len); | |||||
} | |||||
int | |||||
textw(DC *dc, const char *text) { | |||||
return textnw(dc, text, strlen(text)) + dc->font.height; | |||||
} | |||||
void | |||||
weprintf(const char *fmt, ...) { | |||||
va_list ap; | |||||
fprintf(stderr, "%s: warning: ", progname); | |||||
va_start(ap, fmt); | |||||
vfprintf(stderr, fmt, ap); | |||||
va_end(ap); | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/* See LICENSE file for copyright and license details. */ | |||||
#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG]) | |||||
#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG]) | |||||
enum { ColBG, ColFG, ColBorder, ColLast }; | |||||
typedef struct { | |||||
int x, y, w, h; | |||||
Bool invert; | |||||
Display *dpy; | |||||
GC gc; | |||||
Pixmap canvas; | |||||
struct { | |||||
int ascent; | |||||
int descent; | |||||
int height; | |||||
XFontSet set; | |||||
XFontStruct *xfont; | |||||
} font; | |||||
} DC; /* draw context */ | |||||
unsigned long getcolor(DC *dc, const char *colstr); | |||||
void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color); | |||||
void drawtext(DC *dc, const char *text, unsigned long col[ColLast]); | |||||
void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]); | |||||
void initfont(DC *dc, const char *fontstr); | |||||
void freedc(DC *dc); | |||||
DC *initdc(void); | |||||
void mapdc(DC *dc, Window win, unsigned int w, unsigned int h); | |||||
void resizedc(DC *dc, unsigned int w, unsigned int h); | |||||
int textnw(DC *dc, const char *text, size_t len); | |||||
int textw(DC *dc, const char *text); | |||||
void eprintf(const char *fmt, ...); | |||||
void weprintf(const char *fmt, ...); | |||||
const char *progname; |