Clock.c revision 309c398a
1/* $Xorg: Clock.c,v 1.4 2001/02/09 02:05:39 xorgcvs Exp $ */
2/* $XdotOrg: xc/programs/xclock/Clock.c,v 1.3 2004/10/30 20:33:44 alanc Exp $ */
3
4/***********************************************************
5
6Copyright 1987, 1988, 1998  The Open Group
7
8Permission to use, copy, modify, distribute, and sell this software and its
9documentation for any purpose is hereby granted without fee, provided that
10the above copyright notice appear in all copies and that both that
11copyright notice and this permission notice appear in supporting
12documentation.
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of The Open Group shall not be
25used in advertising or otherwise to promote the sale, use or other dealings
26in this Software without prior written authorization from The Open Group.
27
28
29Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
30
31                        All Rights Reserved
32
33Permission to use, copy, modify, and distribute this software and its
34documentation for any purpose and without fee is hereby granted,
35provided that the above copyright notice appear in all copies and that
36both that copyright notice and this permission notice appear in
37supporting documentation, and that the name of Digital not be
38used in advertising or publicity pertaining to distribution of the
39software without specific, written prior permission.
40
41DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
42ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
43DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
44ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
45WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
46ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
47SOFTWARE.
48
49******************************************************************/
50/*
51 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
52 *
53 * Permission is hereby granted, free of charge, to any person obtaining a
54 * copy of this software and associated documentation files (the "Software"),
55 * to deal in the Software without restriction, including without limitation
56 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
57 * and/or sell copies of the Software, and to permit persons to whom the
58 * Software is furnished to do so, subject to the following conditions:
59 *
60 * The above copyright notice and this permission notice (including the next
61 * paragraph) shall be included in all copies or substantial portions of the
62 * Software.
63 *
64 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
67 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
69 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
70 * DEALINGS IN THE SOFTWARE.
71 */
72/*
73 * Authors:  I18N - Steve Swales - March 2000
74 *	     bgpixmap - Alan Coopersmith (as part of STSF project) - Sept. 2001
75 */
76/* $XFree86: xc/programs/xclock/Clock.c,v 3.25 2003/07/04 16:24:30 eich Exp $ */
77
78#ifdef HAVE_CONFIG_H
79# include "config.h"
80#endif
81
82#include <X11/Xlib.h>
83#include <X11/StringDefs.h>
84#include <X11/IntrinsicP.h>
85#include "ClockP.h"
86#include <X11/Xosdefs.h>
87#include <stdio.h>
88#include <X11/Xos.h>
89#include <X11/Xaw/XawInit.h>
90#if !defined(NO_I18N) && defined(HAVE_ICONV)
91#include <iconv.h>
92#include <langinfo.h>
93#include <errno.h>
94#include <limits.h>
95#endif
96
97#if defined(XawVersion) && (XawVersion >= 7000002L)
98#define USE_XAW_PIXMAP_CVT
99#else
100#include <X11/xpm.h>
101#endif
102
103#include <time.h>
104#define Time_t time_t
105
106#ifdef XKB
107#include <X11/extensions/XKBbells.h>
108#endif
109
110#ifndef NO_I18N
111#include <stdlib.h> /* for getenv() */
112#include <locale.h>
113extern Boolean no_locale; /* if True, use old (unlocalized) behaviour */
114#endif
115
116
117/* Private Definitions */
118
119#define VERTICES_IN_HANDS	6	/* to draw triangle */
120#define PI			3.14159265358979
121#define TWOPI			(2. * PI)
122
123#define MINOR_TICK_FRACT	95
124#define SECOND_HAND_FRACT	90
125#define MINUTE_HAND_FRACT	70
126#define HOUR_HAND_FRACT		40
127#define HAND_WIDTH_FRACT	7
128#define SECOND_WIDTH_FRACT	5
129#define SECOND_HAND_TIME	30
130
131#define ANALOG_SIZE_DEFAULT	164
132
133#define max(a, b) ((a) > (b) ? (a) : (b))
134#define min(a, b) ((a) < (b) ? (a) : (b))
135/* #define abs(a) ((a) < 0 ? -(a) : (a)) */
136
137
138/* Initialization of defaults */
139
140#define offset(field) XtOffsetOf(ClockRec, clock.field)
141#define goffset(field) XtOffsetOf(WidgetRec, core.field)
142
143static XtResource resources[] = {
144    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
145	goffset(width), XtRImmediate, (XtPointer) 0},
146    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
147	goffset(height), XtRImmediate, (XtPointer) 0},
148    {XtNupdate, XtCInterval, XtRInt, sizeof(int),
149        offset(update), XtRImmediate, (XtPointer) 60 },
150#ifndef XRENDER
151    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
152        offset(fgpixel), XtRString, XtDefaultForeground},
153#endif
154    {XtNhand, XtCForeground, XtRPixel, sizeof(Pixel),
155        offset(Hdpixel), XtRString, XtDefaultForeground},
156    {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
157        offset(Hipixel), XtRString, XtDefaultForeground},
158    {XtNutime, XtCBoolean, XtRBoolean, sizeof(Boolean),
159	offset(utime), XtRImmediate, (XtPointer) FALSE},
160    {XtNanalog, XtCBoolean, XtRBoolean, sizeof(Boolean),
161        offset(analog), XtRImmediate, (XtPointer) TRUE},
162    {XtNtwentyfour, XtCBoolean, XtRBoolean, sizeof(Boolean),
163        offset(twentyfour), XtRImmediate, (XtPointer) TRUE},
164    {XtNbrief, XtCBoolean, XtRBoolean, sizeof(Boolean),
165        offset(brief), XtRImmediate, (XtPointer) FALSE},
166    {XtNstrftime, XtCString, XtRString, sizeof(String),
167        offset(strftime), XtRString, ""},
168    {XtNchime, XtCBoolean, XtRBoolean, sizeof(Boolean),
169	offset(chime), XtRImmediate, (XtPointer) FALSE },
170    {XtNpadding, XtCMargin, XtRInt, sizeof(int),
171        offset(padding), XtRImmediate, (XtPointer) 8},
172    {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
173        offset(font), XtRString, XtDefaultFont},
174#ifndef NO_I18N
175     {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
176        offset(fontSet), XtRString, XtDefaultFontSet},
177#endif
178    {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof (int),
179    	offset (backing_store), XtRString, "default"},
180#ifdef XRENDER
181    {XtNrender, XtCBoolean, XtRBoolean, sizeof(Boolean),
182	offset(render), XtRImmediate, (XtPointer) TRUE },
183    {XtNbuffer, XtCBoolean, XtRBoolean, sizeof(Boolean),
184	offset(buffer), XtRImmediate, (XtPointer) TRUE },
185    {XtNsharp, XtCBoolean, XtRBoolean, sizeof(Boolean),
186	offset(sharp), XtRImmediate, (XtPointer) FALSE },
187    {XtNforeground, XtCForeground, XtRXftColor, sizeof(XftColor),
188        offset(fg_color), XtRString, XtDefaultForeground},
189    {XtNhourColor, XtCForeground, XtRXftColor, sizeof(XftColor),
190	offset(hour_color), XtRString, XtDefaultForeground},
191    {XtNminuteColor, XtCForeground, XtRXftColor, sizeof(XftColor),
192	offset(min_color), XtRString, XtDefaultForeground},
193    {XtNsecondColor, XtCForeground, XtRXftColor, sizeof(XftColor),
194	offset(sec_color), XtRString, XtDefaultForeground},
195    {XtNmajorColor, XtCForeground, XtRXftColor, sizeof(XftColor),
196	offset(major_color), XtRString, XtDefaultForeground},
197    {XtNminorColor, XtCForeground, XtRXftColor, sizeof(XftColor),
198	offset(minor_color), XtRString, XtDefaultForeground},
199    {XtNface, XtCFace, XtRXftFont, sizeof (XftFont *),
200	offset (face), XtRString, ""},
201#endif
202};
203
204#undef offset
205#undef goffset
206
207static void ClassInitialize ( void );
208static void Initialize ( Widget request, Widget new, ArgList args,
209			 Cardinal *num_args );
210static void Realize ( Widget gw, XtValueMask *valueMask,
211		      XSetWindowAttributes *attrs );
212static void Destroy ( Widget gw );
213static void Resize ( Widget gw );
214static void Redisplay ( Widget gw, XEvent *event, Region region );
215static void clock_tic ( XtPointer client_data, XtIntervalId *id );
216static void erase_hands ( ClockWidget w, struct tm *tm );
217static void ClockAngle ( int tick_units, double *sinp, double *cosp );
218static void DrawLine ( ClockWidget w, Dimension blank_length,
219		       Dimension length, int tick_units );
220static void DrawHand ( ClockWidget w, Dimension length, Dimension width,
221		       int tick_units );
222static void DrawSecond ( ClockWidget w, Dimension length, Dimension width,
223			 Dimension offset, int tick_units );
224static void SetSeg ( ClockWidget w, int x1, int y1, int x2, int y2 );
225static void DrawClockFace ( ClockWidget w );
226static int clock_round ( double x );
227static Boolean SetValues ( Widget gcurrent, Widget grequest, Widget gnew,
228			   ArgList args, Cardinal *num_args );
229#if !defined(NO_I18N) && defined(HAVE_ICONV)
230static char *clock_to_utf8(const char *str, int in_len);
231#endif
232
233ClockClassRec clockClassRec = {
234    { /* core fields */
235    /* superclass		*/	(WidgetClass) &simpleClassRec,
236    /* class_name		*/	"Clock",
237    /* widget_size		*/	sizeof(ClockRec),
238    /* class_initialize		*/	ClassInitialize,
239    /* class_part_initialize	*/	NULL,
240    /* class_inited		*/	FALSE,
241    /* initialize		*/	Initialize,
242    /* initialize_hook		*/	NULL,
243    /* realize			*/	Realize,
244    /* actions			*/	NULL,
245    /* num_actions		*/	0,
246    /* resources		*/	resources,
247    /* resource_count		*/	XtNumber(resources),
248    /* xrm_class		*/	NULLQUARK,
249    /* compress_motion		*/	TRUE,
250    /* compress_exposure	*/	XtExposeCompressMaximal,
251    /* compress_enterleave	*/	TRUE,
252    /* visible_interest		*/	FALSE,
253    /* destroy			*/	Destroy,
254    /* resize			*/	Resize,
255    /* expose			*/	Redisplay,
256    /* set_values		*/	SetValues,
257    /* set_values_hook		*/	NULL,
258    /* set_values_almost	*/	XtInheritSetValuesAlmost,
259    /* get_values_hook		*/	NULL,
260    /* accept_focus		*/	NULL,
261    /* version			*/	XtVersion,
262    /* callback_private		*/	NULL,
263    /* tm_table			*/	NULL,
264    /* query_geometry           */	XtInheritQueryGeometry,
265    /* display_accelerator      */	XtInheritDisplayAccelerator,
266    /* extension                */	NULL
267    },
268    { /* simple fields */
269    /* change_sensitive         */      XtInheritChangeSensitive
270    },
271    { /* clock fields */
272    /* ignore                   */      0
273    }
274};
275
276WidgetClass clockWidgetClass = (WidgetClass) &clockClassRec;
277
278/****************************************************************
279 *
280 * Private Procedures
281 *
282 ****************************************************************/
283#ifndef USE_XAW_PIXMAP_CVT
284static void CvtStringToPixmap(
285    XrmValue*           args,
286    Cardinal*           num_args,
287    XrmValuePtr         fromVal,
288    XrmValuePtr         toVal
289    )
290{
291    static Pixmap	pmap;
292    Pixmap shapemask;
293    char *name = (char *)fromVal->addr;
294    Screen *screen;
295    Display *dpy;
296
297    if (*num_args != 1)
298     XtErrorMsg("wrongParameters","cvtStringToPixmap","XtToolkitError",
299             "String to pixmap conversion needs screen argument",
300              (String *)NULL, (Cardinal *)NULL);
301
302    if (strcmp(name, "None") == 0) {
303        pmap = None;
304    } else {
305	screen = *((Screen **) args[0].addr);
306	dpy = DisplayOfScreen(screen);
307
308	XpmReadFileToPixmap(dpy, RootWindowOfScreen(screen), name, &pmap,
309	  &shapemask, NULL);
310    }
311
312    (*toVal).size = sizeof(Pixmap);
313    (*toVal).addr = (XPointer) &pmap ;
314}
315#endif
316
317#ifdef XRENDER
318static XtConvertArgRec xftColorConvertArgs[] = {
319    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
320     sizeof(Screen *)},
321    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.colormap),
322     sizeof(Colormap)}
323};
324
325#define	donestr(type, value, tstr) \
326	{							\
327	    if (toVal->addr != NULL) {				\
328		if (toVal->size < sizeof(type)) {		\
329		    toVal->size = sizeof(type);			\
330		    XtDisplayStringConversionWarning(dpy, 	\
331			(char*) fromVal->addr, tstr);		\
332		    return False;				\
333		}						\
334		*(type*)(toVal->addr) = (value);		\
335	    }							\
336	    else {						\
337		static type static_val;				\
338		static_val = (value);				\
339		toVal->addr = (XPointer)&static_val;		\
340	    }							\
341	    toVal->size = sizeof(type);				\
342	    return True;					\
343	}
344
345static void
346XmuFreeXftColor (XtAppContext app, XrmValuePtr toVal, XtPointer closure,
347		 XrmValuePtr args, Cardinal *num_args)
348{
349    Screen	*screen;
350    Colormap	colormap;
351    XftColor	*color;
352
353    if (*num_args != 2)
354    {
355	XtAppErrorMsg (app,
356		       "freeXftColor", "wrongParameters",
357		       "XtToolkitError",
358		       "Freeing an XftColor requires screen and colormap arguments",
359		       (String *) NULL, (Cardinal *)NULL);
360	return;
361    }
362
363    screen = *((Screen **) args[0].addr);
364    colormap = *((Colormap *) args[1].addr);
365    color = (XftColor *) toVal->addr;
366    XftColorFree (DisplayOfScreen (screen),
367		  DefaultVisual (DisplayOfScreen (screen),
368				 XScreenNumberOfScreen (screen)),
369		  colormap, color);
370}
371
372static Boolean
373XmuCvtStringToXftColor(Display *dpy,
374		       XrmValue *args, Cardinal *num_args,
375		       XrmValue *fromVal, XrmValue *toVal,
376		       XtPointer *converter_data)
377{
378    char	    *spec;
379    XRenderColor    renderColor;
380    XftColor	    xftColor;
381    Screen	    *screen;
382    Colormap	    colormap;
383
384    if (*num_args != 2)
385    {
386	XtAppErrorMsg (XtDisplayToApplicationContext (dpy),
387		       "cvtStringToXftColor", "wrongParameters",
388		       "XtToolkitError",
389		       "String to render color conversion needs screen and colormap arguments",
390		       (String *) NULL, (Cardinal *)NULL);
391	return False;
392    }
393
394    screen = *((Screen **) args[0].addr);
395    colormap = *((Colormap *) args[1].addr);
396
397    spec = (char *) fromVal->addr;
398    if (strcasecmp (spec, XtDefaultForeground) == 0)
399    {
400	renderColor.red = 0;
401	renderColor.green = 0;
402	renderColor.blue = 0;
403	renderColor.alpha = 0xffff;
404    }
405    else if (strcasecmp (spec, XtDefaultBackground) == 0)
406    {
407	renderColor.red = 0xffff;
408	renderColor.green = 0xffff;
409	renderColor.blue = 0xffff;
410	renderColor.alpha = 0xffff;
411    }
412    else if (!XRenderParseColor (dpy, spec, &renderColor))
413	return False;
414    if (!XftColorAllocValue (dpy,
415			     DefaultVisual (dpy,
416					    XScreenNumberOfScreen (screen)),
417			     colormap,
418			     &renderColor,
419			     &xftColor))
420	return False;
421
422    donestr (XftColor, xftColor, XtRXftColor);
423}
424
425static void
426XmuFreeXftFont (XtAppContext app, XrmValuePtr toVal, XtPointer closure,
427		XrmValuePtr args, Cardinal *num_args)
428{
429    Screen  *screen;
430    XftFont *font;
431
432    if (*num_args != 1)
433    {
434	XtAppErrorMsg (app,
435		       "freeXftFont", "wrongParameters",
436		       "XtToolkitError",
437		       "Freeing an XftFont requires screen argument",
438		       (String *) NULL, (Cardinal *)NULL);
439	return;
440    }
441
442    screen = *((Screen **) args[0].addr);
443    font = *((XftFont **) toVal->addr);
444    if (font)
445	XftFontClose (DisplayOfScreen (screen), font);
446}
447
448static Boolean
449XmuCvtStringToXftFont(Display *dpy,
450		      XrmValue *args, Cardinal *num_args,
451		      XrmValue *fromVal, XrmValue *toVal,
452		      XtPointer *converter_data)
453{
454    char    *name;
455    XftFont *font;
456    Screen  *screen;
457
458    if (*num_args != 1)
459    {
460	XtAppErrorMsg (XtDisplayToApplicationContext (dpy),
461		       "cvtStringToXftFont", "wrongParameters",
462		       "XtToolkitError",
463		       "String to XftFont conversion needs screen argument",
464		       (String *) NULL, (Cardinal *)NULL);
465	return False;
466    }
467
468    screen = *((Screen **) args[0].addr);
469    name = (char *) fromVal->addr;
470
471    font = XftFontOpenName (dpy,
472			    XScreenNumberOfScreen (screen),
473			    name);
474    if (font)
475    {
476	donestr (XftFont *, font, XtRXftFont);
477    }
478    XtDisplayStringConversionWarning(dpy, (char *) fromVal->addr, XtRXftFont);
479    return False;
480}
481
482static XtConvertArgRec xftFontConvertArgs[] = {
483    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
484     sizeof(Screen *)},
485};
486
487#endif
488
489static void
490ClassInitialize(void)
491{
492#ifdef USE_XAW_PIXMAP_CVT
493    XawInitializeWidgetSet();
494#else
495    static XtConvertArgRec scrnConvertArg[] = {
496	{XtBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
497	 sizeof(Screen *)}
498    };
499    XtAddConverter( XtRString, XtRPixmap, CvtStringToPixmap,
500	      	    scrnConvertArg, XtNumber(scrnConvertArg));
501#endif
502    XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
503		    NULL, 0 );
504#ifdef XRENDER
505    XtSetTypeConverter (XtRString, XtRXftColor,
506			XmuCvtStringToXftColor,
507			xftColorConvertArgs, XtNumber(xftColorConvertArgs),
508			XtCacheByDisplay, XmuFreeXftColor);
509    XtSetTypeConverter (XtRString, XtRXftFont,
510			XmuCvtStringToXftFont,
511			xftFontConvertArgs, XtNumber(xftFontConvertArgs),
512			XtCacheByDisplay, XmuFreeXftFont);
513#endif
514}
515
516static char *
517TimeString (ClockWidget w, struct tm *tm)
518{
519   if (w->clock.brief)
520   {
521      if (w->clock.twentyfour)
522      {
523	  static char brief[6];
524	  sprintf (brief, "%02d:%02d", tm->tm_hour, tm->tm_min);
525	  return brief;
526      }
527      else
528      {
529	 static char brief[9];
530	 int hour = tm->tm_hour % 12;
531	 if (!hour) hour = 12;
532	 sprintf (brief, "%02d:%02d %cM", hour, tm->tm_min,
533	    tm->tm_hour >= 12 ? 'P' : 'A');
534	 return brief;
535      }
536   }
537   else if (w->clock.utime)
538   {
539      static char utime[35];
540      Time_t tsec;
541      tsec = time(NULL);
542      sprintf (utime, "%10lu seconds since Epoch", (unsigned long)tsec);
543      return utime;
544   } else if (*w->clock.strftime) {
545     /*Note: this code is probably excessively paranoid
546       about buffer overflow.  The extra size 10 padding
547       is also meant as a further guard against programmer
548       error, although it is a little controversial*/
549     static char ctime[STRFTIME_BUFF_SIZE+10];
550     ctime[0] = ctime[STRFTIME_BUFF_SIZE] = '\0';
551     if (0 < strftime (ctime, STRFTIME_BUFF_SIZE-1,w->clock.strftime, tm)) {
552       ctime[STRFTIME_BUFF_SIZE-1] = '\0';
553       return ctime;
554     } else {
555       return asctime (tm);
556     }
557   }
558   else if (w->clock.twentyfour)
559      return asctime (tm);
560   else
561   {
562      static char long12[28];
563      strftime(long12, sizeof long12, "%a %b %d %I:%M:%S %p %Y", tm);
564      return long12;
565   }
566}
567
568/* ARGSUSED */
569static void
570Initialize (Widget request, Widget new, ArgList args, Cardinal *num_args)
571{
572    ClockWidget w = (ClockWidget)new;
573    XtGCMask		valuemask;
574    XGCValues	myXGCV;
575    int min_height, min_width;
576
577    valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
578    if (w->clock.font != NULL)
579        myXGCV.font = w->clock.font->fid;
580    else
581        valuemask &= ~GCFont;	/* use server default font */
582
583    min_width = min_height = ANALOG_SIZE_DEFAULT;
584    if(!w->clock.analog) {
585       char *str;
586       struct tm tm;
587       Time_t time_value;
588       int len;
589
590#ifndef NO_I18N
591       w->clock.utf8 = False;
592
593       if (!no_locale) {
594	   char *time_locale = setlocale(LC_CTYPE, NULL);
595
596	   if (strstr(time_locale, "UTF-8") || strstr(time_locale, "utf8")) {
597	       w->clock.utf8 = True;
598	   }
599
600	   /*
601	    * initialize time format from CFTIME if set, otherwise
602	    * default to "%c".  This emulates ascftime, but we use
603	    * strftime so we can limit the string buffer size to
604	    * avoid possible buffer overflow.
605	    */
606	   if ((w->clock.strftime == NULL) || (w->clock.strftime[0] == 0)) {
607	       w->clock.strftime = getenv("CFTIME");
608	       if (w->clock.strftime == NULL) {
609		   w->clock.strftime = "%c";
610	       }
611	   }
612       }
613#endif /* NO_I18N */
614
615       (void) time(&time_value);
616       tm = *localtime(&time_value);
617       str = TimeString (w, &tm);
618       len = strlen(str);
619       if (len && str[len - 1] == '\n') str[--len] = '\0';
620
621#ifdef XRENDER
622       if (w->clock.render)
623       {
624	XGlyphInfo  extents;
625#ifndef NO_I18N
626# ifdef HAVE_ICONV
627	char *utf8_str;
628# endif
629	if (w->clock.utf8)
630	    XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
631				(FcChar8 *) str, len, &extents);
632# ifdef HAVE_ICONV
633	else if ((utf8_str = clock_to_utf8(str, len)) != NULL) {
634	        XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
635			(FcChar8 *)utf8_str, strlen(utf8_str), &extents);
636		free(utf8_str);
637	}
638# endif
639	else
640#endif
641	    XftTextExtents8 (XtDisplay (w), w->clock.face,
642			     (FcChar8 *) str, len, &extents);
643	min_width = extents.xOff + 2 * w->clock.padding;
644	min_height = w->clock.face->ascent + w->clock.face->descent +
645		     2 * w->clock.padding;
646        /*fprintf(stderr, "render min_width %i\n", min_width);*/
647       }
648       else
649#endif
650       { /* not XRENDER block */
651#ifndef NO_I18N
652	 if (!no_locale) {
653	   XFontSetExtents *fse;
654
655	   if(w->clock.fontSet == NULL) {
656	       char **missing, *default_str;
657	       int n_missing;
658	       w->clock.fontSet = XCreateFontSet( XtDisplay(w),
659		 XtDefaultFontSet,
660		 &missing,
661		 &n_missing,
662		 &default_str);
663	   }
664	   if (w->clock.fontSet != NULL) {
665	       /* don't free this... it's freed with the XFontSet. */
666	       fse = XExtentsOfFontSet(w->clock.fontSet);
667
668	       min_width = XmbTextEscapement(w->clock.fontSet,str,len) +
669		 2 * w->clock.padding;
670	       min_height = fse->max_logical_extent.height +
671		 3 * w->clock.padding;
672	       /*fprintf(stderr, "fontset min_width %i\n", min_width);*/
673	   } else {
674	       no_locale = True;
675	   }
676	 }
677
678	 if (no_locale)
679#endif /* NO_I18N */
680	 {
681	   if (w->clock.font == NULL)
682	       w->clock.font = XQueryFont( XtDisplay(w),
683					   XGContextFromGC(
684					    DefaultGCOfScreen(XtScreen(w))) );
685	   min_width = XTextWidth(w->clock.font, str, len) +
686	       2 * w->clock.padding;
687	   min_height = w->clock.font->ascent +
688	       w->clock.font->descent + 2 * w->clock.padding;
689	   /*fprintf(stderr, "font min_width %i\n", min_width);*/
690	 }
691       } /* not XRENDER block */
692    }
693    if (w->core.width == 0)
694	w->core.width = min_width;
695    if (w->core.height == 0)
696	w->core.height = min_height;
697
698    myXGCV.foreground = ClockFgPixel (w);
699    myXGCV.background = w->core.background_pixel;
700    if (w->clock.font != NULL)
701	myXGCV.font = w->clock.font->fid;
702    else
703	valuemask &= ~GCFont;	/* use server default font */
704    myXGCV.line_width = 0;
705    w->clock.myGC = XtGetGC((Widget)w, valuemask, &myXGCV);
706
707    valuemask = GCForeground | GCLineWidth | GCGraphicsExposures;
708    myXGCV.foreground = w->core.background_pixel;
709    if (w->core.background_pixmap != XtUnspecifiedPixmap) {
710	myXGCV.tile = w->core.background_pixmap;
711	myXGCV.fill_style = FillTiled;
712	valuemask |= (GCTile | GCFillStyle);
713    }
714    myXGCV.graphics_exposures = False;
715    w->clock.EraseGC = XtGetGC((Widget)w, valuemask, &myXGCV);
716    valuemask &= ~(GCTile | GCFillStyle);
717
718    myXGCV.foreground = w->clock.Hipixel;
719    w->clock.HighGC = XtGetGC((Widget)w, valuemask, &myXGCV);
720
721    valuemask = GCForeground;
722    myXGCV.foreground = w->clock.Hdpixel;
723    w->clock.HandGC = XtGetGC((Widget)w, valuemask, &myXGCV);
724
725    /* make invalid update's use a default */
726    /*if (w->clock.update <= 0) w->clock.update = 60;*/
727    w->clock.show_second_hand = (abs(w->clock.update) <= SECOND_HAND_TIME);
728    w->clock.numseg = 0;
729    w->clock.interval_id = 0;
730    memset (&w->clock.otm, '\0', sizeof (w->clock.otm));
731#ifdef XRENDER
732    {
733	int major, minor;
734
735	if (XRenderQueryVersion (XtDisplay (w), &major, &minor) &&
736	    (major > 0 ||
737	     (major == 0 && minor >= 4)))
738	{
739	    w->clock.can_polygon = True;
740	}
741	else
742	    w->clock.can_polygon = False;
743    }
744    w->clock.pixmap = 0;
745    w->clock.draw = NULL;
746    w->clock.damage.x = 0;
747    w->clock.damage.y = 0;
748    w->clock.damage.height = 0;
749    w->clock.damage.width = 0;
750#endif
751}
752
753#ifdef XRENDER
754static void
755RenderPrepare (ClockWidget  w, XftColor *color)
756{
757    if (!w->clock.draw)
758    {
759	Drawable    d = XtWindow (w);
760	if (w->clock.buffer)
761	{
762	    if (!w->clock.pixmap)
763	    {
764		Arg arg[1];
765		w->clock.pixmap = XCreatePixmap (XtDisplay (w), d,
766						 w->core.width,
767						 w->core.height,
768						 w->core.depth);
769		arg[0].name = XtNbackgroundPixmap;
770		arg[0].value = 0;
771		XtSetValues ((Widget) w, arg, 1);
772	    }
773	    d = w->clock.pixmap;
774	}
775
776	w->clock.draw = XftDrawCreate (XtDisplay (w), d,
777				       DefaultVisual (XtDisplay (w),
778						      DefaultScreen(XtDisplay (w))),
779				       w->core.colormap);
780	w->clock.picture = XftDrawPicture (w->clock.draw);
781    }
782    if (color)
783	w->clock.fill_picture = XftDrawSrcPicture (w->clock.draw, color);
784}
785
786static void
787RenderClip (ClockWidget w)
788{
789    Region	r;
790    Drawable	d;
791
792    RenderPrepare (w, NULL);
793    if (w->clock.buffer)
794	d = w->clock.pixmap;
795    else
796	d = XtWindow (w);
797    XFillRectangle (XtDisplay (w), d, w->clock.EraseGC,
798		    w->clock.damage.x,
799		    w->clock.damage.y,
800		    w->clock.damage.width,
801		    w->clock.damage.height);
802    r = XCreateRegion ();
803    XUnionRectWithRegion (&w->clock.damage,
804			  r, r);
805    XftDrawSetClip (w->clock.draw, r);
806    XDestroyRegion (r);
807}
808
809static void
810RenderTextBounds (ClockWidget w, char *str, int off, int len,
811		  XRectangle *bounds, int *xp, int *yp)
812{
813    XGlyphInfo  head, tail;
814    int	    x, y;
815
816#ifndef NO_I18N
817# ifdef HAVE_ICONV
818    char *utf8_str;
819# endif
820    if (w->clock.utf8)
821    {
822	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
823			    (FcChar8 *) str, off, &head);
824	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
825			    (FcChar8 *) str + off, len - off, &tail);
826    }
827# ifdef HAVE_ICONV
828    else if ((utf8_str = clock_to_utf8(str, off)) != NULL)
829    {
830	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
831			    (FcChar8 *)utf8_str, strlen(utf8_str), &head);
832	free(utf8_str);
833	if ((utf8_str = clock_to_utf8(str+off, len-off)) != NULL) {
834	  XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
835			      (FcChar8 *)utf8_str, strlen(utf8_str), &tail);
836	  free(utf8_str);
837	} else
838	  goto fallback;
839    }
840# endif
841    else
842#endif
843    {
844    fallback:
845	XftTextExtents8 (XtDisplay (w), w->clock.face, (FcChar8 *) str,
846			 off, &head);
847	XftTextExtents8 (XtDisplay (w), w->clock.face, (FcChar8 *) str + off,
848			 len - off, &tail);
849    }
850
851    /*
852     * Compute position of tail
853     */
854    x = w->clock.padding + head.xOff;
855    y = w->clock.face->ascent + w->clock.padding + head.yOff;
856    /*
857     * Compute bounds of tail, pad a bit as the bounds aren't exact
858     */
859    bounds->x = x - tail.x - 1;
860    bounds->y = y - tail.y - 1;
861    bounds->width = tail.width + 2;
862    bounds->height = tail.height + 2;
863    if (xp) *xp = x;
864    if (yp) *yp = y;
865}
866
867static void
868RenderUpdateRectBounds (XRectangle *damage, XRectangle *bounds)
869{
870    int	    x1 = bounds->x;
871    int	    y1 = bounds->y;
872    int	    x2 = bounds->x + bounds->width;
873    int	    y2 = bounds->y + bounds->height;
874    int	    d_x1 = damage->x;
875    int	    d_y1 = damage->y;
876    int	    d_x2 = damage->x + damage->width;
877    int	    d_y2 = damage->y + damage->height;
878
879    if (x1 == x2)
880    {
881	x1 = d_x1;
882	x2 = d_x2;
883    }
884    else
885    {
886	if (d_x1 < x1) x1 = d_x1;
887	if (d_x2 > x2) x2 = d_x2;
888    }
889    if (y1 == y2)
890    {
891	y1 = d_y1;
892	y2 = d_y2;
893    }
894    else
895    {
896	if (d_y1 < y1) y1 = d_y1;
897	if (d_y2 > y2) y2 = d_y2;
898    }
899
900    bounds->x = x1;
901    bounds->y = y1;
902    bounds->width = x2 - x1;
903    bounds->height = y2 - y1;
904}
905
906static Boolean
907RenderRectIn (XRectangle *rect, XRectangle *bounds)
908{
909    int	    x1 = bounds->x;
910    int	    y1 = bounds->y;
911    int	    x2 = bounds->x + bounds->width;
912    int	    y2 = bounds->y + bounds->height;
913    int	    r_x1 = rect->x;
914    int	    r_y1 = rect->y;
915    int	    r_x2 = rect->x + rect->width;
916    int	    r_y2 = rect->y + rect->height;
917
918    return r_x1 < x2 && x1 < r_x2 && r_y1 < y2 && y1 < r_y2;
919}
920
921#define LINE_WIDTH  0.01
922#include <math.h>
923
924#define XCoord(x,w) ((x) * (w)->clock.x_scale + (w)->clock.x_off)
925#define YCoord(y,w) ((y) * (w)->clock.y_scale + (w)->clock.y_off)
926
927static void
928RenderUpdateBounds (XPointDouble *points, int npoints, XRectangle *bounds)
929{
930    int	    x1 = bounds->x;
931    int	    y1 = bounds->y;
932    int	    x2 = bounds->x + bounds->width;
933    int	    y2 = bounds->y + bounds->height;
934
935    while (npoints--)
936    {
937	int	    r_x1 = points[0].x;
938	int	    r_y1 = points[0].y;
939	int	    r_x2 = points[0].x + 1;
940	int	    r_y2 = points[0].y + 1;
941
942	if (x1 == x2)
943	    x2 = x1 = r_x1;
944	if (y1 == y2)
945	    y2 = y1 = r_y1;
946	if (r_x1 < x1) x1 = r_x1;
947	if (r_y1 < y1) y1 = r_y1;
948	if (r_x2 > x2) x2 = r_x2;
949	if (r_y2 > y2) y2 = r_y2;
950	points++;
951    }
952    bounds->x = x1;
953    bounds->y = y1;
954    bounds->width = x2 - x1;
955    bounds->height = y2 - y1;
956}
957
958static Boolean
959RenderCheckBounds (XPointDouble *points, int npoints, XRectangle *bounds)
960{
961    int	    x1 = bounds->x;
962    int	    y1 = bounds->y;
963    int	    x2 = bounds->x + bounds->width;
964    int	    y2 = bounds->y + bounds->height;
965
966    while (npoints--)
967    {
968	if (x1 <= points->x && points->x <= x2 &&
969	    y1 <= points->y && points->y <= y2)
970	    return True;
971	points++;
972    }
973    return False;
974}
975
976static void
977RenderUpdate (ClockWidget w)
978{
979    if (w->clock.buffer && w->clock.pixmap)
980    {
981	XCopyArea (XtDisplay (w), w->clock.pixmap,
982		   XtWindow (w), w->clock.EraseGC,
983		   w->clock.damage.x, w->clock.damage.y,
984		   w->clock.damage.width, w->clock.damage.height,
985		   w->clock.damage.x, w->clock.damage.y);
986    }
987}
988
989static void
990RenderResetBounds (XRectangle *bounds)
991{
992    bounds->x = 0;
993    bounds->y = 0;
994    bounds->width = 0;
995    bounds->height = 0;
996}
997
998static void
999RenderLine (ClockWidget w, XDouble x1, XDouble y1, XDouble x2, XDouble y2,
1000	    XftColor *color,
1001	    Boolean draw)
1002{
1003    XPointDouble    poly[4];
1004    XDouble	    dx = (x2 - x1);
1005    XDouble	    dy = (y2 - y1);
1006    XDouble	    len = sqrt (dx*dx + dy*dy);
1007    XDouble	    ldx = (LINE_WIDTH/2.0) * dy / len;
1008    XDouble	    ldy = (LINE_WIDTH/2.0) * dx / len;
1009
1010    poly[0].x = XCoord (x1 + ldx, w);
1011    poly[0].y = YCoord (y1 - ldy, w);
1012
1013    poly[1].x = XCoord (x2 + ldx, w);
1014    poly[1].y = YCoord (y2 - ldy, w);
1015
1016    poly[2].x = XCoord (x2 - ldx, w);
1017    poly[2].y = YCoord (y2 + ldy, w);
1018
1019    poly[3].x = XCoord (x1 - ldx, w);
1020    poly[3].y = YCoord (y1 + ldy, w);
1021
1022    RenderUpdateBounds (poly, 4, &w->clock.damage);
1023    if (draw)
1024    {
1025	if (RenderCheckBounds (poly, 4, &w->clock.damage))
1026	{
1027	    RenderPrepare (w, color);
1028	    XRenderCompositeDoublePoly (XtDisplay (w),
1029					PictOpOver,
1030					w->clock.fill_picture,
1031					w->clock.picture,
1032					w->clock.mask_format,
1033					0, 0, 0, 0, poly, 4, EvenOddRule);
1034	}
1035    }
1036    else
1037	RenderUpdateBounds (poly, 4, &w->clock.damage);
1038}
1039
1040static void
1041RenderRotate (ClockWidget w, XPointDouble *out, double x, double y, double s, double c)
1042{
1043    out->x = XCoord (x * c - y * s, w);
1044    out->y = YCoord (y * c + x * s, w);
1045}
1046
1047static void
1048RenderHand (ClockWidget w, int tick_units, int size, XftColor *color,
1049	    Boolean draw)
1050{
1051    double	    c, s;
1052    XPointDouble    poly[3];
1053    double	    outer_x;
1054    double	    inner_y;
1055
1056    ClockAngle (tick_units, &c, &s);
1057    s = -s;
1058
1059    /* compute raw positions */
1060    outer_x = size / 100.0;
1061    inner_y = HAND_WIDTH_FRACT / 100.0;
1062
1063    /* rotate them into position */
1064    RenderRotate (w, &poly[0], outer_x, 0.0, s, c);
1065    RenderRotate (w, &poly[1], -inner_y, inner_y, s, c);
1066    RenderRotate (w, &poly[2], -inner_y, -inner_y, s, c);
1067
1068    if (draw)
1069    {
1070	if (RenderCheckBounds (poly, 3, &w->clock.damage))
1071	{
1072	    RenderPrepare (w, color);
1073	    XRenderCompositeDoublePoly (XtDisplay (w),
1074					PictOpOver,
1075					w->clock.fill_picture,
1076					w->clock.picture,
1077					w->clock.mask_format,
1078					0, 0, 0, 0, poly, 3, EvenOddRule);
1079	}
1080    }
1081    RenderUpdateBounds (poly, 3, &w->clock.damage);
1082}
1083
1084static void
1085RenderHands (ClockWidget w, struct tm *tm, Boolean draw)
1086{
1087    RenderHand (w, tm->tm_hour * 300 + tm->tm_min*5, HOUR_HAND_FRACT, &w->clock.hour_color, draw);
1088    RenderHand (w, tm->tm_min * 60 + tm->tm_sec, MINUTE_HAND_FRACT, &w->clock.min_color, draw);
1089}
1090
1091static void
1092RenderSec (ClockWidget w, struct tm *tm, Boolean draw)
1093{
1094    double	    c, s;
1095    XPointDouble    poly[10];
1096    double	    inner_x, middle_x, outer_x, far_x;
1097    double	    middle_y;
1098    double	    line_y;
1099
1100    ClockAngle (tm->tm_sec * 60, &c, &s);
1101
1102    s = -s;
1103
1104    /*
1105     * Compute raw positions
1106     */
1107    line_y = LINE_WIDTH;
1108    inner_x = (MINUTE_HAND_FRACT / 100.0);
1109    middle_x = ((SECOND_HAND_FRACT + MINUTE_HAND_FRACT) / 200.0);
1110    outer_x = (SECOND_HAND_FRACT / 100.0);
1111    far_x = (MINOR_TICK_FRACT / 100.0);
1112    middle_y = (SECOND_WIDTH_FRACT / 100.0);
1113
1114    /*
1115     * Rotate them into position
1116     */
1117    RenderRotate (w, &poly[0], -line_y, line_y, s, c);
1118    RenderRotate (w, &poly[1], inner_x, line_y, s, c);
1119    RenderRotate (w, &poly[2], middle_x, middle_y, s, c);
1120    RenderRotate (w, &poly[3], outer_x, line_y, s, c);
1121    RenderRotate (w, &poly[4], far_x, line_y, s, c);
1122    RenderRotate (w, &poly[5], far_x, -line_y, s, c);
1123    RenderRotate (w, &poly[6], outer_x, -line_y, s, c);
1124    RenderRotate (w, &poly[7], middle_x, -middle_y, s, c);
1125    RenderRotate (w, &poly[8], inner_x, -line_y, s, c);
1126    RenderRotate (w, &poly[9], -line_y, -line_y, s, c);
1127
1128    if (draw)
1129    {
1130	if (RenderCheckBounds (poly, 10, &w->clock.damage))
1131	{
1132	    RenderPrepare (w, &w->clock.sec_color);
1133	    XRenderCompositeDoublePoly (XtDisplay (w),
1134					PictOpOver,
1135					w->clock.fill_picture,
1136					w->clock.picture,
1137					w->clock.mask_format,
1138					0, 0, 0, 0, poly, 10, EvenOddRule);
1139	}
1140    }
1141    else
1142    {
1143	RenderUpdateBounds (poly, 10, &w->clock.damage);
1144    }
1145}
1146
1147#endif
1148
1149static void
1150Realize(Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs)
1151{
1152     ClockWidget	w = (ClockWidget) gw;
1153#ifdef notdef
1154     *valueMask |= CWBitGravity;
1155     attrs->bit_gravity = ForgetGravity;
1156#endif
1157     switch (w->clock.backing_store) {
1158     case Always:
1159     case NotUseful:
1160     case WhenMapped:
1161     	*valueMask |=CWBackingStore;
1162	attrs->backing_store = w->clock.backing_store;
1163	break;
1164     }
1165     (*clockWidgetClass->core_class.superclass->core_class.realize)
1166	 (gw, valueMask, attrs);
1167     Resize(gw);
1168}
1169
1170static void
1171Destroy(Widget gw)
1172{
1173     ClockWidget w = (ClockWidget) gw;
1174     if (w->clock.interval_id) XtRemoveTimeOut (w->clock.interval_id);
1175#ifdef XRENDER
1176    if (w->clock.picture)
1177	XRenderFreePicture (XtDisplay(w), w->clock.picture);
1178    if (w->clock.fill_picture)
1179	XRenderFreePicture (XtDisplay(w), w->clock.fill_picture);
1180#endif
1181     XtReleaseGC (gw, w->clock.myGC);
1182     XtReleaseGC (gw, w->clock.HighGC);
1183     XtReleaseGC (gw, w->clock.HandGC);
1184     XtReleaseGC (gw, w->clock.EraseGC);
1185}
1186
1187static void
1188Resize(Widget gw)
1189{
1190    ClockWidget w = (ClockWidget) gw;
1191    /* don't do this computation if window hasn't been realized yet. */
1192    if (XtIsRealized(gw) && w->clock.analog) {
1193	/* need signed value since Dimension is unsigned */
1194	int radius = ((int) min(w->core.width, w->core.height) - (int) (2 * w->clock.padding)) / 2;
1195        w->clock.radius = (Dimension) max (radius, 1);
1196
1197        w->clock.second_hand_length = (int)(SECOND_HAND_FRACT * w->clock.radius) / 100;
1198        w->clock.minute_hand_length = (int)(MINUTE_HAND_FRACT * w->clock.radius) / 100;
1199        w->clock.hour_hand_length = (int)(HOUR_HAND_FRACT * w->clock.radius) / 100;
1200        w->clock.hand_width = (int)(HAND_WIDTH_FRACT * w->clock.radius) / 100;
1201        w->clock.second_hand_width = (int)(SECOND_WIDTH_FRACT * w->clock.radius) / 100;
1202
1203        w->clock.centerX = w->core.width / 2;
1204        w->clock.centerY = w->core.height / 2;
1205    }
1206#ifdef XRENDER
1207    w->clock.x_scale = 0.45 * w->core.width;
1208    w->clock.y_scale = 0.45 * w->core.height;
1209    w->clock.x_off = 0.5 * w->core.width;
1210    w->clock.y_off = 0.5 * w->core.height;
1211    if (w->clock.pixmap)
1212    {
1213	XFreePixmap (XtDisplay (w), w->clock.pixmap);
1214	w->clock.pixmap = 0;
1215	if (w->clock.draw)
1216	{
1217	    XftDrawDestroy (w->clock.draw);
1218	    w->clock.draw = NULL;
1219	}
1220	w->clock.picture = 0;
1221    }
1222#endif
1223}
1224
1225/* ARGSUSED */
1226static void
1227Redisplay(Widget gw, XEvent *event, Region region)
1228{
1229    ClockWidget w = (ClockWidget) gw;
1230    if (w->clock.analog) {
1231#ifdef XRENDER
1232	if (w->clock.render && w->clock.can_polygon)
1233	    XClipBox (region, &w->clock.damage);
1234	else
1235#endif
1236	{
1237	    if (w->clock.numseg != 0)
1238		erase_hands (w, (struct tm *) 0);
1239	    DrawClockFace(w);
1240	}
1241    } else {
1242#ifdef XRENDER
1243	if (w->clock.render)
1244	    XClipBox (region, &w->clock.damage);
1245#endif
1246	w->clock.prev_time_string[0] = '\0';
1247    }
1248    clock_tic((XtPointer)w, (XtIntervalId *)NULL);
1249}
1250
1251/* Choose the update times for well-defined clock states.
1252 *
1253 * For example, in HH:MM:SS notation the last number rolls over
1254 * every 60 seconds and has at most 60 display states.  The sequence
1255 * depends on its initial value t0 and the update period u, e.g.
1256 *
1257 *   u (s)  d (s)  ti (s)                    m (states)  l (s)
1258 *    2      2     {0,2, .. 58}              30            60
1259 *    7      1     {0,7, .. 56,3, .. 53}     60           420
1260 *   15     15     {0,15,30,45}               4            60
1261 *   45     15     {0,45,30,15}               4           180
1262 *   53      1     {0,53,46, .. 4,57, .. 7}  60          3180
1263 *   58      2     {0,58,56, .. 2}           30          1740
1264 *   60     60     {0}                        1            60
1265 *
1266 * u=  update period in seconds,
1267 * ti= time at update i from the reference, HH:MM:00 or HH:00:00,
1268 * n=  the roll over time, the modulus, 60 s or 3600 s,
1269 * m=  the sequence length, the order of u in the modulo n group Z/nZ,
1270 * l=  the total sequence duration =m*u.
1271 * d=  gcd(n,u) the greatest common divisor
1272 *
1273 * The time t(i) determines the clock state.  It follows from
1274 *
1275 *   t(i)=t(i-1)+u mod n  <=>  t(i)=t(0)+i*u mod n
1276 *
1277 * which defines a { t(0) .. t(m-1) } sequence of m unique elements.
1278 * Hence, u generates a subgroup U={k*u mod n : k in Z} of Z/nZ so
1279 * its order m divides n.  This m satisfies
1280 *
1281 *   t(m)=t(0)  <=>  m*u mod n = 0  <=>  m*u = r*n  <=>  m=n/d, r=u/d
1282 *
1283 * where d divides n and u.  Choosing
1284 *
1285 *   d=gcd(n,u)  <=>  n/d and u/d are coprime  =>  m=n/d is minimum
1286 *
1287 * thus gives the order.  Furthermore, the greatest common divisor d is
1288 * also the minimum value generator of the set U.  Assume a generator e
1289 * where
1290 *
1291 *   e|{n,u}  <=>  Ai,Ej: i*u mod n = j*e  <=>  j=f(i)=(i*u mod n)/e
1292 *
1293 * such that f maps i to m=ord(u) unique values of j.  Its properties are
1294 *
1295 *   j=i*u/e mod n/e   ==>  0<=j<n/e
1296 *
1297 *   ord(u/e, mod n/e)=n/e/gcd(n/e,u/e)=n/d=m  ==>  J={j=f(i)}, |J|=m
1298 *
1299 *   ord(e)=n/gcd(n,e)=n/e
1300 *
1301 * from wich follows
1302 *
1303 *   e=d  ==>  f: I={0,..,m-1} -> J={0,..,m-1}: j=i*r mod m is bijective
1304 *        ==>  D={k*d mod n : k in Z} = U.
1305 *
1306 * Any value e below d is no generator since it yields a non contiguous
1307 * J such that an l=0..n/e-1 exists not in J with l*e not in U.
1308 *
1309 * The update sequence t(i) could be followed using the algorithm:
1310 *
1311 *   1. restore the expected value into t(i),
1312 *   2. calculate the next timeout t(i+1)=t(i)+u mod n,
1313 *   3. verify that the current tc(i) is between t(i)..t(i+1),
1314 *   4. calculate the time to wait w=t(i+1)-tc(i) mod n,
1315 *   5. store t(i+1),
1316 *
1317 * which implements state tracking.  This approach doesn't work well
1318 * since the set timeout w does not guarantee a next call at time
1319 * t(i+1), e.g. due to progam sleeps, time adjustments, and leap
1320 * seconds.  A robust method should only rely on the current time
1321 * tc(i) to identify t(i).  The derivation above shows 2 options:
1322 *
1323 *   1.  n={60,3600} and round to a multiple of d,
1324 *       but if d<u then the sequence is not guaranteed.
1325 *   2.  choose n large and round to a multiple of u,
1326 *       but then the sequence resets at roll-over.
1327 *
1328 * The code below implements (2) with n this year's duration in seconds
1329 * and using local time year's start as epoch.
1330 */
1331static unsigned long
1332waittime(int update, struct timeval *tv, struct tm *tm)
1333{
1334  int twait;
1335  long twaitms;
1336  unsigned long retval;
1337
1338  if(update>0) {
1339    long tcur;
1340    int trem;
1341
1342    tcur=tm->tm_sec+60*(tm->tm_min+60*(tm->tm_hour+24*tm->tm_yday));
1343    /* ti=floor(tcur/u)*u, w=u-(tcur-ti), and tcur-ti==tcur % u */
1344    trem=tcur % update;
1345    twait=update-trem;
1346  } else {
1347    twait=-update;
1348  }
1349
1350  if(tv->tv_usec>0) {
1351    long usec;
1352    twait--;
1353    usec=1000000-tv->tv_usec;
1354    twaitms=(usec+999)/1000;  /* must round up to avoid zero retval */
1355  } else {
1356    twaitms=0;
1357  }
1358
1359  retval=(unsigned long)labs(twaitms+1000*twait);
1360  return retval;
1361}
1362
1363/* ARGSUSED */
1364static void
1365clock_tic(XtPointer client_data, XtIntervalId *id)
1366{
1367        ClockWidget w = (ClockWidget)client_data;
1368	struct tm tm;
1369	Time_t	time_value;
1370	struct timeval	tv;
1371	char	*time_ptr;
1372        register Display *dpy = XtDisplay(w);
1373        register Window win = XtWindow(w);
1374
1375	X_GETTIMEOFDAY (&tv);
1376	time_value = tv.tv_sec;
1377	tm = *localtime(&time_value);
1378	if (w->clock.update && (id || !w->clock.interval_id))
1379	    w->clock.interval_id =
1380		XtAppAddTimeOut( XtWidgetToApplicationContext( (Widget) w),
1381				 waittime(w->clock.update, &tv, &tm),
1382				 clock_tic, (XtPointer)w );
1383	/*
1384	 * Beep on the half hour; double-beep on the hour.
1385	 */
1386	if (w->clock.chime == TRUE) {
1387	    if (w->clock.beeped && (tm.tm_min != 30) &&
1388		(tm.tm_min != 0))
1389	      w->clock.beeped = FALSE;
1390	    if (((tm.tm_min == 30) || (tm.tm_min == 0))
1391		&& (!w->clock.beeped)) {
1392		w->clock.beeped = TRUE;
1393#ifdef XKB
1394		if (tm.tm_min==0) {
1395		    XkbStdBell(dpy,win,50,XkbBI_ClockChimeHour);
1396		    XkbStdBell(dpy,win,50,XkbBI_RepeatingLastBell);
1397		}
1398		else {
1399		    XkbStdBell(dpy,win,50,XkbBI_ClockChimeHalf);
1400		}
1401#else
1402		XBell(dpy, 50);
1403		if (tm.tm_min == 0)
1404		  XBell(dpy, 50);
1405#endif
1406	    }
1407	}
1408	if( w->clock.analog == FALSE ) {
1409	    int	clear_from = w->core.width;
1410	    int i, len, prev_len;
1411
1412	    time_ptr = TimeString (w, &tm);
1413	    len = strlen (time_ptr);
1414	    if (len && time_ptr[len - 1] == '\n') time_ptr[--len] = '\0';
1415	    prev_len = strlen (w->clock.prev_time_string);
1416	    for (i = 0; ((i < len) && (i < prev_len) &&
1417	    		 (w->clock.prev_time_string[i] == time_ptr[i])); i++);
1418
1419#ifdef XRENDER
1420	    if (w->clock.render)
1421	    {
1422		XRectangle  old_tail, new_tail, head;
1423		int	    x, y;
1424#if !defined(NO_I18N) && defined(HAVE_ICONV)
1425		char *utf8_str;
1426#endif
1427
1428		RenderTextBounds (w, w->clock.prev_time_string, i, prev_len,
1429				  &old_tail, NULL, NULL);
1430		RenderUpdateRectBounds (&old_tail, &w->clock.damage);
1431		RenderTextBounds (w, time_ptr, i, len,
1432				  &new_tail, NULL, NULL);
1433		RenderUpdateRectBounds (&new_tail, &w->clock.damage);
1434
1435		while (i)
1436		{
1437		    RenderTextBounds (w, time_ptr, 0, i, &head, NULL, NULL);
1438		    if (!RenderRectIn (&head, &w->clock.damage))
1439			break;
1440		    i--;
1441		}
1442		RenderTextBounds (w, time_ptr, i, len, &new_tail, &x, &y);
1443		RenderClip (w);
1444		RenderPrepare (w, NULL);
1445#ifndef NO_I18N
1446		if (w->clock.utf8) {
1447		    XftDrawStringUtf8 (w->clock.draw,
1448				    &w->clock.fg_color,
1449				    w->clock.face,
1450				    x, y,
1451				    (FcChar8 *) time_ptr + i, len - i);
1452
1453		}
1454# ifdef HAVE_ICONV
1455		else if ((utf8_str =
1456		    clock_to_utf8(time_ptr + i, len - i)) != NULL) {
1457		    	XftDrawStringUtf8 (w->clock.draw,
1458				    &w->clock.fg_color,
1459				    w->clock.face,
1460				    x, y,
1461				    (FcChar8 *)utf8_str, strlen(utf8_str) );
1462		    free(utf8_str);
1463		}
1464# endif
1465		else
1466#endif
1467		{
1468		    XftDrawString8 (w->clock.draw,
1469				    &w->clock.fg_color,
1470				    w->clock.face,
1471				    x, y,
1472				    (FcChar8 *) time_ptr + i, len - i);
1473		}
1474		RenderUpdate (w);
1475		RenderResetBounds (&w->clock.damage);
1476	    }
1477	    else
1478#endif
1479#ifndef NO_I18N
1480	    if(!no_locale) {
1481		if(0 < len) {
1482		    XFontSetExtents *fse
1483		      = XExtentsOfFontSet(w->clock.fontSet);
1484
1485		    XmbDrawImageString(dpy,win,w->clock.fontSet,w->clock.myGC,
1486				       (2+w->clock.padding +
1487					(i?XmbTextEscapement(w->clock.fontSet,
1488							     time_ptr,i):0)),
1489				       2+w->clock.padding+fse->max_logical_extent.height,
1490				       time_ptr+i,len-i
1491			);
1492		    /*
1493		     * Clear any left over bits
1494		     */
1495		    clear_from = XmbTextEscapement (w->clock.fontSet,time_ptr,
1496						    len) + 2+w->clock.padding;
1497		}
1498	    } else
1499#endif /* NO_I18N */
1500	    {
1501		XDrawImageString (dpy, win, w->clock.myGC,
1502				  (1+w->clock.padding +
1503				   XTextWidth (w->clock.font, time_ptr, i)),
1504				  w->clock.font->ascent+w->clock.padding,
1505				  time_ptr + i, len - i);
1506		/*
1507		 * Clear any left over bits
1508		 */
1509		clear_from = XTextWidth (w->clock.font, time_ptr, len)
1510		    	     + 2 + w->clock.padding;
1511	    }
1512	    if (clear_from < (int)w->core.width)
1513		XClearArea (dpy, win,
1514		    clear_from, 0, w->core.width - clear_from, w->core.height,
1515		    False);
1516#ifdef HAVE_STRLCPY
1517	    strlcpy (w->clock.prev_time_string+i, time_ptr+i,
1518		     sizeof(w->clock.prev_time_string)-i);
1519#else
1520	    strncpy (w->clock.prev_time_string+i, time_ptr+i,
1521		     sizeof(w->clock.prev_time_string)-i);
1522	    w->clock.prev_time_string[sizeof(w->clock.prev_time_string)-1] = 0;
1523#endif
1524	} else {
1525			/*
1526			 * The second (or minute) hand is sec (or min)
1527			 * sixtieths around the clock face. The hour hand is
1528			 * (hour + min/60) twelfths of the way around the
1529			 * clock-face.  The derivation is left as an excercise
1530			 * for the reader.
1531			 */
1532
1533			/*
1534			 * 12 hour clock.
1535			 */
1536			if(tm.tm_hour >= 12)
1537				tm.tm_hour -= 12;
1538
1539#ifdef XRENDER
1540			if (w->clock.render && w->clock.can_polygon)
1541			{
1542			    w->clock.mask_format = XRenderFindStandardFormat (XtDisplay (w),
1543									      w->clock.sharp ?
1544									      PictStandardA1 :
1545									      PictStandardA8);
1546			    /*
1547			     * Compute repaint area
1548			     */
1549			    if (tm.tm_min != w->clock.otm.tm_min ||
1550				tm.tm_hour != w->clock.otm.tm_hour ||
1551				tm.tm_sec != w->clock.otm.tm_sec)
1552			    {
1553				RenderHands (w, &w->clock.otm, False);
1554				RenderHands (w, &tm, False);
1555			    }
1556			    if (w->clock.show_second_hand &&
1557				tm.tm_sec != w->clock.otm.tm_sec)
1558			    {
1559				RenderSec (w, &w->clock.otm, False);
1560				RenderSec (w, &tm, False);
1561			    }
1562			    if (w->clock.damage.width &&
1563				w->clock.damage.height)
1564			    {
1565				RenderClip (w);
1566				DrawClockFace (w);
1567				RenderHands (w, &tm, True);
1568				if (w->clock.show_second_hand == TRUE)
1569				    RenderSec (w, &tm, True);
1570			    }
1571			    w->clock.otm = tm;
1572			    RenderUpdate (w);
1573			    RenderResetBounds (&w->clock.damage);
1574			    return;
1575			}
1576#endif
1577
1578			erase_hands (w, &tm);
1579
1580		    if (w->clock.numseg == 0 ||
1581			tm.tm_min != w->clock.otm.tm_min ||
1582			tm.tm_hour != w->clock.otm.tm_hour) {
1583			    w->clock.segbuffptr = w->clock.segbuff;
1584			    w->clock.numseg = 0;
1585			    /*
1586			     * Calculate the hour hand, fill it in with its
1587			     * color and then outline it.  Next, do the same
1588			     * with the minute hand.  This is a cheap hidden
1589			     * line algorithm.
1590			     */
1591			    DrawHand(w,
1592				w->clock.minute_hand_length, w->clock.hand_width,
1593				tm.tm_min * 60
1594			    );
1595			    if(w->clock.Hdpixel != w->core.background_pixel)
1596				XFillPolygon( dpy,
1597				    win, w->clock.HandGC,
1598				    w->clock.segbuff, VERTICES_IN_HANDS,
1599				    Convex, CoordModeOrigin
1600				);
1601			    XDrawLines( dpy,
1602				win, w->clock.HighGC,
1603				w->clock.segbuff, VERTICES_IN_HANDS,
1604				       CoordModeOrigin);
1605			    w->clock.hour = w->clock.segbuffptr;
1606			    DrawHand(w,
1607				w->clock.hour_hand_length, w->clock.hand_width,
1608				tm.tm_hour * 300 + tm.tm_min * 5
1609			    );
1610			    if(w->clock.Hdpixel != w->core.background_pixel) {
1611			      XFillPolygon(dpy,
1612					   win, w->clock.HandGC,
1613					   w->clock.hour,
1614					   VERTICES_IN_HANDS,
1615					   Convex, CoordModeOrigin
1616					   );
1617			    }
1618			    XDrawLines( dpy,
1619				       win, w->clock.HighGC,
1620				       w->clock.hour, VERTICES_IN_HANDS,
1621				       CoordModeOrigin );
1622
1623			    w->clock.sec = w->clock.segbuffptr;
1624		    }
1625		    if (w->clock.show_second_hand == TRUE) {
1626			    w->clock.segbuffptr = w->clock.sec;
1627			    DrawSecond(w,
1628				w->clock.second_hand_length - 2,
1629				w->clock.second_hand_width,
1630				w->clock.minute_hand_length + 2,
1631				tm.tm_sec * 60
1632			    );
1633			    if(w->clock.Hdpixel != w->core.background_pixel)
1634				XFillPolygon( dpy,
1635				    win, w->clock.HandGC,
1636				    w->clock.sec,
1637				    VERTICES_IN_HANDS -2,
1638				    Convex, CoordModeOrigin
1639			    );
1640			    XDrawLines( dpy,
1641				       win, w->clock.HighGC,
1642				       w->clock.sec,
1643				       VERTICES_IN_HANDS-1,
1644				       CoordModeOrigin
1645				        );
1646
1647			}
1648			w->clock.otm = tm;
1649		}
1650}
1651
1652static void
1653erase_hands(ClockWidget w, struct tm *tm)
1654{
1655    /*
1656     * Erase old hands.
1657     */
1658    if(w->clock.numseg > 0) {
1659	Display	*dpy;
1660	Window	win;
1661
1662	dpy = XtDisplay (w);
1663	win = XtWindow (w);
1664	if (w->clock.show_second_hand == TRUE) {
1665	    XDrawLines(dpy, win,
1666		w->clock.EraseGC,
1667		w->clock.sec,
1668		VERTICES_IN_HANDS-1,
1669		CoordModeOrigin);
1670	    if(w->clock.Hdpixel != w->core.background_pixel) {
1671		XFillPolygon(dpy,
1672			win, w->clock.EraseGC,
1673			w->clock.sec,
1674			VERTICES_IN_HANDS-2,
1675			Convex, CoordModeOrigin
1676			);
1677	    }
1678	}
1679	if(!tm || tm->tm_min != w->clock.otm.tm_min ||
1680		  tm->tm_hour != w->clock.otm.tm_hour)
1681 	{
1682	    XDrawLines( dpy, win,
1683			w->clock.EraseGC,
1684			w->clock.segbuff,
1685			VERTICES_IN_HANDS,
1686			CoordModeOrigin);
1687	    XDrawLines( dpy, win,
1688			w->clock.EraseGC,
1689			w->clock.hour,
1690			VERTICES_IN_HANDS,
1691			CoordModeOrigin);
1692	    if(w->clock.Hdpixel != w->core.background_pixel) {
1693		XFillPolygon( dpy, win,
1694 			      w->clock.EraseGC,
1695			      w->clock.segbuff, VERTICES_IN_HANDS,
1696			      Convex, CoordModeOrigin);
1697		XFillPolygon( dpy, win,
1698 			      w->clock.EraseGC,
1699			      w->clock.hour,
1700			      VERTICES_IN_HANDS,
1701			      Convex, CoordModeOrigin);
1702	    }
1703	}
1704    }
1705}
1706
1707static float const Sines[] = {
17080.000000, 0.001745, 0.003490, 0.005235, 0.006981, 0.008726, 0.010471, 0.012217,
17090.013962, 0.015707, 0.017452, 0.019197, 0.020942, 0.022687, 0.024432, 0.026176,
17100.027921, 0.029666, 0.031410, 0.033155, 0.034899, 0.036643, 0.038387, 0.040131,
17110.041875, 0.043619, 0.045362, 0.047106, 0.048849, 0.050592, 0.052335, 0.054078,
17120.055821, 0.057564, 0.059306, 0.061048, 0.062790, 0.064532, 0.066273, 0.068015,
17130.069756, 0.071497, 0.073238, 0.074978, 0.076719, 0.078459, 0.080198, 0.081938,
17140.083677, 0.085416, 0.087155, 0.088894, 0.090632, 0.092370, 0.094108, 0.095845,
17150.097582, 0.099319, 0.101056, 0.102792, 0.104528, 0.106264, 0.107999, 0.109734,
17160.111468, 0.113203, 0.114937, 0.116670, 0.118403, 0.120136, 0.121869, 0.123601,
17170.125333, 0.127064, 0.128795, 0.130526, 0.132256, 0.133986, 0.135715, 0.137444,
17180.139173, 0.140901, 0.142628, 0.144356, 0.146083, 0.147809, 0.149535, 0.151260,
17190.152985, 0.154710, 0.156434, 0.158158, 0.159881, 0.161603, 0.163325, 0.165047,
17200.166768, 0.168489, 0.170209, 0.171929, 0.173648, 0.175366, 0.177084, 0.178802,
17210.180519, 0.182235, 0.183951, 0.185666, 0.187381, 0.189095, 0.190808, 0.192521,
17220.194234, 0.195946, 0.197657, 0.199367, 0.201077, 0.202787, 0.204496, 0.206204,
17230.207911, 0.209618, 0.211324, 0.213030, 0.214735, 0.216439, 0.218143, 0.219846,
17240.221548, 0.223250, 0.224951, 0.226651, 0.228350, 0.230049, 0.231747, 0.233445,
17250.235142, 0.236838, 0.238533, 0.240228, 0.241921, 0.243615, 0.245307, 0.246999,
17260.248689, 0.250380, 0.252069, 0.253757, 0.255445, 0.257132, 0.258819, 0.260504,
17270.262189, 0.263873, 0.265556, 0.267238, 0.268919, 0.270600, 0.272280, 0.273959,
17280.275637, 0.277314, 0.278991, 0.280666, 0.282341, 0.284015, 0.285688, 0.287360,
17290.289031, 0.290702, 0.292371, 0.294040, 0.295708, 0.297374, 0.299040, 0.300705,
17300.302369, 0.304033, 0.305695, 0.307356, 0.309016, 0.310676, 0.312334, 0.313992,
17310.315649, 0.317304, 0.318959, 0.320612, 0.322265, 0.323917, 0.325568, 0.327217,
17320.328866, 0.330514, 0.332161, 0.333806, 0.335451, 0.337095, 0.338737, 0.340379,
17330.342020, 0.343659, 0.345298, 0.346935, 0.348572, 0.350207, 0.351841, 0.353474,
17340.355106, 0.356737, 0.358367, 0.359996, 0.361624, 0.363251, 0.364876, 0.366501,
17350.368124, 0.369746, 0.371367, 0.372987, 0.374606, 0.376224, 0.377840, 0.379456,
17360.381070, 0.382683, 0.384295, 0.385906, 0.387515, 0.389123, 0.390731, 0.392337,
17370.393941, 0.395545, 0.397147, 0.398749, 0.400349, 0.401947, 0.403545, 0.405141,
17380.406736, 0.408330, 0.409923, 0.411514, 0.413104, 0.414693, 0.416280, 0.417867,
17390.419452, 0.421035, 0.422618, 0.424199, 0.425779, 0.427357, 0.428935, 0.430511,
17400.432085, 0.433659, 0.435231, 0.436801, 0.438371, 0.439939, 0.441505, 0.443071,
17410.444635, 0.446197, 0.447759, 0.449318, 0.450877, 0.452434, 0.453990, 0.455544,
17420.457097, 0.458649, 0.460199, 0.461748, 0.463296, 0.464842, 0.466386, 0.467929,
17430.469471, 0.471011, 0.472550, 0.474088, 0.475624, 0.477158, 0.478691, 0.480223,
17440.481753, 0.483282, 0.484809, 0.486335, 0.487859, 0.489382, 0.490903, 0.492423,
17450.493941, 0.495458, 0.496973, 0.498487, 0.499999, 0.501510, 0.503019, 0.504527,
17460.506033, 0.507538, 0.509041, 0.510542, 0.512042, 0.513541, 0.515038, 0.516533,
17470.518027, 0.519519, 0.521009, 0.522498, 0.523985, 0.525471, 0.526955, 0.528438,
17480.529919, 0.531398, 0.532876, 0.534352, 0.535826, 0.537299, 0.538770, 0.540240,
17490.541708, 0.543174, 0.544639, 0.546101, 0.547563, 0.549022, 0.550480, 0.551936,
17500.553391, 0.554844, 0.556295, 0.557745, 0.559192, 0.560638, 0.562083, 0.563526,
17510.564967, 0.566406, 0.567843, 0.569279, 0.570713, 0.572145, 0.573576, 0.575005,
17520.576432, 0.577857, 0.579281, 0.580702, 0.582122, 0.583541, 0.584957, 0.586372,
17530.587785, 0.589196, 0.590605, 0.592013, 0.593418, 0.594822, 0.596224, 0.597625,
17540.599023, 0.600420, 0.601815, 0.603207, 0.604599, 0.605988, 0.607375, 0.608761,
17550.610145, 0.611527, 0.612907, 0.614285, 0.615661, 0.617035, 0.618408, 0.619779,
17560.621147, 0.622514, 0.623879, 0.625242, 0.626603, 0.627963, 0.629320, 0.630675,
17570.632029, 0.633380, 0.634730, 0.636078, 0.637423, 0.638767, 0.640109, 0.641449,
17580.642787, 0.644123, 0.645457, 0.646789, 0.648119, 0.649448, 0.650774, 0.652098,
17590.653420, 0.654740, 0.656059, 0.657375, 0.658689, 0.660001, 0.661311, 0.662620,
17600.663926, 0.665230, 0.666532, 0.667832, 0.669130, 0.670426, 0.671720, 0.673012,
17610.674302, 0.675590, 0.676875, 0.678159, 0.679441, 0.680720, 0.681998, 0.683273,
17620.684547, 0.685818, 0.687087, 0.688354, 0.689619, 0.690882, 0.692143, 0.693401,
17630.694658, 0.695912, 0.697165, 0.698415, 0.699663, 0.700909, 0.702153, 0.703394,
17640.704634, 0.705871, 0.707106,
1765};
1766static float const Cosines[] = {
17671.000000, 0.999998, 0.999993, 0.999986, 0.999975, 0.999961, 0.999945, 0.999925,
17680.999902, 0.999876, 0.999847, 0.999815, 0.999780, 0.999742, 0.999701, 0.999657,
17690.999610, 0.999559, 0.999506, 0.999450, 0.999390, 0.999328, 0.999262, 0.999194,
17700.999122, 0.999048, 0.998970, 0.998889, 0.998806, 0.998719, 0.998629, 0.998536,
17710.998440, 0.998341, 0.998239, 0.998134, 0.998026, 0.997915, 0.997801, 0.997684,
17720.997564, 0.997440, 0.997314, 0.997185, 0.997052, 0.996917, 0.996778, 0.996637,
17730.996492, 0.996345, 0.996194, 0.996041, 0.995884, 0.995724, 0.995561, 0.995396,
17740.995227, 0.995055, 0.994880, 0.994702, 0.994521, 0.994337, 0.994150, 0.993960,
17750.993767, 0.993571, 0.993372, 0.993170, 0.992965, 0.992757, 0.992546, 0.992331,
17760.992114, 0.991894, 0.991671, 0.991444, 0.991215, 0.990983, 0.990747, 0.990509,
17770.990268, 0.990023, 0.989776, 0.989525, 0.989272, 0.989015, 0.988756, 0.988493,
17780.988228, 0.987959, 0.987688, 0.987413, 0.987136, 0.986855, 0.986572, 0.986285,
17790.985996, 0.985703, 0.985407, 0.985109, 0.984807, 0.984503, 0.984195, 0.983885,
17800.983571, 0.983254, 0.982935, 0.982612, 0.982287, 0.981958, 0.981627, 0.981292,
17810.980955, 0.980614, 0.980271, 0.979924, 0.979575, 0.979222, 0.978867, 0.978508,
17820.978147, 0.977783, 0.977415, 0.977045, 0.976672, 0.976296, 0.975916, 0.975534,
17830.975149, 0.974761, 0.974370, 0.973975, 0.973578, 0.973178, 0.972775, 0.972369,
17840.971961, 0.971549, 0.971134, 0.970716, 0.970295, 0.969872, 0.969445, 0.969015,
17850.968583, 0.968147, 0.967709, 0.967267, 0.966823, 0.966376, 0.965925, 0.965472,
17860.965016, 0.964557, 0.964095, 0.963630, 0.963162, 0.962691, 0.962217, 0.961741,
17870.961261, 0.960779, 0.960293, 0.959805, 0.959313, 0.958819, 0.958322, 0.957822,
17880.957319, 0.956813, 0.956304, 0.955793, 0.955278, 0.954760, 0.954240, 0.953716,
17890.953190, 0.952661, 0.952129, 0.951594, 0.951056, 0.950515, 0.949972, 0.949425,
17900.948876, 0.948323, 0.947768, 0.947210, 0.946649, 0.946085, 0.945518, 0.944948,
17910.944376, 0.943800, 0.943222, 0.942641, 0.942057, 0.941470, 0.940880, 0.940288,
17920.939692, 0.939094, 0.938493, 0.937888, 0.937281, 0.936672, 0.936059, 0.935444,
17930.934825, 0.934204, 0.933580, 0.932953, 0.932323, 0.931691, 0.931055, 0.930417,
17940.929776, 0.929132, 0.928485, 0.927836, 0.927183, 0.926528, 0.925870, 0.925209,
17950.924546, 0.923879, 0.923210, 0.922538, 0.921863, 0.921185, 0.920504, 0.919821,
17960.919135, 0.918446, 0.917754, 0.917060, 0.916362, 0.915662, 0.914959, 0.914253,
17970.913545, 0.912834, 0.912120, 0.911403, 0.910683, 0.909961, 0.909236, 0.908508,
17980.907777, 0.907044, 0.906307, 0.905568, 0.904827, 0.904082, 0.903335, 0.902585,
17990.901832, 0.901077, 0.900318, 0.899557, 0.898794, 0.898027, 0.897258, 0.896486,
18000.895711, 0.894934, 0.894154, 0.893371, 0.892585, 0.891797, 0.891006, 0.890212,
18010.889416, 0.888617, 0.887815, 0.887010, 0.886203, 0.885393, 0.884580, 0.883765,
18020.882947, 0.882126, 0.881303, 0.880477, 0.879648, 0.878817, 0.877982, 0.877146,
18030.876306, 0.875464, 0.874619, 0.873772, 0.872922, 0.872069, 0.871213, 0.870355,
18040.869494, 0.868631, 0.867765, 0.866896, 0.866025, 0.865151, 0.864274, 0.863395,
18050.862513, 0.861629, 0.860742, 0.859852, 0.858959, 0.858064, 0.857167, 0.856267,
18060.855364, 0.854458, 0.853550, 0.852640, 0.851726, 0.850811, 0.849892, 0.848971,
18070.848048, 0.847121, 0.846193, 0.845261, 0.844327, 0.843391, 0.842452, 0.841510,
18080.840566, 0.839619, 0.838670, 0.837718, 0.836764, 0.835807, 0.834847, 0.833885,
18090.832921, 0.831954, 0.830984, 0.830012, 0.829037, 0.828060, 0.827080, 0.826098,
18100.825113, 0.824126, 0.823136, 0.822144, 0.821149, 0.820151, 0.819152, 0.818149,
18110.817144, 0.816137, 0.815127, 0.814115, 0.813100, 0.812083, 0.811063, 0.810041,
18120.809016, 0.807989, 0.806960, 0.805928, 0.804893, 0.803856, 0.802817, 0.801775,
18130.800731, 0.799684, 0.798635, 0.797583, 0.796529, 0.795473, 0.794414, 0.793353,
18140.792289, 0.791223, 0.790155, 0.789084, 0.788010, 0.786935, 0.785856, 0.784776,
18150.783693, 0.782608, 0.781520, 0.780430, 0.779337, 0.778243, 0.777145, 0.776046,
18160.774944, 0.773840, 0.772733, 0.771624, 0.770513, 0.769399, 0.768283, 0.767165,
18170.766044, 0.764921, 0.763796, 0.762668, 0.761538, 0.760405, 0.759271, 0.758134,
18180.756995, 0.755853, 0.754709, 0.753563, 0.752414, 0.751264, 0.750111, 0.748955,
18190.747798, 0.746638, 0.745475, 0.744311, 0.743144, 0.741975, 0.740804, 0.739631,
18200.738455, 0.737277, 0.736097, 0.734914, 0.733729, 0.732542, 0.731353, 0.730162,
18210.728968, 0.727772, 0.726574, 0.725374, 0.724171, 0.722967, 0.721760, 0.720551,
18220.719339, 0.718126, 0.716910, 0.715692, 0.714472, 0.713250, 0.712026, 0.710799,
18230.709570, 0.708339, 0.707106,
1824};
1825
1826static void
1827ClockAngle(int tick_units, double *sinp, double *cosp)
1828{
1829    int reduced, upper;
1830
1831    reduced = tick_units % 450;
1832    upper = tick_units / 450;
1833    if (upper & 1)
1834	reduced = 450 - reduced;
1835    if ((upper + 1) & 2) {
1836	*sinp = Cosines[reduced];
1837	*cosp = Sines[reduced];
1838    } else {
1839	*sinp = Sines[reduced];
1840	*cosp = Cosines[reduced];
1841    }
1842    if (upper >= 2 && upper < 6)
1843	*cosp = -*cosp;
1844    if (upper >= 4)
1845	*sinp = -*sinp;
1846}
1847
1848/*
1849 * DrawLine - Draws a line.
1850 *
1851 * blank_length is the distance from the center which the line begins.
1852 * length is the maximum length of the hand.
1853 * Tick_units is a number between zero and 12*60 indicating
1854 * how far around the circle (clockwise) from high noon.
1855 *
1856 * The blank_length feature is because I wanted to draw tick-marks around the
1857 * circle (for seconds).  The obvious means of drawing lines from the center
1858 * to the perimeter, then erasing all but the outside most pixels doesn't
1859 * work because of round-off error (sigh).
1860 */
1861static void
1862DrawLine(ClockWidget w, Dimension blank_length, Dimension length,
1863	 int tick_units)
1864{
1865	double dblank_length = (double)blank_length, dlength = (double)length;
1866	double cosangle, sinangle;
1867	int cx = w->clock.centerX, cy = w->clock.centerY, x1, y1, x2, y2;
1868
1869	/*
1870	 *  Angles are measured from 12 o'clock, clockwise increasing.
1871	 *  Since in X, +x is to the right and +y is downward:
1872	 *
1873	 *	x = x0 + r * sin(theta)
1874	 *	y = y0 - r * cos(theta)
1875	 *
1876	 */
1877	ClockAngle(tick_units, &sinangle, &cosangle);
1878
1879	/* break this out so that stupid compilers can cope */
1880	x1 = cx + (int)(dblank_length * sinangle);
1881	y1 = cy - (int)(dblank_length * cosangle);
1882	x2 = cx + (int)(dlength * sinangle);
1883	y2 = cy - (int)(dlength * cosangle);
1884	SetSeg(w, x1, y1, x2, y2);
1885}
1886
1887/*
1888 * DrawHand - Draws a hand.
1889 *
1890 * length is the maximum length of the hand.
1891 * width is the half-width of the hand.
1892 * Tick_units is a number between zero and 12*60 indicating
1893 * how far around the circle (clockwise) from high noon.
1894 *
1895 */
1896static void
1897DrawHand(ClockWidget w, Dimension length, Dimension width, int tick_units)
1898{
1899
1900	double cosangle, sinangle;
1901	register double ws, wc;
1902	Position x, y, x1, y1, x2, y2;
1903
1904	/*
1905	 *  Angles are measured from 12 o'clock, clockwise increasing.
1906	 *  Since in X, +x is to the right and +y is downward:
1907	 *
1908	 *	x = x0 + r * sin(theta)
1909	 *	y = y0 - r * cos(theta)
1910	 *
1911	 */
1912	ClockAngle(tick_units, &sinangle, &cosangle);
1913	/*
1914	 * Order of points when drawing the hand.
1915	 *
1916	 *		1,4
1917	 *		/ \
1918	 *	       /   \
1919	 *	      /     \
1920	 *	    2 ------- 3
1921	 */
1922	wc = width * cosangle;
1923	ws = width * sinangle;
1924	SetSeg(w,
1925	       x = w->clock.centerX + clock_round(length * sinangle),
1926	       y = w->clock.centerY - clock_round(length * cosangle),
1927	       x1 = w->clock.centerX - clock_round(ws + wc),
1928	       y1 = w->clock.centerY + clock_round(wc - ws));  /* 1 ---- 2 */
1929	/* 2 */
1930	SetSeg(w, x1, y1,
1931	       x2 = w->clock.centerX - clock_round(ws - wc),
1932	       y2 = w->clock.centerY + clock_round(wc + ws));  /* 2 ----- 3 */
1933
1934	SetSeg(w, x2, y2, x, y);	/* 3 ----- 1(4) */
1935}
1936
1937/*
1938 * DrawSecond - Draws the second hand (diamond).
1939 *
1940 * length is the maximum length of the hand.
1941 * width is the half-width of the hand.
1942 * offset is direct distance from center to tail end.
1943 * Tick_units is a number between zero and 12*60 indicating
1944 * how far around the circle (clockwise) from high noon.
1945 *
1946 */
1947static void
1948DrawSecond(ClockWidget w, Dimension length, Dimension width,
1949	   Dimension offset, int tick_units)
1950{
1951
1952	double cosangle, sinangle;
1953	register double ms, mc, ws, wc;
1954	register int mid;
1955	Position x, y;
1956
1957	/*
1958	 *  Angles are measured from 12 o'clock, clockwise increasing.
1959	 *  Since in X, +x is to the right and +y is downward:
1960	 *
1961	 *	x = x0 + r * sin(theta)
1962	 *	y = y0 - r * cos(theta)
1963	 *
1964	 */
1965	ClockAngle(tick_units, &sinangle, &cosangle);
1966	/*
1967	 * Order of points when drawing the hand.
1968	 *
1969	 *		1,5
1970	 *		/ \
1971	 *	       /   \
1972	 *	      /     \
1973	 *	    2<       >4
1974	 *	      \     /
1975	 *	       \   /
1976	 *		\ /
1977	 *	-	 3
1978	 *	|
1979	 *	|
1980	 *   offset
1981	 *	|
1982	 *	|
1983	 *	-	 + center
1984	 */
1985
1986	mid = (int) (length + offset) / 2;
1987	mc = mid * cosangle;
1988	ms = mid * sinangle;
1989	wc = width * cosangle;
1990	ws = width * sinangle;
1991	/*1 ---- 2 */
1992	SetSeg(w,
1993	       x = w->clock.centerX + clock_round(length * sinangle),
1994	       y = w->clock.centerY - clock_round(length * cosangle),
1995	       w->clock.centerX + clock_round(ms - wc),
1996	       w->clock.centerY - clock_round(mc + ws) );
1997	SetSeg(w, w->clock.centerX + clock_round(offset *sinangle),
1998	       w->clock.centerY - clock_round(offset * cosangle), /* 2-----3 */
1999	       w->clock.centerX + clock_round(ms + wc),
2000	       w->clock.centerY - clock_round(mc - ws));
2001	w->clock.segbuffptr->x = x;
2002	w->clock.segbuffptr++->y = y;
2003	w->clock.numseg ++;
2004}
2005
2006static void
2007SetSeg(ClockWidget w, int x1, int y1, int x2, int y2)
2008{
2009	w->clock.segbuffptr->x = x1;
2010	w->clock.segbuffptr++->y = y1;
2011	w->clock.segbuffptr->x = x2;
2012	w->clock.segbuffptr++->y = y2;
2013	w->clock.numseg += 2;
2014}
2015
2016/*
2017 *  Draw the clock face (every fifth tick-mark is longer
2018 *  than the others).
2019 */
2020static void
2021DrawClockFace(ClockWidget w)
2022{
2023	register int i;
2024	register int delta = (int)(w->clock.radius - w->clock.second_hand_length) / 3;
2025
2026	w->clock.segbuffptr = w->clock.segbuff;
2027	w->clock.numseg = 0;
2028	for (i = 0; i < 60; i++)
2029	{
2030#ifdef XRENDER
2031	    if (w->clock.render && w->clock.can_polygon)
2032	    {
2033		double	s, c;
2034		XDouble	x1, y1, x2, y2;
2035		XftColor	*color;
2036		ClockAngle (i * 60, &s, &c);
2037		x1 = c;
2038		y1 = s;
2039		if (i % 5)
2040		{
2041		    x2 = c * (MINOR_TICK_FRACT / 100.0);
2042		    y2 = s * (MINOR_TICK_FRACT / 100.0);
2043		    color = &w->clock.minor_color;
2044		}
2045		else
2046		{
2047		    x2 = c * (SECOND_HAND_FRACT / 100.0);
2048		    y2 = s * (SECOND_HAND_FRACT / 100.0);
2049		    color = &w->clock.major_color;
2050		}
2051		RenderLine (w, x1, y1, x2, y2, color, True);
2052	    }
2053	    else
2054#endif
2055	    {
2056		DrawLine(w, ( (i % 5) == 0 ?
2057			     w->clock.second_hand_length :
2058			     (w->clock.radius - delta) ),
2059			 w->clock.radius, i * 60);
2060	    }
2061	}
2062#ifdef XRENDER
2063	if (w->clock.render && w->clock.can_polygon)
2064	    return;
2065#endif
2066	/*
2067	 * Go ahead and draw it.
2068	 */
2069	XDrawSegments(XtDisplay(w), XtWindow(w),
2070		      w->clock.myGC, (XSegment *) &(w->clock.segbuff[0]),
2071		      w->clock.numseg/2);
2072
2073	w->clock.segbuffptr = w->clock.segbuff;
2074	w->clock.numseg = 0;
2075}
2076
2077static int
2078clock_round(double x)
2079{
2080	return(x >= 0.0 ? (int)(x + .5) : (int)(x - .5));
2081}
2082
2083#ifdef XRENDER
2084static Boolean
2085sameColor (XftColor *old, XftColor *new)
2086{
2087    if (old->color.red != new->color.red) return False;
2088    if (old->color.green != new->color.green) return False;
2089    if (old->color.blue != new->color.blue) return False;
2090    if (old->color.alpha != new->color.alpha) return False;
2091    return True;
2092}
2093#endif
2094
2095/* ARGSUSED */
2096static Boolean
2097SetValues(Widget gcurrent, Widget grequest, Widget gnew,
2098	  ArgList args, Cardinal *num_args)
2099{
2100      ClockWidget current = (ClockWidget) gcurrent;
2101      ClockWidget new = (ClockWidget) gnew;
2102      Boolean redisplay = FALSE;
2103      XtGCMask valuemask;
2104      XGCValues	myXGCV;
2105
2106      /* first check for changes to clock-specific resources.  We'll accept all
2107         the changes, but may need to do some computations first. */
2108
2109      if (new->clock.update != current->clock.update) {
2110	  if (current->clock.interval_id)
2111	      XtRemoveTimeOut (current->clock.interval_id);
2112	  if (new->clock.update && XtIsRealized( (Widget) new))
2113	      new->clock.interval_id = XtAppAddTimeOut(
2114                                         XtWidgetToApplicationContext(gnew),
2115					 abs(new->clock.update)*1000,
2116				         clock_tic, (XtPointer)gnew);
2117
2118	  new->clock.show_second_hand =(abs(new->clock.update) <= SECOND_HAND_TIME);
2119	  if (new->clock.show_second_hand != current->clock.show_second_hand)
2120	    redisplay = TRUE;
2121      }
2122
2123      if (new->clock.padding != current->clock.padding)
2124	   redisplay = TRUE;
2125
2126      if (new->clock.analog != current->clock.analog)
2127	   redisplay = TRUE;
2128
2129       if (new->clock.font != current->clock.font)
2130	   redisplay = TRUE;
2131
2132#ifndef NO_I18N
2133       if (new->clock.fontSet != current->clock.fontSet)
2134	   redisplay = TRUE;
2135#endif
2136
2137      if ((ClockFgPixel(new) != ClockFgPixel (current))
2138          || (new->core.background_pixel != current->core.background_pixel)) {
2139          valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
2140	  myXGCV.foreground = ClockFgPixel (new);
2141	  myXGCV.background = new->core.background_pixel;
2142          myXGCV.font = new->clock.font->fid;
2143	  myXGCV.line_width = 0;
2144	  XtReleaseGC (gcurrent, current->clock.myGC);
2145	  new->clock.myGC = XtGetGC(gcurrent, valuemask, &myXGCV);
2146	  redisplay = TRUE;
2147          }
2148
2149      if (new->clock.Hipixel != current->clock.Hipixel) {
2150          valuemask = GCForeground | GCLineWidth;
2151	  myXGCV.foreground = new->clock.Hipixel;
2152          myXGCV.font = new->clock.font->fid;
2153	  myXGCV.line_width = 0;
2154	  XtReleaseGC (gcurrent, current->clock.HighGC);
2155	  new->clock.HighGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
2156	  redisplay = TRUE;
2157          }
2158
2159      if (new->clock.Hdpixel != current->clock.Hdpixel) {
2160          valuemask = GCForeground;
2161	  myXGCV.foreground = new->clock.Hdpixel;
2162	  XtReleaseGC (gcurrent, current->clock.HandGC);
2163	  new->clock.HandGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
2164	  redisplay = TRUE;
2165          }
2166
2167      if (new->core.background_pixel != current->core.background_pixel) {
2168          valuemask = GCForeground | GCLineWidth | GCGraphicsExposures;
2169	  myXGCV.foreground = new->core.background_pixel;
2170	  myXGCV.line_width = 0;
2171	  myXGCV.graphics_exposures = False;
2172	  XtReleaseGC (gcurrent, current->clock.EraseGC);
2173	  new->clock.EraseGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
2174	  redisplay = TRUE;
2175	  }
2176#ifdef XRENDER
2177     if (new->clock.face != current->clock.face)
2178	redisplay = TRUE;
2179     if (!sameColor (&new->clock.hour_color, &current->clock.fg_color) ||
2180	 !sameColor (&new->clock.hour_color, &current->clock.hour_color) ||
2181	 !sameColor (&new->clock.min_color, &current->clock.min_color) ||
2182	 !sameColor (&new->clock.sec_color, &current->clock.sec_color) ||
2183	 !sameColor (&new->clock.major_color, &current->clock.major_color) ||
2184	 !sameColor (&new->clock.minor_color, &current->clock.minor_color))
2185	redisplay = True;
2186    if (new->clock.sharp != current->clock.sharp)
2187	redisplay = True;
2188    if (new->clock.render != current->clock.render)
2189	redisplay = True;
2190    if (new->clock.buffer != current->clock.buffer)
2191    {
2192	if (new->clock.pixmap)
2193	{
2194	    XFreePixmap (XtDisplay (new), new->clock.pixmap);
2195	    new->clock.pixmap = 0;
2196	}
2197	if (new->clock.draw)
2198	{
2199	    XftDrawDestroy (new->clock.draw);
2200	    new->clock.draw = NULL;
2201	}
2202	new->clock.picture = 0;
2203    }
2204#endif
2205     return (redisplay);
2206
2207}
2208
2209#if !defined(NO_I18N) && defined(HAVE_ICONV)
2210static char *
2211clock_to_utf8(const char *str, int in_len)
2212{
2213    iconv_t cd;
2214    char *buf;
2215    size_t buf_size;
2216    size_t ileft, oleft;
2217    ICONV_CONST char *inptr;
2218    char *outptr;
2219    size_t ret;
2220    const char *code_set = nl_langinfo(CODESET);
2221
2222    if (str == NULL ||code_set == NULL || strcasecmp(code_set, "646") == 0)
2223    	return NULL;
2224
2225    if (strcasecmp(code_set, "UTF-8") == 0)
2226    	return strdup(str);
2227
2228    cd = iconv_open("UTF-8", code_set);
2229    if (cd == (iconv_t)-1)
2230    	return NULL;
2231
2232    buf_size = MB_LEN_MAX * (in_len + 1);
2233    if ((buf = malloc(buf_size)) == NULL) {
2234    	(void) iconv_close(cd);
2235    	return NULL;
2236    }
2237
2238    inptr = str;
2239    ileft = in_len;
2240    outptr = buf;
2241    oleft = buf_size;
2242
2243    ret = iconv(cd, &inptr, &ileft, &outptr, &oleft);
2244    if (ret == (size_t)(-1) || oleft == 0 ) {
2245        free(buf);
2246        buf = NULL;
2247    } else
2248	*outptr = '\0';
2249
2250    (void) iconv_close(cd);
2251    return buf;
2252}
2253#endif
2254