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