Compare commits
26 Commits
807e428b98
...
7489a6da57
Author | SHA1 | Date |
---|---|---|
hazen2215 | 7489a6da57 | |
hazen2215 | d3f7be8978 | |
hazen2215 | 35d070b093 | |
hazen2215 | ca390636ed | |
hazen2215 | f48b9a3781 | |
hazen2215 | 51628af2bc | |
hazen2215 | 1089b2f4de | |
NRK | 7ab0cb5ef0 | |
Lucas de Sena | 0fe460dbd4 | |
Hiltjo Posthuma | dfbbf7f6e1 | |
Hiltjo Posthuma | ba1a347dca | |
Hiltjo Posthuma | bcbc1ef5c4 | |
NRK | 689d9bfcf6 | |
Hiltjo Posthuma | e42c036634 | |
Hiltjo Posthuma | 1d2b462acf | |
Tom Schwindl | 7ec32fe494 | |
Hiltjo Posthuma | fce06f437d | |
Hiltjo Posthuma | 1e8c5b68f4 | |
NRK | 528d39b011 | |
NRK | 32db2b1251 | |
Hiltjo Posthuma | e35976f4a5 | |
Hiltjo Posthuma | 28fb3e2812 | |
Hiltjo Posthuma | fe5d5c6709 | |
Hiltjo Posthuma | e1e1de7b3b | |
NRK | 33685b06e9 | |
NRK | e4827b0c40 |
10
Makefile
10
Makefile
|
@ -10,12 +10,12 @@ all: options dmenu stest
|
|||
|
||||
options:
|
||||
@echo dmenu build options:
|
||||
@echo "CFLAGS = $(CFLAGS)"
|
||||
@echo "LDFLAGS = $(LDFLAGS)"
|
||||
@echo "CFLAGS = $(XCFLAGS)"
|
||||
@echo "LDFLAGS = $(XLDFLAGS)"
|
||||
@echo "CC = $(CC)"
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
$(CC) -c $(XCFLAGS) $<
|
||||
|
||||
config.h:
|
||||
cp config.def.h $@
|
||||
|
@ -23,10 +23,10 @@ config.h:
|
|||
$(OBJ): arg.h config.h config.mk drw.h
|
||||
|
||||
dmenu: dmenu.o drw.o util.o
|
||||
$(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS)
|
||||
$(CC) -o $@ dmenu.o drw.o util.o $(XLDFLAGS)
|
||||
|
||||
stest: stest.o
|
||||
$(CC) -o $@ stest.o $(LDFLAGS)
|
||||
$(CC) -o $@ stest.o $(XLDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz
|
||||
|
|
|
@ -21,3 +21,6 @@ static unsigned int lines = 0;
|
|||
* for example: " /?\"&[]"
|
||||
*/
|
||||
static const char worddelimiters[] = " ";
|
||||
|
||||
/* -n option; preselected item starting from 0 */
|
||||
static unsigned int preselected = 0;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* See LICENSE file for copyright and license details. */
|
||||
/* Default settings; can be overriden by command line. */
|
||||
|
||||
static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
|
||||
/* -fn option overrides fonts[0]; default X11 font or font set */
|
||||
static const char *fonts[] = {
|
||||
"monospace:size=10", "IPAGothic:size=10", "Symbola:size=10"
|
||||
};
|
||||
static const char *prompt = NULL; /* -p option; prompt to the left of input field */
|
||||
static const char *colors[SchemeLast][2] = {
|
||||
/* fg bg */
|
||||
[SchemeNorm] = { "#bbbbbb", "#000000" },
|
||||
[SchemeSel] = { "#eeeeee", "#005577" },
|
||||
[SchemeOut] = { "#000000", "#00ffff" },
|
||||
};
|
||||
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
|
||||
static unsigned int lines = 0;
|
||||
|
||||
/*
|
||||
* Characters not considered part of a word while deleting words
|
||||
* for example: " /?\"&[]"
|
||||
*/
|
||||
static const char worddelimiters[] = " /?\"&[].:";
|
||||
|
||||
/* -n option; preselected item starting from 0 */
|
||||
static unsigned int preselected = 0;
|
|
@ -1,5 +1,5 @@
|
|||
# dmenu version
|
||||
VERSION = 5.1
|
||||
VERSION = 5.2
|
||||
|
||||
# paths
|
||||
PREFIX = /usr/local
|
||||
|
@ -17,15 +17,16 @@ FREETYPELIBS = -lfontconfig -lXft
|
|||
FREETYPEINC = /usr/include/freetype2
|
||||
# OpenBSD (uncomment)
|
||||
#FREETYPEINC = $(X11INC)/freetype2
|
||||
#MANPREFIX = ${PREFIX}/man
|
||||
|
||||
# includes and libs
|
||||
INCS = -I$(X11INC) -I$(FREETYPEINC)
|
||||
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
|
||||
|
||||
# flags
|
||||
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
|
||||
CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS)
|
||||
LDFLAGS = $(LIBS)
|
||||
XCPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
|
||||
XCFLAGS = -std=c99 -pedantic -Wall $(INCS) $(XCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
|
||||
XLDFLAGS = $(LIBS) $(LDFLAGS)
|
||||
|
||||
# compiler and linker
|
||||
CC = cc
|
||||
|
|
5
dmenu.1
5
dmenu.1
|
@ -22,6 +22,8 @@ dmenu \- dynamic menu
|
|||
.IR color ]
|
||||
.RB [ \-w
|
||||
.IR windowid ]
|
||||
.RB [ \-n
|
||||
.IR number ]
|
||||
.P
|
||||
.BR dmenu_run " ..."
|
||||
.SH DESCRIPTION
|
||||
|
@ -80,6 +82,9 @@ prints version information to stdout, then exits.
|
|||
.TP
|
||||
.BI \-w " windowid"
|
||||
embed into windowid.
|
||||
.TP
|
||||
.BI \-n " number"
|
||||
preseslected item starting from 0.
|
||||
.SH USAGE
|
||||
dmenu is completely controlled by the keyboard. Items are selected using the
|
||||
arrow keys, page up, page down, home, and end.
|
||||
|
|
97
dmenu.c
97
dmenu.c
|
@ -22,7 +22,6 @@
|
|||
/* macros */
|
||||
#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
|
||||
* MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
|
||||
#define LENGTH(X) (sizeof X / sizeof X[0])
|
||||
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
|
||||
|
||||
/* enums */
|
||||
|
@ -324,19 +323,19 @@ movewordedge(int dir)
|
|||
static void
|
||||
keypress(XKeyEvent *ev)
|
||||
{
|
||||
char buf[32];
|
||||
char buf[64];
|
||||
int len;
|
||||
KeySym ksym;
|
||||
KeySym ksym = NoSymbol;
|
||||
Status status;
|
||||
|
||||
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
|
||||
switch (status) {
|
||||
default: /* XLookupNone, XBufferOverflow */
|
||||
return;
|
||||
case XLookupChars:
|
||||
case XLookupChars: /* composed string from input method */
|
||||
goto insert;
|
||||
case XLookupKeySym:
|
||||
case XLookupBoth:
|
||||
case XLookupBoth: /* a KeySym and a string are returned: use keysym */
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -358,13 +357,12 @@ keypress(XKeyEvent *ev)
|
|||
case XK_n: ksym = XK_Down; break;
|
||||
case XK_p: ksym = XK_Up; break;
|
||||
|
||||
case XK_u: /* delete left */
|
||||
insert(NULL, 0 - cursor);
|
||||
case XK_k: /* delete right */
|
||||
text[cursor] = '\0';
|
||||
match();
|
||||
break;
|
||||
case XK_u: /* delete left */
|
||||
insert(NULL, 0 - cursor);
|
||||
break;
|
||||
case XK_w: /* delete word */
|
||||
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
|
||||
insert(NULL, nextrune(-1) - cursor);
|
||||
|
@ -517,9 +515,9 @@ insert:
|
|||
case XK_Tab:
|
||||
if (!sel)
|
||||
return;
|
||||
strncpy(text, sel->text, sizeof text - 1);
|
||||
text[sizeof text - 1] = '\0';
|
||||
cursor = strlen(text);
|
||||
cursor = strnlen(sel->text, sizeof text - 1);
|
||||
memcpy(text, sel->text, cursor);
|
||||
text[cursor] = '\0';
|
||||
match();
|
||||
break;
|
||||
}
|
||||
|
@ -549,20 +547,25 @@ paste(void)
|
|||
static void
|
||||
readstdin(void)
|
||||
{
|
||||
char buf[sizeof text], *p;
|
||||
size_t i, size = 0;
|
||||
char *line = NULL;
|
||||
size_t i, itemsiz = 0, linesiz = 0;
|
||||
ssize_t len;
|
||||
|
||||
/* read each line from stdin and add it to the item list */
|
||||
for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
|
||||
if (i + 1 >= size / sizeof *items)
|
||||
if (!(items = realloc(items, (size += BUFSIZ))))
|
||||
die("cannot realloc %zu bytes:", size);
|
||||
if ((p = strchr(buf, '\n')))
|
||||
*p = '\0';
|
||||
if (!(items[i].text = strdup(buf)))
|
||||
die("cannot strdup %zu bytes:", strlen(buf) + 1);
|
||||
for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
|
||||
if (i + 1 >= itemsiz) {
|
||||
itemsiz += 256;
|
||||
if (!(items = realloc(items, itemsiz * sizeof(*items))))
|
||||
die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
|
||||
}
|
||||
if (line[len - 1] == '\n')
|
||||
line[len - 1] = '\0';
|
||||
if (!(items[i].text = strdup(line)))
|
||||
die("strdup:");
|
||||
|
||||
items[i].out = 0;
|
||||
}
|
||||
free(line);
|
||||
if (items)
|
||||
items[i].text = NULL;
|
||||
lines = MIN(lines, i);
|
||||
|
@ -574,7 +577,7 @@ run(void)
|
|||
XEvent ev;
|
||||
|
||||
while (!XNextEvent(dpy, &ev)) {
|
||||
if (XFilterEvent(&ev, win))
|
||||
if (XFilterEvent(&ev, None))
|
||||
continue;
|
||||
switch(ev.type) {
|
||||
case DestroyNotify:
|
||||
|
@ -610,13 +613,12 @@ static void
|
|||
setup(void)
|
||||
{
|
||||
int x, y, i, j;
|
||||
unsigned int du, tmp;
|
||||
unsigned int du;
|
||||
XSetWindowAttributes swa;
|
||||
XIM xim;
|
||||
Window w, dw, *dws;
|
||||
XWindowAttributes wa;
|
||||
XClassHint ch = {"dmenu", "dmenu"};
|
||||
struct item *item;
|
||||
#ifdef XINERAMA
|
||||
XineramaScreenInfo *info;
|
||||
Window pw;
|
||||
|
@ -674,33 +676,49 @@ setup(void)
|
|||
mw = wa.width;
|
||||
}
|
||||
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
|
||||
for (item = items; item && item->text; ++item) {
|
||||
if ((tmp = textw_clamp(item->text, mw/3)) > inputw) {
|
||||
if ((inputw = tmp) == mw/3)
|
||||
break;
|
||||
inputw = mw / 3; /* input width: ~33% of monitor width */
|
||||
match();
|
||||
for (i = 0; i < preselected; i++) {
|
||||
if (sel && sel->right && (sel = sel->right) == next) {
|
||||
curr = next;
|
||||
calcoffsets();
|
||||
}
|
||||
}
|
||||
match();
|
||||
|
||||
/* create menu window */
|
||||
swa.override_redirect = True;
|
||||
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
|
||||
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
|
||||
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
|
||||
win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
|
||||
CopyFromParent, CopyFromParent, CopyFromParent,
|
||||
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
|
||||
XSetClassHint(dpy, win, &ch);
|
||||
|
||||
|
||||
/* input methods */
|
||||
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
|
||||
die("XOpenIM failed: could not open input device");
|
||||
|
||||
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) {
|
||||
XSetLocaleModifiers("@im=local");
|
||||
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) {
|
||||
XSetLocaleModifiers("@im=");
|
||||
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
|
||||
die("XOpenIM failed: could not open input device");
|
||||
}
|
||||
}
|
||||
xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
|
||||
XNClientWindow, win, XNFocusWindow, win, NULL);
|
||||
XNClientWindow, embed ? win : parentwin, XNFocusWindow, embed ? win : parentwin, NULL);
|
||||
|
||||
XMapRaised(dpy, win);
|
||||
XVaNestedList preedit_attr;
|
||||
XPoint spot;
|
||||
|
||||
spot.x = x;
|
||||
spot.y = topbar ? y + mh: 0;
|
||||
|
||||
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
|
||||
XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
|
||||
XFree(preedit_attr);
|
||||
|
||||
if (embed) {
|
||||
XReparentWindow(dpy, win, parentwin, x, y);
|
||||
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
|
||||
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
|
||||
for (i = 0; i < du && dws[i] != win; ++i)
|
||||
|
@ -716,9 +734,8 @@ setup(void)
|
|||
static void
|
||||
usage(void)
|
||||
{
|
||||
fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
|
||||
" [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr);
|
||||
exit(1);
|
||||
die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
|
||||
" [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -760,11 +777,15 @@ main(int argc, char *argv[])
|
|||
colors[SchemeSel][ColFg] = argv[++i];
|
||||
else if (!strcmp(argv[i], "-w")) /* embedding window id */
|
||||
embed = argv[++i];
|
||||
else if (!strcmp(argv[i], "-n")) /* preselected item */
|
||||
preselected = atoi(argv[++i]);
|
||||
else
|
||||
usage();
|
||||
|
||||
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
|
||||
fputs("warning: no locale support\n", stderr);
|
||||
if (!XSetLocaleModifiers(""))
|
||||
fputs("warning: no locale modifiers support\n", stderr);
|
||||
if (!(dpy = XOpenDisplay(NULL)))
|
||||
die("cannot open display");
|
||||
screen = DefaultScreen(dpy);
|
||||
|
|
41
drw.c
41
drw.c
|
@ -133,19 +133,6 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
|
|||
die("no font specified.");
|
||||
}
|
||||
|
||||
/* Do not allow using color fonts. This is a workaround for a BadLength
|
||||
* error from Xft with color glyphs. Modelled on the Xterm workaround. See
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=1498269
|
||||
* https://lists.suckless.org/dev/1701/30932.html
|
||||
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
|
||||
* and lots more all over the internet.
|
||||
*/
|
||||
FcBool iscol;
|
||||
if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
|
||||
XftFontClose(drw->dpy, xfont);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
font = ecalloc(1, sizeof(Fnt));
|
||||
font->xfont = xfont;
|
||||
font->pattern = pattern;
|
||||
|
@ -251,8 +238,8 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
|
|||
int
|
||||
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
|
||||
{
|
||||
int i, ty, ellipsis_x = 0;
|
||||
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, ellipsis_width;
|
||||
int ty, ellipsis_x = 0;
|
||||
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
|
||||
XftDraw *d = NULL;
|
||||
Fnt *usedfont, *curfont, *nextfont;
|
||||
int utf8strlen, utf8charlen, render = x || y || w || h;
|
||||
|
@ -264,10 +251,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
|
|||
XftResult result;
|
||||
int charexists = 0, overflow = 0;
|
||||
/* keep track of a couple codepoints for which we have no match. */
|
||||
enum { nomatches_len = 64 };
|
||||
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
|
||||
static unsigned int nomatches[128], ellipsis_width;
|
||||
|
||||
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
|
||||
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
|
||||
return 0;
|
||||
|
||||
if (!render) {
|
||||
|
@ -283,7 +269,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
|
|||
}
|
||||
|
||||
usedfont = drw->fonts;
|
||||
drw_font_getexts(usedfont, "...", 3, &ellipsis_width, NULL);
|
||||
if (!ellipsis_width && render)
|
||||
ellipsis_width = drw_fontset_getwidth(drw, "...");
|
||||
while (1) {
|
||||
ew = ellipsis_len = utf8strlen = 0;
|
||||
utf8str = text;
|
||||
|
@ -349,11 +336,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
|
|||
* character must be drawn. */
|
||||
charexists = 1;
|
||||
|
||||
for (i = 0; i < nomatches_len; ++i) {
|
||||
/* avoid calling XftFontMatch if we know we won't find a match */
|
||||
if (utf8codepoint == nomatches.codepoint[i])
|
||||
goto no_match;
|
||||
}
|
||||
hash = (unsigned int)utf8codepoint;
|
||||
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
|
||||
hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
|
||||
h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
|
||||
h1 = (hash >> 17) % LENGTH(nomatches);
|
||||
/* avoid expensive XftFontMatch call when we know we won't find a match */
|
||||
if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
|
||||
goto no_match;
|
||||
|
||||
fccharset = FcCharSetCreate();
|
||||
FcCharSetAddChar(fccharset, utf8codepoint);
|
||||
|
@ -366,7 +356,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
|
|||
fcpattern = FcPatternDuplicate(drw->fonts->pattern);
|
||||
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
|
||||
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
|
||||
FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
|
||||
|
||||
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
|
||||
FcDefaultSubstitute(fcpattern);
|
||||
|
@ -383,7 +372,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
|
|||
curfont->next = usedfont;
|
||||
} else {
|
||||
xfont_free(usedfont);
|
||||
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
|
||||
nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
|
||||
no_match:
|
||||
usedfont = drw->fonts;
|
||||
}
|
||||
|
|
23
util.c
23
util.c
|
@ -6,18 +6,9 @@
|
|||
|
||||
#include "util.h"
|
||||
|
||||
void *
|
||||
ecalloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (!(p = calloc(nmemb, size)))
|
||||
die("calloc:");
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
die(const char *fmt, ...) {
|
||||
die(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
@ -33,3 +24,13 @@ die(const char *fmt, ...) {
|
|||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *
|
||||
ecalloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (!(p = calloc(nmemb, size)))
|
||||
die("calloc:");
|
||||
return p;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue