|
@ -21,25 +21,6 @@ |
|
|
#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)) |
|
|
#define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) |
|
|
|
|
|
|
|
|
/* enums */ |
|
|
|
|
|
enum { ColFG, ColBG, ColLast }; |
|
|
|
|
|
|
|
|
|
|
|
/* typedefs */ |
|
|
|
|
|
typedef struct { |
|
|
|
|
|
int x, y, w, h; |
|
|
|
|
|
unsigned long norm[ColLast]; |
|
|
|
|
|
unsigned long sel[ColLast]; |
|
|
|
|
|
Drawable drawable; |
|
|
|
|
|
GC gc; |
|
|
|
|
|
struct { |
|
|
|
|
|
XFontStruct *xfont; |
|
|
|
|
|
XFontSet set; |
|
|
|
|
|
int ascent; |
|
|
|
|
|
int descent; |
|
|
|
|
|
int height; |
|
|
|
|
|
} font; |
|
|
|
|
|
} DC; /* draw context */ |
|
|
|
|
|
|
|
|
|
|
|
typedef struct Item Item; |
|
|
typedef struct Item Item; |
|
|
struct Item { |
|
|
struct Item { |
|
|
char *text; |
|
|
char *text; |
|
@ -53,22 +34,16 @@ 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 cleanup(void); |
|
|
static void drawcursor(void); |
|
|
|
|
|
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 drawtext(const char *text, unsigned long col[ColLast]); |
|
|
|
|
|
static void eprint(const char *errstr, ...); |
|
|
static void eprint(const char *errstr, ...); |
|
|
static unsigned long getcolor(const char *colstr); |
|
|
|
|
|
static Bool grabkeyboard(void); |
|
|
static Bool grabkeyboard(void); |
|
|
static void initfont(const char *fontstr); |
|
|
|
|
|
static void kpress(XKeyEvent * e); |
|
|
static void kpress(XKeyEvent * e); |
|
|
static void match(char *pattern); |
|
|
static void match(char *pattern); |
|
|
static void readstdin(void); |
|
|
static void readstdin(void); |
|
|
static void run(void); |
|
|
static void run(void); |
|
|
static void setup(Bool topbar); |
|
|
static void setup(Bool topbar); |
|
|
static int textnw(const char *text, unsigned int len); |
|
|
|
|
|
static int textw(const char *text); |
|
|
|
|
|
|
|
|
|
|
|
#include "config.h" |
|
|
#include "config.h" |
|
|
|
|
|
|
|
@ -81,11 +56,9 @@ static int promptw = 0; |
|
|
static int ret = 0; |
|
|
static int ret = 0; |
|
|
static int screen; |
|
|
static int screen; |
|
|
static unsigned int mw, mh; |
|
|
static unsigned int mw, mh; |
|
|
static unsigned int cursor = 0; |
|
|
|
|
|
static unsigned int numlockmask = 0; |
|
|
static unsigned int numlockmask = 0; |
|
|
static Bool running = True; |
|
|
static Bool running = True; |
|
|
static Display *dpy; |
|
|
static Display *dpy; |
|
|
static DC dc; |
|
|
|
|
|
static Item *allitems = NULL; /* first of all items */ |
|
|
static Item *allitems = NULL; /* first of all items */ |
|
|
static Item *item = NULL; /* first of pattern matching items */ |
|
|
static Item *item = NULL; /* first of pattern matching items */ |
|
|
static Item *sel = NULL; |
|
|
static Item *sel = NULL; |
|
@ -98,6 +71,8 @@ static char *(*fstrstr)(const char *, const char *) = strstr; |
|
|
static unsigned int lines = 0; |
|
|
static unsigned int lines = 0; |
|
|
static void (*calcoffsets)(void) = calcoffsetsh; |
|
|
static void (*calcoffsets)(void) = calcoffsetsh; |
|
|
|
|
|
|
|
|
|
|
|
#include "draw.c" |
|
|
|
|
|
|
|
|
void |
|
|
void |
|
|
appenditem(Item *i, Item **list, Item **last) { |
|
|
appenditem(Item *i, Item **list, Item **last) { |
|
|
if(!(*last)) |
|
|
if(!(*last)) |
|
@ -161,26 +136,11 @@ cistrstr(const char *s, const char *sub) { |
|
|
|
|
|
|
|
|
void |
|
|
void |
|
|
cleanup(void) { |
|
|
cleanup(void) { |
|
|
if(dc.font.set) |
|
|
|
|
|
XFreeFontSet(dpy, dc.font.set); |
|
|
|
|
|
else |
|
|
|
|
|
XFreeFont(dpy, dc.font.xfont); |
|
|
|
|
|
XFreePixmap(dpy, dc.drawable); |
|
|
|
|
|
XFreeGC(dpy, dc.gc); |
|
|
|
|
|
|
|
|
dccleanup(); |
|
|
XDestroyWindow(dpy, win); |
|
|
XDestroyWindow(dpy, win); |
|
|
XUngrabKeyboard(dpy, CurrentTime); |
|
|
XUngrabKeyboard(dpy, CurrentTime); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void |
|
|
|
|
|
drawcursor(void) { |
|
|
|
|
|
XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 }; |
|
|
|
|
|
|
|
|
|
|
|
r.x += textnw(text, cursor) + dc.font.height / 2; |
|
|
|
|
|
|
|
|
|
|
|
XSetForeground(dpy, dc.gc, dc.norm[ColFG]); |
|
|
|
|
|
XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
void |
|
|
drawmenu(void) { |
|
|
drawmenu(void) { |
|
|
dc.x = 0; |
|
|
dc.x = 0; |
|
@ -199,7 +159,6 @@ drawmenu(void) { |
|
|
if(cmdw && item && lines == 0) |
|
|
if(cmdw && item && lines == 0) |
|
|
dc.w = cmdw; |
|
|
dc.w = cmdw; |
|
|
drawtext(*text ? text : NULL, dc.norm); |
|
|
drawtext(*text ? text : NULL, dc.norm); |
|
|
drawcursor(); |
|
|
|
|
|
if(curr) { |
|
|
if(curr) { |
|
|
if(lines > 0) |
|
|
if(lines > 0) |
|
|
drawmenuv(); |
|
|
drawmenuv(); |
|
@ -243,34 +202,6 @@ drawmenuv(void) { |
|
|
drawtext(NULL, dc.norm); |
|
|
drawtext(NULL, dc.norm); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void |
|
|
|
|
|
drawtext(const char *text, unsigned long col[ColLast]) { |
|
|
|
|
|
char buf[256]; |
|
|
|
|
|
int i, x, y, h, len, olen; |
|
|
|
|
|
XRectangle r = { dc.x, dc.y, dc.w, dc.h }; |
|
|
|
|
|
|
|
|
|
|
|
XSetForeground(dpy, dc.gc, col[ColBG]); |
|
|
|
|
|
XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
|
|
|
|
|
if(!text) |
|
|
|
|
|
return; |
|
|
|
|
|
olen = strlen(text); |
|
|
|
|
|
h = dc.font.height; |
|
|
|
|
|
y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent; |
|
|
|
|
|
x = dc.x + (h / 2); |
|
|
|
|
|
/* shorten text if necessary */ |
|
|
|
|
|
for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); |
|
|
|
|
|
if(!len) |
|
|
|
|
|
return; |
|
|
|
|
|
memcpy(buf, text, len); |
|
|
|
|
|
if(len < olen) |
|
|
|
|
|
for(i = len; i && i > len - 3; buf[--i] = '.'); |
|
|
|
|
|
XSetForeground(dpy, dc.gc, col[ColFG]); |
|
|
|
|
|
if(dc.font.set) |
|
|
|
|
|
XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); |
|
|
|
|
|
else |
|
|
|
|
|
XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
void |
|
|
eprint(const char *errstr, ...) { |
|
|
eprint(const char *errstr, ...) { |
|
|
va_list ap; |
|
|
va_list ap; |
|
@ -281,16 +212,6 @@ eprint(const char *errstr, ...) { |
|
|
exit(EXIT_FAILURE); |
|
|
exit(EXIT_FAILURE); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
unsigned long |
|
|
|
|
|
getcolor(const char *colstr) { |
|
|
|
|
|
Colormap cmap = DefaultColormap(dpy, screen); |
|
|
|
|
|
XColor color; |
|
|
|
|
|
|
|
|
|
|
|
if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) |
|
|
|
|
|
eprint("dmenu: cannot allocate color '%s'\n", colstr); |
|
|
|
|
|
return color.pixel; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Bool |
|
|
Bool |
|
|
grabkeyboard(void) { |
|
|
grabkeyboard(void) { |
|
|
unsigned int len; |
|
|
unsigned int len; |
|
@ -304,37 +225,6 @@ grabkeyboard(void) { |
|
|
return len > 0; |
|
|
return len > 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void |
|
|
|
|
|
initfont(const char *fontstr) { |
|
|
|
|
|
char *def, **missing = NULL; |
|
|
|
|
|
int i, n; |
|
|
|
|
|
|
|
|
|
|
|
if(!fontstr || fontstr[0] == '\0') |
|
|
|
|
|
eprint("dmenu: cannot load font: '%s'\n", fontstr); |
|
|
|
|
|
dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); |
|
|
|
|
|
if(missing) |
|
|
|
|
|
XFreeStringList(missing); |
|
|
|
|
|
if(dc.font.set) { |
|
|
|
|
|
XFontStruct **xfonts; |
|
|
|
|
|
char **font_names; |
|
|
|
|
|
dc.font.ascent = dc.font.descent = 0; |
|
|
|
|
|
n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); |
|
|
|
|
|
for(i = 0; i < n; i++) { |
|
|
|
|
|
dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent); |
|
|
|
|
|
dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent); |
|
|
|
|
|
xfonts++; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) |
|
|
|
|
|
&& !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) |
|
|
|
|
|
eprint("dmenu: cannot load font: '%s'\n", fontstr); |
|
|
|
|
|
dc.font.ascent = dc.font.xfont->ascent; |
|
|
|
|
|
dc.font.descent = dc.font.xfont->descent; |
|
|
|
|
|
} |
|
|
|
|
|
dc.font.height = dc.font.ascent + dc.font.descent; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
void |
|
|
kpress(XKeyEvent * e) { |
|
|
kpress(XKeyEvent * e) { |
|
|
char buf[sizeof text]; |
|
|
char buf[sizeof text]; |
|
@ -381,9 +271,6 @@ kpress(XKeyEvent * e) { |
|
|
case XK_j: |
|
|
case XK_j: |
|
|
ksym = XK_Return; |
|
|
ksym = XK_Return; |
|
|
break; |
|
|
break; |
|
|
case XK_k: |
|
|
|
|
|
text[cursor] = '\0'; |
|
|
|
|
|
break; |
|
|
|
|
|
case XK_n: |
|
|
case XK_n: |
|
|
ksym = XK_Down; |
|
|
ksym = XK_Down; |
|
|
break; |
|
|
break; |
|
@ -391,67 +278,42 @@ kpress(XKeyEvent * e) { |
|
|
ksym = XK_Up; |
|
|
ksym = XK_Up; |
|
|
break; |
|
|
break; |
|
|
case XK_u: |
|
|
case XK_u: |
|
|
memmove(text, text + cursor, sizeof text - cursor + 1); |
|
|
|
|
|
cursor = 0; |
|
|
|
|
|
|
|
|
text[0] = '\0'; |
|
|
match(text); |
|
|
match(text); |
|
|
break; |
|
|
break; |
|
|
case XK_w: |
|
|
case XK_w: |
|
|
if(cursor > 0) { |
|
|
|
|
|
i = cursor; |
|
|
|
|
|
while(i-- > 0 && text[i] == ' '); |
|
|
|
|
|
while(i-- > 0 && text[i] != ' '); |
|
|
|
|
|
memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); |
|
|
|
|
|
cursor = i + 1; |
|
|
|
|
|
match(text); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if(len == 0) |
|
|
|
|
|
return; |
|
|
|
|
|
i = len; |
|
|
|
|
|
while(i-- > 0 && text[i] == ' '); |
|
|
|
|
|
while(i-- > 0 && text[i] != ' '); |
|
|
|
|
|
text[++i] = '\0'; |
|
|
|
|
|
match(text); |
|
|
break; |
|
|
break; |
|
|
case XK_y: |
|
|
|
|
|
{ |
|
|
|
|
|
FILE *fp; |
|
|
|
|
|
char *s; |
|
|
|
|
|
if(!(fp = popen("sselp", "r"))) |
|
|
|
|
|
eprint("dmenu: cannot popen sselp\n"); |
|
|
|
|
|
s = fgets(buf, sizeof buf, fp); |
|
|
|
|
|
pclose(fp); |
|
|
|
|
|
if(s == NULL) |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
num = strlen(buf); |
|
|
|
|
|
if(num && buf[num-1] == '\n') |
|
|
|
|
|
buf[--num] = '\0'; |
|
|
|
|
|
|
|
|
case XK_x: |
|
|
|
|
|
execlp("dinput", "dinput", text, NULL); /* todo: argv */ |
|
|
|
|
|
eprint("dmenu: cannot exec dinput:"); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
switch(ksym) { |
|
|
switch(ksym) { |
|
|
default: |
|
|
default: |
|
|
num = MIN(num, sizeof text - cursor); |
|
|
|
|
|
|
|
|
num = MIN(num, sizeof text); |
|
|
if(num && !iscntrl((int) buf[0])) { |
|
|
if(num && !iscntrl((int) buf[0])) { |
|
|
memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); |
|
|
|
|
|
memcpy(text + cursor, buf, num); |
|
|
|
|
|
cursor += num; |
|
|
|
|
|
|
|
|
memcpy(text + len, buf, num + 1); |
|
|
|
|
|
len += num; |
|
|
match(text); |
|
|
match(text); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
case XK_BackSpace: |
|
|
case XK_BackSpace: |
|
|
if(cursor == 0) |
|
|
|
|
|
|
|
|
if(len == 0) |
|
|
return; |
|
|
return; |
|
|
for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); |
|
|
|
|
|
memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); |
|
|
|
|
|
cursor -= i; |
|
|
|
|
|
match(text); |
|
|
|
|
|
break; |
|
|
|
|
|
case XK_Delete: |
|
|
|
|
|
if(cursor == len) |
|
|
|
|
|
return; |
|
|
|
|
|
for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++); |
|
|
|
|
|
memmove(text + cursor, text + cursor + i, sizeof text - cursor); |
|
|
|
|
|
|
|
|
for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++); |
|
|
|
|
|
len -= i; |
|
|
|
|
|
text[len] = '\0'; |
|
|
match(text); |
|
|
match(text); |
|
|
break; |
|
|
break; |
|
|
case XK_End: |
|
|
case XK_End: |
|
|
if(cursor < len) { |
|
|
|
|
|
cursor = len; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
while(next) { |
|
|
while(next) { |
|
|
sel = curr = next; |
|
|
sel = curr = next; |
|
|
calcoffsets(); |
|
|
calcoffsets(); |
|
@ -464,20 +326,10 @@ kpress(XKeyEvent * e) { |
|
|
running = False; |
|
|
running = False; |
|
|
return; |
|
|
return; |
|
|
case XK_Home: |
|
|
case XK_Home: |
|
|
if(sel == item) { |
|
|
|
|
|
cursor = 0; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
sel = curr = item; |
|
|
sel = curr = item; |
|
|
calcoffsets(); |
|
|
calcoffsets(); |
|
|
break; |
|
|
break; |
|
|
case XK_Left: |
|
|
case XK_Left: |
|
|
if(cursor > 0 && (!sel || !sel->left || lines > 0)) { |
|
|
|
|
|
while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
if(lines > 0) |
|
|
|
|
|
return; |
|
|
|
|
|
case XK_Up: |
|
|
case XK_Up: |
|
|
if(!sel || !sel->left) |
|
|
if(!sel || !sel->left) |
|
|
return; |
|
|
return; |
|
@ -508,12 +360,6 @@ kpress(XKeyEvent * e) { |
|
|
running = False; |
|
|
running = False; |
|
|
return; |
|
|
return; |
|
|
case XK_Right: |
|
|
case XK_Right: |
|
|
if(cursor < len) { |
|
|
|
|
|
while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
if(lines > 0) |
|
|
|
|
|
return; |
|
|
|
|
|
case XK_Down: |
|
|
case XK_Down: |
|
|
if(!sel || !sel->right) |
|
|
if(!sel || !sel->right) |
|
|
return; |
|
|
return; |
|
@ -527,7 +373,6 @@ kpress(XKeyEvent * e) { |
|
|
if(!sel) |
|
|
if(!sel) |
|
|
return; |
|
|
return; |
|
|
strncpy(text, sel->text, sizeof text); |
|
|
strncpy(text, sel->text, sizeof text); |
|
|
cursor = strlen(text); |
|
|
|
|
|
match(text); |
|
|
match(text); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
@ -690,11 +535,7 @@ setup(Bool topbar) { |
|
|
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
|
|
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
|
|
|
|
|
|
|
|
/* pixmap */ |
|
|
/* pixmap */ |
|
|
dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); |
|
|
|
|
|
dc.gc = XCreateGC(dpy, parent, 0, NULL); |
|
|
|
|
|
XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); |
|
|
|
|
|
if(!dc.font.set) |
|
|
|
|
|
XSetFont(dpy, dc.gc, dc.font.xfont->fid); |
|
|
|
|
|
|
|
|
dcsetup(); |
|
|
if(maxname) |
|
|
if(maxname) |
|
|
cmdw = MIN(textw(maxname), mw / 3); |
|
|
cmdw = MIN(textw(maxname), mw / 3); |
|
|
if(prompt) |
|
|
if(prompt) |
|
@ -704,22 +545,6 @@ setup(Bool topbar) { |
|
|
XMapRaised(dpy, win); |
|
|
XMapRaised(dpy, win); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int |
|
|
|
|
|
textnw(const char *text, unsigned int len) { |
|
|
|
|
|
XRectangle r; |
|
|
|
|
|
|
|
|
|
|
|
if(dc.font.set) { |
|
|
|
|
|
XmbTextExtents(dc.font.set, text, len, NULL, &r); |
|
|
|
|
|
return r.width; |
|
|
|
|
|
} |
|
|
|
|
|
return XTextWidth(dc.font.xfont, text, len); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
|
textw(const char *text) { |
|
|
|
|
|
return textnw(text, strlen(text)) + dc.font.height; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
int |
|
|
main(int argc, char *argv[]) { |
|
|
main(int argc, char *argv[]) { |
|
|
unsigned int i; |
|
|
unsigned int i; |
|
|