home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / x / volume10 / xlock / part03 / xlock.c
C/C++ Source or Header  |  1990-12-07  |  22KB  |  733 lines

  1. #ifndef lint
  2. static char sccsid[] = "@(#)xlock.c    23.16 90/10/29 XLOCK SMI";
  3. #endif
  4. /*-
  5.  * xlock.c - X11 client to lock a display and show a screen saver.
  6.  *
  7.  * Copyright (c) 1988-90 by Patrick Naughton and Sun Microsystems, Inc.
  8.  *
  9.  * Permission to use, copy, modify, and distribute this software and its
  10.  * documentation for any purpose and without fee is hereby granted,
  11.  * provided that the above copyright notice appear in all copies and that
  12.  * both that copyright notice and this permission notice appear in
  13.  * supporting documentation.
  14.  *
  15.  * This file is provided AS IS with no warranties of any kind.  The author
  16.  * shall have no liability with respect to the infringement of copyrights,
  17.  * trade secrets or any patents by this file or any part thereof.  In no
  18.  * event will the author be liable for any lost revenue or profits or
  19.  * other special, indirect and consequential damages.
  20.  *
  21.  * Comments and additions should be sent to the author:
  22.  *
  23.  *               naughton@eng.sun.com
  24.  *
  25.  *               Patrick J. Naughton
  26.  *               MS 14-01
  27.  *               Windows and Graphics Group
  28.  *               Sun Microsystems, Inc.
  29.  *               2550 Garcia Ave
  30.  *               Mountain View, CA  94043
  31.  *
  32.  * Revision History:
  33.  * 29-Oct-90: added cast to XFree() arg.
  34.  *          added volume arg to call to XBell().
  35.  * 28-Oct-90: center prompt screen.
  36.  *          make sure Xlib input buffer does not use up all of swap.
  37.  *          make displayed text come from resource file for better I18N.
  38.  *          add backward compatible signal handlers for pre 4.1 machines.
  39.  * 31-Aug-90: added blank mode.
  40.  *          added swarm mode.
  41.  *          moved usleep() and seconds() out to usleep.c.
  42.  *          added SVR4 defines to xlock.h
  43.  * 29-Jul-90: added support for multiple screens to be locked by one xlock.
  44.  *          moved global defines to xlock.h
  45.  *          removed use of allowsig().
  46.  * 07-Jul-90: reworked commandline args and resources to use Xrm.
  47.  *          moved resource processing out to resource.c
  48.  * 02-Jul-90: reworked colors to not use dynamic colormap.
  49.  * 23-May-90: added autoraise when obscured.
  50.  * 15-Apr-90: added hostent alias searching for host authentication.
  51.  * 18-Feb-90: added SunOS3.5 fix.
  52.  *          changed -mono -> -color, and -saver -> -lock.
  53.  *          allow non-locking screensavers to display on remote machine.
  54.  *          added -echokeys to disable echoing of '?'s on input.
  55.  *          cleaned up all of the parameters and defaults.
  56.  * 20-Dec-89: added -xhost to allow access control list to be left alone.
  57.  *          added -screensaver (don't disable screen saver) for the paranoid.
  58.  *          Moved seconds() here from all of the display mode source files.
  59.  *          Fixed bug with calling XUngrabHosts() in finish().
  60.  * 19-Dec-89: Fixed bug in GrabPointer.
  61.  *          Changed fontname to XLFD style.
  62.  * 23-Sep-89: Added fix to allow local hostname:0 as a display.
  63.  *          Put empty case for Enter/Leave events.
  64.  *          Moved colormap installation later in startup.
  65.  * 20-Sep-89: Linted and made -saver mode grab the keyboard and mouse.
  66.  *          Replaced SunView code for life mode with Jim Graham's version,
  67.  *        so I could contrib it without legal problems.
  68.  *          Sent to expo for X11R4 contrib.
  69.  * 19-Sep-89: Added '?'s on input.
  70.  * 27-Mar-89: Added -qix mode.
  71.  *          Fixed GContext->GC.
  72.  * 20-Mar-89: Added backup font (fixed) if XQueryLoadFont() fails.
  73.  *          Changed default font to lucida-sans-24.
  74.  * 08-Mar-89: Added -nice, -mode and -display, built vector for life and hop.
  75.  * 24-Feb-89: Replaced hopalong display with life display from SunView1.
  76.  * 22-Feb-89: Added fix for color servers with n < 8 planes.
  77.  * 16-Feb-89: Updated calling conventions for XCreateHsbColormap();
  78.  *          Added -count for number of iterations per color.
  79.  *          Fixed defaulting mechanism.
  80.  *          Ripped out VMS hacks.
  81.  *          Sent to expo for X11R3 contrib.
  82.  * 15-Feb-89: Changed default font to pellucida-sans-18.
  83.  * 20-Jan-89: Added -verbose and fixed usage message.
  84.  * 19-Jan-89: Fixed monochrome gc bug.
  85.  * 16-Dec-88: Added SunView style password prompting.
  86.  * 19-Sep-88: Changed -color to -mono. (default is color on color displays).
  87.  *          Added -saver option. (just do display... don't lock.)
  88.  * 31-Aug-88: Added -time option.
  89.  *          Removed code for fractals to separate file for modularity.
  90.  *          Added signal handler to restore host access.
  91.  *          Installs dynamic colormap with a Hue Ramp.
  92.  *          If grabs fail then exit.
  93.  *          Added VMS Hacks. (password 'iwiwuu').
  94.  *          Sent to expo for X11R2 contrib.
  95.  * 08-Jun-88: Fixed root password pointer problem and changed PASSLENGTH to 20.
  96.  * 20-May-88: Added -root to allow root to unlock.
  97.  * 12-Apr-88: Added root password override.
  98.  *          Added screen saver override.
  99.  *          Removed XGrabServer/XUngrabServer.
  100.  *          Added access control handling instead.
  101.  * 01-Apr-88: Added XGrabServer/XUngrabServer for more security.
  102.  * 30-Mar-88: Removed startup password requirement.
  103.  *          Removed cursor to avoid phosphor burn.
  104.  * 27-Mar-88: Rotate fractal by 45 degrees clockwise.
  105.  * 24-Mar-88: Added color support. [-color]
  106.  *          wrote the man page.
  107.  * 23-Mar-88: Added HOPALONG routines from Scientific American Sept. 86 p. 14.
  108.  *          added password requirement for invokation
  109.  *          removed option for command line password
  110.  *          added requirement for display to be "unix:0".
  111.  * 22-Mar-88: Recieved Walter Milliken's comp.windows.x posting.
  112.  *
  113.  */
  114.  
  115. #include <stdio.h>
  116. #include <signal.h>
  117. #include <string.h>
  118. #include <pwd.h>
  119.  
  120. #include "xlock.h"
  121. #include <X11/cursorfont.h>
  122.  
  123. extern char *crypt();
  124. extern char *getenv();
  125.  
  126. char       *ProgramName;    /* argv[0] */
  127. perscreen   Scr[MAXSCREENS];
  128. Display    *dsp = NULL;        /* server display connection */
  129. int         screen;        /* current screen */
  130. void        (*callback) () = NULL;
  131. void        (*init) () = NULL;
  132.  
  133. static int  screens;        /* number of screens */
  134. static Window win[MAXSCREENS];    /* window used to cover screen */
  135. static Window icon[MAXSCREENS];    /* window used during password typein */
  136. static Window root[MAXSCREENS];    /* convenience pointer to the root window */
  137. static GC   textgc[MAXSCREENS];    /* graphics context used for text rendering */
  138. static XColor fgcol[MAXSCREENS];/* used for text rendering */
  139. static XColor bgcol[MAXSCREENS];/* background of text screen */
  140. static int  iconx[MAXSCREENS];    /* location of left edge of icon */
  141. static int  icony[MAXSCREENS];    /* location of top edge of icon */
  142. static Cursor mycursor;        /* blank cursor */
  143. static Pixmap lockc;
  144. static Pixmap lockm;        /* pixmaps for cursor and mask */
  145. static char no_bits[] = {0};    /* dummy array for the blank cursor */
  146. static int  passx;        /* position of the ?'s */
  147. static int  passy;
  148. static XFontStruct *font;
  149. static int  sstimeout;        /* screen saver parameters */
  150. static int  ssinterval;
  151. static int  ssblanking;
  152. static int  ssexposures;
  153.  
  154. #define FALLBACK_FONTNAME    "fixed"
  155. #define ICONW            64
  156. #define ICONH            64
  157.  
  158. #define AllPointerEventMask \
  159.     (ButtonPressMask | ButtonReleaseMask | \
  160.     EnterWindowMask | LeaveWindowMask | \
  161.     PointerMotionMask | PointerMotionHintMask | \
  162.     Button1MotionMask | Button2MotionMask | \
  163.     Button3MotionMask | Button4MotionMask | \
  164.     Button5MotionMask | ButtonMotionMask | \
  165.     KeymapStateMask)
  166.  
  167.  
  168. /* VARARGS1 */
  169. void
  170. error(s1, s2)
  171.     char       *s1,
  172.                *s2;
  173. {
  174.     fprintf(stderr, s1, ProgramName, s2);
  175.     exit(1);
  176. }
  177.  
  178. /*
  179.  * Server access control support.
  180.  */
  181.  
  182. static XHostAddress *XHosts;    /* the list of "friendly" client machines */
  183. static int  HostAccessCount;    /* the number of machines in XHosts */
  184. static Bool HostAccessState;    /* whether or not we even look at the list */
  185.  
  186. static void
  187. XGrabHosts(dsp)
  188.     Display    *dsp;
  189. {
  190.     XHosts = XListHosts(dsp, &HostAccessCount, &HostAccessState);
  191.     if (XHosts)
  192.     XRemoveHosts(dsp, XHosts, HostAccessCount);
  193.     XEnableAccessControl(dsp);
  194. }
  195.  
  196. static void
  197. XUngrabHosts(dsp)
  198.     Display    *dsp;
  199. {
  200.     if (XHosts) {
  201.     XAddHosts(dsp, XHosts, HostAccessCount);
  202.     XFree((char *)XHosts);
  203.     }
  204.     if (HostAccessState == False)
  205.     XDisableAccessControl(dsp);
  206. }
  207.  
  208.  
  209. /*
  210.  * Simple wrapper to get an asynchronous grab on the keyboard and mouse.
  211.  * If either grab fails, we sleep for one second and try again since some
  212.  * window manager might have had the mouse grabbed to drive the menu choice
  213.  * that picked "Lock Screen..".  If either one fails the second time we print
  214.  * an error message and exit.
  215.  */
  216. static void
  217. GrabKeyboardAndMouse()
  218. {
  219.     Status      status;
  220.  
  221.     status = XGrabKeyboard(dsp, win[0], True,
  222.                GrabModeAsync, GrabModeAsync, CurrentTime);
  223.     if (status != GrabSuccess) {
  224.     sleep(1);
  225.     status = XGrabKeyboard(dsp, win[0], True,
  226.                    GrabModeAsync, GrabModeAsync, CurrentTime);
  227.  
  228.     if (status != GrabSuccess)
  229.         error("%s: couldn't grab keyboard! (%d)\n", status);
  230.     }
  231.     status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
  232.               GrabModeAsync, GrabModeAsync, None, mycursor,
  233.               CurrentTime);
  234.     if (status != GrabSuccess) {
  235.     sleep(1);
  236.     status = XGrabPointer(dsp, win[0], True, AllPointerEventMask,
  237.                   GrabModeAsync, GrabModeAsync, None, mycursor,
  238.                   CurrentTime);
  239.  
  240.     if (status != GrabSuccess)
  241.         error("%s: couldn't grab pointer! (%d)\n", status);
  242.     }
  243. }
  244.  
  245.  
  246. /*
  247.  * Assuming that we already have an asynch grab on the pointer,
  248.  * just grab it again with a new cursor shape and ignore the return code.
  249.  */
  250. static void
  251. XChangeGrabbedCursor(cursor)
  252.     Cursor      cursor;
  253. {
  254. #ifndef DEBUG
  255.     (void) XGrabPointer(dsp, win[0], True, AllPointerEventMask,
  256.             GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
  257. #endif
  258. }
  259.  
  260.  
  261. /*
  262.  * Restore all grabs, reset screensaver, restore colormap, close connection.
  263.  */
  264. static void
  265. finish()
  266. {
  267.     XSync(dsp, False);
  268.     if (!nolock && !allowaccess)
  269.     XUngrabHosts(dsp);
  270.     XUngrabPointer(dsp, CurrentTime);
  271.     XUngrabKeyboard(dsp, CurrentTime);
  272.     if (!enablesaver)
  273.     XSetScreenSaver(dsp, sstimeout, ssinterval, ssblanking, ssexposures);
  274.     XFlush(dsp);
  275.     XCloseDisplay(dsp);
  276. }
  277.  
  278.  
  279. static int
  280. ReadXString(s, slen)
  281.     char       *s;
  282.     int         slen;
  283. {
  284.     XEvent      event;
  285.     char        keystr[20];
  286.     char        c;
  287.     int         i;
  288.     int         bp;
  289.     int         len;
  290.     int         thisscreen = screen;
  291.     char       *pwbuf = (char *) malloc(slen);
  292.  
  293. #ifdef DEBUG
  294.     XSetInputFocus(dsp, win[screen], RevertToPointerRoot, CurrentTime);
  295. #endif
  296.     for (screen = 0; screen < screens; screen++)
  297.     if (thisscreen == screen)
  298.         init(icon[screen]);
  299.     else
  300.         init(win[screen]);
  301.     bp = 0;
  302.     nice(-nicelevel);        /* make sure we can read the keys... */
  303.     while (True) {
  304.     unsigned long lasteventtime = seconds();
  305.     while (!XPending(dsp)) {
  306.         for (screen = 0; screen < screens; screen++)
  307.         if (thisscreen == screen)
  308.             callback(icon[screen]);
  309.         else
  310.             callback(win[screen]);
  311.         XFlush(dsp);
  312.         usleep(delay);
  313.         if (seconds() - lasteventtime > timeout) {
  314.         nice(nicelevel);
  315.         free(pwbuf);
  316.         screen = thisscreen;
  317.         return 1;
  318.         }
  319.     }
  320.     screen = thisscreen;
  321.     XNextEvent(dsp, &event);
  322.     switch (event.type) {
  323.     case KeyPress:
  324.         len = XLookupString((XKeyEvent *) & event, keystr, 20, NULL, NULL);
  325.         for (i = 0; i < len; i++) {
  326.         c = keystr[i];
  327.         switch (c) {
  328.         case 8:    /* ^H */
  329.         case 127:    /* DEL */
  330.             if (bp > 0)
  331.             bp--;
  332.             break;
  333.         case 10:    /* ^J */
  334.         case 13:    /* ^M */
  335.             s[bp] = '\0';
  336.             nice(nicelevel);
  337.             free(pwbuf);
  338.             return 0;
  339.         case 21:    /* ^U */
  340.             bp = 0;
  341.             break;
  342.         default:
  343.             s[bp] = c;
  344.             if (bp < slen - 1)
  345.             bp++;
  346.             else
  347.             XSync(dsp, True);    /* flush input buffer */
  348.  
  349.         }
  350.         }
  351.         if (echokeys) {
  352.         memset(pwbuf, '?', slen);
  353.         XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
  354.         XFillRectangle(dsp, win[screen], Scr[screen].gc,
  355.                    passx, passy - font->ascent,
  356.                    XTextWidth(font, pwbuf, slen),
  357.                    font->ascent + font->descent);
  358.         XDrawString(dsp, win[screen], textgc[screen],
  359.                 passx, passy, pwbuf, bp);
  360.         }
  361.         /*
  362.          * eat all events if there are more than enough pending... this
  363.          * keeps the Xlib event buffer from growing larger than all
  364.          * available memory and crashing xlock.
  365.          */
  366.         if (XPending(dsp) > 100) {    /* 100 is arbitrarily big enough */
  367.         register Status status;
  368.         do {
  369.             status = XCheckMaskEvent(dsp,
  370.                       KeyPressMask | KeyReleaseMask, &event);
  371.         } while (status);
  372.         XBell(dsp, 100);
  373.         }
  374.         break;
  375.  
  376.     case ButtonPress:
  377.         if (((XButtonEvent *) & event)->window == icon[screen]) {
  378.         nice(nicelevel);
  379.         free(pwbuf);
  380.         return 1;
  381.         }
  382.         break;
  383.  
  384.     case VisibilityNotify:
  385.         if (event.xvisibility.state != VisibilityUnobscured) {
  386. #ifndef DEBUG
  387.         XRaiseWindow(dsp, win[screen]);
  388. #endif
  389.         s[0] = '\0';
  390.         nice(nicelevel);
  391.         free(pwbuf);
  392.         return 1;
  393.         }
  394.         break;
  395.  
  396.     case KeymapNotify:
  397.     case KeyRelease:
  398.     case ButtonRelease:
  399.     case MotionNotify:
  400.     case LeaveNotify:
  401.     case EnterNotify:
  402.         break;
  403.  
  404.     default:
  405.         fprintf(stderr, "%s: unexpected event: %d\n",
  406.             ProgramName, event.type);
  407.         break;
  408.     }
  409.     }
  410. }
  411.  
  412.  
  413. static int
  414. getPassword()
  415. {
  416. #define PASSLENGTH 20
  417.     char        buffer[PASSLENGTH];
  418.     char        userpass[PASSLENGTH];
  419.     char        rootpass[PASSLENGTH];
  420.     struct passwd *pw;
  421.     XWindowAttributes xgwa;
  422.     char       *user = getenv(USERNAME);
  423.     int         y,
  424.                 left,
  425.                 done;
  426.  
  427.     XGetWindowAttributes(dsp, win[screen], &xgwa);
  428.  
  429.     XChangeGrabbedCursor(XCreateFontCursor(dsp, XC_left_ptr));
  430.  
  431.     XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
  432.     XFillRectangle(dsp, win[screen], Scr[screen].gc,
  433.            0, 0, xgwa.width, xgwa.height);
  434.  
  435.     XMapWindow(dsp, icon[screen]);
  436.     XRaiseWindow(dsp, icon[screen]);
  437.  
  438.     left = iconx[screen] + ICONW + font->max_bounds.width;
  439.     y = icony[screen] + font->ascent;
  440.  
  441.     XDrawString(dsp, win[screen], textgc[screen],
  442.         left, y, text_name, strlen(text_name));
  443.     XDrawString(dsp, win[screen], textgc[screen],
  444.         left + 1, y, text_name, strlen(text_name));
  445.     XDrawString(dsp, win[screen], textgc[screen],
  446.         left + XTextWidth(font, text_name, strlen(text_name)), y,
  447.         user, strlen(user));
  448.  
  449.     y += font->ascent + font->descent + 2;
  450.     XDrawString(dsp, win[screen], textgc[screen],
  451.         left, y, text_pass, strlen(text_pass));
  452.     XDrawString(dsp, win[screen], textgc[screen],
  453.         left + 1, y, text_pass, strlen(text_pass));
  454.  
  455.     passx = left + 1 + XTextWidth(font, text_pass, strlen(text_pass))
  456.     + XTextWidth(font, " ", 1);
  457.     passy = y;
  458.  
  459.     y = icony[screen] + ICONH + font->ascent + 2;
  460.     XDrawString(dsp, win[screen], textgc[screen],
  461.         iconx[screen], y, text_info, strlen(text_info));
  462.  
  463.     XFlush(dsp);
  464.  
  465.     y += font->ascent + font->descent + 2;
  466.  
  467.     pw = getpwnam("root");
  468.     strcpy(rootpass, pw->pw_passwd);
  469.  
  470.     pw = getpwnam(user);
  471.     strcpy(userpass, pw->pw_passwd);
  472.  
  473.     done = False;
  474.     while (!done) {
  475.     if (ReadXString(buffer, PASSLENGTH)) {
  476.         XChangeGrabbedCursor(mycursor);
  477.         XUnmapWindow(dsp, icon[screen]);
  478.         XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
  479.         return 1;
  480.     }
  481.     XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel);
  482.     XFillRectangle(dsp, win[screen], Scr[screen].gc,
  483.                iconx[screen], y - font->ascent,
  484.                XTextWidth(font, text_invalid, strlen(text_invalid)),
  485.                font->ascent + font->descent + 2);
  486.  
  487.     XDrawString(dsp, win[screen], textgc[screen],
  488.             iconx[screen], y, text_valid, strlen(text_valid));
  489.  
  490.     done = !((strcmp(crypt(buffer, userpass), userpass))
  491.            && (!allowroot || strcmp(crypt(buffer, rootpass), rootpass)));
  492.  
  493.     if (!done) {
  494.         XSync(dsp, True);    /* flush input buffer */
  495.         sleep(1);
  496.         XFillRectangle(dsp, win[screen], Scr[screen].gc,
  497.                iconx[screen], y - font->ascent,
  498.                XTextWidth(font, text_valid, strlen(text_valid)),
  499.                font->ascent + font->descent + 2);
  500.  
  501.         XDrawString(dsp, win[screen], textgc[screen],
  502.             iconx[screen], y, text_invalid, strlen(text_invalid));
  503.     }
  504.     }
  505.     return 0;
  506. }
  507.  
  508.  
  509. static void
  510. justDisplay()
  511. {
  512.     XEvent      event;
  513.  
  514.     for (screen = 0; screen < screens; screen++)
  515.     init(win[screen]);
  516.     do {
  517.     while (!XPending(dsp)) {
  518.         for (screen = 0; screen < screens; screen++)
  519.         callback(win[screen]);
  520.         XFlush(dsp);
  521.         usleep(delay);
  522.     }
  523.     XNextEvent(dsp, &event);
  524. #ifndef DEBUG
  525.     if (event.type == VisibilityNotify)
  526.         XRaiseWindow(dsp, event.xany.window);
  527. #endif
  528.     } while (event.type != ButtonPress && event.type != KeyPress);
  529.     for (screen = 0; screen < screens; screen++)
  530.     if (event.xbutton.root == RootWindow(dsp, screen))
  531.         break;
  532. }
  533.  
  534.  
  535. static void
  536. sigcatch()
  537. {
  538.     finish();
  539.     error("%s: caught terminate signal.\nAccess control list restored.\n");
  540. }
  541.  
  542.  
  543. static void
  544. lockDisplay()
  545. {
  546.     if (!allowaccess) {
  547. #ifdef SYSV
  548.     sigset_t    oldsigmask;
  549.     sigset_t    newsigmask;
  550.  
  551.     sigemptyset(&newsigmask);
  552.     sigaddset(&newsigmask, SIGHUP);
  553.     sigaddset(&newsigmask, SIGINT);
  554.     sigaddset(&newsigmask, SIGQUIT);
  555.     sigaddset(&newsigmask, SIGTERM);
  556.     sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);
  557. #else
  558.     int         oldsigmask;
  559.  
  560.     oldsigmask = sigblock(sigmask(SIGHUP) |
  561.                   sigmask(SIGINT) |
  562.                   sigmask(SIGQUIT) |
  563.                   sigmask(SIGTERM));
  564. #endif
  565.  
  566.     signal(SIGHUP, sigcatch);
  567.     signal(SIGINT, sigcatch);
  568.     signal(SIGQUIT, sigcatch);
  569.     signal(SIGTERM, sigcatch);
  570.  
  571.     XGrabHosts(dsp);
  572.  
  573. #ifdef SYSV
  574.     sigprocmask(SIG_SETMASK, &oldsigmask, &oldsigmask);
  575. #else
  576.     sigsetmask(oldsigmask);
  577. #endif
  578.     }
  579.     do {
  580.     justDisplay();
  581.     } while (getPassword());
  582. }
  583.  
  584.  
  585. int
  586. main(argc, argv)
  587.     int         argc;
  588.     char       *argv[];
  589. {
  590.     XSetWindowAttributes xswa;
  591.     XGCValues   xgcv;
  592.  
  593.     ProgramName = strrchr(argv[0], '/');
  594.     if (ProgramName)
  595.     ProgramName++;
  596.     else
  597.     ProgramName = argv[0];
  598.  
  599.     GetResources(argc, argv);
  600.  
  601.     CheckResources();
  602.  
  603.     font = XLoadQueryFont(dsp, fontname);
  604.     if (font == NULL) {
  605.     fprintf(stderr, "%s: can't find font: %s, using %s...\n",
  606.         ProgramName, fontname, FALLBACK_FONTNAME);
  607.     font = XLoadQueryFont(dsp, FALLBACK_FONTNAME);
  608.     if (font == NULL)
  609.         error("%s: can't even find %s!!!\n", FALLBACK_FONTNAME);
  610.     }
  611.     screens = ScreenCount(dsp);
  612.     if (screens > MAXSCREENS)
  613.     error("%s: can only support %d screens.\n", MAXSCREENS);
  614.     for (screen = 0; screen < screens; screen++) {
  615.     XColor      tmp;
  616.     Screen     *scr = ScreenOfDisplay(dsp, screen);
  617.     Colormap    cmap = DefaultColormapOfScreen(scr);
  618.  
  619.     root[screen] = RootWindowOfScreen(scr);
  620.     if (mono || CellsOfScreen(scr) == 2) {
  621.         XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp);
  622.         XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp);
  623.         Scr[screen].pixels[0] = fgcol[screen].pixel;
  624.         Scr[screen].pixels[1] = bgcol[screen].pixel;
  625.         Scr[screen].npixels = 2;
  626.     } else {
  627.         int         colorcount = NUMCOLORS;
  628.         u_char      red[NUMCOLORS];
  629.         u_char      green[NUMCOLORS];
  630.         u_char      blue[NUMCOLORS];
  631.         int         i;
  632.  
  633.         if (!XAllocNamedColor(dsp, cmap, background,
  634.                   &bgcol[screen], &tmp)) {
  635.         fprintf(stderr, "couldn't allocate: %s\n", background);
  636.         XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp);
  637.         }
  638.         if (!XAllocNamedColor(dsp, cmap, foreground,
  639.                   &fgcol[screen], &tmp)) {
  640.         fprintf(stderr, "couldn't allocate: %s\n", foreground);
  641.         XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp);
  642.         }
  643.         hsbramp(0.0, saturation, 1.0, 1.0, saturation, 1.0, colorcount,
  644.             red, green, blue);
  645.         Scr[screen].npixels = 0;
  646.         for (i = 0; i < colorcount; i++) {
  647.         XColor      xcolor;
  648.  
  649.         xcolor.red = red[i] << 8;
  650.         xcolor.green = green[i] << 8;
  651.         xcolor.blue = blue[i] << 8;
  652.         xcolor.flags = DoRed | DoGreen | DoBlue;
  653.  
  654.         if (!XAllocColor(dsp, cmap, &xcolor))
  655.             break;
  656.  
  657.         Scr[screen].pixels[i] = xcolor.pixel;
  658.         Scr[screen].npixels++;
  659.         }
  660.         if (verbose)
  661.         fprintf(stderr, "%d pixels allocated\n", Scr[screen].npixels);
  662.     }
  663.  
  664.     xswa.override_redirect = True;
  665.     xswa.background_pixel = BlackPixelOfScreen(scr);
  666.     xswa.event_mask = KeyPressMask | ButtonPressMask | VisibilityChangeMask;
  667.  
  668.     win[screen] = XCreateWindow(dsp, root[screen],
  669.                     0, 0,
  670.                     WidthOfScreen(scr),
  671.                     HeightOfScreen(scr),
  672.                   0, CopyFromParent, InputOutput, CopyFromParent,
  673.               CWOverrideRedirect | CWBackPixel | CWEventMask, &xswa);
  674.  
  675.     xswa.background_pixel = bgcol[screen].pixel;
  676.     xswa.event_mask = ButtonPressMask;
  677.  
  678.     iconx[screen] = (DisplayWidth(dsp, screen) -
  679.              XTextWidth(font, text_info, strlen(text_info))) / 2;
  680.  
  681.     icony[screen] = DisplayHeight(dsp, screen) / 6;
  682.     icon[screen] = XCreateWindow(dsp, win[screen],
  683.                      iconx[screen], icony[screen],
  684.                      ICONW, ICONH,
  685.                      1, CopyFromParent,
  686.                      InputOutput, CopyFromParent,
  687.                      CWBackPixel | CWEventMask, &xswa);
  688.  
  689.     XMapWindow(dsp, win[screen]);
  690.     XRaiseWindow(dsp, win[screen]);
  691.  
  692.     xgcv.foreground = WhitePixelOfScreen(scr);
  693.     xgcv.background = BlackPixelOfScreen(scr);
  694.     Scr[screen].gc = XCreateGC(dsp, win[screen],
  695.                    GCForeground | GCBackground, &xgcv);
  696.  
  697.     xgcv.foreground = fgcol[screen].pixel;
  698.     xgcv.background = bgcol[screen].pixel;
  699.     xgcv.font = font->fid;
  700.     textgc[screen] = XCreateGC(dsp, win[screen],
  701.                 GCFont | GCForeground | GCBackground, &xgcv);
  702.     }
  703.     lockc = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1);
  704.     lockm = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1);
  705.     mycursor = XCreatePixmapCursor(dsp, lockc, lockm,
  706.                    &fgcol[screen], &bgcol[screen], 0, 0);
  707.     XFreePixmap(dsp, lockc);
  708.     XFreePixmap(dsp, lockm);
  709.  
  710.  
  711.     if (!enablesaver) {
  712.     XGetScreenSaver(dsp, &sstimeout, &ssinterval,
  713.             &ssblanking, &ssexposures);
  714.     XSetScreenSaver(dsp, 0, 0, 0, 0);    /* disable screen saver */
  715.     }
  716. #ifndef DEBUG
  717.     GrabKeyboardAndMouse();
  718. #endif
  719.  
  720.     srandom(getpid());
  721.  
  722.     nice(nicelevel);
  723.  
  724.     if (nolock)
  725.     justDisplay();
  726.     else
  727.     lockDisplay();
  728.  
  729.     finish();
  730.  
  731.     return 0;
  732. }
  733.