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