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