x11-ssh-askpass.c revision c056561a
1/* x11-ssh-askpass.c: A generic X11-based password dialog for OpenSSH. 2 * created 1999-Nov-17 03:40 Jim Knoble <jmknoble@jmknoble.cx> 3 * autodate: 2001-Feb-14 04:00 4 * 5 * by Jim Knoble <jmknoble@jmknoble.cx> 6 * Copyright (C) 1999,2000,2001 Jim Knoble 7 * 8 * Disclaimer: 9 * 10 * The software is provided "as is", without warranty of any kind, 11 * express or implied, including but not limited to the warranties of 12 * merchantability, fitness for a particular purpose and 13 * noninfringement. In no event shall the author(s) be liable for any 14 * claim, damages or other liability, whether in an action of 15 * contract, tort or otherwise, arising from, out of or in connection 16 * with the software or the use or other dealings in the software. 17 * 18 * Portions of this code are distantly derived from code in xscreensaver 19 * by Jamie Zawinski <jwz@jwz.org>. That code says: 20 * 21 * --------8<------------------------------------------------8<-------- 22 * xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski <jwz@jwz.org> 23 * 24 * Permission to use, copy, modify, distribute, and sell this software and its 25 * documentation for any purpose is hereby granted without fee, provided that 26 * the above copyright notice appear in all copies and that both that 27 * copyright notice and this permission notice appear in supporting 28 * documentation. No representations are made about the suitability of this 29 * software for any purpose. It is provided "as is" without express or 30 * implied warranty. 31 * --------8<------------------------------------------------8<-------- 32 * 33 * The remainder of this code falls under the same permissions and 34 * provisions as those of the xscreensaver code. 35 */ 36 37#include <ctype.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41 42/* For (get|set)rlimit() ... */ 43#include <sys/time.h> 44#include <sys/resource.h> 45/* ... end */ 46/* For (get|set)rlimit(), sleep(), and getpid() ... */ 47#include <unistd.h> 48/* ... end */ 49 50/* For errno ... */ 51#include <errno.h> 52/* ... end */ 53 54#include <X11/Xlib.h> 55#include <X11/Intrinsic.h> 56#include <X11/Shell.h> 57#include <X11/Xos.h> 58#include <X11/extensions/Xinerama.h> 59#include "dynlist.h" 60#include "drawing.h" 61#include "resources.h" 62#include "x11-ssh-askpass.h" 63 64#undef MAX 65#define MAX(a,b) (((a) > (b)) ? (a) : (b)) 66 67char *progname = NULL; 68char *progclass = NULL; 69XrmDatabase db = 0; 70 71static char *defaults[] = { 72#include "SshAskpass_ad.h" 73 0 74}; 75 76void outOfMemory(AppInfo *app, int line) 77{ 78 fprintf(stderr, "%s[%ld]: Aaahhh! I ran out of memory at line %d.\n", 79 app->appName, (long) app->pid, line); 80 exit(EXIT_STATUS_NO_MEMORY); 81} 82 83void freeIf(void *p) 84{ 85 if (p) { 86 free(p); 87 } 88} 89 90void freeFontIf(AppInfo *app, XFontStruct *f) 91{ 92 if (f) { 93 XFreeFont(app->dpy, f); 94 } 95} 96 97XFontStruct *getFontResource(AppInfo *app, char *instanceName, char *className) 98{ 99 char *fallbackFont = "fixed"; 100 101 XFontStruct *f = NULL; 102 char *s = get_string_resource(instanceName, className); 103 f = XLoadQueryFont(app->dpy, (s ? s : fallbackFont)); 104 if (!f) { 105 f = XLoadQueryFont(app->dpy, fallbackFont); 106 } 107 if (s) { 108 free(s); 109 } 110 return(f); 111} 112 113char *getStringResourceWithDefault(char *instanceName, char *className, 114 char *defaultText) 115{ 116 char *s = get_string_resource(instanceName, className); 117 if (!s) { 118 if (!defaultText) { 119 s = strdup(""); 120 } else { 121 s = strdup(defaultText); 122 } 123 } 124 return(s); 125} 126 127unsigned int getUnsignedIntegerResource(AppInfo *app, char *instanceName, 128 char *className, 129 unsigned int defaultValue) 130{ 131 int n; 132 unsigned int value; 133 char c; 134 char *s = get_string_resource(instanceName, className); 135 char *cp = s; 136 137 if (NULL == s) { 138 return(defaultValue); 139 } 140 while ((*cp) && isspace(*cp)) { 141 /* Skip whitespace. */ 142 cp++; 143 } 144 if (*cp) { 145 if (('0' == cp[0]) && cp[1]) { 146 if (('x' == cp[1]) || ('X' == cp[1])) { 147 /* Hex */ 148 n = sscanf(cp + 2, "%x %c", &value, &c); 149 } else { 150 /* Octal */ 151 n = sscanf(cp + 1, "%o %c", &value, &c); 152 } 153 if (1 == n) { 154 free(s); 155 return(value); 156 } 157 } else { 158 /* Unsigned Decimal */ 159 n = sscanf(cp, "%u %c", &value, &c); 160 if (1 == n) { 161 free(s); 162 return(value); 163 } 164 } 165 } 166 /* If we get here, no conversion succeeded. */ 167 fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n", 168 app->appName, (long) app->pid, s, instanceName); 169 free(s); 170 return(defaultValue); 171} 172 173/* Default resolution is 75 dots/inch. 1 in = 2.54 cm. */ 174#define DefaultResolution ((75 * 10000) / 254) 175long getResolutionResource(AppInfo *app, char *instanceName, char *className, 176 char *defaultResolutionSpec) 177{ 178 char units[3]; 179 char *s; 180 int n; 181 long resolution; 182 unsigned int i; 183 184 memset(units, 0, sizeof(units)); 185 s = getStringResourceWithDefault(instanceName, className, 186 defaultResolutionSpec); 187 /* NOTE: The width of the %s format must be one less than 188 * the length of the units[] array above! 189 */ 190 n = sscanf(s, "%ld / %2s", &resolution, units); 191 if (n != 2) { 192 fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n", 193 app->appName, (long) app->pid, s, instanceName); 194 resolution = DefaultResolution; 195 } else { 196 if (resolution < 0) { 197 /* Resolution specifications should not be negative. */ 198 resolution = -(resolution); 199 } 200 for (i = 0; i < (sizeof(units) - 1); i++) { 201 units[i] = tolower(units[i]); 202 } 203 if ((0 == strcmp(units, "in")) || 204 (0 == strcmp(units, "i")) || 205 (0 == strcmp(units, "\""))) { 206 /* dots/inch */ 207 resolution = resolution * 10000 / 254; 208 } else if ((0 == strcmp(units, "m")) || 209 (0 == strcmp(units, "me"))) { 210 /* dots/meter; no conversion necessary */ 211 ; 212 } else { 213 /* some unit we don't recognize; cringe and stare at the floor */ 214 resolution = DefaultResolution; 215 } 216 } 217 return(resolution); 218} 219#undef DefaultResolution 220 221void calcTextObjectExtents(TextObject *t, XFontStruct *font) { 222 if ((!t) || (!(t->text))) { 223 return; 224 } 225 t->textLength = strlen(t->text); 226 XTextExtents(font, t->text, t->textLength, &(t->direction), 227 &(t->ascent), &(t->descent), &(t->overall)); 228} 229 230void calcLabelTextExtents(LabelInfo *label) 231{ 232 TextObject *t; 233 int first = 1; 234 235 if ((!label) || (!(label->fullText)) || (!(label->font))) { 236 return; 237 } 238 t = label->multiText; 239 while (NULL != t) { 240 if (first) { 241 calcTextObjectExtents(t, label->font); 242 first = 0; 243 } else 244 calcTextObjectExtents(t, label->fixedFont); 245 label->w.height += (t->ascent + t->descent); 246 if (label->w.width < t->overall.width) { 247 label->w.width = t->overall.width; 248 } 249 t = t->next; 250 } 251} 252 253void calcTotalButtonExtents(ButtonInfo *button) 254{ 255 if (!button) { 256 return; 257 } 258 button->w3.w.width = (button->w3.interiorWidth + 259 (2 * button->w3.shadowThickness)); 260 button->w3.w.width += (2 * button->w3.borderWidth); 261 button->w3.w.height = (button->w3.interiorHeight + 262 (2 * button->w3.shadowThickness)); 263 button->w3.w.height += (2 * button->w3.borderWidth); 264} 265 266void calcButtonExtents(ButtonInfo *button) 267{ 268 if (!button) { 269 return; 270 } 271 calcLabelTextExtents(&(button->label)); 272 button->w3.interiorWidth = (button->label.w.width + 273 (2 * button->w3.horizontalSpacing)); 274 button->w3.interiorHeight = (button->label.w.height + 275 (2 * button->w3.verticalSpacing)); 276 calcTotalButtonExtents(button); 277} 278 279void balanceButtonExtents(ButtonInfo *button1, ButtonInfo *button2) 280{ 281 if ((!button1) || (!button2)) { 282 return; 283 } 284 button1->w3.interiorWidth = button2->w3.interiorWidth = 285 MAX(button1->w3.interiorWidth, button2->w3.interiorWidth); 286 button1->w3.interiorHeight = button2->w3.interiorHeight = 287 MAX(button1->w3.interiorHeight, button2->w3.interiorHeight); 288 calcTotalButtonExtents(button1); 289 calcTotalButtonExtents(button2); 290} 291 292void calcButtonLabelPosition(ButtonInfo *button) 293{ 294 if (!button) { 295 return; 296 } 297 button->label.w.x = button->w3.w.x + 298 ((button->w3.w.width - button->label.w.width) / 2); 299 button->label.w.y = button->w3.w.y + 300 ((button->w3.w.height - button->label.w.height) / 2); 301} 302 303Dimension scaleXDimension(AppInfo *app, Dimension unscaled) 304{ 305 Dimension scaled; 306 307 if (((app->defaultXResolution < app->xResolution) && 308 ((app->defaultXResolution + app->xFuzz) < app->xResolution)) || 309 ((app->xResolution < app->defaultXResolution) && 310 ((app->xResolution + app->xFuzz) < app->defaultXResolution))) { 311 scaled = (unscaled * app->xResolution) / app->defaultXResolution; 312 } else { 313 scaled = unscaled; 314 } 315 return(scaled); 316} 317 318Dimension scaleYDimension(AppInfo *app, Dimension unscaled) 319{ 320 Dimension scaled; 321 322 if (((app->defaultYResolution < app->yResolution) && 323 ((app->defaultYResolution + app->yFuzz) < app->yResolution)) || 324 ((app->yResolution < app->defaultYResolution) && 325 ((app->yResolution + app->yFuzz) < app->defaultYResolution))) { 326 scaled = (unscaled * app->yResolution) / app->defaultYResolution; 327 } else { 328 scaled = unscaled; 329 } 330 return(scaled); 331} 332 333/* Assumes 's' is non-NULL. */ 334TextObject *createTextObject(AppInfo *app, char *s) 335{ 336 TextObject *t = malloc(sizeof(*t)); 337 if (NULL == t) { 338 outOfMemory(app, __LINE__); 339 } 340 memset(t, 0, sizeof(*t)); 341 if (('\n' == *s) || ('\0' == *s)) { 342 t->text = " "; 343 } else { 344 t->text = s; 345 } 346 return(t); 347} 348 349/* Assumes 'label' object exists and is zeroed. */ 350void createLabel(AppInfo *app, char *text, LabelInfo *label) 351{ 352 char *substring; 353 TextObject *t; 354 355 if ((!app) || (!text)) { 356 return; 357 } 358 label->fullText = strdup(text); 359 label->multiText = createTextObject(app, label->fullText); 360 t = label->multiText; 361 substring = strchr(label->fullText, '\n'); 362 while (NULL != substring) { 363 *(substring++) = '\0'; 364 t->next = createTextObject(app, substring); 365 if (t->next) { 366 t = t->next; 367 } 368 substring = strchr(substring, '\n'); 369 } 370} 371 372void createDialog(AppInfo *app) 373{ 374 DialogInfo *d; 375 char *labelText; 376 377 if (app->dialog) { 378 return; 379 } 380 d = malloc(sizeof(*d)); 381 if (NULL == d) { 382 outOfMemory(app, __LINE__); 383 } 384 memset(d, 0, sizeof(*d)); 385 386 app->grabKeyboard = 387 get_boolean_resource("grabKeyboard", "GrabKeyboard", True); 388 app->grabPointer = 389 get_boolean_resource("grabPointer", "GrabPointer", False); 390 app->grabServer = 391 get_boolean_resource("grabServer", "GrabServer", False); 392 393 /* inputTimeout resource specified in seconds for easy human interface. 394 * Convert to milliseconds here. 395 */ 396 app->inputTimeout = (unsigned long) 1000 * 397 getUnsignedIntegerResource(app, "inputTimeout", "InputTimeout", 0); 398 399 app->defaultXResolution = 400 getResolutionResource(app, "defaultXResolution", "DefaultXResolution", 401 "75/in"); 402 app->defaultYResolution = 403 getResolutionResource(app, "defaultYResolution", "DefaultYResolution", 404 "75/in"); 405 app->xFuzz = 406 getResolutionResource(app, "xResolutionFuzz", "XResolutionFuzz", "20/in"); 407 app->yFuzz = 408 getResolutionResource(app, "yResolutionFuzz", "YResolutionFuzz", "20/in"); 409 410 d->title = 411 getStringResourceWithDefault("dialog.title", "Dialog.Title", 412 "OpenSSH Authentication Passphrase Request"); 413 d->w3.w.foreground = 414 get_pixel_resource("foreground", "Foreground", 415 app->dpy, app->colormap, app->black); 416 d->w3.w.background = 417 get_pixel_resource("background", "Background", 418 app->dpy, app->colormap, app->white); 419 d->w3.topShadowColor = 420 get_pixel_resource("topShadowColor", "TopShadowColor", 421 app->dpy, app->colormap, app->white); 422 d->w3.bottomShadowColor = 423 get_pixel_resource("bottomShadowColor", "BottomShadowColor", 424 app->dpy, app->colormap, app->black); 425 d->w3.shadowThickness = 426 get_integer_resource("shadowThickness", "ShadowThickness", 3); 427 d->w3.borderColor = 428 get_pixel_resource("borderColor", "BorderColor", 429 app->dpy, app->colormap, app->black); 430 d->w3.borderWidth = 431 get_integer_resource("borderWidth", "BorderWidth", 1); 432 433 d->w3.horizontalSpacing = scaleXDimension(app, 434 get_integer_resource("horizontalSpacing", "Spacing", 5)); 435 d->w3.verticalSpacing = scaleYDimension(app, 436 get_integer_resource("verticalSpacing", "Spacing", 6)); 437 438 if (2 == app->argc) { 439 labelText = strdup(app->argv[1]); 440 } else { 441 labelText = 442 getStringResourceWithDefault("dialog.label", "Dialog.Label", 443 "Please enter your authentication passphrase:"); 444 } 445 createLabel(app, labelText, &(d->label)); 446 freeIf(labelText); 447 d->label.font = getFontResource(app, "dialog.font", "Dialog.Font"); 448 d->label.fixedFont = getFontResource(app, "dialog.fixedFont", 449 "Dialog.FixedFont"); 450 calcLabelTextExtents(&(d->label)); 451 d->label.w.foreground = d->w3.w.foreground; 452 d->label.w.background = d->w3.w.background; 453 454 d->okButton.w3.w.foreground = 455 get_pixel_resource("okButton.foreground", "Button.Foreground", 456 app->dpy, app->colormap, app->black); 457 d->okButton.w3.w.background = 458 get_pixel_resource("okButton.background", "Button.Background", 459 app->dpy, app->colormap, app->white); 460 d->okButton.w3.topShadowColor = 461 get_pixel_resource("okButton.topShadowColor", "Button.TopShadowColor", 462 app->dpy, app->colormap, app->white); 463 d->okButton.w3.bottomShadowColor = 464 get_pixel_resource("okButton.bottomShadowColor", 465 "Button.BottomShadowColor", 466 app->dpy, app->colormap, app->black); 467 d->okButton.w3.shadowThickness = 468 get_integer_resource("okButton.shadowThickness", 469 "Button.ShadowThickness", 2); 470 d->okButton.w3.borderColor = 471 get_pixel_resource("okButton.borderColor", "Button.BorderColor", 472 app->dpy, app->colormap, app->black); 473 d->okButton.w3.borderWidth = 474 get_integer_resource("okButton.borderWidth", "Button.BorderWidth", 1); 475 d->okButton.w3.horizontalSpacing = scaleXDimension(app, 476 get_integer_resource("okButton.horizontalSpacing", "Button.Spacing", 4)); 477 d->okButton.w3.verticalSpacing = scaleYDimension(app, 478 get_integer_resource("okButton.verticalSpacing", "Button.Spacing", 2)); 479 labelText = 480 getStringResourceWithDefault("okButton.label", "Button.Label", "OK"); 481 createLabel(app, labelText, &(d->okButton.label)); 482 freeIf(labelText); 483 d->okButton.label.font = 484 getFontResource(app, "okButton.font", "Button.Font"); 485 calcButtonExtents(&(d->okButton)); 486 d->okButton.label.w.foreground = d->okButton.w3.w.foreground; 487 d->okButton.label.w.background = d->okButton.w3.w.background; 488 489 d->cancelButton.w3.w.foreground = 490 get_pixel_resource("cancelButton.foreground", "Button.Foreground", 491 app->dpy, app->colormap, app->black); 492 d->cancelButton.w3.w.background = 493 get_pixel_resource("cancelButton.background", "Button.Background", 494 app->dpy, app->colormap, app->white); 495 d->cancelButton.w3.topShadowColor = 496 get_pixel_resource("cancelButton.topShadowColor", 497 "Button.TopShadowColor", 498 app->dpy, app->colormap, app->white); 499 d->cancelButton.w3.bottomShadowColor = 500 get_pixel_resource("cancelButton.bottomShadowColor", 501 "Button.BottomShadowColor", 502 app->dpy, app->colormap, app->black); 503 d->cancelButton.w3.shadowThickness = 504 get_integer_resource("cancelButton.shadowThickness", 505 "Button.ShadowThickness", 2); 506 d->cancelButton.w3.borderColor = 507 get_pixel_resource("cancelButton.borderColor", "Button.BorderColor", 508 app->dpy, app->colormap, app->black); 509 d->cancelButton.w3.borderWidth = 510 get_integer_resource("cancelButton.borderWidth", "Button.BorderWidth", 511 1); 512 d->cancelButton.w3.horizontalSpacing = scaleXDimension(app, 513 get_integer_resource("cancelButton.horizontalSpacing", "Button.Spacing", 514 4)); 515 d->cancelButton.w3.verticalSpacing = scaleYDimension(app, 516 get_integer_resource("cancelButton.verticalSpacing", "Button.Spacing", 517 2)); 518 labelText = 519 getStringResourceWithDefault("cancelButton.label", "Button.Label", 520 "Cancel"); 521 createLabel(app, labelText, &(d->cancelButton.label)); 522 freeIf(labelText); 523 d->cancelButton.label.font = 524 getFontResource(app, "cancelButton.font", "Button.Font"); 525 calcButtonExtents(&(d->cancelButton)); 526 d->cancelButton.label.w.foreground = d->cancelButton.w3.w.foreground; 527 d->cancelButton.label.w.background = d->cancelButton.w3.w.background; 528 529 balanceButtonExtents(&(d->okButton), &(d->cancelButton)); 530 531 d->indicator.w3.w.foreground = 532 get_pixel_resource("indicator.foreground", "Indicator.Foreground", 533 app->dpy, app->colormap, app->black); 534 d->indicator.w3.w.background = 535 get_pixel_resource("indicator.background", "Indicator.Background", 536 app->dpy, app->colormap, app->white); 537 d->indicator.w3.w.width = scaleXDimension(app, 538 get_integer_resource("indicator.width", "Indicator.Width", 15)); 539 d->indicator.w3.w.height = scaleYDimension(app, 540 get_integer_resource("indicator.height", "Indicator.Height", 7)); 541 d->indicator.w3.topShadowColor = 542 get_pixel_resource("indicator.topShadowColor", 543 "Indicator.TopShadowColor", 544 app->dpy, app->colormap, app->white); 545 d->indicator.w3.bottomShadowColor = 546 get_pixel_resource("indicator.bottomShadowColor", 547 "Indicator.BottomShadowColor", 548 app->dpy, app->colormap, app->black); 549 d->indicator.w3.shadowThickness = 550 get_integer_resource("indicator.shadowThickness", 551 "Indicator.ShadowThickness", 2); 552 d->indicator.w3.borderColor = 553 get_pixel_resource("indicator.borderColor", "Indicator.BorderColor", 554 app->dpy, app->colormap, app->black); 555 d->indicator.w3.borderWidth = 556 get_integer_resource("indicator.borderWidth", "Indicator.BorderWidth", 557 0); 558 d->indicator.w3.horizontalSpacing = scaleXDimension(app, 559 get_integer_resource("indicator.horizontalSpacing", "Indicator.Spacing", 560 2)); 561 d->indicator.w3.verticalSpacing =scaleYDimension(app, 562 get_integer_resource("indicator.verticalSpacing", "Indicator.Spacing", 563 4)); 564 d->indicator.minimumCount = 565 get_integer_resource("indicator.minimumCount", "Indicator.MinimumCount", 566 8); 567 d->indicator.maximumCount = 568 get_integer_resource("indicator.maximumCount", "Indicator.MaximumCount", 569 24); 570 d->indicator.w3.interiorWidth = d->indicator.w3.w.width; 571 d->indicator.w3.interiorHeight = d->indicator.w3.w.height; 572 d->indicator.w3.w.width += (2 * d->indicator.w3.shadowThickness); 573 d->indicator.w3.w.width += (2 * d->indicator.w3.borderWidth); 574 d->indicator.w3.w.height += (2 * d->indicator.w3.shadowThickness); 575 d->indicator.w3.w.height += (2 * d->indicator.w3.borderWidth); 576 { 577 /* Make sure the indicators can all fit on the screen. 578 * 80% of the screen width seems fine. 579 */ 580 Dimension maxWidth = (app->screen_width * 8 / 10); 581 Dimension extraSpace = ((2 * d->w3.horizontalSpacing) + 582 (2 * d->w3.shadowThickness)); 583 584 if (d->indicator.maximumCount < 8) { 585 d->indicator.maximumCount = 8; 586 } 587 if (((d->indicator.maximumCount * d->indicator.w3.w.width) + 588 ((d->indicator.maximumCount - 1) * 589 d->indicator.w3.horizontalSpacing) + extraSpace) > maxWidth) { 590 d->indicator.maximumCount = 591 ((maxWidth - extraSpace - d->indicator.w3.w.width) / 592 (d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing)) 593 + 1; 594 } 595 if (d->indicator.minimumCount <= 6) { 596 d->indicator.minimumCount = 6; 597 } 598 if (d->indicator.minimumCount > d->indicator.maximumCount) { 599 d->indicator.minimumCount = d->indicator.maximumCount; 600 } 601 } 602 603 { 604 /* Calculate the width and horizontal position of things. */ 605 Dimension labelAreaWidth; 606 Dimension buttonAreaWidth; 607 Dimension indicatorAreaWidth; 608 Dimension extraIndicatorSpace; 609 Dimension singleIndicatorSpace; 610 Dimension interButtonSpace; 611 Dimension w; 612 Position leftX; 613 int i; 614 615 labelAreaWidth = d->label.w.width + (2 * d->w3.horizontalSpacing); 616 buttonAreaWidth = ((3 * d->w3.horizontalSpacing) + 617 d->okButton.w3.w.width + 618 d->cancelButton.w3.w.width); 619 w = MAX(labelAreaWidth, buttonAreaWidth); 620 extraIndicatorSpace = ((2 * d->w3.horizontalSpacing) + 621 d->indicator.w3.w.width); 622 singleIndicatorSpace = (d->indicator.w3.w.width + 623 d->indicator.w3.horizontalSpacing); 624 d->indicator.count = ((w - extraIndicatorSpace) / singleIndicatorSpace); 625 d->indicator.current = 0; 626 d->indicator.count++; /* For gatepost indicator in extra space. */ 627 if (((w - extraIndicatorSpace) % singleIndicatorSpace) > 628 (singleIndicatorSpace / 2)) { 629 d->indicator.count++; 630 } 631 if (d->indicator.count < d->indicator.minimumCount) { 632 d->indicator.count = d->indicator.minimumCount; 633 } 634 if (d->indicator.count > d->indicator.maximumCount) { 635 d->indicator.count = d->indicator.maximumCount; 636 } 637 indicatorAreaWidth = ((singleIndicatorSpace * (d->indicator.count - 1)) + 638 extraIndicatorSpace); 639 d->w3.interiorWidth = MAX(w, indicatorAreaWidth); 640 d->w3.w.width = d->w3.interiorWidth + (2 * d->w3.shadowThickness); 641 642 leftX = (d->w3.w.width - d->label.w.width) / 2; 643 d->label.w.x = leftX; 644 645 leftX = ((d->w3.w.width - 646 (d->indicator.count * d->indicator.w3.w.width) - 647 ((d->indicator.count - 1) * d->indicator.w3.horizontalSpacing)) 648 / 2); 649 { 650 int n = d->indicator.count * sizeof(IndicatorElement); 651 d->indicators = malloc(n); 652 if (NULL == d->indicators) { 653 destroyDialog(app); 654 outOfMemory(app, __LINE__); 655 } 656 memset(d->indicators, 0, n); 657 } 658 d->indicators[0].parent = &(d->indicator); 659 d->indicators[0].w.x = d->indicator.w3.w.x = leftX; 660 d->indicators[0].w.width = d->indicator.w3.w.width; 661 d->indicators[0].isLit = False; 662 for (i = 1; i < d->indicator.count; i++) { 663 d->indicators[i].parent = &(d->indicator); 664 d->indicators[i].w.x = (d->indicators[i - 1].w.x + 665 d->indicator.w3.w.width + 666 d->indicator.w3.horizontalSpacing); 667 d->indicators[i].w.width = d->indicator.w3.w.width; 668 d->indicators[i].isLit = False; 669 } 670 interButtonSpace = ((d->w3.interiorWidth - d->okButton.w3.w.width - 671 d->cancelButton.w3.w.width) / 3); 672 d->okButton.w3.w.x = interButtonSpace + d->w3.shadowThickness; 673 d->cancelButton.w3.w.x = (d->okButton.w3.w.x + d->okButton.w3.w.width + 674 interButtonSpace); 675 } 676 { 677 /* Calculate the height and vertical position of things. */ 678 int i; 679 680 d->w3.interiorHeight = ((4 * d->w3.verticalSpacing) + 681 (2 * d->indicator.w3.verticalSpacing) + 682 d->label.w.height + 683 d->indicator.w3.w.height + 684 d->okButton.w3.w.height); 685 d->w3.w.height = d->w3.interiorHeight + (2 * d->w3.shadowThickness); 686 d->label.w.y = d->w3.shadowThickness + d->w3.verticalSpacing; 687 d->indicator.w3.w.y = (d->label.w.y + d->label.w.height + 688 d->w3.verticalSpacing + 689 d->indicator.w3.verticalSpacing); 690 for (i = 0; i < d->indicator.count; i++) { 691 d->indicators[i].w.y = d->indicator.w3.w.y; 692 d->indicators[i].w.height = d->indicator.w3.w.height; 693 } 694 d->okButton.w3.w.y = d->cancelButton.w3.w.y = 695 (d->indicator.w3.w.y + d->indicator.w3.w.height + 696 d->w3.verticalSpacing + d->indicator.w3.verticalSpacing); 697 } 698 calcButtonLabelPosition(&(d->okButton)); 699 calcButtonLabelPosition(&(d->cancelButton)); 700 701 d->w3.w.x = (app->screen_width - d->w3.w.width) / 2; 702 d->w3.w.y = (app->screen_height - d->w3.w.height) / 3; 703 704 app->dialog = d; 705} 706 707void destroyLabel(AppInfo *app, LabelInfo *label) 708{ 709 TextObject *thisTextObject; 710 TextObject *nextTextObject; 711 712 thisTextObject = label->multiText; 713 nextTextObject = thisTextObject->next; 714 freeIf(thisTextObject); 715 while (NULL != nextTextObject) { 716 thisTextObject = nextTextObject; 717 nextTextObject = thisTextObject->next; 718 freeIf(thisTextObject); 719 } 720 freeIf(label->fullText); 721 freeFontIf(app, label->font); 722 freeFontIf(app, label->fixedFont); 723} 724 725void destroyDialog(AppInfo *app) 726{ 727 DialogInfo *d = app->dialog; 728 729 freeIf(d->title); 730 freeIf(d->indicators); 731 732 destroyLabel(app, &(d->label)); 733 destroyLabel(app, &(d->okButton.label)); 734 destroyLabel(app, &(d->cancelButton.label)); 735 736 XFree(d->sizeHints); 737 XFree(d->wmHints); 738 XFree(d->classHints); 739 XFree(d->windowName.value); 740 741 freeIf(d); 742} 743 744void createDialogWindow(AppInfo *app) 745{ 746 XSetWindowAttributes attr; 747 unsigned long attrMask = 0; 748 DialogInfo *d = app->dialog; 749 750 attr.background_pixel = d->w3.w.background; 751 attrMask |= CWBackPixel; 752 attr.border_pixel = d->w3.borderColor; 753 attrMask |= CWBorderPixel; 754 attr.cursor = None; 755 attrMask |= CWCursor; 756 attr.event_mask = app->eventMask; 757 attrMask |= CWEventMask; 758 759 d->dialogWindow = XCreateWindow(app->dpy, app->rootWindow, 760 d->w3.w.x, d->w3.w.y, 761 d->w3.w.width, d->w3.w.height, 762 d->w3.borderWidth, 763 DefaultDepthOfScreen(app->screen), 764 InputOutput, 765 DefaultVisualOfScreen(app->screen), 766 attrMask, &attr); 767 768 d->sizeHints = XAllocSizeHints(); 769 if (!(d->sizeHints)) { 770 destroyDialog(app); 771 outOfMemory(app, __LINE__); 772 } 773 d->sizeHints->flags = 0; 774 d->sizeHints->flags |= PPosition; 775 d->sizeHints->flags |= PSize; 776 d->sizeHints->min_width = d->w3.w.width; 777 d->sizeHints->min_height = d->w3.w.height; 778 d->sizeHints->flags |= PMinSize; 779 d->sizeHints->max_width = d->w3.w.width; 780 d->sizeHints->max_height = d->w3.w.height; 781 d->sizeHints->flags |= PMaxSize; 782 d->sizeHints->base_width = d->w3.w.width; 783 d->sizeHints->base_height = d->w3.w.height; 784 d->sizeHints->flags |= PBaseSize; 785 786 d->wmHints = XAllocWMHints(); 787 if (!(d->wmHints)) { 788 destroyDialog(app); 789 outOfMemory(app, __LINE__); 790 } 791 d->wmHints->flags = 0; 792 d->wmHints->input = True; 793 d->wmHints->flags |= InputHint; 794 d->wmHints->initial_state = NormalState; 795 d->wmHints->flags |= StateHint; 796 797 d->classHints = XAllocClassHint(); 798 if (!(d->classHints)) { 799 destroyDialog(app); 800 outOfMemory(app, __LINE__); 801 } 802 d->classHints->res_name = app->appName; 803 d->classHints->res_class = app->appClass; 804 805 if (!XStringListToTextProperty(&(d->title), 1, &(d->windowName))) { 806 destroyDialog(app); 807 outOfMemory(app, __LINE__); 808 } 809 XSetWMProperties(app->dpy, d->dialogWindow, &(d->windowName), NULL, 810 app->argv, app->argc, d->sizeHints, 811 d->wmHints, d->classHints); 812 XSetTransientForHint(app->dpy, d->dialogWindow, d->dialogWindow); 813 814 app->wmDeleteWindowAtom = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False); 815 XSetWMProtocols(app->dpy, d->dialogWindow, &(app->wmDeleteWindowAtom), 1); 816} 817 818void createGCs(AppInfo *app) 819{ 820 DialogInfo *d = app->dialog; 821 822 XGCValues gcv; 823 unsigned long gcvMask; 824 825 gcvMask = 0; 826 gcv.foreground = d->w3.w.background; 827 gcvMask |= GCForeground; 828 gcv.fill_style = FillSolid; 829 gcvMask |= GCFillStyle; 830 app->fillGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); 831 832 gcvMask = 0; 833 gcv.foreground = d->w3.borderColor; 834 gcvMask |= GCForeground; 835 gcv.line_width = d->w3.borderWidth; 836 gcvMask |= GCLineWidth; 837 gcv.line_style = LineSolid; 838 gcvMask |= GCLineStyle; 839 gcv.cap_style = CapButt; 840 gcvMask |= GCCapStyle; 841 gcv.join_style = JoinMiter; 842 gcvMask |= GCJoinStyle; 843 app->borderGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); 844 845 gcvMask = 0; 846 gcv.foreground = d->label.w.foreground; 847 gcvMask |= GCForeground; 848 gcv.background = d->label.w.background; 849 gcvMask |= GCBackground; 850 gcv.font = d->label.font->fid; 851 gcvMask |= GCFont; 852 app->textGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); 853 854 gcvMask = 0; 855 gcv.foreground = d->indicator.w3.w.foreground; 856 gcvMask |= GCForeground; 857 gcv.fill_style = FillSolid; 858 gcvMask |= GCFillStyle; 859 app->brightGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); 860 861 gcvMask = 0; 862 gcv.foreground = d->indicator.w3.w.background; 863 gcvMask |= GCForeground; 864 gcv.fill_style = FillSolid; 865 gcvMask |= GCFillStyle; 866 app->dimGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); 867} 868 869void destroyGCs(AppInfo *app) 870{ 871 XFreeGC(app->dpy, app->fillGC); 872 XFreeGC(app->dpy, app->borderGC); 873 XFreeGC(app->dpy, app->textGC); 874 XFreeGC(app->dpy, app->brightGC); 875 XFreeGC(app->dpy, app->dimGC); 876} 877 878void paintLabel(AppInfo *app, Drawable draw, LabelInfo label) 879{ 880 TextObject *t; 881 Position x; 882 Position y; 883 int first = 1; 884 885 if (!(label.fullText)) { 886 return; 887 } 888 XSetForeground(app->dpy, app->textGC, label.w.foreground); 889 XSetBackground(app->dpy, app->textGC, label.w.background); 890 XSetFont(app->dpy, app->textGC, label.font->fid); 891 892 t = label.multiText; 893 x = label.w.x; 894 y = label.w.y + t->ascent; 895 while (NULL != t) { 896 if (!first) 897 XSetFont(app->dpy, app->textGC, label.fixedFont->fid); 898 else 899 first = 0; 900 901 if (t->text) { 902 XDrawString(app->dpy, draw, app->textGC, x, y, t->text, 903 t->textLength); 904 } 905 y += t->descent; 906 t = t->next; 907 if (t) { 908 y += t->ascent; 909 } 910 } 911} 912 913void paintButton(AppInfo *app, Drawable draw, ButtonInfo button) 914{ 915 Position x; 916 Position y; 917 Dimension width; 918 Dimension height; 919 920 if (button.w3.borderWidth > 0) { 921 XSetForeground(app->dpy, app->borderGC, button.w3.borderColor); 922 XFillRectangle(app->dpy, draw, app->borderGC, button.w3.w.x, 923 button.w3.w.y, button.w3.w.width, button.w3.w.height); 924 } 925 if ((button.w3.shadowThickness <= 0) && (button.pressed)) { 926 Pixel tmp = button.w3.w.background; 927 button.w3.w.background = button.w3.w.foreground; 928 button.w3.w.foreground = tmp; 929 tmp = button.label.w.background; 930 button.label.w.background = button.label.w.foreground; 931 button.label.w.foreground = tmp; 932 } 933 x = (button.w3.w.x + button.w3.borderWidth); 934 y = (button.w3.w.y + button.w3.borderWidth); 935 width = (button.w3.w.width - (2 * button.w3.borderWidth)); 936 height = (button.w3.w.height - (2 * button.w3.borderWidth)); 937 if ((button.w3.shadowThickness > 0) && (button.pressed)) { 938 XSetForeground(app->dpy, app->fillGC, button.w3.topShadowColor); 939 } else { 940 XSetForeground(app->dpy, app->fillGC, button.w3.w.background); 941 } 942 XFillRectangle(app->dpy, draw, app->fillGC, x, y, width, height); 943 if (button.w3.shadowThickness > 0) { 944 if (button.pressed) { 945 draw_shaded_rectangle(app->dpy, draw, x, y, width, height, 946 button.w3.shadowThickness, 947 button.w3.bottomShadowColor, 948 button.w3.topShadowColor); 949 } else { 950 draw_shaded_rectangle(app->dpy, draw, x, y, width, height, 951 button.w3.shadowThickness, 952 button.w3.topShadowColor, 953 button.w3.bottomShadowColor); 954 } 955 } 956 if ((button.w3.shadowThickness > 0) && (button.pressed)) { 957 Dimension pressedAdjustment; 958 959 pressedAdjustment = button.w3.shadowThickness / 2; 960 if (pressedAdjustment < 1) { 961 pressedAdjustment = 1; 962 } 963 x = button.label.w.x; 964 y = button.label.w.y; 965 button.label.w.x += pressedAdjustment; 966 button.label.w.y += pressedAdjustment; 967 paintLabel(app, draw, button.label); 968 button.label.w.x = x; 969 button.label.w.y = y; 970 } else { 971 paintLabel(app, draw, button.label); 972 } 973 if ((button.w3.shadowThickness <= 0) && (button.pressed)) { 974 Pixel tmp = button.w3.w.background; 975 button.w3.w.background = button.w3.w.foreground; 976 button.w3.w.foreground = tmp; 977 tmp = button.label.w.background; 978 button.label.w.background = button.label.w.foreground; 979 button.label.w.foreground = tmp; 980 } 981} 982 983void paintIndicator(AppInfo *app, Drawable draw, IndicatorElement indicator) 984{ 985 Position x; 986 Position y; 987 Dimension width; 988 Dimension height; 989 GC gc = app->dimGC; 990 991 if (indicator.parent->w3.borderWidth > 0) { 992 XSetForeground(app->dpy, app->borderGC, 993 indicator.parent->w3.borderColor); 994 XFillRectangle(app->dpy, draw, app->borderGC, indicator.w.x, 995 indicator.w.y, indicator.w.width, indicator.w.height); 996 } 997 if (indicator.isLit) { 998 gc = app->brightGC; 999 } 1000 x = (indicator.w.x + indicator.parent->w3.borderWidth); 1001 y = (indicator.w.y + indicator.parent->w3.borderWidth); 1002 width = (indicator.w.width - (2 * indicator.parent->w3.borderWidth)); 1003 height = (indicator.w.height - (2 * indicator.parent->w3.borderWidth)); 1004 XFillRectangle(app->dpy, draw, gc, x, y, width, height); 1005 if (indicator.parent->w3.shadowThickness > 0) { 1006 draw_shaded_rectangle(app->dpy, draw, x, y, width, height, 1007 indicator.parent->w3.shadowThickness, 1008 indicator.parent->w3.bottomShadowColor, 1009 indicator.parent->w3.topShadowColor); 1010 } 1011} 1012 1013void updateIndicatorElement(AppInfo *app, int i) 1014{ 1015 DialogInfo *d = app->dialog; 1016 1017 d->indicators[i].isLit = !(d->indicators[i].isLit); 1018 paintIndicator(app, d->dialogWindow, d->indicators[i]); 1019} 1020 1021void updateIndicators(AppInfo *app, int condition) 1022{ 1023 DialogInfo *d = app->dialog; 1024 1025 if (condition > 0) { 1026 /* Move forward one. */ 1027 updateIndicatorElement(app, d->indicator.current); 1028 if (d->indicator.current < (d->indicator.count - 1)) { 1029 (d->indicator.current)++; 1030 } else { 1031 d->indicator.current = 0; 1032 } 1033 } else if (condition < 0) { 1034 /* Move backward one. */ 1035 if (d->indicator.current > 0) { 1036 (d->indicator.current)--; 1037 } else { 1038 d->indicator.current = d->indicator.count - 1; 1039 } 1040 updateIndicatorElement(app, d->indicator.current); 1041 } else { 1042 /* Erase them all. */ 1043 int i; 1044 1045 for (i = 0; i < d->indicator.count; i++) { 1046 d->indicators[i].isLit = False; 1047 paintIndicator(app, d->dialogWindow, d->indicators[i]); 1048 } 1049 d->indicator.current = 0; 1050 } 1051 XSync(app->dpy, False); 1052} 1053 1054void paintDialog(AppInfo *app) 1055{ 1056 DialogInfo *d = app->dialog; 1057 Drawable draw = d->dialogWindow; 1058 int i; 1059 1060 XSetForeground(app->dpy, app->fillGC, d->w3.w.background); 1061 XFillRectangle(app->dpy, draw, app->fillGC, 0, 0, 1062 d->w3.w.width, d->w3.w.height); 1063 if (d->w3.shadowThickness > 0) { 1064 draw_shaded_rectangle(app->dpy, draw, 0, 0, 1065 d->w3.w.width, d->w3.w.height, 1066 d->w3.shadowThickness, 1067 d->w3.topShadowColor, 1068 d->w3.bottomShadowColor); 1069 } 1070 paintLabel(app, draw, d->label); 1071 for (i = 0; i < d->indicator.count; i++) { 1072 paintIndicator(app, draw, d->indicators[i]); 1073 } 1074 paintButton(app, draw, d->okButton); 1075 paintButton(app, draw, d->cancelButton); 1076 XSync(app->dpy, False); 1077} 1078 1079void performGrab(AppInfo *app, int grabType, char *grabTypeName, 1080 Bool shouldGrab, Bool *isGrabbed) { 1081 if ((!(shouldGrab)) || (*isGrabbed)) { 1082 return; 1083 } else if ((GRAB_KEYBOARD != grabType) && (GRAB_POINTER != grabType)) { 1084 fprintf(stderr, "%s[%ld]: performGrab: invalid grab type (%d).\n", 1085 app->appName, (long) app->pid, grabType); 1086 return; 1087 } else { 1088 int status = GrabInvalidTime; /* keep gcc -Wall from complaining */ 1089 unsigned int seconds = 0; 1090 int helpful_message = 0; 1091 /* keyboard and pointer */ 1092 Window grabWindow = app->dialog->dialogWindow; 1093 Bool ownerEvents = False; 1094 Bool pointerMode = GrabModeAsync; 1095 Bool keyboardMode = GrabModeAsync; 1096 /* pointer only */ 1097 unsigned int eventMask = ButtonPressMask | ButtonReleaseMask; 1098 Window confineTo = None; 1099 Cursor cursor = None; 1100 1101 *isGrabbed = True; 1102 1103 if (NULL == grabTypeName) { 1104 fprintf(stderr, "%s[%ld]: performGrab: null grab type name.\n", 1105 app->appName, (long) app->pid); 1106 } 1107 1108 if (0 == app->grabFailTimeout) { 1109 /* Ensure we try to perform the grab at least once. */ 1110 app->grabFailTimeout = 1; 1111 } 1112 while (seconds < app->grabFailTimeout) { 1113 XSync(app->dpy, False); 1114 switch (grabType) { 1115 case GRAB_KEYBOARD: 1116 status = XGrabKeyboard(app->dpy, grabWindow, ownerEvents, 1117 pointerMode, keyboardMode, CurrentTime); 1118 break; 1119 case GRAB_POINTER: 1120 status = XGrabPointer(app->dpy, grabWindow, ownerEvents, 1121 eventMask, pointerMode, keyboardMode, 1122 confineTo, cursor, CurrentTime); 1123 break; 1124 } 1125 XSync(app->dpy, False); 1126 if (GrabSuccess == status) { 1127 if (helpful_message) { 1128 fprintf(stderr, "%s[%ld]: Got %s.\n", 1129 app->appName, (long) app->pid, grabTypeName); 1130 } 1131 break; 1132 } 1133 if (!helpful_message) { 1134 fprintf(stderr, "%s[%ld]: Trying to grab %s ...\n", 1135 app->appName, (long) app->pid, grabTypeName); 1136 helpful_message = 1; 1137 } 1138 seconds += app->grabRetryInterval; 1139 sleep(app->grabRetryInterval); 1140 } 1141 if (GrabSuccess != status) { 1142 char *reason = "reason unknown"; 1143 1144 switch (status) { 1145 case AlreadyGrabbed: 1146 reason = "someone else already has it"; 1147 break; 1148 case GrabFrozen: 1149 reason = "someone else has frozen it"; 1150 break; 1151 case GrabInvalidTime: 1152 reason = "bad grab time [this shouldn't happen]"; 1153 break; 1154 case GrabNotViewable: 1155 reason = "grab not viewable [this shouldn't happen]"; 1156 break; 1157 } 1158 fprintf(stderr, "%s[%ld]: Could not grab %s (%s)\n", 1159 app->appName, (long) app->pid, grabTypeName, reason); 1160 exitApp(app, EXIT_STATUS_ERROR); 1161 } 1162 } 1163} 1164 1165 1166void grabKeyboard(AppInfo *app) 1167{ 1168 performGrab(app, GRAB_KEYBOARD, "keyboard", app->grabKeyboard, 1169 &(app->isKeyboardGrabbed)); 1170} 1171 1172void ungrabKeyboard(AppInfo *app) 1173{ 1174 if (app->grabKeyboard) { 1175 XUngrabKeyboard(app->dpy, CurrentTime); 1176 } 1177} 1178 1179void grabPointer(AppInfo *app) 1180{ 1181 performGrab(app, GRAB_POINTER, "pointer", app->grabPointer, 1182 &(app->isPointerGrabbed)); 1183} 1184 1185void ungrabPointer(AppInfo *app) 1186{ 1187 if (app->grabPointer) { 1188 XUngrabPointer(app->dpy, CurrentTime); 1189 } 1190} 1191 1192void grabServer(AppInfo *app) 1193{ 1194 if ((!(app->grabServer)) || (app->isServerGrabbed)) { 1195 return; 1196 } else { 1197 app->isServerGrabbed = True; 1198 XSync(app->dpy, False); 1199 XGrabServer(app->dpy); 1200 XSync(app->dpy, False); 1201 } 1202} 1203 1204void ungrabServer(AppInfo *app) 1205{ 1206 if (app->grabServer) { 1207 XUngrabServer(app->dpy); 1208 } 1209} 1210 1211void cleanUp(AppInfo *app) 1212{ 1213 cancelInputTimeout(app); 1214 XDestroyWindow(app->dpy, app->dialog->dialogWindow); 1215 destroyGCs(app); 1216 destroyDialog(app); 1217 if (app->buf) { 1218 memset(app->buf, 0, app->bufSize); 1219 } 1220 freeIf(app->buf); 1221 ungrabPointer(app); 1222 ungrabKeyboard(app); 1223 ungrabServer(app); 1224} 1225 1226void exitApp(AppInfo *app, int exitCode) 1227{ 1228 cleanUp(app); 1229 exit(exitCode); 1230} 1231 1232void acceptAction(AppInfo *app) 1233{ 1234 int status = append_to_buf(&(app->buf), &(app->bufSize), 1235 &(app->bufIndex), '\0'); 1236 if (APPEND_FAILURE == status) { 1237 cleanUp(app); 1238 outOfMemory(app, __LINE__); 1239 } 1240 fputs(app->buf, stdout); 1241 fputc('\n', stdout); 1242 exitApp(app, EXIT_STATUS_ACCEPT); 1243} 1244 1245void cancelAction(AppInfo *app) 1246{ 1247 exitApp(app, EXIT_STATUS_CANCEL); 1248} 1249 1250void backspacePassphrase(AppInfo *app) 1251{ 1252 if (0 >= app->bufIndex) { 1253 XBell(app->dpy, 0); 1254 return; 1255 } 1256 (app->bufIndex)--; 1257 updateIndicators(app, -1); 1258} 1259 1260void erasePassphrase(AppInfo *app) 1261{ 1262 if (0 >= app->bufIndex) { 1263 XBell(app->dpy, 0); 1264 return; 1265 } 1266 updateIndicators(app, 0); 1267 app->bufIndex = 0; 1268} 1269 1270void addToPassphrase(AppInfo *app, char c) 1271{ 1272 int status = append_to_buf(&(app->buf), &(app->bufSize), 1273 &(app->bufIndex), c); 1274 if (APPEND_FAILURE == status) { 1275 cleanUp(app); 1276 outOfMemory(app, __LINE__); 1277 } 1278 updateIndicators(app, 1); 1279} 1280 1281void handleKeyPress(AppInfo *app, XEvent *event) 1282{ 1283 char s[2]; 1284 int n; 1285 1286 if (event->xkey.send_event) { 1287 /* Pay no attention to synthetic key events. */ 1288 return; 1289 } 1290 cancelInputTimeout(app); 1291 n = XLookupString(&(event->xkey), s, 1, NULL, NULL); 1292 1293 if (1 != n) { 1294 return; 1295 } 1296 s[1] = '\0'; 1297 switch (s[0]) { 1298 case '\010': 1299 case '\177': 1300 backspacePassphrase(app); 1301 break; 1302 case '\025': 1303 case '\030': 1304 erasePassphrase(app); 1305 break; 1306 case '\012': 1307 case '\015': 1308 acceptAction(app); 1309 break; 1310 case '\033': 1311 cancelAction(app); 1312 break; 1313 default: 1314 addToPassphrase(app, s[0]); 1315 break; 1316 } 1317} 1318 1319Bool eventIsInsideButton(AppInfo *app, XEvent *event, ButtonInfo button) 1320{ 1321 /* 'gcc -Wall' complains about 'app' being an unused parameter. 1322 * Tough. We might want to use it later, and then we don't have 1323 * to change it in each place it's called. Performance won't suffer. 1324 */ 1325 int status = False; 1326 int x, y; 1327 1328 switch(event->type) { 1329 case ButtonPress: 1330 case ButtonRelease: 1331 x = event->xbutton.x; 1332 y = event->xbutton.y; 1333 break; 1334 case MotionNotify: 1335 x = event->xmotion.x; 1336 y = event->xmotion.y; 1337 break; 1338 default: 1339 return(False); 1340 } 1341 if ((x >= (button.w3.w.x + button.w3.borderWidth)) && 1342 (x < (button.w3.w.x + button.w3.w.width - 1343 (2 * button.w3.borderWidth))) && 1344 (y >= (button.w3.w.y + button.w3.borderWidth)) && 1345 (y < (button.w3.w.y + button.w3.w.height - 1346 (2 * button.w3.borderWidth)))) { 1347 status = True; 1348 } 1349 return(status); 1350} 1351 1352void handleButtonPress(AppInfo *app, XEvent *event) 1353{ 1354 DialogInfo *d = app->dialog; 1355 1356 cancelInputTimeout(app); 1357 if (event->xbutton.button != Button1) { 1358 return; 1359 } 1360 if (ButtonPress == event->type) { 1361 if (eventIsInsideButton(app, event, d->okButton)) { 1362 d->pressedButton = OK_BUTTON; 1363 d->okButton.pressed = True; 1364 paintButton(app, d->dialogWindow, d->okButton); 1365 } else if (eventIsInsideButton(app, event, d->cancelButton)) { 1366 d->pressedButton = CANCEL_BUTTON; 1367 d->cancelButton.pressed = True; 1368 paintButton(app, d->dialogWindow, d->cancelButton); 1369 } else { 1370 d->pressedButton = NO_BUTTON; 1371 } 1372 } else if (ButtonRelease == event->type) { 1373 if (OK_BUTTON == d->pressedButton) { 1374 if (eventIsInsideButton(app, event, d->okButton)) { 1375 acceptAction(app); 1376 } else { 1377 if (d->okButton.pressed) { 1378 d->okButton.pressed = False; 1379 paintButton(app, d->dialogWindow, d->okButton); 1380 } 1381 } 1382 } else if (CANCEL_BUTTON == d->pressedButton) { 1383 if (eventIsInsideButton(app, event, d->cancelButton)) { 1384 cancelAction(app); 1385 } else { 1386 if (d->cancelButton.pressed) { 1387 d->cancelButton.pressed = False; 1388 paintButton(app, d->dialogWindow, d->cancelButton); 1389 } 1390 } 1391 } 1392 d->pressedButton = NO_BUTTON; 1393 } 1394} 1395 1396void handlePointerMotion(AppInfo *app, XEvent *event) 1397{ 1398 DialogInfo *d = app->dialog; 1399 1400 if (NO_BUTTON == d->pressedButton) { 1401 return; 1402 } else if (OK_BUTTON == d->pressedButton) { 1403 if (eventIsInsideButton(app, event, d->okButton)) { 1404 if (!(d->okButton.pressed)) { 1405 d->okButton.pressed = True; 1406 paintButton(app, d->dialogWindow, d->okButton); 1407 } 1408 } else { 1409 if (d->okButton.pressed) { 1410 d->okButton.pressed = False; 1411 paintButton(app, d->dialogWindow, d->okButton); 1412 } 1413 } 1414 } else if (CANCEL_BUTTON == d->pressedButton) { 1415 if (eventIsInsideButton(app, event, d->cancelButton)) { 1416 if (!(d->cancelButton.pressed)) { 1417 d->cancelButton.pressed = True; 1418 paintButton(app, d->dialogWindow, d->cancelButton); 1419 } 1420 } else { 1421 if (d->cancelButton.pressed) { 1422 d->cancelButton.pressed = False; 1423 paintButton(app, d->dialogWindow, d->cancelButton); 1424 } 1425 } 1426 } 1427} 1428 1429void handleInputTimeout(XtPointer data, XtIntervalId *timerId) 1430{ 1431 /* 'gcc -Wall' complains about 'timerId' being an unused parameter. 1432 * Tough. Xt forces us to have it here. Like it. 1433 */ 1434 AppInfo *app = (AppInfo *) data; 1435 if (app->inputTimeoutActive) { 1436 app->inputTimeoutActive = False; 1437 fprintf(stderr, "%s[%ld]: *Yawn*...timed out after %lu seconds.\n", 1438 app->appName, (long) app->pid, (app->inputTimeout / 1000)); 1439 exitApp(app, EXIT_STATUS_TIMEOUT); 1440 } 1441} 1442 1443void cancelInputTimeout(AppInfo *app) 1444{ 1445 if (app->inputTimeoutActive) { 1446 app->inputTimeoutActive = False; 1447 XtRemoveTimeOut(app->inputTimeoutTimerId); 1448 } 1449} 1450 1451int main(int argc, char **argv) 1452{ 1453 AppInfo app; 1454 XEvent event; 1455 XineramaScreenInfo *screens; 1456 int nscreens; 1457 1458 memset(&app, 0, sizeof(app)); 1459 1460 progclass = "SshAskpass"; 1461 app.toplevelShell = XtAppInitialize(&(app.appContext), progclass, 1462 NULL, 0, &argc, argv, 1463 defaults, NULL, 0); 1464 app.argc = argc; 1465 app.argv = argv; 1466 app.dpy = XtDisplay(app.toplevelShell); 1467 app.screen = DefaultScreenOfDisplay(app.dpy); 1468 app.rootWindow = RootWindowOfScreen(app.screen); 1469 app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy)); 1470 app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy)); 1471 app.colormap = DefaultColormapOfScreen(app.screen); 1472 app.resourceDb = XtDatabase(app.dpy); 1473 XtGetApplicationNameAndClass(app.dpy, &progname, &progclass); 1474 app.appName = progname; 1475 app.appClass = progclass; 1476 /* For resources.c. */ 1477 db = app.resourceDb; 1478 1479 /* Seconds after which keyboard/pointer grab fail. */ 1480 app.grabFailTimeout = 5; 1481 /* Number of seconds to wait between grab attempts. */ 1482 app.grabRetryInterval = 1; 1483 1484 app.pid = getpid(); 1485 1486 { 1487 struct rlimit resourceLimit; 1488 int status; 1489 1490 status = getrlimit(RLIMIT_CORE, &resourceLimit); 1491 if (-1 == status) { 1492 fprintf(stderr, "%s[%ld]: getrlimit failed (%s)\n", app.appName, 1493 (long) app.pid, strerror(errno)); 1494 exit(EXIT_STATUS_ERROR); 1495 } 1496 resourceLimit.rlim_cur = 0; 1497 status = setrlimit(RLIMIT_CORE, &resourceLimit); 1498 if (-1 == status) { 1499 fprintf(stderr, "%s[%ld]: setrlimit failed (%s)\n", app.appName, 1500 (long) app.pid, strerror(errno)); 1501 exit(EXIT_STATUS_ERROR); 1502 } 1503 } 1504 1505 app.screen_width = WidthOfScreen(app.screen); 1506 app.screen_height = HeightOfScreen(app.screen); 1507 if (XineramaIsActive(app.dpy) && 1508 (screens = XineramaQueryScreens(app.dpy, &nscreens)) != NULL && 1509 nscreens) { 1510 app.screen_width = screens[0].width; 1511 app.screen_height = screens[0].height; 1512 XFree(screens); 1513 } 1514 1515 app.xResolution = 1516 app.screen_width * 1000 / WidthMMOfScreen(app.screen); 1517 app.yResolution = 1518 app.screen_height * 1000 / HeightMMOfScreen(app.screen); 1519 1520 createDialog(&app); 1521 createGCs(&app); 1522 1523 app.eventMask = 0; 1524 app.eventMask |= ExposureMask; 1525 app.eventMask |= ButtonPressMask; 1526 app.eventMask |= ButtonReleaseMask; 1527 app.eventMask |= Button1MotionMask; 1528 app.eventMask |= KeyPressMask; 1529 1530 createDialogWindow(&app); 1531 1532 XMapWindow(app.dpy, app.dialog->dialogWindow); 1533 if (app.inputTimeout > 0) { 1534 app.inputTimeoutActive = True; 1535 app.inputTimeoutTimerId = 1536 XtAppAddTimeOut(app.appContext, app.inputTimeout, 1537 handleInputTimeout, (XtPointer) &app); 1538 } 1539 1540 1541 while(True) { 1542 XtAppNextEvent(app.appContext, &event); 1543 switch (event.type) { 1544 case Expose: 1545 grabServer(&app); 1546 grabKeyboard(&app); 1547 grabPointer(&app); 1548 if (event.xexpose.count) { 1549 break; 1550 } 1551 paintDialog(&app); 1552 break; 1553 case ButtonPress: 1554 case ButtonRelease: 1555 handleButtonPress(&app, &event); 1556 break; 1557 case MotionNotify: 1558 handlePointerMotion(&app, &event); 1559 case KeyPress: 1560 handleKeyPress(&app, &event); 1561 break; 1562 case ClientMessage: 1563 if ((32 == event.xclient.format) && 1564 ((unsigned long) event.xclient.data.l[0] == 1565 app.wmDeleteWindowAtom)) { 1566 cancelAction(&app); 1567 } 1568 break; 1569 default: 1570 break; 1571 } 1572 } 1573 1574 fprintf(stderr, "%s[%ld]: This should not happen.\n", app.appName, 1575 (long) app.pid); 1576 return(EXIT_STATUS_ANOMALY); 1577} 1578 1579