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 ();
57 static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
58 static char typed_passwd [1024];
60 static char root_passwd [255];
61 static char user_passwd [255];
64 # define PWTYPE struct spwd *
65 # define PWSLOT sp_pwdp
66 # define GETPW getspnam
68 # define PWTYPE struct passwd *
69 # define PWSLOT pw_passwd
70 # define GETPW getpwnam
78 PWTYPE p = GETPW ("root");
79 if (p && p->PWSLOT && p->PWSLOT[0] != '*')
80 strcpy (root_passwd, p->PWSLOT);
83 fprintf (stderr, "%s: couldn't get root's password\n", progname);
84 strcpy (root_passwd, "*");
87 /* It has been reported that getlogin() returns the wrong user id on some
88 very old SGI systems... */
94 /* getlogin() fails if not attached to a terminal;
95 in that case, use getpwuid(). */
96 struct passwd *p2 = getpwuid (getuid ());
105 if (p && p->PWSLOT &&
106 /* p->PWSLOT[0] != '*' */ /* sensible */
107 (strlen (p->PWSLOT) > 4) /* solaris */
109 strcpy (user_passwd, p->PWSLOT);
112 fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
113 strcpy (user_passwd, "*");
121 #if (XmVersion >= 1002) /* The `destroy' bug apears to be fixed as */
122 # define DESTROY_WORKS /* of Motif 1.2.1, but the `verify-callback' */
123 #endif /* bug is still present. */
126 passwd_cancel_cb (button, client_data, call_data)
128 XtPointer client_data, call_data;
130 passwd_state = pw_cancel;
134 passwd_done_cb (button, client_data, call_data)
136 XtPointer client_data, call_data;
138 if (passwd_state != pw_read) return; /* already done */
139 if (!strcmp ((char *) crypt (typed_passwd, user_passwd), user_passwd))
140 passwd_state = pw_ok;
141 /* do not allow root to have empty passwd */
142 else if (typed_passwd [0] &&
143 !strcmp ((char *) crypt (typed_passwd, root_passwd), root_passwd))
144 passwd_state = pw_ok;
146 passwd_state = pw_fail;
149 #ifdef VERIFY_CALLBACK_WORKS
151 /* #### It looks to me like adding any modifyVerify callback causes
152 #### Motif 1.1.4 to free the the TextF_Value() twice. I can't see
153 #### the bug in the Motif source, but Purify complains, even if
154 #### check_passwd_cb() is a no-op.
156 #### Update: Motif 1.2.1 also loses, but in a different way: it
157 #### writes beyond the end of a malloc'ed block in ModifyVerify().
158 #### Probably this block is the text field's text.
162 check_passwd_cb (button, client_data, call_data)
164 XtPointer client_data, call_data;
166 XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
168 if (passwd_state != pw_read)
170 else if (vcb->reason == XmCR_ACTIVATE)
172 passwd_done_cb (0, 0, 0);
174 else if (vcb->text->length > 1) /* don't allow "paste" operations */
178 else if (vcb->text->ptr != 0)
181 strncat (typed_passwd, vcb->text->ptr, vcb->text->length);
182 typed_passwd [vcb->endPos + vcb->text->length] = 0;
183 for (i = 0; i < vcb->text->length; i++)
184 vcb->text->ptr [i] = '*';
188 #else /* !VERIFY_CALLBACK_WORKS */
190 static void keypress();
191 static void backspace();
192 static void kill_line();
195 static XtActionsRec actions[] = {{"keypress", keypress},
196 {"backspace", backspace},
197 {"kill_line", kill_line},
201 #if 0 /* oh fuck, why doesn't this work? */
202 static char translations[] = "\
203 <Key>BackSpace: backspace()\n\
204 <Key>Delete: backspace()\n\
205 Ctrl<Key>H: backspace()\n\
206 Ctrl<Key>U: kill_line()\n\
207 Ctrl<Key>X: kill_line()\n\
208 Ctrl<Key>J: done()\n\
209 Ctrl<Key>M: done()\n\
213 static char translations[] = "<Key>:keypress()";
217 keypress (w, event, argv, argc)
224 char s [sizeof (typed_passwd)];
225 int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
226 if (size != 1) return;
228 /* hack because I can't get translations to dance to my tune... */
229 if (*s == '\010') { backspace (w, event, argv, argc); return; }
230 if (*s == '\177') { backspace (w, event, argv, argc); return; }
231 if (*s == '\025') { kill_line (w, event, argv, argc); return; }
232 if (*s == '\030') { kill_line (w, event, argv, argc); return; }
233 if (*s == '\012') { done (w, event, argv, argc); return; }
234 if (*s == '\015') { done (w, event, argv, argc); return; }
236 i = j = strlen (typed_passwd);
237 typed_passwd [i] = *s;
241 XmTextFieldSetString (passwd_text, s);
242 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
246 backspace (w, event, argv, argc)
252 char s [sizeof (typed_passwd)];
253 int i = strlen (typed_passwd);
257 typed_passwd [--i] = 0;
261 XmTextFieldSetString (passwd_text, s);
262 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
266 kill_line (w, event, argv, argc)
272 memset (typed_passwd, 0, sizeof (typed_passwd));
273 XmTextFieldSetString (passwd_text, "");
277 done (w, event, argv, argc)
283 passwd_done_cb (w, 0, 0);
286 #endif /* !VERIFY_CALLBACK_WORKS */
289 format_into_label (widget, string)
295 XmString xm_label = 0;
296 XmString new_xm_label;
299 XtSetArg (av [ac], XmNlabelString, &xm_label); ac++;
300 XtGetValues (widget, av, ac);
301 XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
302 if (!strcmp (label, XtName (widget)))
303 strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
305 sprintf (buf, label, string);
306 new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
308 XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
309 XtSetValues (widget, av, ac);
310 XmStringFree (new_xm_label);
315 extern void skull (Display *, Window, GC, GC, int, int, int, int);
319 roger (button, client_data, call_data)
321 XtPointer client_data, call_data;
323 Display *dpy = XtDisplay (button);
324 Screen *screen = XtScreen (button);
325 Window window = XtWindow (button);
330 GC draw_gc, erase_gc;
333 XWindowAttributes xgwa;
334 XGetWindowAttributes (dpy, window, &xgwa);
335 cmap = xgwa.colormap;
336 if (xgwa.width > xgwa.height) size = xgwa.height;
337 else size = xgwa.width;
338 if (size > 40) size -= 30;
339 x = (xgwa.width - size) / 2;
340 y = (xgwa.height - size) / 2;
341 XtSetArg (av [ac], XmNforeground, &fg); ac++;
342 XtSetArg (av [ac], XmNbackground, &bg); ac++;
343 XtGetValues (button, av, ac);
344 /* if it's black on white, swap it cause it looks better (hack hack) */
345 if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
346 fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
348 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
350 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
351 XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
352 skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
353 XFreeGC (dpy, draw_gc);
354 XFreeGC (dpy, erase_gc);
358 make_passwd_dialog (parent)
362 create_passwd_dialog (parent);
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);
368 #ifdef VERIFY_CALLBACK_WORKS
369 XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
370 XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
372 XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
373 XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
376 #if (XmVersion >= 1002)
377 /* The focus stuff changed around; this didn't exist in 1.1.5. */
378 XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0);
381 /* Another random thing necessary in 1.2.1 but not 1.1.5... */
382 XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
384 pw = getpwuid (getuid ());
385 format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
386 format_into_label (passwd_label1, screensaver_version);
390 extern void idle_timer ();
392 static int passwd_idle_timer_tick;
393 static XtIntervalId id;
396 passwd_idle_timer (junk1, junk2)
400 Display *dpy = XtDisplay (passwd_form);
401 Window window = XtWindow (passwd_form);
402 static Dimension x, y, d, s, ss;
404 int max = passwd_timeout / 1000;
406 idle_timer (junk1, junk2);
408 if (passwd_idle_timer_tick == max) /* first time */
413 unsigned long fg, bg;
414 XtSetArg (av [ac], XmNheight, &d); ac++;
415 XtGetValues (passwd_done, av, ac);
417 XtSetArg (av [ac], XmNwidth, &x); ac++;
418 XtSetArg (av [ac], XmNheight, &y); ac++;
419 XtSetArg (av [ac], XmNforeground, &fg); ac++;
420 XtSetArg (av [ac], XmNbackground, &bg); ac++;
421 XtGetValues (passwd_form, av, ac);
426 if (gc) XFreeGC (dpy, gc);
427 gc = XCreateGC (dpy, window, GCForeground, &gcv);
428 s = 360*64 / (passwd_idle_timer_tick - 1);
430 XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
431 XSetForeground (dpy, gc, bg);
437 if (--passwd_idle_timer_tick)
439 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
440 XFillArc (dpy, window, gc, x, y, d, d, ss, s);
445 extern void pop_up_dialog_box ();
446 extern int BadWindow_ehandler ();
449 pop_passwd_dialog (parent)
452 Display *dpy = XtDisplay (passwd_dialog);
455 typed_passwd [0] = 0;
456 passwd_state = pw_read;
457 XmTextFieldSetString (passwd_text, "");
459 XGetInputFocus (dpy, &focus, &revert_to);
460 #ifndef DESTROY_WORKS
461 /* This fucker blows up if we destroy the widget. I can't figure
462 out why. The second destroy phase dereferences freed memory...
463 So we just keep it around; but unrealizing or unmanaging it
464 doesn't work right either, so we hack the window directly. FMH.
466 if (XtWindow (passwd_form))
467 XMapRaised (dpy, XtWindow (passwd_dialog));
470 pop_up_dialog_box (passwd_dialog, passwd_form, 2);
471 XtManageChild (passwd_form);
473 #if (XmVersion < 1002)
474 /* The focus stuff changed around; this causes problems in 1.2.1
475 but is necessary in 1.1.5. */
476 XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
479 passwd_idle_timer_tick = passwd_timeout / 1000;
480 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
483 XGrabServer (dpy); /* ############ DANGER! */
485 while (passwd_state == pw_read)
488 XtAppNextEvent (app, &event);
489 /* wait for timer event */
490 if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
491 passwd_state = pw_time;
492 XtDispatchEvent (&event);
495 XSync (dpy, False); /* ###### (danger over) */
497 if (passwd_state != pw_time)
498 XtRemoveTimeOut (id);
500 if (passwd_state != pw_ok)
503 switch (passwd_state)
505 case pw_time: lose = "Timed out!"; break;
506 case pw_fail: lose = "Sorry!"; break;
507 case pw_cancel: lose = 0; break;
510 XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
513 XmTextFieldSetString (passwd_text, lose);
514 XmTextFieldSetInsertionPosition (passwd_text, strlen (lose) + 1);
515 passwd_idle_timer_tick = 1;
516 id = XtAppAddTimeOut (app, 3000, passwd_idle_timer, 0);
520 XtAppNextEvent (app, &event);
521 if (event.xany.type == 0 && /* wait for timer event */
522 passwd_idle_timer_tick == 0)
524 XtDispatchEvent (&event);
528 memset (typed_passwd, 0, sizeof (typed_passwd));
529 XmTextFieldSetString (passwd_text, "");
530 XtSetKeyboardFocus (parent, None);
533 XtDestroyWidget (passwd_dialog);
536 XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
539 int (*old_handler) ();
540 old_handler = XSetErrorHandler (BadWindow_ehandler);
541 /* I don't understand why this doesn't refocus on the old selected
542 window when MWM is running in click-to-type mode. The value of
543 `focus' seems to be correct. */
544 XSetInputFocus (dpy, focus, revert_to, CurrentTime);
546 XSetErrorHandler (old_handler);
549 return (passwd_state == pw_ok ? True : False);
556 static Bool initted = False;
559 #ifndef VERIFY_CALLBACK_WORKS
560 XtAppAddActions (app, actions, XtNumber (actions));
566 make_passwd_dialog (parent);
567 return pop_passwd_dialog (parent);
570 #endif /* !NO_LOCKING */