initial dmenu / dinput separation b4e63454
Connor Lane Smith · 2010-06-23 12:04 4 file(s) · +561 −205
Makefile +9 −8
3 3
4 4
include config.mk
5 5
6 -
SRC = dmenu.c
6 +
SRC = dinput.c dmenu.c draw.c
7 7
OBJ = ${SRC:.c=.o}
8 8
9 -
all: options dmenu
9 +
all: options dinput dmenu
10 10
11 11
options:
12 12
	@echo dmenu build options:
18 18
	@echo CC $<
19 19
	@${CC} -c ${CFLAGS} $<
20 20
21 -
${OBJ}: config.h config.mk
21 +
${OBJ}: config.h config.mk draw.c
22 22
23 23
config.h:
24 24
	@echo creating $@ from config.def.h
25 25
	@cp config.def.h $@
26 26
27 -
dmenu: ${OBJ}
27 +
.o:
28 28
	@echo CC -o $@
29 -
	@${CC} -o $@ ${OBJ} ${LDFLAGS}
29 +
	@${CC} -o $@ $< ${LDFLAGS}
30 30
31 31
clean:
32 32
	@echo cleaning
33 -
	@rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz
33 +
	@rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz
34 34
35 35
dist: clean
36 36
	@echo creating dist tarball
43 43
install: all
44 44
	@echo installing executable file to ${DESTDIR}${PREFIX}/bin
45 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 48
	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
48 49
	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
49 50
	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
55 56
uninstall:
56 57
	@echo removing executable file from ${DESTDIR}${PREFIX}/bin
57 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 60
	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
60 61
	@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
61 62
dinput.c (added) +387 −0
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 */
25 +
static void cleanup(void);
26 +
static void drawcursor(void);
27 +
static void drawinput(void);
28 +
static void eprint(const char *errstr, ...);
29 +
static Bool grabkeyboard(void);
30 +
static void kpress(XKeyEvent * e);
31 +
static void run(void);
32 +
static void setup(Bool topbar);
33 +
34 +
#include "config.h"
35 +
36 +
/* variables */
37 +
static char *prompt = NULL;
38 +
static char text[4096];
39 +
static int promptw = 0;
40 +
static int ret = 0;
41 +
static int screen;
42 +
static unsigned int mw, mh;
43 +
static unsigned int cursor = 0;
44 +
static unsigned int numlockmask = 0;
45 +
static Bool running = True;
46 +
static Display *dpy;
47 +
static Window parent, win;
48 +
49 +
#include "draw.c"
50 +
51 +
void
52 +
cleanup(void) {
53 +
	dccleanup();
54 +
	XDestroyWindow(dpy, win);
55 +
	XUngrabKeyboard(dpy, CurrentTime);
56 +
}
57 +
58 +
void
59 +
drawcursor(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 +
68 +
void
69 +
drawinput(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 +
89 +
void
90 +
eprint(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 +
99 +
Bool
100 +
grabkeyboard(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 +
112 +
void
113 +
kpress(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 +
239 +
void
240 +
run(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 +
260 +
void
261 +
setup(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 +
334 +
int
335 +
main(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 +
}
dmenu.c +22 −197
21 21
#define MAX(a, b)               ((a) > (b) ? (a) : (b))
22 22
#define IS_UTF8_1ST_CHAR(c)     ((((c) & 0xc0) == 0xc0) || !((c) & 0x80))
23 23
24 -
/* enums */
25 -
enum { ColFG, ColBG, ColLast };
26 -
27 -
/* typedefs */
28 -
typedef 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 -
43 24
typedef struct Item Item;
44 25
struct Item {
45 26
	char *text;
53 34
static void calcoffsetsv(void);
54 35
static char *cistrstr(const char *s, const char *sub);
55 36
static void cleanup(void);
56 -
static void drawcursor(void);
57 37
static void drawmenu(void);
58 38
static void drawmenuh(void);
59 39
static void drawmenuv(void);
60 -
static void drawtext(const char *text, unsigned long col[ColLast]);
61 40
static void eprint(const char *errstr, ...);
62 -
static unsigned long getcolor(const char *colstr);
63 41
static Bool grabkeyboard(void);
64 -
static void initfont(const char *fontstr);
65 42
static void kpress(XKeyEvent * e);
66 43
static void match(char *pattern);
67 44
static void readstdin(void);
68 45
static void run(void);
69 46
static void setup(Bool topbar);
70 -
static int textnw(const char *text, unsigned int len);
71 -
static int textw(const char *text);
72 47
73 48
#include "config.h"
74 49
81 56
static int ret = 0;
82 57
static int screen;
83 58
static unsigned int mw, mh;
84 -
static unsigned int cursor = 0;
85 59
static unsigned int numlockmask = 0;
86 60
static Bool running = True;
87 61
static Display *dpy;
88 -
static DC dc;
89 62
static Item *allitems = NULL;  /* first of all items */
90 63
static Item *item = NULL;      /* first of pattern matching items */
91 64
static Item *sel = NULL;
98 71
static unsigned int lines = 0;
99 72
static void (*calcoffsets)(void) = calcoffsetsh;
100 73
74 +
#include "draw.c"
75 +
101 76
void
102 77
appenditem(Item *i, Item **list, Item **last) {
103 78
	if(!(*last))
161 136
162 137
void
163 138
cleanup(void) {
164 -
	if(dc.font.set)
165 -
		XFreeFontSet(dpy, dc.font.set);
166 -
	else
167 -
		XFreeFont(dpy, dc.font.xfont);
168 -
	XFreePixmap(dpy, dc.drawable);
169 -
	XFreeGC(dpy, dc.gc);
139 +
	dccleanup();
170 140
	XDestroyWindow(dpy, win);
171 141
	XUngrabKeyboard(dpy, CurrentTime);
172 142
}
173 143
174 144
void
175 -
drawcursor(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 -
184 -
void
185 145
drawmenu(void) {
186 146
	dc.x = 0;
187 147
	dc.y = 0;
199 159
	if(cmdw && item && lines == 0)
200 160
		dc.w = cmdw;
201 161
	drawtext(*text ? text : NULL, dc.norm);
202 -
	drawcursor();
203 162
	if(curr) {
204 163
		if(lines > 0)
205 164
			drawmenuv();
244 203
}
245 204
246 205
void
247 -
drawtext(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 -
274 -
void
275 206
eprint(const char *errstr, ...) {
276 207
	va_list ap;
277 208
281 212
	exit(EXIT_FAILURE);
282 213
}
283 214
284 -
unsigned long
285 -
getcolor(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 -
294 215
Bool
295 216
grabkeyboard(void) {
296 217
	unsigned int len;
305 226
}
306 227
307 228
void
308 -
initfont(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 -
338 -
void
339 229
kpress(XKeyEvent * e) {
340 230
	char buf[sizeof text];
341 231
	int num;
381 271
		case XK_j:
382 272
			ksym = XK_Return;
383 273
			break;
384 -
		case XK_k:
385 -
			text[cursor] = '\0';
386 -
			break;
387 274
		case XK_n:
388 275
			ksym = XK_Down;
389 276
			break;
391 278
			ksym = XK_Up;
392 279
			break;
393 280
		case XK_u:
394 -
			memmove(text, text + cursor, sizeof text - cursor + 1);
395 -
			cursor = 0;
281 +
			text[0] = '\0';
396 282
			match(text);
397 283
			break;
398 284
		case XK_w:
399 -
			if(cursor > 0) {
400 -
				i = cursor;
401 -
				while(i-- > 0 && text[i] == ' ');
402 -
				while(i-- > 0 && text[i] != ' ');
403 -
				memmove(text + i + 1, text + cursor, sizeof text - cursor + 1);
404 -
				cursor = i + 1;
405 -
				match(text);
406 -
			}
285 +
			if(len == 0)
286 +
				return;
287 +
			i = len;
288 +
			while(i-- > 0 && text[i] == ' ');
289 +
			while(i-- > 0 && text[i] != ' ');
290 +
			text[++i] = '\0';
291 +
			match(text);
407 292
			break;
408 -
		case XK_y:
409 -
			{
410 -
				FILE *fp;
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';
293 +
		case XK_x:
294 +
			execlp("dinput", "dinput", text, NULL); /* todo: argv */
295 +
			eprint("dmenu: cannot exec dinput:");
422 296
			break;
423 297
		}
424 298
	}
425 299
	switch(ksym) {
426 300
	default:
427 -
		num = MIN(num, sizeof text - cursor);
301 +
		num = MIN(num, sizeof text);
428 302
		if(num && !iscntrl((int) buf[0])) {
429 -
			memmove(text + cursor + num, text + cursor, sizeof text - cursor - num);
430 -
			memcpy(text + cursor, buf, num);
431 -
			cursor += num;
303 +
			memcpy(text + len, buf, num + 1);
304 +
			len += num;
432 305
			match(text);
433 306
		}
434 307
		break;
435 308
	case XK_BackSpace:
436 -
		if(cursor == 0)
309 +
		if(len == 0)
437 310
			return;
438 -
		for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++);
439 -
		memmove(text + cursor - i, text + cursor, sizeof text - cursor + i);
440 -
		cursor -= i;
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);
311 +
		for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++);
312 +
		len -= i;
313 +
		text[len] = '\0';
448 314
		match(text);
449 315
		break;
450 316
	case XK_End:
451 -
		if(cursor < len) {
452 -
			cursor = len;
453 -
			break;
454 -
		}
455 317
		while(next) {
456 318
			sel = curr = next;
457 319
			calcoffsets();
464 326
		running = False;
465 327
		return;
466 328
	case XK_Home:
467 -
		if(sel == item) {
468 -
			cursor = 0;
469 -
			break;
470 -
		}
471 329
		sel = curr = item;
472 330
		calcoffsets();
473 331
		break;
474 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 333
	case XK_Up:
482 334
		if(!sel || !sel->left)
483 335
			return;
508 360
		running = False;
509 361
		return;
510 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 363
	case XK_Down:
518 364
		if(!sel || !sel->right)
519 365
			return;
527 373
		if(!sel)
528 374
			return;
529 375
		strncpy(text, sel->text, sizeof text);
530 -
		cursor = strlen(text);
531 376
		match(text);
532 377
		break;
533 378
	}
690 535
			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
691 536
692 537
	/* pixmap */
693 -
	dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen));
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);
538 +
	dcsetup();
698 539
	if(maxname)
699 540
		cmdw = MIN(textw(maxname), mw / 3);
700 541
	if(prompt)
702 543
	text[0] = '\0';
703 544
	match(text);
704 545
	XMapRaised(dpy, win);
705 -
}
706 -
707 -
int
708 -
textnw(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 -
718 -
int
719 -
textw(const char *text) {
720 -
	return textnw(text, strlen(text)) + dc.font.height;
721 546
}
722 547
723 548
int
draw.c (added) +143 −0
1 +
/* See LICENSE file for copyright and license details. */
2 +
3 +
/* enums */
4 +
enum { ColFG, ColBG, ColLast };
5 +
6 +
/* typedefs */
7 +
typedef 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 */
23 +
static void dccleanup(void);
24 +
static void dcsetup(void);
25 +
static void drawtext(const char *text, unsigned long col[ColLast]);
26 +
static unsigned long getcolor(const char *colstr);
27 +
static void initfont(const char *fontstr);
28 +
static int textnw(const char *text, unsigned int len);
29 +
static int textw(const char *text);
30 +
31 +
static DC dc;
32 +
33 +
void
34 +
dccleanup(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 +
43 +
void
44 +
dcsetup() {
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 +
60 +
void
61 +
drawtext(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 +
88 +
unsigned long
89 +
getcolor(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 +
98 +
void
99 +
initfont(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 +
129 +
int
130 +
textnw(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 +
140 +
int
141 +
textw(const char *text) {
142 +
	return textnw(text, strlen(text)) + dc.font.height;
143 +
}