1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* clock --- displays an analog clock */
3 
4 #if 0
5 static const char sccsid[] = "@(#)clock.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1995 by Jeremie PETIT <petit@aurora.unice.fr>
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * The design was highly inspirated from 'oclock' but not the code,
25  * which is all mine.
26  *
27  * Revision History:
28  * 01-Nov-2000: Allocation checks
29  * 10-May-1997: Compatible with xscreensaver
30  * 24-Dec-1995: Ron Hitchens <ron AT idiom.com> cycles range and defaults
31  *              changed.
32  * 01-Dec-1995: Clock size changes each time it is displayed.
33  * 01-Jun-1995: Written.
34  */
35 
36 #ifdef STANDALONE
37 #define MODE_clock
38 #define DEFAULTS "*delay: 100000 \n" \
39 	"*count: -16 \n" \
40 	"*cycles: 200 \n" \
41 	"*size: -200 \n" \
42 	"*ncolors: 200 \n" \
43 
44 # define free_clock 0
45 # define reshape_clock 0
46 # define clock_handle_event 0
47 #include "xlockmore.h"		/* in xscreensaver distribution */
48 #else /* STANDALONE */
49 #include "xlock.h"		/* in xlockmore distribution */
50 #endif /* STANDALONE */
51 
52 #ifdef MODE_clock
53 
54 ENTRYPOINT ModeSpecOpt clock_opts =
55 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
56 
57 #ifdef USE_MODULES
58 ModStruct   clock_description =
59 {"clock", "init_clock", "draw_clock", "release_clock",
60  "refresh_clock", "init_clock", (char *) NULL, &clock_opts,
61  100000, -16, 200, -200, 64, 1.0, "",
62  "Shows Packard's clock", 0, NULL};
63 
64 #endif
65 
66 #ifdef TM_IN_SYS_TIME
67 #include <sys/time.h>
68 #else
69 #include <time.h>
70 #endif
71 
72 #define MIN_CYCLES     50	/* shortest possible time before moving the clock */
73 #define MAX_CYCLES   1000	/* longest possible time before moving the clock */
74 #define CLOCK_BORDER    9	/* Percentage of the clock size for the border */
75 #define HOURS_SIZE     50	/* Percentage of the clock size for hours length */
76 #define MINUTES_SIZE   75	/* Idem for minutes */
77 #define SECONDS_SIZE   80	/* Idem for seconds */
78 #define HOURS_WIDTH    10	/* Percentage of the clock size for hours width */
79 #define MINUTES_WIDTH   5	/* Idem for minutes */
80 #define SECONDS_WIDTH   2	/* Idem for seconds */
81 #define JEWEL_POSITION 90	/* The relative position of the jewel */
82 #define JEWEL_SIZE      9	/* The relative size of the jewel */
83 #define MINCLOCKS       1
84 #define MINSIZE        16	/* size, minimum */
85 
86 typedef struct {
87 	int         size;	/* length of the hand */
88 	int         width;	/* width of the hand */
89 	unsigned long color;	/* color of the hand */
90 } hand;
91 
92 typedef struct {
93 	hand        hours;	/* the hours hand */
94 	hand        minutes;	/* the minutes hand */
95 	hand        seconds;	/* the seconds hand */
96 	XPoint      pos;
97 	int         size;
98 	int         radius;
99 	int         boxsize;
100 	int         jewel_size;
101 	int         jewel_position;
102 	int         border_width;
103 	unsigned long border_color;
104 	unsigned long jewel_color;
105 	struct tm   dd_time;
106 } oclock;
107 
108 typedef struct {
109 	oclock     *oclocks;
110 	time_t      d_time;
111 	int         clock_count;
112 	int         width, height;
113 	int         nclocks;
114 	int         currentclocks;
115 	int         redrawing;
116 } clockstruct;
117 
118 static clockstruct *clocks = (clockstruct *) NULL;
119 
120 static int
myrand(int minimum,int maximum)121 myrand(int minimum, int maximum)
122 {
123 	return ((int) (minimum + (LRAND() / MAXRAND) * (maximum + 1 - minimum)));
124 }
125 
126 static double
HourToAngle(oclock * oclockp)127 HourToAngle(oclock * oclockp)
128 {
129 	return ((M_PI * ((double) oclockp->dd_time.tm_hour +
130 			 ((double) oclockp->dd_time.tm_min / 60) +
131 		   ((double) oclockp->dd_time.tm_sec / 3600))) / 6 - M_PI_2);
132 }
133 
134 static double
MinutesToAngle(oclock * oclockp)135 MinutesToAngle(oclock * oclockp)
136 {
137 	return ((M_PI * ((double) oclockp->dd_time.tm_min +
138 		    ((double) oclockp->dd_time.tm_sec / 60))) / 30 - M_PI_2);
139 }
140 
141 static double
SecondsToAngle(oclock * oclockp)142 SecondsToAngle(oclock * oclockp)
143 {
144 	return ((M_PI * ((double) oclockp->dd_time.tm_sec) / 30) - M_PI_2);
145 }
146 
147 static void
DrawDisk(Display * display,Window window,GC gc,int center_x,int center_y,int diameter)148 DrawDisk(Display * display, Window window, GC gc,
149 	 int center_x, int center_y, int diameter)
150 {
151 	int         radius = diameter / 2;
152 
153 	if (diameter > 1)
154 		XFillArc(display, window, gc,
155 			 center_x - radius, center_y - radius,
156 			 diameter, diameter,
157 			 0, 23040);
158 	else
159 		XDrawPoint(display, window, gc, center_x, center_y);
160 }
161 
162 static void
DrawBorder(ModeInfo * mi,int clck,short int mode)163 DrawBorder(ModeInfo * mi, int clck, short int mode)
164 {
165 	Display    *display = MI_DISPLAY(mi);
166 	GC          gc = MI_GC(mi);
167 	clockstruct *cp = &clocks[MI_SCREEN(mi)];
168 
169 
170 	if (mode == 0)
171 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
172 	else
173 		XSetForeground(display, gc, cp->oclocks[clck].border_color);
174 
175 	XSetLineAttributes(display, gc, cp->oclocks[clck].border_width,
176 			   LineSolid, CapNotLast, JoinMiter);
177 	XDrawArc(display, MI_WINDOW(mi), gc,
178 		 cp->oclocks[clck].pos.x - cp->oclocks[clck].size / 2,
179 		 cp->oclocks[clck].pos.y - cp->oclocks[clck].size / 2,
180 		 cp->oclocks[clck].size, cp->oclocks[clck].size,
181 		 0, 23040);
182 }
183 
184 static void
DrawJewel(ModeInfo * mi,int clck,short int mode)185 DrawJewel(ModeInfo * mi, int clck, short int mode)
186 {
187 	Display    *display = MI_DISPLAY(mi);
188 	GC          gc = MI_GC(mi);
189 	clockstruct *cp = &clocks[MI_SCREEN(mi)];
190 
191 	XSetLineAttributes(display, gc, 1, LineSolid, CapNotLast, JoinMiter);
192 	if (mode == 0)
193 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
194 	else
195 		XSetForeground(display, gc, cp->oclocks[clck].jewel_color);
196 	DrawDisk(display, MI_WINDOW(mi), gc,
197 		 cp->oclocks[clck].pos.x,
198 		 cp->oclocks[clck].pos.y - cp->oclocks[clck].jewel_position,
199 		 cp->oclocks[clck].jewel_size);
200 }
201 
202 static void
DrawHand(ModeInfo * mi,int clck,hand * h,double angle,short int mode)203 DrawHand(ModeInfo * mi, int clck, hand * h, double angle, short int mode)
204 {
205 	Display    *display = MI_DISPLAY(mi);
206 	Window      window = MI_WINDOW(mi);
207 	GC          gc = MI_GC(mi);
208 	clockstruct *cp = &clocks[MI_SCREEN(mi)];
209 	int         cosi, sinj;
210 
211 	/* mode: 0 for erasing, anything else for drawing */
212 	if (mode == 0)
213 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
214 	else
215 		XSetForeground(display, gc, h->color);
216 	XSetLineAttributes(display, gc, h->width, LineSolid, CapNotLast, JoinMiter);
217 	DrawDisk(display, window, gc,
218 		 cp->oclocks[clck].pos.x, cp->oclocks[clck].pos.y, h->width);
219 	cosi = (int) (cos(angle) * (double) (h->size));
220 	sinj = (int) (sin(angle) * (double) (h->size));
221 	XDrawLine(display, window, gc,
222 		  cp->oclocks[clck].pos.x, cp->oclocks[clck].pos.y,
223 	     cp->oclocks[clck].pos.x + cosi, cp->oclocks[clck].pos.y + sinj);
224 	DrawDisk(display, window, gc,
225 	      cp->oclocks[clck].pos.x + cosi, cp->oclocks[clck].pos.y + sinj,
226 		 h->width);
227 }
228 
229 static void
real_draw_clock(ModeInfo * mi,int clck,short int mode)230 real_draw_clock(ModeInfo * mi, int clck, short int mode)
231 {
232 	clockstruct *cp = &clocks[MI_SCREEN(mi)];
233 
234 	DrawBorder(mi, clck, mode);
235 	DrawJewel(mi, clck, mode);
236 	DrawHand(mi, clck, &cp->oclocks[clck].hours,
237 		 HourToAngle(&cp->oclocks[clck]), mode);
238 	DrawHand(mi, clck, &cp->oclocks[clck].minutes,
239 		 MinutesToAngle(&cp->oclocks[clck]), mode);
240 	DrawHand(mi, clck, &cp->oclocks[clck].seconds,
241 		 SecondsToAngle(&cp->oclocks[clck]), mode);
242 }
243 
244 static int
collide(clockstruct * cp,int clck)245 collide(clockstruct * cp, int clck)
246 {
247 	int         i, d, x, y;
248 
249 	for (i = 0; i < clck; i++) {
250 		x = (cp->oclocks[i].pos.x - cp->oclocks[clck].pos.x);
251 		y = (cp->oclocks[i].pos.y - cp->oclocks[clck].pos.y);
252 		d = (int) sqrt((double) (x * x + y * y));
253 		if (d < (cp->oclocks[i].size + cp->oclocks[i].border_width +
254 		cp->oclocks[clck].size + cp->oclocks[clck].border_width) / 2)
255 			return i;
256 	}
257 	return clck;
258 }
259 
260 static int
new_clock_state(ModeInfo * mi,int clck)261 new_clock_state(ModeInfo * mi, int clck)
262 {
263 	clockstruct *cp = &clocks[MI_SCREEN(mi)];
264 	int         size = MI_SIZE(mi);
265 	int         tryit;
266 
267 	for (tryit = 0; tryit < 8; tryit++) {
268 		/* We change the clock size */
269 		if (size < -MINSIZE)
270 			cp->oclocks[clck].size = NRAND(MIN(-size, MAX(MINSIZE,
271 				MIN(cp->width, cp->height))) - MINSIZE + 1) + MINSIZE;
272 		else if (size < MINSIZE) {
273 			if (!size)
274 				cp->oclocks[clck].size = MAX(MINSIZE, MIN(cp->width, cp->height));
275 			else
276 				cp->oclocks[clck].size = MINSIZE;
277 		} else
278 			cp->oclocks[clck].size = MIN(size, MAX(MINSIZE,
279 						MIN(cp->width, cp->height)));
280 		/* We must compute new attributes for the clock because its size changes */
281 		cp->oclocks[clck].hours.size = (cp->oclocks[clck].size * HOURS_SIZE) / 200;
282 		cp->oclocks[clck].minutes.size =
283 			(cp->oclocks[clck].size * MINUTES_SIZE) / 200;
284 		cp->oclocks[clck].seconds.size =
285 			(cp->oclocks[clck].size * SECONDS_SIZE) / 200;
286 		cp->oclocks[clck].jewel_size = (cp->oclocks[clck].size * JEWEL_SIZE) / 200;
287 
288 		cp->oclocks[clck].border_width =
289 			(cp->oclocks[clck].size * CLOCK_BORDER) / 200;
290 		cp->oclocks[clck].hours.width = (cp->oclocks[clck].size * HOURS_WIDTH) / 200;
291 		cp->oclocks[clck].minutes.width =
292 			(cp->oclocks[clck].size * MINUTES_WIDTH) / 200;
293 		cp->oclocks[clck].seconds.width =
294 			(cp->oclocks[clck].size * SECONDS_WIDTH) / 200;
295 
296 		cp->oclocks[clck].jewel_position =
297 			(cp->oclocks[clck].size * JEWEL_POSITION) / 200;
298 
299 		cp->oclocks[clck].radius =
300 			(cp->oclocks[clck].size / 2) + cp->oclocks[clck].border_width + 1;
301 
302 		/* Here we set new values for the next clock to be displayed */
303 		if (MI_NPIXELS(mi) > 2) {
304 			/* New colors */
305 			cp->oclocks[clck].border_color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
306 			cp->oclocks[clck].jewel_color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
307 			cp->oclocks[clck].hours.color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
308 			cp->oclocks[clck].minutes.color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
309 			cp->oclocks[clck].seconds.color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
310 		}
311 		/* A new position for the clock */
312 		cp->oclocks[clck].pos.x = myrand(cp->oclocks[clck].radius,
313 				       cp->width - cp->oclocks[clck].radius);
314 		cp->oclocks[clck].pos.y = myrand(cp->oclocks[clck].radius,
315 				      cp->height - cp->oclocks[clck].radius);
316 		if (clck == collide(cp, clck))
317 			return True;
318 	}
319 	cp->currentclocks = clck;
320 	return False;
321 }
322 
323 static void
update_clock(ModeInfo * mi,int clck)324 update_clock(ModeInfo * mi, int clck)
325 {
326 	clockstruct *cp = &clocks[MI_SCREEN(mi)];
327 
328 	DrawHand(mi, clck, &cp->oclocks[clck].hours,
329 		 HourToAngle(&cp->oclocks[clck]), 0);
330 	DrawHand(mi, clck, &cp->oclocks[clck].minutes,
331 		 MinutesToAngle(&cp->oclocks[clck]), 0);
332 	DrawHand(mi, clck, &cp->oclocks[clck].seconds,
333 		 SecondsToAngle(&cp->oclocks[clck]), 0);
334 	cp->d_time = time((time_t *) 0);
335 	(void) memcpy((char *) &cp->oclocks[clck].dd_time,
336 		      (char *) localtime(&cp->d_time),
337 		      sizeof (cp->oclocks[clck].dd_time));
338 	DrawHand(mi, clck, &cp->oclocks[clck].hours,
339 		 HourToAngle(&cp->oclocks[clck]), 1);
340 	DrawHand(mi, clck, &cp->oclocks[clck].minutes,
341 		 MinutesToAngle(&cp->oclocks[clck]), 1);
342 	DrawHand(mi, clck, &cp->oclocks[clck].seconds,
343 		 SecondsToAngle(&cp->oclocks[clck]), 1);
344 }
345 
346 static void
free_clock_screen(clockstruct * cp)347 free_clock_screen(clockstruct *cp) {
348 	if (cp == NULL) {
349 		return;
350 	}
351 	if (cp->oclocks != NULL) {
352 		free(cp->oclocks);
353 		/* cp->oclocks = (oclock *) NULL; */
354 	}
355 	cp = NULL;
356 }
357 
358 ENTRYPOINT void
init_clock(ModeInfo * mi)359 init_clock(ModeInfo * mi)
360 {
361 	clockstruct *cp;
362 	int         clck;
363 
364 	MI_INIT(mi, clocks);
365 	cp = &clocks[MI_SCREEN(mi)];
366 
367 	cp->redrawing = 0;
368 	cp->width = MI_WIDTH(mi);
369 	cp->height = MI_HEIGHT(mi);
370 
371 	MI_CLEARWINDOW(mi);
372 
373 	cp->nclocks = MI_COUNT(mi);
374 	if (cp->nclocks < -MINCLOCKS) {
375 		/* if cp->nclocks is random ... the size can change */
376 		if (cp->oclocks != NULL) {
377 			free(cp->oclocks);
378 			cp->oclocks = (oclock *) NULL;
379 		}
380 		cp->nclocks = NRAND(-cp->nclocks - MINCLOCKS + 1) + MINCLOCKS;
381 	} else if (cp->nclocks < MINCLOCKS)
382 		cp->nclocks = MINCLOCKS;
383 	if (cp->oclocks == NULL) {
384 		if ((cp->oclocks = (oclock *) malloc(cp->nclocks *
385 				sizeof (oclock))) == NULL) {
386 			return;
387 		}
388 	}
389 
390 	cp->clock_count = MI_CYCLES(mi);
391 	if (cp->clock_count < MIN_CYCLES)
392 		cp->clock_count = MIN_CYCLES;
393 	if (cp->clock_count > MAX_CYCLES)
394 		cp->clock_count = MAX_CYCLES;
395 
396 	for (clck = 0; clck < cp->nclocks; clck++) {
397 		/* By default, we set the clock's colors to white */
398 		cp->oclocks[clck].border_color = MI_WHITE_PIXEL(mi);
399 		cp->oclocks[clck].jewel_color = MI_WHITE_PIXEL(mi);
400 		cp->oclocks[clck].hours.color = MI_WHITE_PIXEL(mi);
401 		cp->oclocks[clck].minutes.color = MI_WHITE_PIXEL(mi);
402 		cp->oclocks[clck].seconds.color = MI_WHITE_PIXEL(mi);
403 		cp->d_time = time((time_t *) 0);
404 	}
405 	for (clck = 0; clck < cp->nclocks; clck++) {
406 		(void) memcpy((char *) &cp->oclocks[clck].dd_time,
407 			      (char *) localtime(&cp->d_time),
408 			      sizeof (cp->oclocks[clck].dd_time));
409 	}
410 	cp->currentclocks = cp->nclocks;
411 	for (clck = 0; clck < cp->currentclocks; clck++)
412 		if (!new_clock_state(mi, clck))
413 			break;
414 }
415 
416 ENTRYPOINT void
draw_clock(ModeInfo * mi)417 draw_clock(ModeInfo * mi)
418 {
419 	int         clck;
420 	clockstruct *cp;
421 
422 	if (clocks == NULL)
423 		return;
424 	cp = &clocks[MI_SCREEN(mi)];
425 	if (cp->oclocks == NULL)
426 		return;
427 
428 	MI_IS_DRAWN(mi) = True;
429 	if (++cp->clock_count >= MI_CYCLES(mi)) {
430 		cp->clock_count = 0;
431 		for (clck = 0; clck < cp->currentclocks; clck++)
432 			real_draw_clock(mi, clck, 0);
433 		cp->currentclocks = cp->nclocks;
434 		for (clck = 0; clck < cp->currentclocks; clck++)
435 			if (!new_clock_state(mi, clck))
436 				break;
437 		for (clck = 0; clck < cp->currentclocks; clck++)
438 			real_draw_clock(mi, clck, 1);
439 		cp->redrawing = 0;
440 	} else if (cp->d_time != time((time_t *) 0))
441 		for (clck = 0; clck < cp->currentclocks; clck++)
442 			update_clock(mi, clck);
443 	if (cp->redrawing) {
444 		for (clck = 0; clck < cp->currentclocks; clck++)
445 			real_draw_clock(mi, clck, 1);
446 		cp->redrawing = 0;
447 	}
448 }
449 
450 ENTRYPOINT void
release_clock(ModeInfo * mi)451 release_clock(ModeInfo * mi)
452 {
453 	if (clocks != NULL) {
454 		int         screen;
455 
456 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
457 			free_clock_screen(&clocks[screen]);
458 		}
459 		free(clocks);
460 		clocks = (clockstruct *) NULL;
461 	}
462 }
463 
464 #ifndef STANDALONE
465 ENTRYPOINT void
refresh_clock(ModeInfo * mi)466 refresh_clock(ModeInfo * mi)
467 {
468 	clockstruct *cp;
469 
470 	if (clocks == NULL)
471 		return;
472 	cp = &clocks[MI_SCREEN(mi)];
473 
474 	MI_CLEARWINDOW(mi);
475 	cp->redrawing = 1;
476 }
477 #endif
478 
479 XSCREENSAVER_MODULE ("CLOCK", clock)
480 
481 #endif /* MODE_clock */
482