diff options
-rw-r--r-- | LICENSE | 1 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | config.def.h | 10 | ||||
-rw-r--r-- | config.mk | 4 | ||||
-rw-r--r-- | dmenu.c | 241 | ||||
-rw-r--r-- | draw.c | 177 | ||||
-rw-r--r-- | draw.h | 35 | ||||
-rw-r--r-- | drw.c | 413 | ||||
-rw-r--r-- | drw.h | 74 | ||||
-rw-r--r-- | util.c | 23 | ||||
-rw-r--r-- | util.h | 7 |
11 files changed, 684 insertions, 316 deletions
@@ -7,6 +7,7 @@ MIT/X Consortium License | |||
7 | © 2009 Evan Gates <evan.gates@gmail.com> | 7 | © 2009 Evan Gates <evan.gates@gmail.com> |
8 | © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com> | 8 | © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com> |
9 | © 2006-2007 Michał Janeczek <janeczek at gmail dot com> | 9 | © 2006-2007 Michał Janeczek <janeczek at gmail dot com> |
10 | © 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org> | ||
10 | 11 | ||
11 | Permission is hereby granted, free of charge, to any person obtaining a | 12 | Permission is hereby granted, free of charge, to any person obtaining a |
12 | copy of this software and associated documentation files (the "Software"), | 13 | copy of this software and associated documentation files (the "Software"), |
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | include config.mk | 4 | include config.mk |
5 | 5 | ||
6 | SRC = dmenu.c draw.c stest.c | 6 | SRC = drw.c dmenu.c stest.c util.c |
7 | OBJ = ${SRC:.c=.o} | 7 | OBJ = ${SRC:.c=.o} |
8 | 8 | ||
9 | all: options dmenu stest | 9 | all: options dmenu stest |
@@ -15,18 +15,18 @@ options: | |||
15 | @echo "CC = ${CC}" | 15 | @echo "CC = ${CC}" |
16 | 16 | ||
17 | .c.o: | 17 | .c.o: |
18 | @echo CC -c $< | 18 | @echo CC $< |
19 | @${CC} -c $< ${CFLAGS} | 19 | @${CC} -c ${CFLAGS} $< |
20 | 20 | ||
21 | config.h: | 21 | config.h: |
22 | @echo creating $@ from config.def.h | 22 | @echo creating $@ from config.def.h |
23 | @cp config.def.h $@ | 23 | @cp config.def.h $@ |
24 | 24 | ||
25 | ${OBJ}: config.h config.mk draw.h | 25 | ${OBJ}: config.h config.mk drw.h |
26 | 26 | ||
27 | dmenu: dmenu.o draw.o | 27 | dmenu: dmenu.o drw.o util.o |
28 | @echo CC -o $@ | 28 | @echo CC -o $@ |
29 | @${CC} -o $@ dmenu.o draw.o ${LDFLAGS} | 29 | @${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS} |
30 | 30 | ||
31 | stest: stest.o | 31 | stest: stest.o |
32 | @echo CC -o $@ | 32 | @echo CC -o $@ |
@@ -39,7 +39,8 @@ clean: | |||
39 | dist: clean | 39 | dist: clean |
40 | @echo creating dist tarball | 40 | @echo creating dist tarball |
41 | @mkdir -p dmenu-${VERSION} | 41 | @mkdir -p dmenu-${VERSION} |
42 | @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_path dmenu_run stest.1 ${SRC} dmenu-${VERSION} | 42 | @cp LICENSE Makefile README config.mk dmenu.1 drw.h util.h dmenu_path \ |
43 | dmenu_run stest.1 ${SRC} dmenu-${VERSION} | ||
43 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} | 44 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} |
44 | @gzip dmenu-${VERSION}.tar | 45 | @gzip dmenu-${VERSION}.tar |
45 | @rm -rf dmenu-${VERSION} | 46 | @rm -rf dmenu-${VERSION} |
diff --git a/config.def.h b/config.def.h index c2a23fa..4e5e3e7 100644 --- a/config.def.h +++ b/config.def.h | |||
@@ -4,8 +4,11 @@ | |||
4 | /* Default settings; can be overrided by command line. */ | 4 | /* Default settings; can be overrided by command line. */ |
5 | 5 | ||
6 | static Bool topbar = True; /* -b option; if False, dmenu appears at bottom */ | 6 | static Bool topbar = True; /* -b option; if False, dmenu appears at bottom */ |
7 | static const char *font = NULL; /* -fn option; default X11 font or font set */ | 7 | /* -fn option overrides fonts[0]; default X11 font or font set */ |
8 | static const char *prompt = NULL; /* -p option; prompt to the elft of input field */ | 8 | static const char *fonts[] = { |
9 | "monospace:size=10" | ||
10 | }; | ||
11 | static const char *prompt = NULL; /* -p option; prompt to the elft of input field */ | ||
9 | static const char *normbgcolor = "#222222"; /* -nb option; normal background */ | 12 | static const char *normbgcolor = "#222222"; /* -nb option; normal background */ |
10 | static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */ | 13 | static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */ |
11 | static const char *selbgcolor = "#005577"; /* -sb option; selected background */ | 14 | static const char *selbgcolor = "#005577"; /* -sb option; selected background */ |
@@ -13,5 +16,4 @@ static const char *selfgcolor = "#eeeeee"; /* -sf option; selected foreground | |||
13 | static const char *outbgcolor = "#00ffff"; | 16 | static const char *outbgcolor = "#00ffff"; |
14 | static const char *outfgcolor = "#000000"; | 17 | static const char *outfgcolor = "#000000"; |
15 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ | 18 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ |
16 | static unsigned int lines = 0; | 19 | static unsigned int lines = 0; |
17 | |||
@@ -13,8 +13,8 @@ XINERAMALIBS = -lXinerama | |||
13 | XINERAMAFLAGS = -DXINERAMA | 13 | XINERAMAFLAGS = -DXINERAMA |
14 | 14 | ||
15 | # includes and libs | 15 | # includes and libs |
16 | INCS = -I${X11INC} | 16 | INCS = -I${X11INC} -I/usr/include/freetype2 |
17 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} | 17 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} -lfontconfig -lXft |
18 | 18 | ||
19 | # flags | 19 | # flags |
20 | CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | 20 | CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} |
@@ -1,5 +1,6 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | 1 | /* See LICENSE file for copyright and license details. */ |
2 | #include <ctype.h> | 2 | #include <ctype.h> |
3 | #include <locale.h> | ||
3 | #include <stdio.h> | 4 | #include <stdio.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
5 | #include <string.h> | 6 | #include <string.h> |
@@ -11,12 +12,20 @@ | |||
11 | #ifdef XINERAMA | 12 | #ifdef XINERAMA |
12 | #include <X11/extensions/Xinerama.h> | 13 | #include <X11/extensions/Xinerama.h> |
13 | #endif | 14 | #endif |
14 | #include "draw.h" | 15 | #include <X11/Xft/Xft.h> |
15 | 16 | ||
17 | #include "drw.h" | ||
18 | #include "util.h" | ||
19 | |||
20 | /* macros */ | ||
16 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ | 21 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ |
17 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) | 22 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) |
18 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 23 | #define LENGTH(X) (sizeof X / sizeof X[0]) |
19 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | 24 | #define TEXTNW(X,N) (drw_font_getexts_width(drw->fonts[0], (X), (N))) |
25 | #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h) | ||
26 | |||
27 | /* enums */ | ||
28 | enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ | ||
20 | 29 | ||
21 | typedef struct Item Item; | 30 | typedef struct Item Item; |
22 | struct Item { | 31 | struct Item { |
@@ -28,6 +37,7 @@ struct Item { | |||
28 | static void appenditem(Item *item, Item **list, Item **last); | 37 | static void appenditem(Item *item, Item **list, Item **last); |
29 | static void calcoffsets(void); | 38 | static void calcoffsets(void); |
30 | static char *cistrstr(const char *s, const char *sub); | 39 | static char *cistrstr(const char *s, const char *sub); |
40 | static void cleanup(void); | ||
31 | static void drawmenu(void); | 41 | static void drawmenu(void); |
32 | static void grabkeyboard(void); | 42 | static void grabkeyboard(void); |
33 | static void insert(const char *str, ssize_t n); | 43 | static void insert(const char *str, ssize_t n); |
@@ -44,11 +54,7 @@ static char text[BUFSIZ] = ""; | |||
44 | static int bh, mw, mh; | 54 | static int bh, mw, mh; |
45 | static int inputw, promptw; | 55 | static int inputw, promptw; |
46 | static size_t cursor = 0; | 56 | static size_t cursor = 0; |
47 | static unsigned long normcol[ColLast]; | ||
48 | static unsigned long selcol[ColLast]; | ||
49 | static unsigned long outcol[ColLast]; | ||
50 | static Atom clip, utf8; | 57 | static Atom clip, utf8; |
51 | static DC *dc; | ||
52 | static Item *items = NULL; | 58 | static Item *items = NULL; |
53 | static Item *matches, *matchend; | 59 | static Item *matches, *matchend; |
54 | static Item *prev, *curr, *next, *sel; | 60 | static Item *prev, *curr, *next, *sel; |
@@ -56,6 +62,13 @@ static Window win; | |||
56 | static XIC xic; | 62 | static XIC xic; |
57 | static int mon = -1; | 63 | static int mon = -1; |
58 | 64 | ||
65 | static ClrScheme scheme[SchemeLast]; | ||
66 | static Display *dpy; | ||
67 | static int screen; | ||
68 | static Window root; | ||
69 | static Drw *drw; | ||
70 | static int sw, sh; /* X display screen geometry width, height */ | ||
71 | |||
59 | #include "config.h" | 72 | #include "config.h" |
60 | 73 | ||
61 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | 74 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
@@ -69,8 +82,8 @@ main(int argc, char *argv[]) { | |||
69 | for(i = 1; i < argc; i++) | 82 | for(i = 1; i < argc; i++) |
70 | /* these options take no arguments */ | 83 | /* these options take no arguments */ |
71 | if(!strcmp(argv[i], "-v")) { /* prints version information */ | 84 | if(!strcmp(argv[i], "-v")) { /* prints version information */ |
72 | puts("dmenu-"VERSION", © 2006-2014 dmenu engineers, see LICENSE for details"); | 85 | puts("dmenu-"VERSION", © 2006-2015 dmenu engineers, see LICENSE for details"); |
73 | exit(EXIT_SUCCESS); | 86 | exit(0); |
74 | } | 87 | } |
75 | else if(!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ | 88 | else if(!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ |
76 | topbar = False; | 89 | topbar = False; |
@@ -90,7 +103,7 @@ main(int argc, char *argv[]) { | |||
90 | else if(!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ | 103 | else if(!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ |
91 | prompt = argv[++i]; | 104 | prompt = argv[++i]; |
92 | else if(!strcmp(argv[i], "-fn")) /* font or font set */ | 105 | else if(!strcmp(argv[i], "-fn")) /* font or font set */ |
93 | font = argv[++i]; | 106 | fonts[0] = argv[++i]; |
94 | else if(!strcmp(argv[i], "-nb")) /* normal background color */ | 107 | else if(!strcmp(argv[i], "-nb")) /* normal background color */ |
95 | normbgcolor = argv[++i]; | 108 | normbgcolor = argv[++i]; |
96 | else if(!strcmp(argv[i], "-nf")) /* normal foreground color */ | 109 | else if(!strcmp(argv[i], "-nf")) /* normal foreground color */ |
@@ -102,8 +115,19 @@ main(int argc, char *argv[]) { | |||
102 | else | 115 | else |
103 | usage(); | 116 | usage(); |
104 | 117 | ||
105 | dc = initdc(); | 118 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) |
106 | initfont(dc, font); | 119 | fputs("warning: no locale support\n", stderr); |
120 | if(!(dpy = XOpenDisplay(NULL))) | ||
121 | die("dwm: cannot open display\n"); | ||
122 | screen = DefaultScreen(dpy); | ||
123 | root = RootWindow(dpy, screen); | ||
124 | sw = DisplayWidth(dpy, screen); | ||
125 | sh = DisplayHeight(dpy, screen); | ||
126 | drw = drw_create(dpy, screen, root, sw, sh); | ||
127 | drw_load_fonts(drw, fonts, LENGTH(fonts)); | ||
128 | if(!drw->fontcount) | ||
129 | die("No fonts could be loaded.\n"); | ||
130 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
107 | 131 | ||
108 | if(fast) { | 132 | if(fast) { |
109 | grabkeyboard(); | 133 | grabkeyboard(); |
@@ -138,16 +162,30 @@ calcoffsets(void) { | |||
138 | if(lines > 0) | 162 | if(lines > 0) |
139 | n = lines * bh; | 163 | n = lines * bh; |
140 | else | 164 | else |
141 | n = mw - (promptw + inputw + textw(dc, "<") + textw(dc, ">")); | 165 | n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); |
142 | /* calculate which items will begin the next page and previous page */ | 166 | /* calculate which items will begin the next page and previous page */ |
143 | for(i = 0, next = curr; next; next = next->right) | 167 | for(i = 0, next = curr; next; next = next->right) |
144 | if((i += (lines > 0) ? bh : MIN(textw(dc, next->text), n)) > n) | 168 | if((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) |
145 | break; | 169 | break; |
146 | for(i = 0, prev = curr; prev && prev->left; prev = prev->left) | 170 | for(i = 0, prev = curr; prev && prev->left; prev = prev->left) |
147 | if((i += (lines > 0) ? bh : MIN(textw(dc, prev->left->text), n)) > n) | 171 | if((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) |
148 | break; | 172 | break; |
149 | } | 173 | } |
150 | 174 | ||
175 | void | ||
176 | cleanup(void) { | ||
177 | XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||
178 | drw_clr_free(scheme[SchemeNorm].bg); | ||
179 | drw_clr_free(scheme[SchemeNorm].fg); | ||
180 | drw_clr_free(scheme[SchemeSel].fg); | ||
181 | drw_clr_free(scheme[SchemeSel].bg); | ||
182 | drw_clr_free(scheme[SchemeOut].fg); | ||
183 | drw_clr_free(scheme[SchemeOut].bg); | ||
184 | drw_free(drw); | ||
185 | XSync(dpy, False); | ||
186 | XCloseDisplay(dpy); | ||
187 | } | ||
188 | |||
151 | char * | 189 | char * |
152 | cistrstr(const char *s, const char *sub) { | 190 | cistrstr(const char *s, const char *sub) { |
153 | size_t len; | 191 | size_t len; |
@@ -162,50 +200,69 @@ void | |||
162 | drawmenu(void) { | 200 | drawmenu(void) { |
163 | int curpos; | 201 | int curpos; |
164 | Item *item; | 202 | Item *item; |
203 | int x = 0, y = 0, h = bh, w; | ||
165 | 204 | ||
166 | dc->x = 0; | 205 | drw_setscheme(drw, &scheme[SchemeNorm]); |
167 | dc->y = 0; | 206 | drw_rect(drw, 0, 0, mw, mh, True, 1, 1); |
168 | dc->h = bh; | ||
169 | drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol)); | ||
170 | 207 | ||
171 | if(prompt && *prompt) { | 208 | if(prompt && *prompt) { |
172 | dc->w = promptw; | 209 | drw_setscheme(drw, &scheme[SchemeSel]); |
173 | drawtext(dc, prompt, selcol); | 210 | drw_text(drw, x, 0, promptw, bh, prompt, 1); |
174 | dc->x = dc->w; | 211 | x += promptw; |
175 | } | 212 | } |
176 | /* draw input field */ | 213 | /* draw input field */ |
177 | dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw; | 214 | w = (lines > 0 || !matches) ? mw - x : inputw; |
178 | drawtext(dc, text, normcol); | 215 | drw_setscheme(drw, &scheme[SchemeNorm]); |
179 | if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w) | 216 | drw_text(drw, x, 0, w, bh, text, 0); |
180 | drawrect(dc, curpos, 2, 1, dc->h - 4, True, FG(dc, normcol)); | 217 | |
218 | if((curpos = TEXTNW(text, cursor) + bh/2 - 2) < w) { | ||
219 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
220 | drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0); | ||
221 | } | ||
181 | 222 | ||
182 | if(lines > 0) { | 223 | if(lines > 0) { |
183 | /* draw vertical list */ | 224 | /* draw vertical list */ |
184 | dc->w = mw - dc->x; | 225 | w = mw - x; |
185 | for(item = curr; item != next; item = item->right) { | 226 | for(item = curr; item != next; item = item->right) { |
186 | dc->y += dc->h; | 227 | y += h; |
187 | drawtext(dc, item->text, (item == sel) ? selcol : | 228 | if(item == sel) |
188 | (item->out) ? outcol : normcol); | 229 | drw_setscheme(drw, &scheme[SchemeSel]); |
230 | else if(item->out) | ||
231 | drw_setscheme(drw, &scheme[SchemeOut]); | ||
232 | else | ||
233 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
234 | |||
235 | drw_text(drw, x, y, w, bh, item->text, 0); | ||
189 | } | 236 | } |
190 | } | 237 | } |
191 | else if(matches) { | 238 | else if(matches) { |
192 | /* draw horizontal list */ | 239 | /* draw horizontal list */ |
193 | dc->x += inputw; | 240 | x += inputw; |
194 | dc->w = textw(dc, "<"); | 241 | w = TEXTW("<"); |
195 | if(curr->left) | 242 | if(curr->left) { |
196 | drawtext(dc, "<", normcol); | 243 | drw_setscheme(drw, &scheme[SchemeNorm]); |
244 | drw_text(drw, x, 0, w, bh, "<", 0); | ||
245 | } | ||
197 | for(item = curr; item != next; item = item->right) { | 246 | for(item = curr; item != next; item = item->right) { |
198 | dc->x += dc->w; | 247 | x += w; |
199 | dc->w = MIN(textw(dc, item->text), mw - dc->x - textw(dc, ">")); | 248 | w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); |
200 | drawtext(dc, item->text, (item == sel) ? selcol : | 249 | |
201 | (item->out) ? outcol : normcol); | 250 | if(item == sel) |
251 | drw_setscheme(drw, &scheme[SchemeSel]); | ||
252 | else if(item->out) | ||
253 | drw_setscheme(drw, &scheme[SchemeOut]); | ||
254 | else | ||
255 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
256 | drw_text(drw, x, 0, w, bh, item->text, 0); | ||
257 | } | ||
258 | w = TEXTW(">"); | ||
259 | x = mw - w; | ||
260 | if(next) { | ||
261 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
262 | drw_text(drw, x, 0, w, bh, ">", 0); | ||
202 | } | 263 | } |
203 | dc->w = textw(dc, ">"); | ||
204 | dc->x = mw - dc->w; | ||
205 | if(next) | ||
206 | drawtext(dc, ">", normcol); | ||
207 | } | 264 | } |
208 | mapdc(dc, win, mw, mh); | 265 | drw_map(drw, win, 0, 0, mw, mh); |
209 | } | 266 | } |
210 | 267 | ||
211 | void | 268 | void |
@@ -214,12 +271,12 @@ grabkeyboard(void) { | |||
214 | 271 | ||
215 | /* try to grab keyboard, we may have to wait for another process to ungrab */ | 272 | /* try to grab keyboard, we may have to wait for another process to ungrab */ |
216 | for(i = 0; i < 1000; i++) { | 273 | for(i = 0; i < 1000; i++) { |
217 | if(XGrabKeyboard(dc->dpy, DefaultRootWindow(dc->dpy), True, | 274 | if(XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, |
218 | GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) | 275 | GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) |
219 | return; | 276 | return; |
220 | usleep(1000); | 277 | usleep(1000); |
221 | } | 278 | } |
222 | eprintf("cannot grab keyboard\n"); | 279 | die("cannot grab keyboard\n"); |
223 | } | 280 | } |
224 | 281 | ||
225 | void | 282 | void |
@@ -276,14 +333,15 @@ keypress(XKeyEvent *ev) { | |||
276 | insert(NULL, nextrune(-1) - cursor); | 333 | insert(NULL, nextrune(-1) - cursor); |
277 | break; | 334 | break; |
278 | case XK_y: /* paste selection */ | 335 | case XK_y: /* paste selection */ |
279 | XConvertSelection(dc->dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, | 336 | XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, |
280 | utf8, utf8, win, CurrentTime); | 337 | utf8, utf8, win, CurrentTime); |
281 | return; | 338 | return; |
282 | case XK_Return: | 339 | case XK_Return: |
283 | case XK_KP_Enter: | 340 | case XK_KP_Enter: |
284 | break; | 341 | break; |
285 | case XK_bracketleft: | 342 | case XK_bracketleft: |
286 | exit(EXIT_FAILURE); | 343 | cleanup(); |
344 | exit(1); | ||
287 | default: | 345 | default: |
288 | return; | 346 | return; |
289 | } | 347 | } |
@@ -330,7 +388,8 @@ keypress(XKeyEvent *ev) { | |||
330 | sel = matchend; | 388 | sel = matchend; |
331 | break; | 389 | break; |
332 | case XK_Escape: | 390 | case XK_Escape: |
333 | exit(EXIT_FAILURE); | 391 | cleanup(); |
392 | exit(1); | ||
334 | case XK_Home: | 393 | case XK_Home: |
335 | if(sel == matches) { | 394 | if(sel == matches) { |
336 | cursor = 0; | 395 | cursor = 0; |
@@ -368,8 +427,10 @@ keypress(XKeyEvent *ev) { | |||
368 | case XK_Return: | 427 | case XK_Return: |
369 | case XK_KP_Enter: | 428 | case XK_KP_Enter: |
370 | puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); | 429 | puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); |
371 | if(!(ev->state & ControlMask)) | 430 | if(!(ev->state & ControlMask)) { |
372 | exit(EXIT_SUCCESS); | 431 | cleanup(); |
432 | exit(0); | ||
433 | } | ||
373 | if(sel) | 434 | if(sel) |
374 | sel->out = True; | 435 | sel->out = True; |
375 | break; | 436 | break; |
@@ -413,7 +474,7 @@ match(void) { | |||
413 | /* separate input text into tokens to be matched individually */ | 474 | /* separate input text into tokens to be matched individually */ |
414 | for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " ")) | 475 | for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " ")) |
415 | if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) | 476 | if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) |
416 | eprintf("cannot realloc %u bytes\n", tokn * sizeof *tokv); | 477 | die("cannot realloc %u bytes\n", tokn * sizeof *tokv); |
417 | len = tokc ? strlen(tokv[0]) : 0; | 478 | len = tokc ? strlen(tokv[0]) : 0; |
418 | 479 | ||
419 | matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; | 480 | matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; |
@@ -470,7 +531,7 @@ paste(void) { | |||
470 | Atom da; | 531 | Atom da; |
471 | 532 | ||
472 | /* we have been given the current selection, now insert it into input */ | 533 | /* we have been given the current selection, now insert it into input */ |
473 | XGetWindowProperty(dc->dpy, win, utf8, 0, (sizeof text / 4) + 1, False, | 534 | XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, |
474 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | 535 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); |
475 | insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p)); | 536 | insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p)); |
476 | XFree(p); | 537 | XFree(p); |
@@ -486,18 +547,18 @@ readstdin(void) { | |||
486 | for(i = 0; fgets(buf, sizeof buf, stdin); i++) { | 547 | for(i = 0; fgets(buf, sizeof buf, stdin); i++) { |
487 | if(i+1 >= size / sizeof *items) | 548 | if(i+1 >= size / sizeof *items) |
488 | if(!(items = realloc(items, (size += BUFSIZ)))) | 549 | if(!(items = realloc(items, (size += BUFSIZ)))) |
489 | eprintf("cannot realloc %u bytes:", size); | 550 | die("cannot realloc %u bytes:", size); |
490 | if((p = strchr(buf, '\n'))) | 551 | if((p = strchr(buf, '\n'))) |
491 | *p = '\0'; | 552 | *p = '\0'; |
492 | if(!(items[i].text = strdup(buf))) | 553 | if(!(items[i].text = strdup(buf))) |
493 | eprintf("cannot strdup %u bytes:", strlen(buf)+1); | 554 | die("cannot strdup %u bytes:", strlen(buf)+1); |
494 | items[i].out = False; | 555 | items[i].out = False; |
495 | if(strlen(items[i].text) > max) | 556 | if(strlen(items[i].text) > max) |
496 | max = strlen(maxstr = items[i].text); | 557 | max = strlen(maxstr = items[i].text); |
497 | } | 558 | } |
498 | if(items) | 559 | if(items) |
499 | items[i].text = NULL; | 560 | items[i].text = NULL; |
500 | inputw = maxstr ? textw(dc, maxstr) : 0; | 561 | inputw = maxstr ? TEXTW(maxstr) : 0; |
501 | lines = MIN(lines, i); | 562 | lines = MIN(lines, i); |
502 | } | 563 | } |
503 | 564 | ||
@@ -505,13 +566,13 @@ void | |||
505 | run(void) { | 566 | run(void) { |
506 | XEvent ev; | 567 | XEvent ev; |
507 | 568 | ||
508 | while(!XNextEvent(dc->dpy, &ev)) { | 569 | while(!XNextEvent(dpy, &ev)) { |
509 | if(XFilterEvent(&ev, win)) | 570 | if(XFilterEvent(&ev, win)) |
510 | continue; | 571 | continue; |
511 | switch(ev.type) { | 572 | switch(ev.type) { |
512 | case Expose: | 573 | case Expose: |
513 | if(ev.xexpose.count == 0) | 574 | if(ev.xexpose.count == 0) |
514 | mapdc(dc, win, mw, mh); | 575 | drw_map(drw, win, 0, 0, mw, mh); |
515 | break; | 576 | break; |
516 | case KeyPress: | 577 | case KeyPress: |
517 | keypress(&ev.xkey); | 578 | keypress(&ev.xkey); |
@@ -522,7 +583,7 @@ run(void) { | |||
522 | break; | 583 | break; |
523 | case VisibilityNotify: | 584 | case VisibilityNotify: |
524 | if(ev.xvisibility.state != VisibilityUnobscured) | 585 | if(ev.xvisibility.state != VisibilityUnobscured) |
525 | XRaiseWindow(dc->dpy, win); | 586 | XRaiseWindow(dpy, win); |
526 | break; | 587 | break; |
527 | } | 588 | } |
528 | } | 589 | } |
@@ -530,47 +591,45 @@ run(void) { | |||
530 | 591 | ||
531 | void | 592 | void |
532 | setup(void) { | 593 | setup(void) { |
533 | int x, y, screen = DefaultScreen(dc->dpy); | 594 | int x, y; |
534 | Window root = RootWindow(dc->dpy, screen); | ||
535 | XSetWindowAttributes swa; | 595 | XSetWindowAttributes swa; |
536 | XIM xim; | 596 | XIM xim; |
537 | #ifdef XINERAMA | 597 | #ifdef XINERAMA |
538 | int n; | ||
539 | XineramaScreenInfo *info; | 598 | XineramaScreenInfo *info; |
599 | Window w, pw, dw, *dws; | ||
600 | XWindowAttributes wa; | ||
601 | int a, j, di, n, i = 0, area = 0; | ||
602 | unsigned int du; | ||
540 | #endif | 603 | #endif |
541 | 604 | ||
542 | normcol[ColBG] = getcolor(dc, normbgcolor); | 605 | /* init appearance */ |
543 | normcol[ColFG] = getcolor(dc, normfgcolor); | 606 | scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor); |
544 | selcol[ColBG] = getcolor(dc, selbgcolor); | 607 | scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor); |
545 | selcol[ColFG] = getcolor(dc, selfgcolor); | 608 | scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor); |
546 | outcol[ColBG] = getcolor(dc, outbgcolor); | 609 | scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor); |
547 | outcol[ColFG] = getcolor(dc, outfgcolor); | 610 | scheme[SchemeOut].bg = drw_clr_create(drw, outbgcolor); |
611 | scheme[SchemeOut].fg = drw_clr_create(drw, outfgcolor); | ||
548 | 612 | ||
549 | clip = XInternAtom(dc->dpy, "CLIPBOARD", False); | 613 | clip = XInternAtom(dpy, "CLIPBOARD", False); |
550 | utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); | 614 | utf8 = XInternAtom(dpy, "UTF8_STRING", False); |
551 | 615 | ||
552 | /* calculate menu geometry */ | 616 | /* calculate menu geometry */ |
553 | bh = dc->font.height + 2; | 617 | bh = drw->fonts[0]->h + 2; |
554 | lines = MAX(lines, 0); | 618 | lines = MAX(lines, 0); |
555 | mh = (lines + 1) * bh; | 619 | mh = (lines + 1) * bh; |
556 | #ifdef XINERAMA | 620 | #ifdef XINERAMA |
557 | if((info = XineramaQueryScreens(dc->dpy, &n))) { | 621 | if((info = XineramaQueryScreens(dpy, &n))) { |
558 | int a, j, di, i = 0, area = 0; | 622 | XGetInputFocus(dpy, &w, &di); |
559 | unsigned int du; | ||
560 | Window w, pw, dw, *dws; | ||
561 | XWindowAttributes wa; | ||
562 | |||
563 | XGetInputFocus(dc->dpy, &w, &di); | ||
564 | if(mon != -1 && mon < n) | 623 | if(mon != -1 && mon < n) |
565 | i = mon; | 624 | i = mon; |
566 | if(!i && w != root && w != PointerRoot && w != None) { | 625 | if(!i && w != root && w != PointerRoot && w != None) { |
567 | /* find top-level window containing current input focus */ | 626 | /* find top-level window containing current input focus */ |
568 | do { | 627 | do { |
569 | if(XQueryTree(dc->dpy, (pw = w), &dw, &w, &dws, &du) && dws) | 628 | if(XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) |
570 | XFree(dws); | 629 | XFree(dws); |
571 | } while(w != root && w != pw); | 630 | } while(w != root && w != pw); |
572 | /* find xinerama screen with which the window intersects most */ | 631 | /* find xinerama screen with which the window intersects most */ |
573 | if(XGetWindowAttributes(dc->dpy, pw, &wa)) | 632 | if(XGetWindowAttributes(dpy, pw, &wa)) |
574 | for(j = 0; j < n; j++) | 633 | for(j = 0; j < n; j++) |
575 | if((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { | 634 | if((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { |
576 | area = a; | 635 | area = a; |
@@ -578,7 +637,7 @@ setup(void) { | |||
578 | } | 637 | } |
579 | } | 638 | } |
580 | /* no focused window is on screen, so use pointer location instead */ | 639 | /* no focused window is on screen, so use pointer location instead */ |
581 | if(mon == -1 && !area && XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) | 640 | if(mon == -1 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) |
582 | for(i = 0; i < n; i++) | 641 | for(i = 0; i < n; i++) |
583 | if(INTERSECT(x, y, 1, 1, info[i])) | 642 | if(INTERSECT(x, y, 1, 1, info[i])) |
584 | break; | 643 | break; |
@@ -592,29 +651,29 @@ setup(void) { | |||
592 | #endif | 651 | #endif |
593 | { | 652 | { |
594 | x = 0; | 653 | x = 0; |
595 | y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh; | 654 | y = topbar ? 0 : sh - mh; |
596 | mw = DisplayWidth(dc->dpy, screen); | 655 | mw = sw; |
597 | } | 656 | } |
598 | promptw = (prompt && *prompt) ? textw(dc, prompt) : 0; | 657 | promptw = (prompt && *prompt) ? TEXTW(prompt) : 0; |
599 | inputw = MIN(inputw, mw/3); | 658 | inputw = MIN(inputw, mw/3); |
600 | match(); | 659 | match(); |
601 | 660 | ||
602 | /* create menu window */ | 661 | /* create menu window */ |
603 | swa.override_redirect = True; | 662 | swa.override_redirect = True; |
604 | swa.background_pixel = normcol[ColBG]; | 663 | swa.background_pixel = scheme[SchemeNorm].bg->pix; |
605 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | 664 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; |
606 | win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0, | 665 | win = XCreateWindow(dpy, root, x, y, mw, mh, 0, |
607 | DefaultDepth(dc->dpy, screen), CopyFromParent, | 666 | DefaultDepth(dpy, screen), CopyFromParent, |
608 | DefaultVisual(dc->dpy, screen), | 667 | DefaultVisual(dpy, screen), |
609 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); | 668 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); |
610 | 669 | ||
611 | /* open input methods */ | 670 | /* open input methods */ |
612 | xim = XOpenIM(dc->dpy, NULL, NULL, NULL); | 671 | xim = XOpenIM(dpy, NULL, NULL, NULL); |
613 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, | 672 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, |
614 | XNClientWindow, win, XNFocusWindow, win, NULL); | 673 | XNClientWindow, win, XNFocusWindow, win, NULL); |
615 | 674 | ||
616 | XMapRaised(dc->dpy, win); | 675 | XMapRaised(dpy, win); |
617 | resizedc(dc, mw, mh); | 676 | drw_resize(drw, mw, mh); |
618 | drawmenu(); | 677 | drawmenu(); |
619 | } | 678 | } |
620 | 679 | ||
@@ -622,5 +681,5 @@ void | |||
622 | usage(void) { | 681 | usage(void) { |
623 | fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" | 682 | fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" |
624 | " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); | 683 | " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); |
625 | exit(EXIT_FAILURE); | 684 | exit(1); |
626 | } | 685 | } |
@@ -1,177 +0,0 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | #include <locale.h> | ||
3 | #include <stdarg.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <X11/Xlib.h> | ||
8 | #include "draw.h" | ||
9 | |||
10 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | ||
11 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
12 | #define DEFAULTFN "fixed" | ||
13 | |||
14 | static Bool loadfont(DC *dc, const char *fontstr); | ||
15 | |||
16 | void | ||
17 | drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) { | ||
18 | XSetForeground(dc->dpy, dc->gc, color); | ||
19 | if(fill) | ||
20 | XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w, h); | ||
21 | else | ||
22 | XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w-1, h-1); | ||
23 | } | ||
24 | |||
25 | void | ||
26 | drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { | ||
27 | char buf[BUFSIZ]; | ||
28 | size_t mn, n = strlen(text); | ||
29 | |||
30 | /* shorten text if necessary */ | ||
31 | for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) + dc->font.height/2 > dc->w; mn--) | ||
32 | if(mn == 0) | ||
33 | return; | ||
34 | memcpy(buf, text, mn); | ||
35 | if(mn < n) | ||
36 | for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.'); | ||
37 | |||
38 | drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col)); | ||
39 | drawtextn(dc, buf, mn, col); | ||
40 | } | ||
41 | |||
42 | void | ||
43 | drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) { | ||
44 | int x = dc->x + dc->font.height/2; | ||
45 | int y = dc->y + dc->font.ascent+1; | ||
46 | |||
47 | XSetForeground(dc->dpy, dc->gc, FG(dc, col)); | ||
48 | if(dc->font.set) | ||
49 | XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n); | ||
50 | else { | ||
51 | XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); | ||
52 | XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | void | ||
57 | eprintf(const char *fmt, ...) { | ||
58 | va_list ap; | ||
59 | |||
60 | va_start(ap, fmt); | ||
61 | vfprintf(stderr, fmt, ap); | ||
62 | va_end(ap); | ||
63 | |||
64 | if(fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { | ||
65 | fputc(' ', stderr); | ||
66 | perror(NULL); | ||
67 | } | ||
68 | exit(EXIT_FAILURE); | ||
69 | } | ||
70 | |||
71 | void | ||
72 | freedc(DC *dc) { | ||
73 | if(dc->font.set) | ||
74 | XFreeFontSet(dc->dpy, dc->font.set); | ||
75 | if(dc->font.xfont) | ||
76 | XFreeFont(dc->dpy, dc->font.xfont); | ||
77 | if(dc->canvas) | ||
78 | XFreePixmap(dc->dpy, dc->canvas); | ||
79 | XFreeGC(dc->dpy, dc->gc); | ||
80 | XCloseDisplay(dc->dpy); | ||
81 | free(dc); | ||
82 | } | ||
83 | |||
84 | unsigned long | ||
85 | getcolor(DC *dc, const char *colstr) { | ||
86 | Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)); | ||
87 | XColor color; | ||
88 | |||
89 | if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color)) | ||
90 | eprintf("cannot allocate color '%s'\n", colstr); | ||
91 | return color.pixel; | ||
92 | } | ||
93 | |||
94 | DC * | ||
95 | initdc(void) { | ||
96 | DC *dc; | ||
97 | |||
98 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
99 | fputs("no locale support\n", stderr); | ||
100 | if(!(dc = calloc(1, sizeof *dc))) | ||
101 | eprintf("cannot malloc %u bytes:", sizeof *dc); | ||
102 | if(!(dc->dpy = XOpenDisplay(NULL))) | ||
103 | eprintf("cannot open display\n"); | ||
104 | |||
105 | dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL); | ||
106 | XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter); | ||
107 | return dc; | ||
108 | } | ||
109 | |||
110 | void | ||
111 | initfont(DC *dc, const char *fontstr) { | ||
112 | if(!loadfont(dc, fontstr ? fontstr : DEFAULTFN)) { | ||
113 | if(fontstr != NULL) | ||
114 | fprintf(stderr, "cannot load font '%s'\n", fontstr); | ||
115 | if(fontstr == NULL || !loadfont(dc, DEFAULTFN)) | ||
116 | eprintf("cannot load font '%s'\n", DEFAULTFN); | ||
117 | } | ||
118 | dc->font.height = dc->font.ascent + dc->font.descent; | ||
119 | } | ||
120 | |||
121 | Bool | ||
122 | loadfont(DC *dc, const char *fontstr) { | ||
123 | char *def, **missing, **names; | ||
124 | int i, n; | ||
125 | XFontStruct **xfonts; | ||
126 | |||
127 | if(!*fontstr) | ||
128 | return False; | ||
129 | if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { | ||
130 | n = XFontsOfFontSet(dc->font.set, &xfonts, &names); | ||
131 | for(i = 0; i < n; i++) { | ||
132 | dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); | ||
133 | dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); | ||
134 | dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width); | ||
135 | } | ||
136 | } | ||
137 | else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { | ||
138 | dc->font.ascent = dc->font.xfont->ascent; | ||
139 | dc->font.descent = dc->font.xfont->descent; | ||
140 | dc->font.width = dc->font.xfont->max_bounds.width; | ||
141 | } | ||
142 | if(missing) | ||
143 | XFreeStringList(missing); | ||
144 | return dc->font.set || dc->font.xfont; | ||
145 | } | ||
146 | |||
147 | void | ||
148 | mapdc(DC *dc, Window win, unsigned int w, unsigned int h) { | ||
149 | XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0); | ||
150 | } | ||
151 | |||
152 | void | ||
153 | resizedc(DC *dc, unsigned int w, unsigned int h) { | ||
154 | if(dc->canvas) | ||
155 | XFreePixmap(dc->dpy, dc->canvas); | ||
156 | |||
157 | dc->w = w; | ||
158 | dc->h = h; | ||
159 | dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h, | ||
160 | DefaultDepth(dc->dpy, DefaultScreen(dc->dpy))); | ||
161 | } | ||
162 | |||
163 | int | ||
164 | textnw(DC *dc, const char *text, size_t len) { | ||
165 | if(dc->font.set) { | ||
166 | XRectangle r; | ||
167 | |||
168 | XmbTextExtents(dc->font.set, text, len, NULL, &r); | ||
169 | return r.width; | ||
170 | } | ||
171 | return XTextWidth(dc->font.xfont, text, len); | ||
172 | } | ||
173 | |||
174 | int | ||
175 | textw(DC *dc, const char *text) { | ||
176 | return textnw(dc, text, strlen(text)) + dc->font.height; | ||
177 | } | ||
@@ -1,35 +0,0 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | |||
3 | #define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG]) | ||
4 | #define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG]) | ||
5 | |||
6 | enum { ColBG, ColFG, ColBorder, ColLast }; | ||
7 | |||
8 | typedef struct { | ||
9 | int x, y, w, h; | ||
10 | Bool invert; | ||
11 | Display *dpy; | ||
12 | GC gc; | ||
13 | Pixmap canvas; | ||
14 | struct { | ||
15 | int ascent; | ||
16 | int descent; | ||
17 | int height; | ||
18 | int width; | ||
19 | XFontSet set; | ||
20 | XFontStruct *xfont; | ||
21 | } font; | ||
22 | } DC; /* draw context */ | ||
23 | |||
24 | void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color); | ||
25 | void drawtext(DC *dc, const char *text, unsigned long col[ColLast]); | ||
26 | void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]); | ||
27 | void eprintf(const char *fmt, ...); | ||
28 | void freedc(DC *dc); | ||
29 | unsigned long getcolor(DC *dc, const char *colstr); | ||
30 | DC *initdc(void); | ||
31 | void initfont(DC *dc, const char *fontstr); | ||
32 | void mapdc(DC *dc, Window win, unsigned int w, unsigned int h); | ||
33 | void resizedc(DC *dc, unsigned int w, unsigned int h); | ||
34 | int textnw(DC *dc, const char *text, size_t len); | ||
35 | int textw(DC *dc, const char *text); | ||
@@ -0,0 +1,413 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <X11/Xlib.h> | ||
6 | #include <X11/Xft/Xft.h> | ||
7 | |||
8 | #include "drw.h" | ||
9 | #include "util.h" | ||
10 | |||
11 | #define UTF_INVALID 0xFFFD | ||
12 | #define UTF_SIZ 4 | ||
13 | |||
14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; | ||
15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | ||
16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; | ||
17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | ||
18 | |||
19 | static long | ||
20 | utf8decodebyte(const char c, size_t *i) { | ||
21 | for(*i = 0; *i < (UTF_SIZ + 1); ++(*i)) | ||
22 | if(((unsigned char)c & utfmask[*i]) == utfbyte[*i]) | ||
23 | return (unsigned char)c & ~utfmask[*i]; | ||
24 | return 0; | ||
25 | } | ||
26 | |||
27 | static size_t | ||
28 | utf8validate(long *u, size_t i) { | ||
29 | if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) | ||
30 | *u = UTF_INVALID; | ||
31 | for(i = 1; *u > utfmax[i]; ++i) | ||
32 | ; | ||
33 | return i; | ||
34 | } | ||
35 | |||
36 | static size_t | ||
37 | utf8decode(const char *c, long *u, size_t clen) { | ||
38 | size_t i, j, len, type; | ||
39 | long udecoded; | ||
40 | |||
41 | *u = UTF_INVALID; | ||
42 | if(!clen) | ||
43 | return 0; | ||
44 | udecoded = utf8decodebyte(c[0], &len); | ||
45 | if(!BETWEEN(len, 1, UTF_SIZ)) | ||
46 | return 1; | ||
47 | for(i = 1, j = 1; i < clen && j < len; ++i, ++j) { | ||
48 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | ||
49 | if(type != 0) | ||
50 | return j; | ||
51 | } | ||
52 | if(j < len) | ||
53 | return 0; | ||
54 | *u = udecoded; | ||
55 | utf8validate(u, len); | ||
56 | return len; | ||
57 | } | ||
58 | |||
59 | Drw * | ||
60 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) { | ||
61 | Drw *drw = (Drw *)calloc(1, sizeof(Drw)); | ||
62 | if(!drw) | ||
63 | return NULL; | ||
64 | drw->dpy = dpy; | ||
65 | drw->screen = screen; | ||
66 | drw->root = root; | ||
67 | drw->w = w; | ||
68 | drw->h = h; | ||
69 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); | ||
70 | drw->gc = XCreateGC(dpy, root, 0, NULL); | ||
71 | drw->fontcount = 0; | ||
72 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | ||
73 | return drw; | ||
74 | } | ||
75 | |||
76 | void | ||
77 | drw_resize(Drw *drw, unsigned int w, unsigned int h) { | ||
78 | if(!drw) | ||
79 | return; | ||
80 | drw->w = w; | ||
81 | drw->h = h; | ||
82 | if(drw->drawable != 0) | ||
83 | XFreePixmap(drw->dpy, drw->drawable); | ||
84 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); | ||
85 | } | ||
86 | |||
87 | void | ||
88 | drw_free(Drw *drw) { | ||
89 | size_t i; | ||
90 | |||
91 | for (i = 0; i < drw->fontcount; i++) { | ||
92 | drw_font_free(drw->fonts[i]); | ||
93 | } | ||
94 | XFreePixmap(drw->dpy, drw->drawable); | ||
95 | XFreeGC(drw->dpy, drw->gc); | ||
96 | free(drw); | ||
97 | } | ||
98 | |||
99 | /* This function is an implementation detail. Library users should use | ||
100 | * drw_font_create instead. | ||
101 | */ | ||
102 | static Fnt * | ||
103 | drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) { | ||
104 | Fnt *font; | ||
105 | |||
106 | if (!(fontname || fontpattern)) | ||
107 | die("No font specified.\n"); | ||
108 | |||
109 | if (!(font = (Fnt *)calloc(1, sizeof(Fnt)))) | ||
110 | return NULL; | ||
111 | |||
112 | if (fontname) { | ||
113 | /* Using the pattern found at font->xfont->pattern does not yield same | ||
114 | * the same substitution results as using the pattern returned by | ||
115 | * FcNameParse; using the latter results in the desired fallback | ||
116 | * behaviour whereas the former just results in | ||
117 | * missing-character-rectangles being drawn, at least with some fonts. | ||
118 | */ | ||
119 | if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) || | ||
120 | !(font->pattern = FcNameParse((FcChar8 *) fontname))) { | ||
121 | if (font->xfont) { | ||
122 | XftFontClose(drw->dpy, font->xfont); | ||
123 | font->xfont = NULL; | ||
124 | } | ||
125 | fprintf(stderr, "error, cannot load font: '%s'\n", fontname); | ||
126 | } | ||
127 | } else if (fontpattern) { | ||
128 | if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { | ||
129 | fprintf(stderr, "error, cannot load font pattern.\n"); | ||
130 | } else { | ||
131 | font->pattern = NULL; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | if (!font->xfont) { | ||
136 | free(font); | ||
137 | return NULL; | ||
138 | } | ||
139 | |||
140 | font->ascent = font->xfont->ascent; | ||
141 | font->descent = font->xfont->descent; | ||
142 | font->h = font->ascent + font->descent; | ||
143 | font->dpy = drw->dpy; | ||
144 | return font; | ||
145 | } | ||
146 | |||
147 | Fnt* | ||
148 | drw_font_create(Drw *drw, const char *fontname) { | ||
149 | return drw_font_xcreate(drw, fontname, NULL); | ||
150 | } | ||
151 | |||
152 | void | ||
153 | drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) { | ||
154 | size_t i; | ||
155 | Fnt *font; | ||
156 | |||
157 | for (i = 0; i < fontcount; i++) { | ||
158 | if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { | ||
159 | die("Font cache exhausted.\n"); | ||
160 | } else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) { | ||
161 | drw->fonts[drw->fontcount++] = font; | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | void | ||
167 | drw_font_free(Fnt *font) { | ||
168 | if(!font) | ||
169 | return; | ||
170 | if(font->pattern) | ||
171 | FcPatternDestroy(font->pattern); | ||
172 | XftFontClose(font->dpy, font->xfont); | ||
173 | free(font); | ||
174 | } | ||
175 | |||
176 | Clr * | ||
177 | drw_clr_create(Drw *drw, const char *clrname) { | ||
178 | Clr *clr; | ||
179 | Colormap cmap; | ||
180 | Visual *vis; | ||
181 | |||
182 | if(!drw) | ||
183 | return NULL; | ||
184 | clr = (Clr *)calloc(1, sizeof(Clr)); | ||
185 | if(!clr) | ||
186 | return NULL; | ||
187 | cmap = DefaultColormap(drw->dpy, drw->screen); | ||
188 | vis = DefaultVisual(drw->dpy, drw->screen); | ||
189 | if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb)) | ||
190 | die("error, cannot allocate color '%s'\n", clrname); | ||
191 | clr->pix = clr->rgb.pixel; | ||
192 | return clr; | ||
193 | } | ||
194 | |||
195 | void | ||
196 | drw_clr_free(Clr *clr) { | ||
197 | free(clr); | ||
198 | } | ||
199 | |||
200 | void | ||
201 | drw_setscheme(Drw *drw, ClrScheme *scheme) { | ||
202 | if(!drw) | ||
203 | return; | ||
204 | drw->scheme = scheme; | ||
205 | } | ||
206 | |||
207 | void | ||
208 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) { | ||
209 | if(!drw || !drw->scheme) | ||
210 | return; | ||
211 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix); | ||
212 | if(filled) | ||
213 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1); | ||
214 | else if(empty) | ||
215 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
216 | } | ||
217 | |||
218 | int | ||
219 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) { | ||
220 | char buf[1024]; | ||
221 | int tx, ty, th; | ||
222 | Extnts tex; | ||
223 | Colormap cmap; | ||
224 | Visual *vis; | ||
225 | XftDraw *d; | ||
226 | Fnt *curfont, *nextfont; | ||
227 | size_t i, len; | ||
228 | int utf8strlen, utf8charlen, render; | ||
229 | long utf8codepoint = 0; | ||
230 | const char *utf8str; | ||
231 | FcCharSet *fccharset; | ||
232 | FcPattern *fcpattern; | ||
233 | FcPattern *match; | ||
234 | XftResult result; | ||
235 | int charexists = 0; | ||
236 | |||
237 | if (!(render = x || y || w || h)) { | ||
238 | w = ~w; | ||
239 | } | ||
240 | |||
241 | if (!drw || !drw->scheme) { | ||
242 | return 0; | ||
243 | } else if (render) { | ||
244 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix); | ||
245 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
246 | } | ||
247 | |||
248 | if (!text || !drw->fontcount) { | ||
249 | return 0; | ||
250 | } else if (render) { | ||
251 | cmap = DefaultColormap(drw->dpy, drw->screen); | ||
252 | vis = DefaultVisual(drw->dpy, drw->screen); | ||
253 | d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap); | ||
254 | } | ||
255 | |||
256 | curfont = drw->fonts[0]; | ||
257 | while (1) { | ||
258 | utf8strlen = 0; | ||
259 | utf8str = text; | ||
260 | nextfont = NULL; | ||
261 | while (*text) { | ||
262 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); | ||
263 | for (i = 0; i < drw->fontcount; i++) { | ||
264 | charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); | ||
265 | if (charexists) { | ||
266 | if (drw->fonts[i] == curfont) { | ||
267 | utf8strlen += utf8charlen; | ||
268 | text += utf8charlen; | ||
269 | } else { | ||
270 | nextfont = drw->fonts[i]; | ||
271 | } | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | if (!charexists || (nextfont && nextfont != curfont)) { | ||
277 | break; | ||
278 | } else { | ||
279 | charexists = 0; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | if (utf8strlen) { | ||
284 | drw_font_getexts(curfont, utf8str, utf8strlen, &tex); | ||
285 | /* shorten text if necessary */ | ||
286 | for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--) | ||
287 | drw_font_getexts(curfont, utf8str, len, &tex); | ||
288 | |||
289 | if (len) { | ||
290 | memcpy(buf, utf8str, len); | ||
291 | buf[len] = '\0'; | ||
292 | if(len < utf8strlen) | ||
293 | for(i = len; i && i > len - 3; buf[--i] = '.'); | ||
294 | |||
295 | if (render) { | ||
296 | th = curfont->ascent + curfont->descent; | ||
297 | ty = y + (h / 2) - (th / 2) + curfont->ascent; | ||
298 | tx = x + (h / 2); | ||
299 | XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); | ||
300 | } | ||
301 | |||
302 | x += tex.w; | ||
303 | w -= tex.w; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | if (!*text) { | ||
308 | break; | ||
309 | } else if (nextfont) { | ||
310 | charexists = 0; | ||
311 | curfont = nextfont; | ||
312 | } else { | ||
313 | /* Regardless of whether or not a fallback font is found, the | ||
314 | * character must be drawn. | ||
315 | */ | ||
316 | charexists = 1; | ||
317 | |||
318 | if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { | ||
319 | continue; | ||
320 | } | ||
321 | |||
322 | fccharset = FcCharSetCreate(); | ||
323 | FcCharSetAddChar(fccharset, utf8codepoint); | ||
324 | |||
325 | if (!drw->fonts[0]->pattern) { | ||
326 | /* Refer to the comment in drw_font_xcreate for more | ||
327 | * information. | ||
328 | */ | ||
329 | die("The first font in the cache must be loaded from a font string.\n"); | ||
330 | } | ||
331 | |||
332 | fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); | ||
333 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); | ||
334 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); | ||
335 | |||
336 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); | ||
337 | FcDefaultSubstitute(fcpattern); | ||
338 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); | ||
339 | |||
340 | FcCharSetDestroy(fccharset); | ||
341 | FcPatternDestroy(fcpattern); | ||
342 | |||
343 | if (match) { | ||
344 | curfont = drw_font_xcreate(drw, NULL, match); | ||
345 | if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { | ||
346 | drw->fonts[drw->fontcount++] = curfont; | ||
347 | } else { | ||
348 | if (curfont) { | ||
349 | drw_font_free(curfont); | ||
350 | } | ||
351 | curfont = drw->fonts[0]; | ||
352 | } | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | if (render) { | ||
358 | XftDrawDestroy(d); | ||
359 | } | ||
360 | |||
361 | return x; | ||
362 | } | ||
363 | |||
364 | void | ||
365 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { | ||
366 | if(!drw) | ||
367 | return; | ||
368 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); | ||
369 | XSync(drw->dpy, False); | ||
370 | } | ||
371 | |||
372 | |||
373 | void | ||
374 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) { | ||
375 | XGlyphInfo ext; | ||
376 | |||
377 | if(!font || !text) | ||
378 | return; | ||
379 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); | ||
380 | tex->h = font->h; | ||
381 | tex->w = ext.xOff; | ||
382 | } | ||
383 | |||
384 | unsigned int | ||
385 | drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) { | ||
386 | Extnts tex; | ||
387 | |||
388 | if(!font) | ||
389 | return -1; | ||
390 | drw_font_getexts(font, text, len, &tex); | ||
391 | return tex.w; | ||
392 | } | ||
393 | |||
394 | Cur * | ||
395 | drw_cur_create(Drw *drw, int shape) { | ||
396 | Cur *cur; | ||
397 | |||
398 | if(!drw) | ||
399 | return NULL; | ||
400 | cur = (Cur *)calloc(1, sizeof(Cur)); | ||
401 | if (!cur) | ||
402 | return NULL; | ||
403 | cur->cursor = XCreateFontCursor(drw->dpy, shape); | ||
404 | return cur; | ||
405 | } | ||
406 | |||
407 | void | ||
408 | drw_cur_free(Drw *drw, Cur *cursor) { | ||
409 | if(!drw || !cursor) | ||
410 | return; | ||
411 | XFreeCursor(drw->dpy, cursor->cursor); | ||
412 | free(cursor); | ||
413 | } | ||
@@ -0,0 +1,74 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | #define DRW_FONT_CACHE_SIZE 32 | ||
3 | |||
4 | typedef struct { | ||
5 | unsigned long pix; | ||
6 | XftColor rgb; | ||
7 | } Clr; | ||
8 | |||
9 | typedef struct { | ||
10 | Cursor cursor; | ||
11 | } Cur; | ||
12 | |||
13 | typedef struct { | ||
14 | Display *dpy; | ||
15 | int ascent; | ||
16 | int descent; | ||
17 | unsigned int h; | ||
18 | XftFont *xfont; | ||
19 | FcPattern *pattern; | ||
20 | } Fnt; | ||
21 | |||
22 | typedef struct { | ||
23 | Clr *fg; | ||
24 | Clr *bg; | ||
25 | Clr *border; | ||
26 | } ClrScheme; | ||
27 | |||
28 | typedef struct { | ||
29 | unsigned int w, h; | ||
30 | Display *dpy; | ||
31 | int screen; | ||
32 | Window root; | ||
33 | Drawable drawable; | ||
34 | GC gc; | ||
35 | ClrScheme *scheme; | ||
36 | size_t fontcount; | ||
37 | Fnt *fonts[DRW_FONT_CACHE_SIZE]; | ||
38 | } Drw; | ||
39 | |||
40 | typedef struct { | ||
41 | unsigned int w; | ||
42 | unsigned int h; | ||
43 | } Extnts; | ||
44 | |||
45 | /* Drawable abstraction */ | ||
46 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); | ||
47 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); | ||
48 | void drw_free(Drw *drw); | ||
49 | |||
50 | /* Fnt abstraction */ | ||
51 | Fnt *drw_font_create(Drw *drw, const char *fontname); | ||
52 | void drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount); | ||
53 | void drw_font_free(Fnt *font); | ||
54 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *extnts); | ||
55 | unsigned int drw_font_getexts_width(Fnt *font, const char *text, unsigned int len); | ||
56 | |||
57 | /* Colour abstraction */ | ||
58 | Clr *drw_clr_create(Drw *drw, const char *clrname); | ||
59 | void drw_clr_free(Clr *clr); | ||
60 | |||
61 | /* Cursor abstraction */ | ||
62 | Cur *drw_cur_create(Drw *drw, int shape); | ||
63 | void drw_cur_free(Drw *drw, Cur *cursor); | ||
64 | |||
65 | /* Drawing context manipulation */ | ||
66 | void drw_setfont(Drw *drw, Fnt *font); | ||
67 | void drw_setscheme(Drw *drw, ClrScheme *scheme); | ||
68 | |||
69 | /* Drawing functions */ | ||
70 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert); | ||
71 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert); | ||
72 | |||
73 | /* Map functions */ | ||
74 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); | ||
@@ -0,0 +1,23 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | #include <stdarg.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | |||
7 | #include "util.h" | ||
8 | |||
9 | void | ||
10 | die(const char *fmt, ...) { | ||
11 | va_list ap; | ||
12 | |||
13 | va_start(ap, fmt); | ||
14 | vfprintf(stderr, fmt, ap); | ||
15 | va_end(ap); | ||
16 | |||
17 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { | ||
18 | fputc(' ', stderr); | ||
19 | perror(NULL); | ||
20 | } | ||
21 | |||
22 | exit(1); | ||
23 | } | ||
@@ -0,0 +1,7 @@ | |||
1 | /* See LICENSE file for copyright and license details. */ | ||
2 | |||
3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) | ||
4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) | ||
5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) | ||
6 | |||
7 | void die(const char *errstr, ...); | ||