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