478 lines
14 KiB
C
478 lines
14 KiB
C
|
/* -*- Mode: C; tab-width: 4 -*- */
|
||
|
/* clock --- displays an analog clock */
|
||
|
|
||
|
#if !defined( lint ) && !defined( SABER )
|
||
|
static const char sccsid[] = "@(#)clock.c 5.00 2000/11/01 xlockmore";
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*-
|
||
|
* Copyright (c) 1995 by Jeremie PETIT <petit@aurora.unice.fr>
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software and its
|
||
|
* documentation for any purpose and without fee is hereby granted,
|
||
|
* provided that the above copyright notice appear in all copies and that
|
||
|
* both that copyright notice and this permission notice appear in
|
||
|
* supporting documentation.
|
||
|
*
|
||
|
* This file is provided AS IS with no warranties of any kind. The author
|
||
|
* shall have no liability with respect to the infringement of copyrights,
|
||
|
* trade secrets or any patents by this file or any part thereof. In no
|
||
|
* event will the author be liable for any lost revenue or profits or
|
||
|
* other special, indirect and consequential damages.
|
||
|
*
|
||
|
* The design was highly inspirated from 'oclock' but not the code,
|
||
|
* which is all mine.
|
||
|
*
|
||
|
* Revision History:
|
||
|
* 01-Nov-2000: Allocation checks
|
||
|
* 10-May-1997: Compatible with xscreensaver
|
||
|
* 24-Dec-1995: Ron Hitchens <ron@idiom.com> cycles range and defaults
|
||
|
* changed.
|
||
|
* 01-Dec-1995: Clock size changes each time it is displayed.
|
||
|
* 01-Jun-1995: Written.
|
||
|
*/
|
||
|
|
||
|
#ifdef STANDALONE
|
||
|
#define MODE_clock
|
||
|
#define PROGCLASS "Clock"
|
||
|
#define HACK_INIT init_clock
|
||
|
#define HACK_DRAW draw_clock
|
||
|
#define clock_opts xlockmore_opts
|
||
|
#define DEFAULTS "*delay: 100000 \n" \
|
||
|
"*count: -16 \n" \
|
||
|
"*cycles: 200 \n" \
|
||
|
"*size: -200 \n" \
|
||
|
"*ncolors: 200 \n"
|
||
|
#include "xlockmore.h" /* in xscreensaver distribution */
|
||
|
#else /* STANDALONE */
|
||
|
#include "xlock.h" /* in xlockmore distribution */
|
||
|
|
||
|
#endif /* STANDALONE */
|
||
|
|
||
|
#ifdef MODE_clock
|
||
|
|
||
|
ModeSpecOpt clock_opts =
|
||
|
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
|
||
|
|
||
|
#ifdef USE_MODULES
|
||
|
ModStruct clock_description =
|
||
|
{"clock", "init_clock", "draw_clock", "release_clock",
|
||
|
"refresh_clock", "init_clock", (char *) NULL, &clock_opts,
|
||
|
100000, -16, 200, -200, 64, 1.0, "",
|
||
|
"Shows Packard's clock", 0, NULL};
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#ifdef TM_IN_SYS_TIME
|
||
|
#include <sys/time.h>
|
||
|
#else
|
||
|
#include <time.h>
|
||
|
#endif
|
||
|
|
||
|
#define MIN_CYCLES 50 /* shortest possible time before moving the clock */
|
||
|
#define MAX_CYCLES 1000 /* longest possible time before moving the clock */
|
||
|
#define CLOCK_BORDER 9 /* Percentage of the clock size for the border */
|
||
|
#define HOURS_SIZE 50 /* Percentage of the clock size for hours length */
|
||
|
#define MINUTES_SIZE 75 /* Idem for minutes */
|
||
|
#define SECONDS_SIZE 80 /* Idem for seconds */
|
||
|
#define HOURS_WIDTH 10 /* Percentage of the clock size for hours width */
|
||
|
#define MINUTES_WIDTH 5 /* Idem for minutes */
|
||
|
#define SECONDS_WIDTH 2 /* Idem for seconds */
|
||
|
#define JEWEL_POSITION 90 /* The relative position of the jewel */
|
||
|
#define JEWEL_SIZE 9 /* The relative size of the jewel */
|
||
|
#define MINCLOCKS 1
|
||
|
#define MINSIZE 16 /* size, minimum */
|
||
|
|
||
|
typedef struct {
|
||
|
int size; /* length of the hand */
|
||
|
int width; /* width of the hand */
|
||
|
unsigned long color; /* color of the hand */
|
||
|
} hand;
|
||
|
|
||
|
typedef struct {
|
||
|
hand hours; /* the hours hand */
|
||
|
hand minutes; /* the minutes hand */
|
||
|
hand seconds; /* the seconds hand */
|
||
|
XPoint pos;
|
||
|
int size;
|
||
|
int radius;
|
||
|
int boxsize;
|
||
|
int jewel_size;
|
||
|
int jewel_position;
|
||
|
int border_width;
|
||
|
unsigned long border_color;
|
||
|
unsigned long jewel_color;
|
||
|
struct tm dd_time;
|
||
|
} oclock;
|
||
|
|
||
|
typedef struct {
|
||
|
oclock *oclocks;
|
||
|
time_t d_time;
|
||
|
int clock_count;
|
||
|
int width, height;
|
||
|
int nclocks;
|
||
|
int currentclocks;
|
||
|
int redrawing;
|
||
|
} clockstruct;
|
||
|
|
||
|
static clockstruct *clocks = (clockstruct *) NULL;
|
||
|
|
||
|
static int
|
||
|
myrand(int minimum, int maximum)
|
||
|
{
|
||
|
return ((int) (minimum + (LRAND() / MAXRAND) * (maximum + 1 - minimum)));
|
||
|
}
|
||
|
|
||
|
static double
|
||
|
HourToAngle(oclock * oclockp)
|
||
|
{
|
||
|
return ((M_PI * ((double) oclockp->dd_time.tm_hour +
|
||
|
((double) oclockp->dd_time.tm_min / 60) +
|
||
|
((double) oclockp->dd_time.tm_sec / 3600))) / 6 - M_PI_2);
|
||
|
}
|
||
|
|
||
|
static double
|
||
|
MinutesToAngle(oclock * oclockp)
|
||
|
{
|
||
|
return ((M_PI * ((double) oclockp->dd_time.tm_min +
|
||
|
((double) oclockp->dd_time.tm_sec / 60))) / 30 - M_PI_2);
|
||
|
}
|
||
|
|
||
|
static double
|
||
|
SecondsToAngle(oclock * oclockp)
|
||
|
{
|
||
|
return ((M_PI * ((double) oclockp->dd_time.tm_sec) / 30) - M_PI_2);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
DrawDisk(Display * display, Window window, GC gc,
|
||
|
int center_x, int center_y, int diameter)
|
||
|
{
|
||
|
int radius = diameter / 2;
|
||
|
|
||
|
if (diameter > 1)
|
||
|
XFillArc(display, window, gc,
|
||
|
center_x - radius, center_y - radius,
|
||
|
diameter, diameter,
|
||
|
0, 23040);
|
||
|
else
|
||
|
XDrawPoint(display, window, gc, center_x, center_y);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
DrawBorder(ModeInfo * mi, int clck, short int mode)
|
||
|
{
|
||
|
Display *display = MI_DISPLAY(mi);
|
||
|
GC gc = MI_GC(mi);
|
||
|
clockstruct *cp = &clocks[MI_SCREEN(mi)];
|
||
|
|
||
|
|
||
|
if (mode == 0)
|
||
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
||
|
else
|
||
|
XSetForeground(display, gc, cp->oclocks[clck].border_color);
|
||
|
|
||
|
XSetLineAttributes(display, gc, cp->oclocks[clck].border_width,
|
||
|
LineSolid, CapNotLast, JoinMiter);
|
||
|
XDrawArc(display, MI_WINDOW(mi), gc,
|
||
|
cp->oclocks[clck].pos.x - cp->oclocks[clck].size / 2,
|
||
|
cp->oclocks[clck].pos.y - cp->oclocks[clck].size / 2,
|
||
|
cp->oclocks[clck].size, cp->oclocks[clck].size,
|
||
|
0, 23040);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
DrawJewel(ModeInfo * mi, int clck, short int mode)
|
||
|
{
|
||
|
Display *display = MI_DISPLAY(mi);
|
||
|
GC gc = MI_GC(mi);
|
||
|
clockstruct *cp = &clocks[MI_SCREEN(mi)];
|
||
|
|
||
|
XSetLineAttributes(display, gc, 1, LineSolid, CapNotLast, JoinMiter);
|
||
|
if (mode == 0)
|
||
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
||
|
else
|
||
|
XSetForeground(display, gc, cp->oclocks[clck].jewel_color);
|
||
|
DrawDisk(display, MI_WINDOW(mi), gc,
|
||
|
cp->oclocks[clck].pos.x,
|
||
|
cp->oclocks[clck].pos.y - cp->oclocks[clck].jewel_position,
|
||
|
cp->oclocks[clck].jewel_size);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
DrawHand(ModeInfo * mi, int clck, hand * h, double angle, short int mode)
|
||
|
{
|
||
|
Display *display = MI_DISPLAY(mi);
|
||
|
Window window = MI_WINDOW(mi);
|
||
|
GC gc = MI_GC(mi);
|
||
|
clockstruct *cp = &clocks[MI_SCREEN(mi)];
|
||
|
int cosi, sinj;
|
||
|
|
||
|
/* mode: 0 for erasing, anything else for drawing */
|
||
|
if (mode == 0)
|
||
|
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
|
||
|
else
|
||
|
XSetForeground(display, gc, h->color);
|
||
|
XSetLineAttributes(display, gc, h->width, LineSolid, CapNotLast, JoinMiter);
|
||
|
DrawDisk(display, window, gc,
|
||
|
cp->oclocks[clck].pos.x, cp->oclocks[clck].pos.y, h->width);
|
||
|
cosi = (int) (cos(angle) * (double) (h->size));
|
||
|
sinj = (int) (sin(angle) * (double) (h->size));
|
||
|
XDrawLine(display, window, gc,
|
||
|
cp->oclocks[clck].pos.x, cp->oclocks[clck].pos.y,
|
||
|
cp->oclocks[clck].pos.x + cosi, cp->oclocks[clck].pos.y + sinj);
|
||
|
DrawDisk(display, window, gc,
|
||
|
cp->oclocks[clck].pos.x + cosi, cp->oclocks[clck].pos.y + sinj,
|
||
|
h->width);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
real_draw_clock(ModeInfo * mi, int clck, short int mode)
|
||
|
{
|
||
|
clockstruct *cp = &clocks[MI_SCREEN(mi)];
|
||
|
|
||
|
DrawBorder(mi, clck, mode);
|
||
|
DrawJewel(mi, clck, mode);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].hours,
|
||
|
HourToAngle(&cp->oclocks[clck]), mode);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].minutes,
|
||
|
MinutesToAngle(&cp->oclocks[clck]), mode);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].seconds,
|
||
|
SecondsToAngle(&cp->oclocks[clck]), mode);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
collide(clockstruct * cp, int clck)
|
||
|
{
|
||
|
int i, d, x, y;
|
||
|
|
||
|
for (i = 0; i < clck; i++) {
|
||
|
x = (cp->oclocks[i].pos.x - cp->oclocks[clck].pos.x);
|
||
|
y = (cp->oclocks[i].pos.y - cp->oclocks[clck].pos.y);
|
||
|
d = (int) sqrt((double) (x * x + y * y));
|
||
|
if (d < (cp->oclocks[i].size + cp->oclocks[i].border_width +
|
||
|
cp->oclocks[clck].size + cp->oclocks[clck].border_width) / 2)
|
||
|
return i;
|
||
|
}
|
||
|
return clck;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
new_clock_state(ModeInfo * mi, int clck)
|
||
|
{
|
||
|
clockstruct *cp = &clocks[MI_SCREEN(mi)];
|
||
|
int size = MI_SIZE(mi);
|
||
|
int tryit;
|
||
|
|
||
|
for (tryit = 0; tryit < 8; tryit++) {
|
||
|
/* We change the clock size */
|
||
|
if (size < -MINSIZE)
|
||
|
cp->oclocks[clck].size = NRAND(MIN(-size, MAX(MINSIZE,
|
||
|
MIN(cp->width, cp->height))) - MINSIZE + 1) + MINSIZE;
|
||
|
else if (size < MINSIZE) {
|
||
|
if (!size)
|
||
|
cp->oclocks[clck].size = MAX(MINSIZE, MIN(cp->width, cp->height));
|
||
|
else
|
||
|
cp->oclocks[clck].size = MINSIZE;
|
||
|
} else
|
||
|
cp->oclocks[clck].size = MIN(size, MAX(MINSIZE,
|
||
|
MIN(cp->width, cp->height)));
|
||
|
|
||
|
/* We must compute new attributes for the clock because its size changes */
|
||
|
cp->oclocks[clck].hours.size = (cp->oclocks[clck].size * HOURS_SIZE) / 200;
|
||
|
cp->oclocks[clck].minutes.size =
|
||
|
(cp->oclocks[clck].size * MINUTES_SIZE) / 200;
|
||
|
cp->oclocks[clck].seconds.size =
|
||
|
(cp->oclocks[clck].size * SECONDS_SIZE) / 200;
|
||
|
cp->oclocks[clck].jewel_size = (cp->oclocks[clck].size * JEWEL_SIZE) / 200;
|
||
|
|
||
|
cp->oclocks[clck].border_width =
|
||
|
(cp->oclocks[clck].size * CLOCK_BORDER) / 200;
|
||
|
cp->oclocks[clck].hours.width = (cp->oclocks[clck].size * HOURS_WIDTH) / 200;
|
||
|
cp->oclocks[clck].minutes.width =
|
||
|
(cp->oclocks[clck].size * MINUTES_WIDTH) / 200;
|
||
|
cp->oclocks[clck].seconds.width =
|
||
|
(cp->oclocks[clck].size * SECONDS_WIDTH) / 200;
|
||
|
|
||
|
cp->oclocks[clck].jewel_position =
|
||
|
(cp->oclocks[clck].size * JEWEL_POSITION) / 200;
|
||
|
|
||
|
cp->oclocks[clck].radius =
|
||
|
(cp->oclocks[clck].size / 2) + cp->oclocks[clck].border_width + 1;
|
||
|
|
||
|
/* Here we set new values for the next clock to be displayed */
|
||
|
if (MI_NPIXELS(mi) > 2) {
|
||
|
/* New colors */
|
||
|
cp->oclocks[clck].border_color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
||
|
cp->oclocks[clck].jewel_color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
||
|
cp->oclocks[clck].hours.color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
||
|
cp->oclocks[clck].minutes.color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
||
|
cp->oclocks[clck].seconds.color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
|
||
|
}
|
||
|
/* A new position for the clock */
|
||
|
cp->oclocks[clck].pos.x = myrand(cp->oclocks[clck].radius,
|
||
|
cp->width - cp->oclocks[clck].radius);
|
||
|
cp->oclocks[clck].pos.y = myrand(cp->oclocks[clck].radius,
|
||
|
cp->height - cp->oclocks[clck].radius);
|
||
|
if (clck == collide(cp, clck))
|
||
|
return True;
|
||
|
}
|
||
|
cp->currentclocks = clck;
|
||
|
return False;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
update_clock(ModeInfo * mi, int clck)
|
||
|
{
|
||
|
clockstruct *cp = &clocks[MI_SCREEN(mi)];
|
||
|
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].hours,
|
||
|
HourToAngle(&cp->oclocks[clck]), 0);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].minutes,
|
||
|
MinutesToAngle(&cp->oclocks[clck]), 0);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].seconds,
|
||
|
SecondsToAngle(&cp->oclocks[clck]), 0);
|
||
|
cp->d_time = time((time_t *) 0);
|
||
|
(void) memcpy((char *) &cp->oclocks[clck].dd_time,
|
||
|
(char *) localtime(&cp->d_time),
|
||
|
sizeof (cp->oclocks[clck].dd_time));
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].hours,
|
||
|
HourToAngle(&cp->oclocks[clck]), 1);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].minutes,
|
||
|
MinutesToAngle(&cp->oclocks[clck]), 1);
|
||
|
DrawHand(mi, clck, &cp->oclocks[clck].seconds,
|
||
|
SecondsToAngle(&cp->oclocks[clck]), 1);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
init_clock(ModeInfo * mi)
|
||
|
{
|
||
|
clockstruct *cp;
|
||
|
int clck;
|
||
|
|
||
|
if (clocks == NULL) {
|
||
|
if ((clocks = (clockstruct *) calloc(MI_NUM_SCREENS(mi),
|
||
|
sizeof (clockstruct))) == NULL)
|
||
|
return;
|
||
|
}
|
||
|
cp = &clocks[MI_SCREEN(mi)];
|
||
|
|
||
|
|
||
|
cp->redrawing = 0;
|
||
|
cp->width = MI_WIDTH(mi);
|
||
|
cp->height = MI_HEIGHT(mi);
|
||
|
|
||
|
MI_CLEARWINDOW(mi);
|
||
|
|
||
|
cp->nclocks = MI_COUNT(mi);
|
||
|
if (cp->nclocks < -MINCLOCKS) {
|
||
|
/* if cp->nclocks is random ... the size can change */
|
||
|
if (cp->oclocks != NULL) {
|
||
|
free(cp->oclocks);
|
||
|
cp->oclocks = (oclock *) NULL;
|
||
|
}
|
||
|
cp->nclocks = NRAND(-cp->nclocks - MINCLOCKS + 1) + MINCLOCKS;
|
||
|
} else if (cp->nclocks < MINCLOCKS)
|
||
|
cp->nclocks = MINCLOCKS;
|
||
|
if (cp->oclocks == NULL) {
|
||
|
if ((cp->oclocks = (oclock *) malloc(cp->nclocks *
|
||
|
sizeof (oclock))) == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cp->clock_count = MI_CYCLES(mi);
|
||
|
if (cp->clock_count < MIN_CYCLES)
|
||
|
cp->clock_count = MIN_CYCLES;
|
||
|
if (cp->clock_count > MAX_CYCLES)
|
||
|
cp->clock_count = MAX_CYCLES;
|
||
|
|
||
|
for (clck = 0; clck < cp->nclocks; clck++) {
|
||
|
/* By default, we set the clock's colors to white */
|
||
|
cp->oclocks[clck].border_color = MI_WHITE_PIXEL(mi);
|
||
|
cp->oclocks[clck].jewel_color = MI_WHITE_PIXEL(mi);
|
||
|
cp->oclocks[clck].hours.color = MI_WHITE_PIXEL(mi);
|
||
|
cp->oclocks[clck].minutes.color = MI_WHITE_PIXEL(mi);
|
||
|
cp->oclocks[clck].seconds.color = MI_WHITE_PIXEL(mi);
|
||
|
cp->d_time = time((time_t *) 0);
|
||
|
}
|
||
|
for (clck = 0; clck < cp->nclocks; clck++) {
|
||
|
(void) memcpy((char *) &cp->oclocks[clck].dd_time,
|
||
|
(char *) localtime(&cp->d_time),
|
||
|
sizeof (cp->oclocks[clck].dd_time));
|
||
|
}
|
||
|
cp->currentclocks = cp->nclocks;
|
||
|
for (clck = 0; clck < cp->currentclocks; clck++)
|
||
|
if (!new_clock_state(mi, clck))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
draw_clock(ModeInfo * mi)
|
||
|
{
|
||
|
int clck;
|
||
|
clockstruct *cp;
|
||
|
|
||
|
if (clocks == NULL)
|
||
|
return;
|
||
|
cp = &clocks[MI_SCREEN(mi)];
|
||
|
if (cp->oclocks == NULL)
|
||
|
return;
|
||
|
|
||
|
MI_IS_DRAWN(mi) = True;
|
||
|
if (++cp->clock_count >= MI_CYCLES(mi)) {
|
||
|
cp->clock_count = 0;
|
||
|
for (clck = 0; clck < cp->currentclocks; clck++)
|
||
|
real_draw_clock(mi, clck, 0);
|
||
|
cp->currentclocks = cp->nclocks;
|
||
|
for (clck = 0; clck < cp->currentclocks; clck++)
|
||
|
if (!new_clock_state(mi, clck))
|
||
|
break;
|
||
|
for (clck = 0; clck < cp->currentclocks; clck++)
|
||
|
real_draw_clock(mi, clck, 1);
|
||
|
cp->redrawing = 0;
|
||
|
} else if (cp->d_time != time((time_t *) 0))
|
||
|
for (clck = 0; clck < cp->currentclocks; clck++)
|
||
|
update_clock(mi, clck);
|
||
|
if (cp->redrawing) {
|
||
|
for (clck = 0; clck < cp->currentclocks; clck++)
|
||
|
real_draw_clock(mi, clck, 1);
|
||
|
cp->redrawing = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
release_clock(ModeInfo * mi)
|
||
|
{
|
||
|
if (clocks != NULL) {
|
||
|
int screen;
|
||
|
|
||
|
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
|
||
|
clockstruct *cp = &clocks[screen];
|
||
|
|
||
|
if (cp->oclocks != NULL) {
|
||
|
free(cp->oclocks);
|
||
|
/* cp->oclocks = (oclock *) NULL; */
|
||
|
}
|
||
|
}
|
||
|
free(clocks);
|
||
|
clocks = (clockstruct *) NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
refresh_clock(ModeInfo * mi)
|
||
|
{
|
||
|
clockstruct *cp;
|
||
|
|
||
|
if (clocks == NULL)
|
||
|
return;
|
||
|
cp = &clocks[MI_SCREEN(mi)];
|
||
|
|
||
|
MI_CLEARWINDOW(mi);
|
||
|
cp->redrawing = 1;
|
||
|
}
|
||
|
|
||
|
#endif /* MODE_clock */
|