thinglaunch.c - thinglaunch - A simple command and password promtper for X11.
(HTM) git clone git://bitreich.org/thinglaunch
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) 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