http://ftp.x.org/contrib/applications/xscreensaver-2.17.tar.gz
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-1997 Jamie Zawinski <jwz@netscape.com>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Athena locking code contributed by Jon A. Christopher <jac8782@tamu.edu> */
14 /* Copyright 1997, with the same permissions as above. */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #ifndef NO_LOCKING   /* whole file */
21
22 #include <X11/StringDefs.h>
23 #include <X11/Intrinsic.h>
24 #include "xscreensaver.h"
25
26 #ifndef VMS
27 # include <pwd.h>
28 #else /* VMS */
29 extern char *getenv(const char *name);
30 extern int validate_user(char *name, char *password);
31 static char * user_vms;
32 #endif /* VMS */
33
34
35 #ifdef HAVE_ATHENA
36
37 # include <X11/Shell.h>
38 # include <X11/StringDefs.h>
39 # include <X11/Xaw/Text.h>
40 # include <X11/Xaw/Label.h>
41 # include <X11/Xaw/Dialog.h>
42
43 static void passwd_done_cb (Widget, XtPointer, XtPointer);
44 static XtActionsRec actionsList[] =
45 {
46     {"passwdentered", (XtActionProc) passwd_done_cb},
47 };
48
49 static char Translations[] =
50 "\
51 <Key>Return:   passwdentered()\
52 ";
53
54 #else  /* HAVE_MOTIF */
55
56 # include <Xm/Xm.h>
57 # include <Xm/List.h>
58 # include <Xm/TextF.h>
59
60 #endif /* HAVE_MOTIF */
61
62 extern Widget passwd_dialog;
63 extern Widget passwd_form;
64 extern Widget roger_label;
65 extern Widget passwd_label1;
66 extern Widget passwd_label3;
67 extern Widget passwd_cancel;
68
69 #ifdef HAVE_MOTIF
70 extern Widget passwd_text;
71 extern Widget passwd_done;
72 #else  /* HAVE_ATHENA */
73 static Widget passwd_text = 0;  /* gag... */
74 static Widget passwd_done = 0;
75 #endif /* HAVE_ATHENA */
76
77
78
79 static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
80 static char typed_passwd [80];
81
82 \f
83 #if defined(HAVE_ATHENA) || (XmVersion >= 1002)
84    /* The `destroy' bug apears to be fixed as of Motif 1.2.1, but
85       the `verify-callback' bug is still present. */
86 # define DESTROY_WORKS
87 #endif
88
89 static void
90 passwd_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
91 {
92   passwd_state = pw_cancel;
93 }
94
95 static void
96 passwd_done_cb (Widget button, XtPointer client_data, XtPointer call_data)
97 {
98   if (passwd_state != pw_read) return; /* already done */
99 #ifndef VMS
100
101 # ifdef HAVE_ATHENA
102   strncpy(typed_passwd, XawDialogGetValueString(passwd_form),
103           sizeof(typed_passwd)-1);
104   typed_passwd[sizeof(typed_passwd)-1] = 0;
105 # endif /* HAVE_ATHENA */
106   if (passwd_valid_p (typed_passwd))
107     passwd_state = pw_ok;
108   else
109     passwd_state = pw_fail;
110
111 #else   /* VMS */
112   user_vms = getenv("USER");
113   if (validate_user(user_vms,typed_passwd) == 1) 
114     passwd_state = pw_ok;
115   else 
116     passwd_state = pw_fail;
117 #endif /* VMS */
118 }
119
120 #if defined(HAVE_MOTIF) && defined(VERIFY_CALLBACK_WORKS)
121
122   /* It looks to me like adding any modifyVerify callback causes
123      Motif 1.1.4 to free the the TextF_Value() twice.  I can't see
124      the bug in the Motif source, but Purify complains, even if
125      check_passwd_cb() is a no-op.
126
127      Update: Motif 1.2.1 also loses, but in a different way: it
128      writes beyond the end of a malloc'ed block in ModifyVerify().
129      Probably this block is the text field's text.
130    */
131
132 static void 
133 check_passwd_cb (Widget button, XtPointer client_data, XtPointer call_data)
134 {
135   XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
136
137   if (passwd_state != pw_read)
138     return;
139   else if (vcb->reason == XmCR_ACTIVATE)
140     {
141       passwd_done_cb (0, 0, 0);
142     }
143   else if (vcb->text->length > 1)       /* don't allow "paste" operations */
144     {
145       vcb->doit = False;
146     }
147   else if (vcb->text->ptr != 0)
148     {
149       int i;
150       int L = vcb->text->length;
151       if (L >= sizeof(typed_passwd))
152         L = sizeof(typed_passwd)-1;
153       strncat (typed_passwd, vcb->text->ptr, L);
154       typed_passwd [vcb->endPos + L] = 0;
155       for (i = 0; i < vcb->text->length; i++)
156         vcb->text->ptr [i] = '*';
157     }
158 }
159
160 # else /* HAVE_ATHENA || !VERIFY_CALLBACK_WORKS */
161
162 static void keypress (Widget w, XEvent *event, String *av, Cardinal *ac);
163 static void backspace (Widget w, XEvent *event, String *av, Cardinal *ac);
164 static void kill_line (Widget w, XEvent *event, String *av, Cardinal *ac);
165 static void done (Widget w, XEvent *event, String *av, Cardinal *ac);
166
167 static XtActionsRec actions[] = {{"keypress",  keypress},
168                                  {"backspace", backspace},
169                                  {"kill_line", kill_line},
170                                  {"done",      done}
171                                 };
172
173 # ifdef HAVE_MOTIF
174 #  if 0  /* oh fuck, why doesn't this work? */
175 static char translations[] = "\
176 <Key>BackSpace:         backspace()\n\
177 <Key>Delete:            backspace()\n\
178 Ctrl<Key>H:             backspace()\n\
179 Ctrl<Key>U:             kill_line()\n\
180 Ctrl<Key>X:             kill_line()\n\
181 Ctrl<Key>J:             done()\n\
182 Ctrl<Key>M:             done()\n\
183 <Key>:                  keypress()\n\
184 ";
185 #  else  /* !0 */
186 static char translations[] = "<Key>:keypress()";
187 #  endif /* !0 */
188 # endif /* HAVE_MOTIF */
189
190
191 static void
192 text_field_set_string (Widget widget, char *text, int position)
193 {
194 #ifdef HAVE_MOTIF
195   XmTextFieldSetString (widget, text);
196   XmTextFieldSetInsertionPosition (widget, position);
197
198 #else /* HAVE_ATHENA */
199   char *buf;
200   int end_pos;
201
202   XawTextBlock block;
203   block.firstPos = 0;
204   block.length = strlen (text);
205   block.ptr = text;
206   block.format = 0;
207   if (block.length == 0)
208     {
209       buf = XawDialogGetValueString(passwd_form);
210       if (buf)
211         end_pos = strlen(buf);
212       else
213         end_pos = -1;
214     }
215   XawTextReplace (widget, 0, end_pos, &block);
216   XawTextSetInsertionPoint (widget, position);
217 #endif /* HAVE_ATHENA */
218 }
219
220
221 static void
222 keypress (Widget w, XEvent *event, String *argv, Cardinal *argc)
223 {
224   int i, j;
225   char s [sizeof(typed_passwd)];
226   int size = XLookupString ((XKeyEvent *) event, s, sizeof(s)-1, 0, 0);
227   if (size != 1) return;
228
229   /* hack because I can't get translations to dance to my tune... */
230   if (*s == '\010') { backspace (w, event, argv, argc); return; }
231   if (*s == '\177') { backspace (w, event, argv, argc); return; }
232   if (*s == '\025') { kill_line (w, event, argv, argc); return; }
233   if (*s == '\030') { kill_line (w, event, argv, argc); return; }
234   if (*s == '\012') { done (w, event, argv, argc); return; }
235   if (*s == '\015') { done (w, event, argv, argc); return; }
236
237   i = j = strlen (typed_passwd);
238
239   if (i >= (sizeof(typed_passwd)-1))
240     {
241       XBell(XtDisplay(w), 0);
242       return;
243     }
244
245   typed_passwd [i] = *s;
246   s [++i] = 0;
247   while (i--)
248     s [i] = '*';
249
250   text_field_set_string (passwd_text, s, j + 1);
251 }
252
253 static void
254 backspace (Widget w, XEvent *event, String *argv, Cardinal *argc)
255 {
256   char s [sizeof(typed_passwd)];
257   int i = strlen (typed_passwd);
258   int j = i;
259   if (i == 0)
260     return;
261   typed_passwd [--i] = 0;
262   s [i] = 0;
263   while (i--)
264     s [i] = '*';
265
266   text_field_set_string (passwd_text, s, j + 1);
267 }
268
269 static void
270 kill_line (Widget w, XEvent *event, String *argv, Cardinal *argc)
271 {
272   memset (typed_passwd, 0, sizeof(typed_passwd));
273   text_field_set_string (passwd_text, "", 0);
274 }
275
276 static void
277 done (Widget w, XEvent *event, String *argv, Cardinal *argc)
278 {
279   passwd_done_cb (w, 0, 0);
280 }
281
282 #endif /* HAVE_ATHENA || !VERIFY_CALLBACK_WORKS */
283
284
285 extern void skull (Display *, Window, GC, GC, int, int, int, int);
286
287 static void
288 roger (Widget button, XtPointer client_data, XtPointer call_data)
289 {
290   Display *dpy = XtDisplay (button);
291   Screen *screen = XtScreen (button);
292   Window window = XtWindow (button);
293   Arg av [10];
294   int ac = 0;
295   XGCValues gcv;
296   Colormap cmap;
297   GC draw_gc, erase_gc;
298   unsigned int fg, bg;
299   int x, y, size;
300   XWindowAttributes xgwa;
301   XGetWindowAttributes (dpy, window, &xgwa);
302   cmap = xgwa.colormap;
303   if (xgwa.width > xgwa.height) size = xgwa.height;
304   else size = xgwa.width;
305   if (size > 40) size -= 30;
306   x = (xgwa.width - size) / 2;
307   y = (xgwa.height - size) / 2;
308   XtSetArg (av [ac], XtNforeground, &fg); ac++;
309   XtSetArg (av [ac], XtNbackground, &bg); ac++;
310   XtGetValues (button, av, ac);
311   /* if it's black on white, swap it cause it looks better (hack hack) */
312   if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
313     fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
314   gcv.foreground = bg;
315   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
316   gcv.foreground = fg;
317   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
318   XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
319   skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
320   XFreeGC (dpy, draw_gc);
321   XFreeGC (dpy, erase_gc);
322 }
323
324 static void
325 make_passwd_dialog (saver_info *si)
326 {
327   char *username = 0;
328   saver_screen_info *ssi = si->default_screen;
329   Widget parent = ssi->toplevel_shell;
330
331   if (ssi->demo_cmap &&
332       ssi->demo_cmap != ssi->cmap &&
333       ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
334     {
335       XFreeColormap (si->dpy, ssi->demo_cmap);
336       ssi->demo_cmap = 0;
337     }
338
339   if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
340     ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
341   else
342     ssi->demo_cmap = XCreateColormap (si->dpy,
343                                      RootWindowOfScreen (ssi->screen),
344                                      ssi->default_visual, AllocNone);
345
346   create_passwd_dialog (parent, ssi->default_visual, ssi->demo_cmap);
347
348 #ifdef HAVE_ATHENA
349
350   XtVaSetValues(passwd_form, XtNvalue, typed_passwd, 0);
351
352   XawDialogAddButton(passwd_form,"ok", passwd_done_cb, 0);
353   XawDialogAddButton(passwd_form,"cancel", passwd_cancel_cb, 0);
354   passwd_done = XtNameToWidget(passwd_form,"ok");
355   passwd_text = XtNameToWidget(passwd_form,"value");
356
357   XtAppAddActions(XtWidgetToApplicationContext(passwd_text), 
358                   actionsList, XtNumber(actionsList));
359   XtOverrideTranslations(passwd_text, XtParseTranslationTable(Translations));
360
361 #else  /* HAVE_MOTIF */
362
363   XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
364   XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
365   XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
366
367 # ifdef VERIFY_CALLBACK_WORKS
368   XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
369   XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
370 # else
371   XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
372   XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
373 # endif
374
375 # if defined(HAVE_MOTIF) && (XmVersion >= 1002)
376   /* The focus stuff changed around; this didn't exist in 1.1.5. */
377   XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0);
378 # endif
379
380   /* Another random thing necessary in 1.2.1 but not 1.1.5... */
381   XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
382
383 #endif /* HAVE_MOTIF */
384
385 #ifndef VMS
386   {
387     struct passwd *pw = getpwuid (getuid ());
388     username = pw->pw_name;
389   }
390 #else  /* VMS -- from "R.S.Niranjan" <U00C782%BRKVC1@navistar.com> who says
391                  that on OpenVMS 6.1, using `struct passwd' crashes... */
392   username = getenv("USER");
393 #endif /* VMS */
394
395   format_into_label (passwd_label1, si->version);
396   format_into_label (passwd_label3, (username ? username : "???"));
397 }
398
399 static int passwd_idle_timer_tick = -1;
400 static XtIntervalId passwd_idle_id;
401
402 static void
403 passwd_idle_timer (XtPointer closure, XtIntervalId *id)
404 {
405   saver_info *si = (saver_info *) closure;
406   saver_preferences *p = &si->prefs;
407
408   Display *dpy = XtDisplay (passwd_form);
409 #ifdef HAVE_ATHENA
410   Window window = XtWindow (passwd_form);
411 #else  /* MOTIF */
412   Window window = XtWindow (XtParent(passwd_done));
413 #endif /* MOTIF */
414   static Dimension x, y, d, s, ss;
415   static GC gc = 0;
416   int max = p->passwd_timeout / 1000;
417
418   idle_timer ((XtPointer) si, id);
419
420   if (passwd_idle_timer_tick == max)  /* first time */
421     {
422       XGCValues gcv;
423 #ifdef HAVE_MOTIF
424       unsigned long fg = 0, bg = 0, ts = 0, bs = 0;
425       Dimension w = 0, h = 0;
426       XtVaGetValues(XtParent(passwd_done),
427                     XmNwidth, &w,
428                     0);
429       XtVaGetValues(passwd_done,
430                     XmNheight, &h,
431                     XmNy, &y,
432                     XtNforeground, &fg,
433                     XtNbackground, &bg,
434                     XmNtopShadowColor, &ts,
435                     XmNbottomShadowColor, &bs,
436                     0);
437
438       if (ts != bg && ts != fg)
439         fg = ts;
440       if (bs != bg && bs != fg)
441         fg = bs;
442
443       d = h / 2;
444       if (d & 1) d++;
445
446       x = (w / 2);
447
448       x -= d/2;
449       y += d/2;
450
451 #else  /* HAVE_ATHENA */
452
453       Arg av [100];
454       int ac = 0;
455       unsigned long fg = 0, bg = 0;
456       XtSetArg (av [ac], XtNheight, &d); ac++;
457       XtGetValues (passwd_done, av, ac);
458       ac = 0;
459       XtSetArg (av [ac], XtNwidth, &x); ac++;
460       XtSetArg (av [ac], XtNheight, &y); ac++;
461       XtSetArg (av [ac], XtNforeground, &fg); ac++;
462       XtSetArg (av [ac], XtNbackground, &bg); ac++;
463       XtGetValues (passwd_form, av, ac);
464       x -= d;
465       y -= d;
466       d -= 4;
467
468 #endif /* HAVE_ATHENA */
469
470       gcv.foreground = fg;
471       if (gc) XFreeGC (dpy, gc);
472       gc = XCreateGC (dpy, window, GCForeground, &gcv);
473       s = 360*64 / (passwd_idle_timer_tick - 1);
474       ss = 90*64;
475       XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
476       XSetForeground (dpy, gc, bg);
477       x += 1;
478       y += 1;
479       d -= 2;
480     }
481
482   if (--passwd_idle_timer_tick)
483     {
484       passwd_idle_id = XtAppAddTimeOut (si->app, 1000, passwd_idle_timer,
485                                         (XtPointer) si);
486       XFillArc (dpy, window, gc, x, y, d, d, ss, s);
487       ss += s;
488     }
489 }
490
491
492 #ifdef HAVE_ATHENA
493
494 void
495 pop_up_athena_dialog_box (Widget parent, Widget focus, Widget dialog,
496                           Widget form, int where)
497 {
498   /* modified from demo.c */
499   /* I'm sure this is the wrong way to pop up a dialog box, but I can't
500      figure out how else to do it.
501
502      It's important that the screensaver dialogs not get decorated or
503      otherwise reparented by the window manager, because they need to be
504      children of the *real* root window, not the WM's virtual root, in
505      order for us to guarentee that they are visible above the screensaver
506      window itself.
507    */
508   Arg av [100];
509   int ac = 0;
510   Dimension sw, sh, x, y, w, h;
511
512   XtRealizeWidget(dialog);
513   sw = WidthOfScreen (XtScreen (dialog));
514   sh = HeightOfScreen (XtScreen (dialog));
515   ac = 0;
516   XtSetArg (av [ac], XtNwidth, &w); ac++;
517   XtSetArg (av [ac], XtNheight, &h); ac++;
518   XtGetValues (form, av, ac);
519   switch (where)
520     {
521     case 0:     /* center it in the top-right quadrant */
522       x = (sw/2 + w) / 2 + (sw/2) - w;
523       y = (sh/2 + h) / 2 - h;
524       break;
525     case 1:     /* center it in the bottom-right quadrant */
526       x = (sw/2 + w) / 2 + (sw/2) - w;
527       y = (sh/2 + h) / 2 + (sh/2) - h;
528       break;
529     case 2:     /* center it on the screen */
530       x = (sw + w) / 2 - w;
531       y = (sh + h) / 2 - h;
532       break;
533     default:
534       abort ();
535     }
536   if (x + w > sw) x = sw - w;
537   if (y + h > sh) y = sh - h;
538   ac = 0;
539   XtVaSetValues(dialog,
540                 XtNx, x,
541                 XtNy, y,
542                 NULL);
543   XtVaSetValues(form,
544                 XtNx, x,
545                 XtNy, y,
546                 NULL);
547   XtPopup(dialog,XtGrabNone);
548   steal_focus_and_colormap (focus);
549 }
550
551 static void
552 passwd_set_label (char *buf, int len)
553 {
554   Widget label;
555   if (!passwd_text)
556     return;
557   label=XtNameToWidget(XtParent(passwd_text),"*label");
558   XtVaSetValues(label,
559                 XtNlabel, buf,
560                 NULL);
561 }
562 #endif /* HAVE_ATHENA */
563
564 static Bool
565 pop_passwd_dialog (saver_info *si)
566 {
567   saver_preferences *p = &si->prefs;
568   saver_screen_info *ssi = si->default_screen;
569   Widget parent = ssi->toplevel_shell;
570   Display *dpy = XtDisplay (passwd_dialog);
571   Window focus;
572   int revert_to;
573   int i;
574
575   typed_passwd [0] = 0;
576   passwd_state = pw_read;
577   text_field_set_string (passwd_text, "", 0);
578
579   /* In case one of the hacks has unmapped it temporarily...
580      Get that sucker on stage now! */
581   for (i = 0; i < si->nscreens; i++)
582     XMapRaised(si->dpy, si->screens[i].screensaver_window);
583
584   XGetInputFocus (dpy, &focus, &revert_to);
585 #if defined(HAVE_MOTIF) && !defined(DESTROY_WORKS)
586   /* This fucker blows up if we destroy the widget.  I can't figure
587      out why.  The second destroy phase dereferences freed memory...
588      So we just keep it around; but unrealizing or unmanaging it
589      doesn't work right either, so we hack the window directly. FMH.
590    */
591   if (XtWindow (passwd_form))
592     XMapRaised (dpy, XtWindow (passwd_dialog));
593 #endif
594
595 #ifdef HAVE_ATHENA
596   pop_up_athena_dialog_box (parent, passwd_text, passwd_dialog,
597                             passwd_form, 2);
598 #else
599   pop_up_dialog_box (passwd_dialog, passwd_form,
600                      /* for debugging -- don't ask */
601                      (si->prefs.debug_p ? 69 : 0) +
602                      2);
603   XtManageChild (passwd_form);
604 #endif
605
606 #if defined(HAVE_MOTIF) && (XmVersion < 1002)
607   /* The focus stuff changed around; this causes problems in 1.2.1
608      but is necessary in 1.1.5. */
609   XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
610 #endif
611
612   passwd_idle_timer_tick = p->passwd_timeout / 1000;
613   passwd_idle_id = XtAppAddTimeOut (si->app, 1000,  passwd_idle_timer,
614                                     (XtPointer) si);
615
616 #ifdef HAVE_ATHENA
617   if (roger_label)
618     roger(roger_label, 0, 0);
619 #endif /* HAVE_ATHENA */
620
621   if (!si->prefs.debug_p)
622     XGrabServer (dpy);                          /* ############ DANGER! */
623
624   /* this call to ungrab used to be in main_loop() - see comment in
625       xscreensaver.c around line 857. */
626   ungrab_keyboard_and_mouse (si);
627
628   while (passwd_state == pw_read)
629     {
630       XEvent event;
631       XtAppNextEvent (si->app, &event);
632       /* wait for timer event */
633       if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
634         passwd_state = pw_time;
635       XtDispatchEvent (&event);
636     }
637   XUngrabServer (dpy);
638   XSync (dpy, False);                           /* ###### (danger over) */
639
640   if (passwd_state != pw_time)
641     XtRemoveTimeOut (passwd_idle_id);
642
643   if (passwd_state != pw_ok)
644     {
645       char *lose;
646       switch (passwd_state)
647         {
648         case pw_time: lose = "Timed out!"; break;
649         case pw_fail: lose = "Sorry!"; break;
650         case pw_cancel: lose = 0; break;
651         default: abort ();
652         }
653 #ifdef HAVE_MOTIF
654       XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
655 #endif
656       if (lose)
657         {
658 #ifdef HAVE_ATHENA
659           /* show the message */
660           passwd_set_label(lose,strlen(lose)+1);
661
662           /* and clear the password line */
663           memset(typed_passwd, 0, sizeof(typed_passwd));
664           text_field_set_string (passwd_text, "", 0);
665 #else
666           text_field_set_string (passwd_text, lose, strlen (lose) + 1);
667 #endif
668           passwd_idle_timer_tick = 1;
669           passwd_idle_id = XtAppAddTimeOut (si->app, 3000, passwd_idle_timer,
670                                 (XtPointer) si);
671           while (1)
672             {
673               XEvent event;
674               XtAppNextEvent (si->app, &event);
675               if (event.xany.type == 0 &&       /* wait for timer event */
676                   passwd_idle_timer_tick == 0)
677                 break;
678               XtDispatchEvent (&event);
679             }
680         }
681     }
682   memset (typed_passwd, 0, sizeof(typed_passwd));
683   text_field_set_string (passwd_text, "", 0);
684   XtSetKeyboardFocus (parent, None);
685
686 #ifdef DESTROY_WORKS
687   XtDestroyWidget (passwd_dialog);
688   passwd_dialog = 0;
689 #else
690   XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
691 #endif
692   {
693     XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
694     /* I don't understand why this doesn't refocus on the old selected
695        window when MWM is running in click-to-type mode.  The value of
696        `focus' seems to be correct. */
697     XSetInputFocus (dpy, focus, revert_to, CurrentTime);
698     XSync (dpy, False);
699     XSetErrorHandler (old_handler);
700   }
701
702   /* Since we installed our colormap to display the dialog properly, put
703      the old one back, so that the screensaver_window is now displayed
704      properly. */
705   for (i = 0; i < si->nscreens; i++)
706     {
707       saver_screen_info *ssi = &si->screens[i];
708       if (ssi->cmap)
709         XInstallColormap (si->dpy, ssi->cmap);
710     }
711
712   return (passwd_state == pw_ok ? True : False);
713 }
714
715 Bool
716 unlock_p (saver_info *si)
717 {
718   static Bool initted = False;
719   if (! initted)
720     {
721 #ifndef VERIFY_CALLBACK_WORKS
722       XtAppAddActions (si->app, actions, XtNumber (actions));
723 #endif
724       passwd_dialog = 0;
725       initted = True;
726     }
727   if (! passwd_dialog)
728     make_passwd_dialog (si);
729   return pop_passwd_dialog (si);
730 }
731
732 #endif /* !NO_LOCKING -- whole file */