xenocara/app/xlockmore/modes/clock.c
2006-11-26 11:07:42 +00:00

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 */