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