Compare commits

..

27 Commits
4.6 ... 4.8

Author SHA1 Message Date
23051d78dd bump version to 4.8 2018-03-14 19:48:05 +01:00
e2a280541e add key bindings for moving to the word start or end
Mod1+b/^Left and Mod1+f/^Right
2018-03-13 20:10:46 +01:00
889512811d Fix regression in 84a1bc5
Reported by Jochen Sprickerhof, thanks!

Applied patch with minor change (only initialize `i` for XINERAMA).
2018-01-04 23:45:49 +01:00
84a1bc5d0d Instantiate j var outside #ifdef XINEMARA directive because it is used in loop outside directive 2018-01-04 18:14:41 +01:00
f0a5b75d6a drw: drw_scm_create: use Clr type
in this context XftColor is a too low-level type.
2017-11-03 21:10:38 +01:00
1cabeda550 fix a possible free of a uninitialize variable in paste() 2017-11-03 21:07:02 +01:00
41379f7c39 init colors using SchemeLast
this makes it slightly easier to add colors to schemes.
2017-11-03 21:05:29 +01:00
64ab2801fb Set class name on menu window
WM_CLASS is a standard ICCCM property which is used to identify windows.
Window managers and compositors use it to allow per-application
configurable behavior.
2017-11-03 20:41:03 +01:00
f428f3e01a release 4.7 2017-05-02 18:32:04 +02:00
5cd66e2c6c Revert "fix input text matching"
This reverts commit 09d0a36e0370f7ca9bdb171bf93c5ac3131c5a92.

Using strncmp with the length of the user input turns it into a prefix
match rather than an exact match as it's supposed to be.
2016-12-11 12:33:16 +01:00
e90b88e12a Xinerama: correct variable declarations in preprocessor conditional 2016-11-25 14:21:40 +01:00
a280bdad1f die() on calloc failure
thanks Markus Teich and David!
2016-11-05 11:36:42 +01:00
a9a5c6cc2d add embedding support with -w option 2016-10-14 15:26:35 +02:00
a97f550aa7 dmenu.1: group single options 2016-10-14 15:26:34 +02:00
ff8daf8847 dmenu.1: fix -l option 2016-10-14 15:26:34 +02:00
026827fd65 die() consistency: always add newline 2016-08-12 14:39:30 +02:00
d14670b995 fix crash if negative monitor (< -1) was passed
for example: dmenu -m '-9001'
2016-07-26 23:13:06 +02:00
aa92cd68bd Revert "Print highlighted input text only on single match"
This reverts commit a4053bc4e403ae57343f43b7e363a0911bba5a3a.

My bad, was working on the wrong branch and accidently pushed it...
*facepalm*
2016-07-26 23:02:34 +02:00
a4053bc4e4 Print highlighted input text only on single match
When the input text fully matches a single item, do not draw the item
and highlight the input text to show that it matches an item in
opposition to regular input text not matching anything.
2016-07-26 18:29:42 +02:00
657122f781 Partially revert 44c7de3: fix items text width offset calculation
Without this, we discard the item if it's longer than assigned width
instead of truncating it.
2016-07-25 19:57:31 +02:00
3c91eed0fb config.def.h: style improvement, use color Scheme enum 2016-06-28 17:56:25 +02:00
76eb578304 fix: Do not crash on e.g. dmenu < /dev/null 2016-06-28 17:51:28 +02:00
44c7de3dcf import new drw from libsl and minor fixes.
- extract drawitem function (code deduplication)
- fix bug where inputw was not correctly calculated from the widest item, but
  just from the one with the longest strlen() which is not the same. It's better
  now, but does not account for fallback fonts, since it would be too slow to
  calculate all the correct item widths on startup.
- minor code style fixes (indentation, useless line breaks)
2016-06-03 19:13:15 +02:00
b3d9451c2d arg.h: fixed argv checks order
This prevents accessing to a potentially out-of-bounds memory section.

Signed-off-by: Lucas Gabriel Vuotto <l.vuotto92@gmail.com>
2016-02-23 00:31:03 +01:00
3de85ca21c Typofix 2016-01-14 23:02:27 +01:00
bf3deb6357 Shut up glibc about _BSD_SOURCE being deprecated 2015-12-19 20:32:17 +01:00
120e840107 Add config option for word delimiters
Let the user configure word boundaries other than ' ', only works with
the portable character set.
2015-12-19 20:32:14 +01:00
9 changed files with 380 additions and 282 deletions

4
arg.h
View File

@ -10,8 +10,8 @@ extern char *argv0;
/* use main(int argc, char *argv[]) */ /* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][1]\ argv[0] && argv[0][0] == '-'\
&& argv[0][0] == '-';\ && argv[0][1];\
argc--, argv++) {\ argc--, argv++) {\
char argc_;\ char argc_;\
char **argv_;\ char **argv_;\

View File

@ -6,12 +6,18 @@ static int topbar = 1; /* -b option; if 0, dmenu appears a
static const char *fonts[] = { static const char *fonts[] = {
"monospace:size=10" "monospace:size=10"
}; };
static const char *prompt = NULL; /* -p option; prompt to the elft of input field */ static const char *prompt = NULL; /* -p option; prompt to the left of input field */
static const char *normbgcolor = "#222222"; /* -nb option; normal background */ static const char *colors[SchemeLast][2] = {
static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */ /* fg bg */
static const char *selbgcolor = "#005577"; /* -sb option; selected background */ [SchemeNorm] = { "#bbbbbb", "#222222" },
static const char *selfgcolor = "#eeeeee"; /* -sf option; selected foreground */ [SchemeSel] = { "#eeeeee", "#005577" },
static const char *outbgcolor = "#00ffff"; [SchemeOut] = { "#000000", "#00ffff" },
static const char *outfgcolor = "#000000"; };
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0; static unsigned int lines = 0;
/*
* Characters not considered part of a word while deleting words
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";

View File

@ -1,5 +1,5 @@
# dmenu version # dmenu version
VERSION = 4.6 VERSION = 4.8
# paths # paths
PREFIX = /usr/local PREFIX = /usr/local
@ -23,7 +23,7 @@ INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
# flags # flags
CPPFLAGS = -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 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} CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS} LDFLAGS = -s ${LIBS}

24
dmenu.1
View File

@ -3,13 +3,11 @@
dmenu \- dynamic menu dmenu \- dynamic menu
.SH SYNOPSIS .SH SYNOPSIS
.B dmenu .B dmenu
.RB [ \-b ] .RB [ \-bfiv ]
.RB [ \-f ]
.RB [ \-i ]
.RB [ \-l .RB [ \-l
.IR lines ]
.RB [ \-m .RB [ \-m
.IR monitor ] .IR monitor ]
.IR lines ]
.RB [ \-p .RB [ \-p
.IR prompt ] .IR prompt ]
.RB [ \-fn .RB [ \-fn
@ -22,7 +20,8 @@ dmenu \- dynamic menu
.IR color ] .IR color ]
.RB [ \-sf .RB [ \-sf
.IR color ] .IR color ]
.RB [ \-v ] .RB [ \-w
.IR windowid ]
.P .P
.BR dmenu_run " ..." .BR dmenu_run " ..."
.SH DESCRIPTION .SH DESCRIPTION
@ -78,6 +77,9 @@ defines the selected foreground color.
.TP .TP
.B \-v .B \-v
prints version information to stdout, then exits. prints version information to stdout, then exits.
.TP
.BI \-w " windowid"
embed into windowid.
.SH USAGE .SH USAGE
dmenu is completely controlled by the keyboard. Items are selected using the dmenu is completely controlled by the keyboard. Items are selected using the
arrow keys, page up, page down, home, and end. arrow keys, page up, page down, home, and end.
@ -98,6 +100,12 @@ Confirm input. Prints the input text to stdout and exits, returning success.
.B Escape .B Escape
Exit without selecting an item, returning failure. Exit without selecting an item, returning failure.
.TP .TP
.B Ctrl-Left
Move cursor to the start of the current word
.TP
.B Ctrl-Right
Move cursor to the end of the current word
.TP
C\-a C\-a
Home Home
.TP .TP
@ -158,6 +166,12 @@ Paste from primary X selection
C\-Y C\-Y
Paste from X clipboard Paste from X clipboard
.TP .TP
M\-b
Move cursor to the start of the current word
.TP
M\-f
Move cursor to the end of the current word
.TP
M\-g M\-g
Home Home
.TP .TP

280
dmenu.c
View File

@ -22,8 +22,7 @@
#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ #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))) * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
#define LENGTH(X) (sizeof X / sizeof X[0]) #define LENGTH(X) (sizeof X / sizeof X[0])
#define TEXTNW(X,N) (drw_font_getexts_width(drw->fonts[0], (X), (N))) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
#define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
/* enums */ /* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
@ -35,9 +34,10 @@ struct item {
}; };
static char text[BUFSIZ] = ""; static char text[BUFSIZ] = "";
static char *embed;
static int bh, mw, mh; static int bh, mw, mh;
static int sw, sh; /* X display screen geometry width, height */ static int inputw = 0, promptw;
static int inputw, promptw; static int lrpad; /* sum of left and right padding */
static size_t cursor; static size_t cursor;
static struct item *items = NULL; static struct item *items = NULL;
static struct item *matches, *matchend; static struct item *matches, *matchend;
@ -46,11 +46,11 @@ static int mon = -1, screen;
static Atom clip, utf8; static Atom clip, utf8;
static Display *dpy; static Display *dpy;
static Window root, win; static Window root, parentwin, win;
static XIC xic; static XIC xic;
static ClrScheme scheme[SchemeLast];
static Drw *drw; static Drw *drw;
static Clr *scheme[SchemeLast];
#include "config.h" #include "config.h"
@ -94,10 +94,8 @@ cleanup(void)
size_t i; size_t i;
XUngrabKey(dpy, AnyKey, AnyModifier, root); XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < SchemeLast; i++) { for (i = 0; i < SchemeLast; i++)
drw_clr_free(scheme[i].bg); free(scheme[i]);
drw_clr_free(scheme[i].fg);
}
drw_free(drw); drw_free(drw);
XSync(dpy, False); XSync(dpy, False);
XCloseDisplay(dpy); XCloseDisplay(dpy);
@ -114,89 +112,101 @@ cistrstr(const char *s, const char *sub)
return NULL; return NULL;
} }
static int
drawitem(struct item *item, int x, int y, int w)
{
if (item == sel)
drw_setscheme(drw, scheme[SchemeSel]);
else if (item->out)
drw_setscheme(drw, scheme[SchemeOut]);
else
drw_setscheme(drw, scheme[SchemeNorm]);
return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
}
static void static void
drawmenu(void) drawmenu(void)
{ {
int curpos; unsigned int curpos;
struct item *item; struct item *item;
int x = 0, y = 0, h = bh, w; int x = 0, y = 0, w;
drw_setscheme(drw, &scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, 0, 0, mw, mh, 1, 1, 1); drw_rect(drw, 0, 0, mw, mh, 1, 1);
if (prompt && *prompt) { if (prompt && *prompt) {
drw_setscheme(drw, &scheme[SchemeSel]); drw_setscheme(drw, scheme[SchemeSel]);
drw_text(drw, x, 0, promptw, bh, prompt, 0); x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
x += promptw;
} }
/* draw input field */ /* draw input field */
w = (lines > 0 || !matches) ? mw - x : inputw; w = (lines > 0 || !matches) ? mw - x : inputw;
drw_setscheme(drw, &scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, text, 0); drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
if ((curpos = TEXTNW(text, cursor) + bh / 2 - 2) < w) { drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL);
drw_setscheme(drw, &scheme[SchemeNorm]); if ((curpos += lrpad / 2 - 1) < w) {
drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0); drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
} }
if (lines > 0) { if (lines > 0) {
/* draw vertical list */ /* draw vertical list */
w = mw - x; for (item = curr; item != next; item = item->right)
for (item = curr; item != next; item = item->right) { drawitem(item, x, y += bh, mw - x);
y += h;
if (item == sel)
drw_setscheme(drw, &scheme[SchemeSel]);
else if (item->out)
drw_setscheme(drw, &scheme[SchemeOut]);
else
drw_setscheme(drw, &scheme[SchemeNorm]);
drw_text(drw, x, y, w, bh, item->text, 0);
}
} else if (matches) { } else if (matches) {
/* draw horizontal list */ /* draw horizontal list */
x += inputw; x += inputw;
w = TEXTW("<"); w = TEXTW("<");
if (curr->left) { if (curr->left) {
drw_setscheme(drw, &scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, "<", 0); drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
} }
for (item = curr; item != next; item = item->right) { x += w;
x += w; for (item = curr; item != next; item = item->right)
w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">")));
if (item == sel)
drw_setscheme(drw, &scheme[SchemeSel]);
else if (item->out)
drw_setscheme(drw, &scheme[SchemeOut]);
else
drw_setscheme(drw, &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, item->text, 0);
}
w = TEXTW(">");
x = mw - w;
if (next) { if (next) {
drw_setscheme(drw, &scheme[SchemeNorm]); w = TEXTW(">");
drw_text(drw, x, 0, w, bh, ">", 0); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
} }
} }
drw_map(drw, win, 0, 0, mw, mh); drw_map(drw, win, 0, 0, mw, mh);
} }
static void
grabfocus(void)
{
struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 };
Window focuswin;
int i, revertwin;
for (i = 0; i < 100; ++i) {
XGetInputFocus(dpy, &focuswin, &revertwin);
if (focuswin == win)
return;
XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
nanosleep(&ts, NULL);
}
die("cannot grab focus");
}
static void static void
grabkeyboard(void) grabkeyboard(void)
{ {
struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
int i; int i;
if (embed)
return;
/* try to grab keyboard, we may have to wait for another process to ungrab */ /* try to grab keyboard, we may have to wait for another process to ungrab */
for (i = 0; i < 1000; i++) { for (i = 0; i < 1000; i++) {
if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) GrabModeAsync, CurrentTime) == GrabSuccess)
return; return;
nanosleep(&ts, NULL); nanosleep(&ts, NULL);
} }
die("cannot grab keyboard\n"); die("cannot grab keyboard");
} }
static void static void
@ -214,11 +224,11 @@ match(void)
/* separate input text into tokens to be matched individually */ /* separate input text into tokens to be matched individually */
for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
die("cannot realloc %u bytes\n", tokn * sizeof *tokv); die("cannot realloc %u bytes:", tokn * sizeof *tokv);
len = tokc ? strlen(tokv[0]) : 0; len = tokc ? strlen(tokv[0]) : 0;
matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
textsize = strlen(text); textsize = strlen(text) + 1;
for (item = items; item && item->text; item++) { for (item = items; item && item->text; item++) {
for (i = 0; i < tokc; i++) for (i = 0; i < tokc; i++)
if (!fstrstr(item->text, tokv[i])) if (!fstrstr(item->text, tokv[i]))
@ -277,6 +287,22 @@ nextrune(int inc)
return n; return n;
} }
static void
movewordedge(int dir)
{
if (dir < 0) { /* move cursor to the start of the word*/
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
} else { /* move cursor to the end of the word */
while (text[cursor] && strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
while (text[cursor] && !strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
}
}
static void static void
keypress(XKeyEvent *ev) keypress(XKeyEvent *ev)
{ {
@ -314,9 +340,9 @@ keypress(XKeyEvent *ev)
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 && strchr(worddelimiters, text[nextrune(-1)]))
insert(NULL, nextrune(-1) - cursor); insert(NULL, nextrune(-1) - cursor);
while (cursor > 0 && text[nextrune(-1)] != ' ') while (cursor > 0 && !strchr(worddelimiters, 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 */
@ -324,6 +350,14 @@ keypress(XKeyEvent *ev)
XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
utf8, utf8, win, CurrentTime); utf8, utf8, win, CurrentTime);
return; return;
case XK_Left:
movewordedge(-1);
ksym = NoSymbol;
break;
case XK_Right:
movewordedge(+1);
ksym = NoSymbol;
break;
case XK_Return: case XK_Return:
case XK_KP_Enter: case XK_KP_Enter:
break; break;
@ -335,6 +369,14 @@ keypress(XKeyEvent *ev)
} }
else if (ev->state & Mod1Mask) else if (ev->state & Mod1Mask)
switch(ksym) { switch(ksym) {
case XK_b:
movewordedge(-1);
ksym = NoSymbol;
break;
case XK_f:
movewordedge(+1);
ksym = NoSymbol;
break;
case XK_g: ksym = XK_Home; break; case XK_g: ksym = XK_Home; break;
case XK_G: ksym = XK_End; break; case XK_G: ksym = XK_End; break;
case XK_h: ksym = XK_Up; break; case XK_h: ksym = XK_Up; break;
@ -349,6 +391,8 @@ keypress(XKeyEvent *ev)
if (!iscntrl(*buf)) if (!iscntrl(*buf))
insert(buf, len); insert(buf, len);
break; break;
case NoSymbol:
break;
case XK_Delete: case XK_Delete:
if (text[cursor] == '\0') if (text[cursor] == '\0')
return; return;
@ -457,18 +501,21 @@ paste(void)
Atom da; Atom da;
/* we have been given the current selection, now insert it into input */ /* we have been given the current selection, now insert it into input */
XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, if (XGetWindowProperty(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 : (ssize_t)strlen(p)); == Success && p) {
XFree(p); insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
XFree(p);
}
drawmenu(); drawmenu();
} }
static void static void
readstdin(void) readstdin(void)
{ {
char buf[sizeof text], *p, *maxstr = NULL; char buf[sizeof text], *p;
size_t i, max = 0, size = 0; size_t i, imax = 0, size = 0;
unsigned int tmpmax = 0;
/* read each line from stdin and add it to the item list */ /* read each line from stdin and add it to the item list */
for (i = 0; fgets(buf, sizeof buf, stdin); i++) { for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
@ -480,12 +527,15 @@ readstdin(void)
if (!(items[i].text = strdup(buf))) if (!(items[i].text = strdup(buf)))
die("cannot strdup %u bytes:", strlen(buf) + 1); die("cannot strdup %u bytes:", strlen(buf) + 1);
items[i].out = 0; items[i].out = 0;
if (strlen(items[i].text) > max) drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
max = strlen(maxstr = items[i].text); if (tmpmax > inputw) {
inputw = tmpmax;
imax = i;
}
} }
if (items) if (items)
items[i].text = NULL; items[i].text = NULL;
inputw = maxstr ? TEXTW(maxstr) : 0; inputw = items ? TEXTW(items[imax].text) : 0;
lines = MIN(lines, i); lines = MIN(lines, i);
} }
@ -502,6 +552,11 @@ run(void)
if (ev.xexpose.count == 0) if (ev.xexpose.count == 0)
drw_map(drw, win, 0, 0, mw, mh); drw_map(drw, win, 0, 0, mw, mh);
break; break;
case FocusIn:
/* regrab focus from parent window */
if (ev.xfocus.window != win)
grabfocus();
break;
case KeyPress: case KeyPress:
keypress(&ev.xkey); keypress(&ev.xkey);
break; break;
@ -520,36 +575,34 @@ run(void)
static void static void
setup(void) setup(void)
{ {
int x, y; int x, y, i, j;
unsigned int du;
XSetWindowAttributes swa; XSetWindowAttributes swa;
XIM xim; XIM xim;
Window w, dw, *dws;
XWindowAttributes wa;
XClassHint ch = {"dmenu", "dmenu"};
#ifdef XINERAMA #ifdef XINERAMA
XineramaScreenInfo *info; XineramaScreenInfo *info;
Window w, pw, dw, *dws; Window pw;
XWindowAttributes wa; int a, di, n, area = 0;
int a, j, di, n, i = 0, area = 0;
unsigned int du;
#endif #endif
/* init appearance */ /* init appearance */
scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor); for (j = 0; j < SchemeLast; j++)
scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor); scheme[j] = drw_scm_create(drw, colors[j], 2);
scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
scheme[SchemeOut].bg = drw_clr_create(drw, outbgcolor);
scheme[SchemeOut].fg = drw_clr_create(drw, outfgcolor);
clip = XInternAtom(dpy, "CLIPBOARD", False); clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False);
/* calculate menu geometry */ /* calculate menu geometry */
bh = drw->fonts[0]->h + 2; bh = drw->fonts->h + 2;
lines = MAX(lines, 0); lines = MAX(lines, 0);
mh = (lines + 1) * bh; mh = (lines + 1) * bh;
#ifdef XINERAMA #ifdef XINERAMA
if ((info = XineramaQueryScreens(dpy, &n))) { i = 0;
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
XGetInputFocus(dpy, &w, &di); XGetInputFocus(dpy, &w, &di);
if (mon != -1 && mon < n) if (mon >= 0 && mon < n)
i = mon; i = mon;
else if (w != root && w != PointerRoot && w != None) { else if (w != root && w != PointerRoot && w != None) {
/* find top-level window containing current input focus */ /* find top-level window containing current input focus */
@ -566,7 +619,7 @@ setup(void)
} }
} }
/* no focused window is on screen, so use pointer location instead */ /* no focused window is on screen, so use pointer location instead */
if (mon == -1 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
if (INTERSECT(x, y, 1, 1, info[i])) if (INTERSECT(x, y, 1, 1, info[i]))
break; break;
@ -578,22 +631,25 @@ setup(void)
} else } else
#endif #endif
{ {
if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx",
parentwin);
x = 0; x = 0;
y = topbar ? 0 : sh - mh; y = topbar ? 0 : wa.height - mh;
mw = sw; mw = wa.width;
} }
promptw = (prompt && *prompt) ? TEXTW(prompt) : 0; promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
inputw = MIN(inputw, mw/3); inputw = MIN(inputw, mw/3);
match(); match();
/* create menu window */ /* create menu window */
swa.override_redirect = True; swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm].bg->pix; swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dpy, root, x, y, mw, mh, 0, win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
DefaultDepth(dpy, screen), CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent,
DefaultVisual(dpy, screen),
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
XSetClassHint(dpy, win, &ch);
/* open input methods */ /* open input methods */
xim = XOpenIM(dpy, NULL, NULL, NULL); xim = XOpenIM(dpy, NULL, NULL, NULL);
@ -601,6 +657,15 @@ setup(void)
XNClientWindow, win, XNFocusWindow, win, NULL); XNClientWindow, win, XNFocusWindow, win, NULL);
XMapRaised(dpy, win); XMapRaised(dpy, win);
if (embed) {
XSelectInput(dpy, parentwin, FocusChangeMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
for (i = 0; i < du && dws[i] != win; ++i)
XSelectInput(dpy, dws[i], FocusChangeMask);
XFree(dws);
}
grabfocus();
}
drw_resize(drw, mw, mh); drw_resize(drw, mw, mh);
drawmenu(); drawmenu();
} }
@ -608,14 +673,15 @@ setup(void)
static void static void
usage(void) usage(void)
{ {
fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
" [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr);
exit(1); exit(1);
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
XWindowAttributes wa;
int i, fast = 0; int i, fast = 0;
for (i = 1; i < argc; i++) for (i = 1; i < argc; i++)
@ -642,29 +708,33 @@ main(int argc, char *argv[])
else if (!strcmp(argv[i], "-fn")) /* font or font set */ else if (!strcmp(argv[i], "-fn")) /* font or font set */
fonts[0] = argv[++i]; fonts[0] = argv[++i];
else if (!strcmp(argv[i], "-nb")) /* normal background color */ else if (!strcmp(argv[i], "-nb")) /* normal background color */
normbgcolor = argv[++i]; colors[SchemeNorm][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
normfgcolor = argv[++i]; colors[SchemeNorm][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-sb")) /* selected background color */ else if (!strcmp(argv[i], "-sb")) /* selected background color */
selbgcolor = argv[++i]; colors[SchemeSel][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
selfgcolor = argv[++i]; colors[SchemeSel][ColFg] = argv[++i];
else if (!strcmp(argv[i], "-w")) /* embedding window id */
embed = argv[++i];
else else
usage(); usage();
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("warning: no locale support\n", stderr); fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL))) if (!(dpy = XOpenDisplay(NULL)))
die("cannot open display\n"); die("cannot open display");
screen = DefaultScreen(dpy); screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen); root = RootWindow(dpy, screen);
sw = DisplayWidth(dpy, screen); if (!embed || !(parentwin = strtol(embed, NULL, 0)))
sh = DisplayHeight(dpy, screen); parentwin = root;
drw = drw_create(dpy, screen, root, sw, sh); if (!XGetWindowAttributes(dpy, parentwin, &wa))
drw_load_fonts(drw, fonts, LENGTH(fonts)); die("could not get embedding window attributes: 0x%lx",
if (!drw->fontcount) parentwin);
die("no fonts could be loaded.\n"); drw = drw_create(dpy, screen, root, wa.width, wa.height);
drw_setscheme(drw, &scheme[SchemeNorm]); if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded.");
lrpad = drw->fonts->h;
if (fast) { if (fast) {
grabkeyboard(); grabkeyboard();

259
drw.c
View File

@ -63,9 +63,8 @@ utf8decode(const char *c, long *u, size_t clen)
Drw * Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{ {
Drw *drw; Drw *drw = ecalloc(1, sizeof(Drw));
drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy; drw->dpy = dpy;
drw->screen = screen; drw->screen = screen;
drw->root = root; drw->root = root;
@ -73,7 +72,6 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
drw->h = h; drw->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL); drw->gc = XCreateGC(dpy, root, 0, NULL);
drw->fontcount = 0;
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw; return drw;
@ -82,6 +80,9 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
void void
drw_resize(Drw *drw, unsigned int w, unsigned int h) drw_resize(Drw *drw, unsigned int w, unsigned int h)
{ {
if (!drw)
return;
drw->w = w; drw->w = w;
drw->h = h; drw->h = h;
if (drw->drawable) if (drw->drawable)
@ -92,84 +93,56 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
void void
drw_free(Drw *drw) drw_free(Drw *drw)
{ {
size_t i;
for (i = 0; i < drw->fontcount; i++)
drw_font_free(drw->fonts[i]);
XFreePixmap(drw->dpy, drw->drawable); XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc); XFreeGC(drw->dpy, drw->gc);
free(drw); free(drw);
} }
/* This function is an implementation detail. Library users should use /* This function is an implementation detail. Library users should use
* drw_font_create instead. * drw_fontset_create instead.
*/ */
static Fnt * static Fnt *
drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{ {
Fnt *font; Fnt *font;
XftFont *xfont = NULL; XftFont *xfont = NULL;
FcPattern *pattern = NULL; FcPattern *pattern = NULL;
if (fontname) { if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield same /* Using the pattern found at font->xfont->pattern does not yield the
* the same substitution results as using the pattern returned by * same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback * FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in * behaviour whereas the former just results in missing-character
* missing-character-rectangles being drawn, at least with some fonts. * rectangles being drawn, at least with some fonts. */
*/
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font: '%s'\n", fontname); fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
return NULL; return NULL;
} }
if (!(pattern = FcNameParse((FcChar8 *) fontname))) { if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot load font: '%s'\n", fontname); fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont); XftFontClose(drw->dpy, xfont);
return NULL; return NULL;
} }
} else if (fontpattern) { } else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font pattern.\n"); fprintf(stderr, "error, cannot load font from pattern.\n");
return NULL; return NULL;
} }
} else { } else {
die("no font specified.\n"); die("no font specified.");
} }
font = ecalloc(1, sizeof(Fnt)); font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont; font->xfont = xfont;
font->pattern = pattern; font->pattern = pattern;
font->ascent = xfont->ascent; font->h = xfont->ascent + xfont->descent;
font->descent = xfont->descent;
font->h = font->ascent + font->descent;
font->dpy = drw->dpy; font->dpy = drw->dpy;
return font; return font;
} }
Fnt* static void
drw_font_create(Drw *drw, const char *fontname) xfont_free(Fnt *font)
{
return drw_font_xcreate(drw, fontname, NULL);
}
void
drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount)
{
size_t i;
Fnt *font;
for (i = 0; i < fontcount; i++) {
if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
die("Font cache exhausted.\n");
} else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
drw->fonts[drw->fontcount++] = font;
}
}
}
void
drw_font_free(Fnt *font)
{ {
if (!font) if (!font)
return; return;
@ -179,55 +152,98 @@ drw_font_free(Fnt *font)
free(font); free(font);
} }
Clr * Fnt*
drw_clr_create(Drw *drw, const char *clrname) drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
{ {
Clr *clr; Fnt *cur, *ret = NULL;
size_t i;
if (!drw || !fonts)
return NULL;
for (i = 1; i <= fontcount; i++) {
if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
cur->next = ret;
ret = cur;
}
}
return (drw->fonts = ret);
}
void
drw_fontset_free(Fnt *font)
{
if (font) {
drw_fontset_free(font->next);
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
{
if (!drw || !dest || !clrname)
return;
clr = ecalloc(1, sizeof(Clr));
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen),
clrname, &clr->rgb)) clrname, dest))
die("error, cannot allocate color '%s'\n", clrname); die("error, cannot allocate color '%s'", clrname);
clr->pix = clr->rgb.pixel; }
return clr; /* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i]);
return ret;
} }
void void
drw_clr_free(Clr *clr) drw_setfontset(Drw *drw, Fnt *set)
{ {
free(clr); if (drw)
drw->fonts = set;
} }
void void
drw_setscheme(Drw *drw, ClrScheme *scheme) drw_setscheme(Drw *drw, Clr *scm)
{ {
drw->scheme = scheme; if (drw)
drw->scheme = scm;
} }
void void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{ {
if (!drw->scheme) if (!drw || !drw->scheme)
return; return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix); XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled) if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else if (empty) else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
} }
int int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{ {
char buf[1024]; char buf[1024];
int tx, ty, th; int ty;
Extnts tex; unsigned int ew;
XftDraw *d = NULL; XftDraw *d = NULL;
Fnt *curfont, *nextfont; Fnt *usedfont, *curfont, *nextfont;
size_t i, len; size_t i, len;
int utf8strlen, utf8charlen, render; int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0; long utf8codepoint = 0;
const char *utf8str; const char *utf8str;
FcCharSet *fccharset; FcCharSet *fccharset;
@ -236,66 +252,67 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
XftResult result; XftResult result;
int charexists = 0; int charexists = 0;
if (!drw->scheme || !drw->fontcount) if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0; return 0;
if (!(render = x || y || w || h)) { if (!render) {
w = ~w; w = ~w;
} else { } else {
XSetForeground(drw->dpy, drw->gc, invert ? XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
drw->scheme->fg->pix : drw->scheme->bg->pix);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen), DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen)); DefaultColormap(drw->dpy, drw->screen));
x += lpad;
w -= lpad;
} }
curfont = drw->fonts[0]; usedfont = drw->fonts;
while (1) { while (1) {
utf8strlen = 0; utf8strlen = 0;
utf8str = text; utf8str = text;
nextfont = NULL; nextfont = NULL;
while (*text) { while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (i = 0; i < drw->fontcount; i++) { for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) { if (charexists) {
if (drw->fonts[i] == curfont) { if (curfont == usedfont) {
utf8strlen += utf8charlen; utf8strlen += utf8charlen;
text += utf8charlen; text += utf8charlen;
} else { } else {
nextfont = drw->fonts[i]; nextfont = curfont;
} }
break; break;
} }
} }
if (!charexists || (nextfont && nextfont != curfont)) if (!charexists || nextfont)
break; break;
else else
charexists = 0; charexists = 0;
} }
if (utf8strlen) { if (utf8strlen) {
drw_font_getexts(curfont, utf8str, utf8strlen, &tex); drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */ /* shorten text if necessary */
for (len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--) for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(curfont, utf8str, len, &tex); drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
if (len) { if (len) {
memcpy(buf, utf8str, len); memcpy(buf, utf8str, len);
buf[len] = '\0'; buf[len] = '\0';
if (len < utf8strlen) if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.'); for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) { if (render) {
th = curfont->ascent + curfont->descent; ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
ty = y + (h / 2) - (th / 2) + curfont->ascent; XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
tx = x + (h / 2); usedfont->xfont, x, ty, (XftChar8 *)buf, len);
XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
} }
x += tex.w; x += ew;
w -= tex.w; w -= ew;
} }
} }
@ -303,26 +320,21 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
break; break;
} else if (nextfont) { } else if (nextfont) {
charexists = 0; charexists = 0;
curfont = nextfont; usedfont = nextfont;
} else { } else {
/* Regardless of whether or not a fallback font is found, the /* Regardless of whether or not a fallback font is found, the
* character must be drawn. * character must be drawn. */
*/
charexists = 1; charexists = 1;
if (drw->fontcount >= DRW_FONT_CACHE_SIZE)
continue;
fccharset = FcCharSetCreate(); fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint); FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts[0]->pattern) { if (!drw->fonts->pattern) {
/* Refer to the comment in drw_font_xcreate for more /* Refer to the comment in xfont_create for more information. */
* information. */ die("the first font in the cache must be loaded from a font string.");
die("the first font in the cache must be loaded from a font string.\n");
} }
fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); fcpattern = FcPatternDuplicate(drw->fonts->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
@ -334,12 +346,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
FcPatternDestroy(fcpattern); FcPatternDestroy(fcpattern);
if (match) { if (match) {
curfont = drw_font_xcreate(drw, NULL, match); usedfont = xfont_create(drw, NULL, match);
if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
drw->fonts[drw->fontcount++] = curfont; for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */
curfont->next = usedfont;
} else { } else {
drw_font_free(curfont); xfont_free(usedfont);
curfont = drw->fonts[0]; usedfont = drw->fonts;
} }
} }
} }
@ -347,34 +361,40 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
if (d) if (d)
XftDrawDestroy(d); XftDrawDestroy(d);
return x; return x + (render ? w : 0);
} }
void void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{ {
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False); XSync(drw->dpy, False);
} }
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
void void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{ {
XGlyphInfo ext; XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
tex->h = font->h; if (w)
tex->w = ext.xOff; *w = ext.xOff;
} if (h)
*h = font->h;
unsigned int
drw_font_getexts_width(Fnt *font, const char *text, unsigned int len)
{
Extnts tex;
drw_font_getexts(font, text, len, &tex);
return tex.w;
} }
Cur * Cur *
@ -382,7 +402,9 @@ drw_cur_create(Drw *drw, int shape)
{ {
Cur *cur; Cur *cur;
cur = ecalloc(1, sizeof(Cur)); if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape); cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur; return cur;
@ -393,6 +415,7 @@ drw_cur_free(Drw *drw, Cur *cursor)
{ {
if (!cursor) if (!cursor)
return; return;
XFreeCursor(drw->dpy, cursor->cursor); XFreeCursor(drw->dpy, cursor->cursor);
free(cursor); free(cursor);
} }

63
drw.h
View File

@ -1,29 +1,19 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#define DRW_FONT_CACHE_SIZE 32
typedef struct {
unsigned long pix;
XftColor rgb;
} Clr;
typedef struct { typedef struct {
Cursor cursor; Cursor cursor;
} Cur; } Cur;
typedef struct { typedef struct Fnt {
Display *dpy; Display *dpy;
int ascent;
int descent;
unsigned int h; unsigned int h;
XftFont *xfont; XftFont *xfont;
FcPattern *pattern; FcPattern *pattern;
struct Fnt *next;
} Fnt; } Fnt;
typedef struct { enum { ColFg, ColBg }; /* Clr scheme index */
Clr *fg; typedef XftColor Clr;
Clr *bg;
Clr *border;
} ClrScheme;
typedef struct { typedef struct {
unsigned int w, h; unsigned int w, h;
@ -32,43 +22,36 @@ typedef struct {
Window root; Window root;
Drawable drawable; Drawable drawable;
GC gc; GC gc;
ClrScheme *scheme; Clr *scheme;
size_t fontcount; Fnt *fonts;
Fnt *fonts[DRW_FONT_CACHE_SIZE];
} Drw; } Drw;
typedef struct {
unsigned int w;
unsigned int h;
} Extnts;
/* Drawable abstraction */ /* Drawable abstraction */
Drw *drw_create(Display *, int, Window, unsigned int, unsigned int); Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
void drw_resize(Drw *, unsigned int, unsigned int); void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *); void drw_free(Drw *drw);
/* Fnt abstraction */ /* Fnt abstraction */
Fnt *drw_font_create(Drw *, const char *); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_load_fonts(Drw *, const char *[], size_t); void drw_fontset_free(Fnt* set);
void drw_font_free(Fnt *); unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
void drw_font_getexts(Fnt *, const char *, unsigned int, Extnts *); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
unsigned int drw_font_getexts_width(Fnt *, const char *, unsigned int);
/* Colour abstraction */ /* Colorscheme abstraction */
Clr *drw_clr_create(Drw *, const char *); void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
void drw_clr_free(Clr *); Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
/* Cursor abstraction */ /* Cursor abstraction */
Cur *drw_cur_create(Drw *, int); Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *, Cur *); void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */ /* Drawing context manipulation */
void drw_setfont(Drw *, Fnt *); void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *, ClrScheme *); void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */ /* Drawing functions */
void drw_rect(Drw *, int, int, unsigned int, unsigned int, int, int, int); void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *, int, int, unsigned int, unsigned int, const char *, int); int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
/* Map functions */ /* Map functions */
void drw_map(Drw *, Window, int, int, unsigned int, unsigned int); void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

4
util.c
View File

@ -12,7 +12,7 @@ ecalloc(size_t nmemb, size_t size)
void *p; void *p;
if (!(p = calloc(nmemb, size))) if (!(p = calloc(nmemb, size)))
perror(NULL); die("calloc:");
return p; return p;
} }
@ -27,6 +27,8 @@ die(const char *fmt, ...) {
if (fmt[0] && fmt[strlen(fmt)-1] == ':') { if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr); fputc(' ', stderr);
perror(NULL); perror(NULL);
} else {
fputc('\n', stderr);
} }
exit(1); exit(1);

4
util.h
View File

@ -4,5 +4,5 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
void die(const char *errstr, ...); void die(const char *fmt, ...);
void *ecalloc(size_t, size_t); void *ecalloc(size_t nmemb, size_t size);