Clock.c revision e9554658
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 *);
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
328XtConvertArgRec 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
492XtConvertArgRec 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 (str[len - 1] == '\n')
630	   str[--len] = '\0';
631
632#ifdef XRENDER
633       if (w->clock.render)
634       {
635	XGlyphInfo  extents;
636#ifndef NO_I18N
637# ifdef HAVE_ICONV
638	char *utf8_str;
639# endif
640	if (w->clock.utf8)
641	    XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
642				(FcChar8 *) str, len, &extents);
643# ifdef HAVE_ICONV
644	else if ((utf8_str = clock_to_utf8(str)) != NULL) {
645	        XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
646			(FcChar8 *)utf8_str, strlen(utf8_str), &extents);
647		free(utf8_str);
648	}
649# endif
650	else
651#endif
652	    XftTextExtents8 (XtDisplay (w), w->clock.face,
653			     (FcChar8 *) str, len, &extents);
654	min_width = extents.xOff + 2 * w->clock.padding;
655	min_height = w->clock.face->ascent + w->clock.face->descent +
656		     2 * w->clock.padding;
657       }
658       else
659#endif
660#ifndef NO_I18N
661       if (!no_locale) {
662	   XFontSetExtents *fse;
663
664	   if(w->clock.fontSet == NULL) {
665	       char **missing, *default_str;
666	       int n_missing;
667	       w->clock.fontSet = XCreateFontSet( XtDisplay(w),
668		 XtDefaultFontSet,
669		 &missing,
670		 &n_missing,
671		 &default_str);
672	   }
673	   if (w->clock.fontSet != NULL)
674	   {
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,
679		 len)
680		 + 2 * w->clock.padding;
681	       min_height = fse->max_logical_extent.height +
682		 3 * w->clock.padding;
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       }
700    }
701    if (w->core.width == 0)
702	w->core.width = min_width;
703    if (w->core.height == 0)
704	w->core.height = min_height;
705
706    myXGCV.foreground = ClockFgPixel (w);
707    myXGCV.background = w->core.background_pixel;
708    if (w->clock.font != NULL)
709	myXGCV.font = w->clock.font->fid;
710    else
711	valuemask &= ~GCFont;	/* use server default font */
712    myXGCV.line_width = 0;
713    w->clock.myGC = XtGetGC((Widget)w, valuemask, &myXGCV);
714
715    valuemask = GCForeground | GCLineWidth | GCGraphicsExposures;
716    myXGCV.foreground = w->core.background_pixel;
717    if (w->core.background_pixmap != XtUnspecifiedPixmap) {
718	myXGCV.tile = w->core.background_pixmap;
719	myXGCV.fill_style = FillTiled;
720	valuemask |= (GCTile | GCFillStyle);
721    }
722    myXGCV.graphics_exposures = False;
723    w->clock.EraseGC = XtGetGC((Widget)w, valuemask, &myXGCV);
724    valuemask &= ~(GCTile | GCFillStyle);
725
726    myXGCV.foreground = w->clock.Hipixel;
727    w->clock.HighGC = XtGetGC((Widget)w, valuemask, &myXGCV);
728
729    valuemask = GCForeground;
730    myXGCV.foreground = w->clock.Hdpixel;
731    w->clock.HandGC = XtGetGC((Widget)w, valuemask, &myXGCV);
732
733    if (w->clock.update <= 0)
734	w->clock.update = 60;	/* make invalid update's use a default */
735    w->clock.show_second_hand = (w->clock.update <= SECOND_HAND_TIME);
736    w->clock.numseg = 0;
737    w->clock.interval_id = 0;
738    memset (&w->clock.otm, '\0', sizeof (w->clock.otm));
739#ifdef XRENDER
740    {
741	int major, minor;
742
743	if (XRenderQueryVersion (XtDisplay (w), &major, &minor) &&
744	    (major > 0 ||
745	     (major == 0 && minor >= 4)))
746	{
747	    w->clock.can_polygon = True;
748	}
749	else
750	    w->clock.can_polygon = False;
751    }
752    w->clock.pixmap = 0;
753    w->clock.draw = 0;
754    w->clock.damage.x = 0;
755    w->clock.damage.y = 0;
756    w->clock.damage.height = 0;
757    w->clock.damage.width = 0;
758#endif
759}
760
761#ifdef XRENDER
762static void
763RenderPrepare (ClockWidget  w, XftColor *color)
764{
765    if (!w->clock.draw)
766    {
767	Drawable    d = XtWindow (w);
768	if (w->clock.buffer)
769	{
770	    if (!w->clock.pixmap)
771	    {
772		Arg arg[1];
773		w->clock.pixmap = XCreatePixmap (XtDisplay (w), d,
774						 w->core.width,
775						 w->core.height,
776						 w->core.depth);
777		arg[0].name = XtNbackgroundPixmap;
778		arg[0].value = 0;
779		XtSetValues ((Widget) w, arg, 1);
780	    }
781	    d = w->clock.pixmap;
782	}
783
784	w->clock.draw = XftDrawCreate (XtDisplay (w), d,
785				       DefaultVisual (XtDisplay (w),
786						      DefaultScreen(XtDisplay (w))),
787				       w->core.colormap);
788	w->clock.picture = XftDrawPicture (w->clock.draw);
789    }
790    if (color)
791	w->clock.fill_picture = XftDrawSrcPicture (w->clock.draw, color);
792}
793
794static void
795RenderClip (ClockWidget w)
796{
797    Region	r;
798    Drawable	d;
799
800    RenderPrepare (w, 0);
801    if (w->clock.buffer)
802	d = w->clock.pixmap;
803    else
804	d = XtWindow (w);
805    XFillRectangle (XtDisplay (w), d, w->clock.EraseGC,
806		    w->clock.damage.x,
807		    w->clock.damage.y,
808		    w->clock.damage.width,
809		    w->clock.damage.height);
810    r = XCreateRegion ();
811    XUnionRectWithRegion (&w->clock.damage,
812			  r, r);
813    XftDrawSetClip (w->clock.draw, r);
814    XDestroyRegion (r);
815}
816
817static void
818RenderTextBounds (ClockWidget w, char *str, int off, int len,
819		  XRectangle *bounds, int *xp, int *yp)
820{
821    XGlyphInfo  head, tail;
822    int	    x, y;
823
824#ifndef NO_I18N
825# ifdef HAVE_ICONV
826    char *utf8_str;
827# endif
828    if (w->clock.utf8)
829    {
830	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
831			    (FcChar8 *) str, off, &head);
832	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
833			    (FcChar8 *) str + off, len - off, &tail);
834    }
835# ifdef HAVE_ICONV
836    else if ((utf8_str = clock_to_utf8(str)) != NULL)
837    {
838	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
839			    (FcChar8 *)utf8_str, off, &head);
840	XftTextExtentsUtf8 (XtDisplay (w), w->clock.face,
841		   (FcChar8 *)utf8_str + off, strlen(utf8_str) - off, &tail);
842	free(utf8_str);
843    }
844# endif
845    else
846#endif
847    {
848	XftTextExtents8 (XtDisplay (w), w->clock.face, (FcChar8 *) str,
849			 off, &head);
850	XftTextExtents8 (XtDisplay (w), w->clock.face, (FcChar8 *) str + off,
851			 len - off, &tail);
852    }
853
854    /*
855     * Compute position of tail
856     */
857    x = w->clock.padding + head.xOff;
858    y = w->clock.face->ascent + w->clock.padding + head.yOff;
859    /*
860     * Compute bounds of tail, pad a bit as the bounds aren't exact
861     */
862    bounds->x = x - tail.x - 1;
863    bounds->y = y - tail.y - 1;
864    bounds->width = tail.width + 2;
865    bounds->height = tail.height + 2;
866    if (xp) *xp = x;
867    if (yp) *yp = y;
868}
869
870static void
871RenderUpdateRectBounds (XRectangle *damage, XRectangle *bounds)
872{
873    int	    x1 = bounds->x;
874    int	    y1 = bounds->y;
875    int	    x2 = bounds->x + bounds->width;
876    int	    y2 = bounds->y + bounds->height;
877    int	    d_x1 = damage->x;
878    int	    d_y1 = damage->y;
879    int	    d_x2 = damage->x + damage->width;
880    int	    d_y2 = damage->y + damage->height;
881
882    if (x1 == x2)
883    {
884	x1 = d_x1;
885	x2 = d_x2;
886    }
887    else
888    {
889	if (d_x1 < x1) x1 = d_x1;
890	if (d_x2 > x2) x2 = d_x2;
891    }
892    if (y1 == y2)
893    {
894	y1 = d_y1;
895	y2 = d_y2;
896    }
897    else
898    {
899	if (d_y1 < y1) y1 = d_y1;
900	if (d_y2 > y2) y2 = d_y2;
901    }
902
903    bounds->x = x1;
904    bounds->y = y1;
905    bounds->width = x2 - x1;
906    bounds->height = y2 - y1;
907}
908
909static Boolean
910RenderRectIn (XRectangle *rect, XRectangle *bounds)
911{
912    int	    x1 = bounds->x;
913    int	    y1 = bounds->y;
914    int	    x2 = bounds->x + bounds->width;
915    int	    y2 = bounds->y + bounds->height;
916    int	    r_x1 = rect->x;
917    int	    r_y1 = rect->y;
918    int	    r_x2 = rect->x + rect->width;
919    int	    r_y2 = rect->y + rect->height;
920
921    return r_x1 < x2 && x1 < r_x2 && r_y1 < y2 && y1 < r_y2;
922}
923
924#define LINE_WIDTH  0.01
925#include <math.h>
926
927#define XCoord(x,w) ((x) * (w)->clock.x_scale + (w)->clock.x_off)
928#define YCoord(y,w) ((y) * (w)->clock.y_scale + (w)->clock.y_off)
929
930static void
931RenderUpdateBounds (XPointDouble *points, int npoints, XRectangle *bounds)
932{
933    int	    x1 = bounds->x;
934    int	    y1 = bounds->y;
935    int	    x2 = bounds->x + bounds->width;
936    int	    y2 = bounds->y + bounds->height;
937
938    while (npoints--)
939    {
940	int	    r_x1 = points[0].x;
941	int	    r_y1 = points[0].y;
942	int	    r_x2 = points[0].x + 1;
943	int	    r_y2 = points[0].y + 1;
944
945	if (x1 == x2)
946	    x2 = x1 = r_x1;
947	if (y1 == y2)
948	    y2 = y1 = r_y1;
949	if (r_x1 < x1) x1 = r_x1;
950	if (r_y1 < y1) y1 = r_y1;
951	if (r_x2 > x2) x2 = r_x2;
952	if (r_y2 > y2) y2 = r_y2;
953	points++;
954    }
955    bounds->x = x1;
956    bounds->y = y1;
957    bounds->width = x2 - x1;
958    bounds->height = y2 - y1;
959}
960
961static Boolean
962RenderCheckBounds (XPointDouble *points, int npoints, XRectangle *bounds)
963{
964    int	    x1 = bounds->x;
965    int	    y1 = bounds->y;
966    int	    x2 = bounds->x + bounds->width;
967    int	    y2 = bounds->y + bounds->height;
968
969    while (npoints--)
970    {
971	if (x1 <= points->x && points->x <= x2 &&
972	    y1 <= points->y && points->y <= y2)
973	    return True;
974	points++;
975    }
976    return False;
977}
978
979static void
980RenderUpdate (ClockWidget w)
981{
982    if (w->clock.buffer && w->clock.pixmap)
983    {
984	XCopyArea (XtDisplay (w), w->clock.pixmap,
985		   XtWindow (w), w->clock.EraseGC,
986		   w->clock.damage.x, w->clock.damage.y,
987		   w->clock.damage.width, w->clock.damage.height,
988		   w->clock.damage.x, w->clock.damage.y);
989    }
990}
991
992static void
993RenderResetBounds (XRectangle *bounds)
994{
995    bounds->x = 0;
996    bounds->y = 0;
997    bounds->width = 0;
998    bounds->height = 0;
999}
1000
1001static void
1002RenderLine (ClockWidget w, XDouble x1, XDouble y1, XDouble x2, XDouble y2,
1003	    XftColor *color,
1004	    Boolean draw)
1005{
1006    XPointDouble    poly[4];
1007    XDouble	    dx = (x2 - x1);
1008    XDouble	    dy = (y2 - y1);
1009    XDouble	    len = sqrt (dx*dx + dy*dy);
1010    XDouble	    ldx = (LINE_WIDTH/2.0) * dy / len;
1011    XDouble	    ldy = (LINE_WIDTH/2.0) * dx / len;
1012
1013    poly[0].x = XCoord (x1 + ldx, w);
1014    poly[0].y = YCoord (y1 - ldy, w);
1015
1016    poly[1].x = XCoord (x2 + ldx, w);
1017    poly[1].y = YCoord (y2 - ldy, w);
1018
1019    poly[2].x = XCoord (x2 - ldx, w);
1020    poly[2].y = YCoord (y2 + ldy, w);
1021
1022    poly[3].x = XCoord (x1 - ldx, w);
1023    poly[3].y = YCoord (y1 + ldy, w);
1024
1025    RenderUpdateBounds (poly, 4, &w->clock.damage);
1026    if (draw)
1027    {
1028	if (RenderCheckBounds (poly, 4, &w->clock.damage))
1029	{
1030	    RenderPrepare (w, color);
1031	    XRenderCompositeDoublePoly (XtDisplay (w),
1032					PictOpOver,
1033					w->clock.fill_picture,
1034					w->clock.picture,
1035					w->clock.mask_format,
1036					0, 0, 0, 0, poly, 4, EvenOddRule);
1037	}
1038    }
1039    else
1040	RenderUpdateBounds (poly, 4, &w->clock.damage);
1041}
1042
1043static void
1044RenderRotate (ClockWidget w, XPointDouble *out, double x, double y, double s, double c)
1045{
1046    out->x = XCoord (x * c - y * s, w);
1047    out->y = YCoord (y * c + x * s, w);
1048}
1049
1050static void
1051RenderHand (ClockWidget w, int tick_units, int size, XftColor *color,
1052	    Boolean draw)
1053{
1054    double	    c, s;
1055    XPointDouble    poly[3];
1056    double	    outer_x;
1057    double	    inner_y;
1058
1059    ClockAngle (tick_units, &c, &s);
1060    s = -s;
1061
1062    /* compute raw positions */
1063    outer_x = size / 100.0;
1064    inner_y = HAND_WIDTH_FRACT / 100.0;
1065
1066    /* rotate them into position */
1067    RenderRotate (w, &poly[0], outer_x, 0.0, s, c);
1068    RenderRotate (w, &poly[1], -inner_y, inner_y, s, c);
1069    RenderRotate (w, &poly[2], -inner_y, -inner_y, s, c);
1070
1071    if (draw)
1072    {
1073	if (RenderCheckBounds (poly, 3, &w->clock.damage))
1074	{
1075	    RenderPrepare (w, color);
1076	    XRenderCompositeDoublePoly (XtDisplay (w),
1077					PictOpOver,
1078					w->clock.fill_picture,
1079					w->clock.picture,
1080					w->clock.mask_format,
1081					0, 0, 0, 0, poly, 3, EvenOddRule);
1082	}
1083    }
1084    RenderUpdateBounds (poly, 3, &w->clock.damage);
1085}
1086
1087static void
1088RenderHands (ClockWidget w, struct tm *tm, Boolean draw)
1089{
1090    RenderHand (w, tm->tm_hour * 300 + tm->tm_min*5, HOUR_HAND_FRACT, &w->clock.hour_color, draw);
1091    RenderHand (w, tm->tm_min * 60 + tm->tm_sec, MINUTE_HAND_FRACT, &w->clock.min_color, draw);
1092}
1093
1094static void
1095RenderSec (ClockWidget w, struct tm *tm, Boolean draw)
1096{
1097    double	    c, s;
1098    XPointDouble    poly[10];
1099    double	    inner_x, middle_x, outer_x, far_x;
1100    double	    middle_y;
1101    double	    line_y;
1102
1103    ClockAngle (tm->tm_sec * 60, &c, &s);
1104
1105    s = -s;
1106
1107    /*
1108     * Compute raw positions
1109     */
1110    line_y = LINE_WIDTH;
1111    inner_x = (MINUTE_HAND_FRACT / 100.0);
1112    middle_x = ((SECOND_HAND_FRACT + MINUTE_HAND_FRACT) / 200.0);
1113    outer_x = (SECOND_HAND_FRACT / 100.0);
1114    far_x = (MINOR_TICK_FRACT / 100.0);
1115    middle_y = (SECOND_WIDTH_FRACT / 100.0);
1116
1117    /*
1118     * Rotate them into position
1119     */
1120    RenderRotate (w, &poly[0], -line_y, line_y, s, c);
1121    RenderRotate (w, &poly[1], inner_x, line_y, s, c);
1122    RenderRotate (w, &poly[2], middle_x, middle_y, s, c);
1123    RenderRotate (w, &poly[3], outer_x, line_y, s, c);
1124    RenderRotate (w, &poly[4], far_x, line_y, s, c);
1125    RenderRotate (w, &poly[5], far_x, -line_y, s, c);
1126    RenderRotate (w, &poly[6], outer_x, -line_y, s, c);
1127    RenderRotate (w, &poly[7], middle_x, -middle_y, s, c);
1128    RenderRotate (w, &poly[8], inner_x, -line_y, s, c);
1129    RenderRotate (w, &poly[9], -line_y, -line_y, s, c);
1130
1131    if (draw)
1132    {
1133	if (RenderCheckBounds (poly, 10, &w->clock.damage))
1134	{
1135	    RenderPrepare (w, &w->clock.sec_color);
1136	    XRenderCompositeDoublePoly (XtDisplay (w),
1137					PictOpOver,
1138					w->clock.fill_picture,
1139					w->clock.picture,
1140					w->clock.mask_format,
1141					0, 0, 0, 0, poly, 10, EvenOddRule);
1142	}
1143    }
1144    else
1145    {
1146	RenderUpdateBounds (poly, 10, &w->clock.damage);
1147    }
1148}
1149
1150#endif
1151
1152static void
1153Realize(Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs)
1154{
1155     ClockWidget	w = (ClockWidget) gw;
1156#ifdef notdef
1157     *valueMask |= CWBitGravity;
1158     attrs->bit_gravity = ForgetGravity;
1159#endif
1160     switch (w->clock.backing_store) {
1161     case Always:
1162     case NotUseful:
1163     case WhenMapped:
1164     	*valueMask |=CWBackingStore;
1165	attrs->backing_store = w->clock.backing_store;
1166	break;
1167     }
1168     (*clockWidgetClass->core_class.superclass->core_class.realize)
1169	 (gw, valueMask, attrs);
1170     Resize(gw);
1171}
1172
1173static void
1174Destroy(Widget gw)
1175{
1176     ClockWidget w = (ClockWidget) gw;
1177     if (w->clock.interval_id) XtRemoveTimeOut (w->clock.interval_id);
1178#ifdef XRENDER
1179    if (w->clock.picture)
1180	XRenderFreePicture (XtDisplay(w), w->clock.picture);
1181    if (w->clock.fill_picture)
1182	XRenderFreePicture (XtDisplay(w), w->clock.fill_picture);
1183#endif
1184     XtReleaseGC (gw, w->clock.myGC);
1185     XtReleaseGC (gw, w->clock.HighGC);
1186     XtReleaseGC (gw, w->clock.HandGC);
1187     XtReleaseGC (gw, w->clock.EraseGC);
1188}
1189
1190static void
1191Resize(Widget gw)
1192{
1193    ClockWidget w = (ClockWidget) gw;
1194    /* don't do this computation if window hasn't been realized yet. */
1195    if (XtIsRealized(gw) && w->clock.analog) {
1196	/* need signed value since Dimension is unsigned */
1197	int radius = ((int) min(w->core.width, w->core.height) - (int) (2 * w->clock.padding)) / 2;
1198        w->clock.radius = (Dimension) max (radius, 1);
1199
1200        w->clock.second_hand_length = (int)(SECOND_HAND_FRACT * w->clock.radius) / 100;
1201        w->clock.minute_hand_length = (int)(MINUTE_HAND_FRACT * w->clock.radius) / 100;
1202        w->clock.hour_hand_length = (int)(HOUR_HAND_FRACT * w->clock.radius) / 100;
1203        w->clock.hand_width = (int)(HAND_WIDTH_FRACT * w->clock.radius) / 100;
1204        w->clock.second_hand_width = (int)(SECOND_WIDTH_FRACT * w->clock.radius) / 100;
1205
1206        w->clock.centerX = w->core.width / 2;
1207        w->clock.centerY = w->core.height / 2;
1208    }
1209#ifdef XRENDER
1210    w->clock.x_scale = 0.45 * w->core.width;
1211    w->clock.y_scale = 0.45 * w->core.height;
1212    w->clock.x_off = 0.5 * w->core.width;
1213    w->clock.y_off = 0.5 * w->core.height;
1214    if (w->clock.pixmap)
1215    {
1216	XFreePixmap (XtDisplay (w), w->clock.pixmap);
1217	w->clock.pixmap = 0;
1218	if (w->clock.draw)
1219	{
1220	    XftDrawDestroy (w->clock.draw);
1221	    w->clock.draw = 0;
1222	}
1223	w->clock.picture = 0;
1224    }
1225#endif
1226}
1227
1228/* ARGSUSED */
1229static void
1230Redisplay(Widget gw, XEvent *event, Region region)
1231{
1232    ClockWidget w = (ClockWidget) gw;
1233    if (w->clock.analog) {
1234#ifdef XRENDER
1235	if (w->clock.render && w->clock.can_polygon)
1236	    XClipBox (region, &w->clock.damage);
1237	else
1238#endif
1239	{
1240	    if (w->clock.numseg != 0)
1241		erase_hands (w, (struct tm *) 0);
1242	    DrawClockFace(w);
1243	}
1244    } else {
1245#ifdef XRENDER
1246	if (w->clock.render)
1247	    XClipBox (region, &w->clock.damage);
1248#endif
1249	w->clock.prev_time_string[0] = '\0';
1250    }
1251    clock_tic((XtPointer)w, (XtIntervalId *)NULL);
1252}
1253
1254/* ARGSUSED */
1255static void
1256clock_tic(XtPointer client_data, XtIntervalId *id)
1257{
1258        ClockWidget w = (ClockWidget)client_data;
1259	struct tm tm;
1260	Time_t	time_value;
1261	struct timeval	tv;
1262	char	*time_ptr;
1263        register Display *dpy = XtDisplay(w);
1264        register Window win = XtWindow(w);
1265
1266	X_GETTIMEOFDAY (&tv);
1267	if (id || !w->clock.interval_id)
1268	    w->clock.interval_id =
1269		XtAppAddTimeOut( XtWidgetToApplicationContext( (Widget) w),
1270				(w->clock.update - 1) * 1000 + (1000000 - tv.tv_usec)/1000, clock_tic, (XtPointer)w );
1271	time_value = tv.tv_sec;
1272	tm = *localtime(&time_value);
1273	/*
1274	 * Beep on the half hour; double-beep on the hour.
1275	 */
1276	if (w->clock.chime == TRUE) {
1277	    if (w->clock.beeped && (tm.tm_min != 30) &&
1278		(tm.tm_min != 0))
1279	      w->clock.beeped = FALSE;
1280	    if (((tm.tm_min == 30) || (tm.tm_min == 0))
1281		&& (!w->clock.beeped)) {
1282		w->clock.beeped = TRUE;
1283#ifdef XKB
1284		if (tm.tm_min==0) {
1285		    XkbStdBell(dpy,win,50,XkbBI_ClockChimeHour);
1286		    XkbStdBell(dpy,win,50,XkbBI_RepeatingLastBell);
1287		}
1288		else {
1289		    XkbStdBell(dpy,win,50,XkbBI_ClockChimeHalf);
1290		}
1291#else
1292		XBell(dpy, 50);
1293		if (tm.tm_min == 0)
1294		  XBell(dpy, 50);
1295#endif
1296	    }
1297	}
1298	if( w->clock.analog == FALSE ) {
1299	    int	clear_from = w->core.width;
1300	    int i, len, prev_len;
1301
1302	    time_ptr = TimeString (w, &tm);
1303	    len = strlen (time_ptr);
1304	    if (time_ptr[len - 1] == '\n') time_ptr[--len] = '\0';
1305	    prev_len = strlen (w->clock.prev_time_string);
1306	    for (i = 0; ((i < len) && (i < prev_len) &&
1307	    		 (w->clock.prev_time_string[i] == time_ptr[i])); i++);
1308
1309#ifdef XRENDER
1310	    if (w->clock.render)
1311	    {
1312		XRectangle  old_tail, new_tail, head;
1313		int	    x, y;
1314#if !defined(NO_I18N) && defined(HAVE_ICONV)
1315		char *utf8_str;
1316#endif
1317
1318		RenderTextBounds (w, w->clock.prev_time_string, i, prev_len,
1319				  &old_tail, 0, 0);
1320		RenderUpdateRectBounds (&old_tail, &w->clock.damage);
1321		RenderTextBounds (w, time_ptr, i, len,
1322				  &new_tail, 0, 0);
1323		RenderUpdateRectBounds (&new_tail, &w->clock.damage);
1324
1325		while (i)
1326		{
1327		    RenderTextBounds (w, time_ptr, 0, i, &head, 0, 0);
1328		    if (!RenderRectIn (&head, &w->clock.damage))
1329			break;
1330		    i--;
1331		}
1332		RenderTextBounds (w, time_ptr, i, len, &new_tail, &x, &y);
1333		RenderClip (w);
1334		RenderPrepare (w, 0);
1335#ifndef NO_I18N
1336		if (w->clock.utf8) {
1337		    XftDrawStringUtf8 (w->clock.draw,
1338				    &w->clock.fg_color,
1339				    w->clock.face,
1340				    x, y,
1341				    (FcChar8 *) time_ptr + i, len - i);
1342
1343		}
1344# ifdef HAVE_ICONV
1345		else if ((utf8_str =
1346		    clock_to_utf8(time_ptr + i)) != NULL) {
1347		    	XftDrawStringUtf8 (w->clock.draw,
1348				    &w->clock.fg_color,
1349				    w->clock.face,
1350				    x, y,
1351				    (FcChar8 *)utf8_str, strlen(utf8_str) - i);
1352		    free(utf8_str);
1353		}
1354# endif
1355		else
1356#endif
1357		{
1358		    XftDrawString8 (w->clock.draw,
1359				    &w->clock.fg_color,
1360				    w->clock.face,
1361				    x, y,
1362				    (FcChar8 *) time_ptr + i, len - i);
1363		}
1364		RenderUpdate (w);
1365		RenderResetBounds (&w->clock.damage);
1366	    }
1367	    else
1368#endif
1369#ifndef NO_I18N
1370	    if(!no_locale) {
1371		if(0 < len) {
1372		    XFontSetExtents *fse
1373		      = XExtentsOfFontSet(w->clock.fontSet);
1374
1375		    XmbDrawImageString(dpy,win,w->clock.fontSet,w->clock.myGC,
1376				       (2+w->clock.padding +
1377					(i?XmbTextEscapement(w->clock.fontSet,
1378							     time_ptr,i):0)),
1379				       2+w->clock.padding+fse->max_logical_extent.height,
1380				       time_ptr+i,len-i
1381			);
1382		    /*
1383		     * Clear any left over bits
1384		     */
1385		    clear_from = XmbTextEscapement (w->clock.fontSet,time_ptr,
1386						    len) + 2+w->clock.padding;
1387		}
1388	    } else
1389#endif /* NO_I18N */
1390	    {
1391		XDrawImageString (dpy, win, w->clock.myGC,
1392				  (1+w->clock.padding +
1393				   XTextWidth (w->clock.font, time_ptr, i)),
1394				  w->clock.font->ascent+w->clock.padding,
1395				  time_ptr + i, len - i);
1396		/*
1397		 * Clear any left over bits
1398		 */
1399		clear_from = XTextWidth (w->clock.font, time_ptr, len)
1400		    	     + 2 + w->clock.padding;
1401	    }
1402	    if (clear_from < (int)w->core.width)
1403		XClearArea (dpy, win,
1404		    clear_from, 0, w->core.width - clear_from, w->core.height,
1405		    False);
1406#if defined(HAS_STRLCAT) || defined(HAVE_STRLCPY)
1407	    strlcpy (w->clock.prev_time_string+i, time_ptr+i,
1408		     sizeof(w->clock.prev_time_string)-i);
1409#else
1410	    strncpy (w->clock.prev_time_string+i, time_ptr+i,
1411		     sizeof(w->clock.prev_time_string)-i);
1412	    w->clock.prev_time_string[sizeof(w->clock.prev_time_string)-1] = 0;
1413#endif
1414	} else {
1415			/*
1416			 * The second (or minute) hand is sec (or min)
1417			 * sixtieths around the clock face. The hour hand is
1418			 * (hour + min/60) twelfths of the way around the
1419			 * clock-face.  The derivation is left as an excercise
1420			 * for the reader.
1421			 */
1422
1423			/*
1424			 * 12 hour clock.
1425			 */
1426			if(tm.tm_hour >= 12)
1427				tm.tm_hour -= 12;
1428
1429#ifdef XRENDER
1430			if (w->clock.render && w->clock.can_polygon)
1431			{
1432			    w->clock.mask_format = XRenderFindStandardFormat (XtDisplay (w),
1433									      w->clock.sharp ?
1434									      PictStandardA1 :
1435									      PictStandardA8);
1436			    /*
1437			     * Compute repaint area
1438			     */
1439			    if (tm.tm_min != w->clock.otm.tm_min ||
1440				tm.tm_hour != w->clock.otm.tm_hour ||
1441				tm.tm_sec != w->clock.otm.tm_sec)
1442			    {
1443				RenderHands (w, &w->clock.otm, False);
1444				RenderHands (w, &tm, False);
1445			    }
1446			    if (w->clock.show_second_hand &&
1447				tm.tm_sec != w->clock.otm.tm_sec)
1448			    {
1449				RenderSec (w, &w->clock.otm, False);
1450				RenderSec (w, &tm, False);
1451			    }
1452			    if (w->clock.damage.width &&
1453				w->clock.damage.height)
1454			    {
1455				RenderClip (w);
1456				DrawClockFace (w);
1457				RenderHands (w, &tm, True);
1458				if (w->clock.show_second_hand == TRUE)
1459				    RenderSec (w, &tm, True);
1460			    }
1461			    w->clock.otm = tm;
1462			    RenderUpdate (w);
1463			    RenderResetBounds (&w->clock.damage);
1464			    return;
1465			}
1466#endif
1467
1468			erase_hands (w, &tm);
1469
1470		    if (w->clock.numseg == 0 ||
1471			tm.tm_min != w->clock.otm.tm_min ||
1472			tm.tm_hour != w->clock.otm.tm_hour) {
1473			    w->clock.segbuffptr = w->clock.segbuff;
1474			    w->clock.numseg = 0;
1475			    /*
1476			     * Calculate the hour hand, fill it in with its
1477			     * color and then outline it.  Next, do the same
1478			     * with the minute hand.  This is a cheap hidden
1479			     * line algorithm.
1480			     */
1481			    DrawHand(w,
1482				w->clock.minute_hand_length, w->clock.hand_width,
1483				tm.tm_min * 60
1484			    );
1485			    if(w->clock.Hdpixel != w->core.background_pixel)
1486				XFillPolygon( dpy,
1487				    win, w->clock.HandGC,
1488				    w->clock.segbuff, VERTICES_IN_HANDS,
1489				    Convex, CoordModeOrigin
1490				);
1491			    XDrawLines( dpy,
1492				win, w->clock.HighGC,
1493				w->clock.segbuff, VERTICES_IN_HANDS,
1494				       CoordModeOrigin);
1495			    w->clock.hour = w->clock.segbuffptr;
1496			    DrawHand(w,
1497				w->clock.hour_hand_length, w->clock.hand_width,
1498				tm.tm_hour * 300 + tm.tm_min * 5
1499			    );
1500			    if(w->clock.Hdpixel != w->core.background_pixel) {
1501			      XFillPolygon(dpy,
1502					   win, w->clock.HandGC,
1503					   w->clock.hour,
1504					   VERTICES_IN_HANDS,
1505					   Convex, CoordModeOrigin
1506					   );
1507			    }
1508			    XDrawLines( dpy,
1509				       win, w->clock.HighGC,
1510				       w->clock.hour, VERTICES_IN_HANDS,
1511				       CoordModeOrigin );
1512
1513			    w->clock.sec = w->clock.segbuffptr;
1514		    }
1515		    if (w->clock.show_second_hand == TRUE) {
1516			    w->clock.segbuffptr = w->clock.sec;
1517			    DrawSecond(w,
1518				w->clock.second_hand_length - 2,
1519				w->clock.second_hand_width,
1520				w->clock.minute_hand_length + 2,
1521				tm.tm_sec * 60
1522			    );
1523			    if(w->clock.Hdpixel != w->core.background_pixel)
1524				XFillPolygon( dpy,
1525				    win, w->clock.HandGC,
1526				    w->clock.sec,
1527				    VERTICES_IN_HANDS -2,
1528				    Convex, CoordModeOrigin
1529			    );
1530			    XDrawLines( dpy,
1531				       win, w->clock.HighGC,
1532				       w->clock.sec,
1533				       VERTICES_IN_HANDS-1,
1534				       CoordModeOrigin
1535				        );
1536
1537			}
1538			w->clock.otm = tm;
1539		}
1540}
1541
1542static void
1543erase_hands(ClockWidget w, struct tm *tm)
1544{
1545    /*
1546     * Erase old hands.
1547     */
1548    if(w->clock.numseg > 0) {
1549	Display	*dpy;
1550	Window	win;
1551
1552	dpy = XtDisplay (w);
1553	win = XtWindow (w);
1554	if (w->clock.show_second_hand == TRUE) {
1555	    XDrawLines(dpy, win,
1556		w->clock.EraseGC,
1557		w->clock.sec,
1558		VERTICES_IN_HANDS-1,
1559		CoordModeOrigin);
1560	    if(w->clock.Hdpixel != w->core.background_pixel) {
1561		XFillPolygon(dpy,
1562			win, w->clock.EraseGC,
1563			w->clock.sec,
1564			VERTICES_IN_HANDS-2,
1565			Convex, CoordModeOrigin
1566			);
1567	    }
1568	}
1569	if(!tm || tm->tm_min != w->clock.otm.tm_min ||
1570		  tm->tm_hour != w->clock.otm.tm_hour)
1571 	{
1572	    XDrawLines( dpy, win,
1573			w->clock.EraseGC,
1574			w->clock.segbuff,
1575			VERTICES_IN_HANDS,
1576			CoordModeOrigin);
1577	    XDrawLines( dpy, win,
1578			w->clock.EraseGC,
1579			w->clock.hour,
1580			VERTICES_IN_HANDS,
1581			CoordModeOrigin);
1582	    if(w->clock.Hdpixel != w->core.background_pixel) {
1583		XFillPolygon( dpy, win,
1584 			      w->clock.EraseGC,
1585			      w->clock.segbuff, VERTICES_IN_HANDS,
1586			      Convex, CoordModeOrigin);
1587		XFillPolygon( dpy, win,
1588 			      w->clock.EraseGC,
1589			      w->clock.hour,
1590			      VERTICES_IN_HANDS,
1591			      Convex, CoordModeOrigin);
1592	    }
1593	}
1594    }
1595}
1596
1597static float const Sines[] = {
15980.000000, 0.001745, 0.003490, 0.005235, 0.006981, 0.008726, 0.010471, 0.012217,
15990.013962, 0.015707, 0.017452, 0.019197, 0.020942, 0.022687, 0.024432, 0.026176,
16000.027921, 0.029666, 0.031410, 0.033155, 0.034899, 0.036643, 0.038387, 0.040131,
16010.041875, 0.043619, 0.045362, 0.047106, 0.048849, 0.050592, 0.052335, 0.054078,
16020.055821, 0.057564, 0.059306, 0.061048, 0.062790, 0.064532, 0.066273, 0.068015,
16030.069756, 0.071497, 0.073238, 0.074978, 0.076719, 0.078459, 0.080198, 0.081938,
16040.083677, 0.085416, 0.087155, 0.088894, 0.090632, 0.092370, 0.094108, 0.095845,
16050.097582, 0.099319, 0.101056, 0.102792, 0.104528, 0.106264, 0.107999, 0.109734,
16060.111468, 0.113203, 0.114937, 0.116670, 0.118403, 0.120136, 0.121869, 0.123601,
16070.125333, 0.127064, 0.128795, 0.130526, 0.132256, 0.133986, 0.135715, 0.137444,
16080.139173, 0.140901, 0.142628, 0.144356, 0.146083, 0.147809, 0.149535, 0.151260,
16090.152985, 0.154710, 0.156434, 0.158158, 0.159881, 0.161603, 0.163325, 0.165047,
16100.166768, 0.168489, 0.170209, 0.171929, 0.173648, 0.175366, 0.177084, 0.178802,
16110.180519, 0.182235, 0.183951, 0.185666, 0.187381, 0.189095, 0.190808, 0.192521,
16120.194234, 0.195946, 0.197657, 0.199367, 0.201077, 0.202787, 0.204496, 0.206204,
16130.207911, 0.209618, 0.211324, 0.213030, 0.214735, 0.216439, 0.218143, 0.219846,
16140.221548, 0.223250, 0.224951, 0.226651, 0.228350, 0.230049, 0.231747, 0.233445,
16150.235142, 0.236838, 0.238533, 0.240228, 0.241921, 0.243615, 0.245307, 0.246999,
16160.248689, 0.250380, 0.252069, 0.253757, 0.255445, 0.257132, 0.258819, 0.260504,
16170.262189, 0.263873, 0.265556, 0.267238, 0.268919, 0.270600, 0.272280, 0.273959,
16180.275637, 0.277314, 0.278991, 0.280666, 0.282341, 0.284015, 0.285688, 0.287360,
16190.289031, 0.290702, 0.292371, 0.294040, 0.295708, 0.297374, 0.299040, 0.300705,
16200.302369, 0.304033, 0.305695, 0.307356, 0.309016, 0.310676, 0.312334, 0.313992,
16210.315649, 0.317304, 0.318959, 0.320612, 0.322265, 0.323917, 0.325568, 0.327217,
16220.328866, 0.330514, 0.332161, 0.333806, 0.335451, 0.337095, 0.338737, 0.340379,
16230.342020, 0.343659, 0.345298, 0.346935, 0.348572, 0.350207, 0.351841, 0.353474,
16240.355106, 0.356737, 0.358367, 0.359996, 0.361624, 0.363251, 0.364876, 0.366501,
16250.368124, 0.369746, 0.371367, 0.372987, 0.374606, 0.376224, 0.377840, 0.379456,
16260.381070, 0.382683, 0.384295, 0.385906, 0.387515, 0.389123, 0.390731, 0.392337,
16270.393941, 0.395545, 0.397147, 0.398749, 0.400349, 0.401947, 0.403545, 0.405141,
16280.406736, 0.408330, 0.409923, 0.411514, 0.413104, 0.414693, 0.416280, 0.417867,
16290.419452, 0.421035, 0.422618, 0.424199, 0.425779, 0.427357, 0.428935, 0.430511,
16300.432085, 0.433659, 0.435231, 0.436801, 0.438371, 0.439939, 0.441505, 0.443071,
16310.444635, 0.446197, 0.447759, 0.449318, 0.450877, 0.452434, 0.453990, 0.455544,
16320.457097, 0.458649, 0.460199, 0.461748, 0.463296, 0.464842, 0.466386, 0.467929,
16330.469471, 0.471011, 0.472550, 0.474088, 0.475624, 0.477158, 0.478691, 0.480223,
16340.481753, 0.483282, 0.484809, 0.486335, 0.487859, 0.489382, 0.490903, 0.492423,
16350.493941, 0.495458, 0.496973, 0.498487, 0.499999, 0.501510, 0.503019, 0.504527,
16360.506033, 0.507538, 0.509041, 0.510542, 0.512042, 0.513541, 0.515038, 0.516533,
16370.518027, 0.519519, 0.521009, 0.522498, 0.523985, 0.525471, 0.526955, 0.528438,
16380.529919, 0.531398, 0.532876, 0.534352, 0.535826, 0.537299, 0.538770, 0.540240,
16390.541708, 0.543174, 0.544639, 0.546101, 0.547563, 0.549022, 0.550480, 0.551936,
16400.553391, 0.554844, 0.556295, 0.557745, 0.559192, 0.560638, 0.562083, 0.563526,
16410.564967, 0.566406, 0.567843, 0.569279, 0.570713, 0.572145, 0.573576, 0.575005,
16420.576432, 0.577857, 0.579281, 0.580702, 0.582122, 0.583541, 0.584957, 0.586372,
16430.587785, 0.589196, 0.590605, 0.592013, 0.593418, 0.594822, 0.596224, 0.597625,
16440.599023, 0.600420, 0.601815, 0.603207, 0.604599, 0.605988, 0.607375, 0.608761,
16450.610145, 0.611527, 0.612907, 0.614285, 0.615661, 0.617035, 0.618408, 0.619779,
16460.621147, 0.622514, 0.623879, 0.625242, 0.626603, 0.627963, 0.629320, 0.630675,
16470.632029, 0.633380, 0.634730, 0.636078, 0.637423, 0.638767, 0.640109, 0.641449,
16480.642787, 0.644123, 0.645457, 0.646789, 0.648119, 0.649448, 0.650774, 0.652098,
16490.653420, 0.654740, 0.656059, 0.657375, 0.658689, 0.660001, 0.661311, 0.662620,
16500.663926, 0.665230, 0.666532, 0.667832, 0.669130, 0.670426, 0.671720, 0.673012,
16510.674302, 0.675590, 0.676875, 0.678159, 0.679441, 0.680720, 0.681998, 0.683273,
16520.684547, 0.685818, 0.687087, 0.688354, 0.689619, 0.690882, 0.692143, 0.693401,
16530.694658, 0.695912, 0.697165, 0.698415, 0.699663, 0.700909, 0.702153, 0.703394,
16540.704634, 0.705871, 0.707106,
1655};
1656static float const Cosines[] = {
16571.000000, 0.999998, 0.999993, 0.999986, 0.999975, 0.999961, 0.999945, 0.999925,
16580.999902, 0.999876, 0.999847, 0.999815, 0.999780, 0.999742, 0.999701, 0.999657,
16590.999610, 0.999559, 0.999506, 0.999450, 0.999390, 0.999328, 0.999262, 0.999194,
16600.999122, 0.999048, 0.998970, 0.998889, 0.998806, 0.998719, 0.998629, 0.998536,
16610.998440, 0.998341, 0.998239, 0.998134, 0.998026, 0.997915, 0.997801, 0.997684,
16620.997564, 0.997440, 0.997314, 0.997185, 0.997052, 0.996917, 0.996778, 0.996637,
16630.996492, 0.996345, 0.996194, 0.996041, 0.995884, 0.995724, 0.995561, 0.995396,
16640.995227, 0.995055, 0.994880, 0.994702, 0.994521, 0.994337, 0.994150, 0.993960,
16650.993767, 0.993571, 0.993372, 0.993170, 0.992965, 0.992757, 0.992546, 0.992331,
16660.992114, 0.991894, 0.991671, 0.991444, 0.991215, 0.990983, 0.990747, 0.990509,
16670.990268, 0.990023, 0.989776, 0.989525, 0.989272, 0.989015, 0.988756, 0.988493,
16680.988228, 0.987959, 0.987688, 0.987413, 0.987136, 0.986855, 0.986572, 0.986285,
16690.985996, 0.985703, 0.985407, 0.985109, 0.984807, 0.984503, 0.984195, 0.983885,
16700.983571, 0.983254, 0.982935, 0.982612, 0.982287, 0.981958, 0.981627, 0.981292,
16710.980955, 0.980614, 0.980271, 0.979924, 0.979575, 0.979222, 0.978867, 0.978508,
16720.978147, 0.977783, 0.977415, 0.977045, 0.976672, 0.976296, 0.975916, 0.975534,
16730.975149, 0.974761, 0.974370, 0.973975, 0.973578, 0.973178, 0.972775, 0.972369,
16740.971961, 0.971549, 0.971134, 0.970716, 0.970295, 0.969872, 0.969445, 0.969015,
16750.968583, 0.968147, 0.967709, 0.967267, 0.966823, 0.966376, 0.965925, 0.965472,
16760.965016, 0.964557, 0.964095, 0.963630, 0.963162, 0.962691, 0.962217, 0.961741,
16770.961261, 0.960779, 0.960293, 0.959805, 0.959313, 0.958819, 0.958322, 0.957822,
16780.957319, 0.956813, 0.956304, 0.955793, 0.955278, 0.954760, 0.954240, 0.953716,
16790.953190, 0.952661, 0.952129, 0.951594, 0.951056, 0.950515, 0.949972, 0.949425,
16800.948876, 0.948323, 0.947768, 0.947210, 0.946649, 0.946085, 0.945518, 0.944948,
16810.944376, 0.943800, 0.943222, 0.942641, 0.942057, 0.941470, 0.940880, 0.940288,
16820.939692, 0.939094, 0.938493, 0.937888, 0.937281, 0.936672, 0.936059, 0.935444,
16830.934825, 0.934204, 0.933580, 0.932953, 0.932323, 0.931691, 0.931055, 0.930417,
16840.929776, 0.929132, 0.928485, 0.927836, 0.927183, 0.926528, 0.925870, 0.925209,
16850.924546, 0.923879, 0.923210, 0.922538, 0.921863, 0.921185, 0.920504, 0.919821,
16860.919135, 0.918446, 0.917754, 0.917060, 0.916362, 0.915662, 0.914959, 0.914253,
16870.913545, 0.912834, 0.912120, 0.911403, 0.910683, 0.909961, 0.909236, 0.908508,
16880.907777, 0.907044, 0.906307, 0.905568, 0.904827, 0.904082, 0.903335, 0.902585,
16890.901832, 0.901077, 0.900318, 0.899557, 0.898794, 0.898027, 0.897258, 0.896486,
16900.895711, 0.894934, 0.894154, 0.893371, 0.892585, 0.891797, 0.891006, 0.890212,
16910.889416, 0.888617, 0.887815, 0.887010, 0.886203, 0.885393, 0.884580, 0.883765,
16920.882947, 0.882126, 0.881303, 0.880477, 0.879648, 0.878817, 0.877982, 0.877146,
16930.876306, 0.875464, 0.874619, 0.873772, 0.872922, 0.872069, 0.871213, 0.870355,
16940.869494, 0.868631, 0.867765, 0.866896, 0.866025, 0.865151, 0.864274, 0.863395,
16950.862513, 0.861629, 0.860742, 0.859852, 0.858959, 0.858064, 0.857167, 0.856267,
16960.855364, 0.854458, 0.853550, 0.852640, 0.851726, 0.850811, 0.849892, 0.848971,
16970.848048, 0.847121, 0.846193, 0.845261, 0.844327, 0.843391, 0.842452, 0.841510,
16980.840566, 0.839619, 0.838670, 0.837718, 0.836764, 0.835807, 0.834847, 0.833885,
16990.832921, 0.831954, 0.830984, 0.830012, 0.829037, 0.828060, 0.827080, 0.826098,
17000.825113, 0.824126, 0.823136, 0.822144, 0.821149, 0.820151, 0.819152, 0.818149,
17010.817144, 0.816137, 0.815127, 0.814115, 0.813100, 0.812083, 0.811063, 0.810041,
17020.809016, 0.807989, 0.806960, 0.805928, 0.804893, 0.803856, 0.802817, 0.801775,
17030.800731, 0.799684, 0.798635, 0.797583, 0.796529, 0.795473, 0.794414, 0.793353,
17040.792289, 0.791223, 0.790155, 0.789084, 0.788010, 0.786935, 0.785856, 0.784776,
17050.783693, 0.782608, 0.781520, 0.780430, 0.779337, 0.778243, 0.777145, 0.776046,
17060.774944, 0.773840, 0.772733, 0.771624, 0.770513, 0.769399, 0.768283, 0.767165,
17070.766044, 0.764921, 0.763796, 0.762668, 0.761538, 0.760405, 0.759271, 0.758134,
17080.756995, 0.755853, 0.754709, 0.753563, 0.752414, 0.751264, 0.750111, 0.748955,
17090.747798, 0.746638, 0.745475, 0.744311, 0.743144, 0.741975, 0.740804, 0.739631,
17100.738455, 0.737277, 0.736097, 0.734914, 0.733729, 0.732542, 0.731353, 0.730162,
17110.728968, 0.727772, 0.726574, 0.725374, 0.724171, 0.722967, 0.721760, 0.720551,
17120.719339, 0.718126, 0.716910, 0.715692, 0.714472, 0.713250, 0.712026, 0.710799,
17130.709570, 0.708339, 0.707106,
1714};
1715
1716static void
1717ClockAngle(int tick_units, double *sinp, double *cosp)
1718{
1719    int reduced, upper;
1720
1721    reduced = tick_units % 450;
1722    upper = tick_units / 450;
1723    if (upper & 1)
1724	reduced = 450 - reduced;
1725    if ((upper + 1) & 2) {
1726	*sinp = Cosines[reduced];
1727	*cosp = Sines[reduced];
1728    } else {
1729	*sinp = Sines[reduced];
1730	*cosp = Cosines[reduced];
1731    }
1732    if (upper >= 2 && upper < 6)
1733	*cosp = -*cosp;
1734    if (upper >= 4)
1735	*sinp = -*sinp;
1736}
1737
1738/*
1739 * DrawLine - Draws a line.
1740 *
1741 * blank_length is the distance from the center which the line begins.
1742 * length is the maximum length of the hand.
1743 * Tick_units is a number between zero and 12*60 indicating
1744 * how far around the circle (clockwise) from high noon.
1745 *
1746 * The blank_length feature is because I wanted to draw tick-marks around the
1747 * circle (for seconds).  The obvious means of drawing lines from the center
1748 * to the perimeter, then erasing all but the outside most pixels doesn't
1749 * work because of round-off error (sigh).
1750 */
1751static void
1752DrawLine(ClockWidget w, Dimension blank_length, Dimension length,
1753	 int tick_units)
1754{
1755	double dblank_length = (double)blank_length, dlength = (double)length;
1756	double cosangle, sinangle;
1757	int cx = w->clock.centerX, cy = w->clock.centerY, x1, y1, x2, y2;
1758
1759	/*
1760	 *  Angles are measured from 12 o'clock, clockwise increasing.
1761	 *  Since in X, +x is to the right and +y is downward:
1762	 *
1763	 *	x = x0 + r * sin(theta)
1764	 *	y = y0 - r * cos(theta)
1765	 *
1766	 */
1767	ClockAngle(tick_units, &sinangle, &cosangle);
1768
1769	/* break this out so that stupid compilers can cope */
1770	x1 = cx + (int)(dblank_length * sinangle);
1771	y1 = cy - (int)(dblank_length * cosangle);
1772	x2 = cx + (int)(dlength * sinangle);
1773	y2 = cy - (int)(dlength * cosangle);
1774	SetSeg(w, x1, y1, x2, y2);
1775}
1776
1777/*
1778 * DrawHand - Draws a hand.
1779 *
1780 * length is the maximum length of the hand.
1781 * width is the half-width of the hand.
1782 * Tick_units is a number between zero and 12*60 indicating
1783 * how far around the circle (clockwise) from high noon.
1784 *
1785 */
1786static void
1787DrawHand(ClockWidget w, Dimension length, Dimension width, int tick_units)
1788{
1789
1790	double cosangle, sinangle;
1791	register double ws, wc;
1792	Position x, y, x1, y1, x2, y2;
1793
1794	/*
1795	 *  Angles are measured from 12 o'clock, clockwise increasing.
1796	 *  Since in X, +x is to the right and +y is downward:
1797	 *
1798	 *	x = x0 + r * sin(theta)
1799	 *	y = y0 - r * cos(theta)
1800	 *
1801	 */
1802	ClockAngle(tick_units, &sinangle, &cosangle);
1803	/*
1804	 * Order of points when drawing the hand.
1805	 *
1806	 *		1,4
1807	 *		/ \
1808	 *	       /   \
1809	 *	      /     \
1810	 *	    2 ------- 3
1811	 */
1812	wc = width * cosangle;
1813	ws = width * sinangle;
1814	SetSeg(w,
1815	       x = w->clock.centerX + clock_round(length * sinangle),
1816	       y = w->clock.centerY - clock_round(length * cosangle),
1817	       x1 = w->clock.centerX - clock_round(ws + wc),
1818	       y1 = w->clock.centerY + clock_round(wc - ws));  /* 1 ---- 2 */
1819	/* 2 */
1820	SetSeg(w, x1, y1,
1821	       x2 = w->clock.centerX - clock_round(ws - wc),
1822	       y2 = w->clock.centerY + clock_round(wc + ws));  /* 2 ----- 3 */
1823
1824	SetSeg(w, x2, y2, x, y);	/* 3 ----- 1(4) */
1825}
1826
1827/*
1828 * DrawSecond - Draws the second hand (diamond).
1829 *
1830 * length is the maximum length of the hand.
1831 * width is the half-width of the hand.
1832 * offset is direct distance from center to tail end.
1833 * Tick_units is a number between zero and 12*60 indicating
1834 * how far around the circle (clockwise) from high noon.
1835 *
1836 */
1837static void
1838DrawSecond(ClockWidget w, Dimension length, Dimension width,
1839	   Dimension offset, int tick_units)
1840{
1841
1842	double cosangle, sinangle;
1843	register double ms, mc, ws, wc;
1844	register int mid;
1845	Position x, y;
1846
1847	/*
1848	 *  Angles are measured from 12 o'clock, clockwise increasing.
1849	 *  Since in X, +x is to the right and +y is downward:
1850	 *
1851	 *	x = x0 + r * sin(theta)
1852	 *	y = y0 - r * cos(theta)
1853	 *
1854	 */
1855	ClockAngle(tick_units, &sinangle, &cosangle);
1856	/*
1857	 * Order of points when drawing the hand.
1858	 *
1859	 *		1,5
1860	 *		/ \
1861	 *	       /   \
1862	 *	      /     \
1863	 *	    2<       >4
1864	 *	      \     /
1865	 *	       \   /
1866	 *		\ /
1867	 *	-	 3
1868	 *	|
1869	 *	|
1870	 *   offset
1871	 *	|
1872	 *	|
1873	 *	-	 + center
1874	 */
1875
1876	mid = (int) (length + offset) / 2;
1877	mc = mid * cosangle;
1878	ms = mid * sinangle;
1879	wc = width * cosangle;
1880	ws = width * sinangle;
1881	/*1 ---- 2 */
1882	SetSeg(w,
1883	       x = w->clock.centerX + clock_round(length * sinangle),
1884	       y = w->clock.centerY - clock_round(length * cosangle),
1885	       w->clock.centerX + clock_round(ms - wc),
1886	       w->clock.centerY - clock_round(mc + ws) );
1887	SetSeg(w, w->clock.centerX + clock_round(offset *sinangle),
1888	       w->clock.centerY - clock_round(offset * cosangle), /* 2-----3 */
1889	       w->clock.centerX + clock_round(ms + wc),
1890	       w->clock.centerY - clock_round(mc - ws));
1891	w->clock.segbuffptr->x = x;
1892	w->clock.segbuffptr++->y = y;
1893	w->clock.numseg ++;
1894}
1895
1896static void
1897SetSeg(ClockWidget w, int x1, int y1, int x2, int y2)
1898{
1899	w->clock.segbuffptr->x = x1;
1900	w->clock.segbuffptr++->y = y1;
1901	w->clock.segbuffptr->x = x2;
1902	w->clock.segbuffptr++->y = y2;
1903	w->clock.numseg += 2;
1904}
1905
1906/*
1907 *  Draw the clock face (every fifth tick-mark is longer
1908 *  than the others).
1909 */
1910static void
1911DrawClockFace(ClockWidget w)
1912{
1913	register int i;
1914	register int delta = (int)(w->clock.radius - w->clock.second_hand_length) / 3;
1915
1916	w->clock.segbuffptr = w->clock.segbuff;
1917	w->clock.numseg = 0;
1918	for (i = 0; i < 60; i++)
1919	{
1920#ifdef XRENDER
1921	    if (w->clock.render && w->clock.can_polygon)
1922	    {
1923		double	s, c;
1924		XDouble	x1, y1, x2, y2;
1925		XftColor	*color;
1926		ClockAngle (i * 60, &s, &c);
1927		x1 = c;
1928		y1 = s;
1929		if (i % 5)
1930		{
1931		    x2 = c * (MINOR_TICK_FRACT / 100.0);
1932		    y2 = s * (MINOR_TICK_FRACT / 100.0);
1933		    color = &w->clock.minor_color;
1934		}
1935		else
1936		{
1937		    x2 = c * (SECOND_HAND_FRACT / 100.0);
1938		    y2 = s * (SECOND_HAND_FRACT / 100.0);
1939		    color = &w->clock.major_color;
1940		}
1941		RenderLine (w, x1, y1, x2, y2, color, True);
1942	    }
1943	    else
1944#endif
1945	    {
1946		DrawLine(w, ( (i % 5) == 0 ?
1947			     w->clock.second_hand_length :
1948			     (w->clock.radius - delta) ),
1949			 w->clock.radius, i * 60);
1950	    }
1951	}
1952#ifdef XRENDER
1953	if (w->clock.render && w->clock.can_polygon)
1954	    return;
1955#endif
1956	/*
1957	 * Go ahead and draw it.
1958	 */
1959	XDrawSegments(XtDisplay(w), XtWindow(w),
1960		      w->clock.myGC, (XSegment *) &(w->clock.segbuff[0]),
1961		      w->clock.numseg/2);
1962
1963	w->clock.segbuffptr = w->clock.segbuff;
1964	w->clock.numseg = 0;
1965}
1966
1967static int
1968clock_round(double x)
1969{
1970	return(x >= 0.0 ? (int)(x + .5) : (int)(x - .5));
1971}
1972
1973#ifdef XRENDER
1974static Boolean
1975sameColor (XftColor *old, XftColor *new)
1976{
1977    if (old->color.red != new->color.red) return False;
1978    if (old->color.green != new->color.green) return False;
1979    if (old->color.blue != new->color.blue) return False;
1980    if (old->color.alpha != new->color.alpha) return False;
1981    return True;
1982}
1983#endif
1984
1985/* ARGSUSED */
1986static Boolean
1987SetValues(Widget gcurrent, Widget grequest, Widget gnew,
1988	  ArgList args, Cardinal *num_args)
1989{
1990      ClockWidget current = (ClockWidget) gcurrent;
1991      ClockWidget new = (ClockWidget) gnew;
1992      Boolean redisplay = FALSE;
1993      XtGCMask valuemask;
1994      XGCValues	myXGCV;
1995
1996      /* first check for changes to clock-specific resources.  We'll accept all
1997         the changes, but may need to do some computations first. */
1998
1999      if (new->clock.update != current->clock.update) {
2000	  if (current->clock.interval_id)
2001	      XtRemoveTimeOut (current->clock.interval_id);
2002	  if (XtIsRealized( (Widget) new))
2003	      new->clock.interval_id = XtAppAddTimeOut(
2004                                         XtWidgetToApplicationContext(gnew),
2005					 new->clock.update*1000,
2006				         clock_tic, (XtPointer)gnew);
2007
2008	  new->clock.show_second_hand =(new->clock.update <= SECOND_HAND_TIME);
2009	  if (new->clock.show_second_hand != current->clock.show_second_hand)
2010	    redisplay = TRUE;
2011      }
2012
2013      if (new->clock.padding != current->clock.padding)
2014	   redisplay = TRUE;
2015
2016      if (new->clock.analog != current->clock.analog)
2017	   redisplay = TRUE;
2018
2019       if (new->clock.font != current->clock.font)
2020	   redisplay = TRUE;
2021
2022#ifndef NO_I18N
2023       if (new->clock.fontSet != current->clock.fontSet)
2024	   redisplay = TRUE;
2025#endif
2026
2027      if ((ClockFgPixel(new) != ClockFgPixel (current))
2028          || (new->core.background_pixel != current->core.background_pixel)) {
2029          valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
2030	  myXGCV.foreground = ClockFgPixel (new);
2031	  myXGCV.background = new->core.background_pixel;
2032          myXGCV.font = new->clock.font->fid;
2033	  myXGCV.line_width = 0;
2034	  XtReleaseGC (gcurrent, current->clock.myGC);
2035	  new->clock.myGC = XtGetGC(gcurrent, valuemask, &myXGCV);
2036	  redisplay = TRUE;
2037          }
2038
2039      if (new->clock.Hipixel != current->clock.Hipixel) {
2040          valuemask = GCForeground | GCLineWidth;
2041	  myXGCV.foreground = new->clock.Hipixel;
2042          myXGCV.font = new->clock.font->fid;
2043	  myXGCV.line_width = 0;
2044	  XtReleaseGC (gcurrent, current->clock.HighGC);
2045	  new->clock.HighGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
2046	  redisplay = TRUE;
2047          }
2048
2049      if (new->clock.Hdpixel != current->clock.Hdpixel) {
2050          valuemask = GCForeground;
2051	  myXGCV.foreground = new->clock.Hdpixel;
2052	  XtReleaseGC (gcurrent, current->clock.HandGC);
2053	  new->clock.HandGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
2054	  redisplay = TRUE;
2055          }
2056
2057      if (new->core.background_pixel != current->core.background_pixel) {
2058          valuemask = GCForeground | GCLineWidth | GCGraphicsExposures;
2059	  myXGCV.foreground = new->core.background_pixel;
2060	  myXGCV.line_width = 0;
2061	  myXGCV.graphics_exposures = False;
2062	  XtReleaseGC (gcurrent, current->clock.EraseGC);
2063	  new->clock.EraseGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
2064	  redisplay = TRUE;
2065	  }
2066#ifdef XRENDER
2067     if (new->clock.face != current->clock.face)
2068	redisplay = TRUE;
2069     if (!sameColor (&new->clock.hour_color, &current->clock.fg_color) ||
2070	 !sameColor (&new->clock.hour_color, &current->clock.hour_color) ||
2071	 !sameColor (&new->clock.min_color, &current->clock.min_color) ||
2072	 !sameColor (&new->clock.sec_color, &current->clock.sec_color) ||
2073	 !sameColor (&new->clock.major_color, &current->clock.major_color) ||
2074	 !sameColor (&new->clock.minor_color, &current->clock.minor_color))
2075	redisplay = True;
2076    if (new->clock.sharp != current->clock.sharp)
2077	redisplay = True;
2078    if (new->clock.render != current->clock.render)
2079	redisplay = True;
2080    if (new->clock.buffer != current->clock.buffer)
2081    {
2082	if (new->clock.pixmap)
2083	{
2084	    XFreePixmap (XtDisplay (new), new->clock.pixmap);
2085	    new->clock.pixmap = 0;
2086	}
2087	if (new->clock.draw)
2088	{
2089	    XftDrawDestroy (new->clock.draw);
2090	    new->clock.draw = 0;
2091	}
2092	new->clock.picture = 0;
2093    }
2094#endif
2095     return (redisplay);
2096
2097}
2098
2099#if !defined(NO_I18N) && defined(HAVE_ICONV)
2100static char *
2101clock_to_utf8(const char *str)
2102{
2103    iconv_t cd;
2104    char *buf;
2105    size_t in_len;
2106    size_t buf_size;
2107    size_t ileft, oleft;
2108    const char *inptr;
2109    char *outptr;
2110    size_t ret;
2111    const char *code_set = nl_langinfo(CODESET);
2112
2113    if (str == NULL ||code_set == NULL || strcasecmp(code_set, "646") == 0)
2114    	return NULL;
2115
2116    if (strcasecmp(code_set, "UTF-8") == 0)
2117    	return strdup(str);
2118
2119    cd = iconv_open("UTF-8", code_set);
2120    if (cd == (iconv_t)-1)
2121    	return NULL;
2122
2123    in_len = strlen(str);
2124    buf_size = MB_LEN_MAX * (in_len + 1);
2125    if ((buf = malloc(buf_size)) == NULL) {
2126    	(void) iconv_close(cd);
2127    	return NULL;
2128    }
2129
2130    inptr = str;
2131    ileft = in_len;
2132    outptr = buf;
2133    oleft = buf_size;
2134
2135    ret = iconv(cd, &inptr, &ileft, &outptr, &oleft);
2136    if (ret == (size_t)(-1) || oleft == 0 ) {
2137        free(buf);
2138        buf = NULL;
2139    } else
2140	*outptr = '\0';
2141
2142    (void) iconv_close(cd);
2143    return buf;
2144}
2145#endif
2146