updated manpage, added paste, cleaned up, new libdraw

This commit is contained in:
Connor Lane Smith 2010-07-31 14:56:27 +01:00
parent 7d5fe17391
commit a3606ecb0e
4 changed files with 276 additions and 324 deletions

View file

@ -6,7 +6,7 @@ include config.mk
SRC = dmenu.c SRC = dmenu.c
OBJ = ${SRC:.c=.o} OBJ = ${SRC:.c=.o}
all: options dinput dmenu all: options dmenu
options: options:
@echo dmenu build options: @echo dmenu build options:

6
README
View file

@ -8,13 +8,11 @@ Requirements
In order to build dmenu you need the Xlib header files. In order to build dmenu you need the Xlib header files.
You also need libdraw, available from http://hg.suckless.org/libdraw You also need libdraw, available from http://hg.suckless.org/libdraw
Pasting the X selection to dmenu requires the sselp utility at runtime.
Installation Installation
------------ ------------
Edit config.mk to match your local setup (dmenu is installed into Edit config.mk to match your local setup (dmenu is installed into the
the /usr/local namespace by default). /usr/local namespace by default).
Afterwards enter the following command to build and install dmenu (if Afterwards enter the following command to build and install dmenu (if
necessary as root): necessary as root):

102
dmenu.1
View file

@ -3,99 +3,103 @@
dmenu \- dynamic menu dmenu \- dynamic menu
.SH SYNOPSIS .SH SYNOPSIS
.B dmenu .B dmenu
.RB [ \-i ]
.RB [ \-b ] .RB [ \-b ]
.RB [ \-e " <xid>]" .RB [ \-i ]
.RB [ \-l " <lines>]" .RB [ \-l " <lines>]"
.RB [ \-p " <prompt>]"
.RB [ \-fn " <font>]" .RB [ \-fn " <font>]"
.RB [ \-nb " <color>]" .RB [ \-nb " <color>]"
.RB [ \-nf " <color>]" .RB [ \-nf " <color>]"
.RB [ \-p " <prompt>]"
.RB [ \-sb " <color>]" .RB [ \-sb " <color>]"
.RB [ \-sf " <color>]" .RB [ \-sf " <color>]"
.RB [ \-v ] .RB [ \-v ]
.B dmenu_run .B dmenu_run
[<options...>] .RB [ \-b ]
.RB [ \-i ]
.RB [ \-l " <lines>]"
.RB [ \-p " <prompt>]"
.RB [ \-fn " <font>]"
.RB [ \-nb " <color>]"
.RB [ \-nf " <color>]"
.RB [ \-sb " <color>]"
.RB [ \-sf " <color>]"
.RB [ \-v ]
.B dmenu_path .B dmenu_path
.SH DESCRIPTION .SH DESCRIPTION
.SS Overview .SS Overview
dmenu is a generic menu for X, originally designed for .B dmenu
is a generic menu for X, originally designed for
.BR dwm (1). .BR dwm (1).
It manages huge amounts (up to 10.000 and more) of user defined menu items It manages huge amounts (10000 and more) of user defined menu items efficiently.
efficiently. .P
.B dmenu_run
dmenu_run is a dmenu script used by dwm which lists executables in the user's PATH is a dmenu script which lists programs in the user's PATH and executes
and executes the selected item. the selected item.
.P
dmenu_path is a script used by dmenu_run to find and cache a list of executables. .B dmenu_path
is a script used by
.I dmenu_run
to find and cache a list of programs.
.SS Options .SS Options
.TP .TP
.B \-i
makes dmenu match menu entries case insensitively.
.TP
.B \-b .B \-b
defines that dmenu appears at the bottom. dmenu appears at the bottom of the screen.
.TP .TP
.B \-e <xid> .B \-i
reparents dmenu to the window specified by xid. dmenu matches menu entries case insensitively.
.TP .TP
.B \-l <lines> .B \-l <lines>
activates vertical list mode. dmenu lists items vertically, with the given number of lines.
The given number of lines will be displayed. Window height will be adjusted.
.TP
.B \-fn <font>
defines the font.
.TP
.B \-nb <color>
defines the normal background color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-nf <color>
defines the normal foreground color (#RGB, #RRGGBB, and color names are supported).
.TP .TP
.B \-p <prompt> .B \-p <prompt>
defines a prompt to be displayed before the input area. sets the prompt to be displayed to the left of the input area.
.TP
.B \-fn <font>
sets the font.
.TP
.B \-nb <color>
sets the background color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-nf <color>
sets the foreground color (#RGB, #RRGGBB, and color names are supported).
.TP .TP
.B \-sb <color> .B \-sb <color>
defines the selected background color (#RGB, #RRGGBB, and color names are supported). sets the background color of selected items (#RGB, #RRGGBB, and color names are
supported).
.TP .TP
.B \-sf <color> .B \-sf <color>
defines the selected foreground color (#RGB, #RRGGBB, and color names are supported). sets the foreground color of selected items (#RGB, #RRGGBB, and color names are
supported).
.TP .TP
.B \-v .B \-v
prints version information to standard output, then exits. prints version information to standard output, then exits.
.SH USAGE .SH USAGE
dmenu reads a list of newline-separated items from standard input and creates a dmenu reads a list of newline-separated items from standard input and creates a
menu. When the user selects an item or enters any text and presses Return, his/her menu. When the user selects an item or enters any text and presses Return,
choice is printed to standard output and dmenu terminates. their choice is printed to standard output and dmenu terminates.
.P .P
dmenu is completely controlled by the keyboard. Besides standard Unix line editing, dmenu is completely controlled by the keyboard. Besides standard Unix line
and item selection (Up/Down or Left/Right, PageUp/PageDown, Home/End), the following editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the
keys are recognized: following keys are recognized:
.TP .TP
.B Tab (Control\-i) .B Tab (Control\-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 (Control\-j)
Confirm selection and quit (print the selected item to standard output). Returns Confirm selection. Prints the selected item to standard output and exits,
.B 0 returning success.
on termination.
.TP .TP
.B Shift\-Return (Control\-Shift\-j) .B Shift\-Return (Control\-Shift\-j)
Confirm input and quit (print the text in the input field to standard output). Confirm input. Prints the input text to standard output and exits, returning
Returns success.
.B 0
on termination.
.TP .TP
.B Escape (Control\-c) .B Escape (Control\-c)
Quit without selecting an item. Returns Quit without selecting an item, returning failure.
.B 1
on termination.
.TP .TP
.B Control\-y .B Control\-y
Pastes the X selection into the input field. This requires Paste the current X selection into the input field.
.BR sselp (1).
.SH SEE ALSO .SH SEE ALSO
.BR dwm (1), .BR dwm (1),
.BR wmii (1). .BR wmii (1).

478
dmenu.c
View file

@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#ifdef XINERAMA #ifdef XINERAMA
@ -14,78 +15,72 @@
#include <draw.h> #include <draw.h>
#include "config.h" #include "config.h"
#define INRECT(x,y,rx,ry,rw,rh) ((rx) < (x) && (x) < (rx)+(rw) && (ry) < (y) && (y) < (ry)+(rh)) #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
#define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b))
#define IS_UTF8_1ST_CHAR(c) (((c) & 0xc0) == 0xc0 || ((c) & 0x80) == 0x00) #define UTF8_CODEPOINT(c) (((c) & 0xc0) != 0x80)
typedef struct Item Item; typedef struct Item Item;
struct Item { struct Item {
char *text; char *text;
Item *next; /* traverses all items */ Item *next; /* traverses all items */
Item *left, *right; /* traverses items matching current search pattern */ Item *left, *right; /* traverses matching items */
}; };
static void appenditem(Item *i, Item **list, Item **last); static void appenditem(Item *item, Item **list, Item **last);
static void calcoffsetsh(void); static void calcoffsetsh(void);
static void calcoffsetsv(void); static void calcoffsetsv(void);
static char *cistrstr(const char *s, const char *sub); static char *cistrstr(const char *s, const char *sub);
static void cleanup(void);
static void drawitem(const char *s, unsigned long col[ColLast]);
static void drawmenu(void); static void drawmenu(void);
static void drawmenuh(void); static void drawmenuh(void);
static void drawmenuv(void); static void drawmenuv(void);
static void grabkeyboard(void); static void grabkeyboard(void);
static void insert(const char *s, ssize_t n);
static void keypress(XKeyEvent *e); static void keypress(XKeyEvent *e);
static void match(void); static void match(void);
static void paste(Atom atom);
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 char **argp = NULL;
static char *maxname = NULL;
static char *prompt; static char *prompt;
static char text[4096]; static char text[4096];
static int promptw;
static int screen; static int screen;
static size_t cur = 0; static size_t cursor = 0;
static unsigned int cmdw = 0; static unsigned int inputw = 0;
static unsigned int lines = 0; static unsigned int lines = 0;
static unsigned int numlockmask;
static unsigned int mw, mh; static unsigned int mw, mh;
static unsigned int promptw = 0;
static unsigned long normcol[ColLast]; static unsigned long normcol[ColLast];
static unsigned long selcol[ColLast]; static unsigned long selcol[ColLast];
static Atom utf8;
static Bool topbar = True; static Bool topbar = True;
static DC dc; static DC dc;
static Display *dpy; static Item *allitems, *matches;
static Item *allitems = NULL; /* first of all items */ static Item *curr, *prev, *next, *sel;
static Item *item = NULL; /* first of pattern matching items */ static Window root, win;
static Item *sel = NULL;
static Item *next = NULL;
static Item *prev = NULL;
static Item *curr = NULL;
static Window win, root;
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 (*calcoffsets)(void) = calcoffsetsh; static void (*calcoffsets)(void) = calcoffsetsh;
void void
appenditem(Item *i, Item **list, Item **last) { appenditem(Item *item, Item **list, Item **last) {
if(!(*last)) if(!(*last))
*list = i; *list = item;
else else
(*last)->right = i; (*last)->right = item;
i->left = *last; item->left = *last;
i->right = NULL; item->right = NULL;
*last = i; *last = item;
} }
void void
calcoffsetsh(void) { calcoffsetsh(void) {
unsigned int w, x; unsigned int w, x;
w = promptw + cmdw + textw(&dc, "<") + textw(&dc, ">"); w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">");
for(x = w, next = curr; next; next = next->right) for(x = w, next = curr; next; next = next->right)
if((x += MIN(textw(&dc, next->text), mw / 3)) > mw) if((x += MIN(textw(&dc, next->text), mw / 3)) > mw)
break; break;
@ -128,33 +123,6 @@ cistrstr(const char *s, const char *sub) {
return (char *)s; return (char *)s;
} }
void
cleanup(void) {
Item *itm;
while(allitems) {
itm = allitems->next;
free(allitems->text);
free(allitems);
allitems = itm;
}
cleanupdraw(&dc);
XDestroyWindow(dpy, win);
XUngrabKeyboard(dpy, CurrentTime);
XCloseDisplay(dpy);
}
void
drawitem(const char *s, unsigned long col[ColLast]) {
const char *p;
unsigned int w = textnw(&dc, text, strlen(text));
drawbox(&dc, col);
drawtext(&dc, s, col);
for(p = fstrstr(s, text); *text && (p = fstrstr(p, text)); p++)
drawline(&dc, textnw(&dc, s, p-s) + dc.h/2 - 1, dc.h-2, w, 1, col);
}
void void
drawmenu(void) { drawmenu(void) {
dc.x = 0; dc.x = 0;
@ -172,82 +140,89 @@ drawmenu(void) {
dc.x += dc.w; dc.x += dc.w;
} }
dc.w = mw - dc.x; dc.w = mw - dc.x;
/* print command */ /* print input area */
if(cmdw && item && lines == 0) if(matches && lines == 0 && textw(&dc, text) <= inputw)
dc.w = cmdw; dc.w = inputw;
drawtext(&dc, text, normcol); drawtext(&dc, text, normcol);
drawline(&dc, textnw(&dc, text, cur) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); drawline(&dc, textnw(&dc, text, cursor) + dc.h/2 - 2, 2, 1, dc.h-4, normcol);
if(lines > 0) if(lines > 0)
drawmenuv(); drawmenuv();
else if(curr) else if(curr && (dc.w == inputw || curr->next))
drawmenuh(); drawmenuh();
commitdraw(&dc, win); commitdraw(&dc, win);
} }
void void
drawmenuh(void) { drawmenuh(void) {
Item *i; Item *item;
dc.x += cmdw; dc.x += inputw;
dc.w = textw(&dc, "<"); dc.w = textw(&dc, "<");
drawtext(&dc, curr->left ? "<" : NULL, normcol); if(curr->left)
drawtext(&dc, "<", normcol);
dc.x += dc.w; dc.x += dc.w;
for(i = curr; i != next; i = i->right) { for(item = curr; item != next; item = item->right) {
dc.w = MIN(textw(&dc, i->text), mw / 3); dc.w = MIN(textw(&dc, item->text), mw / 3);
drawitem(i->text, (sel == i) ? selcol : normcol); if(item == sel)
drawbox(&dc, selcol);
drawtext(&dc, item->text, (item == sel) ? selcol : normcol);
dc.x += dc.w; dc.x += dc.w;
} }
dc.w = textw(&dc, ">"); dc.w = textw(&dc, ">");
dc.x = mw - dc.w; dc.x = mw - dc.w;
drawtext(&dc, next ? ">" : NULL, normcol); if(next)
drawtext(&dc, ">", normcol);
} }
void void
drawmenuv(void) { drawmenuv(void) {
Item *i; Item *item;
XWindowAttributes wa; XWindowAttributes wa;
dc.y = topbar ? dc.h : 0; dc.y = topbar ? dc.h : 0;
dc.w = mw - dc.x; dc.w = mw - dc.x;
for(i = curr; i != next; i = i->right) { for(item = curr; item != next; item = item->right) {
drawitem(i->text, (sel == i) ? selcol : normcol); if(item == sel)
drawbox(&dc, selcol);
drawtext(&dc, item->text, (item == sel) ? selcol : normcol);
dc.y += dc.h; dc.y += dc.h;
} }
if(!XGetWindowAttributes(dpy, win, &wa)) if(!XGetWindowAttributes(dc.dpy, win, &wa))
eprint("cannot get window attributes"); eprintf("cannot get window attributes\n");
XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); if(wa.height != mh)
XMoveResizeWindow(dc.dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh);
} }
void void
grabkeyboard(void) { grabkeyboard(void) {
unsigned int n; int i;
for(n = 0; n < 1000; n++) { for(i = 0; i < 1000; i++) {
if(!XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime))
return; return;
usleep(1000); usleep(1000);
} }
exit(EXIT_FAILURE); eprintf("cannot grab keyboard\n");
}
void
insert(const char *s, ssize_t n) {
memmove(text + cursor + n, text + cursor, sizeof text - cursor - n);
if(n > 0)
memcpy(text + cursor, s, n);
cursor += n;
match();
} }
void void
keypress(XKeyEvent *e) { keypress(XKeyEvent *e) {
char buf[sizeof text]; char buf[sizeof text];
int num; int n;
unsigned int i, len; size_t len;
KeySym ksym; KeySym ksym;
len = strlen(text); len = strlen(text);
num = XLookupString(e, buf, sizeof buf, &ksym, NULL); XLookupString(e, buf, sizeof buf, &ksym, NULL);
if(ksym == XK_KP_Enter)
ksym = XK_Return;
else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
ksym = (ksym - XK_KP_0) + XK_0;
else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|| IsPrivateKeypadKey(ksym))
return;
/* first check if a control mask is omitted */
if(e->state & ControlMask) { if(e->state & ControlMask) {
switch(tolower(ksym)) { switch(tolower(ksym)) {
default: default:
@ -277,8 +252,8 @@ keypress(XKeyEvent *e) {
case XK_m: case XK_m:
ksym = XK_Return; ksym = XK_Return;
break; break;
case XK_k: case XK_k: /* delete right */
text[cur] = '\0'; text[cursor] = '\0';
break; break;
case XK_n: case XK_n:
ksym = XK_Down; ksym = XK_Down;
@ -286,66 +261,44 @@ keypress(XKeyEvent *e) {
case XK_p: case XK_p:
ksym = XK_Up; ksym = XK_Up;
break; break;
case XK_u: case XK_u: /* delete left */
memmove(text, text + cur, sizeof text - cur + 1); insert(NULL, -cursor);
cur = 0;
match();
break; break;
case XK_w: case XK_w: /* delete word */
if(cur == 0) if(cursor == 0)
return; return;
i = cur; n = 0;
while(i-- > 0 && text[i] == ' '); while(cursor - n++ > 0 && text[cursor - n] == ' ');
while(i-- > 0 && text[i] != ' '); while(cursor - n++ > 0 && text[cursor - n] != ' ');
memmove(text + i + 1, text + cur, sizeof text - cur + 1); insert(NULL, -(--n));
cur = i + 1;
match();
break; break;
case XK_y: case XK_y: /* paste selection */
{ XConvertSelection(dc.dpy, XA_PRIMARY, utf8, None, win, CurrentTime);
FILE *fp; /* causes SelectionNotify event */
char *s;
if(!(fp = fopen("sselp", "r")))
eprint("cannot popen sselp\n");
s = fgets(buf, sizeof buf, fp);
fclose(fp);
if(!s)
return; return;
} }
num = strlen(buf);
if(num && buf[num-1] == '\n')
buf[--num] = '\0';
break;
}
} }
switch(ksym) { switch(ksym) {
default: default:
num = MIN(num, sizeof text); if(!iscntrl((int)*buf))
if(num && !iscntrl((int) buf[0])) { insert(buf, MIN(strlen(buf), sizeof text - cursor));
memmove(text + cur + num, text + cur, sizeof text - cur - num);
memcpy(text + cur, buf, num);
cur += num;
match();
}
break; break;
case XK_BackSpace: case XK_BackSpace:
if(cur == 0) if(cursor == 0)
return; return;
for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[cur - i]); i++); for(n = 1; cursor - n > 0 && !UTF8_CODEPOINT(text[cursor - n]); n++);
memmove(text + cur - i, text + cur, sizeof text - cur + i); insert(NULL, -n);
cur -= i;
match();
break; break;
case XK_Delete: case XK_Delete:
if(cur == len) if(cursor == len)
return; return;
for(i = 1; cur + i < len && !IS_UTF8_1ST_CHAR(text[cur + i]); i++); for(n = 1; cursor + n < len && !UTF8_CODEPOINT(text[cursor + n]); n++);
memmove(text + cur, text + cur + i, sizeof text - cur); cursor += n;
match(); insert(NULL, -n);
break; break;
case XK_End: case XK_End:
if(cur < len) { if(cursor < len) {
cur = len; cursor = len;
break; break;
} }
while(next) { while(next) {
@ -358,19 +311,19 @@ keypress(XKeyEvent *e) {
case XK_Escape: case XK_Escape:
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
case XK_Home: case XK_Home:
if(sel == item) { if(sel == matches) {
cur = 0; cursor = 0;
break; break;
} }
sel = curr = item; sel = curr = matches;
calcoffsets(); calcoffsets();
break; break;
case XK_Left: case XK_Left:
if(cur > 0 && (!sel || !sel->left || lines > 0)) { if(cursor > 0 && (!sel || !sel->left || lines > 0)) {
while(cur-- > 0 && !IS_UTF8_1ST_CHAR(text[cur])); while(cursor-- > 0 && !UTF8_CODEPOINT(text[cursor]));
break; break;
} }
if(lines > 0) else if(lines > 0)
return; return;
case XK_Up: case XK_Up:
if(!sel || !sel->left) if(!sel || !sel->left)
@ -394,15 +347,16 @@ keypress(XKeyEvent *e) {
calcoffsets(); calcoffsets();
break; break;
case XK_Return: case XK_Return:
fprintf(stdout, "%s", ((e->state & ShiftMask) || sel) ? sel->text : text); case XK_KP_Enter:
fputs(((e->state & ShiftMask) || sel) ? sel->text : text, stdout);
fflush(stdout); fflush(stdout);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case XK_Right: case XK_Right:
if(cur < len) { if(cursor < len) {
while(cur++ < len && !IS_UTF8_1ST_CHAR(text[cur])); while(cursor++ < len && !UTF8_CODEPOINT(text[cursor]));
break; break;
} }
if(lines > 0) else if(lines > 0)
return; return;
case XK_Down: case XK_Down:
if(!sel || !sel->right) if(!sel || !sel->right)
@ -417,7 +371,7 @@ keypress(XKeyEvent *e) {
if(!sel) if(!sel)
return; return;
strncpy(text, sel->text, sizeof text); strncpy(text, sel->text, sizeof text);
cur = strlen(text); cursor = strlen(text);
match(); match();
break; break;
} }
@ -427,19 +381,19 @@ keypress(XKeyEvent *e) {
void void
match(void) { match(void) {
unsigned int len; unsigned int len;
Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; Item *item, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
len = strlen(text); len = strlen(text);
item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
for(i = allitems; i; i = i->next) for(item = allitems; item; item = item->next)
if(!fstrncmp(text, i->text, len + 1)) if(!fstrncmp(text, item->text, len + 1))
appenditem(i, &lexact, &exactend); appenditem(item, &lexact, &exactend);
else if(!fstrncmp(text, i->text, len)) else if(!fstrncmp(text, item->text, len))
appenditem(i, &lprefix, &prefixend); appenditem(item, &lprefix, &prefixend);
else if(fstrstr(i->text, text)) else if(fstrstr(item->text, text))
appenditem(i, &lsubstr, &substrend); appenditem(item, &lsubstr, &substrend);
if(lexact) { if(lexact) {
item = lexact; matches = lexact;
itemend = exactend; itemend = exactend;
} }
if(lprefix) { if(lprefix) {
@ -448,7 +402,7 @@ match(void) {
lprefix->left = itemend; lprefix->left = itemend;
} }
else else
item = lprefix; matches = lprefix;
itemend = prefixend; itemend = prefixend;
} }
if(lsubstr) { if(lsubstr) {
@ -457,36 +411,48 @@ match(void) {
lsubstr->left = itemend; lsubstr->left = itemend;
} }
else else
item = lsubstr; matches = lsubstr;
} }
curr = prev = next = sel = item; curr = prev = next = sel = matches;
calcoffsets(); calcoffsets();
} }
void void
readstdin(void) { paste(Atom atom)
char *p, buf[sizeof text]; {
unsigned int len = 0, max = 0; char *p, *q;
Item *i, *new; int di;
unsigned long dl;
Atom da;
i = NULL; XGetWindowProperty(dc.dpy, win, atom, 0, sizeof text - cursor, True,
while(fgets(buf, sizeof buf, stdin)) { utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p));
XFree(p);
drawmenu();
}
void
readstdin(void) {
char buf[sizeof text];
size_t len;
Item *item, *new;
allitems = NULL;
for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) {
len = strlen(buf); len = strlen(buf);
if(buf[len-1] == '\n') if(buf[len-1] == '\n')
buf[--len] = '\0'; buf[--len] = '\0';
if(!(p = strdup(buf)))
eprint("cannot strdup %u bytes\n", len);
if((max = MAX(max, len)) == len)
maxname = p;
if(!(new = malloc(sizeof *new))) if(!(new = malloc(sizeof *new)))
eprint("cannot malloc %u bytes\n", sizeof *new); eprintf("cannot malloc %u bytes\n", sizeof *new);
if(!(new->text = strdup(buf)))
eprintf("cannot strdup %u bytes\n", len);
inputw = MAX(inputw, textw(&dc, new->text));
new->next = new->left = new->right = NULL; new->next = new->left = new->right = NULL;
new->text = p; if(item)
if(!i) item->next = new;
allitems = new;
else else
i->next = new; allitems = new;
i = new;
} }
} }
@ -494,8 +460,7 @@ void
run(void) { run(void) {
XEvent ev; XEvent ev;
XSync(dpy, False); while(!XNextEvent(dc.dpy, &ev))
while(!XNextEvent(dpy, &ev))
switch(ev.type) { switch(ev.type) {
case Expose: case Expose:
if(ev.xexpose.count == 0) if(ev.xexpose.count == 0)
@ -504,62 +469,45 @@ run(void) {
case KeyPress: case KeyPress:
keypress(&ev.xkey); keypress(&ev.xkey);
break; break;
case SelectionNotify:
if(ev.xselection.property != None)
paste(ev.xselection.property);
break;
case VisibilityNotify: case VisibilityNotify:
if(ev.xvisibility.state != VisibilityUnobscured) if(ev.xvisibility.state != VisibilityUnobscured)
XRaiseWindow(dpy, win); XRaiseWindow(dc.dpy, win);
break; break;
} }
exit(EXIT_FAILURE);
} }
void void
setup(void) { setup(void) {
int i, j, x, y; int x, y;
#if XINERAMA #ifdef XINERAMA
int n; int i, n;
XineramaScreenInfo *info = NULL; XineramaScreenInfo *info;
#endif #endif
XModifierKeymap *modmap;
XSetWindowAttributes wa; XSetWindowAttributes wa;
/* init modifier map */
modmap = XGetModifierMapping(dpy);
for(i = 0; i < 8; i++)
for(j = 0; j < modmap->max_keypermod; j++) {
if(modmap->modifiermap[i * modmap->max_keypermod + j]
== XKeysymToKeycode(dpy, XK_Num_Lock))
numlockmask = (1 << i);
}
XFreeModifiermap(modmap);
dc.dpy = dpy;
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);
initfont(&dc, font);
/* input window */
wa.override_redirect = True;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
/* input window geometry */ /* input window geometry */
mh = (dc.font.height + 2) * (lines + 1); mh = (dc.font.height + 2) * (lines + 1);
#if XINERAMA #ifdef XINERAMA
if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { if((info = XineramaQueryScreens(dc.dpy, &n))) {
i = 0;
if(n > 1) {
int di; int di;
unsigned int dui; unsigned int du;
Window dummy; Window dw;
if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui))
XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
for(i = 0; i < n; i++) for(i = 0; i < n; i++)
if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) if(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 = topbar ? info[i].y_org : info[i].y_org + 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);
} }
@ -567,84 +515,86 @@ setup(void) {
#endif #endif
{ {
x = 0; x = 0;
y = topbar ? 0 : DisplayHeight(dpy, screen) - mh; y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh;
mw = DisplayWidth(dpy, screen); mw = DisplayWidth(dc.dpy, screen);
} }
win = XCreateWindow(dpy, root, x, y, mw, mh, 0, /* input window */
DefaultDepth(dpy, screen), CopyFromParent, wa.override_redirect = True;
DefaultVisual(dpy, screen), wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dc.dpy, root, x, y, mw, mh, 0,
DefaultDepth(dc.dpy, screen), CopyFromParent,
DefaultVisual(dc.dpy, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
match();
grabkeyboard();
setupdraw(&dc, win); setupdraw(&dc, win);
if(prompt) inputw = MIN(inputw, mw / 3);
promptw = MIN(textw(&dc, prompt), mw / 5); utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False);
XMapRaised(dpy, win); XMapRaised(dc.dpy, win);
}
void
usage(void) {
fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n"
" [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
exit(EXIT_FAILURE);
} }
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
unsigned int i; int i;
/* command line args */
progname = "dmenu"; progname = "dmenu";
for(i = 1; i < argc; i++) for(i = 1; i < argc; i++)
if(!strcmp(argv[i], "-i")) { /* 1-arg flags */
fstrncmp = strncasecmp; if(!strcmp(argv[i], "-v")) {
fstrstr = cistrstr; fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout);
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")) {
fstrncmp = strncasecmp;
fstrstr = cistrstr;
}
else if(i == argc-1)
usage();
/* 2-arg flags */
else if(!strcmp(argv[i], "-l")) { else if(!strcmp(argv[i], "-l")) {
if(++i < argc) lines = atoi(argv[i]); if((lines = atoi(argv[++i])) > 0)
if(lines > 0)
calcoffsets = calcoffsetsv; calcoffsets = calcoffsetsv;
} }
else if(!strcmp(argv[i], "-fn")) {
if(++i < argc) font = argv[i];
}
else if(!strcmp(argv[i], "-nb")) {
if(++i < argc) normbgcolor = argv[i];
}
else if(!strcmp(argv[i], "-nf")) {
if(++i < argc) normfgcolor = argv[i];
}
else if(!strcmp(argv[i], "-p")) { else if(!strcmp(argv[i], "-p")) {
if(++i < argc) prompt = argv[i]; prompt = argv[++i];
} promptw = MIN(textw(&dc, prompt), mw/5);
else if(!strcmp(argv[i], "-sb")) {
if(++i < argc) selbgcolor = argv[i];
}
else if(!strcmp(argv[i], "-sf")) {
if(++i < argc) selfgcolor = argv[i];
}
else if(!strcmp(argv[i], "-v")) {
printf("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n");
exit(EXIT_SUCCESS);
}
else {
fputs("usage: dmenu [-i] [-b] [-l <lines>] [-fn <font>] [-nb <color>]\n"
" [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
exit(EXIT_FAILURE);
} }
else if(!strcmp(argv[i], "-fn"))
font = argv[++i];
else if(!strcmp(argv[i], "-nb"))
normbgcolor = argv[++i];
else if(!strcmp(argv[i], "-nf"))
normfgcolor = argv[++i];
else if(!strcmp(argv[i], "-sb"))
selbgcolor = argv[++i];
else if(!strcmp(argv[i], "-sf"))
selfgcolor = argv[++i];
else
usage();
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fprintf(stderr, "dmenu: warning: no locale support\n"); fputs("dmenu: warning: no locale support\n", stderr);
if(!(dpy = XOpenDisplay(NULL))) if(!(dc.dpy = XOpenDisplay(NULL)))
eprint("cannot open display\n"); eprintf("cannot open display\n");
if(atexit(&cleanup) != 0) screen = DefaultScreen(dc.dpy);
eprint("cannot register cleanup\n"); root = RootWindow(dc.dpy, screen);
screen = DefaultScreen(dpy); initfont(&dc, font);
root = RootWindow(dpy, screen);
if(!(argp = malloc(sizeof *argp * (argc+2))))
eprint("cannot malloc %u bytes\n", sizeof *argp * (argc+2));
memcpy(argp + 2, argv + 1, sizeof *argp * argc);
readstdin(); readstdin();
grabkeyboard();
setup(); setup();
if(maxname)
cmdw = MIN(textw(&dc, maxname), mw / 3);
match();
run(); run();
return 0;
return EXIT_FAILURE; /* should not reach */
} }