1 /* xscreensaver, Copyright (c) 1993 Jamie Zawinski <jwz@lucid.com>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
25 #include <X11/Intrinsic.h>
35 #include "xscreensaver.h"
41 extern char *screensaver_version;
42 extern char *progname;
43 extern XtAppContext app;
44 extern Bool verbose_p;
46 extern Widget passwd_dialog;
47 extern Widget passwd_form;
48 extern Widget roger_label;
49 extern Widget passwd_label1;
50 extern Widget passwd_label3;
51 extern Widget passwd_text;
52 extern Widget passwd_done;
53 extern Widget passwd_cancel;
55 extern create_passwd_dialog ();
59 pw_read, pw_ok, pw_fail, pw_cancel, pw_time
61 static char typed_passwd[1024];
63 static char root_passwd[255];
64 static char user_passwd[255];
67 # define PWTYPE struct spwd *
68 # define PWSLOT sp_pwdp
69 # define GETPW getspnam
71 # define PWTYPE struct passwd *
72 # define PWSLOT pw_passwd
73 # define GETPW getpwnam
81 PWTYPE p = GETPW ("root");
83 if (p && p->PWSLOT && p->PWSLOT[0] != '*')
84 strcpy (root_passwd, p->PWSLOT);
87 fprintf (stderr, "%s: couldn't get root's password\n", progname);
88 strcpy (root_passwd, "*");
91 /* It has been reported that getlogin() returns the wrong user id on some
92 very old SGI systems... */
93 u = (char *) getlogin ();
98 /* getlogin() fails if not attached to a terminal;
99 in that case, use getpwuid(). */
100 p = getpwuid (getuid ());
104 if (p && p->PWSLOT && p->PWSLOT[0] != '*')
105 strcpy (user_passwd, p->PWSLOT);
108 fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
109 strcpy (user_passwd, "*");
116 # if (XmVersion >= 1002) /* The `destroy' bug apears to be fixed as */
117 # define DESTROY_WORKS /* of Motif 1.2.1, but the `verify-callback' */
118 # endif /* bug is still present. */
122 passwd_cancel_cb (button, client_data, call_data)
124 XtPointer client_data, call_data;
126 passwd_state = pw_cancel;
130 passwd_done_cb (button, client_data, call_data)
132 XtPointer client_data, call_data;
134 if (passwd_state != pw_read)
135 return; /* already done */
137 if (!strcmp ((char *) crypt (typed_passwd, user_passwd), user_passwd))
138 passwd_state = pw_ok;
139 /* do not allow root to have empty passwd */
140 else if (typed_passwd[0] &&
141 !strcmp ((char *) crypt (typed_passwd, root_passwd), root_passwd))
142 passwd_state = pw_ok;
144 passwd_state = pw_fail;
147 #ifdef VERIFY_CALLBACK_WORKS
149 /* #### It looks to me like adding any modifyVerify callback causes
150 #### Motif 1.1.4 to free the the TextF_Value() twice. I can't see
151 #### the bug in the Motif source, but Purify complains, even if
152 #### check_passwd_cb() is a no-op.
154 #### Update: Motif 1.2.1 also loses, but in a different way: it
155 #### writes beyond the end of a malloc'ed block in ModifyVerify().
156 #### Probably this block is the text field's text.
160 check_passwd_cb (button, client_data, call_data)
162 XtPointer client_data, call_data;
164 XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
166 if (passwd_state != pw_read)
168 else if (vcb->reason == XmCR_ACTIVATE)
170 passwd_done_cb (0, 0, 0);
172 else if (vcb->text->length > 1) /* don't allow "paste" operations */
176 else if (vcb->text->ptr != 0)
180 strncat (typed_passwd, vcb->text->ptr, vcb->text->length);
181 typed_passwd[vcb->endPos + vcb->text->length] = 0;
182 for (i = 0; i < vcb->text->length; i++)
183 vcb->text->ptr[i] = '*';
187 #else /* !VERIFY_CALLBACK_WORKS */
189 static void keypress ();
190 static void backspace ();
191 static void kill_line ();
194 static XtActionsRec actions[] =
196 {"keypress", keypress},
197 {"backspace", backspace},
198 {"kill_line", kill_line},
202 #if 0 /* oh fuck, why doesn't this work? */
203 static char translations[] = "\
204 <Key>BackSpace: backspace()\n\
205 <Key>Delete: backspace()\n\
206 Ctrl<Key>H: backspace()\n\
207 Ctrl<Key>U: kill_line()\n\
208 Ctrl<Key>X: kill_line()\n\
209 Ctrl<Key>J: done()\n\
210 Ctrl<Key>M: done()\n\
215 static char translations[] = "<Key>:keypress()";
220 keypress (w, event, argv, argc)
227 char s[sizeof (typed_passwd)];
228 int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
233 /* hack because I can't get translations to dance to my tune... */
236 backspace (w, event, argv, argc);
241 backspace (w, event, argv, argc);
246 kill_line (w, event, argv, argc);
251 kill_line (w, event, argv, argc);
256 done (w, event, argv, argc);
261 done (w, event, argv, argc);
265 i = j = strlen (typed_passwd);
266 typed_passwd[i] = *s;
270 XmTextFieldSetString (passwd_text, s);
271 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
275 backspace (w, event, argv, argc)
281 char s[sizeof (typed_passwd)];
282 int i = strlen (typed_passwd);
287 typed_passwd[--i] = 0;
291 XmTextFieldSetString (passwd_text, s);
292 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
296 kill_line (w, event, argv, argc)
302 memset (typed_passwd, 0, sizeof (typed_passwd));
303 XmTextFieldSetString (passwd_text, "");
307 done (w, event, argv, argc)
313 passwd_done_cb (w, 0, 0);
316 #endif /* !VERIFY_CALLBACK_WORKS */
319 format_into_label (widget, string)
325 XmString xm_label = 0;
326 XmString new_xm_label;
330 XtSetArg (av[ac], XmNlabelString, &xm_label);
332 XtGetValues (widget, av, ac);
333 XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
334 if (!strcmp (label, XtName (widget)))
335 strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
337 sprintf (buf, label, string);
338 new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
340 XtSetArg (av[ac], XmNlabelString, new_xm_label);
342 XtSetValues (widget, av, ac);
343 XmStringFree (new_xm_label);
348 extern void skull (Display *, Window, GC, GC, int, int, int, int);
353 roger (button, client_data, call_data)
355 XtPointer client_data, call_data;
357 Display *dpy = XtDisplay (button);
358 Screen *screen = XtScreen (button);
359 Window window = XtWindow (button);
364 GC draw_gc, erase_gc;
367 XWindowAttributes xgwa;
369 XGetWindowAttributes (dpy, window, &xgwa);
370 cmap = xgwa.colormap;
371 if (xgwa.width > xgwa.height)
377 x = (xgwa.width - size) / 2;
378 y = (xgwa.height - size) / 2;
379 XtSetArg (av[ac], XmNforeground, &fg);
381 XtSetArg (av[ac], XmNbackground, &bg);
383 XtGetValues (button, av, ac);
384 /* if it's black on white, swap it cause it looks better (hack hack) */
385 if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
386 fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
388 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
390 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
391 XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
392 skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
393 XFreeGC (dpy, draw_gc);
394 XFreeGC (dpy, erase_gc);
398 make_passwd_dialog (parent)
403 create_passwd_dialog (parent);
405 XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
406 XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
407 XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
409 #ifdef VERIFY_CALLBACK_WORKS
410 XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
411 XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
413 XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
414 XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
417 pw = getpwuid (getuid ());
418 format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
419 format_into_label (passwd_label1, screensaver_version);
422 extern void idle_timer ();
424 static int passwd_idle_timer_tick;
425 static XtIntervalId id;
428 passwd_idle_timer (junk1, junk2)
432 Display *dpy = XtDisplay (passwd_form);
433 Window window = XtWindow (passwd_form);
434 static Dimension x, y, d, s, ss;
436 int max = passwd_timeout / 1000;
438 idle_timer (junk1, junk2);
440 if (passwd_idle_timer_tick == max) /* first time */
445 unsigned long fg, bg;
447 XtSetArg (av[ac], XmNheight, &d);
449 XtGetValues (passwd_done, av, ac);
451 XtSetArg (av[ac], XmNwidth, &x);
453 XtSetArg (av[ac], XmNheight, &y);
455 XtSetArg (av[ac], XmNforeground, &fg);
457 XtSetArg (av[ac], XmNbackground, &bg);
459 XtGetValues (passwd_form, av, ac);
466 gc = XCreateGC (dpy, window, GCForeground, &gcv);
467 s = 360 * 64 / (passwd_idle_timer_tick - 1);
469 XFillArc (dpy, window, gc, x, y, d, d, 0, 360 * 64);
470 XSetForeground (dpy, gc, bg);
476 if (--passwd_idle_timer_tick)
478 id = XtAppAddTimeOut (app, 1000, (XtTimerCallbackProc) passwd_idle_timer, 0);
479 XFillArc (dpy, window, gc, x, y, d, d, ss, s);
484 extern void pop_up_dialog_box ();
485 extern int BadWindow_ehandler ();
488 pop_passwd_dialog (parent)
491 Display *dpy = XtDisplay (passwd_dialog);
496 passwd_state = pw_read;
497 XmTextFieldSetString (passwd_text, "");
499 XGetInputFocus (dpy, &focus, &revert_to);
500 #ifndef DESTROY_WORKS
501 /* This fucker blows up if we destroy the widget. I can't figure
502 out why. The second destroy phase dereferences freed memory...
503 So we just keep it around; but unrealizing or unmanaging it
504 doesn't work right either, so we hack the window directly. FMH.
506 if (XtWindow (passwd_form))
507 XMapRaised (dpy, XtWindow (passwd_dialog));
509 pop_up_dialog_box (passwd_dialog, passwd_form, 2);
511 XtManageChild (passwd_form);
512 XSetInputFocus (dpy, XtWindow (passwd_dialog), revert_to, CurrentTime);
514 /* #### This doesn't work in 1.2.1... Check return code... */
515 XmProcessTraversal (passwd_text, 0);
517 passwd_idle_timer_tick = passwd_timeout / 1000;
518 id = XtAppAddTimeOut (app, 1000, (XtTimerCallbackProc) passwd_idle_timer, 0);
520 XGrabServer (dpy); /* ############ DANGER! */
522 while (passwd_state == pw_read)
526 XtAppNextEvent (app, &event);
527 /* wait for timer event */
528 if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
529 passwd_state = pw_time;
530 XtDispatchEvent (&event);
533 XSync (dpy, False); /* ###### (danger over) */
535 if (passwd_state != pw_time)
536 XtRemoveTimeOut (id);
538 if (passwd_state != pw_ok)
542 switch (passwd_state)
556 XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
559 XmTextFieldSetString (passwd_text, lose);
560 XmTextFieldSetInsertionPosition (passwd_text, strlen (lose) + 1);
561 passwd_idle_timer_tick = 1;
562 id = XtAppAddTimeOut (app, 3000, (XtTimerCallbackProc) passwd_idle_timer, 0);
567 XtAppNextEvent (app, &event);
568 if (event.xany.type == 0 && /* wait for timer event */
569 passwd_idle_timer_tick == 0)
571 XtDispatchEvent (&event);
575 memset (typed_passwd, 0, sizeof (typed_passwd));
576 XmTextFieldSetString (passwd_text, "");
577 XtSetKeyboardFocus (parent, None);
580 XtDestroyWidget (passwd_dialog);
583 XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
586 int (*old_handler) ();
588 old_handler = XSetErrorHandler (BadWindow_ehandler);
589 /* I don't understand why this doesn't refocus on the old selected
590 window when MWM is running in click-to-type mode. The value of
591 `focus' seems to be correct. */
592 XSetInputFocus (dpy, focus, revert_to, CurrentTime);
594 XSetErrorHandler (old_handler);
597 return (passwd_state == pw_ok ? True : False);
604 static Bool initted = False;
608 #ifndef VERIFY_CALLBACK_WORKS
609 XtAppAddActions (app, actions, XtNumber (actions));
615 make_passwd_dialog (parent);
616 return pop_passwd_dialog (parent);
619 #endif /* !NO_LOCKING */