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