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