source: trunk/third/x11-ssh-askpass/x11-ssh-askpass.c @ 17783

Revision 17783, 45.9 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17782, which included commits to RCS files with non-trunk default branches.
Line 
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
66char *progname = NULL;
67char *progclass = NULL;
68XrmDatabase db = 0;
69
70static char *defaults[] = {
71#include "SshAskpass_ad.h"
72   0
73};
74
75void 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
82void freeIf(void *p)
83{
84   if (p) {
85      free(p);
86   }
87}
88
89void freeFontIf(AppInfo *app, XFontStruct *f)
90{
91   if (f) {
92      XFreeFont(app->dpy, f);
93   }
94}
95
96XFontStruct *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
112char *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
126unsigned 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)
174long 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
220void 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
229void 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
247void 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
260void 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
273void 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
286void 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
297Dimension 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
312Dimension 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. */
328TextObject *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. */
344void 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
366void 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
699void 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
716void 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
735void 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->min_width = d->w3.w.width;
768   d->sizeHints->min_height = d->w3.w.height;
769   d->sizeHints->flags |= PMinSize;
770   d->sizeHints->max_width = d->w3.w.width;
771   d->sizeHints->max_height = d->w3.w.height;
772   d->sizeHints->flags |= PMaxSize;
773   d->sizeHints->base_width = d->w3.w.width;
774   d->sizeHints->base_height = d->w3.w.height;
775   d->sizeHints->flags |= PBaseSize;
776   
777   d->wmHints = XAllocWMHints();
778   if (!(d->wmHints)) {
779      destroyDialog(app);
780      outOfMemory(app, __LINE__);
781   }
782   d->wmHints->flags = 0;
783   d->wmHints->input = True;
784   d->wmHints->flags |= InputHint;
785   d->wmHints->initial_state = NormalState;
786   d->wmHints->flags |= StateHint;
787
788   d->classHints = XAllocClassHint();
789   if (!(d->classHints)) {
790      destroyDialog(app);
791      outOfMemory(app, __LINE__);
792   }
793   d->classHints->res_name = app->appName;
794   d->classHints->res_class = app->appClass;
795
796   if (!XStringListToTextProperty(&(d->title), 1, &(d->windowName))) {
797      destroyDialog(app);
798      outOfMemory(app, __LINE__);
799   }
800   XSetWMProperties(app->dpy, d->dialogWindow, &(d->windowName), NULL,
801                    app->argv, app->argc, d->sizeHints,
802                    d->wmHints, d->classHints);
803   XSetTransientForHint(app->dpy, d->dialogWindow, d->dialogWindow);
804   
805   app->wmDeleteWindowAtom = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False);
806   XSetWMProtocols(app->dpy, d->dialogWindow, &(app->wmDeleteWindowAtom), 1);
807}
808
809void createGCs(AppInfo *app)
810{
811   DialogInfo *d = app->dialog;
812   
813   XGCValues gcv;
814   unsigned long gcvMask;
815   
816   gcvMask = 0;
817   gcv.foreground = d->w3.w.background;
818   gcvMask |= GCForeground;
819   gcv.fill_style = FillSolid;
820   gcvMask |= GCFillStyle;
821   app->fillGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
822   
823   gcvMask = 0;
824   gcv.foreground = d->w3.borderColor;
825   gcvMask |= GCForeground;
826   gcv.line_width = d->w3.borderWidth;
827   gcvMask |= GCLineWidth;
828   gcv.line_style = LineSolid;
829   gcvMask |= GCLineStyle;
830   gcv.cap_style = CapButt;
831   gcvMask |= GCCapStyle;
832   gcv.join_style = JoinMiter;
833   gcvMask |= GCJoinStyle;
834   app->borderGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
835   
836   gcvMask = 0;
837   gcv.foreground = d->label.w.foreground;
838   gcvMask |= GCForeground;
839   gcv.background = d->label.w.background;
840   gcvMask |= GCBackground;
841   gcv.font = d->label.font->fid;
842   gcvMask |= GCFont;
843   app->textGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
844   
845   gcvMask = 0;
846   gcv.foreground = d->indicator.w3.w.foreground;
847   gcvMask |= GCForeground;
848   gcv.fill_style = FillSolid;
849   gcvMask |= GCFillStyle;
850   app->brightGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
851   
852   gcvMask = 0;
853   gcv.foreground = d->indicator.w3.w.background;
854   gcvMask |= GCForeground;
855   gcv.fill_style = FillSolid;
856   gcvMask |= GCFillStyle;
857   app->dimGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
858}
859
860void destroyGCs(AppInfo *app)
861{
862   XFreeGC(app->dpy, app->fillGC);
863   XFreeGC(app->dpy, app->borderGC);
864   XFreeGC(app->dpy, app->textGC);
865   XFreeGC(app->dpy, app->brightGC);
866   XFreeGC(app->dpy, app->dimGC);
867}
868
869void paintLabel(AppInfo *app, Drawable draw, LabelInfo label)
870{
871   TextObject *t;
872   Position x;
873   Position y;
874
875   if (!(label.fullText)) {
876      return;
877   }
878   XSetForeground(app->dpy, app->textGC, label.w.foreground);
879   XSetBackground(app->dpy, app->textGC, label.w.background);
880   XSetFont(app->dpy, app->textGC, label.font->fid);
881   
882   t = label.multiText;
883   x = label.w.x;
884   y = label.w.y + t->ascent;
885   while (NULL != t) {
886      if (t->text) {
887         XDrawString(app->dpy, draw, app->textGC, x, y, t->text,
888                     t->textLength);
889      }
890      y += t->descent;
891      t = t->next;
892      if (t) {
893         y += t->ascent;
894      }
895   }
896}
897
898void paintButton(AppInfo *app, Drawable draw, ButtonInfo button)
899{
900   Position x;
901   Position y;
902   Dimension width;
903   Dimension height;
904   
905   if (button.w3.borderWidth > 0) {
906      XSetForeground(app->dpy, app->borderGC, button.w3.borderColor);
907      XFillRectangle(app->dpy, draw, app->borderGC, button.w3.w.x,
908                     button.w3.w.y, button.w3.w.width, button.w3.w.height);
909   }
910   if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
911      Pixel tmp = button.w3.w.background;
912      button.w3.w.background = button.w3.w.foreground;
913      button.w3.w.foreground = tmp;
914      tmp = button.label.w.background;
915      button.label.w.background = button.label.w.foreground;
916      button.label.w.foreground = tmp;
917   }
918   x = (button.w3.w.x + button.w3.borderWidth);
919   y = (button.w3.w.y + button.w3.borderWidth);
920   width = (button.w3.w.width - (2 * button.w3.borderWidth));
921   height = (button.w3.w.height - (2 * button.w3.borderWidth));
922   if ((button.w3.shadowThickness > 0) && (button.pressed)) {
923      XSetForeground(app->dpy, app->fillGC, button.w3.topShadowColor);
924   } else {
925      XSetForeground(app->dpy, app->fillGC, button.w3.w.background);
926   }
927   XFillRectangle(app->dpy, draw, app->fillGC, x, y, width, height);
928   if (button.w3.shadowThickness > 0) {
929      if (button.pressed) {
930         draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
931                               button.w3.shadowThickness,
932                               button.w3.bottomShadowColor,
933                               button.w3.topShadowColor);
934      } else {
935         draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
936                               button.w3.shadowThickness,
937                               button.w3.topShadowColor,
938                               button.w3.bottomShadowColor);
939      }
940   }
941   if ((button.w3.shadowThickness > 0) && (button.pressed)) {
942      Dimension pressedAdjustment;
943     
944      pressedAdjustment = button.w3.shadowThickness / 2;
945      if (pressedAdjustment < 1) {
946         pressedAdjustment = 1;
947      }
948      x = button.label.w.x;
949      y = button.label.w.y;
950      button.label.w.x += pressedAdjustment;
951      button.label.w.y += pressedAdjustment;
952      paintLabel(app, draw, button.label);
953      button.label.w.x = x;
954      button.label.w.y = y;
955   } else {
956      paintLabel(app, draw, button.label);
957   }
958   if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
959      Pixel tmp = button.w3.w.background;
960      button.w3.w.background = button.w3.w.foreground;
961      button.w3.w.foreground = tmp;
962      tmp = button.label.w.background;
963      button.label.w.background = button.label.w.foreground;
964      button.label.w.foreground = tmp;
965   }
966}
967
968void paintIndicator(AppInfo *app, Drawable draw, IndicatorElement indicator)
969{
970   Position x;
971   Position y;
972   Dimension width;
973   Dimension height;
974   GC gc = app->dimGC;
975   
976   if (indicator.parent->w3.borderWidth > 0) {
977      XSetForeground(app->dpy, app->borderGC,
978                     indicator.parent->w3.borderColor);
979      XFillRectangle(app->dpy, draw, app->borderGC, indicator.w.x,
980                     indicator.w.y, indicator.w.width, indicator.w.height);
981   }
982   if (indicator.isLit) {
983      gc = app->brightGC;
984   }
985   x = (indicator.w.x + indicator.parent->w3.borderWidth);
986   y = (indicator.w.y + indicator.parent->w3.borderWidth);
987   width = (indicator.w.width - (2 * indicator.parent->w3.borderWidth));
988   height = (indicator.w.height - (2 * indicator.parent->w3.borderWidth));
989   XFillRectangle(app->dpy, draw, gc, x, y, width, height);
990   if (indicator.parent->w3.shadowThickness > 0) {
991      draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
992                            indicator.parent->w3.shadowThickness,
993                            indicator.parent->w3.bottomShadowColor,
994                            indicator.parent->w3.topShadowColor);
995   }
996}
997
998void updateIndicatorElement(AppInfo *app, int i)
999{
1000   DialogInfo *d = app->dialog;
1001   
1002   d->indicators[i].isLit = !(d->indicators[i].isLit);
1003   paintIndicator(app, d->dialogWindow, d->indicators[i]);
1004}
1005
1006void updateIndicators(AppInfo *app, int condition)
1007{
1008   DialogInfo *d = app->dialog;
1009   
1010   if (condition > 0) {
1011      /* Move forward one. */
1012      updateIndicatorElement(app, d->indicator.current);
1013      if (d->indicator.current < (d->indicator.count - 1)) {
1014         (d->indicator.current)++;
1015      } else {
1016         d->indicator.current = 0;
1017      }
1018   } else if (condition < 0) {
1019      /* Move backward one. */
1020      if (d->indicator.current > 0) {
1021         (d->indicator.current)--;
1022      } else {
1023         d->indicator.current = d->indicator.count - 1;
1024      }
1025      updateIndicatorElement(app, d->indicator.current);
1026   } else {
1027      /* Erase them all. */
1028      int i;
1029     
1030      for (i = 0; i < d->indicator.count; i++) {
1031         d->indicators[i].isLit = False;
1032         paintIndicator(app, d->dialogWindow, d->indicators[i]);
1033      }
1034      d->indicator.current = 0;
1035   }
1036   XSync(app->dpy, False);
1037}
1038
1039void paintDialog(AppInfo *app)
1040{
1041   DialogInfo *d = app->dialog;
1042   Drawable draw = d->dialogWindow;
1043   int i;
1044   
1045   XSetForeground(app->dpy, app->fillGC, d->w3.w.background);
1046   XFillRectangle(app->dpy, draw, app->fillGC, 0, 0,
1047                  d->w3.w.width, d->w3.w.height);
1048   if (d->w3.shadowThickness > 0) {
1049      draw_shaded_rectangle(app->dpy, draw, 0, 0,
1050                            d->w3.w.width, d->w3.w.height,
1051                            d->w3.shadowThickness,
1052                            d->w3.topShadowColor,
1053                            d->w3.bottomShadowColor);
1054   }
1055   paintLabel(app, draw, d->label);
1056   for (i = 0; i < d->indicator.count; i++) {
1057      paintIndicator(app, draw, d->indicators[i]);
1058   }
1059   paintButton(app, draw, d->okButton);
1060   paintButton(app, draw, d->cancelButton);
1061   XSync(app->dpy, False);
1062}
1063
1064void performGrab(AppInfo *app, int grabType, char *grabTypeName,
1065                 Bool shouldGrab, Bool *isGrabbed) {
1066   if ((!(shouldGrab)) || (*isGrabbed)) {
1067      return;
1068   } else if ((GRAB_KEYBOARD != grabType) && (GRAB_POINTER != grabType)) {
1069      fprintf(stderr, "%s[%ld]: performGrab: invalid grab type (%d).\n",
1070              app->appName, (long) app->pid, grabType);
1071      return;
1072   } else {
1073      int status = GrabInvalidTime;     /* keep gcc -Wall from complaining */
1074      unsigned int seconds = 0;
1075      int helpful_message = 0;
1076      /* keyboard and pointer */
1077      Window grabWindow = app->dialog->dialogWindow;
1078      Bool ownerEvents = False;
1079      Bool pointerMode = GrabModeAsync;
1080      Bool keyboardMode = GrabModeAsync;
1081      /* pointer only */
1082      unsigned int eventMask = ButtonPressMask | ButtonReleaseMask;
1083      Window confineTo = None;
1084      Cursor cursor = None;
1085     
1086      *isGrabbed = True;
1087     
1088      if (NULL == grabTypeName) {
1089         fprintf(stderr, "%s[%ld]: performGrab: null grab type name.\n",
1090                 app->appName, (long) app->pid);
1091      }
1092
1093      if (0 == app->grabFailTimeout) {
1094         /* Ensure we try to perform the grab at least once. */
1095         app->grabFailTimeout = 1;
1096      }
1097      while (seconds < app->grabFailTimeout) {
1098         XSync(app->dpy, False);
1099         switch (grabType) {
1100          case GRAB_KEYBOARD:
1101            status = XGrabKeyboard(app->dpy, grabWindow, ownerEvents,
1102                                   pointerMode, keyboardMode, CurrentTime);
1103            break;
1104          case GRAB_POINTER:
1105            status = XGrabPointer(app->dpy, grabWindow, ownerEvents,
1106                                  eventMask, pointerMode, keyboardMode,
1107                                  confineTo, cursor, CurrentTime);
1108            break;
1109         }
1110         XSync(app->dpy, False);
1111         if (GrabSuccess == status) {
1112            if (helpful_message) {
1113               fprintf(stderr, "%s[%ld]: Got %s.\n",
1114                       app->appName, (long) app->pid, grabTypeName);
1115            }
1116            break;
1117         }
1118         if (!helpful_message) {
1119            fprintf(stderr, "%s[%ld]: Trying to grab %s ...\n",
1120                    app->appName, (long) app->pid, grabTypeName);
1121            helpful_message = 1;
1122         }
1123         seconds += app->grabRetryInterval;
1124         sleep(app->grabRetryInterval);
1125      }
1126      if (GrabSuccess != status) {
1127         char *reason = "reason unknown";
1128         
1129         switch (status) {
1130          case AlreadyGrabbed:
1131            reason = "someone else already has it";
1132            break;
1133          case GrabFrozen:
1134            reason = "someone else has frozen it";
1135            break;
1136          case GrabInvalidTime:
1137            reason = "bad grab time [this shouldn't happen]";
1138            break;
1139          case GrabNotViewable:
1140            reason = "grab not viewable [this shouldn't happen]";
1141            break;
1142         }
1143         fprintf(stderr, "%s[%ld]: Could not grab %s (%s)\n",
1144                 app->appName, (long) app->pid, grabTypeName, reason);
1145         exitApp(app, EXIT_STATUS_ERROR);
1146      }
1147   }
1148}
1149                 
1150
1151void grabKeyboard(AppInfo *app)
1152{
1153   performGrab(app, GRAB_KEYBOARD, "keyboard", app->grabKeyboard,
1154               &(app->isKeyboardGrabbed));
1155}
1156
1157void ungrabKeyboard(AppInfo *app)
1158{
1159   if (app->grabKeyboard) {
1160      XUngrabKeyboard(app->dpy, CurrentTime);
1161   }
1162}
1163
1164void grabPointer(AppInfo *app)
1165{
1166   performGrab(app, GRAB_POINTER, "pointer", app->grabPointer,
1167               &(app->isPointerGrabbed));
1168}
1169
1170void ungrabPointer(AppInfo *app)
1171{
1172   if (app->grabPointer) {
1173      XUngrabPointer(app->dpy, CurrentTime);
1174   }
1175}
1176
1177void grabServer(AppInfo *app)
1178{
1179   if ((!(app->grabServer)) || (app->isServerGrabbed)) {
1180      return;
1181   } else {
1182      app->isServerGrabbed = True;
1183      XSync(app->dpy, False);
1184      XGrabServer(app->dpy);
1185      XSync(app->dpy, False);
1186   }
1187}
1188
1189void ungrabServer(AppInfo *app)
1190{
1191   if (app->grabServer) {
1192      XUngrabServer(app->dpy);
1193   }
1194}
1195
1196void cleanUp(AppInfo *app)
1197{
1198   cancelInputTimeout(app);
1199   XDestroyWindow(app->dpy, app->dialog->dialogWindow);
1200   destroyGCs(app);
1201   destroyDialog(app);
1202   if (app->buf) {
1203      memset(app->buf, 0, app->bufSize);
1204   }
1205   freeIf(app->buf);
1206   ungrabPointer(app);
1207   ungrabKeyboard(app);
1208   ungrabServer(app);
1209}
1210
1211void exitApp(AppInfo *app, int exitCode)
1212{
1213   cleanUp(app);
1214   exit(exitCode);
1215}
1216
1217void acceptAction(AppInfo *app)
1218{
1219   int status = append_to_buf(&(app->buf), &(app->bufSize),
1220                              &(app->bufIndex), '\0');
1221   if (APPEND_FAILURE == status) {
1222      cleanUp(app);
1223      outOfMemory(app, __LINE__);
1224   }
1225   fputs(app->buf, stdout);
1226   fputc('\n', stdout);
1227   exitApp(app, EXIT_STATUS_ACCEPT);
1228}
1229
1230void cancelAction(AppInfo *app)
1231{
1232   exitApp(app, EXIT_STATUS_CANCEL);
1233}
1234
1235void backspacePassphrase(AppInfo *app)
1236{
1237   if (0 >= app->bufIndex) {
1238      XBell(app->dpy, 0);
1239      return;
1240   }
1241   (app->bufIndex)--;
1242   updateIndicators(app, -1);
1243}
1244
1245void erasePassphrase(AppInfo *app)
1246{
1247   if (0 >= app->bufIndex) {
1248      XBell(app->dpy, 0);
1249      return;
1250   }
1251   updateIndicators(app, 0);
1252   app->bufIndex = 0;
1253}
1254
1255void addToPassphrase(AppInfo *app, char c)
1256{
1257   int status = append_to_buf(&(app->buf), &(app->bufSize),
1258                              &(app->bufIndex), c);
1259   if (APPEND_FAILURE == status) {
1260      cleanUp(app);
1261      outOfMemory(app, __LINE__);
1262   }
1263   updateIndicators(app, 1);
1264}
1265
1266void handleKeyPress(AppInfo *app, XEvent *event)
1267{
1268   char s[2];
1269   int n;
1270   
1271   if (event->xkey.send_event) {
1272      /* Pay no attention to synthetic key events. */
1273      return;
1274   }
1275   cancelInputTimeout(app);
1276   n = XLookupString(&(event->xkey), s, 1, NULL, NULL);
1277   
1278   if (1 != n) {
1279      return;
1280   }
1281   s[1] = '\0';
1282   switch (s[0]) {
1283    case '\010':
1284    case '\177':
1285      backspacePassphrase(app);
1286      break;
1287    case '\025':
1288    case '\030':
1289      erasePassphrase(app);
1290      break;
1291    case '\012':
1292    case '\015':
1293      acceptAction(app);
1294      break;
1295    case '\033':
1296      cancelAction(app);
1297      break;
1298    default:
1299      addToPassphrase(app, s[0]);
1300      break;
1301   }
1302}
1303
1304Bool eventIsInsideButton(AppInfo *app, XEvent *event, ButtonInfo button)
1305{
1306   /* 'gcc -Wall' complains about 'app' being an unused parameter.
1307    * Tough.  We might want to use it later, and then we don't have
1308    * to change it in each place it's called.  Performance won't suffer.
1309    */
1310   int status = False;
1311   int x, y;
1312   
1313   switch(event->type) {
1314    case ButtonPress:
1315    case ButtonRelease:
1316      x = event->xbutton.x;
1317      y = event->xbutton.y;
1318      break;
1319    case MotionNotify:
1320      x = event->xmotion.x;
1321      y = event->xmotion.y;
1322      break;
1323    default:
1324      return(False);
1325   }
1326   if ((x >= (button.w3.w.x + button.w3.borderWidth)) &&
1327       (x < (button.w3.w.x + button.w3.w.width -
1328             (2 * button.w3.borderWidth))) &&
1329       (y >= (button.w3.w.y + button.w3.borderWidth)) &&
1330       (y < (button.w3.w.y + button.w3.w.height -
1331             (2 * button.w3.borderWidth)))) {
1332      status = True;
1333   }
1334   return(status);
1335}
1336
1337void handleButtonPress(AppInfo *app, XEvent *event)
1338{
1339   DialogInfo *d = app->dialog;
1340
1341   cancelInputTimeout(app);
1342   if (event->xbutton.button != Button1) {
1343      return;
1344   }
1345   if (ButtonPress == event->type) {
1346      if (eventIsInsideButton(app, event, d->okButton)) {
1347         d->pressedButton = OK_BUTTON;
1348         d->okButton.pressed = True;
1349         paintButton(app, d->dialogWindow, d->okButton);
1350      } else if (eventIsInsideButton(app, event, d->cancelButton)) {
1351         d->pressedButton = CANCEL_BUTTON;
1352         d->cancelButton.pressed = True;
1353         paintButton(app, d->dialogWindow, d->cancelButton);
1354      } else {
1355         d->pressedButton = NO_BUTTON;
1356      }
1357   } else if (ButtonRelease == event->type) {
1358      if (OK_BUTTON == d->pressedButton) {
1359         if (eventIsInsideButton(app, event, d->okButton)) {
1360            acceptAction(app);
1361         } else {
1362            if (d->okButton.pressed) {
1363               d->okButton.pressed = False;
1364               paintButton(app, d->dialogWindow, d->okButton);
1365            }
1366         }
1367      } else if (CANCEL_BUTTON == d->pressedButton) {
1368         if (eventIsInsideButton(app, event, d->cancelButton)) {
1369            cancelAction(app);
1370         } else {
1371            if (d->cancelButton.pressed) {
1372               d->cancelButton.pressed = False;
1373               paintButton(app, d->dialogWindow, d->cancelButton);
1374            }
1375         }
1376      }
1377      d->pressedButton = NO_BUTTON;
1378   }
1379}
1380
1381void handlePointerMotion(AppInfo *app, XEvent *event)
1382{
1383   DialogInfo *d = app->dialog;
1384   
1385   if (NO_BUTTON == d->pressedButton) {
1386      return;
1387   } else if (OK_BUTTON == d->pressedButton) {
1388      if (eventIsInsideButton(app, event, d->okButton)) {
1389         if (!(d->okButton.pressed)) {
1390            d->okButton.pressed = True;
1391            paintButton(app, d->dialogWindow, d->okButton);
1392         }
1393      } else {
1394         if (d->okButton.pressed) {
1395            d->okButton.pressed = False;
1396            paintButton(app, d->dialogWindow, d->okButton);
1397         }
1398      }
1399   } else if (CANCEL_BUTTON == d->pressedButton) {
1400      if (eventIsInsideButton(app, event, d->cancelButton)) {
1401         if (!(d->cancelButton.pressed)) {
1402            d->cancelButton.pressed = True;
1403            paintButton(app, d->dialogWindow, d->cancelButton);
1404         }
1405      } else {
1406         if (d->cancelButton.pressed) {
1407            d->cancelButton.pressed = False;
1408            paintButton(app, d->dialogWindow, d->cancelButton);
1409         }
1410      }
1411   }
1412}
1413
1414void handleInputTimeout(XtPointer data, XtIntervalId *timerId)
1415{
1416   /* 'gcc -Wall' complains about 'timerId' being an unused parameter.
1417    * Tough.  Xt forces us to have it here.  Like it.
1418    */
1419   AppInfo *app = (AppInfo *) data;
1420   if (app->inputTimeoutActive) {
1421      app->inputTimeoutActive = False;
1422      fprintf(stderr, "%s[%ld]: *Yawn*...timed out after %lu seconds.\n",
1423              app->appName, (long) app->pid, (app->inputTimeout / 1000));
1424      exitApp(app, EXIT_STATUS_TIMEOUT);
1425   }
1426}
1427
1428void cancelInputTimeout(AppInfo *app)
1429{
1430   if (app->inputTimeoutActive) {
1431      app->inputTimeoutActive = False;
1432      XtRemoveTimeOut(app->inputTimeoutTimerId);
1433   }
1434}
1435
1436int main(int argc, char **argv)
1437{
1438   AppInfo app;
1439   XEvent event;
1440
1441   memset(&app, 0, sizeof(app));
1442   
1443   progclass = "SshAskpass";
1444   app.toplevelShell = XtAppInitialize(&(app.appContext), progclass,
1445                                        NULL, 0, &argc, argv,
1446                                        defaults, NULL, 0);
1447   app.argc = argc;
1448   app.argv = argv;
1449   app.dpy = XtDisplay(app.toplevelShell);
1450   app.screen = DefaultScreenOfDisplay(app.dpy);
1451   app.rootWindow = RootWindowOfScreen(app.screen);
1452   app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy));
1453   app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy));
1454   app.colormap = DefaultColormapOfScreen(app.screen);
1455   app.resourceDb = XtDatabase(app.dpy);
1456   XtGetApplicationNameAndClass(app.dpy, &progname, &progclass);
1457   app.appName = progname;
1458   app.appClass = progclass;
1459   /* For resources.c. */
1460   db = app.resourceDb;
1461   
1462   /* Seconds after which keyboard/pointer grab fail. */
1463   app.grabFailTimeout = 5;
1464   /* Number of seconds to wait between grab attempts. */
1465   app.grabRetryInterval = 1;
1466   
1467   app.pid = getpid();
1468
1469   {
1470      struct rlimit resourceLimit;
1471      int status;
1472     
1473      status = getrlimit(RLIMIT_CORE, &resourceLimit);
1474      if (-1 == status) {
1475         fprintf(stderr, "%s[%ld]: getrlimit failed (%s)\n", app.appName,
1476                 (long) app.pid, strerror(errno));
1477         exit(EXIT_STATUS_ERROR);
1478      }
1479      resourceLimit.rlim_cur = 0;
1480      status = setrlimit(RLIMIT_CORE, &resourceLimit);
1481      if (-1 == status) {
1482         fprintf(stderr, "%s[%ld]: setrlimit failed (%s)\n", app.appName,
1483                 (long) app.pid, strerror(errno));
1484         exit(EXIT_STATUS_ERROR);
1485      }
1486   }
1487   
1488   app.xResolution =
1489      WidthOfScreen(app.screen) * 1000 / WidthMMOfScreen(app.screen);
1490   app.yResolution =
1491      HeightOfScreen(app.screen) * 1000 / HeightMMOfScreen(app.screen);
1492   
1493   createDialog(&app);
1494   createGCs(&app);
1495   
1496   app.eventMask = 0;
1497   app.eventMask |= ExposureMask;
1498   app.eventMask |= ButtonPressMask;
1499   app.eventMask |= ButtonReleaseMask;
1500   app.eventMask |= Button1MotionMask;
1501   app.eventMask |= KeyPressMask;
1502
1503   createDialogWindow(&app);
1504   
1505   XMapWindow(app.dpy, app.dialog->dialogWindow);
1506   if (app.inputTimeout > 0) {
1507      app.inputTimeoutActive = True;
1508      app.inputTimeoutTimerId =
1509         XtAppAddTimeOut(app.appContext, app.inputTimeout,
1510                         handleInputTimeout, (XtPointer) &app);
1511   }
1512
1513   
1514   while(True) {
1515      XtAppNextEvent(app.appContext, &event);
1516      switch (event.type) {
1517       case Expose:
1518         grabServer(&app);
1519         grabKeyboard(&app);
1520         grabPointer(&app);
1521         if (event.xexpose.count) {
1522            break;
1523         }
1524         paintDialog(&app);
1525         break;
1526       case ButtonPress:
1527       case ButtonRelease:
1528         handleButtonPress(&app, &event);
1529         break;
1530       case MotionNotify:
1531         handlePointerMotion(&app, &event);
1532       case KeyPress:
1533         handleKeyPress(&app, &event);
1534         break;
1535       case ClientMessage:
1536         if ((32 == event.xclient.format) &&
1537             ((unsigned long) event.xclient.data.l[0] ==
1538              app.wmDeleteWindowAtom)) {
1539            cancelAction(&app);
1540         }
1541         break;
1542       default:
1543         break;
1544      }
1545   }
1546
1547   fprintf(stderr, "%s[%ld]: This should not happen.\n", app.appName,
1548           (long) app.pid);
1549   return(EXIT_STATUS_ANOMALY);
1550}
1551
Note: See TracBrowser for help on using the repository browser.