cmdtree

A trie command launcher for X11
git clone git://jb55.com/cmdtree
Log | Files | Refs | README | LICENSE

cmdtree.c (7373B)


      1 #include <stdarg.h>
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <time.h>
      6 
      7 #include <X11/Xlib.h>
      8 #include <X11/Xutil.h>
      9 #include <X11/Xresource.h>
     10 #include <X11/Xft/Xft.h>
     11 
     12 #include <unistd.h>
     13 #include <assert.h>
     14 
     15 #include "drw.h"
     16 #include "util.h"
     17 #include "command.h"
     18 
     19 enum {
     20 	SchemeNorm,
     21 	SchemePrefix,
     22 	SchemeLast,
     23 }; /* color schemes */
     24 
     25 
     26 struct cmdstack {
     27 	struct command *children;
     28 	int nchildren;
     29 };
     30 
     31 #define CMDSTACK_SIZE 32
     32 static struct cmdstack cmdstack[CMDSTACK_SIZE];
     33 static int cmdstack_ptr = 0;
     34 
     35 static Window root, parentwin, win;
     36 static int screen;
     37 static Display *display;
     38 static int sep_width;
     39 static int bh, mw, mh;
     40 static XIC xic;
     41 
     42 #define DEFCMD(b, nme, cmd)			\
     43 	{ .bind = (b),				\
     44 			.name = (nme),		\
     45 			.command = (cmd),	\
     46 			.nchildren = 0,		\
     47 			.children = NULL,	\
     48 			},
     49 
     50 #define DEFPREFIX(b, nme, cs)				\
     51 	{ .bind = b,					\
     52 			.name = nme,			\
     53 			.command = 0,			\
     54 			.nchildren = LENGTH(cs),	\
     55 			.children = (cs),		\
     56 			},
     57 
     58 #include "cfg.h"
     59 
     60 static int
     61 cmdstack_push_(struct command *children, int nchildren) {
     62 	if (cmdstack_ptr + 1 >= CMDSTACK_SIZE)
     63 		return 0;
     64 	cmdstack[cmdstack_ptr].children = children;
     65 	cmdstack[cmdstack_ptr].nchildren = nchildren;
     66 	cmdstack_ptr++;
     67 	return 1;
     68 }
     69 
     70 static int
     71 cmdstack_push(struct command *cmd) {
     72 	return cmdstack_push_(cmd->children, cmd->nchildren);
     73 }
     74 
     75 struct cmdstack *
     76 cmdstack_top() {
     77 	assert(cmdstack_ptr >= 1);
     78 	return &cmdstack[cmdstack_ptr-1];
     79 }
     80 
     81 struct cmdstack *
     82 cmdstack_pop() {
     83 	if (cmdstack_ptr == 1)
     84 		return 0;
     85 
     86 	return &cmdstack[cmdstack_ptr--];
     87 }
     88 
     89 static void
     90 grabkeyboard(void)
     91 {
     92 	struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000  };
     93 	int i;
     94 
     95 	// XXXembed
     96 	/* if (embed) */
     97 	/* 	return; */
     98 	/* try to grab keyboard, we may have to wait for another process to ungrab */
     99 	for (i = 0; i < 1000; i++) {
    100 		if (XGrabKeyboard(display, DefaultRootWindow(display), True,
    101 				  GrabModeAsync, GrabModeAsync, CurrentTime)
    102 		    == GrabSuccess)
    103 			return;
    104 		nanosleep(&ts, NULL);
    105 	}
    106 	die("cannot grab keyboard");
    107 }
    108 
    109 static void
    110 setup(Drw *drw)
    111 {
    112 	int x, y;
    113 	XSetWindowAttributes swa;
    114 	XWindowAttributes wa;
    115 	XIM xim;
    116 	XClassHint ch = {"cmdtree", "cmdtree"};
    117 
    118 	bh = drw->fonts->h + 2;
    119 	/* init appearance */
    120 	drw_scm_create(drw, schemes, LENGTH(schemes));
    121 
    122 	if (!XGetWindowAttributes(display, parentwin, &wa))
    123 		die("could not get embedding window attributes: 0x%lx",
    124 		    parentwin);
    125 	int vertwidth = 200;
    126 	mw = wa.width;
    127 	mh = 1 * bh;
    128 	switch (position) {
    129 	case POSITION_BOTTOM:
    130 		y = wa.height - mh;
    131 		x = 0;
    132 		break;
    133 	case POSITION_TOP:
    134 		x = 0;
    135 		y = 0;
    136 		break;
    137 	case POSITION_LEFT:
    138 		x = 0;
    139 		y = 0;
    140 		// TODO: calc vertwidth = max(textwidth(nodes)))
    141 		mw = vertwidth;
    142 		mh = wa.height;
    143 		break;
    144 	case POSITION_RIGHT:
    145 		x = wa.width - vertwidth;
    146 		y = 0;
    147 		mw = vertwidth;
    148 		mh = wa.height;
    149 	}
    150 	sep_width = drw_fontset_getwidth(drw, separator);
    151 
    152 	swa.override_redirect = True;
    153 	swa.background_pixel = schemes[SchemeNorm].bg_clr.pixel;
    154 	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
    155 
    156 	win = XCreateWindow(display, parentwin, x, y, mw, mh, 0,
    157 	                    CopyFromParent, CopyFromParent, CopyFromParent,
    158 	                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
    159 
    160 	XSetClassHint(display, win, &ch);
    161 
    162 	// what do?
    163 	XMapRaised(display, win);
    164 
    165 	xim = XOpenIM(display, NULL, NULL, NULL);
    166 	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
    167 			XNClientWindow, win, XNFocusWindow, win, NULL);
    168 	XSelectInput(display, win, ExposureMask | KeyPressMask);
    169 	XMapWindow(display, win);
    170 }
    171 
    172 static const char *
    173 bind_name(const char *bind) {
    174 	switch (*bind) {
    175 	case ' ': return "SPC";
    176 	default:  return bind;
    177 	}
    178 }
    179 
    180 static int
    181 draw_command(Drw *drw, int x, int y, struct command *cmd) {
    182 	char buf[256];
    183 	int lpad = 0;
    184 	int pad = 0;
    185 	unsigned int w;
    186 	int invert = 0;
    187 	int isprefix = 0;
    188 	char *name = cmd->name;
    189 	struct scheme *scheme = NULL;
    190 
    191 	//drw_fontset_getwidth(drw, binding) + 1;
    192 
    193 	drw_setscheme(drw, &schemes[SchemeNorm].bind_clr,
    194 		      &schemes[SchemeNorm].bg_clr);
    195 
    196 	const char *bindname = bind_name(cmd->bind);
    197 
    198 	w = drw_fontset_getwidth(drw, bindname);
    199 	x = drw_text(drw, x+pad, y, w, bh, lpad, bindname, invert);
    200 
    201 	w = sep_width;
    202 	drw_setscheme(drw, &schemes[SchemeNorm].arrow_clr,
    203 		      &schemes[SchemeNorm].bg_clr);
    204 	x = drw_text(drw, x+pad, y, w, bh, lpad, separator, invert);
    205 
    206 	isprefix = command_is_prefix(cmd);
    207 
    208 	if (isprefix)
    209 		scheme = &schemes[SchemePrefix];
    210 	else
    211 		scheme = &schemes[SchemeNorm];
    212 
    213 	drw_setscheme(drw, &scheme->name_clr, &scheme->bg_clr);
    214 
    215 	if (scheme->prefix && strlen(scheme->prefix) != 0) {
    216 		snprintf(buf, 256, "%s%s", scheme->prefix, cmd->name);
    217 		name = buf;
    218 	}
    219 
    220 	w = drw_fontset_getwidth(drw, name);
    221 	x = drw_text(drw, x+pad, y, w, bh, lpad, name, invert);
    222 
    223 	return x;
    224 }
    225 
    226 
    227 /* static void */
    228 /* calc_tree_exts(struct node *nodes, int num_nodes, int *rows, int *cols) { */
    229 /* } */
    230 static void
    231 draw_tree_vertical(Drw *drw, struct command *cmds, int ncmds, int x, int y, int w, int h) {
    232 	int i;
    233 	int colw = 0;
    234 
    235 	x += xpad;
    236 	y += ypad;
    237 
    238 	drw_setscheme(drw, &schemes[SchemeNorm].bg_clr,
    239 		      &schemes[SchemeNorm].bg_clr);
    240 	drw_rect(drw, 0, 0, w, h, 1, 1);
    241 
    242 	int c = '0';
    243 
    244 	for (i = 0; i < ncmds; ++i, ++c, y += bh) {
    245 		struct command *cmd = &cmds[i];
    246 		if (y >= mh) {
    247 			x = colw;
    248 			y = 0;
    249 			colw = 0;
    250 		}
    251 		colw = MAX(draw_command(drw, x, y, cmd) + 20, colw);
    252 	}
    253 }
    254 
    255 
    256 static void
    257 draw_tree(Drw *drw, int x, int y, int w, int h) {
    258 	if (!drw)
    259 		return;
    260 
    261 	struct cmdstack *cmdstack = cmdstack_top();
    262 
    263 	draw_tree_vertical(drw, cmdstack->children, cmdstack->nchildren, x, y,
    264 			   w, h);
    265 
    266 	XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
    267 	XSync(drw->dpy, False);
    268 }
    269 
    270 
    271 static void
    272 cleanup(Drw *drw, int code) {
    273 	drw_free(drw);
    274 	exit(code);
    275 }
    276 
    277 static void
    278 run(Drw *drw) {
    279 	XEvent e;
    280 	int done = 0;
    281 	char buf[32];
    282 	KeySym ksym = NoSymbol;
    283 	Status status;
    284 	int code = 0;
    285 	struct cmdstack *cmdstack = NULL;
    286 	struct command *cmd = NULL;
    287 
    288 	while (!done) {
    289 		cmd = NULL;
    290 		XNextEvent(display, &e);
    291 		switch (e.type) {
    292 		case Expose:
    293 			draw_tree(drw, 0, 0, mw, mh);
    294 			break;
    295 		case KeyPress:
    296 			XmbLookupString(xic, (XKeyEvent*)&e, buf, sizeof buf,
    297 					&ksym, &status);
    298 
    299 			if (ksym == XK_Escape) {
    300 				code = 1;
    301 				done = 1;
    302 				break;
    303 			}
    304 			else if (ksym == XK_BackSpace) {
    305 				if (cmdstack_pop())
    306 					draw_tree(drw, 0, 0, mw, mh);
    307 			}
    308 
    309 			cmdstack = cmdstack_top();
    310 			cmd = command_lookup(cmdstack->children,
    311 					     cmdstack->nchildren, buf);
    312 
    313 			if (cmd) {
    314 				if (command_is_prefix(cmd)) {
    315 					cmdstack_push(cmd);
    316 					draw_tree(drw, 0, 0, mw, mh);
    317 				}
    318 				else {
    319 					command_exec(cmd);
    320 				}
    321 			}
    322 
    323 			break;
    324 		}
    325 	}
    326 
    327 	cleanup(drw, code);
    328 }
    329 
    330 
    331 int main(void) {
    332 	XWindowAttributes wa;
    333 	Drw *drw;
    334 
    335 	display = XOpenDisplay(NULL);
    336 	if (display == NULL) {
    337 		fprintf(stderr, "Cannot open display\n");
    338 		exit(1);
    339 	}
    340 
    341 	screen = DefaultScreen(display);
    342 	root = RootWindow(display, screen);
    343 
    344 	parentwin = root;
    345 
    346 	int res = cmdstack_push_(commands, LENGTH(commands));
    347 	assert(res);
    348 
    349 	if (!XGetWindowAttributes(display, parentwin, &wa))
    350 		die("could not get embedding window attributes: 0x%lx",
    351 		    parentwin);
    352 
    353 	drw = drw_create(display, screen, root, wa.width, wa.height);
    354 
    355 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
    356 		die("no fonts could be loaded.");
    357 
    358 	grabkeyboard();
    359 	setup(drw);
    360 	run(drw);
    361 
    362 	XCloseDisplay(display);
    363 	return 0;
    364 }