aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile17
-rw-r--r--dinput.c387
-rw-r--r--dmenu.c219
-rw-r--r--draw.c143
4 files changed, 561 insertions, 205 deletions
diff --git a/Makefile b/Makefile
index 5ea6729..d5884da 100644
--- a/Makefile
+++ b/Makefile
@@ -3,10 +3,10 @@
3 3
4include config.mk 4include config.mk
5 5
6SRC = dmenu.c 6SRC = dinput.c dmenu.c draw.c
7OBJ = ${SRC:.c=.o} 7OBJ = ${SRC:.c=.o}
8 8
9all: options dmenu 9all: options dinput dmenu
10 10
11options: 11options:
12 @echo dmenu build options: 12 @echo dmenu build options:
@@ -18,19 +18,19 @@ options:
18 @echo CC $< 18 @echo CC $<
19 @${CC} -c ${CFLAGS} $< 19 @${CC} -c ${CFLAGS} $<
20 20
21${OBJ}: config.h config.mk 21${OBJ}: config.h config.mk draw.c
22 22
23config.h: 23config.h:
24 @echo creating $@ from config.def.h 24 @echo creating $@ from config.def.h
25 @cp config.def.h $@ 25 @cp config.def.h $@
26 26
27dmenu: ${OBJ} 27.o:
28 @echo CC -o $@ 28 @echo CC -o $@
29 @${CC} -o $@ ${OBJ} ${LDFLAGS} 29 @${CC} -o $@ $< ${LDFLAGS}
30 30
31clean: 31clean:
32 @echo cleaning 32 @echo cleaning
33 @rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz 33 @rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz
34 34
35dist: clean 35dist: clean
36 @echo creating dist tarball 36 @echo creating dist tarball
@@ -43,7 +43,8 @@ dist: clean
43install: all 43install: all
44 @echo installing executable file to ${DESTDIR}${PREFIX}/bin 44 @echo installing executable file to ${DESTDIR}${PREFIX}/bin
45 @mkdir -p ${DESTDIR}${PREFIX}/bin 45 @mkdir -p ${DESTDIR}${PREFIX}/bin
46 @cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin 46 @cp -f dinput dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin
47 @chmod 755 ${DESTDIR}${PREFIX}/bin/dinput
47 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu 48 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
48 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path 49 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
49 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run 50 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
@@ -55,7 +56,7 @@ install: all
55uninstall: 56uninstall:
56 @echo removing executable file from ${DESTDIR}${PREFIX}/bin 57 @echo removing executable file from ${DESTDIR}${PREFIX}/bin
57 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path 58 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path
58 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_run 59 @rm -f ${DESTDIR}${PREFIX}/bin/dinput ${DESTDIR}${PREFIX}/bin/dmenu_run
59 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 60 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
60 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 61 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
61 62
diff --git a/dinput.c b/dinput.c
new file mode 100644
index 0000000..490380d
--- /dev/null
+++ b/dinput.c
@@ -0,0 +1,387 @@
1/* See LICENSE file for copyright and license details. */
2#include <ctype.h>
3#include <locale.h>
4#include <stdarg.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <strings.h>
9#include <unistd.h>
10#include <X11/keysym.h>
11#include <X11/Xlib.h>
12#include <X11/Xutil.h>
13#ifdef XINERAMA
14#include <X11/extensions/Xinerama.h>
15#endif
16
17/* macros */
18#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
19#define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH))
20#define MIN(a, b) ((a) < (b) ? (a) : (b))
21#define MAX(a, b) ((a) > (b) ? (a) : (b))
22#define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80))
23
24/* forward declarations */
25static void cleanup(void);
26static void drawcursor(void);
27static void drawinput(void);
28static void eprint(const char *errstr, ...);
29static Bool grabkeyboard(void);
30static void kpress(XKeyEvent * e);
31static void run(void);
32static void setup(Bool topbar);
33
34#include "config.h"
35
36/* variables */
37static char *prompt = NULL;
38static char text[4096];
39static int promptw = 0;
40static int ret = 0;
41static int screen;
42static unsigned int mw, mh;
43static unsigned int cursor = 0;
44static unsigned int numlockmask = 0;
45static Bool running = True;
46static Display *dpy;
47static Window parent, win;
48
49#include "draw.c"
50
51void
52cleanup(void) {
53 dccleanup();
54 XDestroyWindow(dpy, win);
55 XUngrabKeyboard(dpy, CurrentTime);
56}
57
58void
59drawcursor(void) {
60 XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 };
61
62 r.x += textnw(text, cursor) + dc.font.height / 2;
63
64 XSetForeground(dpy, dc.gc, dc.norm[ColFG]);
65 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
66}
67
68void
69drawinput(void)
70{
71 dc.x = 0;
72 dc.y = 0;
73 dc.w = mw;
74 dc.h = mh;
75 drawtext(NULL, dc.norm);
76 /* print prompt? */
77 if(prompt) {
78 dc.w = promptw;
79 drawtext(prompt, dc.sel);
80 dc.x += dc.w;
81 }
82 dc.w = mw - dc.x;
83 drawtext(*text ? text : NULL, dc.norm);
84 drawcursor();
85 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0);
86 XFlush(dpy);
87}
88
89void
90eprint(const char *errstr, ...) {
91 va_list ap;
92
93 va_start(ap, errstr);
94 vfprintf(stderr, errstr, ap);
95 va_end(ap);
96 exit(EXIT_FAILURE);
97}
98
99Bool
100grabkeyboard(void) {
101 unsigned int len;
102
103 for(len = 1000; len; len--) {
104 if(XGrabKeyboard(dpy, parent, True, GrabModeAsync, GrabModeAsync, CurrentTime)
105 == GrabSuccess)
106 break;
107 usleep(1000);
108 }
109 return len > 0;
110}
111
112void
113kpress(XKeyEvent * e) {
114 char buf[sizeof text];
115 int num;
116 unsigned int i, len;
117 KeySym ksym;
118
119 len = strlen(text);
120 num = XLookupString(e, buf, sizeof buf, &ksym, NULL);
121 if(ksym == XK_KP_Enter)
122 ksym = XK_Return;
123 else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
124 ksym = (ksym - XK_KP_0) + XK_0;
125 else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
126 || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
127 || IsPrivateKeypadKey(ksym))
128 return;
129 /* first check if a control mask is omitted */
130 if(e->state & ControlMask) {
131 switch(tolower(ksym)) {
132 default:
133 return;
134 case XK_a:
135 ksym = XK_Home;
136 break;
137 case XK_b:
138 ksym = XK_Left;
139 break;
140 case XK_c:
141 ksym = XK_Escape;
142 break;
143 case XK_e:
144 ksym = XK_End;
145 break;
146 case XK_f:
147 ksym = XK_Right;
148 break;
149 case XK_h:
150 ksym = XK_BackSpace;
151 break;
152 case XK_j:
153 ksym = XK_Return;
154 break;
155 case XK_k:
156 text[cursor] = '\0';
157 break;
158 case XK_u:
159 memmove(text, text + cursor, sizeof text - cursor + 1);
160 cursor = 0;
161 break;
162 case XK_w:
163 if(cursor > 0) {
164 i = cursor;
165 while(i-- > 0 && text[i] == ' ');
166 while(i-- > 0 && text[i] != ' ');
167 memmove(text + i + 1, text + cursor, sizeof text - cursor + 1);
168 cursor = i + 1;
169 }
170 break;
171 case XK_y:
172 {
173 FILE *fp;
174 char *s;
175 if(!(fp = popen("sselp", "r")))
176 eprint("dinput: cannot popen sselp\n");
177 s = fgets(buf, sizeof buf, fp);
178 pclose(fp);
179 if(s == NULL)
180 return;
181 }
182 num = strlen(buf);
183 if(num && buf[num-1] == '\n')
184 buf[--num] = '\0';
185 break;
186 }
187 }
188 switch(ksym) {
189 default:
190 num = MIN(num, sizeof text - cursor);
191 if(num && !iscntrl((int) buf[0])) {
192 memmove(text + cursor + num, text + cursor, sizeof text - cursor - num);
193 memcpy(text + cursor, buf, num);
194 cursor += num;
195 }
196 break;
197 case XK_BackSpace:
198 if(cursor == 0)
199 return;
200 for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++);
201 memmove(text + cursor - i, text + cursor, sizeof text - cursor + i);
202 cursor -= i;
203 break;
204 case XK_Delete:
205 if(cursor == len)
206 return;
207 for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++);
208 memmove(text + cursor, text + cursor + i, sizeof text - cursor);
209 break;
210 case XK_End:
211 cursor = len;
212 break;
213 case XK_Escape:
214 ret = 1;
215 running = False;
216 return;
217 case XK_Home:
218 cursor = 0;
219 break;
220 case XK_Left:
221 if(cursor == 0)
222 return;
223 while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor]));
224 break;
225 case XK_Return:
226 fprintf(stdout, "%s", text);
227 fflush(stdout);
228 running = False;
229 return;
230 case XK_Right:
231 if(cursor == len)
232 return;
233 while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor]));
234 break;
235 }
236 drawinput();
237}
238
239void
240run(void) {
241 XEvent ev;
242
243 /* main event loop */
244 while(running && !XNextEvent(dpy, &ev))
245 switch (ev.type) {
246 case KeyPress:
247 kpress(&ev.xkey);
248 break;
249 case Expose:
250 if(ev.xexpose.count == 0)
251 drawinput();
252 break;
253 case VisibilityNotify:
254 if (ev.xvisibility.state != VisibilityUnobscured)
255 XRaiseWindow(dpy, win);
256 break;
257 }
258}
259
260void
261setup(Bool topbar) {
262 int i, j, x, y;
263#if XINERAMA
264 int n;
265 XineramaScreenInfo *info = NULL;
266#endif
267 XModifierKeymap *modmap;
268 XSetWindowAttributes wa;
269 XWindowAttributes pwa;
270
271 /* init modifier map */
272 modmap = XGetModifierMapping(dpy);
273 for(i = 0; i < 8; i++)
274 for(j = 0; j < modmap->max_keypermod; j++) {
275 if(modmap->modifiermap[i * modmap->max_keypermod + j]
276 == XKeysymToKeycode(dpy, XK_Num_Lock))
277 numlockmask = (1 << i);
278 }
279 XFreeModifiermap(modmap);
280
281 /* style */
282 dc.norm[ColBG] = getcolor(normbgcolor);
283 dc.norm[ColFG] = getcolor(normfgcolor);
284 dc.sel[ColBG] = getcolor(selbgcolor);
285 dc.sel[ColFG] = getcolor(selfgcolor);
286 initfont(font);
287
288 /* menu window */
289 wa.override_redirect = True;
290 wa.background_pixmap = ParentRelative;
291 wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask;
292
293 /* menu window geometry */
294 mh = (dc.font.height + 2);
295#if XINERAMA
296 if(parent == RootWindow(dpy, screen) && XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) {
297 i = 0;
298 if(n > 1) {
299 int di;
300 unsigned int dui;
301 Window dummy;
302 if(XQueryPointer(dpy, parent, &dummy, &dummy, &x, &y, &di, &di, &dui))
303 for(i = 0; i < n; i++)
304 if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
305 break;
306 }
307 x = info[i].x_org;
308 y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh;
309 mw = info[i].width;
310 XFree(info);
311 }
312 else
313#endif
314 {
315 XGetWindowAttributes(dpy, parent, &pwa);
316 x = 0;
317 y = topbar ? 0 : pwa.height - mh;
318 mw = pwa.width;
319 }
320
321 win = XCreateWindow(dpy, parent, x, y, mw, mh, 0,
322 DefaultDepth(dpy, screen), CopyFromParent,
323 DefaultVisual(dpy, screen),
324 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
325
326 /* pixmap */
327 dcsetup();
328 if(prompt)
329 promptw = MIN(textw(prompt), mw / 5);
330 cursor = strlen(text);
331 XMapRaised(dpy, win);
332}
333
334int
335main(int argc, char *argv[]) {
336 unsigned int i;
337 Bool topbar = True;
338
339 /* command line args */
340 for(i = 1; i < argc; i++)
341 if(!strcmp(argv[i], "-b"))
342 topbar = False;
343 else if(!strcmp(argv[i], "-e")) {
344 if(++i < argc) parent = atoi(argv[i]);
345 }
346 else if(!strcmp(argv[i], "-fn")) {
347 if(++i < argc) font = argv[i];
348 }
349 else if(!strcmp(argv[i], "-nb")) {
350 if(++i < argc) normbgcolor = argv[i];
351 }
352 else if(!strcmp(argv[i], "-nf")) {
353 if(++i < argc) normfgcolor = argv[i];
354 }
355 else if(!strcmp(argv[i], "-p")) {
356 if(++i < argc) prompt = argv[i];
357 }
358 else if(!strcmp(argv[i], "-sb")) {
359 if(++i < argc) selbgcolor = argv[i];
360 }
361 else if(!strcmp(argv[i], "-sf")) {
362 if(++i < argc) selfgcolor = argv[i];
363 }
364 else if(!strcmp(argv[i], "-v"))
365 eprint("dinput-"VERSION", © 2006-2010 dinput engineers, see LICENSE for details\n");
366 else if(!*text)
367 strncpy(text, argv[i], sizeof text);
368 else
369 eprint("usage: dinput [-b] [-e <xid>] [-fn <font>] [-nb <color>] [-nf <color>]\n"
370 " [-p <prompt>] [-sb <color>] [-sf <color>] [-v] [<text>]\n");
371 if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
372 fprintf(stderr, "dinput: warning: no locale support\n");
373 if(!(dpy = XOpenDisplay(NULL)))
374 eprint("dinput: cannot open display\n");
375 screen = DefaultScreen(dpy);
376 if(!parent)
377 parent = RootWindow(dpy, screen);
378
379 running = grabkeyboard();
380 setup(topbar);
381 drawinput();
382 XSync(dpy, False);
383 run();
384 cleanup();
385 XCloseDisplay(dpy);
386 return ret;
387}
diff --git a/dmenu.c b/dmenu.c
index b89c0da..5434eab 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -21,25 +21,6 @@
21#define MAX(a, b) ((a) > (b) ? (a) : (b)) 21#define MAX(a, b) ((a) > (b) ? (a) : (b))
22#define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) 22#define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80))
23 23
24/* enums */
25enum { ColFG, ColBG, ColLast };
26
27/* typedefs */
28typedef struct {
29 int x, y, w, h;
30 unsigned long norm[ColLast];
31 unsigned long sel[ColLast];
32 Drawable drawable;
33 GC gc;
34 struct {
35 XFontStruct *xfont;
36 XFontSet set;
37 int ascent;
38 int descent;
39 int height;
40 } font;
41} DC; /* draw context */
42
43typedef struct Item Item; 24typedef struct Item Item;
44struct Item { 25struct Item {
45 char *text; 26 char *text;
@@ -53,22 +34,16 @@ static void calcoffsetsh(void);
53static void calcoffsetsv(void); 34static void calcoffsetsv(void);
54static char *cistrstr(const char *s, const char *sub); 35static char *cistrstr(const char *s, const char *sub);
55static void cleanup(void); 36static void cleanup(void);
56static void drawcursor(void);
57static void drawmenu(void); 37static void drawmenu(void);
58static void drawmenuh(void); 38static void drawmenuh(void);
59static void drawmenuv(void); 39static void drawmenuv(void);
60static void drawtext(const char *text, unsigned long col[ColLast]);
61static void eprint(const char *errstr, ...); 40static void eprint(const char *errstr, ...);
62static unsigned long getcolor(const char *colstr);
63static Bool grabkeyboard(void); 41static Bool grabkeyboard(void);
64static void initfont(const char *fontstr);
65static void kpress(XKeyEvent * e); 42static void kpress(XKeyEvent * e);
66static void match(char *pattern); 43static void match(char *pattern);
67static void readstdin(void); 44static void readstdin(void);
68static void run(void); 45static void run(void);
69static void setup(Bool topbar); 46static void setup(Bool topbar);
70static int textnw(const char *text, unsigned int len);
71static int textw(const char *text);
72 47
73#include "config.h" 48#include "config.h"
74 49
@@ -81,11 +56,9 @@ static int promptw = 0;
81static int ret = 0; 56static int ret = 0;
82static int screen; 57static int screen;
83static unsigned int mw, mh; 58static unsigned int mw, mh;
84static unsigned int cursor = 0;
85static unsigned int numlockmask = 0; 59static unsigned int numlockmask = 0;
86static Bool running = True; 60static Bool running = True;
87static Display *dpy; 61static Display *dpy;
88static DC dc;
89static Item *allitems = NULL; /* first of all items */ 62static Item *allitems = NULL; /* first of all items */
90static Item *item = NULL; /* first of pattern matching items */ 63static Item *item = NULL; /* first of pattern matching items */
91static Item *sel = NULL; 64static Item *sel = NULL;
@@ -98,6 +71,8 @@ static char *(*fstrstr)(const char *, const char *) = strstr;
98static unsigned int lines = 0; 71static unsigned int lines = 0;
99static void (*calcoffsets)(void) = calcoffsetsh; 72static void (*calcoffsets)(void) = calcoffsetsh;
100 73
74#include "draw.c"
75
101void 76void
102appenditem(Item *i, Item **list, Item **last) { 77appenditem(Item *i, Item **list, Item **last) {
103 if(!(*last)) 78 if(!(*last))
@@ -161,27 +136,12 @@ cistrstr(const char *s, const char *sub) {
161 136
162void 137void
163cleanup(void) { 138cleanup(void) {
164 if(dc.font.set) 139 dccleanup();
165 XFreeFontSet(dpy, dc.font.set);
166 else
167 XFreeFont(dpy, dc.font.xfont);
168 XFreePixmap(dpy, dc.drawable);
169 XFreeGC(dpy, dc.gc);
170 XDestroyWindow(dpy, win); 140 XDestroyWindow(dpy, win);
171 XUngrabKeyboard(dpy, CurrentTime); 141 XUngrabKeyboard(dpy, CurrentTime);
172} 142}
173 143
174void 144void
175drawcursor(void) {
176 XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 };
177
178 r.x += textnw(text, cursor) + dc.font.height / 2;
179
180 XSetForeground(dpy, dc.gc, dc.norm[ColFG]);
181 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
182}
183
184void
185drawmenu(void) { 145drawmenu(void) {
186 dc.x = 0; 146 dc.x = 0;
187 dc.y = 0; 147 dc.y = 0;
@@ -199,7 +159,6 @@ drawmenu(void) {
199 if(cmdw && item && lines == 0) 159 if(cmdw && item && lines == 0)
200 dc.w = cmdw; 160 dc.w = cmdw;
201 drawtext(*text ? text : NULL, dc.norm); 161 drawtext(*text ? text : NULL, dc.norm);
202 drawcursor();
203 if(curr) { 162 if(curr) {
204 if(lines > 0) 163 if(lines > 0)
205 drawmenuv(); 164 drawmenuv();
@@ -244,34 +203,6 @@ drawmenuv(void) {
244} 203}
245 204
246void 205void
247drawtext(const char *text, unsigned long col[ColLast]) {
248 char buf[256];
249 int i, x, y, h, len, olen;
250 XRectangle r = { dc.x, dc.y, dc.w, dc.h };
251
252 XSetForeground(dpy, dc.gc, col[ColBG]);
253 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
254 if(!text)
255 return;
256 olen = strlen(text);
257 h = dc.font.height;
258 y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent;
259 x = dc.x + (h / 2);
260 /* shorten text if necessary */
261 for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
262 if(!len)
263 return;
264 memcpy(buf, text, len);
265 if(len < olen)
266 for(i = len; i && i > len - 3; buf[--i] = '.');
267 XSetForeground(dpy, dc.gc, col[ColFG]);
268 if(dc.font.set)
269 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
270 else
271 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
272}
273
274void
275eprint(const char *errstr, ...) { 206eprint(const char *errstr, ...) {
276 va_list ap; 207 va_list ap;
277 208
@@ -281,16 +212,6 @@ eprint(const char *errstr, ...) {
281 exit(EXIT_FAILURE); 212 exit(EXIT_FAILURE);
282} 213}
283 214
284unsigned long
285getcolor(const char *colstr) {
286 Colormap cmap = DefaultColormap(dpy, screen);
287 XColor color;
288
289 if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
290 eprint("dmenu: cannot allocate color '%s'\n", colstr);
291 return color.pixel;
292}
293
294Bool 215Bool
295grabkeyboard(void) { 216grabkeyboard(void) {
296 unsigned int len; 217 unsigned int len;
@@ -305,37 +226,6 @@ grabkeyboard(void) {
305} 226}
306 227
307void 228void
308initfont(const char *fontstr) {
309 char *def, **missing = NULL;
310 int i, n;
311
312 if(!fontstr || fontstr[0] == '\0')
313 eprint("dmenu: cannot load font: '%s'\n", fontstr);
314 dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
315 if(missing)
316 XFreeStringList(missing);
317 if(dc.font.set) {
318 XFontStruct **xfonts;
319 char **font_names;
320 dc.font.ascent = dc.font.descent = 0;
321 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
322 for(i = 0; i < n; i++) {
323 dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
324 dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent);
325 xfonts++;
326 }
327 }
328 else {
329 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
330 && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
331 eprint("dmenu: cannot load font: '%s'\n", fontstr);
332 dc.font.ascent = dc.font.xfont->ascent;
333 dc.font.descent = dc.font.xfont->descent;
334 }
335 dc.font.height = dc.font.ascent + dc.font.descent;
336}
337
338void
339kpress(XKeyEvent * e) { 229kpress(XKeyEvent * e) {
340 char buf[sizeof text]; 230 char buf[sizeof text];
341 int num; 231 int num;
@@ -381,9 +271,6 @@ kpress(XKeyEvent * e) {
381 case XK_j: 271 case XK_j:
382 ksym = XK_Return; 272 ksym = XK_Return;
383 break; 273 break;
384 case XK_k:
385 text[cursor] = '\0';
386 break;
387 case XK_n: 274 case XK_n:
388 ksym = XK_Down; 275 ksym = XK_Down;
389 break; 276 break;
@@ -391,67 +278,42 @@ kpress(XKeyEvent * e) {
391 ksym = XK_Up; 278 ksym = XK_Up;
392 break; 279 break;
393 case XK_u: 280 case XK_u:
394 memmove(text, text + cursor, sizeof text - cursor + 1); 281 text[0] = '\0';
395 cursor = 0;
396 match(text); 282 match(text);
397 break; 283 break;
398 case XK_w: 284 case XK_w:
399 if(cursor > 0) { 285 if(len == 0)
400 i = cursor; 286 return;
401 while(i-- > 0 && text[i] == ' '); 287 i = len;
402 while(i-- > 0 && text[i] != ' '); 288 while(i-- > 0 && text[i] == ' ');
403 memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); 289 while(i-- > 0 && text[i] != ' ');
404 cursor = i + 1; 290 text[++i] = '\0';
405 match(text); 291 match(text);
406 }
407 break; 292 break;
408 case XK_y: 293 case XK_x:
409 { 294 execlp("dinput", "dinput", text, NULL); /* todo: argv */
410 FILE *fp; 295 eprint("dmenu: cannot exec dinput:");
411 char *s;
412 if(!(fp = popen("sselp", "r")))
413 eprint("dmenu: cannot popen sselp\n");
414 s = fgets(buf, sizeof buf, fp);
415 pclose(fp);
416 if(s == NULL)
417 return;
418 }
419 num = strlen(buf);
420 if(num && buf[num-1] == '\n')
421 buf[--num] = '\0';
422 break; 296 break;
423 } 297 }
424 } 298 }
425 switch(ksym) { 299 switch(ksym) {
426 default: 300 default:
427 num = MIN(num, sizeof text - cursor); 301 num = MIN(num, sizeof text);
428 if(num && !iscntrl((int) buf[0])) { 302 if(num && !iscntrl((int) buf[0])) {
429 memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); 303 memcpy(text + len, buf, num + 1);
430 memcpy(text + cursor, buf, num); 304 len += num;
431 cursor += num;
432 match(text); 305 match(text);
433 } 306 }
434 break; 307 break;
435 case XK_BackSpace: 308 case XK_BackSpace:
436 if(cursor == 0) 309 if(len == 0)
437 return; 310 return;
438 for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); 311 for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++);
439 memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); 312 len -= i;
440 cursor -= i; 313 text[len] = '\0';
441 match(text);
442 break;
443 case XK_Delete:
444 if(cursor == len)
445 return;
446 for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++);
447 memmove(text + cursor, text + cursor + i, sizeof text - cursor);
448 match(text); 314 match(text);
449 break; 315 break;
450 case XK_End: 316 case XK_End:
451 if(cursor < len) {
452 cursor = len;
453 break;
454 }
455 while(next) { 317 while(next) {
456 sel = curr = next; 318 sel = curr = next;
457 calcoffsets(); 319 calcoffsets();
@@ -464,20 +326,10 @@ kpress(XKeyEvent * e) {
464 running = False; 326 running = False;
465 return; 327 return;
466 case XK_Home: 328 case XK_Home:
467 if(sel == item) {
468 cursor = 0;
469 break;
470 }
471 sel = curr = item; 329 sel = curr = item;
472 calcoffsets(); 330 calcoffsets();
473 break; 331 break;
474 case XK_Left: 332 case XK_Left:
475 if(cursor > 0 && (!sel || !sel->left || lines > 0)) {
476 while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor]));
477 break;
478 }
479 if(lines > 0)
480 return;
481 case XK_Up: 333 case XK_Up:
482 if(!sel || !sel->left) 334 if(!sel || !sel->left)
483 return; 335 return;
@@ -508,12 +360,6 @@ kpress(XKeyEvent * e) {
508 running = False; 360 running = False;
509 return; 361 return;
510 case XK_Right: 362 case XK_Right:
511 if(cursor < len) {
512 while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor]));
513 break;
514 }
515 if(lines > 0)
516 return;
517 case XK_Down: 363 case XK_Down:
518 if(!sel || !sel->right) 364 if(!sel || !sel->right)
519 return; 365 return;
@@ -527,7 +373,6 @@ kpress(XKeyEvent * e) {
527 if(!sel) 373 if(!sel)
528 return; 374 return;
529 strncpy(text, sel->text, sizeof text); 375 strncpy(text, sel->text, sizeof text);
530 cursor = strlen(text);
531 match(text); 376 match(text);
532 break; 377 break;
533 } 378 }
@@ -690,11 +535,7 @@ setup(Bool topbar) {
690 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); 535 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
691 536
692 /* pixmap */ 537 /* pixmap */
693 dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); 538 dcsetup();
694 dc.gc = XCreateGC(dpy, parent, 0, NULL);
695 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
696 if(!dc.font.set)
697 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
698 if(maxname) 539 if(maxname)
699 cmdw = MIN(textw(maxname), mw / 3); 540 cmdw = MIN(textw(maxname), mw / 3);
700 if(prompt) 541 if(prompt)
@@ -705,22 +546,6 @@ setup(Bool topbar) {
705} 546}
706 547
707int 548int
708textnw(const char *text, unsigned int len) {
709 XRectangle r;
710
711 if(dc.font.set) {
712 XmbTextExtents(dc.font.set, text, len, NULL, &r);
713 return r.width;
714 }
715 return XTextWidth(dc.font.xfont, text, len);
716}
717
718int
719textw(const char *text) {
720 return textnw(text, strlen(text)) + dc.font.height;
721}
722
723int
724main(int argc, char *argv[]) { 549main(int argc, char *argv[]) {
725 unsigned int i; 550 unsigned int i;
726 Bool topbar = True; 551 Bool topbar = True;
diff --git a/draw.c b/draw.c
new file mode 100644
index 0000000..6851a34
--- /dev/null
+++ b/draw.c
@@ -0,0 +1,143 @@
1/* See LICENSE file for copyright and license details. */
2
3/* enums */
4enum { ColFG, ColBG, ColLast };
5
6/* typedefs */
7typedef struct {
8 int x, y, w, h;
9 unsigned long norm[ColLast];
10 unsigned long sel[ColLast];
11 Drawable drawable;
12 GC gc;
13 struct {
14 XFontStruct *xfont;
15 XFontSet set;
16 int ascent;
17 int descent;
18 int height;
19 } font;
20} DC; /* draw context */
21
22/* forward declarations */
23static void dccleanup(void);
24static void dcsetup(void);
25static void drawtext(const char *text, unsigned long col[ColLast]);
26static unsigned long getcolor(const char *colstr);
27static void initfont(const char *fontstr);
28static int textnw(const char *text, unsigned int len);
29static int textw(const char *text);
30
31static DC dc;
32
33void
34dccleanup(void) {
35 if(dc.font.set)
36 XFreeFontSet(dpy, dc.font.set);
37 else
38 XFreeFont(dpy, dc.font.xfont);
39 XFreePixmap(dpy, dc.drawable);
40 XFreeGC(dpy, dc.gc);
41}
42
43void
44dcsetup() {
45 /* style */
46 dc.norm[ColBG] = getcolor(normbgcolor);
47 dc.norm[ColFG] = getcolor(normfgcolor);
48 dc.sel[ColBG] = getcolor(selbgcolor);
49 dc.sel[ColFG] = getcolor(selfgcolor);
50 initfont(font);
51
52 /* pixmap */
53 dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen));
54 dc.gc = XCreateGC(dpy, parent, 0, NULL);
55 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
56 if(!dc.font.set)
57 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
58}
59
60void
61drawtext(const char *text, unsigned long col[ColLast]) {
62 char buf[256];
63 int i, x, y, h, len, olen;
64 XRectangle r = { dc.x, dc.y, dc.w, dc.h };
65
66 XSetForeground(dpy, dc.gc, col[ColBG]);
67 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
68 if(!text)
69 return;
70 olen = strlen(text);
71 h = dc.font.height;
72 y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent;
73 x = dc.x + (h / 2);
74 /* shorten text if necessary */
75 for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
76 if(!len)
77 return;
78 memcpy(buf, text, len);
79 if(len < olen)
80 for(i = len; i && i > len - 3; buf[--i] = '.');
81 XSetForeground(dpy, dc.gc, col[ColFG]);
82 if(dc.font.set)
83 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
84 else
85 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
86}
87
88unsigned long
89getcolor(const char *colstr) {
90 Colormap cmap = DefaultColormap(dpy, screen);
91 XColor color;
92
93 if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
94 eprint("drawtext: cannot allocate color '%s'\n", colstr);
95 return color.pixel;
96}
97
98void
99initfont(const char *fontstr) {
100 char *def, **missing = NULL;
101 int i, n;
102
103 if(!fontstr || fontstr[0] == '\0')
104 eprint("drawtext: cannot load font: '%s'\n", fontstr);
105 dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
106 if(missing)
107 XFreeStringList(missing);
108 if(dc.font.set) {
109 XFontStruct **xfonts;
110 char **font_names;
111 dc.font.ascent = dc.font.descent = 0;
112 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
113 for(i = 0; i < n; i++) {
114 dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
115 dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent);
116 xfonts++;
117 }
118 }
119 else {
120 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
121 && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
122 eprint("drawtext: cannot load font: '%s'\n", fontstr);
123 dc.font.ascent = dc.font.xfont->ascent;
124 dc.font.descent = dc.font.xfont->descent;
125 }
126 dc.font.height = dc.font.ascent + dc.font.descent;
127}
128
129int
130textnw(const char *text, unsigned int len) {
131 XRectangle r;
132
133 if(dc.font.set) {
134 XmbTextExtents(dc.font.set, text, len, NULL, &r);
135 return r.width;
136 }
137 return XTextWidth(dc.font.xfont, text, len);
138}
139
140int
141textw(const char *text) {
142 return textnw(text, strlen(text)) + dc.font.height;
143}