thinglaunch.c - thinglaunch - A simple command and password promtper for X11. | |
git clone git://bitreich.org/thinglaunch | |
Log | |
Files | |
Refs | |
Tags | |
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 <[email protected]>. 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.… | |
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 = WINHEI… | |
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 l… | |
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 |