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