@@ -3,7 +3,7 @@ | |||
include config.mk | |||
all: options dmenu dmenu_path | |||
all: options dmenu dmenu_path config.mk | |||
options: | |||
@echo dmenu build options: | |||
@@ -11,16 +11,20 @@ options: | |||
@echo "LDFLAGS = ${LDFLAGS}" | |||
@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: | |||
@echo CC -o $@ | |||
@${CC} -o $@ $< ${CFLAGS} ${LDFLAGS} | |||
@${CC} -o $@ $+ ${LDFLAGS} | |||
clean: | |||
@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 | |||
@echo creating dist tarball | |||
@@ -7,8 +7,6 @@ Requirements | |||
------------ | |||
In order to build dmenu you need the Xlib header files. | |||
You also need libdc, available from http://hg.suckless.org/libdraw | |||
Installation | |||
------------ | |||
@@ -16,7 +16,7 @@ XINERAMAFLAGS = -DXINERAMA | |||
# includes and libs | |||
INCS = -I${X11INC} | |||
LIBS = -L${X11LIB} -ldc -lX11 ${XINERAMALIBS} | |||
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} | |||
# flags | |||
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | |||
@@ -10,7 +10,7 @@ | |||
#ifdef XINERAMA | |||
#include <X11/extensions/Xinerama.h> | |||
#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 MIN(a,b) ((a) < (b) ? (a) : (b)) | |||
@@ -81,13 +81,13 @@ calcoffsets(void) { | |||
if(lines > 0) | |||
n = lines * bh; | |||
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) | |||
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; | |||
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; | |||
} | |||
@@ -99,41 +99,41 @@ drawmenu(void) { | |||
dc->x = 0; | |||
dc->y = 0; | |||
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) { | |||
dc->w = promptw; | |||
dc_drawtext(dc, prompt, selcol); | |||
drawtext(dc, prompt, selcol); | |||
dc->x = dc->w; | |||
} | |||
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) { | |||
dc->w = mw - dc->x; | |||
for(item = curr; item != next; item = item->right) { | |||
dc->y += dc->h; | |||
dc_drawtext(dc, item->text, (item == sel) ? selcol : normcol); | |||
drawtext(dc, item->text, (item == sel) ? selcol : normcol); | |||
} | |||
} | |||
else if(matches) { | |||
dc->x += inputw; | |||
dc->w = dc_textw(dc, "<"); | |||
dc->w = textw(dc, "<"); | |||
if(curr->left) | |||
dc_drawtext(dc, "<", normcol); | |||
drawtext(dc, "<", normcol); | |||
for(item = curr; item != next; item = item->right) { | |||
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; | |||
if(next) | |||
dc_drawtext(dc, ">", normcol); | |||
drawtext(dc, ">", normcol); | |||
} | |||
dc_map(dc, win, mw, mh); | |||
mapdc(dc, win, mw, mh); | |||
} | |||
char * | |||
@@ -398,7 +398,7 @@ readstdin(void) { | |||
if(!(item->text = strdup(buf))) | |||
eprintf("cannot strdup %u bytes\n", strlen(buf)+1); | |||
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); | |||
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 */ | |||
bh = dc->font.height + 2; | |||
@@ -481,9 +481,9 @@ setup(void) { | |||
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | |||
grabkeyboard(); | |||
dc_resize(dc, mw, mh); | |||
resizedc(dc, mw, mh); | |||
inputw = MIN(inputw, mw/3); | |||
promptw = prompt ? dc_textw(dc, prompt) : 0; | |||
promptw = prompt ? textw(dc, prompt) : 0; | |||
XMapRaised(dc->dpy, win); | |||
text[0] = '\0'; | |||
match(); | |||
@@ -533,8 +533,8 @@ main(int argc, char *argv[]) { | |||
else | |||
usage(); | |||
dc = dc_init(); | |||
dc_font(dc, font); | |||
dc = initdc(); | |||
initfont(dc, font); | |||
readstdin(); | |||
setup(); | |||
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; |