1 /* xscreensaver, Copyright (c) 1993-1995 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
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 P((Widget));
56 extern void ungrab_keyboard_and_mouse P((void));
58 static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
59 static char typed_passwd [1024];
61 static char root_passwd [255];
62 static char user_passwd [255];
65 # define PWTYPE struct spwd *
66 # define PWSLOT sp_pwdp
67 # define GETPW getspnam
69 # define PWTYPE struct passwd *
70 # define PWSLOT pw_passwd
71 # define GETPW getpwnam
79 PWTYPE p = GETPW ("root");
80 if (p && p->PWSLOT && p->PWSLOT[0] != '*')
81 strcpy (root_passwd, p->PWSLOT);
84 fprintf (stderr, "%s: couldn't get root's password\n", progname);
85 strcpy (root_passwd, "*");
88 /* It has been reported that getlogin() returns the wrong user id on some
89 very old SGI systems... */
95 /* getlogin() fails if not attached to a terminal;
96 in that case, use getpwuid(). */
97 struct passwd *p2 = getpwuid (getuid ());
106 if (p && p->PWSLOT &&
107 /* p->PWSLOT[0] != '*' */ /* sensible */
108 (strlen (p->PWSLOT) > 4) /* solaris */
110 strcpy (user_passwd, p->PWSLOT);
113 fprintf (stderr, "%s: couldn't get password of \"%s\"\n", progname, u);
114 strcpy (user_passwd, "*");
122 #if (XmVersion >= 1002) /* The `destroy' bug apears to be fixed as */
123 # define DESTROY_WORKS /* of Motif 1.2.1, but the `verify-callback' */
124 #endif /* bug is still present. */
127 passwd_cancel_cb (button, client_data, call_data)
129 XtPointer client_data, call_data;
131 passwd_state = pw_cancel;
135 passwd_done_cb (button, client_data, call_data)
137 XtPointer client_data, call_data;
139 if (passwd_state != pw_read) return; /* already done */
140 if (!strcmp ((char *) crypt (typed_passwd, user_passwd), user_passwd))
141 passwd_state = pw_ok;
142 /* do not allow root to have empty passwd */
143 else if (typed_passwd [0] &&
144 !strcmp ((char *) crypt (typed_passwd, root_passwd), root_passwd))
145 passwd_state = pw_ok;
147 passwd_state = pw_fail;
150 #ifdef VERIFY_CALLBACK_WORKS
152 /* #### It looks to me like adding any modifyVerify callback causes
153 #### Motif 1.1.4 to free the the TextF_Value() twice. I can't see
154 #### the bug in the Motif source, but Purify complains, even if
155 #### check_passwd_cb() is a no-op.
157 #### Update: Motif 1.2.1 also loses, but in a different way: it
158 #### writes beyond the end of a malloc'ed block in ModifyVerify().
159 #### Probably this block is the text field's text.
163 check_passwd_cb (button, client_data, call_data)
165 XtPointer client_data, call_data;
167 XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
169 if (passwd_state != pw_read)
171 else if (vcb->reason == XmCR_ACTIVATE)
173 passwd_done_cb (0, 0, 0);
175 else if (vcb->text->length > 1) /* don't allow "paste" operations */
179 else if (vcb->text->ptr != 0)
182 strncat (typed_passwd, vcb->text->ptr, vcb->text->length);
183 typed_passwd [vcb->endPos + vcb->text->length] = 0;
184 for (i = 0; i < vcb->text->length; i++)
185 vcb->text->ptr [i] = '*';
189 #else /* !VERIFY_CALLBACK_WORKS */
191 static void keypress();
192 static void backspace();
193 static void kill_line();
196 static XtActionsRec actions[] = {{"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\
214 static char translations[] = "<Key>:keypress()";
218 keypress (w, event, argv, argc)
225 char s [sizeof (typed_passwd)];
226 int size = XLookupString ((XKeyEvent *) event, s, sizeof (s), 0, 0);
227 if (size != 1) return;
229 /* hack because I can't get translations to dance to my tune... */
230 if (*s == '\010') { backspace (w, event, argv, argc); return; }
231 if (*s == '\177') { backspace (w, event, argv, argc); return; }
232 if (*s == '\025') { kill_line (w, event, argv, argc); return; }
233 if (*s == '\030') { kill_line (w, event, argv, argc); return; }
234 if (*s == '\012') { done (w, event, argv, argc); return; }
235 if (*s == '\015') { done (w, event, argv, argc); return; }
237 i = j = strlen (typed_passwd);
238 typed_passwd [i] = *s;
242 XmTextFieldSetString (passwd_text, s);
243 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
247 backspace (w, event, argv, argc)
253 char s [sizeof (typed_passwd)];
254 int i = strlen (typed_passwd);
258 typed_passwd [--i] = 0;
262 XmTextFieldSetString (passwd_text, s);
263 XmTextFieldSetInsertionPosition (passwd_text, j + 1);
267 kill_line (w, event, argv, argc)
273 memset (typed_passwd, 0, sizeof (typed_passwd));
274 XmTextFieldSetString (passwd_text, "");
278 done (w, event, argv, argc)
284 passwd_done_cb (w, 0, 0);
287 #endif /* !VERIFY_CALLBACK_WORKS */
290 format_into_label (widget, string)
296 XmString xm_label = 0;
297 XmString new_xm_label;
300 XtSetArg (av [ac], XmNlabelString, &xm_label); ac++;
301 XtGetValues (widget, av, ac);
302 XmStringGetLtoR (xm_label, XmSTRING_DEFAULT_CHARSET, &label);
303 if (!strcmp (label, XtName (widget)))
304 strcpy (buf, "ERROR: RESOURCES ARE NOT INSTALLED CORRECTLY");
306 sprintf (buf, label, string);
307 new_xm_label = XmStringCreate (buf, XmSTRING_DEFAULT_CHARSET);
309 XtSetArg (av [ac], XmNlabelString, new_xm_label); ac++;
310 XtSetValues (widget, av, ac);
311 XmStringFree (new_xm_label);
316 extern void skull (Display *, Window, GC, GC, int, int, int, int);
320 roger (button, client_data, call_data)
322 XtPointer client_data, call_data;
324 Display *dpy = XtDisplay (button);
325 Screen *screen = XtScreen (button);
326 Window window = XtWindow (button);
331 GC draw_gc, erase_gc;
334 XWindowAttributes xgwa;
335 XGetWindowAttributes (dpy, window, &xgwa);
336 cmap = xgwa.colormap;
337 if (xgwa.width > xgwa.height) size = xgwa.height;
338 else size = xgwa.width;
339 if (size > 40) size -= 30;
340 x = (xgwa.width - size) / 2;
341 y = (xgwa.height - size) / 2;
342 XtSetArg (av [ac], XmNforeground, &fg); ac++;
343 XtSetArg (av [ac], XmNbackground, &bg); ac++;
344 XtGetValues (button, av, ac);
345 /* if it's black on white, swap it cause it looks better (hack hack) */
346 if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
347 fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
349 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
351 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
352 XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
353 skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
354 XFreeGC (dpy, draw_gc);
355 XFreeGC (dpy, erase_gc);
359 make_passwd_dialog (parent)
363 create_passwd_dialog (parent);
365 XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
366 XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
367 XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
369 #ifdef VERIFY_CALLBACK_WORKS
370 XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
371 XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
373 XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
374 XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
377 #if (XmVersion >= 1002)
378 /* The focus stuff changed around; this didn't exist in 1.1.5. */
379 XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0);
382 /* Another random thing necessary in 1.2.1 but not 1.1.5... */
383 XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
385 pw = getpwuid (getuid ());
386 format_into_label (passwd_label3, (pw->pw_name ? pw->pw_name : "???"));
387 format_into_label (passwd_label1, screensaver_version);
391 extern void idle_timer ();
393 static int passwd_idle_timer_tick;
394 static XtIntervalId id;
397 passwd_idle_timer (junk1, junk2)
401 Display *dpy = XtDisplay (passwd_form);
402 Window window = XtWindow (passwd_form);
403 static Dimension x, y, d, s, ss;
405 int max = passwd_timeout / 1000;
407 idle_timer (junk1, junk2);
409 if (passwd_idle_timer_tick == max) /* first time */
414 unsigned long fg, bg;
415 XtSetArg (av [ac], XmNheight, &d); ac++;
416 XtGetValues (passwd_done, av, ac);
418 XtSetArg (av [ac], XmNwidth, &x); ac++;
419 XtSetArg (av [ac], XmNheight, &y); ac++;
420 XtSetArg (av [ac], XmNforeground, &fg); ac++;
421 XtSetArg (av [ac], XmNbackground, &bg); ac++;
422 XtGetValues (passwd_form, av, ac);
427 if (gc) XFreeGC (dpy, gc);
428 gc = XCreateGC (dpy, window, GCForeground, &gcv);
429 s = 360*64 / (passwd_idle_timer_tick - 1);
431 XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
432 XSetForeground (dpy, gc, bg);
438 if (--passwd_idle_timer_tick)
440 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
441 XFillArc (dpy, window, gc, x, y, d, d, ss, s);
446 extern void pop_up_dialog_box ();
447 extern int BadWindow_ehandler ();
450 pop_passwd_dialog (parent)
453 Display *dpy = XtDisplay (passwd_dialog);
456 typed_passwd [0] = 0;
457 passwd_state = pw_read;
458 XmTextFieldSetString (passwd_text, "");
460 XGetInputFocus (dpy, &focus, &revert_to);
461 #ifndef DESTROY_WORKS
462 /* This fucker blows up if we destroy the widget. I can't figure
463 out why. The second destroy phase dereferences freed memory...
464 So we just keep it around; but unrealizing or unmanaging it
465 doesn't work right either, so we hack the window directly. FMH.
467 if (XtWindow (passwd_form))
468 XMapRaised (dpy, XtWindow (passwd_dialog));
471 pop_up_dialog_box (passwd_dialog, passwd_form, 2);
472 XtManageChild (passwd_form);
474 #if (XmVersion < 1002)
475 /* The focus stuff changed around; this causes problems in 1.2.1
476 but is necessary in 1.1.5. */
477 XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
480 passwd_idle_timer_tick = passwd_timeout / 1000;
481 id = XtAppAddTimeOut (app, 1000, passwd_idle_timer, 0);
484 XGrabServer (dpy); /* ############ DANGER! */
486 /* this call to ungrab used to be in main_loop() - see comment in
487 xscreensaver.c around line 696. */
488 ungrab_keyboard_and_mouse ();
490 while (passwd_state == pw_read)
493 XtAppNextEvent (app, &event);
494 /* wait for timer event */
495 if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
496 passwd_state = pw_time;
497 XtDispatchEvent (&event);
500 XSync (dpy, False); /* ###### (danger over) */
502 if (passwd_state != pw_time)
503 XtRemoveTimeOut (id);
505 if (passwd_state != pw_ok)
508 switch (passwd_state)
510 case pw_time: lose = "Timed out!"; break;
511 case pw_fail: lose = "Sorry!"; break;
512 case pw_cancel: lose = 0; break;
515 XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
518 XmTextFieldSetString (passwd_text, lose);
519 XmTextFieldSetInsertionPosition (passwd_text, strlen (lose) + 1);
520 passwd_idle_timer_tick = 1;
521 id = XtAppAddTimeOut (app, 3000, passwd_idle_timer, 0);
525 XtAppNextEvent (app, &event);
526 if (event.xany.type == 0 && /* wait for timer event */
527 passwd_idle_timer_tick == 0)
529 XtDispatchEvent (&event);
533 memset (typed_passwd, 0, sizeof (typed_passwd));
534 XmTextFieldSetString (passwd_text, "");
535 XtSetKeyboardFocus (parent, None);
538 XtDestroyWidget (passwd_dialog);
541 XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
544 int (*old_handler) ();
545 old_handler = XSetErrorHandler (BadWindow_ehandler);
546 /* I don't understand why this doesn't refocus on the old selected
547 window when MWM is running in click-to-type mode. The value of
548 `focus' seems to be correct. */
549 XSetInputFocus (dpy, focus, revert_to, CurrentTime);
551 XSetErrorHandler (old_handler);
554 return (passwd_state == pw_ok ? True : False);
561 static Bool initted = False;
564 #ifndef VERIFY_CALLBACK_WORKS
565 XtAppAddActions (app, actions, XtNumber (actions));
571 make_passwd_dialog (parent);
572 return pop_passwd_dialog (parent);
575 #endif /* !NO_LOCKING */