1 /* xscreensaver, Copyright (c) 1993 Jamie Zawinski <jwz@mcom.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
24 #include <X11/Intrinsic.h>
30 #include "xscreensaver.h"
38 extern char *getenv();
47 extern char *screensaver_version;
48 extern char *progname;
49 extern XtAppContext app;
50 extern Bool verbose_p;
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_text;
58 extern Widget passwd_done;
59 extern Widget passwd_cancel;
61 extern create_passwd_dialog ();
63 static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
64 static char typed_passwd [1024];
66 static char root_passwd [255];
67 static char user_passwd [255];
68 static char * user_vms;
71 # define PWTYPE struct spwd *
72 # define PWSLOT sp_pwdp
73 # define GETPW getspnam
75 # define PWTYPE struct passwd *
76 # define PWSLOT pw_passwd
77 # define GETPW getpwnam
86 PWTYPE p = GETPW ("root");
87 if (p && p->PWSLOT && p->PWSLOT[0] != '*')
88 strcpy (root_passwd, p->PWSLOT);
91 fprintf (stderr, "%s: couldn't get root's password\n", progname);
92 strcpy (root_passwd, "*");
95 /* It has been reported that getlogin() returns the wrong user id on some
96 very old SGI systems... */
102 /* getlogin() fails if not attached to a terminal;
103 in that case, use getpwuid(). */
104 struct passwd *p2 = getpwuid (getuid ());
114 if (p && p->PWSLOT &&
115 /* p->PWSLOT[0] != '*' */ /* sensible */
116 (strlen (p->PWSLOT) > 4) /* solaris */
118 strcpy (user_passwd, p->PWSLOT);
121 fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
122 strcpy (user_passwd, "*");
134 #if (XmVersion >= 1002) /* The `destroy' bug apears to be fixed as */
135 # define DESTROY_WORKS /* of Motif 1.2.1, but the `verify-callback' */
136 #endif /* bug is still present. */
139 passwd_cancel_cb (button, client_data, call_data)
141 XtPointer client_data, call_data;
143 passwd_state = pw_cancel;
147 passwd_done_cb (button, client_data, call_data)
149 XtPointer client_data, call_data;
152 if (passwd_state != pw_read) return; /* already done */
154 if (!strcmp ((char *) crypt (typed_passwd, user_passwd), user_passwd))
155 passwd_state = pw_ok;
156 /* do not allow root to have empty passwd */
157 else if (typed_passwd [0] &&
158 !strcmp ((char *) crypt (typed_passwd, root_passwd), root_passwd))
159 passwd_state = pw_ok;
161 passwd_state = pw_fail;
163 user_vms = getenv("USER");
164 if (validate_user(user_vms,typed_passwd) == 1 )
165 passwd_state = pw_ok;
167 passwd_state = pw_fail;
171 #ifdef VERIFY_CALLBACK_WORKS
173 /* #### It looks to me like adding any modifyVerify callback causes
174 #### Motif 1.1.4 to free the the TextF_Value() twice. I can't see
175 #### the bug in the Motif source, but Purify complains, even if
176 #### check_passwd_cb() is a no-op.
178 #### Update: Motif 1.2.1 also loses, but in a different way: it
179 #### writes beyond the end of a malloc'ed block in ModifyVerify().
180 #### Probably this block is the text field's text.
184 check_passwd_cb (button, client_data, call_data)
186 XtPointer client_data, call_data;
188 XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
190 if (passwd_state != pw_read)
192 else if (vcb->reason == XmCR_ACTIVATE)
194 passwd_done_cb (0, 0, 0);
196 else if (vcb->text->length > 1) /* don't allow "paste" operations */
200 else if (vcb->text->ptr != 0)
203 strncat (typed_passwd, vcb->text->ptr, vcb->text->length);
204 typed_passwd [vcb->endPos + vcb->text->length] = 0;
205 for (i = 0; i < vcb->text->length; i++)
206 vcb->text->ptr [i] = '*';
210 #else /* !VERIFY_CALLBACK_WORKS */
212 static void keypress();
213 static void backspace();
214 static void kill_line();
217 static XtActionsRec actions[] = {{"keypress", keypress},
218 {"backspace", backspace},
219 {"kill_line", kill_line},
223 #if 0 /* oh fuck, why doesn't this work? */
224 static char translations[] = "\
225 <Key>BackSpace: backspace()\n\
226 <Key>Delete: backspace()\n\
227 Ctrl<Key>H: backspace()\n\
228 Ctrl<Key>U: kill_line()\n\
229 Ctrl<Key>X: kill_line()\n\
230 Ctrl<Key>J: done()\n\
231 Ctrl<Key>M: done()\n\
235 static char translations[] = "<Key>:keypress()";
239 keypress (w, event, argv, argc)
246 char s [sizeof (typed_passwd)];
247 int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
248 if (size != 1) return;
250 /* hack because I can't get translations to dance to my tune... */
251 if (*s == '\010') { backspace (w, event, argv, argc); return; }
252 if (*s == '\177') { backspace (w, event, argv, argc); return; }
253 if (*s == '\025') { kill_line (w, event, argv, argc); return; }
254 if (*s == '\030') { kill_line (w, event, argv, argc); return; }
255 if (*s == '\012') { done (w, event, argv, argc); return; }
256 if (*s == '\015') { done (w, event, argv, argc); return; }
258 i = j = strlen (typed_passwd);
259 typed_passwd [i] = *s;
263 XmTextFieldSetString (passwd_text, s);
264 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
268 backspace (w, event, argv, argc)
274 char s [sizeof (typed_passwd)];
275 int i = strlen (typed_passwd);
279 typed_passwd [--i] = 0;
283 XmTextFieldSetString (passwd_text, s);
284 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
288 kill_line (w, event, argv, argc)
294 memset (typed_passwd, 0, sizeof (typed_passwd));
295 XmTextFieldSetString (passwd_text, "");
299 done (w, event, argv, argc)
305 passwd_done_cb (w, 0, 0);
308 #endif /* !VERIFY_CALLBACK_WORKS */
311 format_into_label (widget, string)
317 XmString xm_label = 0;
318 XmString new_xm_label;
321 XtSetArg (av [ac], XmNlabelString, &xm_label); ac++;
322 XtGetValues (widget, av, ac);
323 XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
324 if (!strcmp (label, XtName (widget)))
325 strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
327 sprintf (buf, label, string);
328 new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
330 XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
331 XtSetValues (widget, av, ac);
332 XmStringFree (new_xm_label);
337 extern void skull (Display *, Window, GC, GC, int, int, int, int);
341 roger (button, client_data, call_data)
343 XtPointer client_data, call_data;
345 Display *dpy = XtDisplay (button);
346 Screen *screen = XtScreen (button);
347 Window window = XtWindow (button);
352 GC draw_gc, erase_gc;
355 XWindowAttributes xgwa;
356 XGetWindowAttributes (dpy, window, &xgwa);
357 cmap = xgwa.colormap;
358 if (xgwa.width > xgwa.height) size = xgwa.height;
359 else size = xgwa.width;
360 if (size > 40) size -= 30;
361 x = (xgwa.width - size) / 2;
362 y = (xgwa.height - size) / 2;
363 XtSetArg (av [ac], XmNforeground, &fg); ac++;
364 XtSetArg (av [ac], XmNbackground, &bg); ac++;
365 XtGetValues (button, av, ac);
366 /* if it's black on white, swap it cause it looks better (hack hack) */
367 if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
368 fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
370 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
372 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
373 XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
374 skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
375 XFreeGC (dpy, draw_gc);
376 XFreeGC (dpy, erase_gc);
380 make_passwd_dialog (parent)
384 create_passwd_dialog (parent);
386 XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
387 XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
388 XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
390 #ifdef VERIFY_CALLBACK_WORKS
391 XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
392 XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
394 XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
395 XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
398 #if (XmVersion >= 1002)
399 /* The focus stuff changed around; this didn't exist in 1.1.5. */
400 XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0);
403 /* Another random thing necessary in 1.2.1 but not 1.1.5... */
404 XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
407 pw = getpwuid (getuid ());
409 pw->pw_name = getenv("USER");
411 format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
412 format_into_label (passwd_label1, screensaver_version);
416 extern void idle_timer ();
418 static int passwd_idle_timer_tick;
419 static XtIntervalId id;
422 passwd_idle_timer (junk1, junk2)
426 Display *dpy = XtDisplay (passwd_form);
427 Window window = XtWindow (passwd_form);
428 static Dimension x, y, d, s, ss;
430 int max = passwd_timeout / 1000;
432 idle_timer (junk1, junk2);
434 if (passwd_idle_timer_tick == max) /* first time */
439 unsigned long fg, bg;
440 XtSetArg (av [ac], XmNheight, &d); ac++;
441 XtGetValues (passwd_done, av, ac);
443 XtSetArg (av [ac], XmNwidth, &x); ac++;
444 XtSetArg (av [ac], XmNheight, &y); ac++;
445 XtSetArg (av [ac], XmNforeground, &fg); ac++;
446 XtSetArg (av [ac], XmNbackground, &bg); ac++;
447 XtGetValues (passwd_form, av, ac);
452 if (gc) XFreeGC (dpy, gc);
453 gc = XCreateGC (dpy, window, GCForeground, &gcv);
454 s = 360*64 / (passwd_idle_timer_tick - 1);
456 XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
457 XSetForeground (dpy, gc, bg);
463 if (--passwd_idle_timer_tick)
465 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
466 XFillArc (dpy, window, gc, x, y, d, d, ss, s);
471 extern void pop_up_dialog_box ();
472 extern int BadWindow_ehandler ();
475 pop_passwd_dialog (parent)
478 Display *dpy = XtDisplay (passwd_dialog);
481 typed_passwd [0] = 0;
482 passwd_state = pw_read;
483 XmTextFieldSetString (passwd_text, "");
485 XGetInputFocus (dpy, &focus, &revert_to);
486 #ifndef DESTROY_WORKS
487 /* This fucker blows up if we destroy the widget. I can't figure
488 out why. The second destroy phase dereferences freed memory...
489 So we just keep it around; but unrealizing or unmanaging it
490 doesn't work right either, so we hack the window directly. FMH.
492 if (XtWindow (passwd_form))
493 XMapRaised (dpy, XtWindow (passwd_dialog));
496 pop_up_dialog_box (passwd_dialog, passwd_form, 2);
497 XtManageChild (passwd_form);
499 #if (XmVersion < 1002)
500 /* The focus stuff changed around; this causes problems in 1.2.1
501 but is necessary in 1.1.5. */
502 XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
505 passwd_idle_timer_tick = passwd_timeout / 1000;
506 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
509 XGrabServer (dpy); /* ############ DANGER! */
511 /* this call to ungrab used to be in main_loop() - see comment in
512 xscreensaver.c around line 696. */
513 ungrab_keyboard_and_mouse ();
515 while (passwd_state == pw_read)
518 XtAppNextEvent (app, &event);
519 /* wait for timer event */
520 if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
521 passwd_state = pw_time;
522 XtDispatchEvent (&event);
525 XSync (dpy, False); /* ###### (danger over) */
527 if (passwd_state != pw_time)
528 XtRemoveTimeOut (id);
530 if (passwd_state != pw_ok)
533 switch (passwd_state)
535 case pw_time: lose = "Timed out!"; break;
536 case pw_fail: lose = "Sorry!"; break;
537 case pw_cancel: lose = 0; break;
540 XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
543 XmTextFieldSetString (passwd_text, lose);
544 XmTextFieldSetInsertionPosition (passwd_text, strlen (lose) + 1);
545 passwd_idle_timer_tick = 1;
546 id = XtAppAddTimeOut (app, 3000, passwd_idle_timer, 0);
550 XtAppNextEvent (app, &event);
551 if (event.xany.type == 0 && /* wait for timer event */
552 passwd_idle_timer_tick == 0)
554 XtDispatchEvent (&event);
558 memset (typed_passwd, 0, sizeof (typed_passwd));
559 XmTextFieldSetString (passwd_text, "");
560 XtSetKeyboardFocus (parent, None);
563 XtDestroyWidget (passwd_dialog);
566 XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
569 int (*old_handler) ();
570 old_handler = XSetErrorHandler (BadWindow_ehandler);
571 /* I don't understand why this doesn't refocus on the old selected
572 window when MWM is running in click-to-type mode. The value of
573 `focus' seems to be correct. */
574 XSetInputFocus (dpy, focus, revert_to, CurrentTime);
576 XSetErrorHandler (old_handler);
579 return (passwd_state == pw_ok ? True : False);
586 static Bool initted = False;
589 #ifndef VERIFY_CALLBACK_WORKS
590 XtAppAddActions (app, actions, XtNumber (actions));
596 make_passwd_dialog (parent);
597 return pop_passwd_dialog (parent);
600 #endif /* !NO_LOCKING */