updated manpage, added paste, cleaned up, new libdraw

main
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
OBJ = ${SRC:.c=.o}
all: options dinput dmenu
all: options dmenu
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.
You also need libdraw, available from http://hg.suckless.org/libdraw
Pasting the X selection to dmenu requires the sselp utility at runtime.
Installation
------------
Edit config.mk to match your local setup (dmenu is installed into
the /usr/local namespace by default).
Edit config.mk to match your local setup (dmenu is installed into the
/usr/local namespace by default).
Afterwards enter the following command to build and install dmenu (if
necessary as root):

102
dmenu.1
View File

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

490
dmenu.c
View File

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