Compare commits

..

No commits in common. "master" and "4.7" have entirely different histories.
master ... 4.7

11 changed files with 210 additions and 396 deletions

12
LICENSE
View File

@ -1,15 +1,13 @@
MIT/X Consortium License MIT/X Consortium License
© 2006-2019 Anselm R Garbe <anselm@garbe.ca> © 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com> © 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2006-2007 Michał Janeczek <janeczek@gmail.com>
© 2007 Kris Maglione <jg@suckless.org>
© 2009 Gottox <gottox@s01.de> © 2009 Gottox <gottox@s01.de>
© 2009 Markus Schnalke <meillo@marmaro.de> © 2009 Markus Schnalke <meillo@marmaro.de>
© 2009 Evan Gates <evan.gates@gmail.com> © 2009 Evan Gates <evan.gates@gmail.com>
© 2010-2012 Connor Lane Smith <cls@lubutu.com> © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2014-2022 Hiltjo Posthuma <hiltjo@codemadness.org> © 2006-2007 Michał Janeczek <janeczek at gmail dot com>
© 2015-2019 Quentin Rameau <quinq@fifth.space> © 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org>
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),

View File

@ -4,61 +4,71 @@
include config.mk include config.mk
SRC = drw.c dmenu.c stest.c util.c SRC = drw.c dmenu.c stest.c util.c
OBJ = $(SRC:.c=.o) OBJ = ${SRC:.c=.o}
all: options dmenu stest all: options dmenu stest
options: options:
@echo dmenu build options: @echo dmenu build options:
@echo "CFLAGS = $(CFLAGS)" @echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = $(LDFLAGS)" @echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = $(CC)" @echo "CC = ${CC}"
.c.o: .c.o:
$(CC) -c $(CFLAGS) $< @echo CC $<
@${CC} -c ${CFLAGS} $<
config.h: config.h:
cp config.def.h $@ @echo creating $@ from config.def.h
@cp config.def.h $@
$(OBJ): arg.h config.h config.mk drw.h ${OBJ}: arg.h config.h config.mk drw.h
dmenu: dmenu.o drw.o util.o dmenu: dmenu.o drw.o util.o
$(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) @echo CC -o $@
@${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS}
stest: stest.o stest: stest.o
$(CC) -o $@ stest.o $(LDFLAGS) @echo CC -o $@
@${CC} -o $@ stest.o ${LDFLAGS}
clean: clean:
rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz @echo cleaning
@rm -f dmenu stest ${OBJ} dmenu-${VERSION}.tar.gz
dist: clean dist: clean
mkdir -p dmenu-$(VERSION) @echo creating dist tarball
cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ @mkdir -p dmenu-${VERSION}
drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ @cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1 \
dmenu-$(VERSION) drw.h util.h dmenu_path dmenu_run stest.1 ${SRC} \
tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) dmenu-${VERSION}
gzip dmenu-$(VERSION).tar @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
rm -rf dmenu-$(VERSION) @gzip dmenu-${VERSION}.tar
@rm -rf dmenu-${VERSION}
install: all install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin @echo installing executables to ${DESTDIR}${PREFIX}/bin
cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin @mkdir -p ${DESTDIR}${PREFIX}/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu @cp -f dmenu dmenu_path dmenu_run stest ${DESTDIR}${PREFIX}/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
chmod 755 $(DESTDIR)$(PREFIX)/bin/stest @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
mkdir -p $(DESTDIR)$(MANPREFIX)/man1 @chmod 755 ${DESTDIR}${PREFIX}/bin/stest
sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1
sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 @sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 @sed "s/VERSION/${VERSION}/g" < stest.1 > ${DESTDIR}${MANPREFIX}/man1/stest.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/stest.1
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ @echo removing executables from ${DESTDIR}${PREFIX}/bin
$(DESTDIR)$(PREFIX)/bin/dmenu_path\ @rm -f ${DESTDIR}${PREFIX}/bin/dmenu
$(DESTDIR)$(PREFIX)/bin/dmenu_run\ @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path
$(DESTDIR)$(PREFIX)/bin/stest\ @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
$(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ @rm -f ${DESTDIR}${PREFIX}/bin/stest
$(DESTDIR)$(MANPREFIX)/man1/stest.1 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@rm -f ${DESTDIR}${MANPREFIX}/man1/stest.1
.PHONY: all options clean dist install uninstall .PHONY: all options clean dist install uninstall

View File

@ -2,10 +2,7 @@
/* Default settings; can be overriden by command line. */ /* Default settings; can be overriden by command line. */
static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
static const unsigned int alpha = 0xf0;
/* -fn option overrides fonts[0]; default X11 font or font set */ /* -fn option overrides fonts[0]; default X11 font or font set */
static const int user_bt = 0; /* add an defined amount of pixels to the bar height */
static const char *fonts[] = { static const char *fonts[] = {
"monospace:size=10" "monospace:size=10"
}; };
@ -16,13 +13,6 @@ static const char *colors[SchemeLast][2] = {
[SchemeSel] = { "#eeeeee", "#005577" }, [SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" }, [SchemeOut] = { "#000000", "#00ffff" },
}; };
static const unsigned int alphas[SchemeLast][2] = {
[SchemeNorm] = { OPAQUE, alpha },
[SchemeSel] = { OPAQUE, alpha },
[SchemeOut] = { OPAQUE, alpha },
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0; static unsigned int lines = 0;

View File

@ -1,9 +1,9 @@
# dmenu version # dmenu version
VERSION = 5.2 VERSION = 4.7
# paths # paths
PREFIX = /usr/local PREFIX = /usr/local
MANPREFIX = $(PREFIX)/share/man MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib X11LIB = /usr/X11R6/lib
@ -16,17 +16,16 @@ XINERAMAFLAGS = -DXINERAMA
FREETYPELIBS = -lfontconfig -lXft FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2 FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment) # OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2 #FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
# includes and libs # includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC) INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
# flags # flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = $(LIBS) LDFLAGS = -s ${LIBS}
# compiler and linker # compiler and linker
CC = cc CC = cc

68
dmenu.1
View File

@ -41,8 +41,8 @@ which lists programs in the user's $PATH and runs the result in their $SHELL.
dmenu appears at the bottom of the screen. dmenu appears at the bottom of the screen.
.TP .TP
.B \-f .B \-f
dmenu grabs the keyboard before reading stdin if not reading from a tty. This dmenu grabs the keyboard before reading stdin. This is faster, but will lock up
is faster, but will lock up X until stdin reaches end\-of\-file. X until stdin reaches end\-of\-file.
.TP .TP
.B \-i .B \-i
dmenu matches menu items case insensitively. dmenu matches menu items case insensitively.
@ -100,94 +100,82 @@ Confirm input. Prints the input text to stdout and exits, returning success.
.B Escape .B Escape
Exit without selecting an item, returning failure. Exit without selecting an item, returning failure.
.TP .TP
.B Ctrl-Left C\-a
Move cursor to the start of the current word
.TP
.B Ctrl-Right
Move cursor to the end of the current word
.TP
.B C\-a
Home Home
.TP .TP
.B C\-b C\-b
Left Left
.TP .TP
.B C\-c C\-c
Escape Escape
.TP .TP
.B C\-d C\-d
Delete Delete
.TP .TP
.B C\-e C\-e
End End
.TP .TP
.B C\-f C\-f
Right Right
.TP .TP
.B C\-g C\-g
Escape Escape
.TP .TP
.B C\-h C\-h
Backspace Backspace
.TP .TP
.B C\-i C\-i
Tab Tab
.TP .TP
.B C\-j C\-j
Return Return
.TP .TP
.B C\-J C\-J
Shift-Return Shift-Return
.TP .TP
.B C\-k C\-k
Delete line right Delete line right
.TP .TP
.B C\-m C\-m
Return Return
.TP .TP
.B C\-M C\-M
Shift-Return Shift-Return
.TP .TP
.B C\-n C\-n
Down Down
.TP .TP
.B C\-p C\-p
Up Up
.TP .TP
.B C\-u C\-u
Delete line left Delete line left
.TP .TP
.B C\-w C\-w
Delete word left Delete word left
.TP .TP
.B C\-y C\-y
Paste from primary X selection Paste from primary X selection
.TP .TP
.B C\-Y C\-Y
Paste from X clipboard Paste from X clipboard
.TP .TP
.B M\-b M\-g
Move cursor to the start of the current word
.TP
.B M\-f
Move cursor to the end of the current word
.TP
.B M\-g
Home Home
.TP .TP
.B M\-G M\-G
End End
.TP .TP
.B M\-h M\-h
Up Up
.TP .TP
.B M\-j M\-j
Page down Page down
.TP .TP
.B M\-k M\-k
Page up Page up
.TP .TP
.B M\-l M\-l
Down Down
.SH SEE ALSO .SH SEE ALSO
.IR dwm (1), .IR dwm (1),

255
dmenu.c
View File

@ -6,11 +6,9 @@
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#ifdef XINERAMA #ifdef XINERAMA
#include <X11/extensions/Xinerama.h> #include <X11/extensions/Xinerama.h>
@ -26,8 +24,6 @@
#define LENGTH(X) (sizeof X / sizeof X[0]) #define LENGTH(X) (sizeof X / sizeof X[0])
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
#define OPAQUE 0xffU
/* enums */ /* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
@ -56,23 +52,10 @@ static XIC xic;
static Drw *drw; static Drw *drw;
static Clr *scheme[SchemeLast]; static Clr *scheme[SchemeLast];
static int useargb = 0;
static Visual *visual;
static int depth;
static Colormap cmap;
#include "config.h" #include "config.h"
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr; static char *(*fstrstr)(const char *, const char *) = strstr;
static void xinitvisual();
static unsigned int
textw_clamp(const char *str, unsigned int n)
{
unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
return MIN(w, n);
}
static void static void
appenditem(struct item *item, struct item **list, struct item **last) appenditem(struct item *item, struct item **list, struct item **last)
@ -98,10 +81,10 @@ calcoffsets(void)
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */ /* calculate which items will begin the next page and previous page */
for (i = 0, next = curr; next; next = next->right) for (i = 0, next = curr; next; next = next->right)
if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) if ((i += (lines > 0) ? bh : MIN(TEXTW(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 : textw_clamp(prev->left->text, n)) > n) if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n)
break; break;
} }
@ -113,29 +96,19 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root); XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < SchemeLast; i++) for (i = 0; i < SchemeLast; i++)
free(scheme[i]); free(scheme[i]);
for (i = 0; items && items[i].text; ++i)
free(items[i].text);
free(items);
drw_free(drw); drw_free(drw);
XSync(dpy, False); XSync(dpy, False);
XCloseDisplay(dpy); XCloseDisplay(dpy);
} }
static char * static char *
cistrstr(const char *h, const char *n) cistrstr(const char *s, const char *sub)
{ {
size_t i; size_t len;
if (!n[0]) for (len = strlen(sub); *s; s++)
return (char *)h; if (!strncasecmp(s, sub, len))
return (char *)s;
for (; *h; ++h) {
for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
tolower((unsigned char)h[i]); ++i)
;
if (n[i] == '\0')
return (char *)h;
}
return NULL; return NULL;
} }
@ -171,7 +144,7 @@ drawmenu(void)
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
curpos = TEXTW(text) - TEXTW(&text[cursor]); drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL);
if ((curpos += lrpad / 2 - 1) < w) { if ((curpos += lrpad / 2 - 1) < w) {
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
@ -191,7 +164,7 @@ drawmenu(void)
} }
x += w; x += w;
for (item = curr; item != next; item = item->right) for (item = curr; item != next; item = item->right)
x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">")));
if (next) { if (next) {
w = TEXTW(">"); w = TEXTW(">");
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
@ -251,7 +224,7 @@ match(void)
/* separate input text into tokens to be matched individually */ /* separate input text into tokens to be matched individually */
for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
die("cannot realloc %zu bytes:", tokn * sizeof *tokv); die("cannot realloc %u bytes:", tokn * sizeof *tokv);
len = tokc ? strlen(tokv[0]) : 0; len = tokc ? strlen(tokv[0]) : 0;
matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
@ -314,42 +287,18 @@ nextrune(int inc)
return n; return n;
} }
static void
movewordedge(int dir)
{
if (dir < 0) { /* move cursor to the start of the word*/
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
} else { /* move cursor to the end of the word */
while (text[cursor] && strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
while (text[cursor] && !strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
}
}
static void static void
keypress(XKeyEvent *ev) keypress(XKeyEvent *ev)
{ {
char buf[64]; char buf[32];
int len; int len;
KeySym ksym = NoSymbol; KeySym ksym = NoSymbol;
Status status; Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) { if (status == XBufferOverflow)
default: /* XLookupNone, XBufferOverflow */
return; return;
case XLookupChars: /* composed string from input method */ if (ev->state & ControlMask)
goto insert;
case XLookupKeySym:
case XLookupBoth: /* a KeySym and a string are returned: use keysym */
break;
}
if (ev->state & ControlMask) {
switch(ksym) { switch(ksym) {
case XK_a: ksym = XK_Home; break; case XK_a: ksym = XK_Home; break;
case XK_b: ksym = XK_Left; break; case XK_b: ksym = XK_Left; break;
@ -385,14 +334,6 @@ keypress(XKeyEvent *ev)
XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime); utf8, utf8, win, CurrentTime);
return; return;
case XK_Left:
case XK_KP_Left:
movewordedge(-1);
goto draw;
case XK_Right:
case XK_KP_Right:
movewordedge(+1);
goto draw;
case XK_Return: case XK_Return:
case XK_KP_Enter: case XK_KP_Enter:
break; break;
@ -402,14 +343,8 @@ keypress(XKeyEvent *ev)
default: default:
return; return;
} }
} else if (ev->state & Mod1Mask) { else if (ev->state & Mod1Mask)
switch(ksym) { switch(ksym) {
case XK_b:
movewordedge(-1);
goto draw;
case XK_f:
movewordedge(+1);
goto draw;
case XK_g: ksym = XK_Home; break; case XK_g: ksym = XK_Home; break;
case XK_G: ksym = XK_End; break; case XK_G: ksym = XK_End; break;
case XK_h: ksym = XK_Up; break; case XK_h: ksym = XK_Up; break;
@ -419,16 +354,12 @@ keypress(XKeyEvent *ev)
default: default:
return; return;
} }
}
switch(ksym) { switch(ksym) {
default: default:
insert: if (!iscntrl(*buf))
if (!iscntrl((unsigned char)*buf))
insert(buf, len); insert(buf, len);
break; break;
case XK_Delete: case XK_Delete:
case XK_KP_Delete:
if (text[cursor] == '\0') if (text[cursor] == '\0')
return; return;
cursor = nextrune(+1); cursor = nextrune(+1);
@ -439,7 +370,6 @@ insert:
insert(NULL, nextrune(-1) - cursor); insert(NULL, nextrune(-1) - cursor);
break; break;
case XK_End: case XK_End:
case XK_KP_End:
if (text[cursor] != '\0') { if (text[cursor] != '\0') {
cursor = strlen(text); cursor = strlen(text);
break; break;
@ -459,7 +389,6 @@ insert:
cleanup(); cleanup();
exit(1); exit(1);
case XK_Home: case XK_Home:
case XK_KP_Home:
if (sel == matches) { if (sel == matches) {
cursor = 0; cursor = 0;
break; break;
@ -468,7 +397,6 @@ insert:
calcoffsets(); calcoffsets();
break; break;
case XK_Left: case XK_Left:
case XK_KP_Left:
if (cursor > 0 && (!sel || !sel->left || lines > 0)) { if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
cursor = nextrune(-1); cursor = nextrune(-1);
break; break;
@ -477,21 +405,18 @@ insert:
return; return;
/* fallthrough */ /* fallthrough */
case XK_Up: case XK_Up:
case XK_KP_Up:
if (sel && sel->left && (sel = sel->left)->right == curr) { if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev; curr = prev;
calcoffsets(); calcoffsets();
} }
break; break;
case XK_Next: case XK_Next:
case XK_KP_Next:
if (!next) if (!next)
return; return;
sel = curr = next; sel = curr = next;
calcoffsets(); calcoffsets();
break; break;
case XK_Prior: case XK_Prior:
case XK_KP_Prior:
if (!prev) if (!prev)
return; return;
sel = curr = prev; sel = curr = prev;
@ -508,7 +433,6 @@ insert:
sel->out = 1; sel->out = 1;
break; break;
case XK_Right: case XK_Right:
case XK_KP_Right:
if (text[cursor] != '\0') { if (text[cursor] != '\0') {
cursor = nextrune(+1); cursor = nextrune(+1);
break; break;
@ -517,7 +441,6 @@ insert:
return; return;
/* fallthrough */ /* fallthrough */
case XK_Down: case XK_Down:
case XK_KP_Down:
if (sel && sel->right && (sel = sel->right) == next) { if (sel && sel->right && (sel = sel->right) == next) {
curr = next; curr = next;
calcoffsets(); calcoffsets();
@ -526,14 +449,12 @@ insert:
case XK_Tab: case XK_Tab:
if (!sel) if (!sel)
return; return;
cursor = strnlen(sel->text, sizeof text - 1); strncpy(text, sel->text, sizeof text - 1);
memcpy(text, sel->text, cursor); text[sizeof text - 1] = '\0';
text[cursor] = '\0'; cursor = strlen(text);
match(); match();
break; break;
} }
draw:
drawmenu(); drawmenu();
} }
@ -546,39 +467,39 @@ paste(void)
Atom da; Atom da;
/* we have been given the current selection, now insert it into input */ /* we have been given the current selection, now insert it into input */
if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
utf8, &da, &di, &dl, &dl, (unsigned char **)&p) utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
== Success && p) { insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); XFree(p);
XFree(p);
}
drawmenu(); drawmenu();
} }
static void static void
readstdin(void) readstdin(void)
{ {
char *line = NULL; char buf[sizeof text], *p;
size_t i, itemsiz = 0, linesiz = 0; size_t i, imax = 0, size = 0;
ssize_t len; unsigned int tmpmax = 0;
/* read each line from stdin and add it to the item list */ /* read each line from stdin and add it to the item list */
for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
if (i + 1 >= itemsiz) { if (i + 1 >= size / sizeof *items)
itemsiz += 256; if (!(items = realloc(items, (size += BUFSIZ))))
if (!(items = realloc(items, itemsiz * sizeof(*items)))) die("cannot realloc %u bytes:", size);
die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); if ((p = strchr(buf, '\n')))
} *p = '\0';
if (line[len - 1] == '\n') if (!(items[i].text = strdup(buf)))
line[len - 1] = '\0'; die("cannot strdup %u bytes:", strlen(buf) + 1);
if (!(items[i].text = strdup(line)))
die("strdup:");
items[i].out = 0; items[i].out = 0;
drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
if (tmpmax > inputw) {
inputw = tmpmax;
imax = i;
}
} }
free(line);
if (items) if (items)
items[i].text = NULL; items[i].text = NULL;
inputw = items ? TEXTW(items[imax].text) : 0;
lines = MIN(lines, i); lines = MIN(lines, i);
} }
@ -591,11 +512,6 @@ run(void)
if (XFilterEvent(&ev, win)) if (XFilterEvent(&ev, win))
continue; continue;
switch(ev.type) { switch(ev.type) {
case DestroyNotify:
if (ev.xdestroywindow.window != win)
break;
cleanup();
exit(1);
case Expose: case Expose:
if (ev.xexpose.count == 0) if (ev.xexpose.count == 0)
drw_map(drw, win, 0, 0, mw, mh); drw_map(drw, win, 0, 0, mw, mh);
@ -623,32 +539,31 @@ run(void)
static void static void
setup(void) setup(void)
{ {
int x, y, i, j; int x, y, i = 0;
unsigned int du; unsigned int du;
XSetWindowAttributes swa; XSetWindowAttributes swa;
XIM xim; XIM xim;
Window w, dw, *dws; Window w, dw, *dws;
XWindowAttributes wa; XWindowAttributes wa;
XClassHint ch = {"dmenu", "dmenu"};
#ifdef XINERAMA #ifdef XINERAMA
XineramaScreenInfo *info; XineramaScreenInfo *info;
Window pw; Window pw;
int a, di, n, area = 0; int a, j, di, n, area = 0;
#endif #endif
/* init appearance */ /* init appearance */
for (j = 0; j < SchemeLast; j++) scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2);
scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2); scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2);
scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2);
clip = XInternAtom(dpy, "CLIPBOARD", False); clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False);
/* calculate menu geometry */ /* calculate menu geometry */
bh = drw->fonts->h; bh = drw->fonts->h + 2;
bh = user_bh ? bh + user_bh : bh + 2;
lines = MAX(lines, 0); lines = MAX(lines, 0);
mh = (lines + 1) * bh; mh = (lines + 1) * bh;
#ifdef XINERAMA #ifdef XINERAMA
i = 0;
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
XGetInputFocus(dpy, &w, &di); XGetInputFocus(dpy, &w, &di);
if (mon >= 0 && mon < n) if (mon >= 0 && mon < n)
@ -670,13 +585,12 @@ setup(void)
/* no focused window is on screen, so use pointer location instead */ /* no focused window is on screen, so use pointer location instead */
if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
if (INTERSECT(x, y, 1, 1, info[i]) != 0) if (INTERSECT(x, y, 1, 1, info[i]))
break; break;
x = info[i].x_org; x = info[i].x_org;
y = info[i].y_org + (topbar ? 0 : info[i].height - mh); y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
mw = info[i].width; mw = info[i].width;
XFree(info); XFree(info);
} else } else
#endif #endif
@ -689,32 +603,25 @@ setup(void)
mw = wa.width; mw = wa.width;
} }
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
inputw = mw / 3; /* input width: ~33% of monitor width */ inputw = MIN(inputw, mw/3);
match(); match();
/* create menu window */ /* create menu window */
swa.override_redirect = True; swa.override_redirect = True;
swa.background_pixel = 0; swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.border_pixel = 0;
swa.colormap = cmap;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
depth, CopyFromParent, visual, CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa); CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
XSetClassHint(dpy, win, &ch);
/* input methods */
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
die("XOpenIM failed: could not open input device");
/* open input methods */
xim = XOpenIM(dpy, NULL, NULL, NULL);
xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, win, XNFocusWindow, win, NULL); XNClientWindow, win, XNFocusWindow, win, NULL);
XMapRaised(dpy, win); XMapRaised(dpy, win);
if (embed) { if (embed) {
XReparentWindow(dpy, win, parentwin, x, y); XSelectInput(dpy, parentwin, FocusChangeMask);
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
for (i = 0; i < du && dws[i] != win; ++i) for (i = 0; i < du && dws[i] != win; ++i)
XSelectInput(dpy, dws[i], FocusChangeMask); XSelectInput(dpy, dws[i], FocusChangeMask);
@ -729,8 +636,9 @@ setup(void)
static void static void
usage(void) usage(void)
{ {
die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
" [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr);
exit(1);
} }
int int
@ -786,18 +694,12 @@ main(int argc, char *argv[])
if (!XGetWindowAttributes(dpy, parentwin, &wa)) if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx", die("could not get embedding window attributes: 0x%lx",
parentwin); parentwin);
xinitvisual(); drw = drw_create(dpy, screen, root, wa.width, wa.height);
drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded."); die("no fonts could be loaded.");
lrpad = drw->fonts->h; lrpad = drw->fonts->h;
#ifdef __OpenBSD__ if (fast) {
if (pledge("stdio rpath", NULL) == -1)
die("pledge");
#endif
if (fast && !isatty(0)) {
grabkeyboard(); grabkeyboard();
readstdin(); readstdin();
} else { } else {
@ -809,40 +711,3 @@ main(int argc, char *argv[])
return 1; /* unreachable */ return 1; /* unreachable */
} }
void
xinitvisual()
{
XVisualInfo *infos;
XRenderPictFormat *fmt;
int nitems;
int i;
XVisualInfo tpl = {
.screen = screen,
.depth = 32,
.class = TrueColor
};
long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
visual = NULL;
for(i = 0; i < nitems; i ++) {
fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
visual = infos[i].visual;
depth = infos[i].depth;
cmap = XCreateColormap(dpy, root, visual, AllocNone);
useargb = 1;
break;
}
}
XFree(infos);
if (! visual) {
visual = DefaultVisual(dpy, screen);
depth = DefaultDepth(dpy, screen);
cmap = DefaultColormap(dpy, screen);
}
}

12
dmenu_path Executable file → Normal file
View File

@ -1,10 +1,10 @@
#!/bin/sh #!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" if [ -d "$cachedir" ]; then
cache="$cachedir/dmenu_run" cache=$cachedir/dmenu_run
else
[ ! -e "$cachedir" ] && mkdir -p "$cachedir" cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
IFS=: IFS=:
if stest -dqr -n "$cache" $PATH; then if stest -dqr -n "$cache" $PATH; then
stest -flx $PATH | sort -u | tee "$cache" stest -flx $PATH | sort -u | tee "$cache"

119
drw.c
View File

@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen)
} }
Drw * Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{ {
Drw *drw = ecalloc(1, sizeof(Drw)); Drw *drw = ecalloc(1, sizeof(Drw));
@ -70,11 +70,8 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->root = root; drw->root = root;
drw->w = w; drw->w = w;
drw->h = h; drw->h = h;
drw->visual = visual; drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->depth = depth; drw->gc = XCreateGC(dpy, root, 0, NULL);
drw->cmap = cmap;
drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw; return drw;
@ -90,7 +87,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
drw->h = h; drw->h = h;
if (drw->drawable) if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable); XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
} }
void void
@ -98,7 +95,6 @@ drw_free(Drw *drw)
{ {
XFreePixmap(drw->dpy, drw->drawable); XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc); XFreeGC(drw->dpy, drw->gc);
drw_fontset_free(drw->fonts);
free(drw); free(drw);
} }
@ -184,22 +180,21 @@ drw_fontset_free(Fnt *font)
} }
void void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
{ {
if (!drw || !dest || !clrname) if (!drw || !dest || !clrname)
return; return;
if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen),
clrname, dest)) clrname, dest))
die("error, cannot allocate color '%s'", clrname); die("error, cannot allocate color '%s'", clrname);
dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
} }
/* Wrapper to create color schemes. The caller has to call free(3) on the /* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */ * returned color scheme when done using it. */
Clr * Clr *
drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{ {
size_t i; size_t i;
Clr *ret; Clr *ret;
@ -209,7 +204,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], si
return NULL; return NULL;
for (i = 0; i < clrcount; i++) for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); drw_clr_create(drw, &ret[i], clrnames[i]);
return ret; return ret;
} }
@ -242,10 +237,12 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{ {
int i, ty, ellipsis_x = 0; char buf[1024];
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; int ty;
unsigned int ew;
XftDraw *d = NULL; XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont; Fnt *usedfont, *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render = x || y || w || h; int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0; long utf8codepoint = 0;
const char *utf8str; const char *utf8str;
@ -253,30 +250,26 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
FcPattern *fcpattern; FcPattern *fcpattern;
FcPattern *match; FcPattern *match;
XftResult result; XftResult result;
int charexists = 0, overflow = 0; int charexists = 0;
/* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 };
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
static unsigned int ellipsis_width = 0;
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0; return 0;
if (!render) { if (!render) {
w = invert ? invert : ~invert; w = ~w;
} else { } else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
x += lpad; x += lpad;
w -= lpad; w -= lpad;
} }
usedfont = drw->fonts; usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
while (1) { while (1) {
ew = ellipsis_len = utf8strlen = 0; utf8strlen = 0;
utf8str = text; utf8str = text;
nextfont = NULL; nextfont = NULL;
while (*text) { while (*text) {
@ -284,27 +277,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
for (curfont = drw->fonts; curfont; curfont = curfont->next) { for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) { if (charexists) {
drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); if (curfont == usedfont) {
if (ew + ellipsis_width <= w) {
/* keep track where the ellipsis still fits */
ellipsis_x = x + ew;
ellipsis_w = w - ew;
ellipsis_len = utf8strlen;
}
if (ew + tmpw > w) {
overflow = 1;
/* called from drw_fontset_getwidth_clamp():
* it wants the width AFTER the overflow
*/
if (!render)
x += tmpw;
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen; utf8strlen += utf8charlen;
text += utf8charlen; text += utf8charlen;
ew += tmpw;
} else { } else {
nextfont = curfont; nextfont = curfont;
} }
@ -312,25 +287,36 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} }
} }
if (overflow || !charexists || nextfont) if (!charexists || nextfont)
break; break;
else else
charexists = 0; charexists = 0;
} }
if (utf8strlen) { if (utf8strlen) {
if (render) { drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; /* shorten text if necessary */
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
}
x += ew;
w -= ew;
}
if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
if (!*text || overflow) { if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
}
x += ew;
w -= ew;
}
}
if (!*text) {
break; break;
} else if (nextfont) { } else if (nextfont) {
charexists = 0; charexists = 0;
@ -340,12 +326,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */ * character must be drawn. */
charexists = 1; charexists = 1;
for (i = 0; i < nomatches_len; ++i) {
/* avoid calling XftFontMatch if we know we won't find a match */
if (utf8codepoint == nomatches.codepoint[i])
goto no_match;
}
fccharset = FcCharSetCreate(); fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint); FcCharSetAddChar(fccharset, utf8codepoint);
@ -373,8 +353,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont; curfont->next = usedfont;
} else { } else {
xfont_free(usedfont); xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
no_match:
usedfont = drw->fonts; usedfont = drw->fonts;
} }
} }
@ -404,15 +382,6 @@ drw_fontset_getwidth(Drw *drw, const char *text)
return drw_text(drw, 0, 0, 0, 0, 0, text, 0); return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
} }
unsigned int
drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
{
unsigned int tmp = 0;
if (drw && drw->fonts && text && n)
tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
return MIN(n, tmp);
}
void void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{ {

10
drw.h
View File

@ -20,9 +20,6 @@ typedef struct {
Display *dpy; Display *dpy;
int screen; int screen;
Window root; Window root;
Visual *visual;
unsigned int depth;
Colormap cmap;
Drawable drawable; Drawable drawable;
GC gc; GC gc;
Clr *scheme; Clr *scheme;
@ -30,7 +27,7 @@ typedef struct {
} Drw; } Drw;
/* Drawable abstraction */ /* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap); Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
void drw_resize(Drw *drw, unsigned int w, unsigned int h); void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw); void drw_free(Drw *drw);
@ -38,12 +35,11 @@ void drw_free(Drw *drw);
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_fontset_free(Fnt* set); void drw_fontset_free(Fnt* set);
unsigned int drw_fontset_getwidth(Drw *drw, const char *text); unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colorscheme abstraction */ /* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
/* Cursor abstraction */ /* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape); Cur *drw_cur_create(Drw *drw, int shape);

View File

@ -84,7 +84,7 @@ main(int argc, char *argv[])
if (!argc) { if (!argc) {
/* read list from stdin */ /* read list from stdin */
while ((n = getline(&line, &linesiz, stdin)) > 0) { while ((n = getline(&line, &linesiz, stdin)) > 0) {
if (line[n - 1] == '\n') if (n && line[n - 1] == '\n')
line[n - 1] = '\0'; line[n - 1] = '\0';
test(line, line); test(line, line);
} }

23
util.c
View File

@ -6,9 +6,18 @@
#include "util.h" #include "util.h"
void void *
die(const char *fmt, ...) ecalloc(size_t nmemb, size_t size)
{ {
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}
void
die(const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
@ -24,13 +33,3 @@ die(const char *fmt, ...)
exit(1); exit(1);
} }
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}