Clock.c revision ac5d998a
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
28Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
29
30                        All Rights Reserved
31
32Permission to use, copy, modify, and distribute this software and its
33documentation for any purpose and without fee is hereby granted,
34provided that the above copyright notice appear in all copies and that
35both that copyright notice and this permission notice appear in
36supporting documentation, and that the name of Digital not be
37used in advertising or publicity pertaining to distribution of the
38software without specific, written prior permission.
39
40DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
41ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
42DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
43ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
44WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
45ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46SOFTWARE.
47
48******************************************************************/
49/*
50 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
51 *
52 * Permission is hereby granted, free of charge, to any person obtaining a
53 * copy of this software and associated documentation files (the "Software"),
54 * to deal in the Software without restriction, including without limitation
55 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
56 * and/or sell copies of the Software, and to permit persons to whom the
57 * Software is furnished to do so, subject to the following conditions:
58 *
59 * The above copyright notice and this permission notice (including the next
60 * paragraph) shall be included in all copies or substantial portions of the
61 * Software.
62 *
63 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
66 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
67 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
68 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
69 * DEALINGS IN THE SOFTWARE.
70 */
71/*
72 * Authors:  I18N - Steve Swales - March 2000
73 *	     bgpixmap - Alan Coopersmith (as part of STSF project) - Sept. 2001
74 */
75/* $XFree86: xc/programs/xclock/Clock.c,v 3.25 2003/07/04 16:24:30 eich Exp $ */
76
77#ifdef HAVE_CONFIG_H
78#include "config.h"
79#endif
80
81#define _GNU_SOURCE
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#include <math.h>
91#if !defined(NO_I18N) && defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
92#include <iconv.h>
93#include <langinfo.h>
94#include <errno.h>
95#include <limits.h>
96#endif
97
98#if defined(XawVersion) && (XawVersion >= 7000002L)
99#define USE_XAW_PIXMAP_CVT
100#else
101#include <X11/xpm.h>
102#endif
103
104#include <time.h>
105#define Time_t time_t
106
107#ifdef XKB
108#include <X11/extensions/XKBbells.h>
109#endif
110
111#ifndef NO_I18N
112#include <stdlib.h>             /* for getenv() */
113#include <locale.h>
114extern Boolean no_locale;       /* if True, use old (unlocalized) behaviour */
115#endif
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/* Initialization of defaults */
138
139#define offset(field) XtOffsetOf(ClockRec, clock.field)
140#define goffset(field) XtOffsetOf(WidgetRec, core.field)
141
142static XtResource resources[] = {
143    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
144     goffset(width), XtRImmediate, (XtPointer) 0},
145    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
146     goffset(height), XtRImmediate, (XtPointer) 0},
147    {XtNupdate, XtCInterval, XtRFloat, sizeof(float),
148     offset(update), XtRString, "60.0"},
149#ifndef XRENDER
150    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
151     offset(fgpixel), XtRString, XtDefaultForeground},
152#endif
153    {XtNhand, XtCForeground, XtRPixel, sizeof(Pixel),
154     offset(Hdpixel), XtRString, XtDefaultForeground},
155    {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
156     offset(Hipixel), XtRString, XtDefaultForeground},
157    {XtNutime, XtCBoolean, XtRBoolean, sizeof(Boolean),
158     offset(utime), XtRImmediate, (XtPointer) FALSE},
159    {XtNanalog, XtCBoolean, XtRBoolean, sizeof(Boolean),
160     offset(analog), XtRImmediate, (XtPointer) TRUE},
161    {XtNtwentyfour, XtCBoolean, XtRBoolean, sizeof(Boolean),
162     offset(twentyfour), XtRImmediate, (XtPointer) TRUE},
163    {XtNbrief, XtCBoolean, XtRBoolean, sizeof(Boolean),
164     offset(brief), XtRImmediate, (XtPointer) FALSE},
165    {XtNstrftime, XtCString, XtRString, sizeof(String),
166     offset(strftime), XtRString, ""},
167    {XtNchime, XtCBoolean, XtRBoolean, sizeof(Boolean),
168     offset(chime), XtRImmediate, (XtPointer) FALSE},
169    {XtNpadding, XtCMargin, XtRInt, sizeof(int),
170     offset(padding), XtRImmediate, (XtPointer) 8},
171    {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
172     offset(font), XtRString, XtDefaultFont},
173#ifndef NO_I18N
174    {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
175     offset(fontSet), XtRString, XtDefaultFontSet},
176#endif
177    {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof(int),
178     offset(backing_store), XtRString, "default"},
179#ifdef XRENDER
180    {XtNrender, XtCBoolean, XtRBoolean, sizeof(Boolean),
181     offset(render), XtRImmediate, (XtPointer) TRUE},
182    {XtNbuffer, XtCBoolean, XtRBoolean, sizeof(Boolean),
183     offset(buffer), XtRImmediate, (XtPointer) TRUE},
184    {XtNsharp, XtCBoolean, XtRBoolean, sizeof(Boolean),
185     offset(sharp), XtRImmediate, (XtPointer) FALSE},
186    {XtNforeground, XtCForeground, XtRXftColor, sizeof(XftColor),
187     offset(fg_color), XtRString, XtDefaultForeground},
188    {XtNhourColor, XtCForeground, XtRXftColor, sizeof(XftColor),
189     offset(hour_color), XtRString, XtDefaultForeground},
190    {XtNminuteColor, XtCForeground, XtRXftColor, sizeof(XftColor),
191     offset(min_color), XtRString, XtDefaultForeground},
192    {XtNsecondColor, XtCForeground, XtRXftColor, sizeof(XftColor),
193     offset(sec_color), XtRString, XtDefaultForeground},
194    {XtNmajorColor, XtCForeground, XtRXftColor, sizeof(XftColor),
195     offset(major_color), XtRString, XtDefaultForeground},
196    {XtNminorColor, XtCForeground, XtRXftColor, sizeof(XftColor),
197     offset(minor_color), XtRString, XtDefaultForeground},
198    {XtNface, XtCFace, XtRXftFont, sizeof(XftFont *),
199     offset(face), XtRString, ""},
200#endif
201};
202
203#undef offset
204#undef goffset
205
206static void ClassInitialize(void);
207static void Initialize(Widget request, Widget new, ArgList args,
208                       Cardinal * num_args);
209static void Realize(Widget gw, XtValueMask * valueMask,
210                    XSetWindowAttributes * attrs);
211static void Destroy(Widget gw);
212static void Resize(Widget gw);
213static void Redisplay(Widget gw, XEvent * event, Region region);
214static void clock_tic(XtPointer client_data, XtIntervalId * id);
215static void erase_hands(ClockWidget w, struct tm *tm);
216static void ClockAngle(double tick_units, double *sinp, double *cosp);
217static void DrawLine(ClockWidget w, Dimension blank_length,
218                     Dimension length, int tick_units);
219static void DrawHand(ClockWidget w, Dimension length, Dimension width,
220                     int tick_units);
221static void DrawSecond(ClockWidget w, Dimension length, Dimension width,
222                       Dimension offset, int tick_units);
223static void SetSeg(ClockWidget w, int x1, int y1, int x2, int y2);
224static void DrawClockFace(ClockWidget w);
225static int clock_round(double x);
226static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew,
227                         ArgList args, Cardinal * num_args);
228#if !defined(NO_I18N) && defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
229static char *clock_to_utf8(const char *str, int in_len);
230#endif
231
232ClockClassRec clockClassRec = {
233    {                           /* core fields */
234     /* superclass               */ (WidgetClass) & simpleClassRec,
235     /* class_name               */ "Clock",
236     /* widget_size              */ sizeof(ClockRec),
237     /* class_initialize         */ ClassInitialize,
238     /* class_part_initialize    */ NULL,
239     /* class_inited             */ FALSE,
240     /* initialize               */ Initialize,
241     /* initialize_hook          */ NULL,
242     /* realize                  */ Realize,
243     /* actions                  */ NULL,
244     /* num_actions              */ 0,
245     /* resources                */ resources,
246     /* resource_count           */ XtNumber(resources),
247     /* xrm_class                */ NULLQUARK,
248     /* compress_motion          */ TRUE,
249     /* compress_exposure        */ XtExposeCompressMaximal,
250     /* compress_enterleave      */ TRUE,
251     /* visible_interest         */ FALSE,
252     /* destroy                  */ Destroy,
253     /* resize                   */ Resize,
254     /* expose                   */ Redisplay,
255     /* set_values               */ SetValues,
256     /* set_values_hook          */ NULL,
257     /* set_values_almost        */ XtInheritSetValuesAlmost,
258     /* get_values_hook          */ NULL,
259     /* accept_focus             */ NULL,
260     /* version                  */ XtVersion,
261     /* callback_private         */ NULL,
262     /* tm_table                 */ NULL,
263     /* query_geometry           */ XtInheritQueryGeometry,
264     /* display_accelerator      */ XtInheritDisplayAccelerator,
265     /* extension                */ NULL
266     },
267    {                           /* simple fields */
268     /* change_sensitive         */ XtInheritChangeSensitive,
269     /* extension                */ NULL
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
285CvtStringToPixmap(XrmValue * args,
286                  Cardinal * num_args, XrmValuePtr fromVal, XrmValuePtr toVal)
287{
288    static Pixmap pmap;
289    Pixmap shapemask;
290    char *name = (char *) fromVal->addr;
291    Screen *screen;
292    Display *dpy;
293
294    if (*num_args != 1)
295        XtErrorMsg("wrongParameters", "cvtStringToPixmap", "XtToolkitError",
296                   "String to pixmap conversion needs screen argument",
297                   (String *) NULL, (Cardinal *) NULL);
298
299    if (strcmp(name, "None") == 0) {
300        pmap = None;
301    }
302    else {
303        screen = *((Screen **) args[0].addr);
304        dpy = DisplayOfScreen(screen);
305
306        XpmReadFileToPixmap(dpy, RootWindowOfScreen(screen), name, &pmap,
307                            &shapemask, NULL);
308    }
309
310    (*toVal).size = sizeof(Pixmap);
311    (*toVal).addr = (XPointer) & pmap;
312}
313#endif
314
315#ifdef XRENDER
316static XtConvertArgRec xftColorConvertArgs[] = {
317    {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.screen),
318     sizeof(Screen *)},
319    {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.colormap),
320     sizeof(Colormap)}
321};
322
323#define	donestr(type, value, tstr) \
324	{							\
325	    if (toVal->addr != NULL) {				\
326		if (toVal->size < sizeof(type)) {		\
327		    toVal->size = sizeof(type);			\
328		    XtDisplayStringConversionWarning(dpy, 	\
329			(char*) fromVal->addr, tstr);		\
330		    return False;				\
331		}						\
332		*(type*)(toVal->addr) = (value);		\
333	    }							\
334	    else {						\
335		static type static_val;				\
336		static_val = (value);				\
337		toVal->addr = (XPointer)&static_val;		\
338	    }							\
339	    toVal->size = sizeof(type);				\
340	    return True;					\
341	}
342
343static void
344XmuFreeXftColor(XtAppContext app, XrmValuePtr toVal, XtPointer closure,
345                XrmValuePtr args, Cardinal * num_args)
346{
347    Screen *screen;
348    Colormap colormap;
349    XftColor *color;
350
351    if (*num_args != 2) {
352        XtAppErrorMsg(app,
353                      "freeXftColor", "wrongParameters",
354                      "XtToolkitError",
355                      "Freeing an XftColor requires screen and colormap arguments",
356                      (String *) NULL, (Cardinal *) NULL);
357        return;
358    }
359
360    screen = *((Screen **) args[0].addr);
361    colormap = *((Colormap *) args[1].addr);
362    color = (XftColor *) toVal->addr;
363    XftColorFree(DisplayOfScreen(screen),
364                 DefaultVisual(DisplayOfScreen(screen),
365                               XScreenNumberOfScreen(screen)), colormap, color);
366}
367
368static Boolean
369XmuCvtStringToXftColor(Display * dpy,
370                       XrmValue * args, Cardinal * num_args,
371                       XrmValue * fromVal, XrmValue * toVal,
372                       XtPointer * converter_data)
373{
374    char *spec;
375    XRenderColor renderColor;
376    XftColor xftColor;
377    Screen *screen;
378    Colormap colormap;
379
380    if (*num_args != 2) {
381        XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
382                      "cvtStringToXftColor", "wrongParameters",
383                      "XtToolkitError",
384                      "String to render color conversion needs screen and colormap arguments",
385                      (String *) NULL, (Cardinal *) NULL);
386        return False;
387    }
388
389    screen = *((Screen **) args[0].addr);
390    colormap = *((Colormap *) args[1].addr);
391
392    spec = (char *) fromVal->addr;
393    if (strcasecmp(spec, XtDefaultForeground) == 0) {
394        renderColor.red = 0;
395        renderColor.green = 0;
396        renderColor.blue = 0;
397        renderColor.alpha = 0xffff;
398    }
399    else if (strcasecmp(spec, XtDefaultBackground) == 0) {
400        renderColor.red = 0xffff;
401        renderColor.green = 0xffff;
402        renderColor.blue = 0xffff;
403        renderColor.alpha = 0xffff;
404    }
405    else if (!XRenderParseColor(dpy, spec, &renderColor))
406        return False;
407    if (!XftColorAllocValue(dpy,
408                            DefaultVisual(dpy,
409                                          XScreenNumberOfScreen(screen)),
410                            colormap, &renderColor, &xftColor))
411        return False;
412
413    donestr(XftColor, xftColor, XtRXftColor);
414}
415
416static void
417XmuFreeXftFont(XtAppContext app, XrmValuePtr toVal, XtPointer closure,
418               XrmValuePtr args, Cardinal * num_args)
419{
420    Screen *screen;
421    XftFont *font;
422
423    if (*num_args != 1) {
424        XtAppErrorMsg(app,
425                      "freeXftFont", "wrongParameters",
426                      "XtToolkitError",
427                      "Freeing an XftFont requires screen argument",
428                      (String *) NULL, (Cardinal *) NULL);
429        return;
430    }
431
432    screen = *((Screen **) args[0].addr);
433    font = *((XftFont **) toVal->addr);
434    if (font)
435        XftFontClose(DisplayOfScreen(screen), font);
436}
437
438static Boolean
439XmuCvtStringToXftFont(Display * dpy,
440                      XrmValue * args, Cardinal * num_args,
441                      XrmValue * fromVal, XrmValue * toVal,
442                      XtPointer * converter_data)
443{
444    char *name;
445    XftFont *font;
446    Screen *screen;
447
448    if (*num_args != 1) {
449        XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
450                      "cvtStringToXftFont", "wrongParameters",
451                      "XtToolkitError",
452                      "String to XftFont conversion needs screen argument",
453                      (String *) NULL, (Cardinal *) NULL);
454        return False;
455    }
456
457    screen = *((Screen **) args[0].addr);
458    name = (char *) fromVal->addr;
459
460    font = XftFontOpenName(dpy, XScreenNumberOfScreen(screen), name);
461    if (font) {
462        donestr(XftFont *, font, XtRXftFont);
463    }
464    XtDisplayStringConversionWarning(dpy, (char *) fromVal->addr, XtRXftFont);
465    return False;
466}
467
468static XtConvertArgRec xftFontConvertArgs[] = {
469    {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.screen),
470     sizeof(Screen *)},
471};
472
473#endif
474
475static void
476ClassInitialize(void)
477{
478#ifdef USE_XAW_PIXMAP_CVT
479    XawInitializeWidgetSet();
480#else
481    static XtConvertArgRec scrnConvertArg[] = {
482        {XtBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
483         sizeof(Screen *)}
484    };
485    XtAddConverter(XtRString, XtRPixmap, CvtStringToPixmap,
486                   scrnConvertArg, XtNumber(scrnConvertArg));
487#endif
488    XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
489                   NULL, 0);
490#ifdef XRENDER
491    XtSetTypeConverter(XtRString, XtRXftColor,
492                       XmuCvtStringToXftColor,
493                       xftColorConvertArgs, XtNumber(xftColorConvertArgs),
494                       XtCacheByDisplay, XmuFreeXftColor);
495    XtSetTypeConverter(XtRString, XtRXftFont,
496                       XmuCvtStringToXftFont,
497                       xftFontConvertArgs, XtNumber(xftFontConvertArgs),
498                       XtCacheByDisplay, XmuFreeXftFont);
499#endif
500}
501
502static char *
503TimeString(ClockWidget w, struct tm *tm)
504{
505    if (w->clock.brief) {
506        if (w->clock.twentyfour) {
507            static char brief[6];
508
509            snprintf(brief, sizeof(brief), "%02d:%02d", tm->tm_hour,
510                     tm->tm_min);
511            return brief;
512        }
513        else {
514            static char brief[9];
515            int hour = tm->tm_hour % 12;
516
517            if (!hour)
518                hour = 12;
519            snprintf(brief, sizeof(brief), "%02d:%02d %cM", hour, tm->tm_min,
520                     tm->tm_hour >= 12 ? 'P' : 'A');
521            return brief;
522        }
523    }
524    else if (w->clock.utime) {
525        static char utime[35];
526        Time_t tsec;
527
528        tsec = time(NULL);
529        snprintf(utime, sizeof(utime), "%10lu seconds since Epoch",
530                 (unsigned long) tsec);
531        return utime;
532    }
533    else if (*w->clock.strftime) {
534        /*Note: this code is probably excessively paranoid
535           about buffer overflow.  The extra size 10 padding
536           is also meant as a further guard against programmer
537           error, although it is a little controversial */
538        static char ctime[STRFTIME_BUFF_SIZE + 10];
539
540        ctime[0] = ctime[STRFTIME_BUFF_SIZE] = '\0';
541        if (0 < strftime(ctime, STRFTIME_BUFF_SIZE - 1, w->clock.strftime, tm)) {
542            ctime[STRFTIME_BUFF_SIZE - 1] = '\0';
543            return ctime;
544        }
545        else {
546            return asctime(tm);
547        }
548    }
549    else if (w->clock.twentyfour)
550        return asctime(tm);
551    else {
552        static char long12[28];
553
554        strftime(long12, sizeof long12, "%a %b %d %I:%M:%S %p %Y", tm);
555        return long12;
556    }
557}
558
559/* ARGSUSED */
560static void
561Initialize(Widget request, Widget new, ArgList args, Cardinal * num_args)
562{
563    ClockWidget w = (ClockWidget) new;
564    XtGCMask valuemask;
565    XGCValues myXGCV;
566    int min_height, min_width;
567
568    valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
569    if (w->clock.font != NULL)
570        myXGCV.font = w->clock.font->fid;
571    else
572        valuemask &= ~GCFont;   /* use server default font */
573
574    min_width = min_height = ANALOG_SIZE_DEFAULT;
575    if (!w->clock.analog) {
576        char *str;
577        struct tm tm;
578        struct timeval tv;
579        int len;
580
581#ifndef NO_I18N
582        w->clock.utf8 = False;
583
584        if (!no_locale) {
585            char *time_locale = setlocale(LC_CTYPE, NULL);
586
587            if (strstr(time_locale, "UTF-8") || strstr(time_locale, "utf8")) {
588                w->clock.utf8 = True;
589            }
590
591            /*
592             * initialize time format from CFTIME if set, otherwise
593             * default to "%c".  This emulates ascftime, but we use
594             * strftime so we can limit the string buffer size to
595             * avoid possible buffer overflow.
596             */
597            if ((w->clock.strftime == NULL) || (w->clock.strftime[0] == 0)) {
598                w->clock.strftime = getenv("CFTIME");
599                if (w->clock.strftime == NULL) {
600                    w->clock.strftime = "%c";
601                }
602            }
603        }
604#endif                          /* NO_I18N */
605
606        X_GETTIMEOFDAY(&tv);
607        tm = *localtime(&tv.tv_sec);
608        str = TimeString(w, &tm);
609        len = strlen(str);
610        if (len && str[len - 1] == '\n')
611            str[--len] = '\0';
612
613#ifdef XRENDER
614        if (w->clock.render) {
615            XGlyphInfo extents;
616
617#ifndef NO_I18N
618#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
619            char *utf8_str;
620#endif
621            if (w->clock.utf8)
622                XftTextExtentsUtf8(XtDisplay(w), w->clock.face,
623                                   (FcChar8 *) str, len, &extents);
624#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
625            else if ((utf8_str = clock_to_utf8(str, len)) != NULL) {
626                XftTextExtentsUtf8(XtDisplay(w), w->clock.face,
627                                   (FcChar8 *) utf8_str, strlen(utf8_str),
628                                   &extents);
629                free(utf8_str);
630            }
631#endif
632            else
633#endif
634                XftTextExtents8(XtDisplay(w), w->clock.face,
635                                (FcChar8 *) str, len, &extents);
636            min_width = extents.xOff + 2 * w->clock.padding;
637            min_height = w->clock.face->ascent + w->clock.face->descent +
638                2 * w->clock.padding;
639            /*fprintf(stderr, "render min_width %i\n", min_width); */
640        }
641        else
642#endif
643        {                       /* not XRENDER block */
644#ifndef NO_I18N
645            if (!no_locale) {
646                XFontSetExtents *fse;
647
648                if (w->clock.fontSet == NULL) {
649                    char **missing, *default_str;
650                    int n_missing;
651
652                    w->clock.fontSet = XCreateFontSet(XtDisplay(w),
653                                                      XtDefaultFontSet,
654                                                      &missing,
655                                                      &n_missing, &default_str);
656                }
657                if (w->clock.fontSet != NULL) {
658                    /* don't free this... it's freed with the XFontSet. */
659                    fse = XExtentsOfFontSet(w->clock.fontSet);
660
661                    min_width = XmbTextEscapement(w->clock.fontSet, str, len) +
662                        2 * w->clock.padding;
663                    min_height = fse->max_logical_extent.height +
664                        3 * w->clock.padding;
665                    /*fprintf(stderr, "fontset min_width %i\n", min_width); */
666                }
667                else {
668                    no_locale = True;
669                }
670            }
671
672            if (no_locale)
673#endif                          /* NO_I18N */
674            {
675                if (w->clock.font == NULL)
676                    w->clock.font = XQueryFont(XtDisplay(w),
677                                               XGContextFromGC(DefaultGCOfScreen
678                                                               (XtScreen(w))));
679                min_width =
680                    XTextWidth(w->clock.font, str, len) + 2 * w->clock.padding;
681                min_height =
682                    w->clock.font->ascent + w->clock.font->descent +
683                    2 * w->clock.padding;
684                /*fprintf(stderr, "font min_width %i\n", min_width); */
685            }
686        }                       /* not XRENDER block */
687    }
688    if (w->core.width == 0)
689        w->core.width = min_width;
690    if (w->core.height == 0)
691        w->core.height = min_height;
692
693    myXGCV.foreground = ClockFgPixel(w);
694    myXGCV.background = w->core.background_pixel;
695    if (w->clock.font != NULL)
696        myXGCV.font = w->clock.font->fid;
697    else
698        valuemask &= ~GCFont;   /* use server default font */
699    myXGCV.line_width = 0;
700    w->clock.myGC = XtGetGC((Widget) w, valuemask, &myXGCV);
701
702    valuemask = GCForeground | GCLineWidth | GCGraphicsExposures;
703    myXGCV.foreground = w->core.background_pixel;
704    if (w->core.background_pixmap != XtUnspecifiedPixmap) {
705        myXGCV.tile = w->core.background_pixmap;
706        myXGCV.fill_style = FillTiled;
707        valuemask |= (GCTile | GCFillStyle);
708    }
709    myXGCV.graphics_exposures = False;
710    w->clock.EraseGC = XtGetGC((Widget) w, valuemask, &myXGCV);
711    valuemask &= ~(GCTile | GCFillStyle);
712
713    myXGCV.foreground = w->clock.Hipixel;
714    w->clock.HighGC = XtGetGC((Widget) w, valuemask, &myXGCV);
715
716    valuemask = GCForeground;
717    myXGCV.foreground = w->clock.Hdpixel;
718    w->clock.HandGC = XtGetGC((Widget) w, valuemask, &myXGCV);
719
720    /* make invalid update's use a default */
721    /*if (w->clock.update <= 0) w->clock.update = 60; */
722    w->clock.show_second_hand =
723        (abs((int) w->clock.update) <= SECOND_HAND_TIME);
724    w->clock.numseg = 0;
725    w->clock.interval_id = 0;
726    memset(&w->clock.otm, '\0', sizeof(w->clock.otm));
727#ifdef XRENDER
728    {
729        int major, minor;
730
731        if (XRenderQueryVersion(XtDisplay(w), &major, &minor) &&
732            (major > 0 || (major == 0 && minor >= 4))) {
733            w->clock.can_polygon = True;
734        }
735        else
736            w->clock.can_polygon = False;
737    }
738    w->clock.pixmap = 0;
739    w->clock.draw = NULL;
740    w->clock.damage.x = 0;
741    w->clock.damage.y = 0;
742    w->clock.damage.height = 0;
743    w->clock.damage.width = 0;
744#endif
745}
746
747#ifdef XRENDER
748static void
749RenderPrepare(ClockWidget w, XftColor * color)
750{
751    if (!w->clock.draw) {
752        Drawable d = XtWindow(w);
753
754        if (w->clock.buffer) {
755            if (!w->clock.pixmap) {
756                Arg arg[1];
757
758                w->clock.pixmap = XCreatePixmap(XtDisplay(w), d,
759                                                w->core.width,
760                                                w->core.height, w->core.depth);
761                arg[0].name = XtNbackgroundPixmap;
762                arg[0].value = 0;
763                XtSetValues((Widget) w, arg, 1);
764            }
765            d = w->clock.pixmap;
766        }
767
768        w->clock.draw = XftDrawCreate(XtDisplay(w), d,
769                                      DefaultVisual(XtDisplay(w),
770                                                    DefaultScreen(XtDisplay
771                                                                  (w))),
772                                      w->core.colormap);
773        w->clock.picture = XftDrawPicture(w->clock.draw);
774    }
775    if (color)
776        w->clock.fill_picture = XftDrawSrcPicture(w->clock.draw, color);
777}
778
779static void
780RenderClip(ClockWidget w)
781{
782    Region r;
783    Drawable d;
784
785    RenderPrepare(w, NULL);
786    if (w->clock.buffer)
787        d = w->clock.pixmap;
788    else
789        d = XtWindow(w);
790    XFillRectangle(XtDisplay(w), d, w->clock.EraseGC,
791                   w->clock.damage.x,
792                   w->clock.damage.y,
793                   w->clock.damage.width, w->clock.damage.height);
794    r = XCreateRegion();
795    XUnionRectWithRegion(&w->clock.damage, r, r);
796    XftDrawSetClip(w->clock.draw, r);
797    XDestroyRegion(r);
798}
799
800static void
801RenderTextBounds(ClockWidget w, char *str, int off, int len,
802                 XRectangle * bounds, int *xp, int *yp)
803{
804    XGlyphInfo head, tail;
805    int x, y;
806
807#ifndef NO_I18N
808#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
809    char *utf8_str;
810#endif
811    if (w->clock.utf8) {
812        XftTextExtentsUtf8(XtDisplay(w), w->clock.face,
813                           (FcChar8 *) str, off, &head);
814        XftTextExtentsUtf8(XtDisplay(w), w->clock.face,
815                           (FcChar8 *) str + off, len - off, &tail);
816    }
817#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
818    else if ((utf8_str = clock_to_utf8(str, off)) != NULL) {
819        XftTextExtentsUtf8(XtDisplay(w), w->clock.face,
820                           (FcChar8 *) utf8_str, strlen(utf8_str), &head);
821        free(utf8_str);
822        if ((utf8_str = clock_to_utf8(str + off, len - off)) != NULL) {
823            XftTextExtentsUtf8(XtDisplay(w), w->clock.face,
824                               (FcChar8 *) utf8_str, strlen(utf8_str), &tail);
825            free(utf8_str);
826        }
827        else
828            goto fallback;
829    }
830#endif
831    else
832#endif
833    {
834 fallback:
835        XftTextExtents8(XtDisplay(w), w->clock.face, (FcChar8 *) str,
836                        off, &head);
837        XftTextExtents8(XtDisplay(w), w->clock.face, (FcChar8 *) str + off,
838                        len - off, &tail);
839    }
840
841    /*
842     * Compute position of tail
843     */
844    x = w->clock.padding + head.xOff;
845    y = w->clock.face->ascent + w->clock.padding + head.yOff;
846    /*
847     * Compute bounds of tail, pad a bit as the bounds aren't exact
848     */
849    bounds->x = x - tail.x - 1;
850    bounds->y = y - tail.y - 1;
851    bounds->width = tail.width + 2;
852    bounds->height = tail.height + 2;
853    if (xp)
854        *xp = x;
855    if (yp)
856        *yp = y;
857}
858
859static void
860RenderUpdateRectBounds(XRectangle * damage, XRectangle * bounds)
861{
862    int x1 = bounds->x;
863    int y1 = bounds->y;
864    int x2 = bounds->x + bounds->width;
865    int y2 = bounds->y + bounds->height;
866    int d_x1 = damage->x;
867    int d_y1 = damage->y;
868    int d_x2 = damage->x + damage->width;
869    int d_y2 = damage->y + damage->height;
870
871    if (x1 == x2) {
872        x1 = d_x1;
873        x2 = d_x2;
874    }
875    else {
876        if (d_x1 < x1)
877            x1 = d_x1;
878        if (d_x2 > x2)
879            x2 = d_x2;
880    }
881    if (y1 == y2) {
882        y1 = d_y1;
883        y2 = d_y2;
884    }
885    else {
886        if (d_y1 < y1)
887            y1 = d_y1;
888        if (d_y2 > y2)
889            y2 = d_y2;
890    }
891
892    bounds->x = x1;
893    bounds->y = y1;
894    bounds->width = x2 - x1;
895    bounds->height = y2 - y1;
896}
897
898static Boolean
899RenderRectIn(XRectangle * rect, XRectangle * bounds)
900{
901    int x1 = bounds->x;
902    int y1 = bounds->y;
903    int x2 = bounds->x + bounds->width;
904    int y2 = bounds->y + bounds->height;
905    int r_x1 = rect->x;
906    int r_y1 = rect->y;
907    int r_x2 = rect->x + rect->width;
908    int r_y2 = rect->y + rect->height;
909
910    return r_x1 < x2 && x1 < r_x2 && r_y1 < y2 && y1 < r_y2;
911}
912
913#define LINE_WIDTH  0.01
914#include <math.h>
915
916#define XCoord(x,w) ((x) * (w)->clock.x_scale + (w)->clock.x_off)
917#define YCoord(y,w) ((y) * (w)->clock.y_scale + (w)->clock.y_off)
918
919static void
920RenderUpdateBounds(XPointDouble * points, int npoints, XRectangle * bounds)
921{
922    int x1 = bounds->x;
923    int y1 = bounds->y;
924    int x2 = bounds->x + bounds->width;
925    int y2 = bounds->y + bounds->height;
926
927    while (npoints--) {
928        int r_x1 = points[0].x;
929        int r_y1 = points[0].y;
930        int r_x2 = points[0].x + 1;
931        int r_y2 = points[0].y + 1;
932
933        if (x1 == x2)
934            x2 = x1 = r_x1;
935        if (y1 == y2)
936            y2 = y1 = r_y1;
937        if (r_x1 < x1)
938            x1 = r_x1;
939        if (r_y1 < y1)
940            y1 = r_y1;
941        if (r_x2 > x2)
942            x2 = r_x2;
943        if (r_y2 > y2)
944            y2 = r_y2;
945        points++;
946    }
947    bounds->x = x1;
948    bounds->y = y1;
949    bounds->width = x2 - x1;
950    bounds->height = y2 - y1;
951}
952
953static Boolean
954RenderCheckBounds(XPointDouble * points, int npoints, XRectangle * bounds)
955{
956    int x1 = bounds->x;
957    int y1 = bounds->y;
958    int x2 = bounds->x + bounds->width;
959    int y2 = bounds->y + bounds->height;
960
961    while (npoints--) {
962        if (x1 <= points->x && points->x <= x2 &&
963            y1 <= points->y && points->y <= y2)
964            return True;
965        points++;
966    }
967    return False;
968}
969
970static void
971RenderUpdate(ClockWidget w)
972{
973    if (w->clock.buffer && w->clock.pixmap) {
974        XCopyArea(XtDisplay(w), w->clock.pixmap,
975                  XtWindow(w), w->clock.EraseGC,
976                  w->clock.damage.x, w->clock.damage.y,
977                  w->clock.damage.width, w->clock.damage.height,
978                  w->clock.damage.x, w->clock.damage.y);
979    }
980}
981
982static void
983RenderResetBounds(XRectangle * bounds)
984{
985    bounds->x = 0;
986    bounds->y = 0;
987    bounds->width = 0;
988    bounds->height = 0;
989}
990
991static void
992RenderLine(ClockWidget w, XDouble x1, XDouble y1, XDouble x2, XDouble y2,
993           XftColor * color, Boolean draw)
994{
995    XPointDouble poly[4];
996    XDouble dx = (x2 - x1);
997    XDouble dy = (y2 - y1);
998    XDouble len = sqrt(dx * dx + dy * dy);
999    XDouble ldx = (LINE_WIDTH / 2.0) * dy / len;
1000    XDouble ldy = (LINE_WIDTH / 2.0) * dx / len;
1001
1002    poly[0].x = XCoord(x1 + ldx, w);
1003    poly[0].y = YCoord(y1 - ldy, w);
1004
1005    poly[1].x = XCoord(x2 + ldx, w);
1006    poly[1].y = YCoord(y2 - ldy, w);
1007
1008    poly[2].x = XCoord(x2 - ldx, w);
1009    poly[2].y = YCoord(y2 + ldy, w);
1010
1011    poly[3].x = XCoord(x1 - ldx, w);
1012    poly[3].y = YCoord(y1 + ldy, w);
1013
1014    RenderUpdateBounds(poly, 4, &w->clock.damage);
1015    if (draw) {
1016        if (RenderCheckBounds(poly, 4, &w->clock.damage)) {
1017            RenderPrepare(w, color);
1018            XRenderCompositeDoublePoly(XtDisplay(w),
1019                                       PictOpOver,
1020                                       w->clock.fill_picture,
1021                                       w->clock.picture,
1022                                       w->clock.mask_format,
1023                                       0, 0, 0, 0, poly, 4, EvenOddRule);
1024        }
1025    }
1026    else
1027        RenderUpdateBounds(poly, 4, &w->clock.damage);
1028}
1029
1030static void
1031RenderRotate(ClockWidget w, XPointDouble * out, double x, double y, double s,
1032             double c)
1033{
1034    out->x = XCoord(x * c - y * s, w);
1035    out->y = YCoord(y * c + x * s, w);
1036}
1037
1038static void
1039RenderHand(ClockWidget w, double tick_units, double size, XftColor * color,
1040           Boolean draw)
1041{
1042    double c, s;
1043    XPointDouble poly[3];
1044    double outer_x;
1045    double inner_y;
1046
1047    ClockAngle(tick_units, &c, &s);
1048    s = -s;
1049
1050    /* compute raw positions */
1051    outer_x = size / 100.0;
1052    inner_y = HAND_WIDTH_FRACT / 100.0;
1053
1054    /* rotate them into position */
1055    RenderRotate(w, &poly[0], outer_x, 0.0, s, c);
1056    RenderRotate(w, &poly[1], -inner_y, inner_y, s, c);
1057    RenderRotate(w, &poly[2], -inner_y, -inner_y, s, c);
1058
1059    if (draw) {
1060        if (RenderCheckBounds(poly, 3, &w->clock.damage)) {
1061            RenderPrepare(w, color);
1062            XRenderCompositeDoublePoly(XtDisplay(w),
1063                                       PictOpOver,
1064                                       w->clock.fill_picture,
1065                                       w->clock.picture,
1066                                       w->clock.mask_format,
1067                                       0, 0, 0, 0, poly, 3, EvenOddRule);
1068        }
1069    }
1070    RenderUpdateBounds(poly, 3, &w->clock.damage);
1071}
1072
1073static void
1074RenderHands(ClockWidget w, struct tm *tm, struct timeval *tv, Boolean draw)
1075{
1076    double sec = tm->tm_sec + tv->tv_usec / 1000000.0;
1077
1078    RenderHand(w, tm->tm_hour * 300 + tm->tm_min * 5 + sec / 12.0, HOUR_HAND_FRACT,
1079               &w->clock.hour_color, draw);
1080    RenderHand(w, tm->tm_min * 60 + sec, MINUTE_HAND_FRACT,
1081               &w->clock.min_color, draw);
1082}
1083
1084static void
1085RenderSec(ClockWidget w, struct tm *tm, struct timeval *tv, Boolean draw)
1086{
1087    double c, s;
1088    XPointDouble poly[10];
1089    double inner_x, middle_x, outer_x, far_x;
1090    double middle_y;
1091    double line_y;
1092    double sec;
1093
1094    sec = tm->tm_sec;
1095
1096    if (w->clock.update < 1.0)
1097        sec += tv->tv_usec / 1000000.0;
1098
1099    ClockAngle((int) (sec * 60.0), &c, &s);
1100
1101    s = -s;
1102
1103    /*
1104     * Compute raw positions
1105     */
1106    line_y = LINE_WIDTH;
1107    inner_x = (MINUTE_HAND_FRACT / 100.0);
1108    middle_x = ((SECOND_HAND_FRACT + MINUTE_HAND_FRACT) / 200.0);
1109    outer_x = (SECOND_HAND_FRACT / 100.0);
1110    far_x = (MINOR_TICK_FRACT / 100.0);
1111    middle_y = (SECOND_WIDTH_FRACT / 100.0);
1112
1113    /*
1114     * Rotate them into position
1115     */
1116    RenderRotate(w, &poly[0], -line_y, line_y, s, c);
1117    RenderRotate(w, &poly[1], inner_x, line_y, s, c);
1118    RenderRotate(w, &poly[2], middle_x, middle_y, s, c);
1119    RenderRotate(w, &poly[3], outer_x, line_y, s, c);
1120    RenderRotate(w, &poly[4], far_x, line_y, s, c);
1121    RenderRotate(w, &poly[5], far_x, -line_y, s, c);
1122    RenderRotate(w, &poly[6], outer_x, -line_y, s, c);
1123    RenderRotate(w, &poly[7], middle_x, -middle_y, s, c);
1124    RenderRotate(w, &poly[8], inner_x, -line_y, s, c);
1125    RenderRotate(w, &poly[9], -line_y, -line_y, s, c);
1126
1127    if (draw) {
1128        if (RenderCheckBounds(poly, 10, &w->clock.damage)) {
1129            RenderPrepare(w, &w->clock.sec_color);
1130            XRenderCompositeDoublePoly(XtDisplay(w),
1131                                       PictOpOver,
1132                                       w->clock.fill_picture,
1133                                       w->clock.picture,
1134                                       w->clock.mask_format,
1135                                       0, 0, 0, 0, poly, 10, EvenOddRule);
1136        }
1137    }
1138    else {
1139        RenderUpdateBounds(poly, 10, &w->clock.damage);
1140    }
1141}
1142
1143#endif
1144
1145static void
1146Realize(Widget gw, XtValueMask * valueMask, XSetWindowAttributes * attrs)
1147{
1148    ClockWidget w = (ClockWidget) gw;
1149
1150#ifdef notdef
1151    *valueMask |= CWBitGravity;
1152    attrs->bit_gravity = ForgetGravity;
1153#endif
1154    switch (w->clock.backing_store) {
1155    case Always:
1156    case NotUseful:
1157    case WhenMapped:
1158        *valueMask |= CWBackingStore;
1159        attrs->backing_store = w->clock.backing_store;
1160        break;
1161    }
1162    (*clockWidgetClass->core_class.superclass->core_class.realize)
1163        (gw, valueMask, attrs);
1164    Resize(gw);
1165}
1166
1167static void
1168Destroy(Widget gw)
1169{
1170    ClockWidget w = (ClockWidget) gw;
1171
1172    if (w->clock.interval_id)
1173        XtRemoveTimeOut(w->clock.interval_id);
1174#ifdef XRENDER
1175    if (w->clock.picture)
1176        XRenderFreePicture(XtDisplay(w), w->clock.picture);
1177    if (w->clock.fill_picture)
1178        XRenderFreePicture(XtDisplay(w), w->clock.fill_picture);
1179#endif
1180    XtReleaseGC(gw, w->clock.myGC);
1181    XtReleaseGC(gw, w->clock.HighGC);
1182    XtReleaseGC(gw, w->clock.HandGC);
1183    XtReleaseGC(gw, w->clock.EraseGC);
1184}
1185
1186static void
1187Resize(Widget gw)
1188{
1189    ClockWidget w = (ClockWidget) gw;
1190
1191    /* don't do this computation if window hasn't been realized yet. */
1192    if (XtIsRealized(gw) && w->clock.analog) {
1193        /* need signed value since Dimension is unsigned */
1194        int radius =
1195            ((int) min(w->core.width, w->core.height) -
1196             (int) (2 * w->clock.padding)) / 2;
1197        w->clock.radius = (Dimension) max(radius, 1);
1198
1199        w->clock.second_hand_length =
1200            (int) (SECOND_HAND_FRACT * w->clock.radius) / 100;
1201        w->clock.minute_hand_length =
1202            (int) (MINUTE_HAND_FRACT * w->clock.radius) / 100;
1203        w->clock.hour_hand_length =
1204            (int) (HOUR_HAND_FRACT * w->clock.radius) / 100;
1205        w->clock.hand_width = (int) (HAND_WIDTH_FRACT * w->clock.radius) / 100;
1206        w->clock.second_hand_width =
1207            (int) (SECOND_WIDTH_FRACT * w->clock.radius) / 100;
1208
1209        w->clock.centerX = w->core.width / 2;
1210        w->clock.centerY = w->core.height / 2;
1211    }
1212#ifdef XRENDER
1213    w->clock.x_scale = 0.45 * w->core.width;
1214    w->clock.y_scale = 0.45 * w->core.height;
1215    w->clock.x_off = 0.5 * w->core.width;
1216    w->clock.y_off = 0.5 * w->core.height;
1217    if (w->clock.pixmap) {
1218        XFreePixmap(XtDisplay(w), w->clock.pixmap);
1219        w->clock.pixmap = 0;
1220        if (w->clock.draw) {
1221            XftDrawDestroy(w->clock.draw);
1222            w->clock.draw = NULL;
1223        }
1224        w->clock.picture = 0;
1225    }
1226#endif
1227}
1228
1229/* ARGSUSED */
1230static void
1231Redisplay(Widget gw, XEvent * event, Region region)
1232{
1233    ClockWidget w = (ClockWidget) gw;
1234
1235    if (w->clock.analog) {
1236#ifdef XRENDER
1237        if (w->clock.render && w->clock.can_polygon)
1238            XClipBox(region, &w->clock.damage);
1239        else
1240#endif
1241        {
1242            if (w->clock.numseg != 0)
1243                erase_hands(w, (struct tm *) 0);
1244            DrawClockFace(w);
1245        }
1246    }
1247    else {
1248#ifdef XRENDER
1249        if (w->clock.render)
1250            XClipBox(region, &w->clock.damage);
1251#endif
1252        w->clock.prev_time_string[0] = '\0';
1253    }
1254    clock_tic((XtPointer) w, (XtIntervalId *) NULL);
1255}
1256
1257#define USEC_MILLIS(us)	((unsigned long) (us) / 1000)
1258#define SEC_MILLIS(s)	((unsigned long) (s) * 1000)
1259#define MIN_MILLIS(m)	SEC_MILLIS((unsigned long) (m) * 60)
1260#define HOUR_MILLIS(h)	MIN_MILLIS((unsigned long) (h) * 60)
1261#define DAY_MILLIS	HOUR_MILLIS((unsigned long) 24)
1262
1263#define MIN_SECS(m)	((unsigned long) (m) * 60)
1264#define HOUR_SECS(h)	MIN_SECS((unsigned long) (h) * 60)
1265
1266/* Seconds since midnight */
1267static unsigned long
1268time_seconds(struct tm *tm)
1269{
1270    return HOUR_SECS(tm->tm_hour) + MIN_SECS(tm->tm_min) + tm->tm_sec;
1271}
1272
1273/* Milliseconds since midnight */
1274static unsigned long
1275time_millis(struct tm *tm, struct timeval *tv)
1276{
1277    return time_seconds(tm) * 1000 + USEC_MILLIS(tv->tv_usec);
1278}
1279
1280/* Round milliseconds to number of intervals (measured in milliseconds) */
1281static unsigned long
1282time_intervals(unsigned long millis, unsigned long interval)
1283{
1284    return (millis + interval / 2) / interval;
1285}
1286
1287/*
1288 * Round the current time to the nearest update interval using
1289 * milliseconds since midnight
1290 */
1291static void
1292round_time(float _update, struct tm *tm, struct timeval *tv)
1293{
1294    /* interval in milliseconds */
1295    unsigned long update = (int) (_update * 1000.0 + 0.5);
1296
1297    /* compute milliseconds since midnight */
1298    unsigned long old_secs = time_seconds(tm);
1299    unsigned long old_millis = time_millis(tm, tv);
1300
1301    /* Nearest number of intervals since midnight */
1302    unsigned long intervals = time_intervals(old_millis, update);
1303
1304    /* The number of milliseconds for that number of intervals */
1305    unsigned long new_millis = intervals * update;
1306    time_t t;
1307
1308    if (new_millis > DAY_MILLIS)
1309        new_millis = DAY_MILLIS;
1310
1311    /* Compute the time_t of that interval by subtracting off the real
1312     * seconds and adding back in the desired second
1313     */
1314
1315    t = tv->tv_sec - old_secs + new_millis / 1000;
1316    *tm = *localtime(&t);
1317    tv->tv_usec = (new_millis % 1000) * 1000;
1318}
1319
1320/* Choose the update times for well-defined clock states.
1321 *
1322 * For example, in HH:MM:SS notation the last number rolls over
1323 * every 60 seconds and has at most 60 display states.  The sequence
1324 * depends on its initial value t0 and the update period u, e.g.
1325 *
1326 *   u (s)  d (s)  ti (s)                    m (states)  l (s)
1327 *    2      2     {0,2, .. 58}              30            60
1328 *    7      1     {0,7, .. 56,3, .. 53}     60           420
1329 *   15     15     {0,15,30,45}               4            60
1330 *   45     15     {0,45,30,15}               4           180
1331 *   53      1     {0,53,46, .. 4,57, .. 7}  60          3180
1332 *   58      2     {0,58,56, .. 2}           30          1740
1333 *   60     60     {0}                        1            60
1334 *
1335 * u=  update period in seconds,
1336 * ti= time at update i from the reference, HH:MM:00 or HH:00:00,
1337 * n=  the roll over time, the modulus, 60 s or 3600 s,
1338 * m=  the sequence length, the order of u in the modulo n group Z/nZ,
1339 * l=  the total sequence duration =m*u.
1340 * d=  gcd(n,u) the greatest common divisor
1341 *
1342 * The time t(i) determines the clock state.  It follows from
1343 *
1344 *   t(i)=t(i-1)+u mod n  <=>  t(i)=t(0)+i*u mod n
1345 *
1346 * which defines a { t(0) .. t(m-1) } sequence of m unique elements.
1347 * Hence, u generates a subgroup U={k*u mod n : k in Z} of Z/nZ so
1348 * its order m divides n.  This m satisfies
1349 *
1350 *   t(m)=t(0)  <=>  m*u mod n = 0  <=>  m*u = r*n  <=>  m=n/d, r=u/d
1351 *
1352 * where d divides n and u.  Choosing
1353 *
1354 *   d=gcd(n,u)  <=>  n/d and u/d are coprime  =>  m=n/d is minimum
1355 *
1356 * thus gives the order.  Furthermore, the greatest common divisor d is
1357 * also the minimum value generator of the set U.  Assume a generator e
1358 * where
1359 *
1360 *   e|{n,u}  <=>  Ai,Ej: i*u mod n = j*e  <=>  j=f(i)=(i*u mod n)/e
1361 *
1362 * such that f maps i to m=ord(u) unique values of j.  Its properties are
1363 *
1364 *   j=i*u/e mod n/e   ==>  0<=j<n/e
1365 *
1366 *   ord(u/e, mod n/e)=n/e/gcd(n/e,u/e)=n/d=m  ==>  J={j=f(i)}, |J|=m
1367 *
1368 *   ord(e)=n/gcd(n,e)=n/e
1369 *
1370 * from which follows
1371 *
1372 *   e=d  ==>  f: I={0,..,m-1} -> J={0,..,m-1}: j=i*r mod m is bijective
1373 *        ==>  D={k*d mod n : k in Z} = U.
1374 *
1375 * Any value e below d is no generator since it yields a non contiguous
1376 * J such that an l=0..n/e-1 exists not in J with l*e not in U.
1377 *
1378 * The update sequence t(i) could be followed using the algorithm:
1379 *
1380 *   1. restore the expected value into t(i),
1381 *   2. calculate the next timeout t(i+1)=t(i)+u mod n,
1382 *   3. verify that the current tc(i) is between t(i)..t(i+1),
1383 *   4. calculate the time to wait w=t(i+1)-tc(i) mod n,
1384 *   5. store t(i+1),
1385 *
1386 * which implements state tracking.  This approach doesn't work well
1387 * since the set timeout w does not guarantee a next call at time
1388 * t(i+1), e.g. due to program sleeps, time adjustments, and leap
1389 * seconds.  A robust method should only rely on the current time
1390 * tc(i) to identify t(i).  The derivation above shows 2 options:
1391 *
1392 *   1.  n={60,3600} and round to a multiple of d,
1393 *       but if d<u then the sequence is not guaranteed.
1394 *   2.  choose n large and round to a multiple of u,
1395 *       but then the sequence resets at roll-over.
1396 *
1397 * The code below implements (2) with n this year's duration in seconds
1398 * and using local time year's start as epoch.
1399 */
1400
1401static unsigned long
1402waittime(float _update, struct timeval *tv, struct tm *tm)
1403{
1404    unsigned long update_millis = (unsigned long) (_update * 1000 + 0.5);
1405    unsigned long millis = time_millis(tm, tv);
1406    unsigned long intervals = time_intervals(millis, update_millis);
1407    unsigned long next = intervals + 1;
1408    unsigned long next_millis = next * update_millis;
1409    unsigned long result;
1410
1411    if (next_millis > DAY_MILLIS)
1412        next_millis = DAY_MILLIS;
1413
1414    result = next_millis - millis;
1415    return result;
1416}
1417
1418/* ARGSUSED */
1419static void
1420clock_tic(XtPointer client_data, XtIntervalId * id)
1421{
1422    ClockWidget w = (ClockWidget) client_data;
1423    struct tm tm;
1424    struct timeval tv;
1425    char *time_ptr;
1426    register Display *dpy = XtDisplay(w);
1427    register Window win = XtWindow(w);
1428
1429    X_GETTIMEOFDAY(&tv);
1430    tm = *localtime(&tv.tv_sec);
1431    if (w->clock.update && (id || !w->clock.interval_id))
1432        w->clock.interval_id =
1433            XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w),
1434                            waittime(w->clock.update, &tv, &tm),
1435                            clock_tic, (XtPointer) w);
1436
1437    round_time(w->clock.update, &tm, &tv);
1438    /*
1439     * Beep on the half hour; double-beep on the hour.
1440     */
1441    if (w->clock.chime == TRUE) {
1442        if (w->clock.beeped && (tm.tm_min != 30) && (tm.tm_min != 0))
1443            w->clock.beeped = FALSE;
1444        if (((tm.tm_min == 30) || (tm.tm_min == 0))
1445            && (!w->clock.beeped)) {
1446            w->clock.beeped = TRUE;
1447#ifdef XKB
1448            if (tm.tm_min == 0) {
1449                XkbStdBell(dpy, win, 50, XkbBI_ClockChimeHour);
1450                XkbStdBell(dpy, win, 50, XkbBI_RepeatingLastBell);
1451            }
1452            else {
1453                XkbStdBell(dpy, win, 50, XkbBI_ClockChimeHalf);
1454            }
1455#else
1456            XBell(dpy, 50);
1457            if (tm.tm_min == 0)
1458                XBell(dpy, 50);
1459#endif
1460        }
1461    }
1462    if (w->clock.analog == FALSE) {
1463        int clear_from = w->core.width;
1464        int i, len, prev_len;
1465
1466        time_ptr = TimeString(w, &tm);
1467        len = strlen(time_ptr);
1468        if (len && time_ptr[len - 1] == '\n')
1469            time_ptr[--len] = '\0';
1470        prev_len = strlen(w->clock.prev_time_string);
1471        for (i = 0; ((i < len) && (i < prev_len) &&
1472                     (w->clock.prev_time_string[i] == time_ptr[i])); i++);
1473
1474#ifdef XRENDER
1475        if (w->clock.render) {
1476            XRectangle old_tail, new_tail, head;
1477            int x, y;
1478
1479#if !defined(NO_I18N) && defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
1480            char *utf8_str;
1481#endif
1482
1483            RenderTextBounds(w, w->clock.prev_time_string, i, prev_len,
1484                             &old_tail, NULL, NULL);
1485            RenderUpdateRectBounds(&old_tail, &w->clock.damage);
1486            RenderTextBounds(w, time_ptr, i, len, &new_tail, NULL, NULL);
1487            RenderUpdateRectBounds(&new_tail, &w->clock.damage);
1488
1489            while (i) {
1490                RenderTextBounds(w, time_ptr, 0, i, &head, NULL, NULL);
1491                if (!RenderRectIn(&head, &w->clock.damage))
1492                    break;
1493                i--;
1494            }
1495            RenderTextBounds(w, time_ptr, i, len, &new_tail, &x, &y);
1496            RenderClip(w);
1497            RenderPrepare(w, NULL);
1498#ifndef NO_I18N
1499            if (w->clock.utf8) {
1500                XftDrawStringUtf8(w->clock.draw,
1501                                  &w->clock.fg_color,
1502                                  w->clock.face,
1503                                  x, y, (FcChar8 *) time_ptr + i, len - i);
1504
1505            }
1506#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
1507            else if ((utf8_str = clock_to_utf8(time_ptr + i, len - i)) != NULL) {
1508                XftDrawStringUtf8(w->clock.draw,
1509                                  &w->clock.fg_color,
1510                                  w->clock.face,
1511                                  x, y, (FcChar8 *) utf8_str, strlen(utf8_str));
1512                free(utf8_str);
1513            }
1514#endif
1515            else
1516#endif
1517            {
1518                XftDrawString8(w->clock.draw,
1519                               &w->clock.fg_color,
1520                               w->clock.face,
1521                               x, y, (FcChar8 *) time_ptr + i, len - i);
1522            }
1523            RenderUpdate(w);
1524            RenderResetBounds(&w->clock.damage);
1525        }
1526        else
1527#endif
1528#ifndef NO_I18N
1529        if (!no_locale) {
1530            if (0 < len) {
1531                XFontSetExtents *fse = XExtentsOfFontSet(w->clock.fontSet);
1532
1533                XmbDrawImageString(dpy, win, w->clock.fontSet, w->clock.myGC,
1534                                   (2 + w->clock.padding +
1535                                    (i ? XmbTextEscapement(w->clock.fontSet,
1536                                                           time_ptr, i) : 0)),
1537                                   2 + w->clock.padding +
1538                                   fse->max_logical_extent.height, time_ptr + i,
1539                                   len - i);
1540                /*
1541                 * Clear any left over bits
1542                 */
1543                clear_from = XmbTextEscapement(w->clock.fontSet, time_ptr,
1544                                               len) + 2 + w->clock.padding;
1545            }
1546        }
1547        else
1548#endif                          /* NO_I18N */
1549        {
1550            XDrawImageString(dpy, win, w->clock.myGC,
1551                             (1 + w->clock.padding +
1552                              XTextWidth(w->clock.font, time_ptr, i)),
1553                             w->clock.font->ascent + w->clock.padding,
1554                             time_ptr + i, len - i);
1555            /*
1556             * Clear any left over bits
1557             */
1558            clear_from = XTextWidth(w->clock.font, time_ptr, len)
1559                + 2 + w->clock.padding;
1560        }
1561        if (clear_from < (int) w->core.width)
1562            XClearArea(dpy, win,
1563                       clear_from, 0, w->core.width - clear_from,
1564                       w->core.height, False);
1565#ifdef HAVE_STRLCPY
1566        strlcpy(w->clock.prev_time_string + i, time_ptr + i,
1567                sizeof(w->clock.prev_time_string) - i);
1568#else
1569        strncpy(w->clock.prev_time_string + i, time_ptr + i,
1570                sizeof(w->clock.prev_time_string) - i);
1571        w->clock.prev_time_string[sizeof(w->clock.prev_time_string) - 1] = 0;
1572#endif
1573    }
1574    else {
1575        /*
1576         * The second (or minute) hand is sec (or min)
1577         * sixtieths around the clock face. The hour hand is
1578         * (hour + min/60) twelfths of the way around the
1579         * clock-face.  The derivation is left as an exercise
1580         * for the reader.
1581         */
1582
1583        /*
1584         * 12 hour clock.
1585         */
1586        if (tm.tm_hour >= 12)
1587            tm.tm_hour -= 12;
1588
1589#ifdef XRENDER
1590        if (w->clock.render && w->clock.can_polygon) {
1591            w->clock.mask_format = XRenderFindStandardFormat(XtDisplay(w),
1592                                                             w->clock.sharp ?
1593                                                             PictStandardA1 :
1594                                                             PictStandardA8);
1595            /*
1596             * Compute repaint area
1597             */
1598            if (tm.tm_min != w->clock.otm.tm_min ||
1599                tm.tm_hour != w->clock.otm.tm_hour ||
1600                tm.tm_sec != w->clock.otm.tm_sec ||
1601		tv.tv_usec != w->clock.otv.tv_usec) {
1602                RenderHands(w, &w->clock.otm, &w->clock.otv, False);
1603                RenderHands(w, &tm, &tv, False);
1604            }
1605            if (w->clock.show_second_hand &&
1606                (tm.tm_sec != w->clock.otm.tm_sec ||
1607                 tv.tv_usec != w->clock.otv.tv_usec)) {
1608                RenderSec(w, &w->clock.otm, &w->clock.otv, False);
1609                RenderSec(w, &tm, &tv, False);
1610            }
1611            if (w->clock.damage.width && w->clock.damage.height) {
1612                RenderClip(w);
1613                DrawClockFace(w);
1614                RenderHands(w, &tm, &tv, True);
1615                if (w->clock.show_second_hand == TRUE)
1616                    RenderSec(w, &tm, &tv, True);
1617            }
1618            w->clock.otm = tm;
1619            w->clock.otv = tv;
1620            RenderUpdate(w);
1621            RenderResetBounds(&w->clock.damage);
1622            return;
1623        }
1624#endif
1625
1626        erase_hands(w, &tm);
1627
1628        if (w->clock.numseg == 0 ||
1629            tm.tm_min != w->clock.otm.tm_min ||
1630            tm.tm_hour != w->clock.otm.tm_hour ||
1631            tm.tm_sec != w->clock.otm.tm_sec) {
1632            w->clock.segbuffptr = w->clock.segbuff;
1633            w->clock.numseg = 0;
1634            /*
1635             * Calculate the hour hand, fill it in with its
1636             * color and then outline it.  Next, do the same
1637             * with the minute hand.  This is a cheap hidden
1638             * line algorithm.
1639             */
1640            DrawHand(w,
1641                     w->clock.minute_hand_length, w->clock.hand_width,
1642                     tm.tm_min * 60 + tm.tm_sec);
1643            if (w->clock.Hdpixel != w->core.background_pixel)
1644                XFillPolygon(dpy,
1645                             win, w->clock.HandGC,
1646                             w->clock.segbuff, VERTICES_IN_HANDS,
1647                             Convex, CoordModeOrigin);
1648            XDrawLines(dpy,
1649                       win, w->clock.HighGC,
1650                       w->clock.segbuff, VERTICES_IN_HANDS, CoordModeOrigin);
1651            w->clock.hour = w->clock.segbuffptr;
1652            DrawHand(w,
1653                     w->clock.hour_hand_length, w->clock.hand_width,
1654                     tm.tm_hour * 300 + tm.tm_min * 5);
1655            if (w->clock.Hdpixel != w->core.background_pixel) {
1656                XFillPolygon(dpy,
1657                             win, w->clock.HandGC,
1658                             w->clock.hour,
1659                             VERTICES_IN_HANDS, Convex, CoordModeOrigin);
1660            }
1661            XDrawLines(dpy,
1662                       win, w->clock.HighGC,
1663                       w->clock.hour, VERTICES_IN_HANDS, CoordModeOrigin);
1664
1665            w->clock.sec = w->clock.segbuffptr;
1666        }
1667        if (w->clock.show_second_hand == TRUE) {
1668            w->clock.segbuffptr = w->clock.sec;
1669            DrawSecond(w,
1670                       w->clock.second_hand_length - 2,
1671                       w->clock.second_hand_width,
1672                       w->clock.minute_hand_length + 2,
1673                       tm.tm_sec * 60 + tv.tv_usec * 60 / 1000000);
1674            if (w->clock.Hdpixel != w->core.background_pixel)
1675                XFillPolygon(dpy,
1676                             win, w->clock.HandGC,
1677                             w->clock.sec,
1678                             VERTICES_IN_HANDS - 2, Convex, CoordModeOrigin);
1679            XDrawLines(dpy,
1680                       win, w->clock.HighGC,
1681                       w->clock.sec, VERTICES_IN_HANDS - 1, CoordModeOrigin);
1682
1683        }
1684        w->clock.otm = tm;
1685        w->clock.otv = tv;
1686    }
1687}
1688
1689static void
1690erase_hands(ClockWidget w, struct tm *tm)
1691{
1692    /*
1693     * Erase old hands.
1694     */
1695    if (w->clock.numseg > 0) {
1696        Display *dpy;
1697        Window win;
1698
1699        dpy = XtDisplay(w);
1700        win = XtWindow(w);
1701        if (w->clock.show_second_hand == TRUE) {
1702            XDrawLines(dpy, win,
1703                       w->clock.EraseGC,
1704                       w->clock.sec, VERTICES_IN_HANDS - 1, CoordModeOrigin);
1705            if (w->clock.Hdpixel != w->core.background_pixel) {
1706                XFillPolygon(dpy,
1707                             win, w->clock.EraseGC,
1708                             w->clock.sec,
1709                             VERTICES_IN_HANDS - 2, Convex, CoordModeOrigin);
1710            }
1711        }
1712        if (!tm || tm->tm_min != w->clock.otm.tm_min ||
1713            tm->tm_hour != w->clock.otm.tm_hour ||
1714            tm->tm_sec != w->clock.otm.tm_sec) {
1715            XDrawLines(dpy, win,
1716                       w->clock.EraseGC,
1717                       w->clock.segbuff, VERTICES_IN_HANDS, CoordModeOrigin);
1718            XDrawLines(dpy, win,
1719                       w->clock.EraseGC,
1720                       w->clock.hour, VERTICES_IN_HANDS, CoordModeOrigin);
1721            if (w->clock.Hdpixel != w->core.background_pixel) {
1722                XFillPolygon(dpy, win,
1723                             w->clock.EraseGC,
1724                             w->clock.segbuff, VERTICES_IN_HANDS,
1725                             Convex, CoordModeOrigin);
1726                XFillPolygon(dpy, win,
1727                             w->clock.EraseGC,
1728                             w->clock.hour,
1729                             VERTICES_IN_HANDS, Convex, CoordModeOrigin);
1730            }
1731        }
1732    }
1733}
1734
1735static void
1736ClockAngle(double tick_units, double *sinp, double *cosp)
1737{
1738    double angle = tick_units * (M_PI / 180 / 10.0);
1739
1740#ifdef HAVE_SINCOS
1741    sincos(angle, sinp, cosp);
1742#else
1743    *sinp = sin(angle);
1744    *cosp = cos(angle);
1745#endif
1746}
1747
1748/*
1749 * DrawLine - Draws a line.
1750 *
1751 * blank_length is the distance from the center which the line begins.
1752 * length is the maximum length of the hand.
1753 * Tick_units is a number between zero and 12*60 indicating
1754 * how far around the circle (clockwise) from high noon.
1755 *
1756 * The blank_length feature is because I wanted to draw tick-marks around the
1757 * circle (for seconds).  The obvious means of drawing lines from the center
1758 * to the perimeter, then erasing all but the outside most pixels doesn't
1759 * work because of round-off error (sigh).
1760 */
1761static void
1762DrawLine(ClockWidget w, Dimension blank_length, Dimension length,
1763         int tick_units)
1764{
1765    double dblank_length = (double) blank_length, dlength = (double) length;
1766    double cosangle, sinangle;
1767    int cx = w->clock.centerX, cy = w->clock.centerY, x1, y1, x2, y2;
1768
1769    /*
1770     *  Angles are measured from 12 o'clock, clockwise increasing.
1771     *  Since in X, +x is to the right and +y is downward:
1772     *
1773     *      x = x0 + r * sin(theta)
1774     *      y = y0 - r * cos(theta)
1775     *
1776     */
1777    ClockAngle(tick_units, &sinangle, &cosangle);
1778
1779    /* break this out so that stupid compilers can cope */
1780    x1 = cx + (int) (dblank_length * sinangle);
1781    y1 = cy - (int) (dblank_length * cosangle);
1782    x2 = cx + (int) (dlength * sinangle);
1783    y2 = cy - (int) (dlength * cosangle);
1784    SetSeg(w, x1, y1, x2, y2);
1785}
1786
1787/*
1788 * DrawHand - Draws a hand.
1789 *
1790 * length is the maximum length of the hand.
1791 * width is the half-width of the hand.
1792 * Tick_units is a number between zero and 12*60 indicating
1793 * how far around the circle (clockwise) from high noon.
1794 *
1795 */
1796static void
1797DrawHand(ClockWidget w, Dimension length, Dimension width, int tick_units)
1798{
1799
1800    double cosangle, sinangle;
1801    register double ws, wc;
1802    Position x, y, x1, y1, x2, y2;
1803
1804    /*
1805     *  Angles are measured from 12 o'clock, clockwise increasing.
1806     *  Since in X, +x is to the right and +y is downward:
1807     *
1808     *      x = x0 + r * sin(theta)
1809     *      y = y0 - r * cos(theta)
1810     *
1811     */
1812    ClockAngle(tick_units, &sinangle, &cosangle);
1813    /*
1814     * Order of points when drawing the hand.
1815     *
1816     *              1,4
1817     *              / \
1818     *             /   \
1819     *            /     \
1820     *          2 ------- 3
1821     */
1822    wc = width * cosangle;
1823    ws = width * sinangle;
1824    SetSeg(w,
1825	   x = w->clock.centerX + clock_round(length * sinangle),
1826	   y = w->clock.centerY - clock_round(length * cosangle),
1827	   x1 = w->clock.centerX - clock_round(ws + wc),
1828	   y1 = w->clock.centerY + clock_round(wc - ws));  /* 1 ---- 2 */
1829    /* 2 */
1830    SetSeg(w, x1, y1,
1831	   x2 = w->clock.centerX - clock_round(ws - wc),
1832	   y2 = w->clock.centerY + clock_round(wc + ws));  /* 2 ----- 3 */
1833
1834    SetSeg(w, x2, y2, x, y);	/* 3 ----- 1(4) */
1835}
1836
1837/*
1838 * DrawSecond - Draws the second hand (diamond).
1839 *
1840 * length is the maximum length of the hand.
1841 * width is the half-width of the hand.
1842 * offset is direct distance from center to tail end.
1843 * Tick_units is a number between zero and 12*60 indicating
1844 * how far around the circle (clockwise) from high noon.
1845 *
1846 */
1847static void
1848DrawSecond(ClockWidget w, Dimension length, Dimension width,
1849           Dimension offset, int tick_units)
1850{
1851
1852    double cosangle, sinangle;
1853    register double ms, mc, ws, wc;
1854    register int mid;
1855    Position x, y;
1856
1857    /*
1858     *  Angles are measured from 12 o'clock, clockwise increasing.
1859     *  Since in X, +x is to the right and +y is downward:
1860     *
1861     *      x = x0 + r * sin(theta)
1862     *      y = y0 - r * cos(theta)
1863     *
1864     */
1865    ClockAngle(tick_units, &sinangle, &cosangle);
1866    /*
1867     * Order of points when drawing the hand.
1868     *
1869     *              1,5
1870     *              / \
1871     *             /   \
1872     *            /     \
1873     *          2<       >4
1874     *            \     /
1875     *             \   /
1876     *              \ /
1877     *      -        3
1878     *      |
1879     *      |
1880     *   offset
1881     *      |
1882     *      |
1883     *      -        + center
1884     */
1885
1886    mid = (int) (length + offset) / 2;
1887    mc = mid * cosangle;
1888    ms = mid * sinangle;
1889    wc = width * cosangle;
1890    ws = width * sinangle;
1891    /*1 ---- 2 */
1892    SetSeg(w,
1893           x = w->clock.centerX + clock_round(length * sinangle),
1894           y = w->clock.centerY - clock_round(length * cosangle),
1895           w->clock.centerX + clock_round(ms - wc),
1896           w->clock.centerY - clock_round(mc + ws));
1897    SetSeg(w, w->clock.centerX + clock_round(offset * sinangle), w->clock.centerY - clock_round(offset * cosangle),     /* 2-----3 */
1898           w->clock.centerX + clock_round(ms + wc),
1899           w->clock.centerY - clock_round(mc - ws));
1900    w->clock.segbuffptr->x = x;
1901    w->clock.segbuffptr++->y = y;
1902    w->clock.numseg++;
1903}
1904
1905static void
1906SetSeg(ClockWidget w, int x1, int y1, int x2, int y2)
1907{
1908    w->clock.segbuffptr->x = x1;
1909    w->clock.segbuffptr++->y = y1;
1910    w->clock.segbuffptr->x = x2;
1911    w->clock.segbuffptr++->y = y2;
1912    w->clock.numseg += 2;
1913}
1914
1915/*
1916 *  Draw the clock face (every fifth tick-mark is longer
1917 *  than the others).
1918 */
1919static void
1920DrawClockFace(ClockWidget w)
1921{
1922    register int i;
1923    register int delta =
1924        (int) (w->clock.radius - w->clock.second_hand_length) / 3;
1925
1926    w->clock.segbuffptr = w->clock.segbuff;
1927    w->clock.numseg = 0;
1928    for (i = 0; i < 60; i++) {
1929#ifdef XRENDER
1930        if (w->clock.render && w->clock.can_polygon) {
1931            double s, c;
1932            XDouble x1, y1, x2, y2;
1933            XftColor *color;
1934
1935            ClockAngle(i * 60, &s, &c);
1936            x1 = c;
1937            y1 = s;
1938            if (i % 5) {
1939                x2 = c * (MINOR_TICK_FRACT / 100.0);
1940                y2 = s * (MINOR_TICK_FRACT / 100.0);
1941                color = &w->clock.minor_color;
1942            }
1943            else {
1944                x2 = c * (SECOND_HAND_FRACT / 100.0);
1945                y2 = s * (SECOND_HAND_FRACT / 100.0);
1946                color = &w->clock.major_color;
1947            }
1948            RenderLine(w, x1, y1, x2, y2, color, True);
1949        }
1950        else
1951#endif
1952        {
1953            DrawLine(w, ((i % 5) == 0 ?
1954                         w->clock.second_hand_length :
1955                         (w->clock.radius - delta)), w->clock.radius, i * 60);
1956        }
1957    }
1958#ifdef XRENDER
1959    if (w->clock.render && w->clock.can_polygon)
1960        return;
1961#endif
1962    /*
1963     * Go ahead and draw it.
1964     */
1965    XDrawSegments(XtDisplay(w), XtWindow(w),
1966                  w->clock.myGC, (XSegment *) & (w->clock.segbuff[0]),
1967                  w->clock.numseg / 2);
1968
1969    w->clock.segbuffptr = w->clock.segbuff;
1970    w->clock.numseg = 0;
1971}
1972
1973static int
1974clock_round(double x)
1975{
1976    return (x >= 0.0 ? (int) (x + .5) : (int) (x - .5));
1977}
1978
1979#ifdef XRENDER
1980static Boolean
1981sameColor(XftColor * old, XftColor * new)
1982{
1983    if (old->color.red != new->color.red)
1984        return False;
1985    if (old->color.green != new->color.green)
1986        return False;
1987    if (old->color.blue != new->color.blue)
1988        return False;
1989    if (old->color.alpha != new->color.alpha)
1990        return False;
1991    return True;
1992}
1993#endif
1994
1995/* ARGSUSED */
1996static Boolean
1997SetValues(Widget gcurrent, Widget grequest, Widget gnew,
1998          ArgList args, Cardinal * num_args)
1999{
2000    ClockWidget current = (ClockWidget) gcurrent;
2001    ClockWidget new = (ClockWidget) gnew;
2002    Boolean redisplay = FALSE;
2003    XtGCMask valuemask;
2004    XGCValues myXGCV;
2005
2006    /* first check for changes to clock-specific resources.  We'll accept all
2007       the changes, but may need to do some computations first. */
2008
2009    if (new->clock.update != current->clock.update) {
2010        if (current->clock.interval_id)
2011            XtRemoveTimeOut(current->clock.interval_id);
2012        if (new->clock.update && XtIsRealized((Widget) new))
2013            new->clock.interval_id =
2014                XtAppAddTimeOut(XtWidgetToApplicationContext(gnew),
2015                                fabsf(new->clock.update) * 1000, clock_tic,
2016                                (XtPointer) gnew);
2017
2018        new->clock.show_second_hand =
2019            (fabsf(new->clock.update) <= SECOND_HAND_TIME);
2020        if (new->clock.show_second_hand != current->clock.show_second_hand)
2021            redisplay = TRUE;
2022    }
2023
2024    if (new->clock.padding != current->clock.padding)
2025        redisplay = TRUE;
2026
2027    if (new->clock.analog != current->clock.analog)
2028        redisplay = TRUE;
2029
2030    if (new->clock.font != current->clock.font)
2031        redisplay = TRUE;
2032
2033#ifndef NO_I18N
2034    if (new->clock.fontSet != current->clock.fontSet)
2035        redisplay = TRUE;
2036#endif
2037
2038    if ((ClockFgPixel(new) != ClockFgPixel(current))
2039        || (new->core.background_pixel != current->core.background_pixel)) {
2040        valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
2041        myXGCV.foreground = ClockFgPixel(new);
2042        myXGCV.background = new->core.background_pixel;
2043        myXGCV.font = new->clock.font->fid;
2044        myXGCV.line_width = 0;
2045        XtReleaseGC(gcurrent, current->clock.myGC);
2046        new->clock.myGC = XtGetGC(gcurrent, valuemask, &myXGCV);
2047        redisplay = TRUE;
2048    }
2049
2050    if (new->clock.Hipixel != current->clock.Hipixel) {
2051        valuemask = GCForeground | GCLineWidth;
2052        myXGCV.foreground = new->clock.Hipixel;
2053        myXGCV.font = new->clock.font->fid;
2054        myXGCV.line_width = 0;
2055        XtReleaseGC(gcurrent, current->clock.HighGC);
2056        new->clock.HighGC = XtGetGC((Widget) gcurrent, valuemask, &myXGCV);
2057        redisplay = TRUE;
2058    }
2059
2060    if (new->clock.Hdpixel != current->clock.Hdpixel) {
2061        valuemask = GCForeground;
2062        myXGCV.foreground = new->clock.Hdpixel;
2063        XtReleaseGC(gcurrent, current->clock.HandGC);
2064        new->clock.HandGC = XtGetGC((Widget) gcurrent, valuemask, &myXGCV);
2065        redisplay = TRUE;
2066    }
2067
2068    if (new->core.background_pixel != current->core.background_pixel) {
2069        valuemask = GCForeground | GCLineWidth | GCGraphicsExposures;
2070        myXGCV.foreground = new->core.background_pixel;
2071        myXGCV.line_width = 0;
2072        myXGCV.graphics_exposures = False;
2073        XtReleaseGC(gcurrent, current->clock.EraseGC);
2074        new->clock.EraseGC = XtGetGC((Widget) gcurrent, valuemask, &myXGCV);
2075        redisplay = TRUE;
2076    }
2077#ifdef XRENDER
2078    if (new->clock.face != current->clock.face)
2079        redisplay = TRUE;
2080    if (!sameColor(&new->clock.hour_color, &current->clock.fg_color) ||
2081        !sameColor(&new->clock.hour_color, &current->clock.hour_color) ||
2082        !sameColor(&new->clock.min_color, &current->clock.min_color) ||
2083        !sameColor(&new->clock.sec_color, &current->clock.sec_color) ||
2084        !sameColor(&new->clock.major_color, &current->clock.major_color) ||
2085        !sameColor(&new->clock.minor_color, &current->clock.minor_color))
2086        redisplay = True;
2087    if (new->clock.sharp != current->clock.sharp)
2088        redisplay = True;
2089    if (new->clock.render != current->clock.render)
2090        redisplay = True;
2091    if (new->clock.buffer != current->clock.buffer) {
2092        if (new->clock.pixmap) {
2093            XFreePixmap(XtDisplay(new), new->clock.pixmap);
2094            new->clock.pixmap = 0;
2095        }
2096        if (new->clock.draw) {
2097            XftDrawDestroy(new->clock.draw);
2098            new->clock.draw = NULL;
2099        }
2100        new->clock.picture = 0;
2101    }
2102#endif
2103    return (redisplay);
2104
2105}
2106
2107#if !defined(NO_I18N) && defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2108static char *
2109clock_to_utf8(const char *str, int in_len)
2110{
2111    iconv_t cd;
2112    char *buf;
2113    size_t buf_size;
2114    size_t ileft, oleft;
2115    ICONV_CONST char *inptr;
2116    char *outptr;
2117    size_t ret;
2118    const char *code_set = nl_langinfo(CODESET);
2119
2120    if (str == NULL || code_set == NULL || strcasecmp(code_set, "646") == 0)
2121        return NULL;
2122
2123    if (strcasecmp(code_set, "UTF-8") == 0)
2124        return strdup(str);
2125
2126    cd = iconv_open("UTF-8", code_set);
2127    if (cd == (iconv_t) - 1)
2128        return NULL;
2129
2130    buf_size = MB_LEN_MAX * (in_len + 1);
2131    if ((buf = malloc(buf_size)) == NULL) {
2132        (void) iconv_close(cd);
2133        return NULL;
2134    }
2135
2136    inptr = str;
2137    ileft = in_len;
2138    outptr = buf;
2139    oleft = buf_size;
2140
2141    ret = iconv(cd, &inptr, &ileft, &outptr, &oleft);
2142    if (ret == (size_t) (-1) || oleft == 0) {
2143        free(buf);
2144        buf = NULL;
2145    }
2146    else
2147        *outptr = '\0';
2148
2149    (void) iconv_close(cd);
2150    return buf;
2151}
2152#endif
2153