initial dmenu / dinput separation
							parent
							
								
									bff1526d31
								
							
						
					
					
						commit
						b4e63454e5
					
				
							
								
								
									
										17
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										17
									
								
								Makefile
								
								
								
								
							|  | @ -3,10 +3,10 @@ | ||||||
| 
 | 
 | ||||||
| include config.mk | include config.mk | ||||||
| 
 | 
 | ||||||
| SRC = dmenu.c | SRC = dinput.c dmenu.c draw.c | ||||||
| OBJ = ${SRC:.c=.o} | OBJ = ${SRC:.c=.o} | ||||||
| 
 | 
 | ||||||
| all: options dmenu | all: options dinput dmenu | ||||||
| 
 | 
 | ||||||
| options: | options: | ||||||
| 	@echo dmenu build options: | 	@echo dmenu build options: | ||||||
|  | @ -18,19 +18,19 @@ options: | ||||||
| 	@echo CC $< | 	@echo CC $< | ||||||
| 	@${CC} -c ${CFLAGS} $< | 	@${CC} -c ${CFLAGS} $< | ||||||
| 
 | 
 | ||||||
| ${OBJ}: config.h config.mk | ${OBJ}: config.h config.mk draw.c | ||||||
| 
 | 
 | ||||||
| config.h: | config.h: | ||||||
| 	@echo creating $@ from config.def.h | 	@echo creating $@ from config.def.h | ||||||
| 	@cp config.def.h $@ | 	@cp config.def.h $@ | ||||||
| 
 | 
 | ||||||
| dmenu: ${OBJ} | .o: | ||||||
| 	@echo CC -o $@ | 	@echo CC -o $@ | ||||||
| 	@${CC} -o $@ ${OBJ} ${LDFLAGS} | 	@${CC} -o $@ $< ${LDFLAGS} | ||||||
| 
 | 
 | ||||||
| clean: | clean: | ||||||
| 	@echo cleaning | 	@echo cleaning | ||||||
| 	@rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz | 	@rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz | ||||||
| 
 | 
 | ||||||
| dist: clean | dist: clean | ||||||
| 	@echo creating dist tarball | 	@echo creating dist tarball | ||||||
|  | @ -43,7 +43,8 @@ dist: clean | ||||||
| install: all | install: all | ||||||
| 	@echo installing executable file to ${DESTDIR}${PREFIX}/bin | 	@echo installing executable file to ${DESTDIR}${PREFIX}/bin | ||||||
| 	@mkdir -p ${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 | ||||||
| 	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path | 	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path | ||||||
| 	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run | 	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run | ||||||
|  | @ -55,7 +56,7 @@ install: all | ||||||
| uninstall: | uninstall: | ||||||
| 	@echo removing executable file from ${DESTDIR}${PREFIX}/bin | 	@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_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 | 	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 | ||||||
| 	@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 	@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | } | ||||||
							
								
								
									
										219
									
								
								dmenu.c
								
								
								
								
							
							
						
						
									
										219
									
								
								dmenu.c
								
								
								
								
							|  | @ -21,25 +21,6 @@ | ||||||
| #define MAX(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)) | #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; | typedef struct Item Item; | ||||||
| struct Item { | struct Item { | ||||||
| 	char *text; | 	char *text; | ||||||
|  | @ -53,22 +34,16 @@ static void calcoffsetsh(void); | ||||||
| static void calcoffsetsv(void); | static void calcoffsetsv(void); | ||||||
| static char *cistrstr(const char *s, const char *sub); | static char *cistrstr(const char *s, const char *sub); | ||||||
| static void cleanup(void); | static void cleanup(void); | ||||||
| static void drawcursor(void); |  | ||||||
| static void drawmenu(void); | static void drawmenu(void); | ||||||
| static void drawmenuh(void); | static void drawmenuh(void); | ||||||
| static void drawmenuv(void); | static void drawmenuv(void); | ||||||
| static void drawtext(const char *text, unsigned long col[ColLast]); |  | ||||||
| static void eprint(const char *errstr, ...); | static void eprint(const char *errstr, ...); | ||||||
| static unsigned long getcolor(const char *colstr); |  | ||||||
| static Bool grabkeyboard(void); | static Bool grabkeyboard(void); | ||||||
| static void initfont(const char *fontstr); |  | ||||||
| static void kpress(XKeyEvent * e); | static void kpress(XKeyEvent * e); | ||||||
| static void match(char *pattern); | static void match(char *pattern); | ||||||
| static void readstdin(void); | static void readstdin(void); | ||||||
| static void run(void); | static void run(void); | ||||||
| static void setup(Bool topbar); | static void setup(Bool topbar); | ||||||
| static int textnw(const char *text, unsigned int len); |  | ||||||
| static int textw(const char *text); |  | ||||||
| 
 | 
 | ||||||
| #include "config.h" | #include "config.h" | ||||||
| 
 | 
 | ||||||
|  | @ -81,11 +56,9 @@ static int promptw = 0; | ||||||
| static int ret = 0; | static int ret = 0; | ||||||
| static int screen; | static int screen; | ||||||
| static unsigned int mw, mh; | static unsigned int mw, mh; | ||||||
| static unsigned int cursor = 0; |  | ||||||
| static unsigned int numlockmask = 0; | static unsigned int numlockmask = 0; | ||||||
| static Bool running = True; | static Bool running = True; | ||||||
| static Display *dpy; | static Display *dpy; | ||||||
| static DC dc; |  | ||||||
| static Item *allitems = NULL;  /* first of all items */ | static Item *allitems = NULL;  /* first of all items */ | ||||||
| static Item *item = NULL;      /* first of pattern matching items */ | static Item *item = NULL;      /* first of pattern matching items */ | ||||||
| static Item *sel = NULL; | static Item *sel = NULL; | ||||||
|  | @ -98,6 +71,8 @@ static char *(*fstrstr)(const char *, const char *) = strstr; | ||||||
| static unsigned int lines = 0; | static unsigned int lines = 0; | ||||||
| static void (*calcoffsets)(void) = calcoffsetsh; | static void (*calcoffsets)(void) = calcoffsetsh; | ||||||
| 
 | 
 | ||||||
|  | #include "draw.c" | ||||||
|  | 
 | ||||||
| void | void | ||||||
| appenditem(Item *i, Item **list, Item **last) { | appenditem(Item *i, Item **list, Item **last) { | ||||||
| 	if(!(*last)) | 	if(!(*last)) | ||||||
|  | @ -161,26 +136,11 @@ cistrstr(const char *s, const char *sub) { | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| cleanup(void) { | cleanup(void) { | ||||||
| 	if(dc.font.set) | 	dccleanup(); | ||||||
| 		XFreeFontSet(dpy, dc.font.set); |  | ||||||
| 	else |  | ||||||
| 		XFreeFont(dpy, dc.font.xfont); |  | ||||||
| 	XFreePixmap(dpy, dc.drawable); |  | ||||||
| 	XFreeGC(dpy, dc.gc); |  | ||||||
| 	XDestroyWindow(dpy, win); | 	XDestroyWindow(dpy, win); | ||||||
| 	XUngrabKeyboard(dpy, CurrentTime); | 	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 | void | ||||||
| drawmenu(void) { | drawmenu(void) { | ||||||
| 	dc.x = 0; | 	dc.x = 0; | ||||||
|  | @ -199,7 +159,6 @@ drawmenu(void) { | ||||||
| 	if(cmdw && item && lines == 0) | 	if(cmdw && item && lines == 0) | ||||||
| 		dc.w = cmdw; | 		dc.w = cmdw; | ||||||
| 	drawtext(*text ? text : NULL, dc.norm); | 	drawtext(*text ? text : NULL, dc.norm); | ||||||
| 	drawcursor(); |  | ||||||
| 	if(curr) { | 	if(curr) { | ||||||
| 		if(lines > 0) | 		if(lines > 0) | ||||||
| 			drawmenuv(); | 			drawmenuv(); | ||||||
|  | @ -243,34 +202,6 @@ drawmenuv(void) { | ||||||
| 	drawtext(NULL, dc.norm); | 	drawtext(NULL, dc.norm); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 | void | ||||||
| eprint(const char *errstr, ...) { | eprint(const char *errstr, ...) { | ||||||
| 	va_list ap; | 	va_list ap; | ||||||
|  | @ -281,16 +212,6 @@ eprint(const char *errstr, ...) { | ||||||
| 	exit(EXIT_FAILURE); | 	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 | Bool | ||||||
| grabkeyboard(void) { | grabkeyboard(void) { | ||||||
| 	unsigned int len; | 	unsigned int len; | ||||||
|  | @ -304,37 +225,6 @@ grabkeyboard(void) { | ||||||
| 	return len > 0; | 	return len > 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 | void | ||||||
| kpress(XKeyEvent * e) { | kpress(XKeyEvent * e) { | ||||||
| 	char buf[sizeof text]; | 	char buf[sizeof text]; | ||||||
|  | @ -381,9 +271,6 @@ kpress(XKeyEvent * e) { | ||||||
| 		case XK_j: | 		case XK_j: | ||||||
| 			ksym = XK_Return; | 			ksym = XK_Return; | ||||||
| 			break; | 			break; | ||||||
| 		case XK_k: |  | ||||||
| 			text[cursor] = '\0'; |  | ||||||
| 			break; |  | ||||||
| 		case XK_n: | 		case XK_n: | ||||||
| 			ksym = XK_Down; | 			ksym = XK_Down; | ||||||
| 			break; | 			break; | ||||||
|  | @ -391,67 +278,42 @@ kpress(XKeyEvent * e) { | ||||||
| 			ksym = XK_Up; | 			ksym = XK_Up; | ||||||
| 			break; | 			break; | ||||||
| 		case XK_u: | 		case XK_u: | ||||||
| 			memmove(text, text + cursor, sizeof text - cursor + 1); | 			text[0] = '\0'; | ||||||
| 			cursor = 0; |  | ||||||
| 			match(text); | 			match(text); | ||||||
| 			break; | 			break; | ||||||
| 		case XK_w: | 		case XK_w: | ||||||
| 			if(cursor > 0) { | 			if(len == 0) | ||||||
| 				i = cursor; | 				return; | ||||||
| 				while(i-- > 0 && text[i] == ' '); | 			i = len; | ||||||
| 				while(i-- > 0 && text[i] != ' '); | 			while(i-- > 0 && text[i] == ' '); | ||||||
| 				memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); | 			while(i-- > 0 && text[i] != ' '); | ||||||
| 				cursor = i + 1; | 			text[++i] = '\0'; | ||||||
| 				match(text); | 			match(text); | ||||||
| 			} |  | ||||||
| 			break; | 			break; | ||||||
| 		case XK_y: | 		case XK_x: | ||||||
| 			{ | 			execlp("dinput", "dinput", text, NULL); /* todo: argv */ | ||||||
| 				FILE *fp; | 			eprint("dmenu: cannot exec dinput:"); | ||||||
| 				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'; |  | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	switch(ksym) { | 	switch(ksym) { | ||||||
| 	default: | 	default: | ||||||
| 		num = MIN(num, sizeof text - cursor); | 		num = MIN(num, sizeof text); | ||||||
| 		if(num && !iscntrl((int) buf[0])) { | 		if(num && !iscntrl((int) buf[0])) { | ||||||
| 			memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); | 			memcpy(text + len, buf, num + 1); | ||||||
| 			memcpy(text + cursor, buf, num); | 			len += num; | ||||||
| 			cursor += num; |  | ||||||
| 			match(text); | 			match(text); | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case XK_BackSpace: | 	case XK_BackSpace: | ||||||
| 		if(cursor == 0) | 		if(len == 0) | ||||||
| 			return; | 			return; | ||||||
| 		for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); | 		for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++); | ||||||
| 		memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); | 		len -= i; | ||||||
| 		cursor -= i; | 		text[len] = '\0'; | ||||||
| 		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); |  | ||||||
| 		match(text); | 		match(text); | ||||||
| 		break; | 		break; | ||||||
| 	case XK_End: | 	case XK_End: | ||||||
| 		if(cursor < len) { |  | ||||||
| 			cursor = len; |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		while(next) { | 		while(next) { | ||||||
| 			sel = curr = next; | 			sel = curr = next; | ||||||
| 			calcoffsets(); | 			calcoffsets(); | ||||||
|  | @ -464,20 +326,10 @@ kpress(XKeyEvent * e) { | ||||||
| 		running = False; | 		running = False; | ||||||
| 		return; | 		return; | ||||||
| 	case XK_Home: | 	case XK_Home: | ||||||
| 		if(sel == item) { |  | ||||||
| 			cursor = 0; |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		sel = curr = item; | 		sel = curr = item; | ||||||
| 		calcoffsets(); | 		calcoffsets(); | ||||||
| 		break; | 		break; | ||||||
| 	case XK_Left: | 	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: | 	case XK_Up: | ||||||
| 		if(!sel || !sel->left) | 		if(!sel || !sel->left) | ||||||
| 			return; | 			return; | ||||||
|  | @ -508,12 +360,6 @@ kpress(XKeyEvent * e) { | ||||||
| 		running = False; | 		running = False; | ||||||
| 		return; | 		return; | ||||||
| 	case XK_Right: | 	case XK_Right: | ||||||
| 		if(cursor < len) { |  | ||||||
| 			while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		if(lines > 0) |  | ||||||
| 			return; |  | ||||||
| 	case XK_Down: | 	case XK_Down: | ||||||
| 		if(!sel || !sel->right) | 		if(!sel || !sel->right) | ||||||
| 			return; | 			return; | ||||||
|  | @ -527,7 +373,6 @@ kpress(XKeyEvent * e) { | ||||||
| 		if(!sel) | 		if(!sel) | ||||||
| 			return; | 			return; | ||||||
| 		strncpy(text, sel->text, sizeof text); | 		strncpy(text, sel->text, sizeof text); | ||||||
| 		cursor = strlen(text); |  | ||||||
| 		match(text); | 		match(text); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  | @ -690,11 +535,7 @@ setup(Bool topbar) { | ||||||
| 			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | 			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||||||
| 
 | 
 | ||||||
| 	/* pixmap */ | 	/* pixmap */ | ||||||
| 	dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); | 	dcsetup(); | ||||||
| 	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); |  | ||||||
| 	if(maxname) | 	if(maxname) | ||||||
| 		cmdw = MIN(textw(maxname), mw / 3); | 		cmdw = MIN(textw(maxname), mw / 3); | ||||||
| 	if(prompt) | 	if(prompt) | ||||||
|  | @ -704,22 +545,6 @@ setup(Bool topbar) { | ||||||
| 	XMapRaised(dpy, win); | 	XMapRaised(dpy, win); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 | int | ||||||
| main(int argc, char *argv[]) { | main(int argc, char *argv[]) { | ||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue