#include /* Copyright 1985 Massachusetts Institute of Technology */ /* * xclock.c MIT Project Athena, X Window system clock. * * This program provides the user with a small * window contining a digital clock with day and date. * Parameters are variable from the command line. * * Author: Tony Della Fera, DEC * September, 1984 * Hacked up by a cast of thousands.... */ #include #include #include #include #define PI 3.141592 #define SEG_BUFF_SIZE 128 #define SECOND_HAND_FRACT 90 #define MINUTE_HAND_FRACT 70 #define HOUR_HAND_FRACT 40 #define SECOND_HAND_TIME 30 #define DEF_UPDATE 60 #define DEF_BORDER 2 #define DEF_VECTOR_HEIGHT 1 #define DEF_VECTOR_WIDTH 1 #define DEF_DIGITAL_PADDING 10 #define DEF_DIGITAL_FONT "6x10" #define DEF_ANALOG_PADDING 8 #define DEF_ANALOG_WIDTH 164 #define DEF_ANALOG_HEIGHT 164 #define DEF_BORDER_COLOR BlackPixel #define DEF_HIGH_COLOR BlackPixel #define DEF_FRGRND_COLOR BlackPixel #define DEF_BKGRND_COLOR WhitePixel #define UNINIT -1 #define FAILURE 0 #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #define abs(a) ((a) < 0 ? -(a) : (a)) typedef enum _bool {FALSE, TRUE} Bool; Bool XClockDebug = FALSE; Bool AnalogClock = FALSE; Bool ShowSecondHand = FALSE; Bool Reverse = FALSE; int CenterX = 0; int CenterY = 0; int NumSegs = 0; int FrgrndColor; int BkgrndColor; int BorderColor; int HighColor; Vertex SegBuff[SEG_BUFF_SIZE]; Vertex *SegBuffPtr; Window ClockWindow; main(argc, argv) int argc; char **argv; { char time_string[26]; register char *time_ptr = time_string; #ifdef DEBUG register Bool debug = XClockDebug; #endif register int i; register int radius; register int padding = UNINIT; /* UNINIT = parameter uninitialized. */ int border = UNINIT; /* UNINIT = parameter uninitialized. */ int update = UNINIT; /* UNINIT = parameter uninitialized. */ int win_width = UNINIT; /* UNINIT = parameter uninitialized. */ int win_height = UNINIT; /* UNINIT = parameter uninitialized. */ int second_hand_length; int minute_hand_length; int hour_hand_length; int readfds = 0; int maxfds = 0; int string_width; int status; Bool even_update = FALSE; long time_value; char *index(); char *ctime(), asctim(); char *strind; char *fn = DEF_DIGITAL_FONT; char *fore_color; char *back_color; char *high_color; char *brdr_color; char *geom = NULL; char *display = NULL; int rvflag = 0; Color cdef; struct tm *localtime(); struct timeval timeout; struct tm tm; struct tm otm; Font font; FontInfo font_info; XEvent event; char *def_val; def_val = XGetDefault(argv[0], "BorderWidth"); if (def_val != NULL) border = atoi(def_val); def_val = XGetDefault(argv[0], "BodyFont"); if (def_val != NULL) fn = def_val; fore_color = XGetDefault(argv[0], "Foreground"); back_color = XGetDefault(argv[0], "Background"); high_color = XGetDefault(argv[0], "Highlight"); brdr_color = XGetDefault(argv[0], "Border"); def_val = XGetDefault(argv[0], "ReverseVideo"); if(def_val != NULL && strcmp(def_val, "on") == 0) rvflag++; def_val = XGetDefault(argv[0], "InternalBorder"); if (def_val != NULL) padding = atoi(def_val); if ((def_val = XGetDefault(argv[0], "Mode")) != NULL) { if (strcmp(def_val, "analog") == 0) AnalogClock = TRUE; if (strcmp(def_val, "digital") == 0) AnalogClock = FALSE; if (strcmp(def_val, "Analog") == 0) AnalogClock = TRUE; if (strcmp(def_val, "Digital") == 0) AnalogClock = FALSE; } def_val = XGetDefault(argv[0],"Update"); if (def_val != NULL) update = atoi(def_val); for (i = 1; i < argc; i++) { if (argv [i] [0] == '=') { geom = argv[i]; continue; } strind = index(argv[i], ':'); if(strind != NULL) { display = argv[i]; continue; } strind = index(argv [i], '-'); if (strind == NULL) Syntax(argv[0]); if (strcmp(argv [i], "-a") == 0 || strcmp(argv [i], "-analog") == 0) { AnalogClock = TRUE; continue; } if (strcmp(argv [i], "-d") == 0 || strcmp(argv [i], "-digital") == 0) { AnalogClock = FALSE; continue; } if (strcmp(argv [i], "-bw") == 0 || strcmp(argv [i], "-border") == 0) { if (++i >= argc) Syntax(argv[0]); border = atoi(argv [i]); continue; } #ifdef DEBUG if (strcmp(argv [i], "-debug") == 0) { XClockDebug = TRUE; debug = TRUE; continue; } #endif if (strcmp(argv [i], "-fn") == 0 || strcmp(argv [i], "-font") == 0) { if (++i >= argc) Syntax(argv[0]); fn = argv [i]; continue; } if (strcmp(argv [i], "-fg") == 0 || strcmp(argv [i], "-foreground") == 0) { if (++i >= argc) Syntax(argv[0]); fore_color = argv [i]; continue; } if (strcmp(argv [i], "-bg") == 0 || strcmp(argv [i], "-background") == 0) { if (++i >= argc) Syntax(argv[0]); back_color = argv [i]; continue; } if (strcmp(argv [i], "-hl") == 0 || strcmp(argv [i], "-highlight") == 0) { if (++i >= argc) Syntax(argv[0]); high_color = argv [i]; continue; } if (strcmp(argv [i], "-bd") == 0 || strcmp(argv [i], "-bordercolor") == 0) { if (++i >= argc) Syntax(argv[0]); brdr_color = argv [i]; continue; } if (strcmp(argv [i], "-help") == 0) { Syntax(argv[0]); } if (strcmp(argv [i], "-p") == 0 || strcmp(argv [i], "-padding") == 0) { if (++i >= argc) Syntax(argv[0]); padding = atoi(argv [i]); continue; } if (strcmp(argv [i], "-rv") == 0 || strcmp(argv [i], "-reverse") == 0) { rvflag++; continue; } if (strcmp(argv [i], "-u") == 0 || strcmp(argv [i], "-update") == 0) { if (++i >= argc) Syntax(argv[0]); update = atoi(argv [i]); continue; } Syntax(argv[0]); } /* * Open up the display. */ if (XOpenDisplay(display) == NULL) { XClockError("Error while trying to open display"); } if (rvflag) Reverse = TRUE; /* * Set up colors and pixmaps. */ if (brdr_color != NULL) { if (DisplayCells() > 2) { if ( XParseColor(brdr_color, &cdef) && XGetHardwareColor(&cdef) ) BorderColor = cdef.pixel; else BorderColor = DEF_BORDER_COLOR; } else if (strcmp(brdr_color, "black") == 0) BorderColor = BlackPixel; else if (strcmp(brdr_color, "white") == 0) BorderColor = WhitePixel; else BorderColor = DEF_BORDER_COLOR; } else BorderColor = DEF_BORDER_COLOR; if (fore_color != NULL) { if (DisplayCells() > 2) { if ( XParseColor(fore_color, &cdef) && XGetHardwareColor(&cdef) ) FrgrndColor = cdef.pixel; else FrgrndColor = DEF_FRGRND_COLOR; } else if (strcmp(fore_color, "black") == 0) FrgrndColor = BlackPixel; else if (strcmp(fore_color, "white") == 0) FrgrndColor = WhitePixel; else FrgrndColor = DEF_FRGRND_COLOR; } else FrgrndColor = DEF_FRGRND_COLOR; if (back_color != NULL) { if (DisplayCells() > 2) { if ( XParseColor(back_color, &cdef) && XGetHardwareColor(&cdef) ) BkgrndColor = cdef.pixel; else BkgrndColor = DEF_BKGRND_COLOR; } else if (strcmp(back_color, "black") == 0) BkgrndColor = BlackPixel; else if (strcmp(back_color, "white") == 0) BkgrndColor = WhitePixel; else BkgrndColor = DEF_BKGRND_COLOR; } else BkgrndColor = DEF_BKGRND_COLOR; if ((high_color != NULL) && AnalogClock) { if (DisplayCells() > 2) { if ( XParseColor(high_color, &cdef) && XGetHardwareColor(&cdef) ) HighColor = cdef.pixel; else HighColor = DEF_HIGH_COLOR; } else if (strcmp(high_color, "black") == 0) HighColor = BlackPixel; else if (strcmp(high_color, "white") == 0) HighColor = WhitePixel; else HighColor = DEF_HIGH_COLOR; } else HighColor = DEF_HIGH_COLOR; if (Reverse) { HighColor = BorderColor = BkgrndColor; BkgrndColor = FrgrndColor; FrgrndColor = HighColor; } /* * Set up analog and digital specific defaults. */ if (AnalogClock == TRUE) { /* * Set analog defaults. */ if (padding == UNINIT) padding = DEF_ANALOG_PADDING; if (update == UNINIT) update = DEF_UPDATE; if (update <= SECOND_HAND_TIME) ShowSecondHand = TRUE; /* * Initialize the segment buffer and segment buffer pointer. */ SegBuffPtr = SegBuff; /* * Initialize the number of "segments" in the buffer; each * segment is one Vertex or 3 shorts, an x value, a y value * and the flags as passed to XDraw. */ NumSegs = 0; } else { /* * Set digital defaults. */ if (padding == UNINIT) padding = DEF_DIGITAL_PADDING; if (update == UNINIT) update = DEF_UPDATE; /* * Get font dependent information and determine window * size from a test string. */ time(&time_value); time_ptr = ctime(&time_value); time_ptr[strlen(time_ptr) - 1] = 0; font = XGetFont(fn); if (font == FAILURE) XClockError("Can't get font"); status = XQueryFont(font, &font_info); if (status == FAILURE) XClockError("Can't query font"); string_width = XQueryWidth (time_ptr, font); } /* * Now set analog and digital independent defaults. */ if (border == UNINIT) border = DEF_BORDER; if (update > 1 && ((60 / update) * update == 60)) even_update = TRUE; /* * Open the main window. */ { int min_width, min_height; char default_geom[20]; OpaqueFrame frame; if (AnalogClock) { min_width = DEF_ANALOG_WIDTH/3; min_height = DEF_ANALOG_HEIGHT/3; sprintf (default_geom, "%dx%d-0-0", DEF_ANALOG_WIDTH, DEF_ANALOG_HEIGHT); } else { min_width = string_width + (2 * padding); min_height = font_info.height + (2 * padding); sprintf (default_geom, "%dx%d-0-0", min_width, min_height); } frame.bdrwidth = border; frame.border = XMakeTile (BorderColor); frame.background = XMakeTile (BkgrndColor); ClockWindow = XCreate ( AnalogClock ? "Analog XClock" : "Digital XClock", argv[0], geom, default_geom, &frame, min_width, min_height); if (ClockWindow == FAILURE) XClockError("Can't open clock window"); } /* * Select window exposure events to see if the contents of * the window have been erased or altered. Select unmap events so * we can stop output when iconified. */ XSelectInput(ClockWindow, ExposeWindow|UnmapWindow); /* * Map clock window to screen. */ XMapWindow(ClockWindow); /* * Initialize the select system call's maximum file * descriptor number to be one more than the file descriptor * number of the X connection. */ maxfds = dpyno() + 1; /* * Initialize the select timeout structure. */ timeout.tv_sec = update; timeout.tv_usec = 0; /* * Initialize the old-time structure. */ otm = *localtime(&time_value); /* * Synchronize X before proceeding. */ XSync(FALSE); /* * Main clock loop. */ while (TRUE) { time(&time_value); tm = *localtime(&time_value); if (even_update) { /* Truncate to update interval, get new timeout */ timeout.tv_sec = update - tm.tm_sec; tm.tm_sec = (tm.tm_sec / update) * update; timeout.tv_sec += tm.tm_sec; } if (AnalogClock == FALSE) { /* * See if there are any events pending that * arn't supposed to be there. */ if (XPending() != 0) { /* * There is an event pending so we must * check to see if it is an ExposeWindow * event, if it is anything else somthing * went wrong! */ XNextEvent(&event); if (event.type == UnmapWindow) { XPeekEvent(&event); continue; } if (event.type != ExposeWindow) { XClockError("Unexpected X_Event (digital mode)"); } } time_ptr = asctime(&tm); time_ptr[strlen(time_ptr) - 1] = 0; XTextPad ( ClockWindow, padding, padding, time_ptr, strlen(time_ptr), font, 0, 0, FrgrndColor, BkgrndColor, GXcopy, AllPlanes ); } else { /* * Look for an X_Event associated with xclock. */ if (XPending() != 0) { /* * There is an event pending so we must * check to see if it is an ExposeWindow * event, if it is anything else, somthing * went wrong! */ XNextEvent(&event); if (event.type == UnmapWindow) { XPeekEvent(&event); continue; } if (event.type == ExposeWindow) { /* * Ok, we have a window exposure event, * refresh the clock face. Check to * see if the window has changed size. */ XExposeWindowEvent *exp_event = (XExposeWindowEvent *)&event; if ((exp_event->width != win_width) || (exp_event->height != win_height)) { win_width = exp_event->width; win_height = exp_event->height; radius = (min(win_width, win_height) -(2 * padding)) / 2; second_hand_length = ((SECOND_HAND_FRACT * radius) / 100); minute_hand_length = ((MINUTE_HAND_FRACT * radius) / 100); hour_hand_length = ((HOUR_HAND_FRACT * radius) / 100); CenterX = win_width / 2; CenterY = win_height / 2; } DrawClockFace(second_hand_length, radius); } else { /* * We should never get here! */ XClockError("Unexpected X_Event (analog mode)"); } } /* * The second (or minute) hand is sec (or min) * sixtieths around the clock face. The hour hand is * (hour + min/60) twelfths of the way around the * clock-face. The derivation is left as an excercise * for the reader. */ /* * Erase old hands. */ if (ShowSecondHand == TRUE) { DrawHand(1, second_hand_length, ((double) otm.tm_sec)/60.0); } DrawHand(1, minute_hand_length, ((double) otm.tm_min)/60.0); DrawHand(1, hour_hand_length, (((double)otm.tm_hour) + (((double)otm.tm_min)/60.0))/12.0); Sync(BkgrndColor); /* * 12 hour clock. */ if(tm.tm_hour > 12) tm.tm_hour -= 12; /* * Draw new hands. */ if (ShowSecondHand == TRUE) { DrawHand(1, second_hand_length, ((double) tm.tm_sec)/60.0); } DrawHand(1, minute_hand_length, ((double) tm.tm_min)/60.0); DrawHand(1, hour_hand_length, (((double) tm.tm_hour) + (((double) tm.tm_min)/60.0))/12.0); Sync(HighColor); /* * Make the new time now be the old time. */ otm = tm; } XFlush (); /* * Use the select system call on the file descriptor in * the display structure to determine if there is work * to be done. If not block untill timeout. Remember to * reset the file descriptor before each select. */ readfds = 1 << dpyno(); if (select(maxfds, &readfds, NULL, NULL, &timeout) == -1) XClockError("Error in select on display file descriptor"); } } /* * DrawHand - Draws (or erases) a hand. Fraction_of_a_circle is a * fraction between 0 and 1 (inclusive) indicating how far around the * circle (clockwise) high noon. * * Blank_length is the distance from the center which the hand begins * (logically 0, but I have the feature, I might as well use it). * length is the maximum length of the hand. * * The blank_length feature is because I wanted to draw tick-marks around the * circle (for seconds). The obvious means of drawing lines from the center * to the perimeter, then erasing all but the outside most pixels doesn't * work because of round-off error (sigh). */ DrawHand(blank_length, length, fraction_of_a_circle) int blank_length; int length; double fraction_of_a_circle; { double cos(); double sin(); double angle; /* * A full circle is 2 PI radians. * Angles are measured from 6 o'clock, not noon (or so it seems). * Thus, when fraction_of_a_circle is 0, we want angle to be PI, * and when fraction_of_a_circle is 1, we want angle to be 3 PI. * * Also, angles increase counter-clockwise, not clockwise, so we * throw in a factor of -1 so our clock doesn't run backwards. */ angle = (-2 * PI * fraction_of_a_circle) + PI; /* * Add the move instruction to the segment buffer and increment * the "next" pointer. */ SegBuffPtr->x = CenterX + (int)((float)blank_length * sin(angle)); SegBuffPtr->y = CenterY + (int)((float)blank_length * cos(angle)); SegBuffPtr->flags = VertexDontDraw; SegBuffPtr++; NumSegs++; /* * Add the new point to the buffer and increment the "next" pointer. */ SegBuffPtr->x = CenterX + (int)((float)length * sin(angle)); SegBuffPtr->y = CenterY + (int)((float)length * cos(angle)); SegBuffPtr->flags = VertexDrawLastPoint; SegBuffPtr++; NumSegs++; } /* * Draw the clock face (every fifth tick-mark is longer * than the others). */ DrawClockFace(second_hand, radius) int second_hand; int radius; { register Bool debug = XClockDebug; register int i; register int delta = (radius - second_hand) / 3; XClear(ClockWindow); for (i = 0; i < 60; i++) { if ((i % 5) == 0) { DrawHand(second_hand, radius, ((double) i)/60.); } else { DrawHand((radius - delta), radius, ((double) i)/60.); } } /* * Flush the buffer to the VS100. */ Sync(FrgrndColor); } /* * This routine synchronizes the segment buffer contents with the * VS100 screen. In effect, it flushes the buffer contents to the * screen. */ Sync(color) int color; /* X drawing color. */ { register Bool debug = XClockDebug; /* * Call the X draw curve routine. */ XDraw( ClockWindow, SegBuff, NumSegs, DEF_VECTOR_WIDTH, DEF_VECTOR_HEIGHT, color, GXcopy, AllPlanes ); /* * Reset the segment buffer pointer and the segment counter. */ SegBuffPtr = SegBuff; NumSegs = 0; } /* * Report the syntax for calling xclock. */ Syntax(call) char *call; { printf ("Usage: %s [-analog] [-bw ] [-digital]\n", call); printf (" [-fg ] [-bg ] [-hl ] [-bd ]\n"); printf (" [-fn ] [-help] [-padding ]\n"); printf (" [-rv] [-update ] [[]:[]]\n"); printf (" [=[][x][<+->[<+->]]]\n\n"); printf ("Default: %s -digital -bw %d -font %s -padding %d -update %d =-0-0\n\n", call, DEF_BORDER, DEF_DIGITAL_FONT, DEF_DIGITAL_PADDING, DEF_UPDATE); printf ("Default: %s -analog -bw %d -padding %d -update %d =%dx%d-0-0\n\n", call, DEF_BORDER, DEF_DIGITAL_PADDING, DEF_UPDATE, DEF_ANALOG_WIDTH, DEF_ANALOG_HEIGHT); printf ("Notes: The order in which switches are specified is not significant.\n"); printf (" In analog mode the second hand only appears for update times\n"); printf (" less than or equal to %d seconds.\n", SECOND_HAND_TIME); exit(0); } /* * XClockError - Fatal xclock error. */ XClockError (identifier) char *identifier; { register Bool debug = XClockDebug; if (debug) printf("XClockError: Fatal xclock error encountered.\n"); perror("xclock"); fprintf(stderr, "xclock: %s\n", identifier); exit(1); } /* * End of xclock.c */