Return to Home page [Home].
/* $Id: analogclock.ca,v 1.26 2017/04/27 17:53:20 rkiesling Exp $ */
/*
This file is part of analogclock.
Copyright © 2016 Robert Kiesling, rk3314042@gmail.com.
Permission is granted to copy this software provided that this copyright
notice is included in all source code modules.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <math.h>
Circle new rim;
Pen new rimPen;
Integer new centerX;
Integer new centerY;
Integer new radius;
Integer new winWidth;
Integer new winHeight;
Integer new displayWidth;
Integer new displayHeight;
Boolean new fullScreen;
List new tickMarks;
Pen new tickPen;
Integer new hoursLength;
Integer new minutesLength;
Integer new secondsLength;
Pen new secondsPen;
Pen new minutePen;
Pen new hourPen;
Integer new displayAlpha;
String new xFontDescStr;
String new ftFontDescStr;
Float new ftFontPtSize;
Boolean new useFTFonts;
Boolean new displayText;
Application new clockApp;
#define SECONDS_LENGTH (radius - (rimPen width / 2) - 1)
#define SECONDS_WIDTH (radius * 0.02)
#define HOURS_LENGTH (radius * 0.5)
#define HOURS_WIDTH (radius * 0.05)
#define MINUTES_LENGTH (radius * 0.7)
#define MINUTES_WIDTH (radius * 0.04)
/* How far from the center a tickmark begins. These dimensions
are given as a fraction of the clock's radius. */
#define BIG_TICK_START_PCT 0.85
#define SMALL_TICK_START_PCT 0.90
/* Width of the rim's border. */
#define RIM_WIDTH (radius * 0.04)
#define RIM_WIDTH_FULLSCREEN (radius * 0.02)
/* Margin between the rim and the edge of the window. */
#define WIN_MARGIN RIM_WIDTH
/* Pixels of space between the rim's centerline and the
outer end of a tick mark. */
#define TICK_MARGIN (RIM_WIDTH + (RIM_WIDTH * 0.2))
#define TICK_MARGIN_FULLSCREEN (RIM_WIDTH)
/* Default window dimensions. */
#define WIN_WIDTH 200
#define WIN_HEIGHT 200
#define WIN_X 10
#define WIN_Y 10
/* From the #defines in X11/keysymdef.h */
#define X_Escape_keycode 0xff1b
#define X_Control_L_keycode 0xffe3
#define X_Control_R_keycode 0xffe4
#define CTRL_C 3
/* The default alpha value and the alpha limit. */
#define DEFAULT_ALPHA 0x8888
#define OPAQUE 0xffff;
#define DEFAULT_HAND_COLOR "slategray4"
#define DEFAULT_RIM_COLOR "darkslategray"
/*
* Font stuff
*/
X11Font new xFont;
X11FreeTypeFont new ftFont;
Integer new air;
/* Freetype outline fonts. The default font(s) are Type 1, and
are in the ghostscript package(s). DejaVu is often included
in distros as a package of its own. */
#define DEFAULT_FT_FONT "Nimbus Sans L"
#define FALLBACK_FT_FONT "DejaVu Sans"
/* If you prefer an arty look, uncomment this line. */
/* #define BAUHAUSE */
#ifdef BAUHAUS
#undef DEFAULT_FT_FONT
#define DEFAULT_FT_FONT "URW Gothic L"
#endif
/* X bitmap fonts. */
#define DEFAULT_X_FONT "fixed"
/* These are mostly for testing. */
#define DEFAULT_X_SANS_FONT "-*-helvetica-bold-r-*-*-14-140-*-*-*-*-*-*"
#define DEFAULT_X_MONO_FONT "-*-courier-medium-r-*-*-*-120-*-*-*-*-*-*"
/* For use in full screen mode. */
#define BIG_X_SANS_FONT "-*-helvetica-medium-r-*-*-*-240-*-*-*-*-*-*"
/* The amount of space between the font baseline
and the vertical center of the window. */
#define VERT_AIR_PCT_XFONT 0.2
#define VERT_AIR_PCT_FTFONT 0.15
#define DEFAULT_TEXT_COLOR "tan"
/* The center hub stuff. */
#define HUB_DIAMETER_PCT 0.04
#define AXLE_DIAMETER_PCT 0.01
String new bgColor;
String new rimColor;
String new geomString;
Boolean new displaySeconds;
Boolean new displayRim;
Boolean new displayTickMarks;
String new secondHandColor;
String new minuteHandColor;
String new hourHandColor;
String new tickColor;
String new textColor;
void exit_help (char *);
#ifdef APPLE
/*
If Ctalk on your machine orders things differently than what the
MacOslinker prefers, then the program might need these in libctalk
(in xrender.c), *even if they're declared static*. (At least mine
does.) You might need to adjust this define - YMMV.
*/
double degree_to_radian (double);
double opp_side (double, double);
double adj_side (double, double);
#else
double degree_to_radian (double d) {
double radians;
radians = d * (3.1416 / 180);
return radians;
}
double opp_side (double deg, double hyp) {
return hyp * sin(degree_to_radian (deg));
}
double adj_side (double deg, double hyp) {
return hyp * cos(degree_to_radian (deg));
}
#endif /* #ifdef APPLE */
CTime instanceMethod getUTCTime (void) {
self utcTime;
return self;
}
AssociativeArray new weekDayNames;
Application instanceMethod initDateStuff (void) {
weekDayNames = 0, "Sunday", 1, "Monday", 2, "Tuesday", 3,
"Wednesday", 4, "Thursday", 5, "Friday", 6, "Saturday",
7, "Sunday";
if (displayText)
xFont getFontInfo xFontDescStr;
}
Application instanceMethod faceDimensions (void) {
centerX = self winWidth / 2;
centerY = self winHeight / 2;
if (self winWidth > self winHeight) {
radius = (self winHeight / 2);
if (displayText) {
if (useFTFonts) {
if (fullScreen) {
air = self winHeight * VERT_AIR_PCT_XFONT;
} else {
air = self winHeight * VERT_AIR_PCT_FTFONT;
}
} else {
air = self winHeight * VERT_AIR_PCT_XFONT;
}
}
} else {
radius = (self winWidth / 2);
if (displayText) {
if (useFTFonts) {
air = self winWidth * VERT_AIR_PCT_FTFONT;
} else {
air = self winWidth * VERT_AIR_PCT_XFONT;
}
}
}
radius -= WIN_MARGIN;
rim center x = centerX;
rim center y = centerY;
rim radius = radius;
rimPen colorName = rimColor;
if (fullScreen)
rimPen width = RIM_WIDTH_FULLSCREEN;
else
rimPen width = RIM_WIDTH;
rimPen alpha = displayAlpha;
tickMarks delete;
}
X11CanvasPane instanceMethod printDate (Integer dayOfMonth,
Integer dayOfWeek,
Integer winWidth,
Integer winHeight) {
String new buf;
Integer new dateY;
Integer new dateX;
Integer new textWidth;
dateY = winHeight / 2;
dateY -= air;
buf printOn "%s %d", (weekDayNames at dayOfWeek), dayOfMonth;
if (useFTFonts) {
textWidth = ftFont textWidth buf;
} else {
textWidth = xFont textWidth buf;
}
dateX = winWidth / 2;
dateX -= (textWidth / 2);
self putStrXY dateX, dateY, buf, (xFont fontDesc), textColor;
}
X11CanvasPane instanceMethod drawTime (Integer seconds, Integer minutes,
Integer hours) {
double theta;
Integer new endX;
Integer new endY;
Integer new hub_adj;
Integer new short_side;
if (hours >= 12) {
theta = ((hours - 12) * 30) - 90;
} else {
theta = (hours * 30) - 90;
}
theta += (minutes * 6) / 12;
endX = (int)adj_side (theta, hoursLength);
endX += centerX;
endY = (int) opp_side (theta, hoursLength);
endY += centerY;
self pen alpha = displayAlpha;
self drawLine centerX, centerY, endX, endY, hourPen;
theta = (minutes * 6) - 90;
endX = (int) adj_side (theta, minutesLength);
endX += centerX;
endY = (int) opp_side (theta, minutesLength);
endY += centerY;
self drawLine centerX, centerY, endX, endY, minutePen;
if (displaySeconds) {
theta = (seconds * 6) - 90;
endX = (int) adj_side (theta, secondsLength);
endX += centerX;
endY = (int) opp_side (theta, secondsLength);
endY += centerY;
self drawLine centerX, centerY, endX, endY, secondsPen;
}
if (winWidth < winHeight)
short_side = winWidth;
else
short_side = winHeight;
if (short_side > 125) {
self pen width = short_side * HUB_DIAMETER_PCT;
self pen colorName = minuteHandColor;
if (self usingXRender) {
self pen alpha = 0xffff;
self drawPoint rim center x, rim center y;
} else {
hub_adj = (self pen width) / 2;
self drawPoint (rim center x - hub_adj), (rim center y - hub_adj);
}
if (short_side > 150) {
self pen width = short_side * AXLE_DIAMETER_PCT;
self pen colorName = "black";
if (self usingXRender) {
self pen alpha = 0xffff;
self drawPoint rim center x, rim center y;
} else {
hub_adj = (self pen width) / 2;
self drawPoint (rim center x - hub_adj), (rim center y - hub_adj);
}
}
}
}
X11CanvasPane instanceMethod initFace (Integer centerX, Integer centerY,
Integer radius) {
double d, d_r, d_t;
int d_adj, d_opp, d_int;
Symbol new tickPtr;
/***/
/* rim center x = centerX;
rim center y = centerY;
rim radius = radius; */
/* rimPen colorName = rimColor;
if (fullScreen)
rimPen width = RIM_WIDTH_FULLSCREEN;
else
rimPen width = RIM_WIDTH; */
/* rimPen alpha = displayAlpha; */
if (displayTickMarks) {
d_r = radius;
for (d = 0; d < 360; d += 1.0) {
d_int = d;
if ((d_int % 6) == 0) {
if ((d_int % 30) == 0) {
d_t = d + 90.0;
*tickPtr = Line basicNew "tickMark";
d_adj = (int)adj_side (d_t, (BIG_TICK_START_PCT * d_r));
d_opp = (int) opp_side (d_t, (BIG_TICK_START_PCT * d_r));
(*tickPtr) start x = centerX + d_adj;
(*tickPtr) start y = centerY + d_opp;
if (fullScreen) {
d_adj = (int)adj_side (d_t, (d_r - TICK_MARGIN_FULLSCREEN));
d_opp = (int) opp_side (d_t, (d_r - TICK_MARGIN_FULLSCREEN));
} else {
d_adj = (int)adj_side (d_t, (d_r - TICK_MARGIN));
d_opp = (int) opp_side (d_t, (d_r - TICK_MARGIN));
}
(*tickPtr) end x = centerX + d_adj;
(*tickPtr) end y = centerY + d_opp;
tickMarks push *tickPtr;
} else {
d_t = d + 90.0;
*tickPtr = Line basicNew "tickMark";
d_adj = (int)adj_side (d_t, (SMALL_TICK_START_PCT * d_r));
d_opp = (int) opp_side (d_t, (SMALL_TICK_START_PCT * d_r));
(*tickPtr) start x = centerX + d_adj;
(*tickPtr) start y = centerY + d_opp;
if (fullScreen) {
d_adj = (int)adj_side (d_t, (d_r - TICK_MARGIN_FULLSCREEN));
d_opp = (int) opp_side (d_t, (d_r - TICK_MARGIN_FULLSCREEN));
} else {
d_adj = (int)adj_side (d_t, (d_r - TICK_MARGIN));
d_opp = (int) opp_side (d_t, (d_r - TICK_MARGIN));
}
(*tickPtr) end x = centerX + d_adj;
(*tickPtr) end y = centerY + d_opp;
tickMarks push *tickPtr;
}
}
}
}
tickPen width = 1;
tickPen colorName = tickColor;
tickPen alpha = displayAlpha;
secondsLength = SECONDS_LENGTH;
minutesLength = MINUTES_LENGTH;
hoursLength = HOURS_LENGTH;
secondsPen width = SECONDS_WIDTH;
secondsPen colorName = secondHandColor;
secondsPen alpha = displayAlpha;
minutePen width = MINUTES_WIDTH;
minutePen colorName = minuteHandColor;
minutePen alpha = displayAlpha;
hourPen width = HOURS_WIDTH;
hourPen colorName = hourHandColor;
hourPen alpha = displayAlpha;
}
X11CanvasPane instanceMethod drawFace (Integer centerX, Integer centerY,
Integer radius) {
self clear;
if (displayRim) {
rimPen alpha = 0xffff;
self drawCircle rim, rimPen, FALSE, bgColor;
}
if (displayTickMarks) {
tickMarks map {
tickPen alpha = displayAlpha;
super drawLine self, tickPen;
}
}
}
Application instanceMethod clockOptions (void) {
Integer new i;
Integer new nParams;
String new param;
nParams = self cmdLineArgs size;
for (i = 1; i < nParams; i++) {
param = self cmdLineArgs at i;
if (param == "-g") {
geomString = self cmdLineArgs at i + 1;
i = i + 1;
continue;
}
if (param == "-bg") {
bgColor = self cmdLineArgs at i + 1;
i = i + 1;
continue;
}
if (param == "-rc") {
rimColor = self cmdLineArgs at i + 1;
i += 1;
continue;
}
if (param == "-sh") {
secondHandColor = self cmdLineArgs at i + 1;
i += 1;
continue;
}
if (param == "-mh") {
minuteHandColor = self cmdLineArgs at i + 1;
i += 1;
continue;
}
if (param == "-hh") {
hourHandColor = self cmdLineArgs at i + 1;
i += 1;
continue;
}
if (param == "-st") {
tickColor = self cmdLineArgs at i + 1;
displayTickMarks = true;
i += 1;
continue;
}
if (param == "-fc") {
displayText = true;
textColor = self cmdLineArgs at i + 1;
i += 1;
continue;
}
if (param == "-fn") {
displayText = true;
if (useFTFonts) {
ftFontDescStr = self cmdLineArgs at i + 1;
} else {
xFontDescStr = self cmdLineArgs at i + 1;
}
i += 1;
continue;
}
if (param == "-pt") {
displayText = true;
if (useFTFonts)
ftFontPtSize = (self cmdLineArgs at i + 1) asFloat;
i += 1;
continue;
}
if (param == "-norender") {
self useXRender false;
continue;
}
if (param == "-xfonts") {
useFTFonts = False;
displayText = true;
continue;
}
if (param == "-ns") { displaySeconds = FALSE; continue; }
if (param == "-nr") { displayRim = FALSE; continue; }
if (param == "-t") { displayTickMarks = TRUE; continue; }
if (param == "-noalpha") { displayAlpha = OPAQUE; continue; }
if (param == "-nt") { displayText = False; continue; }
if (param == "-fullscreen") { fullScreen = true; continue; }
if ((param == "-h") || (param == "--help"))
exit_help ("analogclock");
printf ("analogclock: Unknown option: %s. Exiting.\n", param);
exit_help ("analogclock");
}
}
Application instanceMethod findFonts (void) {
Integer new result;
if (useFTFonts && displayText) {
ftFont initFontLib;
if (fullScreen == true) {
if (ftFontPtSize == 0.0) {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, 24.0;
} else {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, ftFontPtSize;
}
} else {
if (ftFontPtSize == 0.0) {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, 16.0;
} else {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, ftFontPtSize;
}
}
if (result == 0) {
ftFont alpha DEFAULT_ALPHA;
} else {
printf ("analogclock: Could not find the font, \"%s.\" "
"Trying the font, \"%s.\"\n", ftFontDescStr, FALLBACK_FT_FONT);
ftFontDescStr = FALLBACK_FT_FONT;
if (fullScreen == true) {
if (ftFontPtSize == 0.0) {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, 24.0;
} else {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, ftFontPtSize;
}
} else {
if (ftFontPtSize == 0.0) {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, 16.0;
} else {
result = ftFont selectFont ftFontDescStr, 0, 80, 72, ftFontPtSize;
}
}
if (result == 0) {
ftFont alpha DEFAULT_ALPHA;
} else {
printf ("analogclock: Could not find the font, \"%s.\" "
"Using a bitmapped font.\n", FALLBACK_FT_FONT);
useFTFonts = false;
if (fullScreen == true) {
xFontDescStr = BIG_X_SANS_FONT;
}
}
}
ftFont namedX11Color textColor;
} else if (displayText) {
if (fullScreen == true) {
xFontDescStr = BIG_X_SANS_FONT;
}
}
}
int main (int argc, char **argv) {
X11Pane new xPane;
InputEvent new e;
X11PaneDispatcher new xTopLevelPane;
X11CanvasPane new xCanvasPane;
Integer new prevSeconds;
CalendarTime new timeNow;
Boolean new control_keypress;
clockApp enableExceptionTrace;
/* Handles Control-C's in the xterm. */
clockApp installExitHandlerBasic;
displaySeconds = True;
displayRim = True;
displayTickMarks = False;
rimColor = DEFAULT_RIM_COLOR;
bgColor = "black";
secondHandColor = DEFAULT_HAND_COLOR;
minuteHandColor = DEFAULT_HAND_COLOR;
hourHandColor = DEFAULT_HAND_COLOR;
tickColor = DEFAULT_RIM_COLOR;
textColor = DEFAULT_TEXT_COLOR;
displayAlpha = DEFAULT_ALPHA;
ftFontDescStr = DEFAULT_FT_FONT;
xFontDescStr = DEFAULT_X_FONT;
useFTFonts = True;
displayText = True;
fullScreen = false;
ftFontPtSize = 0.0;
control_keypress = false;
clockApp parseArgs argc, argv;
clockApp clockOptions;
/* Make sure we have a reasonable Xft (and X Render) installation.*/
if (ftFont version < 10) {
useFTFonts = false;
}
if (useFTFonts) {
clockApp findFonts;
}
if (fullScreen == true) {
clockApp winWidth = xPane displayWidth;
clockApp winHeight = xPane displayHeight;
clockApp winXOrg = 0;
clockApp winYOrg = 0;
} else if (geomString length > 0) {
clockApp parseX11Geometry geomString;
if (clockApp winWidth == 0) {
clockApp winWidth = WIN_WIDTH;
}
if (clockApp winHeight == 0) {
clockApp winHeight = WIN_HEIGHT;
}
if (clockApp winXOrg == 0) {
clockApp winXOrg = WIN_X;
}
if (clockApp winYOrg == 0) {
clockApp winYOrg = WIN_Y;
}
} else {
clockApp winWidth = WIN_WIDTH;
clockApp winHeight = WIN_HEIGHT;
clockApp winXOrg = WIN_X;
clockApp winYOrg = WIN_Y;
}
xPane borderWidth = 0;
winWidth = clockApp winWidth;
winHeight = clockApp winHeight;
/* Some of the graphics do not (yet) allow for resizing
bufers. */
xPane inputStream eventMask = KEYPRESS|WINDELETE|RESIZENOTIFY;
xPane initialize clockApp winXOrg, clockApp winYOrg,
clockApp winWidth, clockApp winHeight, clockApp geomFlags;
xTopLevelPane attachTo xPane;
xCanvasPane attachTo xTopLevelPane;
xPane setResources "analogClock", "AnalogClock";
xPane map;
xPane raiseWindow;
xPane openEventStream;
xPane setWMTitle "Analog Clock";
clockApp initDateStuff;
xCanvasPane background bgColor;
clockApp faceDimensions;
xCanvasPane initFace centerX, centerY, radius;
while (TRUE) {
timeNow getUTCTime;
timeNow localTime;
if (timeNow seconds != prevSeconds) {
xCanvasPane drawFace centerX, centerY, radius;
if (displayText) {
xCanvasPane faceRegular;
xCanvasPane printDate timeNow dom, timeNow dow,
clockApp winWidth, clockApp winHeight;
}
xCanvasPane drawTime timeNow seconds, timeNow minutes,
timeNow hours;
xCanvasPane refresh;
prevSeconds = timeNow seconds;
xPane inputStream queueInput;
if (xPane inputStream eventPending) {
e become xPane inputStream inputQueue unshift;
xPane subPaneNotify e; /* Call the classes' built-in event handlers. */
switch (e eventClass value)
{
case WINDELETE:
xPane deleteAndClose;
exit (0);
break;
case RESIZENOTIFY:
clockApp winWidth = e xEventData3;
clockApp winHeight = e xEventData4;
clockApp faceDimensions;
xCanvasPane background bgColor;
xCanvasPane clear;
xCanvasPane initFace centerX, centerY, radius;
xCanvasPane refresh;
break;
case KEYPRESS:
/* Escape or Control-C key closes the window.
X11CanvasPane objects don't have a built-in
keyboard handler, so there's one here. */
if (e xEventData5 value == X_Escape_keycode) {
/* tickMarks delete; */
xPane deleteAndClose;
exit (0);
} else if (e xEventData5 == X_Control_L_keycode ||
e xEventData5 == X_Control_R_keycode) {
control_keypress = true;
continue;
} else if (control_keypress) {
if (e xEventData5 == CTRL_C) {
/* tickMarks delete; */
xPane deleteAndClose;
exit (0);
} else {
control_keypress = false;
}
} else {
control_keypress = false;
}
break;
default:
break;
}
}
}
clockApp uSleep 100000;
}
}
void exit_help (char *cmd) {
printf ("\nUsage: %s [-h] | [-noalpha] [-norender] [-nr] [-ns] [-nt] [-pt <ptsize>] [-st] [-t] [-xfonts] [-fullscreen] [-bg <color>] [-fc <color>] [-hh <color>] [-mh <color>] [-rc <color>] [-sh <color>] [-st <color>] [-fn <font>] [-g <geom>]\n", cmd);
printf ("-bg <color> Background color.\n");
printf ("-fc <color> Text foreground color.\n");
printf ("-g <geom> Initial window size and position\n");
printf ("-fn <font> Use <font> to display text - Refer to analogclock(1) for details.\n");
printf ("-fullscreen Tell the clock to occupy the entire screen. Pressing [Escape] or\n");
printf (" [Control-C] closes the window and exits the program.\n");
printf ("-h Print this message and exit.\n");
printf ("-hh <color> Hour hand color.\n");
printf ("-mh <color> Minute hand color.\n");
printf ("-noalpha Don't use alpha blending.\n");
printf ("-norender Don't use the X Render extension to draw graphics.\n");
printf ("-nr Don't display the rim.\n");
printf ("-ns Don't display the second hand.\n");
printf ("-nt Don't display the day and date.\n");
printf ("-pt <ptSize> The height of the text in points. This is only needed for\n");
printf (" outline fonts; X bitmap fonts have the font size included\n");
printf (" in the fontspec.\n");
printf ("-rc <color> Rim color.\n");
printf ("-sh <color> Second hand color.\n");
printf ("-st <color> Tick mark color. (Sets -t option.)\n");
printf ("-t Display tick marks.\n");
printf ("-xfonts Use bitmap fonts, even if outline fonts are available.\n");
printf ("Please report bugs to: rk3314042@gmail.com.\n");
exit (0);
Return to Home Page [Home].