thinglaunch

A simple command and password prompter for X11.
git clone git://r-36.net/thinglaunch
Log | Files | Refs | LICENSE

thinglaunch.c (10624B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  *
      5  * For now this is a slightly modified version of the original from
      6  * Matt Johnston <matt@ucc.asn.au>. See LICENSE.orig for his messages.
      7  */
      8 
      9 #include <X11/keysym.h>
     10 #include <X11/Xlib.h>
     11 #include <X11/Xatom.h>
     12 #include <X11/Xutil.h>
     13 #include <X11/Xlocale.h>
     14 
     15 #include <errno.h>
     16 #include <libgen.h>
     17 #include <locale.h>
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <stdarg.h>
     21 #include <string.h>
     22 #include <strings.h>
     23 #include <unistd.h>
     24 #include <wchar.h>
     25 
     26 #include "arg.h"
     27 #include "config.h"
     28 
     29 unsigned long getcolor(const char *colstr);
     30 XIMStyle choosebetterstyle(XIMStyle style1, XIMStyle style2);
     31 void initim(void);
     32 void createwindow(void);
     33 void setupgc(void);
     34 void eventloop(void);
     35 void grabhack(void);
     36 void redraw(void);
     37 void keypress(XKeyEvent *keyevent);
     38 void execcmd(void);
     39 void die(char *errstr, ...);
     40 
     41 Display *dpy;
     42 GC gc;
     43 GC rectgc;
     44 XIM im;
     45 XIC ic;
     46 Window win;
     47 XFontStruct *font_info;
     48 XFontSet fontset;
     49 int screen, issecret = 0, tostdout = 0;
     50 unsigned long fgcol, bgcol;
     51 static char *name = "thinglaunch";
     52 
     53 char *argv0;
     54 
     55 #define MAXCMD 255
     56 #define WINWIDTH 640
     57 #define WINHEIGHT 25
     58 
     59 /* the actual commandline */
     60 wchar_t command[MAXCMD+1];
     61 wchar_t secret[MAXCMD+1];
     62 char cbuf[MAXCMD*4+1];
     63 
     64 void
     65 usage(void)
     66 {
     67 	fprintf(stderr, "usage: %s [-hos] [-p prompt]\n", argv0);
     68 	exit(1);
     69 }
     70 
     71 int
     72 main(int argc, char *argv[])
     73 {
     74 	char promptb[256];
     75 
     76 	if (strstr(argv[0], "thingaskpass")) {
     77 		issecret = 1;
     78 		tostdout = 1;
     79 		prompt = "secret> ";
     80 	}
     81 	if (strstr(argv[0], "thingsudoaskpass")) {
     82 		issecret = 1;
     83 		tostdout = 1;
     84 		if (argc > 1) {
     85 			snprintf(promptb, sizeof(promptb),
     86 					"sudo('%s')> ", argv[1]);
     87 			prompt = promptb;
     88 		} else {
     89 			prompt = "sudo> ";
     90 		}
     91 		argc = 0;
     92 	}
     93 
     94 	if (argc > 1) {
     95 		ARGBEGIN {
     96 		case 'o':
     97 			tostdout = 1;
     98 			break;
     99 		case 's':
    100 			issecret = 1;
    101 			break;
    102 		case 'p':
    103 			prompt = EARGF(usage());
    104 			break;
    105 		default:
    106 		case 'h':
    107 			usage();
    108 			break;
    109 		} ARGEND;
    110 
    111 		if (argc > 0)
    112 			prompt = argv[0];
    113 	}
    114 
    115 	bzero(command, sizeof(command));
    116 	bzero(secret, sizeof(secret));
    117 
    118 	createwindow();
    119 	setupgc();
    120 	grabhack();
    121 	eventloop();
    122 
    123 	return 0;
    124 }
    125 
    126 unsigned long
    127 getcolor(const char *colstr)
    128 {
    129 	Colormap cmap = DefaultColormap(dpy, screen);
    130 	XColor color;
    131 
    132 	if (!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
    133 		die("error, cannot allocate color '%s'\n", colstr);
    134 	return color.pixel;
    135 }
    136 
    137 /*
    138  * Stolen from:
    139  * http://menehune.opt.wfu.edu/Kokua/Irix_6.5.21_doc_cd/usr/share/\
    140  * Insight/library/SGI_bookshelves/SGI_Developer/books/XLib_PG/sgi_\
    141  * html/ch11.html#S2-1002-11-11
    142  */
    143 XIMStyle
    144 choosebetterstyle(XIMStyle style1, XIMStyle style2)
    145 {
    146 	XIMStyle s,t;
    147 	XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks |
    148 		XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
    149 	XIMStyle status = XIMStatusArea | XIMStatusCallbacks |
    150 		XIMStatusNothing | XIMStatusNone;
    151 	if (style1 == 0) return style2;
    152 	if (style2 == 0) return style1;
    153 	if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
    154 		return style1;
    155 	s = style1 & preedit;
    156 	t = style2 & preedit;
    157 	if (s != t) {
    158 		if (s | t | XIMPreeditCallbacks)
    159 			return (s == XIMPreeditCallbacks)?style1:style2;
    160 		else if (s | t | XIMPreeditPosition)
    161 			return (s == XIMPreeditPosition)?style1:style2;
    162 		else if (s | t | XIMPreeditArea)
    163 			return (s == XIMPreeditArea)?style1:style2;
    164 		else if (s | t | XIMPreeditNothing)
    165 			return (s == XIMPreeditNothing)?style1:style2;
    166 	}
    167 	else { /* if preedit flags are the same, compare status flags */
    168 		s = style1 & status;
    169 		t = style2 & status;
    170 		if (s | t | XIMStatusCallbacks)
    171 			return (s == XIMStatusCallbacks)?style1:style2;
    172 		else if (s | t | XIMStatusArea)
    173 			return (s == XIMStatusArea)?style1:style2;
    174 		else if (s | t | XIMStatusNothing)
    175 			return (s == XIMStatusNothing)?style1:style2;
    176 	}
    177 }
    178 
    179 void
    180 initim(void)
    181 {
    182 	XIMStyles *im_supported_styles;
    183 	XIMStyle app_supported_styles;
    184 	XIMStyle style;
    185 	XIMStyle best_style;
    186 	XVaNestedList list;
    187 	char **missing_charsets;
    188 	int num_missing_charsets = 0;
    189 	char *default_string;
    190 	int i;
    191 
    192 	fontset = XCreateFontSet(dpy, font, &missing_charsets,
    193 			&num_missing_charsets, &default_string);
    194 	if (num_missing_charsets > 0)
    195 		XFreeStringList(missing_charsets);
    196 
    197 	if (!(im = XOpenIM(dpy, NULL, NULL, NULL)))
    198 		die("Couldn't open input method.\n");
    199 
    200 	XGetIMValues(im, XNQueryInputStyle, &im_supported_styles, NULL);
    201 	app_supported_styles = XIMPreeditNone | XIMPreeditNothing \
    202 	       | XIMPreeditArea;
    203 	app_supported_styles |= XIMStatusNone | XIMStatusNothing \
    204 				| XIMStatusArea;
    205 
    206 	for(i = 0, best_style = 0; i < im_supported_styles->count_styles;
    207 			i++) {
    208 		style = im_supported_styles->supported_styles[i];
    209 		if ((style & app_supported_styles) == style)
    210 			best_style = choosebetterstyle(style, best_style);
    211 	}
    212 	if (best_style == 0)
    213 		die("no common shared interaction style found.\n");
    214 	XFree(im_supported_styles);
    215 
    216 	list = XVaCreateNestedList(0, XNFontSet, fontset, NULL);
    217 	ic = XCreateIC(im, XNInputStyle, best_style, XNClientWindow, win,
    218 			XNPreeditAttributes, list, XNStatusAttributes,
    219 			list, NULL);
    220 	XFree(list);
    221 	if (ic == NULL)
    222 		die("Could not create input context.\n");
    223 }
    224 
    225 void
    226 createwindow(void)
    227 {
    228 	char *display_name;
    229 	int display_width, display_height;
    230 	int top, left;
    231 	XSizeHints *win_size_hints;
    232 	XSetWindowAttributes attrib;
    233 	XClassHint *ch;
    234 	XTextProperty str;
    235 
    236 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
    237 		fprintf(stderr, "warning: no locale support.\n");
    238 
    239 	display_name = getenv("DISPLAY");
    240 	if (display_name == NULL)
    241 		die("DISPLAY not set.\n");
    242 
    243 	dpy = XOpenDisplay(display_name);
    244 	if (dpy == NULL)
    245 		die("Couldn't connect to DISPLAY.\n");
    246 
    247 	if (!XSetLocaleModifiers(""))
    248 		fprintf(stderr, "warning: could not set local modifiers.\n");
    249 
    250 	initim();
    251 
    252 	screen = DefaultScreen(dpy);
    253 	display_width = DisplayWidth(dpy, screen);
    254 	display_height = DisplayHeight(dpy, screen);
    255 
    256 	top = (display_height/2 - WINHEIGHT/2);
    257 	left = (display_width/2 - WINWIDTH/2);
    258 
    259 	bgcol = getcolor(normbgcolor);
    260 	fgcol = getcolor(normfgcolor);
    261 
    262 	/*win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen),
    263 			left, top, WINWIDTH, WINHEIGHT, borderwidth,
    264 			bgcol, bgcol);*/
    265 
    266 	attrib.override_redirect = True;
    267 	win = XCreateWindow(dpy, RootWindow(dpy, screen),
    268 			left, top, WINWIDTH, WINHEIGHT,
    269 			0, CopyFromParent,InputOutput,CopyFromParent,
    270 			CWOverrideRedirect,&attrib);
    271 
    272 	/* set up the window hints etc */
    273 	win_size_hints = XAllocSizeHints();
    274 	if (!win_size_hints)
    275 		die("out of memory allocating hints.\n");
    276 
    277 	win_size_hints->flags = PMaxSize | PMinSize;
    278 	win_size_hints->min_width = win_size_hints->max_width = WINWIDTH;
    279 
    280 	win_size_hints->min_height = win_size_hints->max_height = WINHEIGHT;
    281 
    282 	XStringListToTextProperty(&name, 1, &str);
    283 	ch = XAllocClassHint();
    284 	ch->res_class = name;
    285 	ch->res_name = name;
    286 
    287 	XSetWMProperties(dpy, win, &str, &str, NULL, 0, win_size_hints,
    288 			NULL, ch);
    289 
    290 	XFree(win_size_hints);
    291 	XFree(ch);
    292 	XFree(str.value);
    293 
    294 	XMapWindow(dpy, win);
    295 }
    296 
    297 void
    298 setupgc(void)
    299 {
    300 	XGCValues values;
    301 	int valuemask = 0;
    302 	int line_width = 1;
    303 	int line_style = LineSolid;
    304 	int cap_style = CapButt;
    305 	int join_style = JoinBevel;
    306 
    307 	gc = XCreateGC(dpy, win, valuemask, &values);
    308 	rectgc = XCreateGC(dpy, win, valuemask, &values);
    309 	XSetForeground(dpy, gc, fgcol);
    310 	XSetBackground(dpy, gc, bgcol);
    311 
    312 	XSetForeground(dpy, rectgc, bgcol);
    313 	XSetBackground(dpy, rectgc, bgcol);
    314 
    315 	XSetLineAttributes(dpy, gc, line_width, line_style,
    316 			cap_style, join_style);
    317 
    318 	/* setup the font */
    319 	font_info = XLoadQueryFont(dpy, font);
    320 	if (!font_info)
    321 		die("couldn't load font.\n");
    322 
    323 	XSetFont(dpy, gc, font_info->fid);
    324 }
    325 
    326 void
    327 eventloop(void)
    328 {
    329 	XEvent e;
    330 
    331 	redraw();
    332 
    333 	XSelectInput(dpy, win, ExposureMask | KeyPressMask);
    334 
    335 	for (;;) {
    336 		XNextEvent(dpy, &e);
    337 		switch(e.type) {
    338 		case Expose:
    339 			redraw();
    340 			break;
    341 		case KeyPress:
    342 			keypress(&e.xkey);
    343 			break;
    344 		default:
    345 			break;
    346 		}
    347 	}
    348 }
    349 
    350 /* this loop is required since pwm grabs the keyboard during the event loop */
    351 void
    352 grabhack(void)
    353 {
    354 	long maxwait = 3000000; /* 3 seconds */
    355 	long interval = 5000; /* 5 millisec */
    356 	long i;
    357 	int x;
    358 
    359 	redraw();
    360 
    361 	/* if it takes longer than maxwait, just die */
    362 	for (i = 0; i < (maxwait / interval); i++) {
    363 		usleep(interval);
    364 		x = XGrabKeyboard(dpy, win, False, GrabModeAsync,
    365 				GrabModeAsync, CurrentTime);
    366 		if (x == 0)
    367 			return;
    368 	}
    369 
    370 	die("Couldn't grab keyboard.\n");
    371 }
    372 
    373 void
    374 redraw(void)
    375 {
    376 	int font_height, textwidth, promptwidth, dir, ascent, descent;
    377 	XCharStruct cs;
    378 	XRectangle ink, logical;
    379 
    380 	font_height = font_info->ascent + font_info->descent;
    381 	XTextExtents(font_info, prompt, strlen(prompt), &dir, &ascent,
    382 			&descent, &cs);
    383 	promptwidth = cs.width;
    384 	XwcTextExtents(fontset, command, wcslen(command), &ink, &logical);
    385 	textwidth = logical.width;
    386 	textwidth += promptwidth;
    387 
    388 	XFillRectangle(dpy, win, rectgc, 0, 0, WINWIDTH, WINHEIGHT);
    389 	XDrawRectangle(dpy, win, gc, 0, 0, WINWIDTH-1, WINHEIGHT-1);
    390 	XDrawString(dpy, win, gc, 2, font_height+2, prompt,
    391 			strlen(prompt));
    392 	XwcDrawString(dpy, win, fontset, gc, 4 + promptwidth,
    393 			font_height+2, command, wcslen(command));
    394 	XDrawLine(dpy, win, gc, 4 + textwidth, font_height + 2,
    395 			4 + textwidth + 10, font_height+2);
    396 
    397 	XFlush(dpy);
    398 }
    399 
    400 void
    401 keypress(XKeyEvent *keyevent)
    402 {
    403 	KeySym key_symbol;
    404 	int len;
    405 	wchar_t buffer[3];
    406 
    407 	len = XwcLookupString(ic, keyevent, buffer, 3, &key_symbol, NULL);
    408 	buffer[len] = L'\0';
    409 
    410 	switch(key_symbol) {
    411 	case XK_Escape:
    412 		exit(1);
    413 		break;
    414 	case XK_BackSpace:
    415 		len = wcslen(command);
    416 		if (len > 0) {
    417 			command[len-1] = L'\0';
    418 			if (issecret)
    419 				secret[len-1] = L'\0';
    420 		}
    421 		break;
    422 	case XK_Return:
    423 	case XK_KP_Enter:
    424 		execcmd();
    425 		break;
    426 	case XK_c:
    427 		if (keyevent->state & ControlMask)
    428 			exit(1);
    429 	default:
    430 		if (key_symbol > 255)
    431 			break;
    432 
    433 		len = wcslen(command);
    434 		if (len < MAXCMD) {
    435 			if (issecret) {
    436 				secret[len] = buffer[0];
    437 				secret[len+1] = L'\0';
    438 				command[len] = L'*';
    439 				command[len+1] = L'\0';
    440 			} else {
    441 				command[len] = buffer[0];
    442 				command[len+1] = L'\0';
    443 			}
    444 		}
    445 		break;
    446 	}
    447 	redraw();
    448 }
    449 
    450 void
    451 execcmd(void)
    452 {
    453 	char *shell;
    454 	pid_t pid;
    455 
    456 	XDestroyWindow(dpy, win);
    457 
    458 	bzero(cbuf, sizeof(cbuf));
    459 	if (issecret)
    460 		wcstombs(cbuf, secret, sizeof(cbuf)-1);
    461 	else
    462 		wcstombs(cbuf, command, sizeof(cbuf)-1);
    463 
    464 	if (tostdout) {
    465 		printf("%s\n", cbuf);
    466 		exit(0);
    467 	}
    468 
    469 	switch ((pid = fork())) {
    470 	case -1:
    471 		die("fork: %s\n", strerror(errno));
    472 	case 0:
    473 		break;
    474 	default:
    475 		_exit(0);
    476 	}
    477 
    478 	shell = getenv("SHELL");
    479 	if (!shell)
    480 		shell = "/bin/sh";
    481 
    482 	execlp(shell, basename(shell), "-c", cbuf, (char *)NULL);
    483 	die("execlp: %s\n", strerror(errno));
    484 }
    485 
    486 void
    487 die(char *errstr, ...)
    488 {
    489 	va_list ap;
    490 
    491 	va_start(ap, errstr);
    492 	vfprintf(stderr, errstr, ap);
    493 	va_end(ap);
    494 
    495 	exit(1);
    496 }
    497