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