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