home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / catclock / xclock.c < prev   
Encoding:
C/C++ Source or Header  |  1995-05-03  |  62.5 KB  |  2,577 lines

  1. /*  $Header: xclock.c,v 1.1 93/04/14 10:48:02 iv Locked $ */
  2. /*  Copyright 1985 Massachusetts Institute of Technology */
  3.  
  4. /*
  5.  *  xclock.c MIT Project Athena, X Window system clock.
  6.  *
  7.  *  This program provides the user with a small
  8.  *  window contining a digital clock with day and date.
  9.  *  Parameters are variable from the command line.
  10.  *
  11.  *   Author:    Tony Della Fera, DEC
  12.  *        September, 1984
  13.  *   Hacked up by a cast of thousands....
  14.  *
  15.  *   Ported to X11 & Motif :
  16.  *    Philip J. Schneider, DEC
  17.  *      1990
  18.  */
  19. #include <stdio.h>
  20. #include <malloc.h>
  21. #include <math.h>
  22. #include <pwd.h>
  23. #include <unistd.h>
  24. #include <time.h>
  25.  
  26.  
  27. /*
  28.  *  X11 includes
  29.  */
  30. #include <X11/Xlib.h>
  31. #include <X11/Xos.h>
  32. #include <X11/Xutil.h>
  33. #include <X11/cursorfont.h>
  34. #include <X11/Intrinsic.h>
  35. #include <X11/Shell.h>
  36. #include <X11/StringDefs.h>
  37.  
  38. /*
  39.  *  Motif includes
  40.  */
  41. #include <Xm/Xm.h>
  42. #include <Xm/Form.h>
  43. #include <Xm/DrawingA.h>
  44. #include <Xm/PushB.h>
  45. #include <Xm/ToggleB.h>
  46. #include <Xm/MenuShell.h>
  47. #include <Xm/RowColumn.h>
  48. #include <Xm/Separator.h>
  49.  
  50. #include "alarm.h"
  51.  
  52. /*
  53.  *  Cat bitmap includes
  54.  */
  55. #include "catback.xbm"
  56. #include "catwhite.xbm"
  57. #include "cattie.xbm"
  58. #include "eyes.xbm"    
  59. #include "tail.xbm"
  60.  
  61. /*
  62.  *  Icon pixmap includes
  63.  */
  64. #include "analog.xbm"
  65. #include "digital.xbm"
  66. #include "cat.xbm"
  67.  
  68. /*
  69.  *  Patch info
  70.  */
  71. #include "patchlevel.h"
  72.  
  73.  
  74. /*
  75.  *  Clock Mode -- type of clock displayed
  76.  */
  77. #define ANALOG_CLOCK    0        /*  Ye olde X10 xclock face    */
  78. #define DIGITAL_CLOCK    1        /*  ASCII clock            */
  79. #define CAT_CLOCK    2        /*  KitCat (R) clock face    */
  80.  
  81. static int    clockMode;        /*  One of the above :-)    */
  82.  
  83.  
  84.  
  85. /*
  86.  *  Cat body part pixmaps
  87.  */
  88. static Pixmap    *eyePixmap  = (Pixmap *)NULL;    /*  Array of eyes    */
  89. static Pixmap    *tailPixmap = (Pixmap *)NULL;    /*  Array of tails    */
  90.  
  91. /*
  92.  *  Cat GC's
  93.  */
  94. static GC    catGC;            /*  For drawing cat's body    */
  95. static GC    tailGC;            /*  For drawing cat's tail    */
  96. static GC    eyeGC;            /*  For drawing cat's eyes    */
  97.  
  98. /*
  99.  *  Default cat dimension stuff -- don't change sizes!!!!
  100.  */
  101. #define DEF_N_TAILS        16    /*  Default resolution        */
  102. #define TAIL_HEIGHT         100    /*  Tail pixmap height        */
  103. #define DEF_CAT_WIDTH        150    /*  Cat body pixmap width    */
  104. #define DEF_CAT_HEIGHT        300    /*  Cat body pixmap height    */
  105. #define DEF_CAT_BOTTOM        210    /*  Distance to cat's butt    */
  106.  
  107.  
  108. /*
  109.  *  Analog clock width and height
  110.  */
  111. #define DEF_ANALOG_WIDTH    164    /*  Chosen for historical    */
  112. #define DEF_ANALOG_HEIGHT    164    /*  reasons :-)            */
  113.  
  114. /*
  115.  *  Digital time string origin
  116.  */
  117. static int     digitalX, digitalY;
  118.  
  119.  
  120. /*
  121.  *  Clock hand stuff
  122.  */
  123. #define VERTICES_IN_HANDS    4    /*  Hands are triangles        */
  124. #define SECOND_HAND_FRACT    90    /*  Percentages of radius    */
  125. #define MINUTE_HAND_FRACT    70    
  126. #define HOUR_HAND_FRACT        40
  127. #define HAND_WIDTH_FRACT    7
  128. #define SECOND_WIDTH_FRACT    5
  129.  
  130. #define SECOND_HAND_TIME    30    /*  Update limit for second hand*/
  131.  
  132. static int     centerX = 0;        /*  Window coord origin of    */
  133. static int     centerY = 0;        /*  clock hands.        */
  134.  
  135. static int     radius;            /*  Radius of clock face    */
  136.  
  137. static int     secondHandLength;    /*  Current lengths and widths    */
  138. static int     minuteHandLength;
  139. static int     hourHandLength;
  140. static int     handWidth;    
  141. static int     secondHandWidth;
  142.  
  143. #define SEG_BUFF_SIZE        128        /*  Max buffer size    */
  144. static int     numSegs = 0;            /*  Segments in buffer    */
  145. static XPoint     segBuf[SEG_BUFF_SIZE];        /*  Buffer        */
  146. static XPoint     *segBufPtr = (XPoint *)NULL;    /*  Current pointer    */
  147.  
  148.  
  149.  
  150. /*
  151.  *  Default font for digital display
  152.  */
  153. #define DEF_DIGITAL_FONT    "fixed"        
  154.  
  155.  
  156. /*
  157.  *  Padding defaults
  158.  */
  159. #define DEF_DIGITAL_PADDING    10    /*  Space around time display    */
  160. #define DEF_ANALOG_PADDING    8    /*  Radius padding for analog    */
  161.  
  162.  
  163. /*
  164.  *  Alarm stuff
  165.  */
  166. #define DEF_ALARM_FILE        "/.Catclock"    /*  Alarm settings file    */
  167. #define DEF_ALARM_PERIOD    60        
  168.  
  169. static Boolean     alarmOn    = False;    /*  Alarm set?            */
  170. static Boolean     alarmState = False;    /*  Seems to be unused        */
  171. static char     alarmBuf[BUFSIZ];    /*  Path name to alarm file    */
  172.  
  173.  
  174. /*
  175.  *  Time stuff
  176.  */
  177. static struct tm tm;            /*  What time is it?        */
  178. static struct tm otm;            /*  What time was it?        */
  179.  
  180.  
  181.  
  182. /*
  183.  *  X11 Stuff
  184.  */
  185. static Window        clockWindow = (Window)NULL;
  186. static XtAppContext      appContext;
  187.        Display         *dpy;
  188.        Window         root;
  189.        int         screen;
  190.  
  191.        GC    gc;            /*  For tick-marks, text, etc.    */
  192. static GC    handGC;            /*  For drawing hands        */
  193. static GC    eraseGC;        /*  For erasing hands        */
  194. static GC    highGC;            /*  For hand borders        */
  195.  
  196.  
  197. #define UNINIT            -1
  198. static int     winWidth  = UNINIT;    /*  Global window width        */
  199. static int    winHeight = UNINIT;    /*  Global window height    */
  200.  
  201.  
  202. /*
  203.  *  Resources user can set in addition to normal Xt resources
  204.  */
  205. typedef struct {
  206.     XFontStruct        *font;            /*  For alarm & analog    */
  207.  
  208.     Pixel        foreground;        /*  Foreground        */
  209.     Pixel        background;        /*  Background        */
  210.  
  211.     Pixel        highlightColor;        /*  Hand outline/border    */
  212.     Pixel        handColor;        /*  Hand interior    */
  213.     Pixel        catColor;        /*  Cat's body        */
  214.     Pixel        detailColor;        /*  Cat's paws, belly,    */
  215.                         /*  face, and eyes.    */
  216.     Pixel        tieColor;        /*  Cat's tie color    */
  217.     int            nTails;            /*  Tail/eye resolution    */
  218.  
  219.     int            padding;        /*  Font spacing    */
  220.     char        *modeString;        /*  Display mode    */
  221.  
  222.     int            update;            /*  Seconds between    */
  223.                             /*  updates        */
  224.     Boolean        alarmSet;        /*  Alarm on?        */
  225.     Boolean        alarmBell;        /*  Audible alarm?    */
  226.     char        *alarmFile;        /*  Alarm setting file    */
  227.     int            alarmBellPeriod;    /*  How long to ring    */
  228.                         /*  alarm        */
  229.     Boolean        chime;            /*  Chime on hour?    */
  230.  
  231.     int            help;            /*  Display syntax    */
  232.  
  233. } ApplicationData, *ApplicationDataPtr;
  234.  
  235. static ApplicationData appData;
  236.  
  237.  
  238.  
  239. /*
  240.  *  Miscellaneous stuff
  241.  */
  242. #define TWOPI            (2.0 * M_PI)        /*  2.0 * M_PI    */
  243.  
  244. #define DEF_UPDATE        60
  245.  
  246. #define min(a, b)         ((a) < (b) ? (a) : (b))
  247. #define max(a, b)        ((a) > (b) ? (a) : (b))
  248.  
  249. static Boolean    noSeconds      = False;       /*  Update time >= 1 minute?    */
  250. static Boolean    evenUpdate     = False;    /*  Even update interval?    */
  251. static Boolean    showSecondHand = False;       /*  Display second hand?    */
  252.  
  253. static Boolean    iconified = False;       /*  Clock iconified?        */
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260. main(argc, argv)
  261.     int     argc;
  262.     char     *argv[];
  263. {
  264.     int        n;
  265.     Arg        args[10];
  266.     Widget    topLevel, form, canvas;
  267.     int        stringWidth,
  268.             stringAscent, stringDescent;
  269.     XGCValues    gcv;
  270.     u_long     valueMask;
  271.     XmFontList    fontList = (XmFontList)NULL;
  272.     extern void    ParseGeometry();
  273.  
  274.     
  275.     static XtResource    resources[] = {
  276.     { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  277.         XtOffset(ApplicationDataPtr, font),
  278.         XtRString, (XtPointer)DEF_DIGITAL_FONT },
  279.  
  280.     { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  281.         XtOffset(ApplicationDataPtr, foreground),
  282.         XtRString, (XtPointer)"XtdefaultForeground" },
  283.  
  284.     { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
  285.         XtOffset(ApplicationDataPtr, background),
  286.         XtRString, (XtPointer)"XtdefaultBackground" },
  287.  
  288.     { "highlight", "HighlightColor", XtRPixel, sizeof(Pixel),
  289.         XtOffset(ApplicationDataPtr, highlightColor),
  290.         XtRString, (XtPointer)"XtdefaultForeground" },
  291.     
  292.     { "hands", "Hands", XtRPixel, sizeof(Pixel),
  293.         XtOffset(ApplicationDataPtr, handColor),
  294.         XtRString, (XtPointer)"XtdefaultForeground" },
  295.     
  296.     { "catColor", "CatColor", XtRPixel, sizeof(Pixel),
  297.         XtOffset(ApplicationDataPtr, catColor),
  298.         XtRString, (XtPointer)"XtdefaultForeground" },
  299.     
  300.     { "detailColor", "DetailColor", XtRPixel, sizeof(Pixel),
  301.         XtOffset(ApplicationDataPtr, detailColor),
  302.         XtRString, (XtPointer)"XtdefaultBackground" },
  303.     
  304.     { "tieColor", "TieColor", XtRPixel, sizeof(Pixel),
  305.         XtOffset(ApplicationDataPtr, tieColor),
  306.         XtRString, (XtPointer)"XtdefaultBackground" },
  307.  
  308.     { "padding", "Padding", XtRInt, sizeof(int),
  309.         XtOffset(ApplicationDataPtr, padding),
  310.         XtRImmediate, (XtPointer)UNINIT },
  311.     
  312.     { "nTails", "NTails", XtRInt, sizeof(int),
  313.         XtOffset(ApplicationDataPtr, nTails),
  314.         XtRImmediate, (XtPointer)DEF_N_TAILS },
  315.     
  316.     { "update", "Update", XtRInt, sizeof(int),
  317.         XtOffset(ApplicationDataPtr, update),
  318.         XtRImmediate, (XtPointer)DEF_UPDATE },
  319.     
  320.     { "alarm", "Alarm", XtRBoolean, sizeof(Boolean),
  321.         XtOffset(ApplicationDataPtr, alarmSet),
  322.         XtRImmediate, (XtPointer)False },
  323.     
  324.     { "bell", "Bell", XtRBoolean, sizeof(Boolean),
  325.         XtOffset(ApplicationDataPtr, alarmBell),
  326.         XtRImmediate, (XtPointer)False },
  327.     
  328.     { "file", "File", XtRString, sizeof(char *),
  329.         XtOffset(ApplicationDataPtr, alarmFile),
  330.         XtRString, (XtPointer)NULL },
  331.     
  332.     { "period", "Period", XtRInt, sizeof(int),
  333.         XtOffset(ApplicationDataPtr, alarmBellPeriod),
  334.         XtRImmediate, (XtPointer)DEF_ALARM_PERIOD },
  335.     
  336.     { "chime", "Chime", XtRBoolean, sizeof(Boolean),
  337.         XtOffset(ApplicationDataPtr, chime),
  338.         XtRImmediate, (XtPointer)False },
  339.  
  340.     { "mode", "Mode", XtRString, sizeof(char *),
  341.         XtOffset(ApplicationDataPtr, modeString),
  342.         XtRImmediate, (XtPointer)"analog"},
  343.  
  344.     { "help", "Help", XtRBoolean, sizeof(Boolean),
  345.         XtOffset(ApplicationDataPtr, help),
  346.         XtRImmediate, (XtPointer)False},
  347.     };
  348.     
  349.     
  350.     static XrmOptionDescRec options[] = {
  351.     { "-bg",      "*background",   XrmoptionSepArg, (XtPointer)NULL },
  352.     { "-background",  "*background",   XrmoptionSepArg, (XtPointer)NULL },
  353.  
  354.     { "-fg",      "*foreground",   XrmoptionSepArg, (XtPointer)NULL },
  355.     { "-foreground",  "*foreground",   XrmoptionSepArg, (XtPointer)NULL },
  356.  
  357.     { "-fn",      "*font",       XrmoptionSepArg, (XtPointer)NULL },
  358.     { "-font",      "*font",       XrmoptionSepArg, (XtPointer)NULL },
  359.  
  360.     { "-hl",          "*highlight",    XrmoptionSepArg, (XtPointer)NULL },
  361.     { "-highlight",   "*highlight",    XrmoptionSepArg, (XtPointer)NULL },
  362.  
  363.     { "-hd",          "*hands",        XrmoptionSepArg, (XtPointer)NULL },
  364.     { "-hands",       "*hands",        XrmoptionSepArg, (XtPointer)NULL },
  365.  
  366.     { "-catcolor",    "*catColor",     XrmoptionSepArg, (XtPointer)NULL },
  367.     { "-detailcolor", "*detailColor",  XrmoptionSepArg, (XtPointer)NULL },
  368.     { "-tiecolor",    "*tieColor",     XrmoptionSepArg, (XtPointer)NULL },
  369.  
  370.     { "-padding",     "*padding",      XrmoptionSepArg, (XtPointer)NULL },
  371.     { "-mode",        "*mode",         XrmoptionSepArg, (XtPointer)NULL },
  372.     { "-ntails",      "*nTails",       XrmoptionSepArg, (XtPointer)NULL },
  373.     { "-update",      "*update",       XrmoptionSepArg, (XtPointer)NULL },
  374.     { "-alarm",       "*alarm",        XrmoptionNoArg,  (XtPointer)"on" },
  375.     { "-bell",        "*bell",         XrmoptionNoArg,  (XtPointer)"on" },
  376.     { "-file",        "*file",         XrmoptionSepArg, (XtPointer)NULL },
  377.     { "-period",      "*period",       XrmoptionSepArg, (XtPointer)NULL },
  378.     { "-chime",       "*chime",        XrmoptionNoArg,  (XtPointer)"on" },
  379.     { "-help",        "*help",         XrmoptionNoArg,  (XtPointer)"on" },
  380.     };
  381.     
  382.     extern XtEventHandler    MapCallback();
  383.     
  384.  
  385.     /*
  386.      *  Open up the system
  387.      */
  388.     topLevel = XtAppInitialize(&appContext, "CatClock",
  389.                    (XrmOptionDescList)(&options[0]),
  390.                    XtNumber(options),
  391.                    (unsigned int *)&argc, argv, NULL,
  392.                    NULL, 0);
  393.     /*
  394.      *  Get resources . . .
  395.      */
  396.     XtGetApplicationResources(topLevel, &appData, resources,
  397.                   XtNumber(resources), NULL, 0);
  398.  
  399.     /*
  400.      *  Print help message and exit if asked
  401.      */
  402.     if (appData.help) {
  403.     extern void Syntax();
  404.  
  405.     Syntax(argv[0]);
  406.     }
  407.  
  408.     /*
  409.      *  Save away often-used X stuff
  410.      */
  411.     dpy    = XtDisplay(topLevel);
  412.     screen = DefaultScreen(dpy);
  413.     root   = DefaultRootWindow(dpy);
  414.  
  415.     /*
  416.      *  See if user specified iconic startup for clock
  417.      */
  418.     n = 0;
  419.     XtSetArg(args[n], XmNiconic, &iconified);    n++;
  420.     XtGetValues(topLevel, args, n);
  421.  
  422.  
  423.     /*
  424.      *  Get/set clock mode
  425.      */
  426.     if (strcmp(appData.modeString, "cat") == 0) {
  427.         clockMode = CAT_CLOCK;
  428.     } else if (strcmp(appData.modeString, "analog") == 0) {
  429.         clockMode = ANALOG_CLOCK;
  430.     } else if (strcmp(appData.modeString, "digital") == 0) {
  431.         clockMode = DIGITAL_CLOCK;
  432.     } else {
  433.     clockMode = ANALOG_CLOCK;
  434.     }
  435.  
  436.  
  437.     /*
  438.      *  Create icon pixmap
  439.      */
  440.     {
  441.     Pixmap     iconPixmap;
  442.     char    *data;
  443.     u_int    width, height;
  444.     
  445.     switch (clockMode) {
  446.         case ANALOG_CLOCK : {
  447.         data   = analog_bits;
  448.         width  = analog_width;
  449.         height = analog_height;
  450.         break;
  451.         }
  452.         case CAT_CLOCK : {
  453.         data   = cat_bits;
  454.         width  = cat_width;
  455.         height = cat_height;
  456.         break;
  457.         }
  458.         case DIGITAL_CLOCK : {
  459.         data   = digital_bits;
  460.         width  = digital_width;
  461.         height = digital_height;
  462.         break;
  463.         }
  464.     }
  465.  
  466.     iconPixmap = XCreateBitmapFromData(dpy, root,
  467.                        data, width, height);
  468.     n = 0;
  469.     XtSetArg(args[n], XmNiconPixmap, iconPixmap);    n++;
  470.     XtSetValues(topLevel, args, n);
  471.     }
  472.  
  473.  
  474.     
  475.     /*
  476.      *  Get the font info
  477.      */
  478.     if (appData.font == (XFontStruct *)NULL) {
  479.     appData.font = XLoadQueryFont(dpy, DEF_DIGITAL_FONT);
  480.     if (appData.font == (XFontStruct *)NULL) {
  481.         fprintf(stderr,
  482.             "xclock : Unable to load default font %s. Please re-run with another font\n",
  483.             DEF_DIGITAL_FONT);
  484.         exit(-1);
  485.     }
  486.     }
  487.     fontList = XmFontListCreate(appData.font, XmSTRING_ISO8859_1);
  488.     if (fontList == (XmFontList)NULL) {
  489.         fprintf(stderr, "xclock : Unable to create font list -- exiting\n");
  490.     exit(-1);
  491.     }
  492.     
  493.     
  494.     /*
  495.      *  Set mode-dependent stuff
  496.      */
  497.     switch (clockMode) {
  498.     case ANALOG_CLOCK : {
  499.         /*
  500.          *  Padding is space between clock face and
  501.          *  edge of window
  502.          */
  503.         if (appData.padding == UNINIT) {
  504.         appData.padding = DEF_ANALOG_PADDING;
  505.         }
  506.  
  507.         /*
  508.          *  Check if we should show second hand --
  509.          *  if greater than threshhold, don't show it
  510.          */
  511.         if (appData.update <= SECOND_HAND_TIME) {
  512.         showSecondHand = True;
  513.         }
  514.         break;
  515.     }
  516.     case CAT_CLOCK : {
  517.         /*
  518.          *  Padding is space between clock face and
  519.          *  edge of cat's belly.
  520.          */
  521.         if (appData.padding == UNINIT) {
  522.         appData.padding = DEF_ANALOG_PADDING;
  523.         }
  524.         
  525.         /*
  526.          *  Bound the number of tails
  527.          */
  528.         if (appData.nTails < 1) {
  529.         appData.nTails = 1;
  530.         }
  531.         if (appData.nTails > 36) {
  532.         appData.nTails = 36;
  533.         }
  534.         
  535.         /*
  536.          *  Update rate depends on number of tails,
  537.          *  so tail swings at approximately 60 hz.
  538.          */
  539.         appData.update = (int)(0.5 + 1000.0 / appData.nTails);
  540.         
  541.         /*
  542.          *  Drawing the second hand on the cat is ugly.
  543.          */
  544.         showSecondHand = False;
  545.  
  546.         break;
  547.     }
  548.     case DIGITAL_CLOCK : {
  549.         char    *timePtr;
  550.         time_t    timeValue;
  551.         int        stringDir;
  552.         XCharStruct    xCharStr;
  553.         extern void    DigitalString();
  554.  
  555.         /*
  556.          *  Padding is around time text
  557.          */
  558.         if (appData.padding == UNINIT) {
  559.         appData.padding = DEF_DIGITAL_PADDING;
  560.         }
  561.  
  562.         /*
  563.          *  Check if we should show second hand --
  564.          *  if greater than threshhold, don't show it
  565.          */
  566.         if (appData.update >= 60) {
  567.         noSeconds = True;
  568.         }
  569.         
  570.         /*
  571.          * Get font dependent information and determine window
  572.          * size from a test string.
  573.          */
  574.         time(&timeValue);
  575.         timePtr = ctime(&timeValue);
  576.         DigitalString(timePtr);
  577.  
  578.         XTextExtents(appData.font, timePtr, strlen(timePtr),
  579.              &stringDir, &stringAscent, &stringDescent, &xCharStr);
  580.  
  581.         stringWidth = XTextWidth(appData.font, timePtr, strlen(timePtr));
  582.         
  583.         break;
  584.     }
  585.     }
  586.     
  587.     /*
  588.      *  "ParseGeometry"  looks at the user-specified geometry
  589.      *  specification string, and attempts to apply it in a rational
  590.      *  fashion to the clock in whatever mode it happens to be in.
  591.      *  For example, for analog mode, any sort of geometry is OK, but
  592.      *  the cat must be a certain size, although the x and y origins
  593.      *  should not be ignored, etc.
  594.      */
  595.     ParseGeometry(topLevel, stringWidth, stringAscent, stringDescent);
  596.  
  597.  
  598.     /*
  599.      *  Create widgets for xclock :
  600.      *
  601.      *
  602.      *  Outermost widget is a form widget.
  603.      */
  604.     n = 0;
  605.     form = XmCreateForm(topLevel, "form", args, n);
  606.     XtManageChild(form);
  607.  
  608.  
  609.     /*
  610.      *  "canvas" is the display widget
  611.      */
  612.     n = 0;
  613.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);    n++;
  614.     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);    n++;
  615.     XtSetArg(args[n], XmNleftAttachment,   XmATTACH_FORM);    n++;
  616.     XtSetArg(args[n], XmNrightAttachment,  XmATTACH_FORM);    n++;
  617.     XtSetArg(args[n], XmNforeground,       appData.foreground);    n++;
  618.     XtSetArg(args[n], XmNbackground,       appData.background);    n++;
  619.     canvas = XmCreateDrawingArea(form, "drawingArea", args, n);
  620.     XtManageChild(canvas);
  621.  
  622.  
  623.     /*
  624.      *  Make all the windows, etc.
  625.      */
  626.     XtRealizeWidget(topLevel);
  627.  
  628.  
  629.     /*
  630.      *  When clock is first mapped, we can start
  631.      *  the ticking.  This handler is removed following
  632.      *  that first map event.  This approach is
  633.      *  necessary (I think) to make the clock
  634.      *  start up correctly with different window
  635.      *  managers, or without one, for that matter.
  636.      */
  637.     XtAddEventHandler(topLevel, StructureNotifyMask,
  638.               False, MapCallback, (XtPointer)NULL);
  639.  
  640.  
  641.     /*
  642.      *  Cache the window associated with the XmDrawingArea
  643.      */
  644.     clockWindow = XtWindow(canvas);
  645.  
  646.     /*
  647.      *  Supply a cursor for this application
  648.      */
  649.     {
  650.     Cursor arrow = XCreateFontCursor(dpy, XC_top_left_arrow);
  651.  
  652.     XDefineCursor(dpy, clockWindow, arrow);
  653.     }
  654.  
  655.  
  656.     /* 
  657.      *  Check if update interval is even or odd # of seconds
  658.      */
  659.     if (appData.update > 1 &&
  660.     ((60 / appData.update) * appData.update == 60)) {
  661.     evenUpdate = True;
  662.     }
  663.  
  664.     /*
  665.      *  Set the sizes of the hands for analog and cat mode
  666.      */
  667.     switch (clockMode) {
  668.     case ANALOG_CLOCK : {
  669.         radius = (min(winWidth,
  670.               winHeight) - (2 * appData.padding)) / 2;
  671.         
  672.         secondHandLength = ((SECOND_HAND_FRACT  * radius) / 100);
  673.         minuteHandLength = ((MINUTE_HAND_FRACT  * radius) / 100);
  674.         hourHandLength   = ((HOUR_HAND_FRACT    * radius) / 100);
  675.         handWidth        = ((HAND_WIDTH_FRACT   * radius) / 100);
  676.         secondHandWidth  = ((SECOND_WIDTH_FRACT * radius) / 100);
  677.         
  678.         centerX = winWidth  / 2;
  679.         centerY = winHeight / 2;
  680.         
  681.         break;
  682.     }
  683.     case CAT_CLOCK : {
  684.         winWidth  = DEF_CAT_WIDTH;
  685.         winHeight = DEF_CAT_HEIGHT;
  686.         
  687.         radius = Round((min(winWidth,
  688.                 winHeight)-(2 * appData.padding)) / 3.45);
  689.         
  690.         secondHandLength =  ((SECOND_HAND_FRACT  * radius)  / 100);
  691.         minuteHandLength =  ((MINUTE_HAND_FRACT  * radius)  / 100);
  692.         hourHandLength   =  ((HOUR_HAND_FRACT    * radius)  / 100);
  693.         
  694.         handWidth        =  ((HAND_WIDTH_FRACT   * radius) / 100) * 2;
  695.         secondHandWidth  =  ((SECOND_WIDTH_FRACT * radius) / 100);
  696.         
  697.         centerX = winWidth  / 2;
  698.         centerY = winHeight / 2;
  699.         
  700.         break;
  701.     }
  702.     }
  703.     
  704.     /*
  705.      *  Create the GC's
  706.      */
  707.     valueMask = GCForeground | GCBackground | GCFont |
  708.               GCLineWidth  | GCGraphicsExposures;
  709.     
  710.     gcv.background = appData.background;
  711.     gcv.foreground = appData.foreground;   
  712.     
  713.     gcv.graphics_exposures = False;
  714.     gcv.line_width = 0;
  715.     
  716.     if (appData.font != NULL) {
  717.     gcv.font = appData.font->fid;
  718.     } else {
  719.     valueMask &= ~GCFont;    /* use server default font */
  720.     }
  721.     
  722.     gc = XCreateGC(dpy, clockWindow,  valueMask, &gcv);
  723.     
  724.     valueMask = GCForeground | GCLineWidth ;
  725.     gcv.foreground = appData.background;
  726.     eraseGC = XCreateGC(dpy, clockWindow,  valueMask, &gcv);
  727.     
  728.     gcv.foreground = appData.highlightColor;
  729.     highGC = XCreateGC(dpy, clockWindow,  valueMask, &gcv);
  730.     
  731.     valueMask = GCForeground;
  732.     gcv.foreground = appData.handColor;
  733.     handGC = XCreateGC(dpy, clockWindow,  valueMask, &gcv);
  734.  
  735.  
  736.     /*
  737.      *  Make sure we have an alarm file.  If not
  738.      *  specified in command line or .Xdefaults file, then
  739.      *  use default of "$HOME/DEF_ALARM_FILE"
  740.      */
  741.     if (!appData.alarmFile) {
  742.     char        *cp;
  743.     struct passwd     *pw;
  744.     char         *getenv();
  745.     
  746.     if (cp = getenv("HOME")) {
  747.         strcpy(alarmBuf, cp);
  748.     } else if (pw = getpwuid(getuid())) {
  749.         strcpy(alarmBuf, pw->pw_dir);
  750.     } else {
  751.         *alarmBuf = 0;
  752.     }
  753.     
  754.     strcat(appData.alarmFile = alarmBuf, DEF_ALARM_FILE);
  755.     }
  756.  
  757.     /*
  758.      *  Set up the alarm and alarm bell
  759.      */
  760.     InitBellAlarm(clockWindow,
  761.           winWidth, winHeight,
  762.           appData.font, fontList,
  763.           appData.foreground, appData.background,
  764.           &alarmState, &alarmOn);
  765.     SetAlarm(appData.alarmSet ? appData.alarmFile : NULL);
  766.     SetBell(appData.alarmBell ? appData.alarmBellPeriod : 0);
  767.     
  768.  
  769.     /*
  770.      *  Create cat pixmaps, etc. if in CAT_CLOCK mode
  771.      */
  772.     if (clockMode == CAT_CLOCK) {
  773.     extern void InitializeCat();
  774.     
  775.     InitializeCat(appData.catColor,
  776.               appData.detailColor,
  777.               appData.tieColor);
  778.     }
  779.     
  780.  
  781.     /*
  782.      *  Finally, install necessary callbacks
  783.      */
  784.     {
  785.     extern XtCallbackProc     HandleExpose(),
  786.                 HandleResize(),
  787.                 HandleInput();
  788.     
  789.     XtAddCallback(canvas, XmNexposeCallback, HandleExpose, NULL);
  790.     XtAddCallback(canvas, XmNresizeCallback, HandleResize, NULL);
  791.     XtAddCallback(canvas, XmNinputCallback,  HandleInput,  NULL);
  792.     }
  793.  
  794.  
  795.     /*
  796.      *  Start processing events
  797.      */
  798.     XtAppMainLoop(appContext);
  799. }
  800.  
  801.  
  802.  
  803.  
  804. static void ParseGeometry(topLevel, stringWidth, stringAscent, stringDescent)
  805.     Widget    topLevel;
  806.     int        stringWidth, stringAscent, stringDescent;
  807. {
  808.     int        n;
  809.     Arg        args[10];
  810.     char    *geomString = NULL;
  811.     char    *geometry,
  812.         xString[80], yString[80],
  813.         widthString[80], heightString[80];
  814.  
  815.  
  816.     /*
  817.      *  Grab the geometry string from the topLevel widget
  818.      */
  819.     n = 0;
  820.     XtSetArg(args[n], XmNgeometry, &geomString);    n++;
  821.     XtGetValues(topLevel, args, n);
  822.  
  823.     geometry = malloc(80);
  824.     
  825.     switch (clockMode) {
  826.     case ANALOG_CLOCK : {
  827.         if (geomString == NULL) {
  828.         /* 
  829.          *  User didn't specify any geometry, so we
  830.          *  use the default.
  831.          */
  832.         sprintf(geometry, "%dx%d",
  833.             DEF_ANALOG_WIDTH, DEF_ANALOG_HEIGHT);
  834.         } else {
  835.         /*
  836.          *  Gotta do some work.
  837.          */
  838.         int x, y, width, height;
  839.         int geomMask;
  840.         
  841.         /*
  842.          *  Find out what's been set
  843.          */
  844.         geomMask = XParseGeometry(geomString,
  845.                       &x, &y, &width, &height);
  846.         
  847.         if (geomMask == AllValues) {
  848.             /*
  849.              *  If width, height, x, and y have been set,
  850.              *  start off with those.
  851.              */
  852.             strcpy(geometry, geomString);
  853.         } else if (geomMask & NoValue) {
  854.             /*
  855.              *  If none have been set (null geometry string???)
  856.              *  then start with default width and height.
  857.              */
  858.             sprintf(geometry,
  859.                 "%dx%d", DEF_ANALOG_WIDTH, DEF_ANALOG_HEIGHT);
  860.         } else {
  861.             /*
  862.              *  One or more things have been set . . .
  863.              *
  864.              *  Check width . . .
  865.              */
  866.             if (!(geomMask & WidthValue)) {
  867.             width = DEF_ANALOG_WIDTH;
  868.             }
  869.             sprintf(widthString, "%d", width);
  870.             
  871.             /*
  872.              *  Check height . . .
  873.              */
  874.             if (!(geomMask & HeightValue)) {
  875.             height = DEF_ANALOG_HEIGHT;
  876.             }
  877.             sprintf(heightString, "x%d", height);
  878.             
  879.             /*
  880.              *  Check x origin . . .
  881.              */
  882.             if (!(geomMask & XValue)) {
  883.             strcpy(xString, "");
  884.             } else {
  885.             /*  There is an x value - gotta do something with it */
  886.             if (geomMask & XNegative) {
  887.                 sprintf(xString, "-%d", x);
  888.             } else {
  889.                 sprintf(xString, "+%d", x);
  890.             }
  891.             }
  892.             
  893.             /*
  894.              *  Check y origin . . .
  895.              */
  896.             if (!(geomMask & YValue)) {
  897.             strcpy(yString, "");
  898.             } else {
  899.             /*  There is a y value - gotta do something with it */
  900.             if (geomMask & YNegative) {
  901.                 sprintf(yString, "-%d", y);
  902.             } else {
  903.                 sprintf(yString, "+%d", y);
  904.             }
  905.             }
  906.             
  907.             /*
  908.              *  Put them all together
  909.              */
  910.             geometry[0] = '\0';
  911.             strcat(geometry, widthString);
  912.             strcat(geometry, heightString);
  913.             strcat(geometry, xString);
  914.             strcat(geometry, yString);
  915.         }
  916.         }
  917.         
  918.         /*
  919.          *  Stash the width and height in some globals (ugh!)
  920.          */
  921.         sscanf(geometry, "%dx%d", &winWidth, &winHeight);
  922.         
  923.         /*
  924.          *  Set the geometry of the topLevel widget
  925.          */
  926.         n = 0;
  927.         XtSetArg(args[n], XmNgeometry, geometry);    n++;
  928.         XtSetValues(topLevel, args, n);
  929.         
  930.         break; 
  931.     }
  932.     case DIGITAL_CLOCK : {
  933.         int     minWidth, minHeight;
  934.         int     bellWidth, bellHeight;
  935.         
  936.         /*
  937.          *  Size depends on the bell icon size
  938.          */
  939.         GetBellSize(&bellWidth, &bellHeight);
  940.         
  941.         digitalX = appData.padding + bellWidth;
  942.         digitalY = appData.padding + stringAscent;
  943.         
  944.         minWidth = (2 * digitalX) + stringWidth;
  945.         minHeight = digitalY + appData.padding + stringDescent;
  946.         
  947.         if (geomString == NULL) {
  948.         /* 
  949.          *  User didn't specify any geometry, so we
  950.          *  use the default.
  951.          */
  952.         sprintf(geometry, "%dx%d", minWidth, minHeight);
  953.         } else {
  954.         /*
  955.          *  Gotta do some work.
  956.          */
  957.         int    x, y, width, height;
  958.         int     geomMask;
  959.         
  960.         /*
  961.          *  Find out what's been set
  962.          */
  963.         geomMask = XParseGeometry(geomString,
  964.                       &x, &y, &width, &height);
  965.         
  966.         if (geomMask == AllValues) {
  967.             /*
  968.              *  If width, height, x, and y have been set,
  969.              *  start off with those.
  970.              */
  971.             strcpy(geometry, geomString);
  972.             
  973.             digitalX = (int)(0.5 + width / 2.0 - stringWidth / 2.0);
  974.             if (digitalX < bellWidth + appData.padding) {
  975.             digitalX = bellWidth + appData.padding;
  976.             }
  977.             
  978.             digitalY = (int)(0.5 + height/2.0 +
  979.                      appData.font->max_bounds.ascent / 2.0);
  980.             if (digitalY < 0) {
  981.             digitalY = 0;
  982.             }
  983.         } else if (geomMask & NoValue) {
  984.             /*
  985.              *  If none have been set (null geometry string???)
  986.              *  then start with default width and height.
  987.              */
  988.             sprintf(geometry,
  989.                 "%dx%d", minWidth, minHeight);
  990.         } else {
  991.             /*
  992.              *  One or more things have been set . . .
  993.              *
  994.              *  Check width . . .
  995.              */
  996.             if (!(geomMask & WidthValue)) {
  997.             width = minWidth;
  998.             } else {
  999.             digitalX = (int)(0.5 + max(width, minWidth) / 2.0 -
  1000.                      stringWidth / 2.0);
  1001.             }
  1002.             sprintf(widthString, "%d", width);
  1003.             
  1004.             /*
  1005.              *  Check height . . .
  1006.              */
  1007.             if (!(geomMask & HeightValue)) {
  1008.             height = minHeight;
  1009.             }  else {
  1010.             digitalY = (int)(0.5 + max(height, minHeight) / 2.0 +
  1011.                      appData.font->max_bounds.ascent/2.0);
  1012.             
  1013.             }
  1014.             sprintf(heightString, "x%d", height);
  1015.             
  1016.             /*
  1017.              *  Check x origin . . .
  1018.              */
  1019.             if (!(geomMask & XValue)) {
  1020.             strcpy(xString, "");
  1021.             } else {
  1022.             if (geomMask & XNegative) {
  1023.                 sprintf(xString, "-%d", x);
  1024.             } else {
  1025.                 sprintf(xString, "+%d", x);
  1026.             }
  1027.             }
  1028.             
  1029.             /*
  1030.              *  Check y origin . . .
  1031.              */
  1032.             if (!(geomMask & YValue)) {
  1033.             strcpy(yString, "");
  1034.             } else {
  1035.             if (geomMask & YNegative) {
  1036.                 sprintf(yString, "-%d", y);
  1037.             } else {
  1038.                 sprintf(yString, "+%d", y);
  1039.             }
  1040.             }
  1041.             
  1042.             /*
  1043.              *  Put them all together
  1044.              */
  1045.             geometry[0] = '\0';
  1046.             strcat(geometry, widthString);
  1047.             strcat(geometry, heightString);
  1048.             strcat(geometry, xString);
  1049.             strcat(geometry, yString);
  1050.         }
  1051.         }
  1052.         
  1053.         /*
  1054.          *  Stash the width and height in some globals (ugh!)
  1055.          */
  1056.         sscanf(geometry, "%dx%d", &winWidth, &winHeight);
  1057.         
  1058.         /*
  1059.          *  Set the geometry of the topLevel widget
  1060.          */
  1061.         n = 0;
  1062.         XtSetArg(args[n], XmNgeometry,  geometry);    n++;
  1063.         XtSetValues(topLevel, args, n);
  1064.         
  1065.         break; 
  1066.     }
  1067.     case CAT_CLOCK : {
  1068.         if (geomString == NULL) {
  1069.         /* 
  1070.          *  User didn't specify any geometry, so we
  1071.          *  use the default.
  1072.          */
  1073.         sprintf(geometry, "%dx%d", DEF_CAT_WIDTH, DEF_CAT_HEIGHT);
  1074.         } else {
  1075.         /*
  1076.          *  Gotta do some work.
  1077.          */
  1078.         int     x, y, width, height;
  1079.         int     geomMask;
  1080.         
  1081.         /*
  1082.          *  Find out what's been set
  1083.          */
  1084.         geomMask = XParseGeometry(geomString,
  1085.                       &x, &y, &width, &height);
  1086.         
  1087.         /*
  1088.          *  Fix the cat width and height
  1089.          */
  1090.         sprintf(widthString,  "%d",  DEF_CAT_WIDTH);
  1091.         sprintf(heightString, "x%d", DEF_CAT_HEIGHT);
  1092.         
  1093.         /*
  1094.          *  Use the x and y values, if any
  1095.          */
  1096.         if (!(geomMask & XValue)) {
  1097.             strcpy(xString, "");
  1098.         } else {
  1099.             if (geomMask & XNegative) {
  1100.             sprintf(xString, "-%d", x);
  1101.             } else {
  1102.             sprintf(xString, "+%d", x);
  1103.             }
  1104.         }
  1105.         
  1106.         if (!(geomMask & YValue)) {
  1107.             strcpy(yString, "");
  1108.         } else {
  1109.             if (geomMask & YNegative) {
  1110.             sprintf(yString, "-%d", y);
  1111.             } else {
  1112.             sprintf(yString, "+%d", y);
  1113.             }
  1114.         }
  1115.         
  1116.         /*
  1117.          *  Put them all together
  1118.          */        
  1119.         geometry[0] = '\0';
  1120.         strcat(geometry, widthString);
  1121.         strcat(geometry, heightString);
  1122.         strcat(geometry, xString);
  1123.         strcat(geometry, yString);
  1124.         }
  1125.         
  1126.         /*
  1127.          *  Stash the width and height in some globals (ugh!)
  1128.          */
  1129.         sscanf(geometry, "%dx%d", &winWidth, &winHeight);
  1130.         
  1131.         /*
  1132.          *  Set the geometry of the topLevel widget
  1133.          */
  1134.         n = 0;
  1135.         XtSetArg(args[n], XmNwidth,     DEF_CAT_WIDTH);    n++;
  1136.         XtSetArg(args[n], XmNheight,    DEF_CAT_HEIGHT);    n++;
  1137.         XtSetArg(args[n], XmNminWidth,  DEF_CAT_WIDTH);    n++;
  1138.         XtSetArg(args[n], XmNminHeight, DEF_CAT_HEIGHT);    n++;
  1139.         XtSetArg(args[n], XmNmaxWidth,  DEF_CAT_WIDTH);    n++;
  1140.         XtSetArg(args[n], XmNmaxHeight, DEF_CAT_HEIGHT);    n++;
  1141.         XtSetArg(args[n], XmNgeometry,  geometry);        n++;    
  1142.         XtSetValues(topLevel, args, n);
  1143.         
  1144.         break;
  1145.     }
  1146.     }
  1147. }    
  1148.  
  1149.  
  1150.  
  1151. /*
  1152.  *  DrawLine - Draws a line.
  1153.  *
  1154.  *  blankLength is the distance from the center which the line begins.
  1155.  *  length is the maximum length of the hand.
  1156.  *  fractionOfACircle is a fraction between 0 and 1 (inclusive) indicating
  1157.  *  how far around the circle (clockwise) from high noon.
  1158.  *
  1159.  *  The blankLength feature is because I wanted to draw tick-marks around the
  1160.  *  circle (for seconds).  The obvious means of drawing lines from the center
  1161.  *  to the perimeter, then erasing all but the outside most pixels doesn't
  1162.  *  work because of round-off error (sigh).
  1163.  */
  1164. static void DrawLine(blankLength, length, fractionOfACircle)
  1165.     int        blankLength;
  1166.     int        length;
  1167.     double     fractionOfACircle;
  1168. {
  1169.     double         angle, cosAngle, sinAngle;
  1170.     extern void        SetSeg();
  1171.  
  1172.     /*
  1173.      *  A full circle is 2 M_PI radians.
  1174.      *  Angles are measured from 12 o'clock, clockwise increasing.
  1175.      *  Since in X, +x is to the right and +y is downward:
  1176.      *
  1177.      *    x = x0 + r * sin(theta)
  1178.      *    y = y0 - r * cos(theta)
  1179.      *
  1180.      */
  1181.     angle = TWOPI * fractionOfACircle;
  1182.     cosAngle = cos(angle);
  1183.     sinAngle = sin(angle);
  1184.     
  1185.     SetSeg(centerX + (int)(blankLength * sinAngle),
  1186.        centerY - (int)(blankLength * cosAngle),
  1187.        centerX + (int)(length * sinAngle),
  1188.        centerY - (int)(length * cosAngle));
  1189. }
  1190.  
  1191.  
  1192.  
  1193. /*
  1194.  *  DrawHand - Draws a hand.
  1195.  *
  1196.  *  length is the maximum length of the hand.
  1197.  *  width is the half-width of the hand.
  1198.  *  fractionOfACircle is a fraction between 0 and 1 (inclusive) indicating
  1199.  *  how far around the circle (clockwise) from high noon.
  1200.  *
  1201.  */
  1202. static void DrawHand(length, width, fractionOfACircle)
  1203.     int        length, width;
  1204.     double     fractionOfACircle;
  1205. {
  1206.     double    angle, cosAngle, sinAngle;
  1207.     double    ws, wc;
  1208.     int        x, y, x1, y1, x2, y2;
  1209.     extern void        SetSeg();
  1210.     
  1211.     /*
  1212.      *  A full circle is 2 PI radians.
  1213.      *  Angles are measured from 12 o'clock, clockwise increasing.
  1214.      *  Since in X, +x is to the right and +y is downward:
  1215.      *
  1216.      *    x = x0 + r * sin(theta)
  1217.      *    y = y0 - r * cos(theta)
  1218.      *
  1219.      */
  1220.     angle = TWOPI * fractionOfACircle;
  1221.     cosAngle = cos(angle);
  1222.     sinAngle = sin(angle);
  1223.  
  1224.     /*
  1225.      * Order of points when drawing the hand.
  1226.      *
  1227.      *        1,4
  1228.      *        / \
  1229.      *           /   \
  1230.      *          /     \
  1231.      *        2 ------- 3
  1232.      */
  1233.     wc = width * cosAngle;
  1234.     ws = width * sinAngle;
  1235.     SetSeg(x = centerX + Round(length * sinAngle),
  1236.        y = centerY - Round(length * cosAngle),
  1237.        x1 = centerX - Round(ws + wc), 
  1238.        y1 = centerY + Round(wc - ws));  /* 1 ---- 2 */
  1239.     /* 2 */
  1240.     SetSeg(x1, y1, 
  1241.        x2 = centerX - Round(ws - wc), 
  1242.        y2 = centerY + Round(wc + ws));  /* 2 ----- 3 */
  1243.     
  1244.     SetSeg(x2, y2, x, y);    /* 3 ----- 1(4) */
  1245. }
  1246.  
  1247.  
  1248.  
  1249. /*
  1250.  *  DrawSecond - Draws the second hand (diamond).
  1251.  *
  1252.  *  length is the maximum length of the hand.
  1253.  *  width is the half-width of the hand.
  1254.  *  offset is direct distance from Center to tail end.
  1255.  *  fractionOfACircle is a fraction between 0 and 1 (inclusive) indicating
  1256.  *  how far around the circle (clockwise) from high noon.
  1257.  *
  1258.  */
  1259. static void DrawSecond(length, width, offset, fractionOfACircle)
  1260.     int        length, width, offset;
  1261.     double     fractionOfACircle;
  1262. {
  1263.     double    angle, cosAngle, sinAngle;
  1264.     double     ms, mc, ws, wc;
  1265.     int     mid;
  1266.     int     x, y;
  1267.     extern void        SetSeg();
  1268.     
  1269.     /*
  1270.      *  A full circle is 2 PI radians.
  1271.      *  Angles are measured from 12 o'clock, clockwise increasing.
  1272.      *  Since in X, +x is to the right and +y is downward:
  1273.      *
  1274.      *    x = x0 + r * sin(theta)
  1275.      *    y = y0 - r * cos(theta)
  1276.      *
  1277.      */
  1278.     angle = TWOPI * fractionOfACircle;
  1279.     cosAngle = cos(angle);
  1280.     sinAngle = sin(angle);
  1281.  
  1282.     /*
  1283.      * Order of points when drawing the hand.
  1284.      *
  1285.      *        1,5
  1286.      *        / \
  1287.      *           /   \
  1288.      *          /     \
  1289.      *        2<       >4
  1290.      *          \     /
  1291.      *           \   /
  1292.      *        \ /
  1293.      *    -     3
  1294.      *    |
  1295.      *    |
  1296.      *   offset
  1297.      *    |
  1298.      *    |
  1299.      *    -     + center
  1300.      */
  1301.     mid = (length + offset) / 2;
  1302.     mc = mid * cosAngle;
  1303.     ms = mid * sinAngle;
  1304.     wc = width * cosAngle;
  1305.     ws = width * sinAngle;
  1306.     /*1 ---- 2 */
  1307.     SetSeg(x = centerX + Round(length * sinAngle),
  1308.        y = centerY - Round(length * cosAngle),
  1309.        centerX + Round(ms - wc),
  1310.        centerY - Round(mc + ws) );
  1311.     SetSeg(centerX + Round(ms - wc), 
  1312.        centerY - Round(mc + ws),
  1313.        centerX + Round(offset * sinAngle),
  1314.        centerY - Round(offset * cosAngle)); /* 2-----3 */
  1315.  
  1316.     SetSeg(centerX + Round(offset *sinAngle),
  1317.        centerY - Round(offset * cosAngle), /* 3-----4 */
  1318.        centerX + Round(ms + wc),
  1319.        centerY - Round(mc - ws));       
  1320.     
  1321.     segBufPtr->x = x;
  1322.     segBufPtr++->y = y;
  1323.     numSegs++;
  1324. }
  1325.  
  1326.  
  1327. static void SetSeg(x1, y1, x2, y2)
  1328.     int x1, y1, x2, y2;
  1329. {
  1330.     segBufPtr->x   = x1;
  1331.     segBufPtr++->y = y1;
  1332.     segBufPtr->x   = x2;
  1333.     segBufPtr++->y = y2;
  1334.  
  1335.     numSegs += 2;
  1336. }
  1337.  
  1338.  
  1339.  
  1340. /*
  1341.  *  Draw the clock face (every fifth tick-mark is longer
  1342.  *  than the others).
  1343.  */
  1344. static void DrawClockFace(secondHand, radius)
  1345.     int     secondHand;
  1346.     int     radius;
  1347. {
  1348.     int        i;
  1349.     int     delta = (radius - secondHand) / 3;
  1350.  
  1351.     segBufPtr = segBuf;
  1352.     numSegs = 0;
  1353.     
  1354.     switch (clockMode) {
  1355.     case ANALOG_CLOCK : {
  1356.         XClearWindow(dpy, clockWindow);
  1357.  
  1358.         for (i = 0; i < 60; i++) {
  1359.         DrawLine((i % 5) == 0 ? secondHand : (radius - delta),
  1360.              radius, ((double) i) / 60.);
  1361.         }
  1362.         
  1363.         /*
  1364.          * Go ahead and draw it.
  1365.          */
  1366.         XDrawSegments(dpy, clockWindow,
  1367.               gc, (XSegment *) segBuf,
  1368.               numSegs / 2);
  1369.  
  1370.         break;
  1371.     }
  1372.     case CAT_CLOCK : {
  1373.         XFillRectangle(dpy, clockWindow, catGC, 
  1374.                0, 0, winWidth, winHeight);
  1375.  
  1376.         break;
  1377.     }
  1378.     case DIGITAL_CLOCK : {
  1379.         XClearWindow(dpy, clockWindow);
  1380.  
  1381.         break;
  1382.     }
  1383.     }
  1384.     
  1385.     segBufPtr = segBuf;
  1386.     numSegs = 0;
  1387.     DrawBell(False);
  1388. }
  1389.  
  1390.  
  1391.  
  1392.  
  1393.  
  1394. static int Round(x)
  1395.     double x;
  1396. {
  1397.     return (x >= 0.0 ? (int)(x + 0.5) : (int)(x - 0.5));
  1398. }
  1399.  
  1400.  
  1401.  
  1402. static void DigitalString(str)
  1403.     char *str;
  1404. {
  1405.     char *cp;
  1406.     
  1407.     str[24] = 0;
  1408.  
  1409.     if (str[11] == '0') {
  1410.     str[11] = ' ';
  1411.     }
  1412.  
  1413.     if (noSeconds) {
  1414.     str += 16;
  1415.     cp = str + 3;
  1416.     while (*str++ = *cp++);
  1417.     }
  1418. }
  1419.  
  1420.  
  1421. /*
  1422.  * Report the syntax for calling xclock.
  1423.  */
  1424. static void Syntax(call)
  1425.     char *call;
  1426. {
  1427.     printf("Usage: %s [toolkitoptions]\n", call);
  1428.     printf("       [-mode <analog, digital, cat>]\n");
  1429.     printf("       [-alarm]  [-bell]  [-chime]\n");
  1430.     printf("       [-file <alarm file>]  [-period <seconds>]\n");
  1431.     printf("       [-hl <color>]  [-hd <color>]\n");
  1432.     printf("       [-catcolor <color>]\n"); 
  1433.     printf("       [-detailcolor <color>]\n"); 
  1434.     printf("       [-tiecolor <color>]\n"); 
  1435.     printf("       [-padding <pixels>]\n");
  1436.     printf("       [-update <seconds>]\n");
  1437.     printf("       [-ntails <number]\n");
  1438.     printf("       [-help]\n\n");
  1439.  
  1440.     exit(0);
  1441. }
  1442.  
  1443.  
  1444. static void InitializeCat(catColor, detailColor, tieColor)
  1445.     Pixel    catColor, detailColor, tieColor;
  1446. {
  1447.     Pixmap        catPix;
  1448.     Pixmap         catBack;
  1449.     Pixmap         catWhite;
  1450.     Pixmap         catTie;
  1451.     int         fillStyle;
  1452.     int            i;
  1453.     XGCValues         gcv;
  1454.     unsigned long     valueMask;
  1455.     GC            gc1, gc2;
  1456.     extern GC        CreateTailGC();
  1457.     extern GC        CreateEyeGC();
  1458.  
  1459.  
  1460.     catPix = XCreatePixmap(dpy, root, winWidth, winHeight,
  1461.                DefaultDepth(dpy, screen));
  1462.     
  1463.     valueMask = GCForeground | GCBackground | GCGraphicsExposures;
  1464.  
  1465.     gcv.background = appData.background;
  1466.     gcv.foreground = catColor;   
  1467.     gcv.graphics_exposures = False;
  1468.     gcv.line_width = 0;
  1469.     gc1 = XCreateGC(dpy, root, valueMask, &gcv);
  1470.  
  1471.     fillStyle = FillOpaqueStippled;
  1472.     XSetFillStyle(dpy, gc1, fillStyle);
  1473.  
  1474.     catBack = XCreateBitmapFromData(dpy, root, catback_bits,
  1475.                     catback_width, catback_height);
  1476.  
  1477.     XSetStipple(dpy, gc1, catBack);
  1478.     XSetTSOrigin(dpy, gc1, 0, 0);
  1479.  
  1480.     XFillRectangle(dpy, catPix, gc1, 
  1481.            0, 0, winWidth, winHeight);
  1482.  
  1483.     fillStyle = FillStippled;
  1484.     XSetFillStyle (dpy, gc1, fillStyle);
  1485.     XFreeGC(dpy, gc1);
  1486.  
  1487.     valueMask = GCForeground | GCBackground | GCGraphicsExposures;
  1488.  
  1489.     gcv.background         = appData.background;
  1490.     gcv.foreground         = detailColor;   
  1491.     gcv.graphics_exposures = False;
  1492.     gcv.line_width         = 0;
  1493.     gc2 = XCreateGC(dpy, root, valueMask, &gcv);
  1494.     
  1495.     fillStyle = FillStippled;
  1496.     XSetFillStyle(dpy, gc2, fillStyle);
  1497.     catWhite = XCreateBitmapFromData(dpy, root, catwhite_bits,
  1498.                      catwhite_width, catwhite_height);
  1499.     
  1500.     XSetStipple(dpy, gc2, catWhite);
  1501.     XSetTSOrigin(dpy, gc2, 0, 0);
  1502.     XFillRectangle(dpy, catPix, gc2, 
  1503.            0, 0, winWidth, winHeight);
  1504.     XFreeGC(dpy, gc2);
  1505.     
  1506.     gcv.background         = appData.background;
  1507.     gcv.foreground         = tieColor;   
  1508.     gcv.graphics_exposures = False;
  1509.     gcv.line_width         = 0;
  1510.     catGC = XCreateGC(dpy, root, valueMask, &gcv);
  1511.  
  1512.     fillStyle = FillStippled;
  1513.     XSetFillStyle (dpy, catGC, fillStyle);
  1514.     catTie = XCreateBitmapFromData(dpy, root, cattie_bits,
  1515.                    cattie_width, cattie_height);
  1516.  
  1517.     XSetStipple(dpy, catGC, catTie);
  1518.     XSetTSOrigin(dpy, catGC, 0, 0);
  1519.     XFillRectangle(dpy, catPix, catGC, 0, 0, winWidth, winHeight);
  1520.     
  1521.     /*
  1522.      *  Now, let's create the Backround Pixmap for the Cat Clock using catGC 
  1523.      *  We will use this pixmap to fill in the window backround.        
  1524.      */
  1525.     fillStyle = FillTiled;
  1526.     XSetFillStyle(dpy, catGC, fillStyle);
  1527.     XSetTile(dpy, catGC, catPix);
  1528.     XSetTSOrigin(dpy, catGC, 0, 0);
  1529.  
  1530.  
  1531.     /*
  1532.      *  Create the arrays of tail and eye pixmaps
  1533.      */
  1534.     tailGC = CreateTailGC();
  1535.     eyeGC  = CreateEyeGC();
  1536.  
  1537.     tailPixmap = (Pixmap *)malloc((appData.nTails + 1) * sizeof(Pixmap));
  1538.     eyePixmap  = (Pixmap *)malloc((appData.nTails + 1) * sizeof(Pixmap));
  1539.  
  1540.     for (i = 0; i <= appData.nTails; i++) {
  1541.     static Pixmap CreateTailPixmap();
  1542.     static Pixmap CreateEyePixmap();
  1543.  
  1544.     tailPixmap[i] = CreateTailPixmap(i * M_PI/(appData.nTails));
  1545.     eyePixmap[i]  = CreateEyePixmap(i * M_PI/(appData.nTails));
  1546.     }
  1547. }
  1548.  
  1549.  
  1550.  
  1551. static void UpdateEyesAndTail()
  1552. {
  1553.     static int    curTail = 0;    /*  Index into tail pixmap array    */
  1554.     static int    tailDir = 1;    /*  Left or right swing            */
  1555.  
  1556.     /*
  1557.      *  Draw new tail & eyes (Don't change values here!!)
  1558.      */
  1559.     XCopyPlane(dpy, tailPixmap[curTail], clockWindow,
  1560.            tailGC, 0, 0, winWidth, TAIL_HEIGHT,
  1561.            0, DEF_CAT_BOTTOM + 1, 0x1);
  1562.     XCopyPlane(dpy, eyePixmap[curTail], clockWindow,
  1563.            eyeGC, 0, 0, eyes_width, eyes_height,
  1564.            49, 30, 0x1);
  1565.  
  1566.     /*
  1567.      *  Figure out which tail & eyes are next
  1568.      */
  1569.     if (curTail == 0 && tailDir == -1) {
  1570.     curTail = 1;
  1571.     tailDir = 1;
  1572.     } else if (curTail == appData.nTails && tailDir == 1) {
  1573.     curTail = appData.nTails - 1;
  1574.     tailDir = -1;
  1575.     } else {
  1576.     curTail += tailDir;
  1577.     }
  1578. }
  1579.  
  1580.  
  1581. static GC CreateTailGC()
  1582. {
  1583.     GC            tailGC;
  1584.     XGCValues        tailGCV;
  1585.     unsigned long    valueMask;
  1586.  
  1587.     tailGCV.function           = GXcopy;
  1588.     tailGCV.plane_mask         = AllPlanes;
  1589.     tailGCV.foreground         = appData.catColor;
  1590.     tailGCV.background         = appData.background;
  1591.     tailGCV.line_width         = 15;
  1592.     tailGCV.line_style         = LineSolid;
  1593.     tailGCV.cap_style          = CapRound;
  1594.     tailGCV.join_style         = JoinRound;
  1595.     tailGCV.fill_style         = FillSolid;
  1596.     tailGCV.subwindow_mode     = ClipByChildren;
  1597.     tailGCV.clip_x_origin      = 0;
  1598.     tailGCV.clip_y_origin      = 0;
  1599.     tailGCV.clip_mask          = None;
  1600.     tailGCV.graphics_exposures = False;
  1601.  
  1602.     valueMask =
  1603.         GCFunction  | GCPlaneMask     | GCForeground  | GCBackground  |
  1604.         GCLineWidth | GCLineStyle     | GCCapStyle    | GCJoinStyle   |
  1605.         GCFillStyle | GCSubwindowMode | GCClipXOrigin | GCClipYOrigin |
  1606.         GCClipMask  | GCGraphicsExposures;
  1607.  
  1608.     tailGC = XCreateGC(dpy, clockWindow, valueMask, &tailGCV); 
  1609.     
  1610.     return (tailGC);
  1611. }
  1612.  
  1613.  
  1614.  
  1615. static GC  CreateEyeGC()
  1616. {
  1617.     GC            eyeGC;
  1618.     XGCValues        eyeGCV;
  1619.     unsigned long    valueMask;
  1620.  
  1621.     eyeGCV.function           = GXcopy;
  1622.     eyeGCV.plane_mask         = AllPlanes;
  1623.     eyeGCV.foreground         = appData.catColor;
  1624.     eyeGCV.background         = appData.detailColor;
  1625.     eyeGCV.line_width         = 15;
  1626.     eyeGCV.line_style         = LineSolid;
  1627.     eyeGCV.cap_style          = CapRound;
  1628.     eyeGCV.join_style         = JoinRound;
  1629.     eyeGCV.fill_style         = FillSolid;
  1630.     eyeGCV.subwindow_mode     = ClipByChildren;
  1631.     eyeGCV.clip_x_origin      = 0;
  1632.     eyeGCV.clip_y_origin      = 0;
  1633.     eyeGCV.clip_mask          = None;
  1634.     eyeGCV.graphics_exposures = False;
  1635.  
  1636.     valueMask =
  1637.         GCFunction  | GCPlaneMask     | GCForeground  | GCBackground  |
  1638.         GCLineWidth | GCLineStyle     | GCCapStyle    | GCJoinStyle   |
  1639.         GCFillStyle | GCSubwindowMode | GCClipXOrigin | GCClipYOrigin |
  1640.         GCClipMask  | GCGraphicsExposures;
  1641.  
  1642.     eyeGC = XCreateGC(dpy, clockWindow, valueMask, &eyeGCV);
  1643.     
  1644.     return (eyeGC);
  1645. }
  1646.  
  1647.     
  1648.  
  1649. static Pixmap CreateTailPixmap(t)
  1650.     double    t;                  /*  "Time" parameter  */
  1651. {
  1652.     Pixmap    tailBitmap;
  1653.     GC        bitmapGC;
  1654.     double    sinTheta, cosTheta;    /*  Pendulum parameters    */
  1655.     double    A = 0.4;
  1656.     double    omega = 1.0;
  1657.     double    phi = 3 * M_PI_2;
  1658.     double    angle;
  1659.  
  1660.     static XPoint tailOffset = { 74, -15 };
  1661.  
  1662. #define N_TAIL_PTS    7
  1663.     static XPoint  tail[N_TAIL_PTS] = {    /*  "Center" tail definition */
  1664.     {  0,  0 },
  1665.     {  0, 76 },
  1666.     {  3, 82 },
  1667.     { 10, 84 },
  1668.     { 18, 82 },
  1669.     { 21, 76 },
  1670.     { 21, 70 },
  1671.     };
  1672.  
  1673.     XPoint        offCenterTail[N_TAIL_PTS];  /* off center tail    */
  1674.     XPoint        newTail[N_TAIL_PTS];        /*  Tail at time "t"  */
  1675.     XGCValues        bitmapGCV;            /*  GC for drawing      */
  1676.     unsigned long     valueMask;        
  1677.     int            i;
  1678.  
  1679.     /*
  1680.      *  Create GC for drawing tail
  1681.      */
  1682.     bitmapGCV.function          = GXcopy;
  1683.     bitmapGCV.plane_mask     = AllPlanes;
  1684.     bitmapGCV.foreground     = 1;
  1685.     bitmapGCV.background     = 0;
  1686.     bitmapGCV.line_width     = 15;
  1687.     bitmapGCV.line_style     = LineSolid;
  1688.     bitmapGCV.cap_style      = CapRound;
  1689.     bitmapGCV.join_style     = JoinRound;
  1690.     bitmapGCV.fill_style     = FillSolid;
  1691.     bitmapGCV.subwindow_mode = ClipByChildren;
  1692.     bitmapGCV.clip_x_origin  = 0;
  1693.     bitmapGCV.clip_y_origin  = 0;
  1694.     bitmapGCV.clip_mask      = None;
  1695.  
  1696.     valueMask =
  1697.         GCFunction  | GCPlaneMask     | GCForeground  | GCBackground  |
  1698.         GCLineWidth | GCLineStyle     | GCCapStyle    | GCJoinStyle   |
  1699.         GCFillStyle | GCSubwindowMode | GCClipXOrigin | GCClipYOrigin |
  1700.         GCClipMask;
  1701.  
  1702.     tailBitmap = XCreateBitmapFromData(dpy, root,
  1703.                        tail_bits, tail_width, tail_height);
  1704.     bitmapGC = XCreateGC(dpy, tailBitmap, valueMask, &bitmapGCV);
  1705.  
  1706.  
  1707.     {
  1708.     /*
  1709.      *  Create an "off-center" tail to deal with the fact that
  1710.      *  the tail has a hook to it.  A real pendulum so shaped would
  1711.      *  hang a bit to the left (as you look at the cat).
  1712.      */
  1713.     angle = -0.08;
  1714.     sinTheta = sin(angle);
  1715.     cosTheta = cos(angle);
  1716.     
  1717.     for (i = 0; i < N_TAIL_PTS; i++) {
  1718.         offCenterTail[i].x = (int)((double)(tail[i].x) * cosTheta +
  1719.                        (double)(tail[i].y) * sinTheta);
  1720.         offCenterTail[i].y = (int)((double)(-tail[i].x) * sinTheta +
  1721.                        (double)(tail[i].y) * cosTheta);
  1722.     }
  1723.     }
  1724.  
  1725.  
  1726.     /*
  1727.      *  Compute pendulum function.
  1728.      */
  1729.     angle = A * sin(omega * t + phi);
  1730.     sinTheta = sin(angle);
  1731.     cosTheta = cos(angle);
  1732.     
  1733.     /*
  1734.      *  Rotate the center tail about its origin by "angle" degrees.
  1735.      */
  1736.     for (i = 0; i < N_TAIL_PTS; i++) {
  1737.     newTail[i].x = (int)((double)(offCenterTail[i].x) * cosTheta +
  1738.                  (double)(offCenterTail[i].y) * sinTheta);
  1739.     newTail[i].y = (int)((double)(-offCenterTail[i].x) * sinTheta +
  1740.                  (double)(offCenterTail[i].y) * cosTheta);
  1741.     
  1742.     newTail[i].x += tailOffset.x;
  1743.     newTail[i].y += tailOffset.y;
  1744.     }
  1745.     
  1746.     /*
  1747.      *  Create pixmap for drawing tail (and stippling on update)
  1748.      */
  1749.     XDrawLines(dpy, tailBitmap, bitmapGC,
  1750.            newTail, N_TAIL_PTS, CoordModeOrigin);
  1751.  
  1752.     XFreeGC(dpy, bitmapGC);
  1753.     
  1754.     return (tailBitmap);
  1755. }
  1756.  
  1757.  
  1758. static Pixmap CreateEyePixmap(t)
  1759.     double    t;                  /*  "Time" parameter  */
  1760. {
  1761.     Pixmap    eyeBitmap;
  1762.     GC        bitmapGC;
  1763.  
  1764.     double    A = 0.7;
  1765.     double    omega = 1.0;
  1766.     double    phi = 3 * M_PI_2;
  1767.     double    angle;
  1768.  
  1769.     double    u, w;            /*  Sphere parameters    */
  1770.     float    r;            /*  Radius        */
  1771.     float    x0, y0, z0;        /*  Center of sphere    */
  1772.  
  1773.     XPoint    pts[100];
  1774.     
  1775.     XGCValues        bitmapGCV;    /*  GC for drawing      */
  1776.     unsigned long     valueMask;
  1777.     int            i, j;
  1778.  
  1779.     typedef struct {
  1780.     double    x, y, z;
  1781.     } Point3D;
  1782.  
  1783.     /*
  1784.      *  Create GC for drawing eyes
  1785.      */
  1786.     bitmapGCV.function          = GXcopy;
  1787.     bitmapGCV.plane_mask     = AllPlanes;
  1788.     bitmapGCV.foreground     = 1;
  1789.     bitmapGCV.background     = 0;
  1790.     bitmapGCV.line_width     = 15;
  1791.     bitmapGCV.line_style     = LineSolid;
  1792.     bitmapGCV.cap_style      = CapRound;
  1793.     bitmapGCV.join_style     = JoinRound;
  1794.     bitmapGCV.fill_style     = FillSolid;
  1795.     bitmapGCV.subwindow_mode = ClipByChildren;
  1796.     bitmapGCV.clip_x_origin  = 0;
  1797.     bitmapGCV.clip_y_origin  = 0;
  1798.     bitmapGCV.clip_mask      = None;
  1799.  
  1800.     valueMask =
  1801.         GCFunction  | GCPlaneMask     | GCForeground  | GCBackground  |
  1802.         GCLineWidth | GCLineStyle     | GCCapStyle    | GCJoinStyle   |
  1803.         GCFillStyle | GCSubwindowMode | GCClipXOrigin | GCClipYOrigin |
  1804.         GCClipMask;
  1805.  
  1806.     eyeBitmap = XCreateBitmapFromData(dpy, root,
  1807.                       eyes_bits, eyes_width, eyes_height);
  1808.     bitmapGC = XCreateGC(dpy, eyeBitmap, valueMask, &bitmapGCV);
  1809.  
  1810.     /*
  1811.      *  Compute pendulum function.
  1812.      */
  1813.     w = M_PI/2.0;
  1814.  
  1815.     angle = A * sin(omega * t + phi) + w;
  1816.  
  1817.     x0 = 0.0;
  1818.     y0 = 0.0;
  1819.     z0 = 2.0;
  1820.     r  = 1.0;
  1821.  
  1822.     for (i = 0, u = -M_PI/2.0; u < M_PI/2.0; i++, u += 0.25) {
  1823.     Point3D    pt;
  1824.  
  1825.     pt.x = x0 + r * cos(u) * cos(angle + M_PI/7.0);
  1826.     pt.z = z0 + r * cos(u) * sin(angle + M_PI/7.0);
  1827.     pt.y = y0 + r * sin(u);
  1828.  
  1829.     pts[i].x = (int)(((pt.z == 0.0 ? pt.x : pt.x / pt.z) * 23.0) + 12.0);
  1830.     pts[i].y = (int)(((pt.z == 0.0 ? pt.y : pt.y / pt.z) * 23.0) + 11.0);
  1831.     }
  1832.  
  1833.     for (u = M_PI/2.0; u > -M_PI/2.0; i++, u -= 0.25) {
  1834.     Point3D    pt;
  1835.  
  1836.     pt.x = x0 + r * cos(u) * cos(angle - M_PI/7.0);
  1837.     pt.z = z0 + r * cos(u) * sin(angle - M_PI/7.0);
  1838.     pt.y = y0 + r * sin(u);
  1839.  
  1840.     pts[i].x = (int)(((pt.z == 0.0 ? pt.x : pt.x / pt.z) * 23.0) + 12.0);
  1841.     pts[i].y = (int)(((pt.z == 0.0 ? pt.y : pt.y / pt.z) * 23.0) + 11.0);
  1842.     }
  1843.  
  1844.     /*
  1845.      *  Create pixmap for drawing eye (and stippling on update)
  1846.      */
  1847.     XFillPolygon(dpy, eyeBitmap, bitmapGC, pts, i, Nonconvex, CoordModeOrigin);
  1848.  
  1849.     for (j = 0; j < i; j++) {
  1850.     pts[j].x += 31;
  1851.     }
  1852.     XFillPolygon(dpy, eyeBitmap, bitmapGC, pts, i, Nonconvex, CoordModeOrigin);
  1853.     
  1854.     XFreeGC(dpy, bitmapGC);
  1855.  
  1856.     return (eyeBitmap);
  1857. }
  1858.  
  1859.  
  1860.  
  1861. static Widget  CreateToggle(label, parent)
  1862.     char   *label;
  1863.     Widget  parent;
  1864.     
  1865. {
  1866.     Widget    w;
  1867.     int      n;
  1868.     Arg        args[1];
  1869.     XmString    tcs;
  1870.  
  1871.     tcs = XmStringLtoRCreate(label, XmSTRING_DEFAULT_CHARSET);
  1872.  
  1873.     n = 0;
  1874.     XtSetArg(args[n], XmNlabelString, tcs);  n++;
  1875.     w = XmCreateToggleButton(parent, "toggle", args, n);
  1876.     XtManageChild(w);
  1877.  
  1878.     XmStringFree(tcs);
  1879.  
  1880.     return (w);
  1881. }
  1882.  
  1883.  
  1884. static Widget  CreatePushButton(label, parent)
  1885.     char   *label;
  1886.     Widget  parent;
  1887. {
  1888.     Widget    w;
  1889.     int        n;
  1890.     Arg        args[1];
  1891.     XmString    tcs;
  1892.  
  1893.     tcs = XmStringLtoRCreate(label, XmSTRING_DEFAULT_CHARSET);
  1894.  
  1895.     n = 0;
  1896.     XtSetArg(args[n], XmNlabelString, tcs);  n++;
  1897.     w = XmCreatePushButton(parent, "pushButton", args, n);
  1898.     XtManageChild(w);
  1899.  
  1900.     XmStringFree(tcs);
  1901.  
  1902.     return (w);
  1903. }
  1904.  
  1905.  
  1906.  
  1907.  
  1908. static XtCallbackProc HandleExpose(w, clientData, callData)
  1909.     Widget            w;
  1910.     XtPointer            clientData;
  1911.     XmDrawingAreaCallbackStruct    *callData;
  1912. {
  1913.     extern XtTimerCallbackProc     Tick();    
  1914.  
  1915.     /*
  1916.      *  Ignore if more expose events for this window in the queue
  1917.      */
  1918.     if (((XExposeEvent *)(callData->event))->count > 0) {
  1919.     return;
  1920.     }
  1921.  
  1922.  
  1923.     /*
  1924.      *  Redraw the clock face in the correct mode
  1925.      */
  1926.     switch (clockMode) {
  1927.     case ANALOG_CLOCK : {
  1928.         if (numSegs != 0) {
  1929.         extern void EraseHands();
  1930.  
  1931.         EraseHands(w, (struct tm *)NULL);
  1932.         }
  1933.         DrawClockFace(secondHandLength, radius);
  1934.         break;
  1935.     }
  1936.     case CAT_CLOCK : {
  1937.         DrawClockFace(secondHandLength, radius);
  1938.         break;
  1939.     }
  1940.     case DIGITAL_CLOCK : {
  1941.         DrawClockFace(secondHandLength, radius);
  1942.         break;
  1943.     }
  1944.     }
  1945.  
  1946.     /*
  1947.      *  Call the Tick routine so that the face gets
  1948.      *  refreshed with the correct time.  However, call
  1949.      *  it with arg #2 as "False", so the ticking doesn't
  1950.      *  propagate.
  1951.      */
  1952.     Tick(w, False);
  1953. }
  1954.  
  1955.  
  1956.  
  1957.     
  1958. static XtTimerCallbackProc Tick(w, add)
  1959.     Widget    w;
  1960.     int        add;
  1961. {
  1962.     static Bool     beeped = False;       /*  Beeped already?        */
  1963.     time_t        timeValue;       /*  What time is it?        */
  1964.  
  1965.     /*
  1966.      *  Don't do anything when clock is iconified.  Ticking will
  1967.      *  restart when the clock is de-iconifed.
  1968.      */
  1969.     if (iconified) {
  1970.     return;
  1971.     }
  1972.  
  1973.     /*
  1974.      *  Get the time and convert.
  1975.      */
  1976.     time(&timeValue);
  1977.     tm = *localtime(&timeValue);
  1978.  
  1979.  
  1980.     /*
  1981.      *  If ticking is to continue, add the next timeout
  1982.      */
  1983.     if (add) {
  1984.     switch (clockMode) {
  1985.         case ANALOG_CLOCK  :
  1986.             case DIGITAL_CLOCK : {
  1987.         if (evenUpdate) {
  1988.             int    t1, t2;
  1989.  
  1990.             t1 = appData.update - tm.tm_sec;
  1991.             t2 = (tm.tm_sec / appData.update) * appData.update;
  1992.             t1 += t2;
  1993.             XtAppAddTimeOut(appContext, t1 * 1000,
  1994.                     Tick, w);
  1995.         } else {
  1996.             XtAppAddTimeOut(appContext, appData.update * 1000,
  1997.                     Tick, w);
  1998.         }
  1999.         break;
  2000.         }
  2001.         case CAT_CLOCK : {
  2002.         XtAppAddTimeOut(appContext, appData.update, Tick, w);
  2003.         break;
  2004.         }
  2005.     }
  2006.     }
  2007.  
  2008.  
  2009.     /*
  2010.      *  Beep on the half hour; double-beep on the hour.
  2011.      */
  2012.     if (appData.chime) {
  2013.     if (beeped && (tm.tm_min != 30) && (tm.tm_min != 0)) {
  2014.         beeped = 0;
  2015.     }
  2016.     if (((tm.tm_min == 30) || (tm.tm_min == 0)) && (!beeped)) {
  2017.         beeped = 1;
  2018.         XBell(dpy, 50);
  2019.         if (tm.tm_min == 0) {
  2020.         XBell(dpy, 50);
  2021.         }
  2022.     }
  2023.     }
  2024.     
  2025.     switch (clockMode) {
  2026.     case ANALOG_CLOCK : 
  2027.         case CAT_CLOCK    : {
  2028.         /*
  2029.          *  The second (or minute) hand is sec (or min) 
  2030.          *  sixtieths around the clock face. The hour hand is
  2031.          *  (hour + min/60) twelfths of the way around the
  2032.          *  clock-face.  The derivation is left as an excercise
  2033.          *  for the reader.
  2034.          */
  2035.         
  2036.         /*
  2037.          *  12 hour clock.
  2038.          */
  2039.         if (tm.tm_hour > 12) {
  2040.         tm.tm_hour -= 12;
  2041.         }
  2042.         
  2043.         if (clockMode == ANALOG_CLOCK) {
  2044.         EraseHands((Widget)NULL, &tm);
  2045.         }
  2046.  
  2047.         if (numSegs == 0 ||    tm.tm_min != otm.tm_min ||
  2048.         tm.tm_hour != otm.tm_hour) {
  2049.         
  2050.         segBufPtr = segBuf;
  2051.         numSegs = 0;
  2052.         
  2053.         if (clockMode == CAT_CLOCK) {
  2054.             DrawClockFace(secondHandLength, radius);
  2055.         }
  2056.         
  2057.         /*
  2058.          *  Calculate the minute hand, fill it in with its
  2059.          *  color and then outline it.  Next, do the same
  2060.          *  with the hour hand.  This is a cheap hidden
  2061.          *  line algorithm.
  2062.          */
  2063.         DrawHand(minuteHandLength, handWidth,
  2064.              ((double) tm.tm_min)/60.0);
  2065.         if (appData.handColor != appData.background) {
  2066.             XFillPolygon(dpy,
  2067.                  clockWindow, handGC,
  2068.                  segBuf, VERTICES_IN_HANDS + 2,
  2069.                  Convex, CoordModeOrigin);
  2070.         }
  2071.         
  2072.         XDrawLines(dpy,
  2073.                clockWindow, highGC,
  2074.                segBuf, VERTICES_IN_HANDS + 2,
  2075.                CoordModeOrigin);
  2076.         
  2077.         DrawHand(hourHandLength, handWidth,
  2078.              ((((double)tm.tm_hour) + 
  2079.                (((double)tm.tm_min) / 60.0)) / 12.0));
  2080.         
  2081.         if (appData.handColor != appData.background) {
  2082.             XFillPolygon(dpy,
  2083.                  clockWindow, handGC,
  2084.                  &(segBuf[VERTICES_IN_HANDS + 2]),
  2085.                  VERTICES_IN_HANDS + 2,
  2086.                  Convex, CoordModeOrigin);
  2087.         }
  2088.  
  2089.         XDrawLines(dpy,
  2090.                clockWindow, highGC,
  2091.                &(segBuf[VERTICES_IN_HANDS + 2]),
  2092.                VERTICES_IN_HANDS + 2,
  2093.                CoordModeOrigin);
  2094.         }
  2095.         
  2096.         if (clockMode == ANALOG_CLOCK) {
  2097.         if (showSecondHand) {
  2098.             numSegs = 2 * (VERTICES_IN_HANDS + 2);
  2099.             segBufPtr = &(segBuf[numSegs]);
  2100.  
  2101.             DrawSecond(secondHandLength - 2, secondHandWidth,
  2102.                    minuteHandLength + 2,
  2103.                    ((double)tm.tm_sec) / 60.0);
  2104.  
  2105.             if (appData.handColor != appData.background) {
  2106.             XFillPolygon(dpy, clockWindow, handGC,
  2107.                      &(segBuf[(VERTICES_IN_HANDS + 2) * 2]),
  2108.                      VERTICES_IN_HANDS * 2 - 1,
  2109.                      Convex, CoordModeOrigin);
  2110.             }
  2111.             
  2112.             XDrawLines(dpy, clockWindow, highGC,
  2113.                    &(segBuf[(VERTICES_IN_HANDS + 2) * 2]),
  2114.                    VERTICES_IN_HANDS * 2 - 1,
  2115.                    CoordModeOrigin);
  2116.         }
  2117.         } else {
  2118.         UpdateEyesAndTail();
  2119.         }
  2120.         
  2121.         break;
  2122.     }
  2123.     
  2124.     case DIGITAL_CLOCK : {
  2125.         char *timePtr;
  2126.         
  2127.         timePtr = asctime(&tm);
  2128.         DigitalString(timePtr);
  2129.         XDrawImageString(dpy, clockWindow, gc,
  2130.                  digitalX, digitalY,
  2131.                  timePtr, strlen(timePtr));
  2132.         break;
  2133.     }
  2134.     }
  2135.     
  2136.     otm = tm;
  2137.     
  2138.     if (clockMode == CAT_CLOCK) {
  2139.     XSync(dpy, False);
  2140.     } else {
  2141.     XFlush(dpy);
  2142.     }
  2143. }
  2144.  
  2145.  
  2146.  
  2147. static XtCallbackProc HandleInput(w, clientData, callData)
  2148.     Widget            w;
  2149.     XtPointer            clientData;
  2150.     XmDrawingAreaCallbackStruct    *callData;
  2151. {
  2152.     int         n;
  2153.     Arg             args[10];
  2154.     static int        menuInited = False;
  2155.     static Widget    menuShell, menu, setW, bellW, chimeW,
  2156.                 ackW, rereadW, editW, exitW;
  2157.     static Widget    sepW1, sepW2;
  2158.  
  2159.     extern XtCallbackProc    AlarmSetCallback(),
  2160.                 AlarmBellCallback(),
  2161.                 ChimeCallback(),
  2162.                 AckAlarmCallback(),
  2163.                 RereadAlarmCallback(),
  2164.                     EditAlarmCallback(),
  2165.                 ExitCallback();
  2166.     
  2167.     if (callData->event->type != ButtonPress) {
  2168.     return;
  2169.     }
  2170.     
  2171.     if (((XButtonEvent *)(callData->event))->button != Button1) {
  2172.     return;
  2173.     }
  2174.     
  2175.     if (!menuInited) {
  2176.     menuInited = True;
  2177.     
  2178.     n = 0;
  2179.     XtSetArg(args[n], XmNwidth,  100);    n++;
  2180.     XtSetArg(args[n], XmNheight, 100);    n++;
  2181.     menuShell = XmCreateMenuShell(w, "menuShell", args, n);
  2182.     
  2183.     n = 0;
  2184.     XtSetArg(args[n], XmNrowColumnType, XmMENU_POPUP);    n++;
  2185.     XtSetArg(args[n], XmNwhichButton,   1);            n++;
  2186.     menu = XmCreateRowColumn(menuShell, "menu", args, n);
  2187.     
  2188.     
  2189.     n = 0;
  2190.     sepW1 = XmCreateSeparator(menu, "sepW1", args, n);
  2191.     XtManageChild(sepW1);
  2192.     
  2193.     setW = CreateToggle("Alarm Set", menu);
  2194.     n = 0;
  2195.     XtSetArg(args[n], XmNset, appData.alarmSet ? True : False);    n++;
  2196.     XtSetValues(setW, args, n);
  2197.     XtAddCallback(setW, XmNvalueChangedCallback,
  2198.               AlarmSetCallback, NULL);
  2199.     
  2200.     bellW = CreateToggle("Alarm Bell", menu);
  2201.     n = 0;
  2202.     XtSetArg(args[n], XmNset, appData.alarmBell ?  True : False);    n++;
  2203.     XtSetValues(bellW, args, n);
  2204.     XtAddCallback(bellW, XmNvalueChangedCallback,
  2205.               AlarmBellCallback, NULL);
  2206.     
  2207.     chimeW = CreateToggle("Chime", menu);
  2208.     n = 0;
  2209.     XtSetArg(args[n], XmNset, appData.chime ?  True : False);    n++;
  2210.     XtSetValues(chimeW, args, n);
  2211.     XtAddCallback(chimeW, XmNvalueChangedCallback,
  2212.               ChimeCallback, NULL);
  2213.     
  2214.     ackW = CreatePushButton("Acknowledge Alarm", menu);
  2215.     XtAddCallback(ackW, XmNactivateCallback,
  2216.               AckAlarmCallback, NULL);
  2217.     
  2218.     rereadW = CreatePushButton("Reread Alarm File", menu);
  2219.     XtAddCallback(rereadW, XmNactivateCallback,
  2220.               RereadAlarmCallback, setW);
  2221.  
  2222.     editW = CreatePushButton("Edit Alarm File", menu);
  2223.     XtAddCallback(editW, XmNactivateCallback,
  2224.               EditAlarmCallback, setW);
  2225.     
  2226.     n = 0;
  2227.     sepW2 = XmCreateSeparator(menu, "sepW2", args, n);
  2228.     XtManageChild(sepW2);
  2229.     
  2230.     exitW = CreatePushButton("Exit", menu);
  2231.     XtAddCallback(exitW, XmNactivateCallback, ExitCallback, NULL);
  2232.     }
  2233.     
  2234.     XmMenuPosition(menu, callData->event);
  2235.     XtManageChild(menu);
  2236. }
  2237.  
  2238.  
  2239.  
  2240.  
  2241. static XtCallbackProc AlarmSetCallback(w, clientData, callData)
  2242.     Widget                w;
  2243.     XtPointer                clientData;
  2244.     XmToggleButtonCallbackStruct    *callData;
  2245. {
  2246.     if (appData.alarmSet = appData.alarmSet ? False : True) {
  2247.     SetAlarm(appData.alarmFile);
  2248.     } else {
  2249.     if (alarmOn) {
  2250.         AlarmOff();
  2251.         DrawClockFace(secondHandLength, radius);
  2252.     }
  2253.     SetAlarm(NULL);
  2254.     }
  2255. }
  2256.  
  2257.  
  2258.  
  2259. static XtCallbackProc AlarmBellCallback(w, clientData, callData)
  2260.     Widget                w;
  2261.     XtPointer                clientData;
  2262.     XmToggleButtonCallbackStruct    *callData;
  2263. {
  2264.     SetBell((appData.alarmBell = appData.alarmBell ? False : True) ?
  2265.         appData.alarmBellPeriod : 0);
  2266. }
  2267.  
  2268.  
  2269.  
  2270. static XtCallbackProc ChimeCallback(w, clientData, callData)
  2271.     Widget                w;
  2272.     XtPointer                clientData;
  2273.     XmToggleButtonCallbackStruct    *callData;
  2274. {
  2275.     appData.chime = appData.chime ? False : True;
  2276. }
  2277.  
  2278.  
  2279.  
  2280. static XtCallbackProc AckAlarmCallback(w, clientData, callData)
  2281.     Widget            w;
  2282.     XtPointer            clientData;
  2283.     XmPushButtonCallbackStruct    *callData;
  2284. {
  2285.     if (alarmOn) {
  2286.     AlarmOff();
  2287.     SetAlarm(appData.alarmSet ? appData.alarmFile : NULL);
  2288.     DrawClockFace(secondHandLength, radius);    
  2289.     }
  2290. }
  2291.  
  2292.  
  2293.  
  2294. static XtCallbackProc RereadAlarmCallback(w, setW, callData)
  2295.     Widget            w;
  2296.     Widget            setW;
  2297.     XmPushButtonCallbackStruct    *callData;
  2298. {
  2299.     int        n;
  2300.     Arg        args[1];
  2301.  
  2302.     appData.alarmSet = True;
  2303.  
  2304.     if (alarmOn) {
  2305.     AlarmOff();
  2306.     DrawClockFace(secondHandLength, radius);
  2307.     }
  2308.  
  2309.     SetAlarm(appData.alarmFile);
  2310.  
  2311.     n = 0;
  2312.     XtSetArg(args[n], XmNset, True);    n++;
  2313.     XtSetValues(setW, args, n);
  2314. }
  2315.  
  2316.  
  2317.  
  2318.  
  2319. static XtCallbackProc EditAlarmCallback(w, setW, callData)
  2320.     Widget            w;
  2321.     Widget            setW;
  2322.     XmPushButtonCallbackStruct    *callData;
  2323. {
  2324.     extern char    *getenv();
  2325.     char    *editor;
  2326.     char    cmdString[80];
  2327.  
  2328.     editor = getenv("EDITOR");
  2329.  
  2330.     if (editor == NULL) {
  2331.     strcpy(cmdString, "xterm -e vi ");
  2332.     strcat(cmdString, " \0");
  2333. #ifdef HAS_GNU_EMACS
  2334.     } else if (strcmp(editor, "emacs") == 0) {
  2335.     strcpy(cmdString, "emacs ");
  2336.     strcat(cmdString, " ");
  2337. #else
  2338.     } else if (strcmp(editor, "emacs") == 0) {
  2339.     strcpy(cmdString, "xterm -e emacs ");
  2340.     strcat(cmdString, " ");
  2341. #endif  /*  HAS_GNU_EMACS  */
  2342.     } else if (strcmp(editor, "vi") == 0) {
  2343.     strcpy(cmdString, "xterm -e vi ");
  2344.     strcat(cmdString, " ");
  2345.     } else {
  2346.     strcpy(cmdString, "xterm -e ");
  2347.     strcat(cmdString, " ");
  2348.     strcat(cmdString, editor);
  2349.     strcat(cmdString, " ");
  2350.     }
  2351.  
  2352.     strcat(cmdString, appData.alarmFile);
  2353.  
  2354.     if (system(cmdString) != 127) {
  2355.     RereadAlarmCallback(w, setW, callData);
  2356.     }
  2357. }
  2358.  
  2359.  
  2360.  
  2361.  
  2362. static XtCallbackProc ExitCallback(w, clientData, callData)
  2363.     Widget            w;
  2364.     XtPointer            clientData;
  2365.     XmPushButtonCallbackStruct    *callData;
  2366. {
  2367.     exit(0);
  2368. }
  2369.  
  2370.     
  2371.  
  2372.  
  2373. static XtCallbackProc HandleResize(w, clientData, callData)
  2374.     Widget            w;
  2375.     XtPointer            clientData;
  2376.     XmDrawingAreaCallbackStruct    *callData;
  2377. {
  2378.     XWindowAttributes xwa;
  2379.     
  2380.     XGetWindowAttributes(dpy, clockWindow, &xwa);
  2381.  
  2382.     switch (clockMode) {
  2383.     case ANALOG_CLOCK : {
  2384.         if ((xwa.width != winWidth) || (xwa.height != winHeight)) {
  2385.         winWidth  = xwa.width;
  2386.         winHeight = xwa.height;
  2387.         
  2388.         radius = (min(winWidth,
  2389.                   winHeight) - (2 * appData.padding)) / 2;
  2390.         
  2391.         secondHandLength = ((SECOND_HAND_FRACT  * radius) / 100);
  2392.         minuteHandLength = ((MINUTE_HAND_FRACT  * radius) / 100);
  2393.         hourHandLength   = ((HOUR_HAND_FRACT    * radius) / 100);
  2394.         
  2395.         handWidth        = ((HAND_WIDTH_FRACT   * radius) / 100);
  2396.         secondHandWidth  = ((SECOND_WIDTH_FRACT * radius) / 100);
  2397.  
  2398.         centerX = winWidth  / 2;
  2399.         centerY = winHeight / 2;
  2400.  
  2401.         DrawClockFace(secondHandLength, radius);
  2402.         }
  2403.  
  2404.         break;
  2405.     }
  2406.     case CAT_CLOCK : {
  2407.         printf("HandleResize : shouldn't have been called with cat\n");
  2408.  
  2409.         break;
  2410.     }
  2411.     case DIGITAL_CLOCK : {
  2412.         int        stringWidth, stringHeight;
  2413.         int        ascent, descent;
  2414.         int        stringDir;
  2415.         XCharStruct    xCharStr;
  2416.         char    *timePtr;
  2417.         time_t    timeValue;
  2418.         int        bellWidth, bellHeight;
  2419.  
  2420.         /*
  2421.          * Get font dependent information and determine window
  2422.          * size from a test string.
  2423.          */
  2424.         time(&timeValue);
  2425.         timePtr = ctime(&timeValue);
  2426.         DigitalString(timePtr);
  2427.  
  2428.         XTextExtents(appData.font, timePtr, strlen(timePtr),
  2429.              &stringDir, &ascent, &descent, &xCharStr);
  2430.         
  2431.         stringHeight = ascent + descent;
  2432.         stringWidth = XTextWidth(appData.font, timePtr, strlen(timePtr));
  2433.         
  2434.         GetBellSize(&bellWidth, &bellHeight);
  2435.         
  2436.         digitalX = (int)(0.5 + (xwa.width / 2.0 - stringWidth / 2.0));
  2437.         if (digitalX < bellWidth + appData.padding) {
  2438.         digitalX = bellWidth + appData.padding;
  2439.         }
  2440.         
  2441.         digitalY = (int)(0.5 + (xwa.height - stringHeight) / 2.0 + ascent);
  2442.         if (digitalY < 0) {
  2443.         digitalY = 0;
  2444.         }
  2445.  
  2446.         DrawClockFace(secondHandLength, radius);
  2447.         
  2448.         break;
  2449.     }
  2450.     }
  2451.  
  2452.     /*
  2453.      *  Call the Tick routine so that the face gets
  2454.      *  refreshed with the correct time.  However, call
  2455.      *  it with arg #2 as "False", so the ticking doesn't
  2456.      *  propagate.
  2457.      */
  2458.     Tick(w, False);
  2459. }
  2460.  
  2461.  
  2462.  
  2463. static void EraseHands(w, tm)
  2464.     Widget    w;
  2465.     struct tm    *tm;
  2466. {
  2467.     if (numSegs > 0) {
  2468.     if (showSecondHand) {
  2469.         XDrawLines(dpy, clockWindow, eraseGC,
  2470.                &(segBuf[2 * (VERTICES_IN_HANDS + 2)]),
  2471.                VERTICES_IN_HANDS * 2 - 1,
  2472.                CoordModeOrigin);
  2473.  
  2474.         if (appData.handColor != appData.background) {
  2475.         XFillPolygon(dpy, clockWindow, eraseGC,
  2476.                  &(segBuf[2 * (VERTICES_IN_HANDS + 2)]),
  2477.                  VERTICES_IN_HANDS * 2 - 1,
  2478.                  Convex, CoordModeOrigin);
  2479.         }
  2480.     }
  2481.     
  2482.     if (!tm || tm->tm_min != otm.tm_min || tm->tm_hour != otm.tm_hour) {
  2483.         XDrawLines(dpy, clockWindow, eraseGC,
  2484.                segBuf, VERTICES_IN_HANDS + 2,
  2485.                CoordModeOrigin);
  2486.  
  2487.         XDrawLines(dpy, clockWindow, eraseGC,
  2488.                &(segBuf[VERTICES_IN_HANDS + 2]), VERTICES_IN_HANDS,
  2489.                CoordModeOrigin);
  2490.  
  2491.         if (appData.handColor != appData.background) {
  2492.         XFillPolygon(dpy, clockWindow, eraseGC,
  2493.                  segBuf, VERTICES_IN_HANDS + 2,
  2494.                  Convex, CoordModeOrigin);
  2495.  
  2496.         XFillPolygon(dpy, clockWindow, eraseGC,
  2497.                  &(segBuf[VERTICES_IN_HANDS + 2]),
  2498.                  VERTICES_IN_HANDS + 2,
  2499.                  Convex, CoordModeOrigin);
  2500.         }
  2501.     }
  2502.     }
  2503. }
  2504.  
  2505.  
  2506. static XtEventHandler PropertyChangeCallback(w, clientData,
  2507.                          event, continueToDispatch)
  2508.     Widget        w;
  2509.     XtPointer        clientData;
  2510.     XPropertyEvent    *event;
  2511.     Boolean        *continueToDispatch;
  2512. {
  2513.     Atom         actualType;
  2514.     int          actualFormat;
  2515.     unsigned long     nItems, bytesAfter;
  2516.     unsigned char     *data;
  2517.     long             *state;
  2518.     
  2519.     *continueToDispatch = True;
  2520.  
  2521.     if (event->type == PropertyNotify) {
  2522.  
  2523. #ifdef DEBUG
  2524.     printf("Got PropertyNotify event\n");
  2525.     printf("Atom is %s, the state is %d\n",
  2526.            XGetAtomName(dpy, event->atom), event->state);
  2527. #endif /*  DEBUG  */    
  2528.  
  2529.     XGetWindowProperty(event->display, event->window, event->atom,
  2530.                0L, 1L, False, AnyPropertyType,  &actualType,
  2531.                &actualFormat, &nItems, &bytesAfter, &data);
  2532.  
  2533. #ifdef DEBUG    
  2534.     printf("actualType: %s, actualFormat: %d, nItems: %d, bytesAfter: %d\n",
  2535.            XGetAtomName(dpy, actualType), actualFormat, nItems, bytesAfter);
  2536. #endif /*  DEBUG  */    
  2537.     
  2538.     state = (long *)data;
  2539.     
  2540.     switch (*state) {
  2541.         case NormalState : {
  2542.         if (iconified) {
  2543.             iconified = False;
  2544.             Tick(XtWindowToWidget(dpy, clockWindow), True);
  2545.         }
  2546.         break;
  2547.         }
  2548.         case IconicState : {
  2549.         if (!iconified) {
  2550.             iconified = True;
  2551.         }
  2552.         break;
  2553.         }
  2554.         case WithdrawnState : {
  2555.         break;
  2556.         }
  2557.     }
  2558.     }
  2559. }
  2560.  
  2561.  
  2562. static XtEventHandler MapCallback(w, clientData, event)
  2563.     Widget    w;
  2564.     XtPointer    clientData;
  2565.     XEvent    *event;
  2566. {
  2567.     if (event->type == MapNotify) {
  2568.     iconified = False;
  2569.     XtRemoveEventHandler(w, StructureNotifyMask, False,
  2570.                  MapCallback, (XtPointer)NULL);
  2571.     XtAddEventHandler(w, PropertyChangeMask, False,
  2572.               PropertyChangeCallback, (XtPointer)NULL);
  2573.     XSync(dpy, False);
  2574.     Tick(XtWindowToWidget(dpy, clockWindow), True);
  2575.     }
  2576. }
  2577.