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