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