diff options
Diffstat (limited to 'apps/xclock/Clock.c')
-rw-r--r-- | apps/xclock/Clock.c | 2263 |
1 files changed, 2263 insertions, 0 deletions
diff --git a/apps/xclock/Clock.c b/apps/xclock/Clock.c new file mode 100644 index 000000000..cbb0e4208 --- /dev/null +++ b/apps/xclock/Clock.c @@ -0,0 +1,2263 @@ +/* $Xorg: Clock.c,v 1.4 2001/02/09 02:05:39 xorgcvs Exp $ */ +/* $XdotOrg: xc/programs/xclock/Clock.c,v 1.3 2004/10/30 20:33:44 alanc Exp $ */ + +/*********************************************************** + +Copyright 1987, 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ +/* + * Copyright 2004 Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR + * LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, + * MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. + * IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, + * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, + * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE + * THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE + * SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * Authors: I18N - Steve Swales - March 2000 + * bgpixmap - Alan Coopersmith (as part of STSF project) - Sept. 2001 + */ +/* $XFree86: xc/programs/xclock/Clock.c,v 3.25 2003/07/04 16:24:30 eich Exp $ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <X11/Xlib.h> +#include <X11/StringDefs.h> +#include <X11/IntrinsicP.h> +#include "ClockP.h" +#include <X11/Xosdefs.h> +#include <stdio.h> +#include <X11/Xos.h> +#include <X11/Xaw/XawInit.h> +#if !defined(NO_I18N) && defined(HAVE_ICONV) +#include <iconv.h> +#include <langinfo.h> +#include <errno.h> +#include <limits.h> +#endif + +#if defined(XawVersion) && (XawVersion >= 7000002L) +#define USE_XAW_PIXMAP_CVT +#else +#include <X11/xpm.h> +#endif + +#include <time.h> +#define Time_t time_t + +#ifdef XKB +#include <X11/extensions/XKBbells.h> +#endif + +#ifndef NO_I18N +#include <stdlib.h> /* for getenv() */ +#include <locale.h> +extern Boolean no_locale; /* if True, use old (unlocalized) behaviour */ +#endif + + +/* Private Definitions */ + +#define VERTICES_IN_HANDS 6 /* to draw triangle */ +#define PI 3.14159265358979 +#define TWOPI (2. * PI) + +#define MINOR_TICK_FRACT 95 +#define SECOND_HAND_FRACT 90 +#define MINUTE_HAND_FRACT 70 +#define HOUR_HAND_FRACT 40 +#define HAND_WIDTH_FRACT 7 +#define SECOND_WIDTH_FRACT 5 +#define SECOND_HAND_TIME 30 + +#define ANALOG_SIZE_DEFAULT 164 + +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) +/* #define abs(a) ((a) < 0 ? -(a) : (a)) */ + + +/* Initialization of defaults */ + +#define offset(field) XtOffsetOf(ClockRec, clock.field) +#define goffset(field) XtOffsetOf(WidgetRec, core.field) + +static XtResource resources[] = { + {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension), + goffset(width), XtRImmediate, (XtPointer) 0}, + {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension), + goffset(height), XtRImmediate, (XtPointer) 0}, + {XtNupdate, XtCInterval, XtRInt, sizeof(int), + offset(update), XtRImmediate, (XtPointer) 60 }, +#ifndef XRENDER + {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + offset(fgpixel), XtRString, XtDefaultForeground}, +#endif + {XtNhand, XtCForeground, XtRPixel, sizeof(Pixel), + offset(Hdpixel), XtRString, XtDefaultForeground}, + {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel), + offset(Hipixel), XtRString, XtDefaultForeground}, + {XtNutime, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(utime), XtRImmediate, (XtPointer) FALSE}, + {XtNanalog, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(analog), XtRImmediate, (XtPointer) TRUE}, + {XtNtwentyfour, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(twentyfour), XtRImmediate, (XtPointer) TRUE}, + {XtNbrief, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(brief), XtRImmediate, (XtPointer) FALSE}, + {XtNstrftime, XtCString, XtRString, sizeof(String), + offset(strftime), XtRString, ""}, + {XtNchime, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(chime), XtRImmediate, (XtPointer) FALSE }, + {XtNpadding, XtCMargin, XtRInt, sizeof(int), + offset(padding), XtRImmediate, (XtPointer) 8}, + {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), + offset(font), XtRString, XtDefaultFont}, +#ifndef NO_I18N + {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet), + offset(fontSet), XtRString, XtDefaultFontSet}, +#endif + {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof (int), + offset (backing_store), XtRString, "default"}, +#ifdef XRENDER + {XtNrender, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(render), XtRImmediate, (XtPointer) TRUE }, + {XtNbuffer, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(buffer), XtRImmediate, (XtPointer) TRUE }, + {XtNsharp, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(sharp), XtRImmediate, (XtPointer) FALSE }, + {XtNforeground, XtCForeground, XtRXftColor, sizeof(XftColor), + offset(fg_color), XtRString, XtDefaultForeground}, + {XtNhourColor, XtCForeground, XtRXftColor, sizeof(XftColor), + offset(hour_color), XtRString, XtDefaultForeground}, + {XtNminuteColor, XtCForeground, XtRXftColor, sizeof(XftColor), + offset(min_color), XtRString, XtDefaultForeground}, + {XtNsecondColor, XtCForeground, XtRXftColor, sizeof(XftColor), + offset(sec_color), XtRString, XtDefaultForeground}, + {XtNmajorColor, XtCForeground, XtRXftColor, sizeof(XftColor), + offset(major_color), XtRString, XtDefaultForeground}, + {XtNminorColor, XtCForeground, XtRXftColor, sizeof(XftColor), + offset(minor_color), XtRString, XtDefaultForeground}, + {XtNface, XtCFace, XtRXftFont, sizeof (XftFont *), + offset (face), XtRString, ""}, +#endif +}; + +#undef offset +#undef goffset + +static void ClassInitialize ( void ); +static void Initialize ( Widget request, Widget new, ArgList args, + Cardinal *num_args ); +static void Realize ( Widget gw, XtValueMask *valueMask, + XSetWindowAttributes *attrs ); +static void Destroy ( Widget gw ); +static void Resize ( Widget gw ); +static void Redisplay ( Widget gw, XEvent *event, Region region ); +static void clock_tic ( XtPointer client_data, XtIntervalId *id ); +static void erase_hands ( ClockWidget w, struct tm *tm ); +static void ClockAngle ( int tick_units, double *sinp, double *cosp ); +static void DrawLine ( ClockWidget w, Dimension blank_length, + Dimension length, int tick_units ); +static void DrawHand ( ClockWidget w, Dimension length, Dimension width, + int tick_units ); +static void DrawSecond ( ClockWidget w, Dimension length, Dimension width, + Dimension offset, int tick_units ); +static void SetSeg ( ClockWidget w, int x1, int y1, int x2, int y2 ); +static void DrawClockFace ( ClockWidget w ); +static int clock_round ( double x ); +static Boolean SetValues ( Widget gcurrent, Widget grequest, Widget gnew, + ArgList args, Cardinal *num_args ); +#if !defined(NO_I18N) && defined(HAVE_ICONV) +static char *clock_to_utf8(const char *str, int in_len); +#endif + +ClockClassRec clockClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &simpleClassRec, + /* class_name */ "Clock", + /* widget_size */ sizeof(ClockRec), + /* class_initialize */ ClassInitialize, + /* class_part_initialize */ NULL, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ Realize, + /* actions */ NULL, + /* num_actions */ 0, + /* resources */ resources, + /* resource_count */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ XtExposeCompressMaximal, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ Destroy, + /* resize */ Resize, + /* expose */ Redisplay, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ NULL, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* simple fields */ + /* change_sensitive */ XtInheritChangeSensitive + }, + { /* clock fields */ + /* ignore */ 0 + } +}; + +WidgetClass clockWidgetClass = (WidgetClass) &clockClassRec; + +/**************************************************************** + * + * Private Procedures + * + ****************************************************************/ +#ifndef USE_XAW_PIXMAP_CVT +static void CvtStringToPixmap( + XrmValue* args, + Cardinal* num_args, + XrmValuePtr fromVal, + XrmValuePtr toVal + ) +{ + static Pixmap pmap; + Pixmap shapemask; + char *name = (char *)fromVal->addr; + Screen *screen; + Display *dpy; + + if (*num_args != 1) + XtErrorMsg("wrongParameters","cvtStringToPixmap","XtToolkitError", + "String to pixmap conversion needs screen argument", + (String *)NULL, (Cardinal *)NULL); + + if (strcmp(name, "None") == 0) { + pmap = None; + } else { + screen = *((Screen **) args[0].addr); + dpy = DisplayOfScreen(screen); + + XpmReadFileToPixmap(dpy, RootWindowOfScreen(screen), name, &pmap, + &shapemask, NULL); + } + + (*toVal).size = sizeof(Pixmap); + (*toVal).addr = (XPointer) &pmap ; +} +#endif + +#ifdef XRENDER +static XtConvertArgRec xftColorConvertArgs[] = { + {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen), + sizeof(Screen *)}, + {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.colormap), + sizeof(Colormap)} +}; + +#define donestr(type, value, tstr) \ + { \ + if (toVal->addr != NULL) { \ + if (toVal->size < sizeof(type)) { \ + toVal->size = sizeof(type); \ + XtDisplayStringConversionWarning(dpy, \ + (char*) fromVal->addr, tstr); \ + return False; \ + } \ + *(type*)(toVal->addr) = (value); \ + } \ + else { \ + static type static_val; \ + static_val = (value); \ + toVal->addr = (XPointer)&static_val; \ + } \ + toVal->size = sizeof(type); \ + return True; \ + } + +static void +XmuFreeXftColor (XtAppContext app, XrmValuePtr toVal, XtPointer closure, + XrmValuePtr args, Cardinal *num_args) +{ + Screen *screen; + Colormap colormap; + XftColor *color; + + if (*num_args != 2) + { + XtAppErrorMsg (app, + "freeXftColor", "wrongParameters", + "XtToolkitError", + "Freeing an XftColor requires screen and colormap arguments", + (String *) NULL, (Cardinal *)NULL); + return; + } + + screen = *((Screen **) args[0].addr); + colormap = *((Colormap *) args[1].addr); + color = (XftColor *) toVal->addr; + XftColorFree (DisplayOfScreen (screen), + DefaultVisual (DisplayOfScreen (screen), + XScreenNumberOfScreen (screen)), + colormap, color); +} + +static Boolean +XmuCvtStringToXftColor(Display *dpy, + XrmValue *args, Cardinal *num_args, + XrmValue *fromVal, XrmValue *toVal, + XtPointer *converter_data) +{ + char *spec; + XRenderColor renderColor; + XftColor xftColor; + Screen *screen; + Colormap colormap; + + if (*num_args != 2) + { + XtAppErrorMsg (XtDisplayToApplicationContext (dpy), + "cvtStringToXftColor", "wrongParameters", + "XtToolkitError", + "String to render color conversion needs screen and colormap arguments", + (String *) NULL, (Cardinal *)NULL); + return False; + } + + screen = *((Screen **) args[0].addr); + colormap = *((Colormap *) args[1].addr); + + spec = (char *) fromVal->addr; + if (strcasecmp (spec, XtDefaultForeground) == 0) + { + renderColor.red = 0; + renderColor.green = 0; + renderColor.blue = 0; + renderColor.alpha = 0xffff; + } + else if (strcasecmp (spec, XtDefaultBackground) == 0) + { + renderColor.red = 0xffff; + renderColor.green = 0xffff; + renderColor.blue = 0xffff; + renderColor.alpha = 0xffff; + } + else if (!XRenderParseColor (dpy, spec, &renderColor)) + return False; + if (!XftColorAllocValue (dpy, + DefaultVisual (dpy, + XScreenNumberOfScreen (screen)), + colormap, + &renderColor, + &xftColor)) + return False; + + donestr (XftColor, xftColor, XtRXftColor); +} + +static void +XmuFreeXftFont (XtAppContext app, XrmValuePtr toVal, XtPointer closure, + XrmValuePtr args, Cardinal *num_args) +{ + Screen *screen; + XftFont *font; + + if (*num_args != 1) + { + XtAppErrorMsg (app, + "freeXftFont", "wrongParameters", + "XtToolkitError", + "Freeing an XftFont requires screen argument", + (String *) NULL, (Cardinal *)NULL); + return; + } + + screen = *((Screen **) args[0].addr); + font = *((XftFont **) toVal->addr); + if (font) + XftFontClose (DisplayOfScreen (screen), font); +} + +static Boolean +XmuCvtStringToXftFont(Display *dpy, + XrmValue *args, Cardinal *num_args, + XrmValue *fromVal, XrmValue *toVal, + XtPointer *converter_data) +{ + char *name; + XftFont *font; + Screen *screen; + + if (*num_args != 1) + { + XtAppErrorMsg (XtDisplayToApplicationContext (dpy), + "cvtStringToXftFont", "wrongParameters", + "XtToolkitError", + "String to XftFont conversion needs screen argument", + (String *) NULL, (Cardinal *)NULL); + return False; + } + + screen = *((Screen **) args[0].addr); + name = (char *) fromVal->addr; + + font = XftFontOpenName (dpy, + XScreenNumberOfScreen (screen), + name); + if (font) + { + donestr (XftFont *, font, XtRXftFont); + } + XtDisplayStringConversionWarning(dpy, (char *) fromVal->addr, XtRXftFont); + return False; +} + +static XtConvertArgRec xftFontConvertArgs[] = { + {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen), + sizeof(Screen *)}, +}; + +#endif + +static void +ClassInitialize(void) +{ +#ifdef USE_XAW_PIXMAP_CVT + XawInitializeWidgetSet(); +#else + static XtConvertArgRec scrnConvertArg[] = { + {XtBaseOffset, (XtPointer) XtOffset(Widget, core.screen), + sizeof(Screen *)} + }; + XtAddConverter( XtRString, XtRPixmap, CvtStringToPixmap, + scrnConvertArg, XtNumber(scrnConvertArg)); +#endif + XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore, + NULL, 0 ); +#ifdef XRENDER + XtSetTypeConverter (XtRString, XtRXftColor, + XmuCvtStringToXftColor, + xftColorConvertArgs, XtNumber(xftColorConvertArgs), + XtCacheByDisplay, XmuFreeXftColor); + XtSetTypeConverter (XtRString, XtRXftFont, + XmuCvtStringToXftFont, + xftFontConvertArgs, XtNumber(xftFontConvertArgs), + XtCacheByDisplay, XmuFreeXftFont); +#endif +} + +static char * +TimeString (ClockWidget w, struct tm *tm) +{ + if (w->clock.brief) + { + if (w->clock.twentyfour) + { + static char brief[6]; + sprintf (brief, "%02d:%02d", tm->tm_hour, tm->tm_min); + return brief; + } + else + { + static char brief[9]; + int hour = tm->tm_hour % 12; + if (!hour) hour = 12; + sprintf (brief, "%02d:%02d %cM", hour, tm->tm_min, + tm->tm_hour >= 12 ? 'P' : 'A'); + return brief; + } + } + else if (w->clock.utime) + { + static char utime[35]; + Time_t tsec; + tsec = time(NULL); + sprintf (utime, "%10lu seconds since Epoch", (unsigned long)tsec); + return utime; + } else if (*w->clock.strftime) { + /*Note: this code is probably excessively paranoid + about buffer overflow. The extra size 10 padding + is also meant as a further guard against programmer + error, although it is a little controversial*/ + static char ctime[STRFTIME_BUFF_SIZE+10]; + ctime[0] = ctime[STRFTIME_BUFF_SIZE] = '\0'; + if (0 < strftime (ctime, STRFTIME_BUFF_SIZE-1,w->clock.strftime, tm)) { + ctime[STRFTIME_BUFF_SIZE-1] = '\0'; + return ctime; + } else { + return asctime (tm); + } + } + else if (w->clock.twentyfour) + return asctime (tm); + else + { + static char long12[28]; + strftime(long12, sizeof long12, "%a %b %d %I:%M:%S %p %Y", tm); + return long12; + } +} + +/* ARGSUSED */ +static void +Initialize (Widget request, Widget new, ArgList args, Cardinal *num_args) +{ + ClockWidget w = (ClockWidget)new; + XtGCMask valuemask; + XGCValues myXGCV; + int min_height, min_width; + + valuemask = GCForeground | GCBackground | GCFont | GCLineWidth; + if (w->clock.font != NULL) + myXGCV.font = w->clock.font->fid; + else + valuemask &= ~GCFont; /* use server default font */ + + min_width = min_height = ANALOG_SIZE_DEFAULT; + if(!w->clock.analog) { + char *str; + struct tm tm; + Time_t time_value; + int len; + +#ifndef NO_I18N + w->clock.utf8 = False; + + if (!no_locale) { + char *time_locale = setlocale(LC_TIME, NULL); + + if (strstr(time_locale, "UTF-8") || strstr(time_locale, "utf8")) { + w->clock.utf8 = True; + } + + /* + * initialize time format from CFTIME if set, otherwise + * default to "%c". This emulates ascftime, but we use + * strftime so we can limit the string buffer size to + * avoid possible buffer overflow. + */ + if ((w->clock.strftime == NULL) || (w->clock.strftime[0] == 0)) { + w->clock.strftime = getenv("CFTIME"); + if (w->clock.strftime == NULL) { + w->clock.strftime = "%c"; + } + } + } +#endif /* NO_I18N */ + + (void) time(&time_value); + tm = *localtime(&time_value); + str = TimeString (w, &tm); + len = strlen(str); + if (len && str[len - 1] == '\n') str[--len] = '\0'; + +#ifdef XRENDER + if (w->clock.render) + { + XGlyphInfo extents; +#ifndef NO_I18N +# ifdef HAVE_ICONV + char *utf8_str; +# endif + if (w->clock.utf8) + XftTextExtentsUtf8 (XtDisplay (w), w->clock.face, + (FcChar8 *) str, len, &extents); +# ifdef HAVE_ICONV + else if ((utf8_str = clock_to_utf8(str, len)) != NULL) { + XftTextExtentsUtf8 (XtDisplay (w), w->clock.face, + (FcChar8 *)utf8_str, strlen(utf8_str), &extents); + free(utf8_str); + } +# endif + else +#endif + XftTextExtents8 (XtDisplay (w), w->clock.face, + (FcChar8 *) str, len, &extents); + min_width = extents.xOff + 2 * w->clock.padding; + min_height = w->clock.face->ascent + w->clock.face->descent + + 2 * w->clock.padding; + /*fprintf(stderr, "render min_width %i\n", min_width);*/ + } + else +#endif + { /* not XRENDER block */ +#ifndef NO_I18N + if (!no_locale) { + XFontSetExtents *fse; + + if(w->clock.fontSet == NULL) { + char **missing, *default_str; + int n_missing; + w->clock.fontSet = XCreateFontSet( XtDisplay(w), + XtDefaultFontSet, + &missing, + &n_missing, + &default_str); + } + if (w->clock.fontSet != NULL) { + /* don't free this... it's freed with the XFontSet. */ + fse = XExtentsOfFontSet(w->clock.fontSet); + + min_width = XmbTextEscapement(w->clock.fontSet,str,len) + + 2 * w->clock.padding; + min_height = fse->max_logical_extent.height + + 3 * w->clock.padding; + /*fprintf(stderr, "fontset min_width %i\n", min_width);*/ + } else { + no_locale = True; + } + } + + if (no_locale) +#endif /* NO_I18N */ + { + if (w->clock.font == NULL) + w->clock.font = XQueryFont( XtDisplay(w), + XGContextFromGC( + DefaultGCOfScreen(XtScreen(w))) ); + min_width = XTextWidth(w->clock.font, str, len) + + 2 * w->clock.padding; + min_height = w->clock.font->ascent + + w->clock.font->descent + 2 * w->clock.padding; + /*fprintf(stderr, "font min_width %i\n", min_width);*/ + } + } /* not XRENDER block */ + } + if (w->core.width == 0) + w->core.width = min_width; + if (w->core.height == 0) + w->core.height = min_height; + + myXGCV.foreground = ClockFgPixel (w); + myXGCV.background = w->core.background_pixel; + if (w->clock.font != NULL) + myXGCV.font = w->clock.font->fid; + else + valuemask &= ~GCFont; /* use server default font */ + myXGCV.line_width = 0; + w->clock.myGC = XtGetGC((Widget)w, valuemask, &myXGCV); + + valuemask = GCForeground | GCLineWidth | GCGraphicsExposures; + myXGCV.foreground = w->core.background_pixel; + if (w->core.background_pixmap != XtUnspecifiedPixmap) { + myXGCV.tile = w->core.background_pixmap; + myXGCV.fill_style = FillTiled; + valuemask |= (GCTile | GCFillStyle); + } + myXGCV.graphics_exposures = False; + w->clock.EraseGC = XtGetGC((Widget)w, valuemask, &myXGCV); + valuemask &= ~(GCTile | GCFillStyle); + + myXGCV.foreground = w->clock.Hipixel; + w->clock.HighGC = XtGetGC((Widget)w, valuemask, &myXGCV); + + valuemask = GCForeground; + myXGCV.foreground = w->clock.Hdpixel; + w->clock.HandGC = XtGetGC((Widget)w, valuemask, &myXGCV); + + /* make invalid update's use a default */ + /*if (w->clock.update <= 0) w->clock.update = 60;*/ + w->clock.show_second_hand = (abs(w->clock.update) <= SECOND_HAND_TIME); + w->clock.numseg = 0; + w->clock.interval_id = 0; + memset (&w->clock.otm, '\0', sizeof (w->clock.otm)); +#ifdef XRENDER + { + int major, minor; + + if (XRenderQueryVersion (XtDisplay (w), &major, &minor) && + (major > 0 || + (major == 0 && minor >= 4))) + { + w->clock.can_polygon = True; + } + else + w->clock.can_polygon = False; + } + w->clock.pixmap = 0; + w->clock.draw = NULL; + w->clock.damage.x = 0; + w->clock.damage.y = 0; + w->clock.damage.height = 0; + w->clock.damage.width = 0; +#endif +} + +#ifdef XRENDER +static void +RenderPrepare (ClockWidget w, XftColor *color) +{ + if (!w->clock.draw) + { + Drawable d = XtWindow (w); + if (w->clock.buffer) + { + if (!w->clock.pixmap) + { + Arg arg[1]; + w->clock.pixmap = XCreatePixmap (XtDisplay (w), d, + w->core.width, + w->core.height, + w->core.depth); + arg[0].name = XtNbackgroundPixmap; + arg[0].value = 0; + XtSetValues ((Widget) w, arg, 1); + } + d = w->clock.pixmap; + } + + w->clock.draw = XftDrawCreate (XtDisplay (w), d, + DefaultVisual (XtDisplay (w), + DefaultScreen(XtDisplay (w))), + w->core.colormap); + w->clock.picture = XftDrawPicture (w->clock.draw); + } + if (color) + w->clock.fill_picture = XftDrawSrcPicture (w->clock.draw, color); +} + +static void +RenderClip (ClockWidget w) +{ + Region r; + Drawable d; + + RenderPrepare (w, NULL); + if (w->clock.buffer) + d = w->clock.pixmap; + else + d = XtWindow (w); + XFillRectangle (XtDisplay (w), d, w->clock.EraseGC, + w->clock.damage.x, + w->clock.damage.y, + w->clock.damage.width, + w->clock.damage.height); + r = XCreateRegion (); + XUnionRectWithRegion (&w->clock.damage, + r, r); + XftDrawSetClip (w->clock.draw, r); + XDestroyRegion (r); +} + +static void +RenderTextBounds (ClockWidget w, char *str, int off, int len, + XRectangle *bounds, int *xp, int *yp) +{ + XGlyphInfo head, tail; + int x, y; + +#ifndef NO_I18N +# ifdef HAVE_ICONV + char *utf8_str; +# endif + if (w->clock.utf8) + { + XftTextExtentsUtf8 (XtDisplay (w), w->clock.face, + (FcChar8 *) str, off, &head); + XftTextExtentsUtf8 (XtDisplay (w), w->clock.face, + (FcChar8 *) str + off, len - off, &tail); + } +# ifdef HAVE_ICONV + else if ((utf8_str = clock_to_utf8(str, off)) != NULL) + { + XftTextExtentsUtf8 (XtDisplay (w), w->clock.face, + (FcChar8 *)utf8_str, strlen(utf8_str), &head); + free(utf8_str); + if ((utf8_str = clock_to_utf8(str+off, len-off)) != NULL) { + XftTextExtentsUtf8 (XtDisplay (w), w->clock.face, + (FcChar8 *)utf8_str, strlen(utf8_str), &tail); + free(utf8_str); + } else + goto fallback; + } +# endif + else +#endif + { + fallback: + XftTextExtents8 (XtDisplay (w), w->clock.face, (FcChar8 *) str, + off, &head); + XftTextExtents8 (XtDisplay (w), w->clock.face, (FcChar8 *) str + off, + len - off, &tail); + } + + /* + * Compute position of tail + */ + x = w->clock.padding + head.xOff; + y = w->clock.face->ascent + w->clock.padding + head.yOff; + /* + * Compute bounds of tail, pad a bit as the bounds aren't exact + */ + bounds->x = x - tail.x - 1; + bounds->y = y - tail.y - 1; + bounds->width = tail.width + 2; + bounds->height = tail.height + 2; + if (xp) *xp = x; + if (yp) *yp = y; +} + +static void +RenderUpdateRectBounds (XRectangle *damage, XRectangle *bounds) +{ + int x1 = bounds->x; + int y1 = bounds->y; + int x2 = bounds->x + bounds->width; + int y2 = bounds->y + bounds->height; + int d_x1 = damage->x; + int d_y1 = damage->y; + int d_x2 = damage->x + damage->width; + int d_y2 = damage->y + damage->height; + + if (x1 == x2) + { + x1 = d_x1; + x2 = d_x2; + } + else + { + if (d_x1 < x1) x1 = d_x1; + if (d_x2 > x2) x2 = d_x2; + } + if (y1 == y2) + { + y1 = d_y1; + y2 = d_y2; + } + else + { + if (d_y1 < y1) y1 = d_y1; + if (d_y2 > y2) y2 = d_y2; + } + + bounds->x = x1; + bounds->y = y1; + bounds->width = x2 - x1; + bounds->height = y2 - y1; +} + +static Boolean +RenderRectIn (XRectangle *rect, XRectangle *bounds) +{ + int x1 = bounds->x; + int y1 = bounds->y; + int x2 = bounds->x + bounds->width; + int y2 = bounds->y + bounds->height; + int r_x1 = rect->x; + int r_y1 = rect->y; + int r_x2 = rect->x + rect->width; + int r_y2 = rect->y + rect->height; + + return r_x1 < x2 && x1 < r_x2 && r_y1 < y2 && y1 < r_y2; +} + +#define LINE_WIDTH 0.01 +#include <math.h> + +#define XCoord(x,w) ((x) * (w)->clock.x_scale + (w)->clock.x_off) +#define YCoord(y,w) ((y) * (w)->clock.y_scale + (w)->clock.y_off) + +static void +RenderUpdateBounds (XPointDouble *points, int npoints, XRectangle *bounds) +{ + int x1 = bounds->x; + int y1 = bounds->y; + int x2 = bounds->x + bounds->width; + int y2 = bounds->y + bounds->height; + + while (npoints--) + { + int r_x1 = points[0].x; + int r_y1 = points[0].y; + int r_x2 = points[0].x + 1; + int r_y2 = points[0].y + 1; + + if (x1 == x2) + x2 = x1 = r_x1; + if (y1 == y2) + y2 = y1 = r_y1; + if (r_x1 < x1) x1 = r_x1; + if (r_y1 < y1) y1 = r_y1; + if (r_x2 > x2) x2 = r_x2; + if (r_y2 > y2) y2 = r_y2; + points++; + } + bounds->x = x1; + bounds->y = y1; + bounds->width = x2 - x1; + bounds->height = y2 - y1; +} + +static Boolean +RenderCheckBounds (XPointDouble *points, int npoints, XRectangle *bounds) +{ + int x1 = bounds->x; + int y1 = bounds->y; + int x2 = bounds->x + bounds->width; + int y2 = bounds->y + bounds->height; + + while (npoints--) + { + if (x1 <= points->x && points->x <= x2 && + y1 <= points->y && points->y <= y2) + return True; + points++; + } + return False; +} + +static void +RenderUpdate (ClockWidget w) +{ + if (w->clock.buffer && w->clock.pixmap) + { + XCopyArea (XtDisplay (w), w->clock.pixmap, + XtWindow (w), w->clock.EraseGC, + w->clock.damage.x, w->clock.damage.y, + w->clock.damage.width, w->clock.damage.height, + w->clock.damage.x, w->clock.damage.y); + } +} + +static void +RenderResetBounds (XRectangle *bounds) +{ + bounds->x = 0; + bounds->y = 0; + bounds->width = 0; + bounds->height = 0; +} + +static void +RenderLine (ClockWidget w, XDouble x1, XDouble y1, XDouble x2, XDouble y2, + XftColor *color, + Boolean draw) +{ + XPointDouble poly[4]; + XDouble dx = (x2 - x1); + XDouble dy = (y2 - y1); + XDouble len = sqrt (dx*dx + dy*dy); + XDouble ldx = (LINE_WIDTH/2.0) * dy / len; + XDouble ldy = (LINE_WIDTH/2.0) * dx / len; + + poly[0].x = XCoord (x1 + ldx, w); + poly[0].y = YCoord (y1 - ldy, w); + + poly[1].x = XCoord (x2 + ldx, w); + poly[1].y = YCoord (y2 - ldy, w); + + poly[2].x = XCoord (x2 - ldx, w); + poly[2].y = YCoord (y2 + ldy, w); + + poly[3].x = XCoord (x1 - ldx, w); + poly[3].y = YCoord (y1 + ldy, w); + + RenderUpdateBounds (poly, 4, &w->clock.damage); + if (draw) + { + if (RenderCheckBounds (poly, 4, &w->clock.damage)) + { + RenderPrepare (w, color); + XRenderCompositeDoublePoly (XtDisplay (w), + PictOpOver, + w->clock.fill_picture, + w->clock.picture, + w->clock.mask_format, + 0, 0, 0, 0, poly, 4, EvenOddRule); + } + } + else + RenderUpdateBounds (poly, 4, &w->clock.damage); +} + +static void +RenderRotate (ClockWidget w, XPointDouble *out, double x, double y, double s, double c) +{ + out->x = XCoord (x * c - y * s, w); + out->y = YCoord (y * c + x * s, w); +} + +static void +RenderHand (ClockWidget w, int tick_units, int size, XftColor *color, + Boolean draw) +{ + double c, s; + XPointDouble poly[3]; + double outer_x; + double inner_y; + + ClockAngle (tick_units, &c, &s); + s = -s; + + /* compute raw positions */ + outer_x = size / 100.0; + inner_y = HAND_WIDTH_FRACT / 100.0; + + /* rotate them into position */ + RenderRotate (w, &poly[0], outer_x, 0.0, s, c); + RenderRotate (w, &poly[1], -inner_y, inner_y, s, c); + RenderRotate (w, &poly[2], -inner_y, -inner_y, s, c); + + if (draw) + { + if (RenderCheckBounds (poly, 3, &w->clock.damage)) + { + RenderPrepare (w, color); + XRenderCompositeDoublePoly (XtDisplay (w), + PictOpOver, + w->clock.fill_picture, + w->clock.picture, + w->clock.mask_format, + 0, 0, 0, 0, poly, 3, EvenOddRule); + } + } + RenderUpdateBounds (poly, 3, &w->clock.damage); +} + +static void +RenderHands (ClockWidget w, struct tm *tm, Boolean draw) +{ + RenderHand (w, tm->tm_hour * 300 + tm->tm_min*5, HOUR_HAND_FRACT, &w->clock.hour_color, draw); + RenderHand (w, tm->tm_min * 60 + tm->tm_sec, MINUTE_HAND_FRACT, &w->clock.min_color, draw); +} + +static void +RenderSec (ClockWidget w, struct tm *tm, Boolean draw) +{ + double c, s; + XPointDouble poly[10]; + double inner_x, middle_x, outer_x, far_x; + double middle_y; + double line_y; + + ClockAngle (tm->tm_sec * 60, &c, &s); + + s = -s; + + /* + * Compute raw positions + */ + line_y = LINE_WIDTH; + inner_x = (MINUTE_HAND_FRACT / 100.0); + middle_x = ((SECOND_HAND_FRACT + MINUTE_HAND_FRACT) / 200.0); + outer_x = (SECOND_HAND_FRACT / 100.0); + far_x = (MINOR_TICK_FRACT / 100.0); + middle_y = (SECOND_WIDTH_FRACT / 100.0); + + /* + * Rotate them into position + */ + RenderRotate (w, &poly[0], -line_y, line_y, s, c); + RenderRotate (w, &poly[1], inner_x, line_y, s, c); + RenderRotate (w, &poly[2], middle_x, middle_y, s, c); + RenderRotate (w, &poly[3], outer_x, line_y, s, c); + RenderRotate (w, &poly[4], far_x, line_y, s, c); + RenderRotate (w, &poly[5], far_x, -line_y, s, c); + RenderRotate (w, &poly[6], outer_x, -line_y, s, c); + RenderRotate (w, &poly[7], middle_x, -middle_y, s, c); + RenderRotate (w, &poly[8], inner_x, -line_y, s, c); + RenderRotate (w, &poly[9], -line_y, -line_y, s, c); + + if (draw) + { + if (RenderCheckBounds (poly, 10, &w->clock.damage)) + { + RenderPrepare (w, &w->clock.sec_color); + XRenderCompositeDoublePoly (XtDisplay (w), + PictOpOver, + w->clock.fill_picture, + w->clock.picture, + w->clock.mask_format, + 0, 0, 0, 0, poly, 10, EvenOddRule); + } + } + else + { + RenderUpdateBounds (poly, 10, &w->clock.damage); + } +} + +#endif + +static void +Realize(Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs) +{ + ClockWidget w = (ClockWidget) gw; +#ifdef notdef + *valueMask |= CWBitGravity; + attrs->bit_gravity = ForgetGravity; +#endif + switch (w->clock.backing_store) { + case Always: + case NotUseful: + case WhenMapped: + *valueMask |=CWBackingStore; + attrs->backing_store = w->clock.backing_store; + break; + } + (*clockWidgetClass->core_class.superclass->core_class.realize) + (gw, valueMask, attrs); + Resize(gw); +} + +static void +Destroy(Widget gw) +{ + ClockWidget w = (ClockWidget) gw; + if (w->clock.interval_id) XtRemoveTimeOut (w->clock.interval_id); +#ifdef XRENDER + if (w->clock.picture) + XRenderFreePicture (XtDisplay(w), w->clock.picture); + if (w->clock.fill_picture) + XRenderFreePicture (XtDisplay(w), w->clock.fill_picture); +#endif + XtReleaseGC (gw, w->clock.myGC); + XtReleaseGC (gw, w->clock.HighGC); + XtReleaseGC (gw, w->clock.HandGC); + XtReleaseGC (gw, w->clock.EraseGC); +} + +static void +Resize(Widget gw) +{ + ClockWidget w = (ClockWidget) gw; + /* don't do this computation if window hasn't been realized yet. */ + if (XtIsRealized(gw) && w->clock.analog) { + /* need signed value since Dimension is unsigned */ + int radius = ((int) min(w->core.width, w->core.height) - (int) (2 * w->clock.padding)) / 2; + w->clock.radius = (Dimension) max (radius, 1); + + w->clock.second_hand_length = (int)(SECOND_HAND_FRACT * w->clock.radius) / 100; + w->clock.minute_hand_length = (int)(MINUTE_HAND_FRACT * w->clock.radius) / 100; + w->clock.hour_hand_length = (int)(HOUR_HAND_FRACT * w->clock.radius) / 100; + w->clock.hand_width = (int)(HAND_WIDTH_FRACT * w->clock.radius) / 100; + w->clock.second_hand_width = (int)(SECOND_WIDTH_FRACT * w->clock.radius) / 100; + + w->clock.centerX = w->core.width / 2; + w->clock.centerY = w->core.height / 2; + } +#ifdef XRENDER + w->clock.x_scale = 0.45 * w->core.width; + w->clock.y_scale = 0.45 * w->core.height; + w->clock.x_off = 0.5 * w->core.width; + w->clock.y_off = 0.5 * w->core.height; + if (w->clock.pixmap) + { + XFreePixmap (XtDisplay (w), w->clock.pixmap); + w->clock.pixmap = 0; + if (w->clock.draw) + { + XftDrawDestroy (w->clock.draw); + w->clock.draw = NULL; + } + w->clock.picture = 0; + } +#endif +} + +/* ARGSUSED */ +static void +Redisplay(Widget gw, XEvent *event, Region region) +{ + ClockWidget w = (ClockWidget) gw; + if (w->clock.analog) { +#ifdef XRENDER + if (w->clock.render && w->clock.can_polygon) + XClipBox (region, &w->clock.damage); + else +#endif + { + if (w->clock.numseg != 0) + erase_hands (w, (struct tm *) 0); + DrawClockFace(w); + } + } else { +#ifdef XRENDER + if (w->clock.render) + XClipBox (region, &w->clock.damage); +#endif + w->clock.prev_time_string[0] = '\0'; + } + clock_tic((XtPointer)w, (XtIntervalId *)NULL); +} + +/* Choose the update times for well-defined clock states. + * + * For example, in HH:MM:SS notation the last number rolls over + * every 60 seconds and has at most 60 display states. The sequence + * depends on its initial value t0 and the update period u, e.g. + * + * u (s) d (s) ti (s) m (states) l (s) + * 2 2 {0,2, .. 58} 30 60 + * 7 1 {0,7, .. 56,3, .. 53} 60 420 + * 15 15 {0,15,30,45} 4 60 + * 45 15 {0,45,30,15} 4 180 + * 53 1 {0,53,46, .. 4,57, .. 7} 60 3180 + * 58 2 {0,58,56, .. 2} 30 1740 + * 60 60 {0} 1 60 + * + * u= update period in seconds, + * ti= time at update i from the reference, HH:MM:00 or HH:00:00, + * n= the roll over time, the modulus, 60 s or 3600 s, + * m= the sequence length, the order of u in the modulo n group Z/nZ, + * l= the total sequence duration =m*u. + * d= gcd(n,u) the greatest common divisor + * + * The time t(i) determines the clock state. It follows from + * + * t(i)=t(i-1)+u mod n <=> t(i)=t(0)+i*u mod n + * + * which defines a { t(0) .. t(m-1) } sequence of m unique elements. + * Hence, u generates a subgroup U={k*u mod n : k in Z} of Z/nZ so + * its order m divides n. This m satisfies + * + * t(m)=t(0) <=> m*u mod n = 0 <=> m*u = r*n <=> m=n/d, r=u/d + * + * where d divides n and u. Choosing + * + * d=gcd(n,u) <=> n/d and u/d are coprime => m=n/d is minimum + * + * thus gives the order. Furthermore, the greatest common divisor d is + * also the minimum value generator of the set U. Assume a generator e + * where + * + * e|{n,u} <=> Ai,Ej: i*u mod n = j*e <=> j=f(i)=(i*u mod n)/e + * + * such that f maps i to m=ord(u) unique values of j. Its properties are + * + * j=i*u/e mod n/e ==> 0<=j<n/e + * + * ord(u/e, mod n/e)=n/e/gcd(n/e,u/e)=n/d=m ==> J={j=f(i)}, |J|=m + * + * ord(e)=n/gcd(n,e)=n/e + * + * from wich follows + * + * e=d ==> f: I={0,..,m-1} -> J={0,..,m-1}: j=i*r mod m is bijective + * ==> D={k*d mod n : k in Z} = U. + * + * Any value e below d is no generator since it yields a non contiguous + * J such that an l=0..n/e-1 exists not in J with l*e not in U. + * + * The update sequence t(i) could be followed using the algorithm: + * + * 1. restore the expected value into t(i), + * 2. calculate the next timeout t(i+1)=t(i)+u mod n, + * 3. verify that the current tc(i) is between t(i)..t(i+1), + * 4. calculate the time to wait w=t(i+1)-tc(i) mod n, + * 5. store t(i+1), + * + * which implements state tracking. This approach doesn't work well + * since the set timeout w does not guarantee a next call at time + * t(i+1), e.g. due to progam sleeps, time adjustments, and leap + * seconds. A robust method should only rely on the current time + * tc(i) to identify t(i). The derivation above shows 2 options: + * + * 1. n={60,3600} and round to a multiple of d, + * but if d<u then the sequence is not guaranteed. + * 2. choose n large and round to a multiple of u, + * but then the sequence resets at roll-over. + * + * The code below implements (2) with n this year's duration in seconds + * and using local time year's start as epoch. + */ +static unsigned long +waittime(int update, struct timeval *tv, struct tm *tm) +{ + int twait; + long twaitms; + unsigned long retval; + + if(update>0) { + long tcur; + int trem; + + tcur=tm->tm_sec+60*(tm->tm_min+60*(tm->tm_hour+24*tm->tm_yday)); + /* ti=floor(tcur/u)*u, w=u-(tcur-ti), and tcur-ti==tcur % u */ + trem=tcur % update; + twait=update-trem; + } else { + twait=-update; + } + + if(tv->tv_usec>0) { + long usec; + twait--; + usec=1000000-tv->tv_usec; + twaitms=(usec+999)/1000; /* must round up to avoid zero retval */ + } else { + twaitms=0; + } + + retval=(unsigned long)labs(twaitms+1000*twait); + return retval; +} + +/* ARGSUSED */ +static void +clock_tic(XtPointer client_data, XtIntervalId *id) +{ + ClockWidget w = (ClockWidget)client_data; + struct tm tm; + Time_t time_value; + struct timeval tv; + char *time_ptr; + register Display *dpy = XtDisplay(w); + register Window win = XtWindow(w); + + X_GETTIMEOFDAY (&tv); + time_value = tv.tv_sec; + tm = *localtime(&time_value); + if (w->clock.update && (id || !w->clock.interval_id)) + w->clock.interval_id = + XtAppAddTimeOut( XtWidgetToApplicationContext( (Widget) w), + waittime(w->clock.update, &tv, &tm), + clock_tic, (XtPointer)w ); + /* + * Beep on the half hour; double-beep on the hour. + */ + if (w->clock.chime == TRUE) { + if (w->clock.beeped && (tm.tm_min != 30) && + (tm.tm_min != 0)) + w->clock.beeped = FALSE; + if (((tm.tm_min == 30) || (tm.tm_min == 0)) + && (!w->clock.beeped)) { + w->clock.beeped = TRUE; +#ifdef XKB + if (tm.tm_min==0) { + XkbStdBell(dpy,win,50,XkbBI_ClockChimeHour); + XkbStdBell(dpy,win,50,XkbBI_RepeatingLastBell); + } + else { + XkbStdBell(dpy,win,50,XkbBI_ClockChimeHalf); + } +#else + XBell(dpy, 50); + if (tm.tm_min == 0) + XBell(dpy, 50); +#endif + } + } + if( w->clock.analog == FALSE ) { + int clear_from = w->core.width; + int i, len, prev_len; + + time_ptr = TimeString (w, &tm); + len = strlen (time_ptr); + if (len && time_ptr[len - 1] == '\n') time_ptr[--len] = '\0'; + prev_len = strlen (w->clock.prev_time_string); + for (i = 0; ((i < len) && (i < prev_len) && + (w->clock.prev_time_string[i] == time_ptr[i])); i++); + +#ifdef XRENDER + if (w->clock.render) + { + XRectangle old_tail, new_tail, head; + int x, y; +#if !defined(NO_I18N) && defined(HAVE_ICONV) + char *utf8_str; +#endif + + RenderTextBounds (w, w->clock.prev_time_string, i, prev_len, + &old_tail, NULL, NULL); + RenderUpdateRectBounds (&old_tail, &w->clock.damage); + RenderTextBounds (w, time_ptr, i, len, + &new_tail, NULL, NULL); + RenderUpdateRectBounds (&new_tail, &w->clock.damage); + + while (i) + { + RenderTextBounds (w, time_ptr, 0, i, &head, NULL, NULL); + if (!RenderRectIn (&head, &w->clock.damage)) + break; + i--; + } + RenderTextBounds (w, time_ptr, i, len, &new_tail, &x, &y); + RenderClip (w); + RenderPrepare (w, NULL); +#ifndef NO_I18N + if (w->clock.utf8) { + XftDrawStringUtf8 (w->clock.draw, + &w->clock.fg_color, + w->clock.face, + x, y, + (FcChar8 *) time_ptr + i, len - i); + + } +# ifdef HAVE_ICONV + else if ((utf8_str = + clock_to_utf8(time_ptr + i, len - i)) != NULL) { + XftDrawStringUtf8 (w->clock.draw, + &w->clock.fg_color, + w->clock.face, + x, y, + (FcChar8 *)utf8_str, strlen(utf8_str) ); + free(utf8_str); + } +# endif + else +#endif + { + XftDrawString8 (w->clock.draw, + &w->clock.fg_color, + w->clock.face, + x, y, + (FcChar8 *) time_ptr + i, len - i); + } + RenderUpdate (w); + RenderResetBounds (&w->clock.damage); + } + else +#endif +#ifndef NO_I18N + if(!no_locale) { + if(0 < len) { + XFontSetExtents *fse + = XExtentsOfFontSet(w->clock.fontSet); + + XmbDrawImageString(dpy,win,w->clock.fontSet,w->clock.myGC, + (2+w->clock.padding + + (i?XmbTextEscapement(w->clock.fontSet, + time_ptr,i):0)), + 2+w->clock.padding+fse->max_logical_extent.height, + time_ptr+i,len-i + ); + /* + * Clear any left over bits + */ + clear_from = XmbTextEscapement (w->clock.fontSet,time_ptr, + len) + 2+w->clock.padding; + } + } else +#endif /* NO_I18N */ + { + XDrawImageString (dpy, win, w->clock.myGC, + (1+w->clock.padding + + XTextWidth (w->clock.font, time_ptr, i)), + w->clock.font->ascent+w->clock.padding, + time_ptr + i, len - i); + /* + * Clear any left over bits + */ + clear_from = XTextWidth (w->clock.font, time_ptr, len) + + 2 + w->clock.padding; + } + if (clear_from < (int)w->core.width) + XClearArea (dpy, win, + clear_from, 0, w->core.width - clear_from, w->core.height, + False); +#if defined(HAS_STRLCAT) || defined(HAVE_STRLCPY) + strlcpy (w->clock.prev_time_string+i, time_ptr+i, + sizeof(w->clock.prev_time_string)-i); +#else + strncpy (w->clock.prev_time_string+i, time_ptr+i, + sizeof(w->clock.prev_time_string)-i); + w->clock.prev_time_string[sizeof(w->clock.prev_time_string)-1] = 0; +#endif + } else { + /* + * The second (or minute) hand is sec (or min) + * sixtieths around the clock face. The hour hand is + * (hour + min/60) twelfths of the way around the + * clock-face. The derivation is left as an excercise + * for the reader. + */ + + /* + * 12 hour clock. + */ + if(tm.tm_hour >= 12) + tm.tm_hour -= 12; + +#ifdef XRENDER + if (w->clock.render && w->clock.can_polygon) + { + w->clock.mask_format = XRenderFindStandardFormat (XtDisplay (w), + w->clock.sharp ? + PictStandardA1 : + PictStandardA8); + /* + * Compute repaint area + */ + if (tm.tm_min != w->clock.otm.tm_min || + tm.tm_hour != w->clock.otm.tm_hour || + tm.tm_sec != w->clock.otm.tm_sec) + { + RenderHands (w, &w->clock.otm, False); + RenderHands (w, &tm, False); + } + if (w->clock.show_second_hand && + tm.tm_sec != w->clock.otm.tm_sec) + { + RenderSec (w, &w->clock.otm, False); + RenderSec (w, &tm, False); + } + if (w->clock.damage.width && + w->clock.damage.height) + { + RenderClip (w); + DrawClockFace (w); + RenderHands (w, &tm, True); + if (w->clock.show_second_hand == TRUE) + RenderSec (w, &tm, True); + } + w->clock.otm = tm; + RenderUpdate (w); + RenderResetBounds (&w->clock.damage); + return; + } +#endif + + erase_hands (w, &tm); + + if (w->clock.numseg == 0 || + tm.tm_min != w->clock.otm.tm_min || + tm.tm_hour != w->clock.otm.tm_hour) { + w->clock.segbuffptr = w->clock.segbuff; + w->clock.numseg = 0; + /* + * Calculate the hour hand, fill it in with its + * color and then outline it. Next, do the same + * with the minute hand. This is a cheap hidden + * line algorithm. + */ + DrawHand(w, + w->clock.minute_hand_length, w->clock.hand_width, + tm.tm_min * 60 + ); + if(w->clock.Hdpixel != w->core.background_pixel) + XFillPolygon( dpy, + win, w->clock.HandGC, + w->clock.segbuff, VERTICES_IN_HANDS, + Convex, CoordModeOrigin + ); + XDrawLines( dpy, + win, w->clock.HighGC, + w->clock.segbuff, VERTICES_IN_HANDS, + CoordModeOrigin); + w->clock.hour = w->clock.segbuffptr; + DrawHand(w, + w->clock.hour_hand_length, w->clock.hand_width, + tm.tm_hour * 300 + tm.tm_min * 5 + ); + if(w->clock.Hdpixel != w->core.background_pixel) { + XFillPolygon(dpy, + win, w->clock.HandGC, + w->clock.hour, + VERTICES_IN_HANDS, + Convex, CoordModeOrigin + ); + } + XDrawLines( dpy, + win, w->clock.HighGC, + w->clock.hour, VERTICES_IN_HANDS, + CoordModeOrigin ); + + w->clock.sec = w->clock.segbuffptr; + } + if (w->clock.show_second_hand == TRUE) { + w->clock.segbuffptr = w->clock.sec; + DrawSecond(w, + w->clock.second_hand_length - 2, + w->clock.second_hand_width, + w->clock.minute_hand_length + 2, + tm.tm_sec * 60 + ); + if(w->clock.Hdpixel != w->core.background_pixel) + XFillPolygon( dpy, + win, w->clock.HandGC, + w->clock.sec, + VERTICES_IN_HANDS -2, + Convex, CoordModeOrigin + ); + XDrawLines( dpy, + win, w->clock.HighGC, + w->clock.sec, + VERTICES_IN_HANDS-1, + CoordModeOrigin + ); + + } + w->clock.otm = tm; + } +} + +static void +erase_hands(ClockWidget w, struct tm *tm) +{ + /* + * Erase old hands. + */ + if(w->clock.numseg > 0) { + Display *dpy; + Window win; + + dpy = XtDisplay (w); + win = XtWindow (w); + if (w->clock.show_second_hand == TRUE) { + XDrawLines(dpy, win, + w->clock.EraseGC, + w->clock.sec, + VERTICES_IN_HANDS-1, + CoordModeOrigin); + if(w->clock.Hdpixel != w->core.background_pixel) { + XFillPolygon(dpy, + win, w->clock.EraseGC, + w->clock.sec, + VERTICES_IN_HANDS-2, + Convex, CoordModeOrigin + ); + } + } + if(!tm || tm->tm_min != w->clock.otm.tm_min || + tm->tm_hour != w->clock.otm.tm_hour) + { + XDrawLines( dpy, win, + w->clock.EraseGC, + w->clock.segbuff, + VERTICES_IN_HANDS, + CoordModeOrigin); + XDrawLines( dpy, win, + w->clock.EraseGC, + w->clock.hour, + VERTICES_IN_HANDS, + CoordModeOrigin); + if(w->clock.Hdpixel != w->core.background_pixel) { + XFillPolygon( dpy, win, + w->clock.EraseGC, + w->clock.segbuff, VERTICES_IN_HANDS, + Convex, CoordModeOrigin); + XFillPolygon( dpy, win, + w->clock.EraseGC, + w->clock.hour, + VERTICES_IN_HANDS, + Convex, CoordModeOrigin); + } + } + } +} + +static float const Sines[] = { +0.000000, 0.001745, 0.003490, 0.005235, 0.006981, 0.008726, 0.010471, 0.012217, +0.013962, 0.015707, 0.017452, 0.019197, 0.020942, 0.022687, 0.024432, 0.026176, +0.027921, 0.029666, 0.031410, 0.033155, 0.034899, 0.036643, 0.038387, 0.040131, +0.041875, 0.043619, 0.045362, 0.047106, 0.048849, 0.050592, 0.052335, 0.054078, +0.055821, 0.057564, 0.059306, 0.061048, 0.062790, 0.064532, 0.066273, 0.068015, +0.069756, 0.071497, 0.073238, 0.074978, 0.076719, 0.078459, 0.080198, 0.081938, +0.083677, 0.085416, 0.087155, 0.088894, 0.090632, 0.092370, 0.094108, 0.095845, +0.097582, 0.099319, 0.101056, 0.102792, 0.104528, 0.106264, 0.107999, 0.109734, +0.111468, 0.113203, 0.114937, 0.116670, 0.118403, 0.120136, 0.121869, 0.123601, +0.125333, 0.127064, 0.128795, 0.130526, 0.132256, 0.133986, 0.135715, 0.137444, +0.139173, 0.140901, 0.142628, 0.144356, 0.146083, 0.147809, 0.149535, 0.151260, +0.152985, 0.154710, 0.156434, 0.158158, 0.159881, 0.161603, 0.163325, 0.165047, +0.166768, 0.168489, 0.170209, 0.171929, 0.173648, 0.175366, 0.177084, 0.178802, +0.180519, 0.182235, 0.183951, 0.185666, 0.187381, 0.189095, 0.190808, 0.192521, +0.194234, 0.195946, 0.197657, 0.199367, 0.201077, 0.202787, 0.204496, 0.206204, +0.207911, 0.209618, 0.211324, 0.213030, 0.214735, 0.216439, 0.218143, 0.219846, +0.221548, 0.223250, 0.224951, 0.226651, 0.228350, 0.230049, 0.231747, 0.233445, +0.235142, 0.236838, 0.238533, 0.240228, 0.241921, 0.243615, 0.245307, 0.246999, +0.248689, 0.250380, 0.252069, 0.253757, 0.255445, 0.257132, 0.258819, 0.260504, +0.262189, 0.263873, 0.265556, 0.267238, 0.268919, 0.270600, 0.272280, 0.273959, +0.275637, 0.277314, 0.278991, 0.280666, 0.282341, 0.284015, 0.285688, 0.287360, +0.289031, 0.290702, 0.292371, 0.294040, 0.295708, 0.297374, 0.299040, 0.300705, +0.302369, 0.304033, 0.305695, 0.307356, 0.309016, 0.310676, 0.312334, 0.313992, +0.315649, 0.317304, 0.318959, 0.320612, 0.322265, 0.323917, 0.325568, 0.327217, +0.328866, 0.330514, 0.332161, 0.333806, 0.335451, 0.337095, 0.338737, 0.340379, +0.342020, 0.343659, 0.345298, 0.346935, 0.348572, 0.350207, 0.351841, 0.353474, +0.355106, 0.356737, 0.358367, 0.359996, 0.361624, 0.363251, 0.364876, 0.366501, +0.368124, 0.369746, 0.371367, 0.372987, 0.374606, 0.376224, 0.377840, 0.379456, +0.381070, 0.382683, 0.384295, 0.385906, 0.387515, 0.389123, 0.390731, 0.392337, +0.393941, 0.395545, 0.397147, 0.398749, 0.400349, 0.401947, 0.403545, 0.405141, +0.406736, 0.408330, 0.409923, 0.411514, 0.413104, 0.414693, 0.416280, 0.417867, +0.419452, 0.421035, 0.422618, 0.424199, 0.425779, 0.427357, 0.428935, 0.430511, +0.432085, 0.433659, 0.435231, 0.436801, 0.438371, 0.439939, 0.441505, 0.443071, +0.444635, 0.446197, 0.447759, 0.449318, 0.450877, 0.452434, 0.453990, 0.455544, +0.457097, 0.458649, 0.460199, 0.461748, 0.463296, 0.464842, 0.466386, 0.467929, +0.469471, 0.471011, 0.472550, 0.474088, 0.475624, 0.477158, 0.478691, 0.480223, +0.481753, 0.483282, 0.484809, 0.486335, 0.487859, 0.489382, 0.490903, 0.492423, +0.493941, 0.495458, 0.496973, 0.498487, 0.499999, 0.501510, 0.503019, 0.504527, +0.506033, 0.507538, 0.509041, 0.510542, 0.512042, 0.513541, 0.515038, 0.516533, +0.518027, 0.519519, 0.521009, 0.522498, 0.523985, 0.525471, 0.526955, 0.528438, +0.529919, 0.531398, 0.532876, 0.534352, 0.535826, 0.537299, 0.538770, 0.540240, +0.541708, 0.543174, 0.544639, 0.546101, 0.547563, 0.549022, 0.550480, 0.551936, +0.553391, 0.554844, 0.556295, 0.557745, 0.559192, 0.560638, 0.562083, 0.563526, +0.564967, 0.566406, 0.567843, 0.569279, 0.570713, 0.572145, 0.573576, 0.575005, +0.576432, 0.577857, 0.579281, 0.580702, 0.582122, 0.583541, 0.584957, 0.586372, +0.587785, 0.589196, 0.590605, 0.592013, 0.593418, 0.594822, 0.596224, 0.597625, +0.599023, 0.600420, 0.601815, 0.603207, 0.604599, 0.605988, 0.607375, 0.608761, +0.610145, 0.611527, 0.612907, 0.614285, 0.615661, 0.617035, 0.618408, 0.619779, +0.621147, 0.622514, 0.623879, 0.625242, 0.626603, 0.627963, 0.629320, 0.630675, +0.632029, 0.633380, 0.634730, 0.636078, 0.637423, 0.638767, 0.640109, 0.641449, +0.642787, 0.644123, 0.645457, 0.646789, 0.648119, 0.649448, 0.650774, 0.652098, +0.653420, 0.654740, 0.656059, 0.657375, 0.658689, 0.660001, 0.661311, 0.662620, +0.663926, 0.665230, 0.666532, 0.667832, 0.669130, 0.670426, 0.671720, 0.673012, +0.674302, 0.675590, 0.676875, 0.678159, 0.679441, 0.680720, 0.681998, 0.683273, +0.684547, 0.685818, 0.687087, 0.688354, 0.689619, 0.690882, 0.692143, 0.693401, +0.694658, 0.695912, 0.697165, 0.698415, 0.699663, 0.700909, 0.702153, 0.703394, +0.704634, 0.705871, 0.707106, +}; +static float const Cosines[] = { +1.000000, 0.999998, 0.999993, 0.999986, 0.999975, 0.999961, 0.999945, 0.999925, +0.999902, 0.999876, 0.999847, 0.999815, 0.999780, 0.999742, 0.999701, 0.999657, +0.999610, 0.999559, 0.999506, 0.999450, 0.999390, 0.999328, 0.999262, 0.999194, +0.999122, 0.999048, 0.998970, 0.998889, 0.998806, 0.998719, 0.998629, 0.998536, +0.998440, 0.998341, 0.998239, 0.998134, 0.998026, 0.997915, 0.997801, 0.997684, +0.997564, 0.997440, 0.997314, 0.997185, 0.997052, 0.996917, 0.996778, 0.996637, +0.996492, 0.996345, 0.996194, 0.996041, 0.995884, 0.995724, 0.995561, 0.995396, +0.995227, 0.995055, 0.994880, 0.994702, 0.994521, 0.994337, 0.994150, 0.993960, +0.993767, 0.993571, 0.993372, 0.993170, 0.992965, 0.992757, 0.992546, 0.992331, +0.992114, 0.991894, 0.991671, 0.991444, 0.991215, 0.990983, 0.990747, 0.990509, +0.990268, 0.990023, 0.989776, 0.989525, 0.989272, 0.989015, 0.988756, 0.988493, +0.988228, 0.987959, 0.987688, 0.987413, 0.987136, 0.986855, 0.986572, 0.986285, +0.985996, 0.985703, 0.985407, 0.985109, 0.984807, 0.984503, 0.984195, 0.983885, +0.983571, 0.983254, 0.982935, 0.982612, 0.982287, 0.981958, 0.981627, 0.981292, +0.980955, 0.980614, 0.980271, 0.979924, 0.979575, 0.979222, 0.978867, 0.978508, +0.978147, 0.977783, 0.977415, 0.977045, 0.976672, 0.976296, 0.975916, 0.975534, +0.975149, 0.974761, 0.974370, 0.973975, 0.973578, 0.973178, 0.972775, 0.972369, +0.971961, 0.971549, 0.971134, 0.970716, 0.970295, 0.969872, 0.969445, 0.969015, +0.968583, 0.968147, 0.967709, 0.967267, 0.966823, 0.966376, 0.965925, 0.965472, +0.965016, 0.964557, 0.964095, 0.963630, 0.963162, 0.962691, 0.962217, 0.961741, +0.961261, 0.960779, 0.960293, 0.959805, 0.959313, 0.958819, 0.958322, 0.957822, +0.957319, 0.956813, 0.956304, 0.955793, 0.955278, 0.954760, 0.954240, 0.953716, +0.953190, 0.952661, 0.952129, 0.951594, 0.951056, 0.950515, 0.949972, 0.949425, +0.948876, 0.948323, 0.947768, 0.947210, 0.946649, 0.946085, 0.945518, 0.944948, +0.944376, 0.943800, 0.943222, 0.942641, 0.942057, 0.941470, 0.940880, 0.940288, +0.939692, 0.939094, 0.938493, 0.937888, 0.937281, 0.936672, 0.936059, 0.935444, +0.934825, 0.934204, 0.933580, 0.932953, 0.932323, 0.931691, 0.931055, 0.930417, +0.929776, 0.929132, 0.928485, 0.927836, 0.927183, 0.926528, 0.925870, 0.925209, +0.924546, 0.923879, 0.923210, 0.922538, 0.921863, 0.921185, 0.920504, 0.919821, +0.919135, 0.918446, 0.917754, 0.917060, 0.916362, 0.915662, 0.914959, 0.914253, +0.913545, 0.912834, 0.912120, 0.911403, 0.910683, 0.909961, 0.909236, 0.908508, +0.907777, 0.907044, 0.906307, 0.905568, 0.904827, 0.904082, 0.903335, 0.902585, +0.901832, 0.901077, 0.900318, 0.899557, 0.898794, 0.898027, 0.897258, 0.896486, +0.895711, 0.894934, 0.894154, 0.893371, 0.892585, 0.891797, 0.891006, 0.890212, +0.889416, 0.888617, 0.887815, 0.887010, 0.886203, 0.885393, 0.884580, 0.883765, +0.882947, 0.882126, 0.881303, 0.880477, 0.879648, 0.878817, 0.877982, 0.877146, +0.876306, 0.875464, 0.874619, 0.873772, 0.872922, 0.872069, 0.871213, 0.870355, +0.869494, 0.868631, 0.867765, 0.866896, 0.866025, 0.865151, 0.864274, 0.863395, +0.862513, 0.861629, 0.860742, 0.859852, 0.858959, 0.858064, 0.857167, 0.856267, +0.855364, 0.854458, 0.853550, 0.852640, 0.851726, 0.850811, 0.849892, 0.848971, +0.848048, 0.847121, 0.846193, 0.845261, 0.844327, 0.843391, 0.842452, 0.841510, +0.840566, 0.839619, 0.838670, 0.837718, 0.836764, 0.835807, 0.834847, 0.833885, +0.832921, 0.831954, 0.830984, 0.830012, 0.829037, 0.828060, 0.827080, 0.826098, +0.825113, 0.824126, 0.823136, 0.822144, 0.821149, 0.820151, 0.819152, 0.818149, +0.817144, 0.816137, 0.815127, 0.814115, 0.813100, 0.812083, 0.811063, 0.810041, +0.809016, 0.807989, 0.806960, 0.805928, 0.804893, 0.803856, 0.802817, 0.801775, +0.800731, 0.799684, 0.798635, 0.797583, 0.796529, 0.795473, 0.794414, 0.793353, +0.792289, 0.791223, 0.790155, 0.789084, 0.788010, 0.786935, 0.785856, 0.784776, +0.783693, 0.782608, 0.781520, 0.780430, 0.779337, 0.778243, 0.777145, 0.776046, +0.774944, 0.773840, 0.772733, 0.771624, 0.770513, 0.769399, 0.768283, 0.767165, +0.766044, 0.764921, 0.763796, 0.762668, 0.761538, 0.760405, 0.759271, 0.758134, +0.756995, 0.755853, 0.754709, 0.753563, 0.752414, 0.751264, 0.750111, 0.748955, +0.747798, 0.746638, 0.745475, 0.744311, 0.743144, 0.741975, 0.740804, 0.739631, +0.738455, 0.737277, 0.736097, 0.734914, 0.733729, 0.732542, 0.731353, 0.730162, +0.728968, 0.727772, 0.726574, 0.725374, 0.724171, 0.722967, 0.721760, 0.720551, +0.719339, 0.718126, 0.716910, 0.715692, 0.714472, 0.713250, 0.712026, 0.710799, +0.709570, 0.708339, 0.707106, +}; + +static void +ClockAngle(int tick_units, double *sinp, double *cosp) +{ + int reduced, upper; + + reduced = tick_units % 450; + upper = tick_units / 450; + if (upper & 1) + reduced = 450 - reduced; + if ((upper + 1) & 2) { + *sinp = Cosines[reduced]; + *cosp = Sines[reduced]; + } else { + *sinp = Sines[reduced]; + *cosp = Cosines[reduced]; + } + if (upper >= 2 && upper < 6) + *cosp = -*cosp; + if (upper >= 4) + *sinp = -*sinp; +} + +/* + * DrawLine - Draws a line. + * + * blank_length is the distance from the center which the line begins. + * length is the maximum length of the hand. + * Tick_units is a number between zero and 12*60 indicating + * how far around the circle (clockwise) from high noon. + * + * The blank_length feature is because I wanted to draw tick-marks around the + * circle (for seconds). The obvious means of drawing lines from the center + * to the perimeter, then erasing all but the outside most pixels doesn't + * work because of round-off error (sigh). + */ +static void +DrawLine(ClockWidget w, Dimension blank_length, Dimension length, + int tick_units) +{ + double dblank_length = (double)blank_length, dlength = (double)length; + double cosangle, sinangle; + int cx = w->clock.centerX, cy = w->clock.centerY, x1, y1, x2, y2; + + /* + * Angles are measured from 12 o'clock, clockwise increasing. + * Since in X, +x is to the right and +y is downward: + * + * x = x0 + r * sin(theta) + * y = y0 - r * cos(theta) + * + */ + ClockAngle(tick_units, &sinangle, &cosangle); + + /* break this out so that stupid compilers can cope */ + x1 = cx + (int)(dblank_length * sinangle); + y1 = cy - (int)(dblank_length * cosangle); + x2 = cx + (int)(dlength * sinangle); + y2 = cy - (int)(dlength * cosangle); + SetSeg(w, x1, y1, x2, y2); +} + +/* + * DrawHand - Draws a hand. + * + * length is the maximum length of the hand. + * width is the half-width of the hand. + * Tick_units is a number between zero and 12*60 indicating + * how far around the circle (clockwise) from high noon. + * + */ +static void +DrawHand(ClockWidget w, Dimension length, Dimension width, int tick_units) +{ + + double cosangle, sinangle; + register double ws, wc; + Position x, y, x1, y1, x2, y2; + + /* + * Angles are measured from 12 o'clock, clockwise increasing. + * Since in X, +x is to the right and +y is downward: + * + * x = x0 + r * sin(theta) + * y = y0 - r * cos(theta) + * + */ + ClockAngle(tick_units, &sinangle, &cosangle); + /* + * Order of points when drawing the hand. + * + * 1,4 + * / \ + * / \ + * / \ + * 2 ------- 3 + */ + wc = width * cosangle; + ws = width * sinangle; + SetSeg(w, + x = w->clock.centerX + clock_round(length * sinangle), + y = w->clock.centerY - clock_round(length * cosangle), + x1 = w->clock.centerX - clock_round(ws + wc), + y1 = w->clock.centerY + clock_round(wc - ws)); /* 1 ---- 2 */ + /* 2 */ + SetSeg(w, x1, y1, + x2 = w->clock.centerX - clock_round(ws - wc), + y2 = w->clock.centerY + clock_round(wc + ws)); /* 2 ----- 3 */ + + SetSeg(w, x2, y2, x, y); /* 3 ----- 1(4) */ +} + +/* + * DrawSecond - Draws the second hand (diamond). + * + * length is the maximum length of the hand. + * width is the half-width of the hand. + * offset is direct distance from center to tail end. + * Tick_units is a number between zero and 12*60 indicating + * how far around the circle (clockwise) from high noon. + * + */ +static void +DrawSecond(ClockWidget w, Dimension length, Dimension width, + Dimension offset, int tick_units) +{ + + double cosangle, sinangle; + register double ms, mc, ws, wc; + register int mid; + Position x, y; + + /* + * Angles are measured from 12 o'clock, clockwise increasing. + * Since in X, +x is to the right and +y is downward: + * + * x = x0 + r * sin(theta) + * y = y0 - r * cos(theta) + * + */ + ClockAngle(tick_units, &sinangle, &cosangle); + /* + * Order of points when drawing the hand. + * + * 1,5 + * / \ + * / \ + * / \ + * 2< >4 + * \ / + * \ / + * \ / + * - 3 + * | + * | + * offset + * | + * | + * - + center + */ + + mid = (int) (length + offset) / 2; + mc = mid * cosangle; + ms = mid * sinangle; + wc = width * cosangle; + ws = width * sinangle; + /*1 ---- 2 */ + SetSeg(w, + x = w->clock.centerX + clock_round(length * sinangle), + y = w->clock.centerY - clock_round(length * cosangle), + w->clock.centerX + clock_round(ms - wc), + w->clock.centerY - clock_round(mc + ws) ); + SetSeg(w, w->clock.centerX + clock_round(offset *sinangle), + w->clock.centerY - clock_round(offset * cosangle), /* 2-----3 */ + w->clock.centerX + clock_round(ms + wc), + w->clock.centerY - clock_round(mc - ws)); + w->clock.segbuffptr->x = x; + w->clock.segbuffptr++->y = y; + w->clock.numseg ++; +} + +static void +SetSeg(ClockWidget w, int x1, int y1, int x2, int y2) +{ + w->clock.segbuffptr->x = x1; + w->clock.segbuffptr++->y = y1; + w->clock.segbuffptr->x = x2; + w->clock.segbuffptr++->y = y2; + w->clock.numseg += 2; +} + +/* + * Draw the clock face (every fifth tick-mark is longer + * than the others). + */ +static void +DrawClockFace(ClockWidget w) +{ + register int i; + register int delta = (int)(w->clock.radius - w->clock.second_hand_length) / 3; + + w->clock.segbuffptr = w->clock.segbuff; + w->clock.numseg = 0; + for (i = 0; i < 60; i++) + { +#ifdef XRENDER + if (w->clock.render && w->clock.can_polygon) + { + double s, c; + XDouble x1, y1, x2, y2; + XftColor *color; + ClockAngle (i * 60, &s, &c); + x1 = c; + y1 = s; + if (i % 5) + { + x2 = c * (MINOR_TICK_FRACT / 100.0); + y2 = s * (MINOR_TICK_FRACT / 100.0); + color = &w->clock.minor_color; + } + else + { + x2 = c * (SECOND_HAND_FRACT / 100.0); + y2 = s * (SECOND_HAND_FRACT / 100.0); + color = &w->clock.major_color; + } + RenderLine (w, x1, y1, x2, y2, color, True); + } + else +#endif + { + DrawLine(w, ( (i % 5) == 0 ? + w->clock.second_hand_length : + (w->clock.radius - delta) ), + w->clock.radius, i * 60); + } + } +#ifdef XRENDER + if (w->clock.render && w->clock.can_polygon) + return; +#endif + /* + * Go ahead and draw it. + */ + XDrawSegments(XtDisplay(w), XtWindow(w), + w->clock.myGC, (XSegment *) &(w->clock.segbuff[0]), + w->clock.numseg/2); + + w->clock.segbuffptr = w->clock.segbuff; + w->clock.numseg = 0; +} + +static int +clock_round(double x) +{ + return(x >= 0.0 ? (int)(x + .5) : (int)(x - .5)); +} + +#ifdef XRENDER +static Boolean +sameColor (XftColor *old, XftColor *new) +{ + if (old->color.red != new->color.red) return False; + if (old->color.green != new->color.green) return False; + if (old->color.blue != new->color.blue) return False; + if (old->color.alpha != new->color.alpha) return False; + return True; +} +#endif + +/* ARGSUSED */ +static Boolean +SetValues(Widget gcurrent, Widget grequest, Widget gnew, + ArgList args, Cardinal *num_args) +{ + ClockWidget current = (ClockWidget) gcurrent; + ClockWidget new = (ClockWidget) gnew; + Boolean redisplay = FALSE; + XtGCMask valuemask; + XGCValues myXGCV; + + /* first check for changes to clock-specific resources. We'll accept all + the changes, but may need to do some computations first. */ + + if (new->clock.update != current->clock.update) { + if (current->clock.interval_id) + XtRemoveTimeOut (current->clock.interval_id); + if (new->clock.update && XtIsRealized( (Widget) new)) + new->clock.interval_id = XtAppAddTimeOut( + XtWidgetToApplicationContext(gnew), + abs(new->clock.update)*1000, + clock_tic, (XtPointer)gnew); + + new->clock.show_second_hand =(abs(new->clock.update) <= SECOND_HAND_TIME); + if (new->clock.show_second_hand != current->clock.show_second_hand) + redisplay = TRUE; + } + + if (new->clock.padding != current->clock.padding) + redisplay = TRUE; + + if (new->clock.analog != current->clock.analog) + redisplay = TRUE; + + if (new->clock.font != current->clock.font) + redisplay = TRUE; + +#ifndef NO_I18N + if (new->clock.fontSet != current->clock.fontSet) + redisplay = TRUE; +#endif + + if ((ClockFgPixel(new) != ClockFgPixel (current)) + || (new->core.background_pixel != current->core.background_pixel)) { + valuemask = GCForeground | GCBackground | GCFont | GCLineWidth; + myXGCV.foreground = ClockFgPixel (new); + myXGCV.background = new->core.background_pixel; + myXGCV.font = new->clock.font->fid; + myXGCV.line_width = 0; + XtReleaseGC (gcurrent, current->clock.myGC); + new->clock.myGC = XtGetGC(gcurrent, valuemask, &myXGCV); + redisplay = TRUE; + } + + if (new->clock.Hipixel != current->clock.Hipixel) { + valuemask = GCForeground | GCLineWidth; + myXGCV.foreground = new->clock.Hipixel; + myXGCV.font = new->clock.font->fid; + myXGCV.line_width = 0; + XtReleaseGC (gcurrent, current->clock.HighGC); + new->clock.HighGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV); + redisplay = TRUE; + } + + if (new->clock.Hdpixel != current->clock.Hdpixel) { + valuemask = GCForeground; + myXGCV.foreground = new->clock.Hdpixel; + XtReleaseGC (gcurrent, current->clock.HandGC); + new->clock.HandGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV); + redisplay = TRUE; + } + + if (new->core.background_pixel != current->core.background_pixel) { + valuemask = GCForeground | GCLineWidth | GCGraphicsExposures; + myXGCV.foreground = new->core.background_pixel; + myXGCV.line_width = 0; + myXGCV.graphics_exposures = False; + XtReleaseGC (gcurrent, current->clock.EraseGC); + new->clock.EraseGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV); + redisplay = TRUE; + } +#ifdef XRENDER + if (new->clock.face != current->clock.face) + redisplay = TRUE; + if (!sameColor (&new->clock.hour_color, ¤t->clock.fg_color) || + !sameColor (&new->clock.hour_color, ¤t->clock.hour_color) || + !sameColor (&new->clock.min_color, ¤t->clock.min_color) || + !sameColor (&new->clock.sec_color, ¤t->clock.sec_color) || + !sameColor (&new->clock.major_color, ¤t->clock.major_color) || + !sameColor (&new->clock.minor_color, ¤t->clock.minor_color)) + redisplay = True; + if (new->clock.sharp != current->clock.sharp) + redisplay = True; + if (new->clock.render != current->clock.render) + redisplay = True; + if (new->clock.buffer != current->clock.buffer) + { + if (new->clock.pixmap) + { + XFreePixmap (XtDisplay (new), new->clock.pixmap); + new->clock.pixmap = 0; + } + if (new->clock.draw) + { + XftDrawDestroy (new->clock.draw); + new->clock.draw = NULL; + } + new->clock.picture = 0; + } +#endif + return (redisplay); + +} + +#if !defined(NO_I18N) && defined(HAVE_ICONV) +static char * +clock_to_utf8(const char *str, int in_len) +{ + iconv_t cd; + char *buf; + size_t buf_size; + size_t ileft, oleft; + const char *inptr; + char *outptr; + size_t ret; + const char *code_set = nl_langinfo(CODESET); + + if (str == NULL ||code_set == NULL || strcasecmp(code_set, "646") == 0) + return NULL; + + if (strcasecmp(code_set, "UTF-8") == 0) + return strdup(str); + + cd = iconv_open("UTF-8", code_set); + if (cd == (iconv_t)-1) + return NULL; + + buf_size = MB_LEN_MAX * (in_len + 1); + if ((buf = malloc(buf_size)) == NULL) { + (void) iconv_close(cd); + return NULL; + } + + inptr = str; + ileft = in_len; + outptr = buf; + oleft = buf_size; + + ret = iconv(cd, &inptr, &ileft, &outptr, &oleft); + if (ret == (size_t)(-1) || oleft == 0 ) { + free(buf); + buf = NULL; + } else + *outptr = '\0'; + + (void) iconv_close(cd); + return buf; +} +#endif |