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