home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume9 / xscreen / part02 / screensaver.c < prev   
Encoding:
C/C++ Source or Header  |  1987-04-19  |  29.0 KB  |  1,265 lines

  1. /*
  2.  * screensaver - blanks displays under X window system
  3.  *    (requires X version 10)
  4.  *
  5.  * Written by:
  6.  *    Edward Moy
  7.  *    Academic Computing Services
  8.  *    University of California at Berkeley
  9.  */
  10. #include <ctype.h>
  11. #include <stdio.h>
  12. #include <signal.h>
  13. #include <strings.h>
  14. #include <pwd.h>
  15. #include <setjmp.h>
  16. #include <utmp.h>
  17. #include <errno.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <sys/ioctl.h>
  21. #include <sys/time.h>
  22. #include <sys/file.h>
  23. #include <X/Xlib.h>
  24. #include "menu.h"
  25. #include "arrow"
  26. #include "arrowmask"
  27.  
  28. #define    AnyButtonMask    (LeftMask | MiddleMask | RightMask)
  29. #define    BITMAPOPENERR    0
  30. #define    BITMAPOK    1
  31. #define    BITSPERBYTE    8
  32. #define    BITSPERSHORT    (BITSPERBYTE * sizeof(short))
  33. #define    BOTTOM        3
  34. #define    CURHEIGHT    16
  35. #define    CURWIDTH    16
  36. #define    DEFAULTHEIGHT    38
  37. #define    DEFAULTWIDTH    48
  38. #define    DELTA        4
  39. #define    FALSE        0
  40. #define    IDLETIME    300
  41. #define    IMAGESPERSEC    5
  42. #define    LEFT        0
  43. #define    MICROSEC    (1000000 / IMAGESPERSEC)
  44. #define    NWALLS        4
  45. #define    PAD        4
  46. #define    PASSWORDLEN    9
  47. #define    RANCNTMAX    32
  48. #define    RIGHT        2
  49. #define    SELECTERR    -1
  50. #define SINN        20
  51. #define    TIMEOUT        0
  52. #define    TOP        1
  53. #define    TRUE        1
  54. #define    USERLEN        5
  55. #define    VERIFYTIMEOUT    45
  56.  
  57. #define    max(a,b)    ((a) > (b) ? (a) : (b))
  58. #define    rangex(x)    ((x) >= areawidth ? areawidth-1 : ((x) < 0 ? 0 : (x)))
  59. #define    rangey(y)    ((y) >= areaheight ? areaheight-1 : ((y) < 0 ? 0 : (y)))
  60. #define    xmove(x)    (signx * sin1000[SINN - sinindex] * x / 1000 + x0)
  61. #define    ymove(y)    (signy * sin1000[sinindex] * y / 1000 + y0)
  62.  
  63. #define    MENU_ONOFF    0
  64. #define    MENU_AUTOLOCK    (MENU_ONOFF+1)
  65. #define    MENU_LINE    (MENU_AUTOLOCK+1)
  66. #define    MENU_BLANK    (MENU_LINE+1)
  67. #define    MENU_LOCK    (MENU_BLANK+1)
  68. #define    MENU_LINE2    (MENU_LOCK+1)
  69. #define    MENU_EXIT    (MENU_LINE2+1)
  70.  
  71. struct bm {
  72.     struct bm *next;
  73.     short *bits;
  74.     int x;
  75.     int y;
  76.     int width;
  77.     int height;
  78.     int color;
  79. };
  80.  
  81. int am;
  82. int amicon;
  83. int areaheight;
  84. int areawidth;
  85. Cursor arrow;
  86. int autolock;
  87. char *bitmap_file = XSCREENSAVER;
  88. int bitmap_height;
  89. int bitmap_width;
  90. Window black;
  91. Cursor blackcursor;
  92. int center;
  93. FILE *console = stderr;
  94. short *cursor_bits;
  95. int cursor_height;
  96. int cursor_width;
  97. FontInfo *finfo;
  98. Font font;
  99. int fontheight;
  100. char *fontname = "9x15";
  101. int halfwidth;
  102. Window icon;
  103. int iconx = DELTA;
  104. int idletime = IDLETIME;
  105. struct timeval idletimeout = {
  106.     IDLETIME,
  107.     0,
  108. };
  109. Pixmap image;
  110. int imageheight;
  111. Pixmap imagepm;
  112. int imagewidth;
  113. int locked;
  114. char *mtext[] = {
  115.     "Screensaving",
  116.     "Auto-Lock",
  117.     "-",
  118.     "Blank Screen",
  119.     "Lock Screen",
  120.     "-",
  121.     "Exit",
  122.     0,
  123. };
  124. struct timeval movetimeout = {
  125.     0,
  126.     MICROSEC,
  127. };
  128. Menu *menu;
  129. char *myname;
  130. int mynamelen;
  131. int newtime;
  132. Vertex outline[5] = {
  133.     {0, 0, VertexDontDraw},
  134.     {0, 0, VertexRelative},
  135.     {0, 0, VertexRelative},
  136.     {0, 0, VertexRelative},
  137.     {0, 0, VertexRelative},
  138. };
  139. char password[] = "Password:";
  140. int passwordwidth;
  141. struct passwd *pw;
  142. int rancnt;
  143. char rootpasswd[32];
  144. int screensave = TRUE;
  145. int sin1000[SINN + 1] = {
  146.     258, 309, 358, 406, 453, 500, 544, 587, 629, 669,
  147.     707, 743, 777, 809, 838, 866, 891, 913, 933, 951,
  148.     965,
  149. };
  150. int sinindex;
  151. int signx;
  152. int signy;
  153. int spacewidth;
  154. int step;
  155. Pixmap text;
  156. int timemove;
  157. struct timeval *timeout;
  158. struct itimerval timer;
  159. char timestr[] = "00:00";
  160. int timewidth;
  161. char *tty;
  162. char user[] = "User:";
  163. int userwidth;
  164. jmp_buf verifyjmp;
  165. struct timeval verifytimeout = {
  166.     VERIFYTIMEOUT,
  167.     0,
  168. };
  169. int X_input_fd = -1;
  170. int x0;
  171. int xlast;
  172. int y0;
  173. int ylast;
  174.  
  175. extern int errno;
  176.  
  177. main(argc, argv)
  178. char **argv;
  179. {
  180.     register XEvent *ev;
  181.     register int i, maxfds, status, readfds;
  182.     register char *cp, *disp = NULL, *geom = NULL, *Xin = NULL;
  183.     register Display *display;
  184.     int rfds;
  185.     XEvent xevent;
  186.  
  187.     if(myname = rindex(argv[0], '/'))
  188.         myname++;
  189.     else
  190.         myname = argv[0];
  191.     mynamelen = strlen(myname);
  192.     if(cp = XGetDefault(myname, "AutoLock"))
  193.         if(strcmp(cp, "on") == 0)
  194.             autolock = TRUE;
  195.     if(cp = XGetDefault(myname, "BitmapFile"))
  196.         bitmap_file = cp;
  197.     if(cp = XGetDefault(myname, "Font"))
  198.         fontname = cp;
  199.     if(cp = XGetDefault(myname, "IdleTime"))
  200.         idletime = idletimeout.tv_sec = atoi(cp);
  201.     if(cp = XGetDefault(myname, "InputTrack"))
  202.         Xin = cp;
  203.     while(--argc > 0) {
  204.         if(**++argv == '-')
  205.             switch((*argv)[1]) {
  206.              case 'L':    /* login tty */
  207.                 if(--argc <= 0)
  208.                     fatal("No argument for -L");
  209.                 if(strncmp(tty = *++argv, "tty", 3) != 0)
  210.                     fatal("Illegal argument for -L");
  211.                 break;
  212.              case 'b':    /* alternate bitmap file */
  213.                 if(--argc <= 0)
  214.                     fatal("No argument for -b");
  215.                 bitmap_file = *++argv;
  216.                 break;
  217.              case 'f':    /* alternate font */
  218.                 if(--argc <= 0)
  219.                     fatal("No argument for -f");
  220.                 fontname = *++argv;
  221.                 break;
  222.              case 'i':    /* X input track file */
  223.                 if(--argc <= 0)
  224.                     fatal("No argument for -i");
  225.                 Xin = *++argv;
  226.                 break;
  227.              case 'l':    /* set auto-lock */
  228.                  autolock = TRUE;
  229.                 break;
  230.              case 't':    /* different idle time */
  231.                 if(--argc <= 0)
  232.                     fatal("No argument for -t");
  233.                 if((idletime = idletimeout.tv_sec =
  234.                  atoi(*++argv)) <= 0)
  235.                     fatal("Illegal argument for -t");
  236.                 break;
  237.              default:
  238.                 fatal("Unknown option");
  239.             }
  240.         else if(**argv == '=')
  241.             geom = *argv;
  242.         else break;
  243.     }
  244.     if(argc > 0 && index(disp = *argv, ':') == NULL)
  245.         fatal("Illegal display");
  246.     if((pw = getpwnam("root")) && *pw->pw_passwd)
  247.         strcpy(rootpasswd, pw->pw_passwd);
  248.     if(!tty) {
  249.         if((pw = getpwuid(getuid())) == NULL)
  250.             fatal("No password entry");
  251.         endpwent();
  252.     }
  253.     /*
  254.      * Let the parent exit; the child will do the real work.
  255.      */
  256. #ifndef DEBUG
  257.     if((i = fork()) < 0)
  258.         fatal("Can't fork");
  259.     if(i)    /* the parent */
  260.         exit(0);
  261. #endif DEBUG
  262.     signal(SIGINT, SIG_IGN);
  263.     signal(SIGQUIT, SIG_IGN);
  264.     nice(10);
  265.     if(tty) {
  266.         /*
  267.          * Detach the controlling tty.
  268.          */
  269.         for (i = 0; i < 10; i++)
  270.             (void) close(i);
  271.         (void) open("/", 0);
  272.         (void) dup2(0, 1);
  273.         (void) dup2(0, 2);
  274.         if((i = open("/dev/tty", 2)) >= 0) {
  275.             ioctl(i, TIOCNOTTY, 0);
  276.             (void) close(i);
  277.         }
  278.         console = NULL;
  279.         signal(SIGHUP, SIG_IGN);
  280.     }
  281. #ifdef DEBUG
  282.     freopen("ss.db", "w", stderr);
  283.     setlinebuf(stderr);
  284. #endif DEBUG
  285.     /*
  286.      * Wait until the server is active.
  287.      */
  288.     while((display = XOpenDisplay(disp)) == NULL)
  289.         sleep(10);
  290.     if(Xin)
  291.         X_input_fd = open(Xin, O_RDONLY, 0);
  292.     init(geom);
  293.     /*
  294.      * Initialize the select system call's maximum file
  295.      * descriptor number to be one more than the file descriptor
  296.      * number of the X connection.
  297.      */
  298.     maxfds = dpyno() + 1;
  299.     /*
  300.      * Use the select system call on the file descriptor in
  301.      * the display structure to determine if there is work
  302.      * to be done.  If not block until timeout.  Remember to
  303.      * reset the file descriptor before each select.
  304.      */
  305.     readfds = 1 << dpyno();
  306.     /*
  307.      * The main process loop.  After the first sleep period, we check if
  308.      * the master ends of all ptys have been written to within the idle
  309.      * period.  If not, we blank the screen.  Otherwise we calculate
  310.      * when we should wake up again to check.
  311.      */
  312.     timeout = screensave ? &idletimeout : NULL;
  313.     ev = &xevent;
  314.     XSync(TRUE);
  315.     for( ; ; ) {
  316.         if(XPending()) {
  317.             XNextEvent(ev);
  318.             switch(ev->type) {
  319.              case UnmapWindow:
  320.                 /*
  321.                  * If we are still in movetimeout mode, then
  322.                  * something unmapped use.  If we are locked
  323.                  * remap the black window and go through the
  324.                  * verification process.
  325.                  */
  326.                 if(timeout == &movetimeout) {
  327.                     if(locked) {
  328.                         XUnmapWindow(icon);
  329.                         XMapWindow(black);
  330.                     }
  331.                     restorescreen();
  332.                 }
  333.                 break;
  334.              case ButtonReleased:
  335.              case KeyPressed:
  336.                 /*
  337.                  * Restore the screen and map the icon if we are
  338.                  * in move timeout mode.
  339.                  */
  340.                 if(ev->window == black) {
  341.                     if(timeout == &movetimeout)
  342.                         restorescreen();
  343.                 } else if(ev->type != ButtonReleased ||
  344.                  (((XButtonEvent *)ev)->detail & 0xff)
  345.                  != MiddleButton) {
  346.                     XUnmapWindow(icon);
  347.                     blank(autolock);
  348.                     i = IMAGESPERSEC;
  349.                 }
  350.                 break;
  351.              case ButtonPressed:
  352.                 /*
  353.                  * For the icon, do the menu if middle button.
  354.                  */
  355.                 if(ev->window != black &&
  356.                  (((XButtonEvent *)ev)->detail & 0xff)
  357.                  == MiddleButton)
  358.                     i = domenu(ev);
  359.                 break;
  360.              case ExposeWindow:
  361.                 if(ev->window == black) {
  362.                     /*
  363.                      * If we are still doing an idle
  364.                      * timeout, a window manager must
  365.                      * have deiconified us.
  366.                      */
  367.                     if(timeout == &idletimeout) {
  368.                         blank(autolock);
  369.                         i = IMAGESPERSEC;
  370.                     }
  371.                 } else
  372.                     /*
  373.                      * refresh the icon
  374.                      */
  375.                     refreshicon();
  376.                 break;
  377.             }
  378.         } else {
  379.             rfds = readfds;
  380.             if((status = select(maxfds, &rfds, NULL, NULL, timeout))
  381.              < 0) {
  382.                 if(errno != EINTR)
  383.                     fatal("Error in select");
  384.             } else if(status == TIMEOUT) {
  385.                 if(timeout == &idletimeout) {
  386.                     if((timeout->tv_sec = tryagain()) == 0){
  387.                         XUnmapWindow(icon);
  388.                         blank(autolock);
  389.                         i = IMAGESPERSEC;
  390.                     }
  391.                 } else {
  392.                     drawimage();
  393.                     if(--i <= 0) {
  394.                         XRaiseWindow(black);
  395.                         XSync(FALSE);
  396.                         newtimestr();
  397.                         i = IMAGESPERSEC;
  398.                     }
  399.                 }
  400.             }
  401.         }
  402.     }
  403. }
  404.  
  405. /*
  406.  * init() does most of the window/bitmap initialization.
  407.  */
  408. init(geom)
  409. char *geom;
  410. {
  411.     register Bitmap pic, cur;
  412.     register int i;
  413.     register Pixmap *ip;
  414.     struct bm *head, *headam, *headpm;
  415.     register int xx, yy, compensate = FALSE;
  416.     int x = 0;
  417.     int y = 0;
  418.     int w, h;
  419.     char pmname[128];
  420.     extern int onalarm();
  421.  
  422.     /*
  423.      * Make a window the size of the root window, so that it will cover up
  424.      * the entire screen.
  425.      */
  426. #ifdef DEBUG
  427.     if((black = XCreateWindow(RootWindow, 0, 0, DisplayWidth(),
  428.      DisplayHeight() / 2, 0, (Pixmap)0, BlackPixmap)) == NULL)
  429. #else DEBUG
  430.     if((black = XCreateWindow(RootWindow, 0, 0, DisplayWidth(),
  431.      DisplayHeight(), 0, (Pixmap)0, BlackPixmap)) == NULL)
  432. #endif DEBUG
  433.         fatal("Can't create window");
  434.     XSelectInput(black, KeyPressed | ButtonPressed | ButtonReleased |
  435.      ExposeWindow | UnmapWindow);
  436.     /*
  437.      * Make an all black cursor, which will be invisible against the
  438.      * window's black tile.
  439.      */
  440.     XQueryCursorShape(CURWIDTH, CURHEIGHT, &cursor_width, &cursor_height);
  441.     if((cursor_bits = (short *)calloc((cursor_width + (BITSPERSHORT - 1))
  442.      / BITSPERSHORT * cursor_height, sizeof(short))) == NULL)
  443.         fatal("No memory for cursor");
  444.     if((cur = XStoreBitmap(cursor_width, cursor_height, cursor_bits)) ==
  445.      NULL)
  446.         fatal("Can't make cursor");
  447.     free(cursor_bits);
  448.     if((blackcursor = XStoreCursor(cur, cur, 0, 0, BlackPixel, BlackPixel,
  449.      GXcopy)) == NULL)
  450.         fatal("Can't build cursor");
  451.     XFreeBitmap(cur);
  452.     XDefineCursor(black, blackcursor);
  453.     /*
  454.      * Now get the font and the font info for the characters of the time.
  455.      */
  456.     if((finfo = XOpenFont(fontname)) == NULL)
  457.         fatal("Can't get font");
  458.     font = finfo->id;
  459.     fontheight = finfo->height + DELTA;
  460.     outline[4].y = -(outline[2].y = 5 * finfo->height + 5);
  461.     /*
  462.      * We create the image by reading in the bitmap(s) and making the
  463.      * icon the right size.  Then we draw the image in the icon and save it
  464.      * with addition black space space all around (except the top), so
  465.      * that we can just redraw the image, which will overwrite the
  466.      * previous image (no erasing is needed).
  467.      */
  468.     setupimage(bitmap_file, &bitmap_width, &bitmap_height, &headam, FALSE);
  469.     strcpy(pmname, bitmap_file);
  470.     strcat(pmname, ".pm");
  471.     if(setupimage(pmname, &w, &h, &headpm, TRUE)) {
  472.         if(w > bitmap_width)
  473.             bitmap_width = w;
  474.         if(h > bitmap_height)
  475.             bitmap_height = h;
  476.     } else
  477.         headpm = NULL;
  478.     x0 = y0 = DELTA;
  479.     /*
  480.      * This is how far to move horizontally from the upper left
  481.      * corner of the bitmap to the upper left corner of the time
  482.      * string.
  483.      */
  484.     timemove = (bitmap_width - (timewidth = XStringWidth(timestr,
  485.      finfo, 0, 0))) / 2;
  486.     halfwidth = XStringWidth("0", finfo, 0, 0) / 2;
  487.     if(timemove < 0) {
  488.         compensate = TRUE;
  489.         iconx = (x0 -= timemove);
  490.         timemove = 0;
  491.     }
  492.     /*
  493.      * The image will be a pixmap containing the bitmap and room for
  494.      * the time string.  It will be further enlarged with a black
  495.      * border so that we can simply redraw the image, which will
  496.      * overlap the image one step size back.
  497.      */
  498.     imagewidth = max(bitmap_width, timewidth) + 2 * DELTA;
  499.     imageheight = bitmap_height + DELTA;
  500.     /*
  501.      * Determine the position of the icon.
  502.      */
  503.     if(geom && (i = XParseGeometry(geom, &x, &y, &w, &h))) {
  504.         if((i & XValue) && (i & XNegative))
  505.             x = DisplayWidth() + x - imagewidth - 2 * PAD;
  506.         if((i & YValue) && (i & YNegative))
  507. #ifdef DEBUG
  508.             y = DisplayHeight() / 2 + y - imageheight - 2 * PAD -
  509.              DELTA;
  510. #else DEBUG
  511.             y = DisplayHeight() + y - imageheight - 2 * PAD - DELTA;
  512. #endif DEBUG
  513.     }
  514.     /*
  515.      * Create the icon window, positioned by the geom string, but
  516.      * compensate so that the icon is initially all on the screen.
  517.      * This is so we can make a pixmap of the image.  The window is
  518.      * then reset to what the user wants.
  519.      */
  520.     if((xx = x) < 0) {
  521.         compensate = TRUE;
  522.         xx = 0;
  523.     }
  524.     if((yy = y) < 0) {
  525.         compensate = TRUE;
  526.         yy = 0;
  527.     }
  528.     if(xx > (i = DisplayWidth() - imagewidth - 2 * PAD)) {
  529.         compensate = TRUE;
  530.         xx = i;
  531.     }
  532.     if(yy > (i = DisplayHeight() - imageheight - 2 * PAD)) {
  533.         compensate = TRUE;
  534.         yy = i;
  535.     }
  536.     if((icon = XCreateWindow(RootWindow, xx, yy, imagewidth, imageheight +
  537.      DELTA, PAD, WhitePixmap, BlackPixmap)) == NULL)
  538.         fatal("Can't create icon window");
  539.     if(arrow = XCreateCursor(arrow_width, arrow_height, arrow_bits,
  540.      arrowmask_bits, arrow_x_hot, arrow_y_hot, BlackPixel, WhitePixel,
  541.      GXcopy))
  542.         XDefineCursor(icon, arrow);
  543.     /*
  544.      * Grab the server to make sure we get the icon done properly.
  545.      */
  546. #ifndef DEBUG
  547.     XGrabServer();
  548. #endif DEBUG
  549.     XMapWindow(icon);
  550.     if(headpm) {
  551.         signal(SIGALRM, SIG_IGN);
  552.         seticonalarm();
  553.         if(amicon) {
  554.             head = headpm;
  555.             ip = &imagepm;
  556.         } else {
  557.             head = headam;
  558.             ip = ℑ
  559.         }
  560.         drawbm(head);
  561.         *ip = XPixmapSave(icon, 0, DELTA, imagewidth, imageheight);
  562.         XClear(icon);
  563.         if(i = amicon) {
  564.             head = headam;
  565.             ip = ℑ
  566.         } else {
  567.             head = headpm;
  568.             ip = &imagepm;
  569.         }
  570.     } else {
  571.         head = headam;
  572.         ip = ℑ
  573.     }
  574.     drawbm(head);
  575.     *ip = XPixmapSave(icon, 0, DELTA, imagewidth, imageheight);
  576.     if(!image && !imagepm)
  577.         fatal("Can't make image");
  578.     if(!image) {
  579.         image = imagepm;
  580.         imagepm = NULL;
  581.     }
  582. #ifndef DEBUG
  583.     XUngrabServer();
  584. #endif DEBUG
  585.     XSelectInput(icon, ExposeWindow | ButtonPressed | ButtonReleased);
  586.     XSetIconWindow(black, icon);
  587.     if(compensate) {
  588.         XConfigureWindow(icon, x, y, bitmap_width + 2 * DELTA,
  589.          bitmap_height + 2 * DELTA);
  590.         refreshicon();
  591.     }
  592.     signal(SIGALRM, onalarm);
  593.     if(imagepm) {
  594.         seticonalarm();
  595.         if(i != amicon)
  596.             refreshicon();
  597.     }
  598.     /*
  599.      * The area is the size on the rectangle that the image will
  600.      * bounce in. Since everything is with reference to the upper
  601.      * left corner, all we need to do is prevent the upper left
  602.      * corner of the image from going outside the area bounds.
  603.      */
  604.     areawidth = DisplayWidth() - imagewidth;
  605. #ifdef DEBUG
  606.     areaheight = DisplayHeight() / 2 - imageheight - fontheight;
  607. #else DEBUG
  608.     areaheight = DisplayHeight() - imageheight - fontheight;
  609. #endif DEBUG
  610.     spacewidth = XStringWidth("n", finfo, 0, 0);
  611.     passwordwidth = XStringWidth(password, finfo, 0, 0) + 2 * spacewidth;
  612.     userwidth = XStringWidth(user, finfo, 0, 0) + spacewidth;
  613.     signx = (random() & 1) ? 1 : -1;
  614.     signy = (random() & 1) ? 1 : -1;
  615. }
  616.  
  617. drawbm(bp)
  618. register struct bm *bp;
  619. {
  620.     register struct bm *np;
  621.     register Bitmap mask;
  622.  
  623.     if(bp->bits == NULL) {    /* default */
  624.         XPixSet(icon, x0, y0, DEFAULTWIDTH, DEFAULTHEIGHT, WhitePixel);
  625.         XPixSet(icon, x0 + PAD, y0 + PAD, DEFAULTWIDTH - 2 * PAD,
  626.          DEFAULTHEIGHT - 2 * PAD, BlackPixel);
  627.         free((char *)bp);
  628.         return;
  629.     }
  630.     while(bp) {
  631.         if(mask = XStoreBitmap(bp->width, bp->height, bp->bits)) {
  632.             XPixFill(icon, x0 + bp->x, y0 + bp->y, bp->width,
  633.              bp->height, bp->color, mask, GXcopy, AllPlanes);
  634.             XFreeBitmap(mask);
  635.         }
  636.         np = bp->next;
  637.         free((char *)bp);
  638.         bp = np;
  639.     }
  640. }
  641.  
  642. /*
  643.  * restorescreen() unmaps the black window, maps the icon window and sets the
  644.  * idle timeout.  If locked, then verify password.
  645.  */
  646. restorescreen()
  647. {
  648.     if(locked) {
  649.         if(verify()) {
  650.             XUngrabMouse();
  651.             XSync(TRUE);
  652.             locked = FALSE;
  653.         } else
  654.             return;    /* still blanked */
  655.     }
  656.     XFreePixmap(text);
  657.     XUnmapWindow(black);
  658.     XMapWindow(icon);
  659.     idletimeout.tv_sec = idletime;
  660.     timeout = screensave ? &idletimeout : NULL;
  661.     if(imagepm)
  662.         seticonalarm();
  663. }
  664.  
  665. /*
  666.  * refreshicon() draws the icon in the icon window.
  667.  */
  668. refreshicon()
  669. {
  670.     newtimestr();
  671.     XPixmapPut(icon, iconx, 0, DELTA, DELTA, bitmap_width, bitmap_height,
  672.      (imagepm && !amicon) ? imagepm : image, GXcopy, AllPlanes);
  673. }
  674.  
  675. /*
  676.  * domenu() handles the icon menu.
  677.  */
  678. domenu(reply)
  679. register XKeyOrButtonEvent *reply;
  680. {
  681.     register char **ptr;
  682.     register int item, l = autolock;
  683.     static int inited;
  684.     static int wasscreensave;
  685.     static int wasautolock;
  686.     extern int justexpose();
  687.  
  688.     if(!inited) {
  689.         extern Cursor Menu_DefaultCursor;
  690.  
  691.         inited++;
  692.         InitMenu(myname);
  693.         if(arrow)
  694.             Menu_DefaultCursor = arrow;
  695.     }
  696.     if (menu == NULL) {
  697.         if ((menu = NewMenu("ScreenSaver", 0)) == NULL)
  698.             fatal("Can't create menu");
  699.         for(ptr = mtext ; *ptr ; ptr++)
  700.             AddMenuItem(menu, *ptr);
  701.         if(wasscreensave = screensave)
  702.             CheckItem(menu, MENU_ONOFF);
  703.         if(wasautolock = autolock)
  704.             CheckItem(menu, MENU_AUTOLOCK);
  705.         DisableItem(menu, MENU_LINE);
  706.         DisableItem(menu, MENU_LINE2);
  707.     } else {
  708.         if(wasscreensave != screensave)
  709.             SetItemCheck(menu, MENU_ONOFF, (wasscreensave =
  710.              screensave));
  711.         if(wasautolock != autolock)
  712.             SetItemCheck(menu, MENU_AUTOLOCK, (wasautolock =
  713.              autolock));
  714.     }
  715.     SetMenuEventHandler(menu, justexpose);
  716.     if((item = TrackMenu(menu, reply)) < 0)
  717.         return(0);
  718.     switch (item) {
  719.      case MENU_ONOFF:
  720.         timeout = (screensave = !screensave) ? &idletimeout : NULL;
  721.         return(0);
  722.      case MENU_AUTOLOCK:
  723.         autolock = !autolock;
  724.         return(0);
  725.      case MENU_LOCK:
  726.         l = TRUE;
  727.          /* drop through */
  728.      case MENU_BLANK:
  729.         timeout = &idletimeout;
  730.         XUnmapWindow(icon);
  731.         blank(l);
  732.         return(IMAGESPERSEC);
  733.      case MENU_EXIT:
  734.         exit(0);
  735.     }
  736. }
  737.  
  738. /*
  739.  * justexpose() just handles exposure events for the icon when doing the menu.
  740.  */
  741. justexpose(ev)
  742. XEvent *ev;
  743. {
  744.     if(ev->type == ExposeWindow)
  745.         refreshicon();
  746. }
  747.  
  748. /*
  749.  * tryagain() finds the most recently written to master end of a pty.  If
  750.  * the idle time has since elapsed, zero is returned.  Otherwise, it returns
  751.  * the amount of time to wait until checking again.  If X_input_fd >= 0,
  752.  * then check this file descriptor only (assumed that the server is modifying
  753.  * this file everytime it gets input).
  754.  */
  755. tryagain()
  756. {
  757.     static int idlecount = 0;
  758.     static char group[] = "pqrstuvw";
  759.     static char single[] = "0123456789abcdef";
  760.     static char pty[] = "/dev/pty??";
  761.     register char *g, *s;
  762.     register long ltime;
  763.     register int i, mintime;
  764.     struct stat sbuf;
  765.  
  766.     ltime = time((long *)0);
  767.     if(X_input_fd >= 0 && fstat(X_input_fd, &sbuf) == 0)
  768.         mintime = ltime - sbuf.st_ctime;
  769.     else {
  770.         mintime = 2 * idletime;
  771.         for(g = group ; *g ; g++) {
  772.             pty[8] = *g;
  773.             for(s = single ; *s ; s++) {
  774.                 pty[9] = *s;
  775.                 if(stat(pty, &sbuf) < 0)
  776.                     break;    /* skip rest of sequence */
  777.                 if((i = ltime - sbuf.st_mtime) < 0) {
  778.                     if(-i > idletime)
  779.                         continue;
  780.                     i = 0;
  781.                 }
  782.                 if(i < mintime)
  783.                     mintime = i;
  784.             }
  785.         }
  786.     }
  787.     return((i = idletime - mintime) <= 0 ? 0 : i);
  788. }
  789.  
  790. /*
  791.  * blank() maps a black window over the screen and sets up the image and
  792.  * time string.  If wantlock is TRUE, locking will occur if checklogin() also
  793.  * returns TRUE.
  794.  */
  795. blank(wantlock)
  796. int wantlock;
  797. {
  798.     register int i;
  799.  
  800.     /*
  801.      * Turn off icon alarm, if on.
  802.      */
  803.     if(imagepm) {
  804.         bzero((char *)&timer, sizeof(timer));
  805.         setitimer(ITIMER_REAL, &timer, (struct itimerval *)NULL);
  806.     }
  807.     /*
  808.      * Get rid of any left over events.
  809.      */
  810.     XSync(TRUE);
  811.     /*
  812.      * Map the window.
  813.      */
  814.     XMapWindow(black);
  815.     /*
  816.      * Set locked if requested and someone is logged in.  If then, we can't
  817.      * grab the mouse, return.
  818.      */
  819.     if(wantlock && (locked = checklogin()) &&
  820.      !XGrabMouse(black, blackcursor, KeyPressed | ButtonPressed |
  821.      ButtonReleased | ExposeWindow | UnmapWindow)) {
  822.         XUnmapWindow(black);
  823.         XMapWindow(icon);
  824.         (timeout = &idletimeout)->tv_sec = idletime;
  825.         return;
  826.     }
  827.     /*
  828.      * Pick a random place on the screen to start.
  829.      */
  830.     x0 = random() % areawidth;
  831.     y0 = random() % areaheight;
  832.     /*
  833.      * Initialize and then pick a random direction to go in.
  834.      */
  835.     xlast = x0;
  836.     ylast = y0;
  837.     rancnt = 0;
  838.     step = 0;
  839.     newdirection(random() % NWALLS);
  840.     /*
  841.      * Make a Pixmap of the time.
  842.      */
  843.     newtimestr();
  844.     XRaiseWindow(black);    /* make sure we're on top */
  845.     XText(black, x0 + timemove + center + DELTA, y0 + DELTA, timestr,
  846.      strlen(timestr), font, WhitePixel, BlackPixel);
  847.     if((text = XPixmapSave(black, x0, y0, imagewidth, fontheight)) ==
  848.      (Pixmap)0)
  849.         fatal("Can't make time image");
  850.     newtime = 0;
  851.     XPixmapPut(black, 0, 0, x0, y0 + fontheight, imagewidth, imageheight,
  852.      (imagepm && !am) ? imagepm : image, GXcopy, AllPlanes);
  853.     timeout = &movetimeout;
  854. }
  855.  
  856. /*
  857.  * setupimage() reads in the bitmap(s) and draws it on the screen at x0, y0.
  858.  * The width and height of the image is returned.  If nodefault is set, zero
  859.  * is returned if the file can't be opened.
  860.  */
  861. setupimage(file, width, height, head, nodefault)
  862. char *file;
  863. int *width, *height;
  864. struct bm **head;
  865. int nodefault;
  866. {
  867.     register FILE *fp;
  868.     register int totalw, totalh;
  869.     register char *cp;
  870.     register struct bm **bp = head;
  871.     int w, h, x, y;
  872.     short *bits;
  873.     char color[64];
  874.     char name[128];
  875.     char line[BUFSIZ];
  876.     Color col;
  877.  
  878.     /*
  879.      * Read the bitmap file into bitmap_bits.
  880.      */
  881.     switch(XReadBitmapFile(file, &w, &h, &bits, NULL, NULL)) {
  882.      case BITMAPOPENERR:
  883. defaultimage:
  884.         if(nodefault)
  885.             return(FALSE);
  886.         if((*bp = (struct bm *)calloc(1, sizeof(struct bm))) == NULL)
  887.             fatal("Out of memory for bitmap structure");
  888.         *width = (*bp)->width = DEFAULTWIDTH;
  889.         *height = (*bp)->height = DEFAULTHEIGHT;
  890.         (*bp)->color = WhitePixel;
  891.         return(TRUE);
  892.      case BITMAPOK:
  893.         if((*bp = (struct bm *)calloc(1, sizeof(struct bm))) == NULL)
  894.             fatal("Out of memory for bitmap structure");
  895.         *width = (*bp)->width = w;
  896.         *height = (*bp)->height = h;
  897.         (*bp)->bits = bits;
  898.         (*bp)->color = WhitePixel;
  899.         return(TRUE);
  900.     }
  901.     /*
  902.      * Assume that the file is a bitmap list file.
  903.      */
  904.     if((fp = fopen(file, "r")) == NULL)
  905.         goto defaultimage;
  906.     totalh = totalw = 0;
  907.     strcpy(name, file);
  908.     if(cp = rindex(name, '/'))
  909.         cp++;
  910.     else
  911.         cp = name;
  912.     while(fgets(line, sizeof(line), fp)) {
  913.         if(*line == '#')    /* comment */
  914.             continue;
  915.         if(sscanf(line, "%s %s %d %d", cp, color, &x, &y) != 4)
  916.             continue;
  917.         if(XReadBitmapFile(name, &w, &h, &bits, NULL, NULL) != BITMAPOK)
  918.             continue;
  919.         if((*bp = (struct bm *)calloc(1, sizeof(struct bm))) == NULL)
  920.             fatal("Out of memory for bitmap structure");
  921.         (*bp)->x = x;
  922.         (*bp)->y = y;
  923.         (*bp)->width = w;
  924.         (*bp)->height = h;
  925.         (*bp)->bits = bits;
  926.         if(DisplayCells() > 2) {
  927.             if(!XParseColor(color, &col) ||
  928.              !XGetHardwareColor(&col))
  929.                 col.pixel = WhitePixel;
  930.         } else
  931.             col.pixel = (strcmp(color, "black") == 0) ? BlackPixel :
  932.              WhitePixel;
  933.         (*bp)->color = col.pixel;
  934.         if((w += x) > totalw)
  935.             totalw = w;
  936.         if((h += y) > totalh)
  937.             totalh = h;
  938.         bp = &(*bp)->next;
  939.     }
  940.     fclose(fp);
  941.     if(totalw <= 0 || totalh <= 0) {
  942.         if(bp != head)
  943.             freebm(*head);
  944.         goto defaultimage;
  945.     }
  946.     *width = totalw;
  947.     *height = totalh;
  948.     return(TRUE);
  949. }
  950.  
  951. freebm(bp)
  952. register struct bm *bp;
  953. {
  954.     register struct bm *np;
  955.  
  956.     while(bp) {
  957.         np = bp->next;
  958.         free((char *)bp);
  959.         bp = np;
  960.     }
  961. }
  962.  
  963. /*
  964.  * drawimage draws the actual image, after each timeout.
  965.  */
  966. drawimage()
  967. {
  968.     register int x, y;
  969.  
  970.     /*
  971.      * Step along in the current direction.  If we hit an edge,
  972.      * calculate another direction to go in (bounce off).  The loop
  973.      * is just in case we are in a corner and would go off in the
  974.      * wrong direction.
  975.      */
  976.     for( ; ; step = 0) {
  977.         step += DELTA;
  978.         x = xmove(step);
  979.         y = ymove(step);
  980.         if(x < 0) {
  981.             newdirection(LEFT);
  982.             continue;
  983.         }
  984.         if(y < 0) {
  985.             newdirection(TOP);
  986.             continue;
  987.         }
  988.         if(x >= areawidth) {
  989.             newdirection(RIGHT);
  990.             continue;
  991.         }
  992.         if(y >= areaheight) {
  993.             newdirection(BOTTOM);
  994.             continue;
  995.         }
  996.         break;
  997.     }
  998.     /*
  999.      * Draw the image and the time string.
  1000.      */
  1001.     xlast = x;
  1002.     ylast = y;
  1003.     if(newtime) {
  1004.         XPixSet(black, x, y, imagewidth, fontheight, BlackPixel);
  1005.         XText(black, x + timemove + center + DELTA, y + DELTA, timestr,
  1006.          strlen(timestr), font, WhitePixel, BlackPixel);
  1007.         XFreePixmap(text);
  1008.         if((text = XPixmapSave(black, x, y, imagewidth, fontheight)) ==
  1009.          (Pixmap)0)
  1010.             fatal("Can't make time image");
  1011.         newtime = 0;
  1012.     } else
  1013.         XPixmapPut(black, 0, 0, x, y, imagewidth, fontheight, text,
  1014.          GXcopy, AllPlanes);
  1015.     XPixmapPut(black, 0, 0, x, y + fontheight, imagewidth, imageheight,
  1016.      (imagepm && !am) ? imagepm : image, GXcopy, AllPlanes);
  1017. }
  1018.  
  1019. /*
  1020.  * newdirection() calculates a random direction to go in, after encountering
  1021.  * a wall.  sinindex is the index into the sin1000[] array, which is used to
  1022.  * calculate the sin and cosine of the trajectory.  Since sin1000[] is for
  1023.  * quadrant one, we have signx and signy to move in all four quadrants.
  1024.  */
  1025. newdirection(wall)
  1026. {
  1027.     if(rancnt-- <= 0) {
  1028.         sinindex = random() % SINN;
  1029.         rancnt = random() % RANCNTMAX;
  1030.     }
  1031.     switch(wall) {
  1032.      case LEFT:
  1033.         signx = 1;
  1034.         break;
  1035.      case TOP:
  1036.         signy = 1;
  1037.         break;
  1038.      case RIGHT:
  1039.         signx = -1;
  1040.         break;
  1041.      case BOTTOM:
  1042.         signy = -1;
  1043.         break;
  1044.     }
  1045.     x0 = xlast;
  1046.     y0 = ylast;
  1047. }
  1048.  
  1049. /*
  1050.  * newtimestr() makes a new time string.
  1051.  */
  1052. newtimestr()
  1053. {
  1054.     register struct tm *tp;
  1055.     register int hour;
  1056.     static int hr = -1;
  1057.     static int minute = -1;
  1058.     long t;
  1059.  
  1060.     time(&t);
  1061.     tp = localtime(&t);
  1062.     am = 1;
  1063.     if((hour = tp->tm_hour) >= 12) {
  1064.         if(hour > 12)
  1065.             hour -= 12;
  1066.         am = 0;
  1067.     } else if(hour == 0)
  1068.         hour = 12;
  1069.     if(tp->tm_min == minute && hr == hour)
  1070.         return;
  1071.     sprintf(timestr, "%d:%02d", hr = hour, minute = tp->tm_min);
  1072.     center = hr < 10 ? halfwidth : 0;
  1073.     newtime++;
  1074. }
  1075.  
  1076. /*
  1077.  * fatal() prints an error message, either to the stderr or to /dev/console
  1078.  * and then exits.
  1079.  */
  1080. fatal(str)
  1081. char *str;
  1082. {
  1083.     if(console || (console = fopen("/dev/console", "w")))
  1084.         fprintf(console, "%s: %s\n", myname, str);
  1085.     exit(1);
  1086. }
  1087.  
  1088. /*
  1089.  * verify() displays a password dialog box and if the typed in password is
  1090.  * correct, returns TRUE.  If the password is not completed in VERIFYTIMEOUT
  1091.  * seconds the box disappears and FALSE is returned.  But is checklogin()
  1092.  * returns FALSE, we return TRUE so that we allow new logins.
  1093.  */
  1094. verify()
  1095. {
  1096.     register char *cp, *bp;
  1097.     register int fh, x, y, w, h, x0, y0, status = FALSE;
  1098.     int i;
  1099.     char pstr[32];
  1100.     XEvent ev;
  1101.     char *crypt();
  1102.  
  1103. #ifdef DEBUG
  1104.     fputs("In verify()\n", stderr);
  1105. #endif DEBUG
  1106.     if(!checklogin())
  1107.         return(TRUE);
  1108. #ifndef DEBUG
  1109.     XGrabServer();
  1110. #else DEBUG
  1111.     fputs("checklogin returns TRUE\n", stderr);
  1112. #endif DEBUG
  1113.     if((i = XStringWidth(pw->pw_name, finfo, 0, 0) + userwidth)
  1114.      < passwordwidth)
  1115.         i = passwordwidth;
  1116.     w = i + 2 * (fh = finfo->height);
  1117.     h = 5 * fh;
  1118.     x0 = (DisplayWidth() - w) / 2;
  1119. #ifdef DEBUG
  1120.     y0 = (DisplayHeight() / 2 - h) / 2;
  1121. #else DEBUG
  1122.     y0 = (DisplayHeight() - h) / 2;
  1123. #endif DEBUG
  1124.     XRaiseWindow(black);
  1125.     XPixSet(black, x0, y0, w, h, WhitePixel);
  1126.     outline[0].x = x0 - 3;
  1127.     outline[0].y = y0 - 3;
  1128.     outline[3].x = -(outline[1].x = w + 5);
  1129.     XDraw(black, outline, 5, 1, 1, WhitePixel, GXcopy, AllPlanes);
  1130.     XTextMask(black, x = x0 + fh, y = y0 + fh, user, USERLEN,
  1131.      font, BlackPixel);
  1132.     XTextMask(black, x + userwidth, y, pw->pw_name, strlen(pw->pw_name),
  1133.      font, BlackPixel);
  1134.     XTextMask(black, x, y += 2 * fh, password, PASSWORDLEN, font,
  1135.      BlackPixel);
  1136.     XPixSet(black, x + passwordwidth - spacewidth, y, spacewidth,
  1137.      fh, BlackPixel);
  1138.     XSync(TRUE);
  1139.     bzero((char *)&timer, sizeof(timer));
  1140.     timer.it_value = verifytimeout;
  1141.     setitimer(ITIMER_REAL, &timer, (struct itimerval *)NULL);
  1142.     if(setjmp(verifyjmp) == 0) {
  1143.         for(cp = pstr, x = sizeof(pstr) - 1 ; ; ) {
  1144.             XNextEvent(&ev);
  1145.             if(ev.type != KeyPressed)
  1146.                 continue;
  1147.             bp = XLookupMapping(&ev, &i);
  1148.             if(i <= 0)
  1149.                 continue;
  1150.             while(i-- > 0) {
  1151.                 if(*bp == '\r' || *bp == '\n') {
  1152.                     *cp = 0;
  1153.                     if(!(status = (strcmp(pw->pw_passwd,
  1154.                      crypt(pstr, pw->pw_passwd)) == 0)) &&
  1155.                      *rootpasswd)
  1156.                         status = (strcmp(rootpasswd,
  1157.                          crypt(pstr, rootpasswd)) == 0);
  1158. #ifdef DEBUG
  1159.                     fprintf(stderr, "password %s\n",
  1160.                      status ? "matches" : "doesn't match");
  1161. #endif DEBUG
  1162.                     bp = NULL;    /* break out */
  1163.                     break;
  1164.                 }
  1165.                 if(x > 0) {
  1166.                     *cp++ = *bp;
  1167.                     x--;
  1168.                 }
  1169.                 bp++;
  1170.             }
  1171.             if(bp == NULL)
  1172.                 break;
  1173.         }
  1174.     }
  1175.     bzero((char *)&timer, sizeof(timer));
  1176.     setitimer(ITIMER_REAL, &timer, (struct itimerval *)NULL);
  1177.     XPixSet(black, x0 - 3, y0 - 3, w + 6, h + 6, BlackPixel);
  1178. #ifndef DEBUG
  1179.     XUngrabServer();
  1180. #endif DEBUG
  1181.     XSync(TRUE);
  1182.     return(status);
  1183. }
  1184.  
  1185. /*
  1186.  * onalarm() is called when the VERIFYTIMEOUT period has expired.
  1187.  */
  1188. onalarm()
  1189. {
  1190.     if(timeout == &movetimeout) {
  1191.         if(timer.it_value.tv_sec == 0)    /* stray alarm */
  1192.             return;
  1193. #ifdef DEBUG
  1194.         fputs("In onalarm() verify\n", stderr);
  1195. #endif DEBUG
  1196.         longjmp(verifyjmp, TRUE);
  1197.     } else if(imagepm) {
  1198.         seticonalarm();
  1199.         refreshicon();
  1200.     }
  1201. }
  1202.  
  1203. /*
  1204.  * checklogin() sees if someone is logged into tty.  If tty is not set,
  1205.  * TRUE is always returned.
  1206.  */
  1207. checklogin()
  1208. {
  1209.     register FILE *fp;
  1210.     struct utmp utmp;
  1211.  
  1212. #ifdef DEBUG
  1213.     fputs("In checklogin()\n", stderr);
  1214. #endif DEBUG
  1215.     if(!tty)
  1216.         return(TRUE);
  1217.     if((fp = fopen("/etc/utmp", "r")) == NULL)
  1218.         return(FALSE);
  1219.     for( ; ; ) {
  1220.         if(fread((char *)&utmp, sizeof(utmp), 1, fp) <= 0) {
  1221.             fclose(fp);
  1222.             return(FALSE);
  1223.         }
  1224.         if(strcmp(tty, utmp.ut_line) == 0) {
  1225.             fclose(fp);
  1226.             if(*utmp.ut_name)    /* someone logged in */
  1227. #ifdef DEBUG
  1228.             {
  1229.                 fprintf(stderr, "%s logged in\n", utmp.ut_name);
  1230.                 break;
  1231.             }
  1232. #else DEBUG
  1233.                 break;
  1234. #endif DEBUG
  1235.             return(FALSE);
  1236.         }
  1237.     }
  1238.     pw = getpwnam(utmp.ut_name);
  1239.     endpwent();
  1240.     return(pw != NULL && *pw->pw_passwd);
  1241. }
  1242.  
  1243. /*
  1244.  * seticonalarm() sets an alarm signal to redraw the icon (am <-> pm).
  1245.  */
  1246. seticonalarm()
  1247. {
  1248.     register struct tm *tp;
  1249.     struct itimerval itimer;
  1250.     long t;
  1251.  
  1252.     time(&t);
  1253.     tp = localtime(&t);
  1254.     am = 1;
  1255.     if(tp->tm_hour >= 12) {
  1256.         tp->tm_hour -= 12;
  1257.         amicon = FALSE;
  1258.     } else
  1259.         amicon = TRUE;
  1260.     bzero((char *)&itimer, sizeof(itimer));
  1261.     itimer.it_value.tv_sec = 60 * (60 * (12 - tp->tm_hour) - tp->tm_min) -
  1262.      tp->tm_sec;
  1263.     setitimer(ITIMER_REAL, &itimer, (struct itimerval *)NULL);
  1264. }
  1265.