diff options
| author | Connor Lane Smith <cls@lubutu.com> | 2010-06-23 12:04:54 +0100 | 
|---|---|---|
| committer | Connor Lane Smith <cls@lubutu.com> | 2010-06-23 12:04:54 +0100 | 
| commit | b4e63454e5768d38682405d252a81b1149273c0b (patch) | |
| tree | fb559aa5488d2f365ca33531a11ed75fabf02abf | |
| parent | bff1526d31faaf289804174f6477d61736e82443 (diff) | |
| download | dmenu-b4e63454e5768d38682405d252a81b1149273c0b.tar.gz dmenu-b4e63454e5768d38682405d252a81b1149273c0b.zip | |
initial dmenu / dinput separation
Diffstat (limited to '')
| -rw-r--r-- | Makefile | 17 | ||||
| -rw-r--r-- | dinput.c | 387 | ||||
| -rw-r--r-- | dmenu.c | 219 | ||||
| -rw-r--r-- | draw.c | 143 | 
4 files changed, 561 insertions, 205 deletions
| @@ -3,10 +3,10 @@  include config.mk -SRC = dmenu.c +SRC = dinput.c dmenu.c draw.c  OBJ = ${SRC:.c=.o} -all: options dmenu +all: options dinput dmenu  options:  	@echo dmenu build options: @@ -18,19 +18,19 @@ options:  	@echo CC $<  	@${CC} -c ${CFLAGS} $< -${OBJ}: config.h config.mk +${OBJ}: config.h config.mk draw.c  config.h:  	@echo creating $@ from config.def.h  	@cp config.def.h $@ -dmenu: ${OBJ} +.o:  	@echo CC -o $@ -	@${CC} -o $@ ${OBJ} ${LDFLAGS} +	@${CC} -o $@ $< ${LDFLAGS}  clean:  	@echo cleaning -	@rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz +	@rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz  dist: clean  	@echo creating dist tarball @@ -43,7 +43,8 @@ dist: clean  install: all  	@echo installing executable file to ${DESTDIR}${PREFIX}/bin  	@mkdir -p ${DESTDIR}${PREFIX}/bin -	@cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin +	@cp -f dinput dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin +	@chmod 755 ${DESTDIR}${PREFIX}/bin/dinput  	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu  	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path  	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run @@ -55,7 +56,7 @@ install: all  uninstall:  	@echo removing executable file from ${DESTDIR}${PREFIX}/bin  	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path -	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_run +	@rm -f ${DESTDIR}${PREFIX}/bin/dinput ${DESTDIR}${PREFIX}/bin/dmenu_run  	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1  	@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 diff --git a/dinput.c b/dinput.c new file mode 100644 index 0000000..490380d --- /dev/null +++ b/dinput.c @@ -0,0 +1,387 @@ +/* See LICENSE file for copyright and license details. */ +#include <ctype.h> +#include <locale.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <X11/keysym.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#ifdef XINERAMA +#include <X11/extensions/Xinerama.h> +#endif + +/* macros */ +#define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask)) +#define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH)) +#define MIN(a, b)               ((a) < (b) ? (a) : (b)) +#define MAX(a, b)               ((a) > (b) ? (a) : (b)) +#define IS_UTF8_1ST_CHAR(c)     ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) + +/* forward declarations */ +static void cleanup(void); +static void drawcursor(void); +static void drawinput(void); +static void eprint(const char *errstr, ...); +static Bool grabkeyboard(void); +static void kpress(XKeyEvent * e); +static void run(void); +static void setup(Bool topbar); + +#include "config.h" + +/* variables */ +static char *prompt = NULL; +static char text[4096]; +static int promptw = 0; +static int ret = 0; +static int screen; +static unsigned int mw, mh; +static unsigned int cursor = 0; +static unsigned int numlockmask = 0; +static Bool running = True; +static Display *dpy; +static Window parent, win; + +#include "draw.c" + +void +cleanup(void) { +	dccleanup(); +	XDestroyWindow(dpy, win); +	XUngrabKeyboard(dpy, CurrentTime); +} + +void +drawcursor(void) { +	XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 }; + +	r.x += textnw(text, cursor) + dc.font.height / 2; + +	XSetForeground(dpy, dc.gc, dc.norm[ColFG]); +	XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); +} + +void +drawinput(void) +{ +	dc.x = 0; +	dc.y = 0; +	dc.w = mw; +	dc.h = mh; +	drawtext(NULL, dc.norm); +	/* print prompt? */ +	if(prompt) { +		dc.w = promptw; +		drawtext(prompt, dc.sel); +		dc.x += dc.w; +	} +	dc.w = mw - dc.x; +	drawtext(*text ? text : NULL, dc.norm); +	drawcursor(); +	XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0); +	XFlush(dpy); +} + +void +eprint(const char *errstr, ...) { +	va_list ap; + +	va_start(ap, errstr); +	vfprintf(stderr, errstr, ap); +	va_end(ap); +	exit(EXIT_FAILURE); +} + +Bool +grabkeyboard(void) { +	unsigned int len; + +	for(len = 1000; len; len--) { +		if(XGrabKeyboard(dpy, parent, True, GrabModeAsync, GrabModeAsync, CurrentTime) +		== GrabSuccess) +			break; +		usleep(1000); +	} +	return len > 0; +} + +void +kpress(XKeyEvent * e) { +	char buf[sizeof text]; +	int num; +	unsigned int i, len; +	KeySym ksym; + +	len = strlen(text); +	num = XLookupString(e, buf, sizeof buf, &ksym, NULL); +	if(ksym == XK_KP_Enter) +		ksym = XK_Return; +	else if(ksym >= XK_KP_0 && ksym <= XK_KP_9) +		ksym = (ksym - XK_KP_0) + XK_0; +	else if(IsFunctionKey(ksym) || IsKeypadKey(ksym) +	|| IsMiscFunctionKey(ksym) || IsPFKey(ksym) +	|| IsPrivateKeypadKey(ksym)) +		return; +	/* first check if a control mask is omitted */ +	if(e->state & ControlMask) { +		switch(tolower(ksym)) { +		default: +			return; +		case XK_a: +			ksym = XK_Home; +			break; +		case XK_b: +			ksym = XK_Left; +			break; +		case XK_c: +			ksym = XK_Escape; +			break; +		case XK_e: +			ksym = XK_End; +			break; +		case XK_f: +			ksym = XK_Right; +			break; +		case XK_h: +			ksym = XK_BackSpace; +			break; +		case XK_j: +			ksym = XK_Return; +			break; +		case XK_k: +			text[cursor] = '\0'; +			break; +		case XK_u: +			memmove(text, text + cursor, sizeof text - cursor + 1); +			cursor = 0; +			break; +		case XK_w: +			if(cursor > 0) { +				i = cursor; +				while(i-- > 0 && text[i] == ' '); +				while(i-- > 0 && text[i] != ' '); +				memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); +				cursor = i + 1; +			} +			break; +		case XK_y: +			{ +				FILE *fp; +				char *s; +				if(!(fp = popen("sselp", "r"))) +					eprint("dinput: cannot popen sselp\n"); +				s = fgets(buf, sizeof buf, fp); +				pclose(fp); +				if(s == NULL) +					return; +			} +			num = strlen(buf); +			if(num && buf[num-1] == '\n') +				buf[--num] = '\0'; +			break; +		} +	} +	switch(ksym) { +	default: +		num = MIN(num, sizeof text - cursor); +		if(num && !iscntrl((int) buf[0])) { +			memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); +			memcpy(text + cursor, buf, num); +			cursor += num; +		} +		break; +	case XK_BackSpace: +		if(cursor == 0) +			return; +		for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); +		memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); +		cursor -= i; +		break; +	case XK_Delete: +		if(cursor == len) +			return; +		for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++); +		memmove(text + cursor, text + cursor + i, sizeof text - cursor); +		break; +	case XK_End: +		cursor = len; +		break; +	case XK_Escape: +		ret = 1; +		running = False; +		return; +	case XK_Home: +		cursor = 0; +		break; +	case XK_Left: +		if(cursor == 0) +			return; +		while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); +		break; +	case XK_Return: +		fprintf(stdout, "%s", text); +		fflush(stdout); +		running = False; +		return; +	case XK_Right: +		if(cursor == len) +			return; +		while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); +		break; +	} +	drawinput(); +} + +void +run(void) { +	XEvent ev; + +	/* main event loop */ +	while(running && !XNextEvent(dpy, &ev)) +		switch (ev.type) { +		case KeyPress: +			kpress(&ev.xkey); +			break; +		case Expose: +			if(ev.xexpose.count == 0) +				drawinput(); +			break; +		case VisibilityNotify: +			if (ev.xvisibility.state != VisibilityUnobscured) +				XRaiseWindow(dpy, win); +			break; +		} +} + +void +setup(Bool topbar) { +	int i, j, x, y; +#if XINERAMA +	int n; +	XineramaScreenInfo *info = NULL; +#endif +	XModifierKeymap *modmap; +	XSetWindowAttributes wa; +	XWindowAttributes pwa; + +	/* init modifier map */ +	modmap = XGetModifierMapping(dpy); +	for(i = 0; i < 8; i++) +		for(j = 0; j < modmap->max_keypermod; j++) { +			if(modmap->modifiermap[i * modmap->max_keypermod + j] +			== XKeysymToKeycode(dpy, XK_Num_Lock)) +				numlockmask = (1 << i); +		} +	XFreeModifiermap(modmap); + +	/* style */ +	dc.norm[ColBG] = getcolor(normbgcolor); +	dc.norm[ColFG] = getcolor(normfgcolor); +	dc.sel[ColBG] = getcolor(selbgcolor); +	dc.sel[ColFG] = getcolor(selfgcolor); +	initfont(font); + +	/* menu window */ +	wa.override_redirect = True; +	wa.background_pixmap = ParentRelative; +	wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask; + +	/* menu window geometry */ +	mh = (dc.font.height + 2); +#if XINERAMA +	if(parent == RootWindow(dpy, screen) && XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { +		i = 0; +		if(n > 1) { +			int di; +			unsigned int dui; +			Window dummy; +			if(XQueryPointer(dpy, parent, &dummy, &dummy, &x, &y, &di, &di, &dui)) +				for(i = 0; i < n; i++) +					if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) +						break; +		} +		x = info[i].x_org; +		y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh; +		mw = info[i].width; +		XFree(info); +	} +	else +#endif +	{ +		XGetWindowAttributes(dpy, parent, &pwa); +		x = 0; +		y = topbar ? 0 : pwa.height - mh; +		mw = pwa.width; +	} + +	win = XCreateWindow(dpy, parent, x, y, mw, mh, 0, +			DefaultDepth(dpy, screen), CopyFromParent, +			DefaultVisual(dpy, screen), +			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + +	/* pixmap */ +	dcsetup(); +	if(prompt) +		promptw = MIN(textw(prompt), mw / 5); +	cursor = strlen(text); +	XMapRaised(dpy, win); +} + +int +main(int argc, char *argv[]) { +	unsigned int i; +	Bool topbar = True; + +	/* command line args */ +	for(i = 1; i < argc; i++) +		if(!strcmp(argv[i], "-b")) +			topbar = False; +		else if(!strcmp(argv[i], "-e")) { +			if(++i < argc) parent = atoi(argv[i]); +		} +		else if(!strcmp(argv[i], "-fn")) { +			if(++i < argc) font = argv[i]; +		} +		else if(!strcmp(argv[i], "-nb")) { +			if(++i < argc) normbgcolor = argv[i]; +		} +		else if(!strcmp(argv[i], "-nf")) { +			if(++i < argc) normfgcolor = argv[i]; +		} +		else if(!strcmp(argv[i], "-p")) { +			if(++i < argc) prompt = argv[i]; +		} +		else if(!strcmp(argv[i], "-sb")) { +			if(++i < argc) selbgcolor = argv[i]; +		} +		else if(!strcmp(argv[i], "-sf")) { +			if(++i < argc) selfgcolor = argv[i]; +		} +		else if(!strcmp(argv[i], "-v")) +			eprint("dinput-"VERSION", © 2006-2010 dinput engineers, see LICENSE for details\n"); +		else if(!*text) +			strncpy(text, argv[i], sizeof text); +		else +			eprint("usage: dinput [-b] [-e <xid>] [-fn <font>] [-nb <color>] [-nf <color>]\n" +			       "              [-p <prompt>] [-sb <color>] [-sf <color>] [-v] [<text>]\n"); +	if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) +		fprintf(stderr, "dinput: warning: no locale support\n"); +	if(!(dpy = XOpenDisplay(NULL))) +		eprint("dinput: cannot open display\n"); +	screen = DefaultScreen(dpy); +	if(!parent) +		parent = RootWindow(dpy, screen); + +	running = grabkeyboard(); +	setup(topbar); +	drawinput(); +	XSync(dpy, False); +	run(); +	cleanup(); +	XCloseDisplay(dpy); +	return ret; +} @@ -21,25 +21,6 @@  #define MAX(a, b)               ((a) > (b) ? (a) : (b))  #define IS_UTF8_1ST_CHAR(c)     ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) -/* enums */ -enum { ColFG, ColBG, ColLast }; - -/* typedefs */ -typedef struct { -	int x, y, w, h; -	unsigned long norm[ColLast]; -	unsigned long sel[ColLast]; -	Drawable drawable; -	GC gc; -	struct { -		XFontStruct *xfont; -		XFontSet set; -		int ascent; -		int descent; -		int height; -	} font; -} DC; /* draw context */ -  typedef struct Item Item;  struct Item {  	char *text; @@ -53,22 +34,16 @@ static void calcoffsetsh(void);  static void calcoffsetsv(void);  static char *cistrstr(const char *s, const char *sub);  static void cleanup(void); -static void drawcursor(void);  static void drawmenu(void);  static void drawmenuh(void);  static void drawmenuv(void); -static void drawtext(const char *text, unsigned long col[ColLast]);  static void eprint(const char *errstr, ...); -static unsigned long getcolor(const char *colstr);  static Bool grabkeyboard(void); -static void initfont(const char *fontstr);  static void kpress(XKeyEvent * e);  static void match(char *pattern);  static void readstdin(void);  static void run(void);  static void setup(Bool topbar); -static int textnw(const char *text, unsigned int len); -static int textw(const char *text);  #include "config.h" @@ -81,11 +56,9 @@ static int promptw = 0;  static int ret = 0;  static int screen;  static unsigned int mw, mh; -static unsigned int cursor = 0;  static unsigned int numlockmask = 0;  static Bool running = True;  static Display *dpy; -static DC dc;  static Item *allitems = NULL;  /* first of all items */  static Item *item = NULL;      /* first of pattern matching items */  static Item *sel = NULL; @@ -98,6 +71,8 @@ static char *(*fstrstr)(const char *, const char *) = strstr;  static unsigned int lines = 0;  static void (*calcoffsets)(void) = calcoffsetsh; +#include "draw.c" +  void  appenditem(Item *i, Item **list, Item **last) {  	if(!(*last)) @@ -161,27 +136,12 @@ cistrstr(const char *s, const char *sub) {  void  cleanup(void) { -	if(dc.font.set) -		XFreeFontSet(dpy, dc.font.set); -	else -		XFreeFont(dpy, dc.font.xfont); -	XFreePixmap(dpy, dc.drawable); -	XFreeGC(dpy, dc.gc); +	dccleanup();  	XDestroyWindow(dpy, win);  	XUngrabKeyboard(dpy, CurrentTime);  }  void -drawcursor(void) { -	XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 }; - -	r.x += textnw(text, cursor) + dc.font.height / 2; - -	XSetForeground(dpy, dc.gc, dc.norm[ColFG]); -	XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); -} - -void  drawmenu(void) {  	dc.x = 0;  	dc.y = 0; @@ -199,7 +159,6 @@ drawmenu(void) {  	if(cmdw && item && lines == 0)  		dc.w = cmdw;  	drawtext(*text ? text : NULL, dc.norm); -	drawcursor();  	if(curr) {  		if(lines > 0)  			drawmenuv(); @@ -244,34 +203,6 @@ drawmenuv(void) {  }  void -drawtext(const char *text, unsigned long col[ColLast]) { -	char buf[256]; -	int i, x, y, h, len, olen; -	XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - -	XSetForeground(dpy, dc.gc, col[ColBG]); -	XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); -	if(!text) -		return; -	olen = strlen(text); -	h = dc.font.height; -	y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent; -	x = dc.x + (h / 2); -	/* shorten text if necessary */ -	for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); -	if(!len) -		return; -	memcpy(buf, text, len); -	if(len < olen) -		for(i = len; i && i > len - 3; buf[--i] = '.'); -	XSetForeground(dpy, dc.gc, col[ColFG]); -	if(dc.font.set) -		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); -	else -		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); -} - -void  eprint(const char *errstr, ...) {  	va_list ap; @@ -281,16 +212,6 @@ eprint(const char *errstr, ...) {  	exit(EXIT_FAILURE);  } -unsigned long -getcolor(const char *colstr) { -	Colormap cmap = DefaultColormap(dpy, screen); -	XColor color; - -	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) -		eprint("dmenu: cannot allocate color '%s'\n", colstr); -	return color.pixel; -} -  Bool  grabkeyboard(void) {  	unsigned int len; @@ -305,37 +226,6 @@ grabkeyboard(void) {  }  void -initfont(const char *fontstr) { -	char *def, **missing = NULL; -	int i, n; - -	if(!fontstr || fontstr[0] == '\0') -		eprint("dmenu: cannot load font: '%s'\n", fontstr); -	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); -	if(missing) -		XFreeStringList(missing); -	if(dc.font.set) { -		XFontStruct **xfonts; -		char **font_names; -		dc.font.ascent = dc.font.descent = 0; -		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); -		for(i = 0; i < n; i++) { -			dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent); -			dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent); -			xfonts++; -		} -	} -	else { -		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) -		&& !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) -			eprint("dmenu: cannot load font: '%s'\n", fontstr); -		dc.font.ascent = dc.font.xfont->ascent; -		dc.font.descent = dc.font.xfont->descent; -	} -	dc.font.height = dc.font.ascent + dc.font.descent; -} - -void  kpress(XKeyEvent * e) {  	char buf[sizeof text];  	int num; @@ -381,9 +271,6 @@ kpress(XKeyEvent * e) {  		case XK_j:  			ksym = XK_Return;  			break; -		case XK_k: -			text[cursor] = '\0'; -			break;  		case XK_n:  			ksym = XK_Down;  			break; @@ -391,67 +278,42 @@ kpress(XKeyEvent * e) {  			ksym = XK_Up;  			break;  		case XK_u: -			memmove(text, text + cursor, sizeof text - cursor + 1); -			cursor = 0; +			text[0] = '\0';  			match(text);  			break;  		case XK_w: -			if(cursor > 0) { -				i = cursor; -				while(i-- > 0 && text[i] == ' '); -				while(i-- > 0 && text[i] != ' '); -				memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); -				cursor = i + 1; -				match(text); -			} +			if(len == 0) +				return; +			i = len; +			while(i-- > 0 && text[i] == ' '); +			while(i-- > 0 && text[i] != ' '); +			text[++i] = '\0'; +			match(text);  			break; -		case XK_y: -			{ -				FILE *fp; -				char *s; -				if(!(fp = popen("sselp", "r"))) -					eprint("dmenu: cannot popen sselp\n"); -				s = fgets(buf, sizeof buf, fp); -				pclose(fp); -				if(s == NULL) -					return; -			} -			num = strlen(buf); -			if(num && buf[num-1] == '\n') -				buf[--num] = '\0'; +		case XK_x: +			execlp("dinput", "dinput", text, NULL); /* todo: argv */ +			eprint("dmenu: cannot exec dinput:");  			break;  		}  	}  	switch(ksym) {  	default: -		num = MIN(num, sizeof text - cursor); +		num = MIN(num, sizeof text);  		if(num && !iscntrl((int) buf[0])) { -			memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); -			memcpy(text + cursor, buf, num); -			cursor += num; +			memcpy(text + len, buf, num + 1); +			len += num;  			match(text);  		}  		break;  	case XK_BackSpace: -		if(cursor == 0) +		if(len == 0)  			return; -		for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); -		memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); -		cursor -= i; -		match(text); -		break; -	case XK_Delete: -		if(cursor == len) -			return; -		for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++); -		memmove(text + cursor, text + cursor + i, sizeof text - cursor); +		for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++); +		len -= i; +		text[len] = '\0';  		match(text);  		break;  	case XK_End: -		if(cursor < len) { -			cursor = len; -			break; -		}  		while(next) {  			sel = curr = next;  			calcoffsets(); @@ -464,20 +326,10 @@ kpress(XKeyEvent * e) {  		running = False;  		return;  	case XK_Home: -		if(sel == item) { -			cursor = 0; -			break; -		}  		sel = curr = item;  		calcoffsets();  		break;  	case XK_Left: -		if(cursor > 0 && (!sel || !sel->left || lines > 0)) { -			while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); -			break; -		} -		if(lines > 0) -			return;  	case XK_Up:  		if(!sel || !sel->left)  			return; @@ -508,12 +360,6 @@ kpress(XKeyEvent * e) {  		running = False;  		return;  	case XK_Right: -		if(cursor < len) { -			while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); -			break; -		} -		if(lines > 0) -			return;  	case XK_Down:  		if(!sel || !sel->right)  			return; @@ -527,7 +373,6 @@ kpress(XKeyEvent * e) {  		if(!sel)  			return;  		strncpy(text, sel->text, sizeof text); -		cursor = strlen(text);  		match(text);  		break;  	} @@ -690,11 +535,7 @@ setup(Bool topbar) {  			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);  	/* pixmap */ -	dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); -	dc.gc = XCreateGC(dpy, parent, 0, NULL); -	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); -	if(!dc.font.set) -		XSetFont(dpy, dc.gc, dc.font.xfont->fid); +	dcsetup();  	if(maxname)  		cmdw = MIN(textw(maxname), mw / 3);  	if(prompt) @@ -705,22 +546,6 @@ setup(Bool topbar) {  }  int -textnw(const char *text, unsigned int len) { -	XRectangle r; - -	if(dc.font.set) { -		XmbTextExtents(dc.font.set, text, len, NULL, &r); -		return r.width; -	} -	return XTextWidth(dc.font.xfont, text, len); -} - -int -textw(const char *text) { -	return textnw(text, strlen(text)) + dc.font.height; -} - -int  main(int argc, char *argv[]) {  	unsigned int i;  	Bool topbar = True; @@ -0,0 +1,143 @@ +/* See LICENSE file for copyright and license details. */ + +/* enums */ +enum { ColFG, ColBG, ColLast }; + +/* typedefs */ +typedef struct { +	int x, y, w, h; +	unsigned long norm[ColLast]; +	unsigned long sel[ColLast]; +	Drawable drawable; +	GC gc; +	struct { +		XFontStruct *xfont; +		XFontSet set; +		int ascent; +		int descent; +		int height; +	} font; +} DC; /* draw context */ + +/* forward declarations */ +static void dccleanup(void); +static void dcsetup(void); +static void drawtext(const char *text, unsigned long col[ColLast]); +static unsigned long getcolor(const char *colstr); +static void initfont(const char *fontstr); +static int textnw(const char *text, unsigned int len); +static int textw(const char *text); + +static DC dc; + +void +dccleanup(void) { +	if(dc.font.set) +		XFreeFontSet(dpy, dc.font.set); +	else +		XFreeFont(dpy, dc.font.xfont); +	XFreePixmap(dpy, dc.drawable); +	XFreeGC(dpy, dc.gc); +} + +void +dcsetup() { +	/* style */ +	dc.norm[ColBG] = getcolor(normbgcolor); +	dc.norm[ColFG] = getcolor(normfgcolor); +	dc.sel[ColBG] = getcolor(selbgcolor); +	dc.sel[ColFG] = getcolor(selfgcolor); +	initfont(font); + +	/* pixmap */ +	dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); +	dc.gc = XCreateGC(dpy, parent, 0, NULL); +	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); +	if(!dc.font.set) +		XSetFont(dpy, dc.gc, dc.font.xfont->fid); +} + +void +drawtext(const char *text, unsigned long col[ColLast]) { +	char buf[256]; +	int i, x, y, h, len, olen; +	XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + +	XSetForeground(dpy, dc.gc, col[ColBG]); +	XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); +	if(!text) +		return; +	olen = strlen(text); +	h = dc.font.height; +	y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent; +	x = dc.x + (h / 2); +	/* shorten text if necessary */ +	for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); +	if(!len) +		return; +	memcpy(buf, text, len); +	if(len < olen) +		for(i = len; i && i > len - 3; buf[--i] = '.'); +	XSetForeground(dpy, dc.gc, col[ColFG]); +	if(dc.font.set) +		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); +	else +		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); +} + +unsigned long +getcolor(const char *colstr) { +	Colormap cmap = DefaultColormap(dpy, screen); +	XColor color; + +	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) +		eprint("drawtext: cannot allocate color '%s'\n", colstr); +	return color.pixel; +} + +void +initfont(const char *fontstr) { +	char *def, **missing = NULL; +	int i, n; + +	if(!fontstr || fontstr[0] == '\0') +		eprint("drawtext: cannot load font: '%s'\n", fontstr); +	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); +	if(missing) +		XFreeStringList(missing); +	if(dc.font.set) { +		XFontStruct **xfonts; +		char **font_names; +		dc.font.ascent = dc.font.descent = 0; +		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); +		for(i = 0; i < n; i++) { +			dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent); +			dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent); +			xfonts++; +		} +	} +	else { +		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) +		&& !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) +			eprint("drawtext: cannot load font: '%s'\n", fontstr); +		dc.font.ascent = dc.font.xfont->ascent; +		dc.font.descent = dc.font.xfont->descent; +	} +	dc.font.height = dc.font.ascent + dc.font.descent; +} + +int +textnw(const char *text, unsigned int len) { +	XRectangle r; + +	if(dc.font.set) { +		XmbTextExtents(dc.font.set, text, len, NULL, &r); +		return r.width; +	} +	return XTextWidth(dc.font.xfont, text, len); +} + +int +textw(const char *text) { +	return textnw(text, strlen(text)) + dc.font.height; +} | 
