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