Compare commits

...

56 Commits
4.2 ... 4.4.1

Author SHA1 Message Date
91dc06d0b8 prepare 4.4.1 release 2011-09-19 10:41:30 +01:00
3af80cd7d8 change version to 'hg' 2011-09-19 10:40:07 +01:00
4692f84ff0 dmenu_run: sh -c 2011-07-24 20:04:58 +01:00
c805dcbeaf fix loadfont 2011-07-22 18:16:57 +01:00
a6cbc6c9a7 Added tag 4.4 for changeset 2b105eaae831 2011-07-19 21:30:14 +01:00
bae58f61cf add lsx.1 to dist 2011-07-19 21:30:09 +01:00
1636e12210 tweaks 2011-07-17 14:06:53 +01:00
2b92c95230 efficiency tweaks 2011-07-14 20:03:08 +01:00
16a0c0d52a fix extra warning 2011-07-14 20:03:00 +01:00
5867c53390 simpler vline 2011-07-06 13:40:36 +01:00
34a816f87d rebind ^N ^P 2011-07-04 16:55:09 +01:00
0288b576ca include limits.h 2011-06-25 17:02:14 +01:00
59dafc4ad6 make clean lsx 2011-06-23 22:39:20 +01:00
49672e9d05 merge lsx -> default 2011-06-23 20:27:28 +01:00
07d452e0c8 close lsx branch 2011-06-23 20:26:57 +01:00
88d44169e9 lsx: check snprintf 2011-06-23 20:04:50 +01:00
f7cbe710a2 update draw.c 2011-06-18 22:11:19 +01:00
86e9e6f1ec simpler lsx 2011-06-18 07:50:46 +01:00
eadf090413 new dmenu_run 2011-06-13 21:50:31 +01:00
8ec1b41cac add lsx.1 2011-06-13 19:32:45 +01:00
ab3bcac0bf new lsx branch 2011-06-13 19:28:30 +01:00
723cbabc12 add ^M 2011-06-13 19:25:40 +01:00
86775cc44b Added tag 4.3.1 for changeset 34a2d77049a9 2011-05-18 17:01:59 +01:00
2103ae2891 bugfix 2011-05-18 17:01:44 +01:00
b028ec0e07 Added tag 4.3 for changeset 14c79f054bdf 2011-05-18 16:21:45 +01:00
bb3e33def3 no -m, cleanup 2011-05-18 16:20:03 +01:00
3a60b19514 fix possible overflow 2011-05-16 23:35:14 +01:00
dd2f298252 fixed extra warnings 2011-05-16 12:59:31 +01:00
11ea52d170 portability 2011-05-15 21:54:26 +01:00
4354074607 efficient incremental search 2011-05-15 16:05:32 +01:00
4776d6dc61 match first 2011-05-15 14:21:00 +01:00
d0051e7bb8 faster grab 2011-05-15 14:13:31 +01:00
fb67bd666e cleanup 2011-05-15 13:58:54 +01:00
f5bd1eed8d fast but inexact 2011-05-15 13:02:33 +01:00
1654d6cd62 cleanup 2011-05-15 02:37:49 +01:00
6cc0b0dc08 increment version 2011-05-14 23:26:41 +01:00
b2bb6ebec9 only match links 2011-05-14 23:21:38 +01:00
a662fc6e0a follow symlinks 2011-05-14 23:14:31 +01:00
d066975e78 cleanup 2011-05-14 22:43:42 +01:00
26236a4900 whoops, wrong dmenu_path version 2011-05-14 22:12:28 +01:00
15505bd711 fast dmenu_path script 2011-05-14 20:43:11 +01:00
86468aafe5 instant ^E 2011-05-14 18:39:27 +01:00
be9afce035 fix typo 2011-05-14 17:47:12 +01:00
3c067598fc use array for items 2011-05-14 17:46:20 +01:00
86019c5855 fixed xinerama corner case 2011-05-12 13:17:41 +01:00
0291c722fb fixed -m bug (thanks Rob) 2011-05-11 12:25:50 +01:00
e0e6b071c6 fast grab patch (thanks Rob) 2011-05-08 15:15:24 +01:00
cd3b4915c3 helpful errors 2011-05-06 21:13:02 +01:00
210b303941 paring 2011-05-05 15:46:48 +01:00
47e3e8be7b update license 2011-01-07 18:55:00 +00:00
d515d2758f cache option in config.mk 2011-01-07 18:54:40 +00:00
a8ea79715f gmake spews 2010-12-01 20:25:10 +00:00
3e0a141016 posix makefile 2010-12-01 20:23:16 +00:00
cbd8257914 Added tag 4.2.1 for changeset abb6579a324f 2010-11-20 09:25:08 +00:00
22cf9626c7 fixed dist target bug 2010-11-20 09:25:04 +00:00
9e13ecabe8 Added tag 4.2 for changeset 379813a051f0 2010-11-19 11:51:01 +00:00
12 changed files with 294 additions and 355 deletions

View File

@ -42,3 +42,8 @@ e4c81a78ffbad6ba4d1ad119cc654da6eca63a4c 3.2
78f9f72cc9c6bdb022ff8908486b61ef5e242aad 4.0 78f9f72cc9c6bdb022ff8908486b61ef5e242aad 4.0
844587572673cf6326c3f61737264a46b728fc0a 4.1 844587572673cf6326c3f61737264a46b728fc0a 4.1
72749a826cab0baa805620e44a22e54486c97a4e 4.1.1 72749a826cab0baa805620e44a22e54486c97a4e 4.1.1
379813a051f03a1b20bdbfdc2d2d1d2d794ace48 4.2
abb6579a324fffdf6a23c2fa4c32911277da594a 4.2.1
14c79f054bdf43ff3213af8e60a783192e92a018 4.3
34a2d77049a95b02f3332a0b88f9370965ebcfad 4.3.1
2b105eaae8315b076da93056da9ecd60de5a7ac9 4.4

View File

@ -1,7 +1,7 @@
MIT/X Consortium License MIT/X Consortium License
© 2010 Connor Lane Smith <cls@lubutu.com> © 2010-2011 Connor Lane Smith <cls@lubutu.com>
© 2006-2010 Anselm R Garbe <anselm@garbe.us> © 2006-2011 Anselm R Garbe <anselm@garbe.us>
© 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>

View File

@ -3,7 +3,10 @@
include config.mk include config.mk
all: options dmenu dmenu_path SRC = dmenu.c draw.c lsx.c
OBJ = ${SRC:.c=.o}
all: options dmenu lsx
options: options:
@echo dmenu build options: @echo dmenu build options:
@ -11,25 +14,28 @@ options:
@echo "LDFLAGS = ${LDFLAGS}" @echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}" @echo "CC = ${CC}"
dmenu: dmenu.o draw.o .c.o:
dmenu_path: dmenu_path.o
.c.o: config.mk
@echo CC -c $< @echo CC -c $<
@${CC} -c $< ${CFLAGS} @${CC} -c $< ${CFLAGS}
dmenu dmenu_path: ${OBJ}: config.mk
dmenu: dmenu.o draw.o
@echo CC -o $@ @echo CC -o $@
@${CC} -o $@ $+ ${LDFLAGS} @${CC} -o $@ dmenu.o draw.o ${LDFLAGS}
lsx: lsx.o
@echo CC -o $@
@${CC} -o $@ lsx.o ${LDFLAGS}
clean: clean:
@echo cleaning @echo cleaning
@rm -f dmenu dmenu.o draw.o dmenu_path dmenu_path.o dmenu-${VERSION}.tar.gz @rm -f dmenu lsx ${OBJ} dmenu-${VERSION}.tar.gz
dist: clean dist: clean
@echo creating dist tarball @echo creating dist tarball
@mkdir -p dmenu-${VERSION} @mkdir -p dmenu-${VERSION}
@cp LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path.c dmenu_run dmenu-${VERSION} @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run lsx.1 ${SRC} dmenu-${VERSION}
@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
@gzip dmenu-${VERSION}.tar @gzip dmenu-${VERSION}.tar
@rm -rf dmenu-${VERSION} @rm -rf dmenu-${VERSION}
@ -37,21 +43,24 @@ dist: clean
install: all install: all
@echo installing executables to ${DESTDIR}${PREFIX}/bin @echo installing executables to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin @cp -f dmenu dmenu_run lsx ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 @chmod 755 ${DESTDIR}${PREFIX}/bin/lsx
@echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1 @sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@sed "s/VERSION/${VERSION}/g" < lsx.1 > ${DESTDIR}${MANPREFIX}/man1/lsx.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/lsx.1
uninstall: uninstall:
@echo removing executables from ${DESTDIR}${PREFIX}/bin @echo removing executables from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu @rm -f ${DESTDIR}${PREFIX}/bin/dmenu
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
@rm -f ${DESTDIR}${PREFIX}/bin/lsx
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@rm -f ${DESTDIR}${MANPREFIX}/man1/lsx.1
.PHONY: all options clean dist install uninstall .PHONY: all options clean dist install uninstall

View File

@ -1,7 +1,5 @@
# dmenu version # dmenu version
VERSION = 4.2 VERSION = 4.4.1
# Customize below to fit your system
# paths # paths
PREFIX = /usr/local PREFIX = /usr/local
@ -20,7 +18,7 @@ LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
# flags # flags
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS} LDFLAGS = -s ${LIBS}
# compiler and linker # compiler and linker

53
dmenu.1
View File

@ -4,11 +4,10 @@ dmenu \- dynamic menu
.SH SYNOPSIS .SH SYNOPSIS
.B dmenu .B dmenu
.RB [ \-b ] .RB [ \-b ]
.RB [ \-f ]
.RB [ \-i ] .RB [ \-i ]
.RB [ \-l .RB [ \-l
.IR lines ] .IR lines ]
.RB [ \-m
.IR monitor ]
.RB [ \-p .RB [ \-p
.IR prompt ] .IR prompt ]
.RB [ \-fn .RB [ \-fn
@ -24,38 +23,34 @@ dmenu \- dynamic menu
.RB [ \-v ] .RB [ \-v ]
.P .P
.BR dmenu_run " ..." .BR dmenu_run " ..."
.P
.B dmenu_path
.SH DESCRIPTION .SH DESCRIPTION
.B dmenu .B dmenu
is a dynamic menu for X, originally designed for is a dynamic menu for X, originally designed for
.BR dwm (1). .IR dwm (1).
It manages huge numbers of user-defined menu items efficiently. It manages huge numbers of user\-defined menu items efficiently.
.P .P
dmenu reads a list of newline-separated items from standard input and creates a dmenu reads a list of newline\-separated items from stdin and creates a menu.
menu. When the user selects an item or enters any text and presses Return, When the user selects an item or enters any text and presses Return, their
their choice is printed to standard output and dmenu terminates. choice is printed to stdout and dmenu terminates.
.P .P
.B dmenu_run .B dmenu_run
is a dmenu script used by dwm which lists programs in the user's PATH and is a dmenu script used by dwm which lists programs in the user's $PATH and
executes the selected item. executes the selected item.
.P
.B dmenu_path
is a program used by dmenu_run to find and cache a list of executables.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-b .B \-b
dmenu appears at the bottom of the screen. dmenu appears at the bottom of the screen.
.TP .TP
.B \-f
dmenu grabs the keyboard before reading stdin. This is faster, but may lock up
X if stdin is from a terminal.
.TP
.B \-i .B \-i
dmenu matches menu items case insensitively. dmenu matches menu items case insensitively.
.TP .TP
.BI \-l " lines" .BI \-l " lines"
dmenu lists items vertically, with the given number of lines. dmenu lists items vertically, with the given number of lines.
.TP .TP
.BI \-m " monitor"
dmenu appears on the given Xinerama screen.
.TP
.BI \-p " prompt" .BI \-p " prompt"
defines the prompt to be displayed to the left of the input field. defines the prompt to be displayed to the left of the input field.
.TP .TP
@ -66,7 +61,7 @@ defines the font or font set used.
defines the normal background color. defines the normal background color.
.IR #RGB , .IR #RGB ,
.IR #RRGGBB , .IR #RRGGBB ,
and color names are supported. and X color names are supported.
.TP .TP
.BI \-nf " color" .BI \-nf " color"
defines the normal foreground color. defines the normal foreground color.
@ -78,27 +73,27 @@ defines the selected background color.
defines the selected foreground color. defines the selected foreground color.
.TP .TP
.B \-v .B \-v
prints version information to standard output, then exits. prints version information to stdout, then exits.
.SH USAGE .SH USAGE
dmenu is completely controlled by the keyboard. Besides standard Unix line dmenu is completely controlled by the keyboard. Besides standard Unix line
editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the editing and item selection (arrow keys, page up/down, home and end), the
following keys are recognized: following keys are recognized:
.TP .TP
.B Tab (Control\-i) .B Tab (Ctrl\-i)
Copy the selected item to the input field. Copy the selected item to the input field.
.TP .TP
.B Return (Control\-j) .B Return (Ctrl\-j)
Confirm selection. Prints the selected item to standard output and exits, Confirm selection. Prints the selected item to stdout and exits, returning
returning success.
.TP
.B Shift\-Return (Control\-Shift\-j)
Confirm input. Prints the input text to standard output and exits, returning
success. success.
.TP .TP
.B Escape (Control\-c) .B Shift\-Return (Ctrl\-Shift\-j)
Confirm input. Prints the input text to stdout and exits, returning success.
.TP
.B Escape (Ctrl\-c)
Exit without selecting an item, returning failure. Exit without selecting an item, returning failure.
.TP .TP
.B Control\-y .B Ctrl\-y
Paste the current X selection into the input field. Paste the current X selection into the input field.
.SH SEE ALSO .SH SEE ALSO
.BR dwm (1) .IR dwm (1),
.IR lsx (1)

290
dmenu.c
View File

@ -3,6 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include <unistd.h> #include <unistd.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -19,31 +20,28 @@
typedef struct Item Item; typedef struct Item Item;
struct Item { struct Item {
char *text; char *text;
Item *next; /* traverses all items */ Item *left, *right;
Item *left, *right; /* traverses matching items */
}; };
static void appenditem(Item *item, Item **list, Item **last); static void appenditem(Item *item, Item **list, Item **last);
static void calcoffsets(void); static void calcoffsets(void);
static char *cistrstr(const char *s, const char *sub);
static void drawmenu(void); static void drawmenu(void);
static char *fstrstr(const char *s, const char *sub);
static void grabkeyboard(void); static void grabkeyboard(void);
static void insert(const char *s, ssize_t n); static void insert(const char *str, ssize_t n);
static void keypress(XKeyEvent *ev); static void keypress(XKeyEvent *ev);
static void match(void); static void match(Bool sub);
static size_t nextrune(int incr); static size_t nextrune(int inc);
static void paste(void); static void paste(void);
static void readstdin(void); static void readstdin(void);
static void run(void); static void run(void);
static void setup(void); static void setup(void);
static void usage(void); static void usage(void);
static char text[BUFSIZ]; static char text[BUFSIZ] = "";
static int bh, mw, mh; static int bh, mw, mh;
static int inputw = 0; static int inputw, promptw;
static int lines = 0; static int lines = 0;
static int monitor = -1;
static int promptw;
static size_t cursor = 0; static size_t cursor = 0;
static const char *font = NULL; static const char *font = NULL;
static const char *prompt = NULL; static const char *prompt = NULL;
@ -57,34 +55,37 @@ static Atom utf8;
static Bool topbar = True; static Bool topbar = True;
static DC *dc; static DC *dc;
static Item *items = NULL; static Item *items = NULL;
static Item *matches, *sel; static Item *matches, *matchend;
static Item *prev, *curr, *next; static Item *prev, *curr, *next, *sel;
static Window root, win; static Window win;
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;
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
Bool fast = False;
int i; int i;
progname = "dmenu";
for(i = 1; i < argc; i++) for(i = 1; i < argc; i++)
/* single flags */ /* single flags */
if(!strcmp(argv[i], "-v")) { if(!strcmp(argv[i], "-v")) {
fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout); puts("dmenu-"VERSION", © 2006-2011 dmenu engineers, see LICENSE for details");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
else if(!strcmp(argv[i], "-b")) else if(!strcmp(argv[i], "-b"))
topbar = False; topbar = False;
else if(!strcmp(argv[i], "-i")) else if(!strcmp(argv[i], "-f"))
fast = True;
else if(!strcmp(argv[i], "-i")) {
fstrncmp = strncasecmp; fstrncmp = strncasecmp;
else if(i == argc-1) fstrstr = cistrstr;
}
else if(i+1 == argc)
usage(); usage();
/* double flags */ /* double flags */
else if(!strcmp(argv[i], "-l")) else if(!strcmp(argv[i], "-l"))
lines = atoi(argv[++i]); lines = atoi(argv[++i]);
else if(!strcmp(argv[i], "-m"))
monitor = atoi(argv[++i]);
else if(!strcmp(argv[i], "-p")) else if(!strcmp(argv[i], "-p"))
prompt = argv[++i]; prompt = argv[++i];
else if(!strcmp(argv[i], "-fn")) else if(!strcmp(argv[i], "-fn"))
@ -102,11 +103,19 @@ main(int argc, char *argv[]) {
dc = initdc(); dc = initdc();
initfont(dc, font); initfont(dc, font);
readstdin();
if(fast) {
grabkeyboard();
readstdin();
}
else {
readstdin();
grabkeyboard();
}
setup(); setup();
run(); run();
return EXIT_FAILURE; /* should not reach */ return EXIT_FAILURE; /* unreachable */
} }
void void
@ -115,6 +124,7 @@ appenditem(Item *item, Item **list, Item **last) {
*list = item; *list = item;
else else
(*last)->right = item; (*last)->right = item;
item->left = *last; item->left = *last;
item->right = NULL; item->right = NULL;
*last = item; *last = item;
@ -122,7 +132,7 @@ appenditem(Item *item, Item **list, Item **last) {
void void
calcoffsets(void) { calcoffsets(void) {
unsigned int i, n; int i, n;
if(lines > 0) if(lines > 0)
n = lines * bh; n = lines * bh;
@ -137,6 +147,16 @@ calcoffsets(void) {
break; break;
} }
char *
cistrstr(const char *s, const char *sub) {
size_t len;
for(len = strlen(sub); *s; s++)
if(!strncasecmp(s, sub, len))
return (char *)s;
return NULL;
}
void void
drawmenu(void) { drawmenu(void) {
int curpos; int curpos;
@ -182,22 +202,13 @@ drawmenu(void) {
mapdc(dc, win, mw, mh); mapdc(dc, win, mw, mh);
} }
char *
fstrstr(const char *s, const char *sub) {
size_t len;
for(len = strlen(sub); *s; s++)
if(!fstrncmp(s, sub, len))
return (char *)s;
return NULL;
}
void void
grabkeyboard(void) { grabkeyboard(void) {
int i; int i;
for(i = 0; i < 1000; i++) { for(i = 0; i < 1000; i++) {
if(!XGrabKeyboard(dc->dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) if(XGrabKeyboard(dc->dpy, DefaultRootWindow(dc->dpy), True,
GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
return; return;
usleep(1000); usleep(1000);
} }
@ -205,77 +216,58 @@ grabkeyboard(void) {
} }
void void
insert(const char *s, ssize_t n) { insert(const char *str, ssize_t n) {
if(strlen(text) + n > sizeof text - 1) if(strlen(text) + n > sizeof text - 1)
return; return;
memmove(text + cursor + n, text + cursor, sizeof text - cursor - MAX(n, 0)); memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
if(n > 0) if(n > 0)
memcpy(text + cursor, s, n); memcpy(&text[cursor], str, n);
cursor += n; cursor += n;
match(); match(n > 0 && text[cursor] == '\0');
} }
void void
keypress(XKeyEvent *ev) { keypress(XKeyEvent *ev) {
char buf[32]; char buf[32];
size_t len;
KeySym ksym; KeySym ksym;
len = strlen(text);
XLookupString(ev, buf, sizeof buf, &ksym, NULL); XLookupString(ev, buf, sizeof buf, &ksym, NULL);
if(ev->state & ControlMask) { if(ev->state & ControlMask) {
switch(tolower(ksym)) { KeySym lower, upper;
default:
return; XConvertCase(ksym, &lower, &upper);
case XK_a: switch(lower) {
ksym = XK_Home; case XK_a: ksym = XK_Home; break;
break; case XK_b: ksym = XK_Left; break;
case XK_b: case XK_c: ksym = XK_Escape; break;
ksym = XK_Left; case XK_d: ksym = XK_Delete; break;
break; case XK_e: ksym = XK_End; break;
case XK_c: case XK_f: ksym = XK_Right; break;
ksym = XK_Escape; case XK_h: ksym = XK_BackSpace; break;
break; case XK_i: ksym = XK_Tab; break;
case XK_d: case XK_j: ksym = XK_Return; break;
ksym = XK_Delete; case XK_m: ksym = XK_Return; break;
break; case XK_n: ksym = XK_Up; break;
case XK_e: case XK_p: ksym = XK_Down; break;
ksym = XK_End;
break; case XK_k: /* delete right */
case XK_f:
ksym = XK_Right;
break;
case XK_h:
ksym = XK_BackSpace;
break;
case XK_i:
ksym = XK_Tab;
break;
case XK_j:
ksym = XK_Return;
break;
case XK_k: /* delete right */
text[cursor] = '\0'; text[cursor] = '\0';
match(); match(False);
break; break;
case XK_n: case XK_u: /* delete left */
ksym = XK_Down;
break;
case XK_p:
ksym = XK_Up;
break;
case XK_u: /* delete left */
insert(NULL, 0 - cursor); insert(NULL, 0 - cursor);
break; break;
case XK_w: /* delete word */ case XK_w: /* delete word */
while(cursor > 0 && text[nextrune(-1)] == ' ') while(cursor > 0 && text[nextrune(-1)] == ' ')
insert(NULL, nextrune(-1) - cursor); insert(NULL, nextrune(-1) - cursor);
while(cursor > 0 && text[nextrune(-1)] != ' ') while(cursor > 0 && text[nextrune(-1)] != ' ')
insert(NULL, nextrune(-1) - cursor); insert(NULL, nextrune(-1) - cursor);
break; break;
case XK_y: /* paste selection */ case XK_y: /* paste selection */
XConvertSelection(dc->dpy, XA_PRIMARY, utf8, utf8, win, CurrentTime); XConvertSelection(dc->dpy, XA_PRIMARY, utf8, utf8, win, CurrentTime);
return; return;
default:
return;
} }
} }
switch(ksym) { switch(ksym) {
@ -284,24 +276,29 @@ keypress(XKeyEvent *ev) {
insert(buf, strlen(buf)); insert(buf, strlen(buf));
break; break;
case XK_Delete: case XK_Delete:
if(cursor == len) if(text[cursor] == '\0')
return; return;
cursor = nextrune(+1); cursor = nextrune(+1);
/* fallthrough */
case XK_BackSpace: case XK_BackSpace:
if(cursor > 0) if(cursor == 0)
insert(NULL, nextrune(-1) - cursor); return;
insert(NULL, nextrune(-1) - cursor);
break; break;
case XK_End: case XK_End:
if(cursor < len) { if(text[cursor] != '\0') {
cursor = len; cursor = strlen(text);
break; break;
} }
while(next) { if(next) {
sel = curr = next; curr = matchend;
calcoffsets(); calcoffsets();
curr = prev;
calcoffsets();
while(next && (curr = curr->right))
calcoffsets();
} }
while(sel && sel->right) sel = matchend;
sel = sel->right;
break; break;
case XK_Escape: case XK_Escape:
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -318,8 +315,7 @@ keypress(XKeyEvent *ev) {
cursor = nextrune(-1); cursor = nextrune(-1);
break; break;
} }
else if(lines > 0) /* fallthrough */
return;
case XK_Up: case XK_Up:
if(sel && sel->left && (sel = sel->left)->right == curr) { if(sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev; curr = prev;
@ -340,16 +336,14 @@ keypress(XKeyEvent *ev) {
break; break;
case XK_Return: case XK_Return:
case XK_KP_Enter: case XK_KP_Enter:
fputs((sel && !(ev->state & ShiftMask)) ? sel->text : text, stdout); puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
fflush(stdout);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case XK_Right: case XK_Right:
if(cursor < len) { if(text[cursor] != '\0') {
cursor = nextrune(+1); cursor = nextrune(+1);
break; break;
} }
else if(lines > 0) /* fallthrough */
return;
case XK_Down: case XK_Down:
if(sel && sel->right && (sel = sel->right) == next) { if(sel && sel->right && (sel = sel->right) == next) {
curr = next; curr = next;
@ -361,58 +355,58 @@ keypress(XKeyEvent *ev) {
return; return;
strncpy(text, sel->text, sizeof text); strncpy(text, sel->text, sizeof text);
cursor = strlen(text); cursor = strlen(text);
match(); match(True);
break; break;
} }
drawmenu(); drawmenu();
} }
void void
match(void) { match(Bool sub) {
size_t len; size_t len = strlen(text);
Item *item, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; Item *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
Item *item, *lnext;
len = strlen(text); lexact = lprefix = lsubstr = exactend = prefixend = substrend = NULL;
matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; for(item = sub ? matches : items; item && item->text; item = lnext) {
for(item = items; item; item = item->next) lnext = sub ? item->right : item + 1;
if(!fstrncmp(text, item->text, len + 1)) if(!fstrncmp(text, item->text, len + 1))
appenditem(item, &lexact, &exactend); appenditem(item, &lexact, &exactend);
else if(!fstrncmp(text, item->text, len)) else if(!fstrncmp(text, item->text, len))
appenditem(item, &lprefix, &prefixend); appenditem(item, &lprefix, &prefixend);
else if(fstrstr(item->text, text)) else if(fstrstr(item->text, text))
appenditem(item, &lsubstr, &substrend); appenditem(item, &lsubstr, &substrend);
if(lexact) {
matches = lexact;
itemend = exactend;
} }
matches = lexact;
matchend = exactend;
if(lprefix) { if(lprefix) {
if(itemend) { if(matchend) {
itemend->right = lprefix; matchend->right = lprefix;
lprefix->left = itemend; lprefix->left = matchend;
} }
else else
matches = lprefix; matches = lprefix;
itemend = prefixend; matchend = prefixend;
} }
if(lsubstr) { if(lsubstr) {
if(itemend) { if(matchend) {
itemend->right = lsubstr; matchend->right = lsubstr;
lsubstr->left = itemend; lsubstr->left = matchend;
} }
else else
matches = lsubstr; matches = lsubstr;
matchend = substrend;
} }
curr = prev = next = sel = matches; curr = sel = matches;
calcoffsets(); calcoffsets();
} }
size_t size_t
nextrune(int incr) { nextrune(int inc) {
size_t n, len; ssize_t n;
len = strlen(text); for(n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc);
for(n = cursor + incr; n >= 0 && n < len && (text[n] & 0xc0) == 0x80; n += incr);
return n; return n;
} }
@ -425,26 +419,30 @@ paste(void) {
XGetWindowProperty(dc->dpy, win, utf8, 0, (sizeof text / 4) + 1, False, XGetWindowProperty(dc->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);
insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p));
XFree(p); XFree(p);
drawmenu(); drawmenu();
} }
void void
readstdin(void) { readstdin(void) {
char buf[sizeof text], *p; char buf[sizeof text], *p, *maxstr = NULL;
Item *item, **end; size_t i, max = 0, size = 0;
for(end = &items; fgets(buf, sizeof buf, stdin); *end = item, end = &item->next) { for(i = 0; fgets(buf, sizeof buf, stdin); i++) {
if(i+1 >= size / sizeof *items)
if(!(items = realloc(items, (size += BUFSIZ))))
eprintf("cannot realloc %u bytes:", size);
if((p = strchr(buf, '\n'))) if((p = strchr(buf, '\n')))
*p = '\0'; *p = '\0';
if(!(item = malloc(sizeof *item))) if(!(items[i].text = strdup(buf)))
eprintf("cannot malloc %u bytes\n", sizeof *item); eprintf("cannot strdup %u bytes:", strlen(buf)+1);
if(!(item->text = strdup(buf))) if(strlen(items[i].text) > max)
eprintf("cannot strdup %u bytes\n", strlen(buf)+1); max = strlen(maxstr = items[i].text);
item->next = item->left = item->right = NULL;
inputw = MAX(inputw, textw(dc, item->text));
} }
if(items)
items[i].text = NULL;
inputw = maxstr ? textw(dc, maxstr) : 0;
} }
void void
@ -455,7 +453,7 @@ run(void) {
switch(ev.type) { switch(ev.type) {
case Expose: case Expose:
if(ev.xexpose.count == 0) if(ev.xexpose.count == 0)
drawmenu(); mapdc(dc, win, mw, mh);
break; break;
case KeyPress: case KeyPress:
keypress(&ev.xkey); keypress(&ev.xkey);
@ -473,21 +471,20 @@ run(void) {
void void
setup(void) { setup(void) {
int x, y, screen; int x, y, screen = DefaultScreen(dc->dpy);
Window root = RootWindow(dc->dpy, screen);
XSetWindowAttributes wa; XSetWindowAttributes wa;
#ifdef XINERAMA #ifdef XINERAMA
int n; int n;
XineramaScreenInfo *info; XineramaScreenInfo *info;
#endif #endif
screen = DefaultScreen(dc->dpy);
root = RootWindow(dc->dpy, screen);
utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
normcol[ColBG] = getcolor(dc, normbgcolor); normcol[ColBG] = getcolor(dc, normbgcolor);
normcol[ColFG] = getcolor(dc, normfgcolor); normcol[ColFG] = getcolor(dc, normfgcolor);
selcol[ColBG] = getcolor(dc, selbgcolor); selcol[ColBG] = getcolor(dc, selbgcolor);
selcol[ColFG] = getcolor(dc, selfgcolor); selcol[ColFG] = getcolor(dc, selfgcolor);
utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
/* menu geometry */ /* menu geometry */
bh = dc->font.height + 2; bh = dc->font.height + 2;
@ -500,9 +497,8 @@ setup(void) {
Window dw; Window dw;
XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du); XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
for(i = 0; i < n; i++) for(i = 0; i < n-1; i++)
if((monitor == info[i].screen_number) if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
|| (monitor < 0 && INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)))
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);
@ -516,6 +512,10 @@ setup(void) {
y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh; y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh;
mw = DisplayWidth(dc->dpy, screen); mw = DisplayWidth(dc->dpy, screen);
} }
promptw = prompt ? textw(dc, prompt) : 0;
inputw = MIN(inputw, mw/3);
match(False);
/* menu window */ /* menu window */
wa.override_redirect = True; wa.override_redirect = True;
wa.background_pixmap = ParentRelative; wa.background_pixmap = ParentRelative;
@ -525,18 +525,14 @@ setup(void) {
DefaultVisual(dc->dpy, screen), DefaultVisual(dc->dpy, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
grabkeyboard();
resizedc(dc, mw, mh);
inputw = MIN(inputw, mw/3);
promptw = prompt ? textw(dc, prompt) : 0;
XMapRaised(dc->dpy, win); XMapRaised(dc->dpy, win);
text[0] = '\0'; resizedc(dc, mw, mh);
match(); drawmenu();
} }
void void
usage(void) { usage(void) {
fputs("usage: dmenu [-b] [-i] [-l lines] [-m monitor] [-p prompt] [-fn font]\n" fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font]\n"
" [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View File

@ -1,101 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#define CACHE ".dmenu_cache"
static void die(const char *s);
static int qstrcmp(const void *a, const void *b);
static void scan(void);
static int uptodate(void);
static char **items = NULL;
static const char *home, *path;
int
main(void) {
if(!(home = getenv("HOME")))
die("no $HOME");
if(!(path = getenv("PATH")))
die("no $PATH");
if(chdir(home) < 0)
die("chdir failed");
if(uptodate()) {
execlp("cat", "cat", CACHE, NULL);
die("exec failed");
}
scan();
return EXIT_SUCCESS;
}
void
die(const char *s) {
fprintf(stderr, "dmenu_path: %s\n", s);
exit(EXIT_FAILURE);
}
int
qstrcmp(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
void
scan(void) {
char buf[PATH_MAX];
char *dir, *p;
size_t i, count;
struct dirent *ent;
DIR *dp;
FILE *cache;
count = 0;
if(!(p = strdup(path)))
die("strdup failed");
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":")) {
if(!(dp = opendir(dir)))
continue;
while((ent = readdir(dp))) {
snprintf(buf, sizeof buf, "%s/%s", dir, ent->d_name);
if(ent->d_name[0] == '.' || access(buf, X_OK) < 0)
continue;
if(!(items = realloc(items, ++count * sizeof *items)))
die("malloc failed");
if(!(items[count-1] = strdup(ent->d_name)))
die("strdup failed");
}
closedir(dp);
}
qsort(items, count, sizeof *items, qstrcmp);
if(!(cache = fopen(CACHE, "w")))
die("open failed");
for(i = 0; i < count; i++) {
if(i > 0 && !strcmp(items[i], items[i-1]))
continue;
fprintf(cache, "%s\n", items[i]);
fprintf(stdout, "%s\n", items[i]);
}
fclose(cache);
free(p);
}
int
uptodate(void) {
char *dir, *p;
time_t mtime;
struct stat st;
if(stat(CACHE, &st) < 0)
return 0;
mtime = st.st_mtime;
if(!(p = strdup(path)))
die("strdup failed");
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":"))
if(!stat(dir, &st) && st.st_mtime > mtime)
return 0;
free(p);
return 1;
}

View File

@ -1,2 +1,9 @@
#!/bin/sh #!/bin/sh
exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe CACHE=${XDG_CACHE_HOME:-"$HOME/.cache"}/dmenu_run
(
IFS=:
if test "`ls -dt $PATH "$CACHE" 2> /dev/null | sed 1q`" != "$CACHE"; then
mkdir -p "`dirname "$CACHE"`" && lsx $PATH | sort -u > "$CACHE"
fi
)
cmd=`dmenu "$@" < "$CACHE"` && exec sh -c "$cmd"

87
draw.c
View File

@ -7,33 +7,28 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include "draw.h" #include "draw.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DEFFONT "fixed" #define DEFAULTFN "fixed"
static Bool loadfont(DC *dc, const char *fontstr); static Bool loadfont(DC *dc, const char *fontstr);
void void
drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) { 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); XSetForeground(dc->dpy, dc->gc, color);
(fill ? XFillRectangles : XDrawRectangles)(dc->dpy, dc->canvas, dc->gc, &r, 1); if(fill)
XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w, h);
else
XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w-1, h-1);
} }
void void
drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { drawtext(DC *dc, const char *text, unsigned long col[ColLast]) {
char buf[256]; char buf[BUFSIZ];
size_t n, mn; size_t mn, n = strlen(text);
/* shorten text if necessary */ /* shorten text if necessary */
n = strlen(text); for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) + dc->font.height/2 > dc->w; mn--)
for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) > dc->w - dc->font.height/2; mn--)
if(mn == 0) if(mn == 0)
return; return;
memcpy(buf, text, mn); memcpy(buf, text, mn);
@ -46,10 +41,8 @@ drawtext(DC *dc, const char *text, unsigned long col[ColLast]) {
void void
drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) { drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) {
int x, y; int x = dc->x + dc->font.height/2;
int y = dc->y + dc->font.ascent+1;
x = dc->x + dc->font.height/2;
y = dc->y + dc->font.ascent+1;
XSetForeground(dc->dpy, dc->gc, FG(dc, col)); XSetForeground(dc->dpy, dc->gc, FG(dc, col));
if(dc->font.set) if(dc->font.set)
@ -64,10 +57,14 @@ void
eprintf(const char *fmt, ...) { eprintf(const char *fmt, ...) {
va_list ap; va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
if(fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
}
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -99,55 +96,52 @@ initdc(void) {
DC *dc; DC *dc;
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
weprintf("no locale support\n"); fputs("no locale support\n", stderr);
if(!(dc = malloc(sizeof *dc))) if(!(dc = calloc(1, sizeof *dc)))
eprintf("cannot malloc %u bytes\n", sizeof *dc); eprintf("cannot malloc %u bytes:", sizeof *dc);
if(!(dc->dpy = XOpenDisplay(NULL))) if(!(dc->dpy = XOpenDisplay(NULL)))
eprintf("cannot open display\n"); eprintf("cannot open display\n");
dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL); dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL);
XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter); XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter);
dc->font.xfont = NULL;
dc->font.set = NULL;
dc->canvas = None;
return dc; return dc;
} }
void void
initfont(DC *dc, const char *fontstr) { initfont(DC *dc, const char *fontstr) {
if(!loadfont(dc, fontstr ? fontstr : DEFFONT)) { if(!loadfont(dc, fontstr ? fontstr : DEFAULTFN)) {
if(fontstr != NULL) if(fontstr != NULL)
weprintf("cannot load font '%s'\n", fontstr); fprintf(stderr, "cannot load font '%s'\n", fontstr);
if(fontstr == NULL || !loadfont(dc, DEFFONT)) if(fontstr == NULL || !loadfont(dc, DEFAULTFN))
eprintf("cannot load font '%s'\n", DEFFONT); eprintf("cannot load font '%s'\n", DEFAULTFN);
} }
dc->font.height = dc->font.ascent + dc->font.descent; dc->font.height = dc->font.ascent + dc->font.descent;
} }
Bool Bool
loadfont(DC *dc, const char *fontstr) { loadfont(DC *dc, const char *fontstr) {
char *def, **missing; char *def, **missing, **names;
int i, n; int i, n;
XFontStruct **xfonts;
if(!*fontstr) if(!*fontstr)
return False; return False;
if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
char **names;
XFontStruct **xfonts;
n = XFontsOfFontSet(dc->font.set, &xfonts, &names); n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
for(i = dc->font.ascent = dc->font.descent = 0; i < n; i++) { for(i = 0; i < n; i++) {
dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent);
dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width);
} }
} }
else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
dc->font.ascent = dc->font.xfont->ascent; dc->font.ascent = dc->font.xfont->ascent;
dc->font.descent = dc->font.xfont->descent; dc->font.descent = dc->font.xfont->descent;
dc->font.width = dc->font.xfont->max_bounds.width;
} }
if(missing) if(missing)
XFreeStringList(missing); XFreeStringList(missing);
return (dc->font.set || dc->font.xfont); return dc->font.set || dc->font.xfont;
} }
void void
@ -159,12 +153,11 @@ void
resizedc(DC *dc, unsigned int w, unsigned int h) { resizedc(DC *dc, unsigned int w, unsigned int h) {
if(dc->canvas) if(dc->canvas)
XFreePixmap(dc->dpy, 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->w = w;
dc->h = h; dc->h = h;
dc->invert = False; dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)));
} }
int int
@ -182,13 +175,3 @@ int
textw(DC *dc, const char *text) { textw(DC *dc, const char *text) {
return textnw(dc, text, strlen(text)) + dc->font.height; 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);
}

10
draw.h
View File

@ -15,23 +15,21 @@ typedef struct {
int ascent; int ascent;
int descent; int descent;
int height; int height;
int width;
XFontSet set; XFontSet set;
XFontStruct *xfont; XFontStruct *xfont;
} font; } font;
} DC; /* draw context */ } 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 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 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 drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]);
void initfont(DC *dc, const char *fontstr); void eprintf(const char *fmt, ...);
void freedc(DC *dc); void freedc(DC *dc);
unsigned long getcolor(DC *dc, const char *colstr);
DC *initdc(void); DC *initdc(void);
void initfont(DC *dc, const char *fontstr);
void mapdc(DC *dc, Window win, unsigned int w, unsigned int h); void mapdc(DC *dc, Window win, unsigned int w, unsigned int h);
void resizedc(DC *dc, 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 textnw(DC *dc, const char *text, size_t len);
int textw(DC *dc, const char *text); int textw(DC *dc, const char *text);
void eprintf(const char *fmt, ...);
void weprintf(const char *fmt, ...);
const char *progname;

11
lsx.1 Normal file
View File

@ -0,0 +1,11 @@
.TH LSX 1 dmenu\-VERSION
.SH NAME
lsx \- list executables
.SH SYNOPSIS
.B lsx
.RI [ directory ...]
.SH DESCRIPTION
.B lsx
lists the executables in each
.IR directory .
If none are given the current working directory is used.

38
lsx.c Normal file
View File

@ -0,0 +1,38 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
static void lsx(const char *dir);
int
main(int argc, char *argv[]) {
int i;
if(argc < 2)
lsx(".");
else for(i = 1; i < argc; i++)
lsx(argv[i]);
return EXIT_SUCCESS;
}
void
lsx(const char *dir) {
char buf[PATH_MAX];
struct dirent *d;
struct stat st;
DIR *dp;
if(!(dp = opendir(dir))) {
perror(dir);
return;
}
while((d = readdir(dp)))
if(snprintf(buf, sizeof buf, "%s/%s", dir, d->d_name) < (int)sizeof buf
&& !stat(buf, &st) && S_ISREG(st.st_mode) && access(buf, X_OK) == 0)
puts(d->d_name);
closedir(dp);
}