/* xkill.c - lock a VaxStation (but not quite like xlock!)
 *
 * Revision 2.0 - revised from xlock 2.0, 12/20/86
 *
 */

#include <stdio.h>
#include <X/Xlib.h>
#include <signal.h>
#include <sys/time.h>

#define	FAILURE	0			    /*Failure return code */
#define AllEvents 0xffff		    /*Select all events  */
#include "mask.h"			    /* contains locked cursor mask */
#include "curs.h"			    /* contains locked cursor form */

#define BIGFONT "sbdr66"		    /* font to use for large type */
#define SMALLFONT "kilter28"		    /* font to use for rest of type */

int refresh;				    /* need to refresh flag */
int mousex, mousey;			    /* initial mouse coords */
Window w0;				    /* main window */
Window w1;				    /* dummy window id */
Font bfont;				    /* font id for BIGFONT */
Font sfont;				    /* font id for SMALLFONT */

int done(), update();

main(argc, argv)
     int argc;
     char *argv[];
{
  char *getlogin(), *index(), *getpass();
  char c, pass[128];
  char display[80],lockpass[80], *tmp;
  int code, num, i;
  struct itimerval time1, time2;
  
  Bitmap curs,mask;
  Cursor cursor;
  XEvent event;
  FontInfo *bfinfo, *sfinfo;
  Window *children;
  XKeyPressedEvent *key;
  extern KeyMapEntry StdMap[];

  display[0]='\0';
  if (argc>=2)
    strncpy(display,argv[1],80);

  tmp = getpass("Password: ");
  strncpy(lockpass,tmp,80);
  tmp = getpass("Again: ");
  if (strcmp(lockpass,tmp))
    exit(1);

  /* open the user's display */
  if (XOpenDisplay(display) == NULL) 
    Error("Unable to open display");

  /* check if another xlock is running */
  /* we should check XQueryTree return code, but */
  /* there is currently in a bug.  It is invalid */
  XQueryTree(RootWindow, &w1, &num, &children);
  for (i = 0; i < num; i++) {
    char *name;
    XFetchName(children[i], &name);
    if (!strcmp(name, "xlock")) {
      printf("Sorry, xlock already running on that display\n");
      exit(0);
    }
    free(name);
  }
  free(children);
  
  /* open the user's big font */
  if ((bfinfo = XOpenFont(BIGFONT)) == FAILURE)
    Error("Can't open big font");
	
  /* set the font id */
  bfont = bfinfo->id;

  /* open the user's small font */
  if ((sfinfo = XOpenFont(SMALLFONT)) == FAILURE)
    Error("Cant open small font");

  /* set the font id */
  sfont = sfinfo->id;

  /* create the main window */
  w0 = XCreateWindow(RootWindow, 30, 30, 900, 800,
		     2, BlackPixmap, WhitePixmap);

  if (w0 == FAILURE) Error("bad window");
	
  /* give the window a name */
  XStoreName(w0,"xkill");
  
  /* Set up a resize hint, 0x0 is minimum size, */
  /* 1 is the x increment, 1 is the y increment*/
  XSetResizeHint(w0, 0, 0, 1, 1);

  /* Window won't be displayed 'till we map  it */
  XMapWindow(w0);

  /* Clear the window */
  XClear(w0);

  /* Store away the mouse pos, so we can restore it when done */
  XQueryMouse(RootWindow, &mousex, &mousey, &w1);

  /* Make sure the mouse is in our window */
  XWarpMouse(w0, 400, 400);

  showlocked(w0, sfont, bfont);   /* put up the locked mesg */
	
  XFlush();				/* flush output */

  /* set up the locked cursor by */
  /* creating a cursor bitmap and a mask bitmap and */
  /* melding them into a cursor */
  curs = XStoreBitmap(curs_width,curs_height,curs_bits);
  mask = XStoreBitmap(mask_width,mask_height,mask_bits);
  cursor = XStoreCursor(curs,mask,8,8,WhitePixel,BlackPixel,GXcopy);
  
  XSelectInput(w0,0x0000);		/* select no events */
  
  XFocusKeyboard(w0);			/* sieze keyboard */
  
  /* sieze mouse, setting cursor to the locked cursor */
  /* and simultaneously setting event mask */
  XGrabMouse(w0,cursor, ButtonPressed);
  
  /* select the input we are interested in */
  XSelectInput(w0, ButtonPressed | KeyPressed |
	       ExposeWindow | ExposeRegion | ExposeCopy |
	       FocusChange | UnmapWindow);

  signal(SIGHUP, done);			/* if HUP received, we */
                                          /* should unlock*/

  /* set up a timer and an interupt service */
  /* the interupt service will handle screen refresh */
  signal(SIGALRM, update);

  time1.it_value.tv_sec = 0;		/* first timeout in .1 sec */
  time1.it_value.tv_usec = 100000;

  time1.it_interval.tv_sec = 0;		/* and more every .1 sec */
  time1.it_interval.tv_usec = 100000;

  setitimer(ITIMER_REAL, &time1, &time2);	/* activate timer */

  pass[0]='\0';				/* initialized pswd attempt */
  
  while (1) {				/* main locked loop */

    while (!XPending())		/* wait for an event */
      sigpause(0);

    XNextEvent(&event);		/* get the event */

    switch (event.type) {		/* and handle it */
    case ButtonPressed:	/* buttons just feep */
      XFeep(0);
      break;
    case ExposeRegion:
    case ExposeWindow:
    case ExposeCopy:	/* exposures flag refresh */
      refresh++;		/* which is done in update */
      break;		/* which as called by dummy */
    case FocusChange:	/* keep a grip on keyboard  */
      XFocusKeyboard(w0);
      break;
    case UnmapWindow:	/* keep our window displayed */
      XMapWindow(w0);
      XFocusKeyboard(w0);
      XFlush();
      break;
    case KeyPressed:	/* password char */
      /* decode keypress */
      key = (XKeyPressedEvent *)&event;
      code = key->detail;
      c = StdMap[code & ValueMask][KeyState(code)];
      /* if cr or lf we should validate password */
      if (c == '\n' || c == '\r') {
	if (strcmp(lockpass,pass) == 0)
	  done();
	else {
	  XFeep(0);
	  XFlush();
	}
	pass[0] = '\0';
      }
      /* not a cr or lf so tack char onto string */
      else {
	int temp;
	if ((temp=strlen(pass))<128) {
	  pass[temp++]=c;
	  pass[temp]='\0';
	}
      }
      break;
    default:
      break;
    }
  }
}

/* Error - print out and error message, and exit */
Error(arg)
char	*arg;
{
	printf("%s\n",arg);
	exit(1);
}
/* showlocked - forms the screen of a locked terminal */
showlocked(window, font, bfont)
Window *window;					/* window to write in */
Font font, bfont;				/* fonts to use */
{
     int line = 0;

     XClear(window);				/* start with window empty */
     line = 5;					/* start on the fifth line */
  
     /* put up text */
     /* all text is centered in window */
     writecenter(window, "This terminal has been", font, line);
     line += 30;

     writecenter(window,"temporarily",font, line);
     line += 30;

     line += 30;
     writecenter(window, "Locked", bfont, line);
     line += 96;

     line += 360;

     writecenter(window, "Type the password and <Return> to unlock.",
		 font, line);
     line += 30;
   }
 

/* writecenter - put text centered on a particular line in a window */
writecenter(window, text, font, line)
Window *window;					/* window to write in */
Font font;					/* font to write with */
char *text;					/* text (null term.) string */
int line;					/* line to place text on */
{
  WindowInfo *winfo;
  FontInfo *finfo;
  int center;
  int start;
  int len;

  /* set aside some storage for font and window info */
  winfo = (WindowInfo *)calloc(sizeof(WindowInfo), 1);
  finfo = (FontInfo *)calloc(sizeof(FontInfo), 1);

  XQueryWindow(window, winfo);			/* find out about the window*/
  center = winfo->width/2;			/* find the center */

  XQueryFont(font, finfo);			/* find the font width */
  if ((start = center - finfo->width*(len = strlen(text))/2) < 0)
     start = 0;

  XText(window, start, line,			/* write the text */
	text, len,
	font, BlackPixel, WhitePixel);

}
/* update - called once a second to refresh timer line, and check for */
/* timer expiration */
update()
{
         if (!refresh)
	   return;
	 /* Make sure we are top window */
	 XRaiseWindow(w0);

	 showlocked(w0, sfont, bfont);

	 XFlush();				/* flush output */
	 refresh=0;
}
	
/* successful completion */
/* restore mouse and exit */
int done()
{
	 XWarpMouse(RootWindow, mousex, mousey);
	 XFlush();
	 exit(0);
}

