1 /*
2 * Dclock.c (v.2.0) -- a digital clock widget.
3 * Copyright (c) 1988 Dan Heller <argv@sun.com>
4 * Modifications 2/93 by Tim Edwards <tim@sinh.stanford.edu>
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <limits.h>
12 #include <math.h>
13 #include <time.h>
14 #include <sys/wait.h>
15 #include <errno.h>
16 #include <X11/IntrinsicP.h>
17 #include <X11/Xos.h>
18 #include <X11/StringDefs.h>
19 #include "DclockP.h"
20
21 static void
22 Initialize(), Resize(), ResizeNow(), Realize(), Destroy(), Redisplay(),
23 GetGC(), invert_bitmap(), build_segments(), timeout(), toggle_bell(),
24 toggle_reverse_video(), toggle_scroll(), toggle_seconds(), show_bell(),
25 toggle_military_time(), toggle_date(), make_number(), make_number_one(),
26 set_alarm(),
27 show_date(), show_alarm(), scroll_time(), toggle_alarm(), outline_digit(),
28 toggle_fade(), toggle_tails(), increase_slope(), decrease_slope(),
29 widen_segment(), thin_segment(), increase_space(), decrease_space(),
30 toggle_blink(), print_help(), print_dump(), playbell(), toggle_dateup();
31
32 static void XfFillArc(), XfFillPolygon();
33
34 /* some definitions */
35
36 #define BORDER 5
37 #define CLOCK_WIDTH 220
38 #define CLOCK_HEIGHT 80
39 #define DATE_FMT "%A, %B %d, %Y"
40 #define when break;case
41 #define otherwise break;default
42
43 #define sign(a) ((a) < 0.0 ? -1 : 1)
44 #define abs(a) ((a) < 0 ? -(a) : (a))
45
46 /* the x-value of the colon as measured from the window x=0 */
47
48 #define cx (int)(digit_w + (w->dclock.miltime ? digit_w : \
49 w->dclock.space_factor * 2 * digit_w + segxwidth))
50
51 /* A few variables and initial values */
52
53 static Boolean SetValues(), show_time();
54 static Dimension winwidth = CLOCK_WIDTH;
55 static Dimension winheight = CLOCK_HEIGHT;
56 static Boolean false = False;
57 static Boolean true = True;
58 static int fade_rate = 50;
59 static float y_ratio;
60 static float sslope = 6.0;
61 static float sspacer = 0.09;
62 static float smallrat = 0.67;
63 static float secgap = 0.3;
64 static float widfac = 0.13;
65 static Pixmap old_pix[4];
66 static int old_digs[4];
67 static struct tm before;
68 static char *saved_date;
69 static int cur_position; /* outline current digit for setting alarm */
70 static struct { int hrs, mins; } Alarm;
71 static int TopOffset = 0;
72
73 /* define what keys do what when pressed in the window */
74
75 static char defaultTranslations[] =
76 "<Key>b: toggle-bell() \n\
77 <Key>j: toggle-scroll() \n\
78 <Key>f: toggle-fade() \n\
79 <Key>s: toggle-seconds() \n\
80 <Key>r: toggle-reverse-video() \n\
81 <Key>colon: toggle-blink() \n\
82 <Key>m: toggle-military-time() \n\
83 <Key>d: toggle-date() \n\
84 <Key>u: toggle-dateup() \n\
85 <Key>t: toggle-tails() \n\
86 <Key>a: toggle-alarm() \n\
87 <Key>h: print-help() \n\
88 Shift<Key>slash: print-help() \n\
89 <Key>v: print-dump() \n\
90 <Key>+: widen-segment() \n\
91 <Key>-: thin-segment() \n\
92 <Key>slash: slope-increase() \n\
93 <Key>backslash: slope-decrease() \n\
94 Shift<Key>comma: space-decrease() \n\
95 Shift<Key>period: space-increase() \n\
96 <BtnDown>: set-alarm()";
97
98 static XtActionsRec actionsList[] = {
99 { "toggle-bell", toggle_bell },
100 { "toggle-scroll", toggle_scroll },
101 { "toggle-fade", toggle_fade },
102 { "toggle-seconds", toggle_seconds },
103 { "toggle-reverse-video", toggle_reverse_video },
104 { "toggle-blink", toggle_blink },
105 { "toggle-military-time", toggle_military_time },
106 { "toggle-date", toggle_date },
107 { "toggle-dateup", toggle_dateup },
108 { "toggle-tails", toggle_tails },
109 { "toggle-alarm", toggle_alarm },
110 { "widen-segment", widen_segment },
111 { "thin-segment", thin_segment },
112 { "slope-decrease", decrease_slope },
113 { "slope-increase", increase_slope },
114 { "space-decrease", decrease_space },
115 { "space-increase", increase_space },
116 { "set-alarm", set_alarm },
117 { "print-help", print_help },
118 { "print-dump", print_dump },
119 };
120
121 /* Initialization values for all of the X resources */
122
123 static XtResource resources[] = {
124 { XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
125 XtOffset(Widget,core.width), XtRDimension, (caddr_t)&winwidth },
126 { XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
127 XtOffset(Widget,core.height), XtRDimension, (caddr_t)&winheight },
128 { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
129 XtOffset(DclockWidget,dclock.foreground), XtRString, "Chartreuse"},
130 { XtNled_off, XtCForeground, XtRPixel, sizeof(Pixel),
131 XtOffset(DclockWidget,dclock.led_off), XtRString, "DarkGreen"},
132 { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
133 XtOffset(DclockWidget,dclock.background), XtRString, "DarkSlateGray"},
134 { XtNtails, XtCBoolean, XtRBoolean, sizeof (Boolean),
135 XtOffset(DclockWidget,dclock.tails), XtRBoolean, (caddr_t)&true},
136 { XtNfade, XtCBoolean, XtRBoolean, sizeof (Boolean),
137 XtOffset(DclockWidget,dclock.fade), XtRBoolean, (caddr_t)&true},
138 { XtNfadeRate, XtCTime, XtRInt, sizeof (int),
139 XtOffset(DclockWidget,dclock.fade_rate), XtRInt, (caddr_t)&fade_rate},
140 { XtNscroll, XtCBoolean, XtRBoolean, sizeof (Boolean),
141 XtOffset(DclockWidget,dclock.scroll), XtRBoolean, (caddr_t)&false},
142 { XtNdisplayTime, XtCBoolean, XtRBoolean, sizeof (Boolean),
143 XtOffset(DclockWidget,dclock.display_time), XtRBoolean, (caddr_t)&true},
144 { XtNalarm, XtCBoolean, XtRBoolean, sizeof (Boolean),
145 XtOffset(DclockWidget,dclock.alarm), XtRBoolean, (caddr_t)&false},
146 { XtNalarmTime, XtCTime, XtRString, sizeof (char *),
147 XtOffset(DclockWidget,dclock.alarm_time), XtRString, "00:00" },
148 { XtNalarmPersist, XtCBoolean, XtRBoolean, sizeof (Boolean),
149 XtOffset(DclockWidget,dclock.alarm_persist), XtRBoolean, (caddr_t)&false},
150 { XtNbell, XtCBoolean, XtRBoolean, sizeof (Boolean),
151 XtOffset(DclockWidget,dclock.bell), XtRBoolean, (caddr_t)&false},
152 { XtNseconds, XtCBoolean, XtRBoolean, sizeof (Boolean),
153 XtOffset(DclockWidget,dclock.seconds), XtRBoolean, (caddr_t)&false},
154 { XtNblink, XtCBoolean, XtRBoolean, sizeof (Boolean),
155 XtOffset(DclockWidget,dclock.blink), XtRBoolean, (caddr_t)&true},
156 { XtNmilitaryTime, XtCBoolean, XtRBoolean, sizeof (Boolean),
157 XtOffset(DclockWidget,dclock.miltime), XtRBoolean, (caddr_t)&false},
158 { XtNutcTime, XtCBoolean, XtRBoolean, sizeof (Boolean),
159 XtOffset(DclockWidget,dclock.utc), XtRBoolean, (caddr_t)&false},
160 { XtNdate, XtCString, XtRString, sizeof (String),
161 XtOffset(DclockWidget,dclock.date_fmt), XtRString, NULL},
162 { XtNdateUp, XtCBoolean, XtRBoolean, sizeof (Boolean),
163 XtOffset(DclockWidget,dclock.dateup), XtRBoolean, (caddr_t)&false},
164 { XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
165 XtOffset(DclockWidget,dclock.font), XtRString, "fixed"},
166 #ifdef XFT_SUPPORT
167 { XftNfontName, "fontName", XtRString, sizeof(String),
168 XtOffset(DclockWidget,dclock.xftfontname), XtRString, NULL},
169 #endif
170 { XtNangle, "Slope", XtRFloat, sizeof(float),
171 XtOffset(DclockWidget,dclock.angle), XtRFloat, (caddr_t)&sslope},
172 { XtNwidthFactor, "Ratio", XtRFloat, sizeof(float),
173 XtOffset(DclockWidget,dclock.width_factor), XtRFloat, (caddr_t)&widfac},
174 { XtNsmallRatio, "Ratio", XtRFloat, sizeof(float),
175 XtOffset(DclockWidget,dclock.small_ratio), XtRFloat,(caddr_t)&smallrat},
176 { XtNsecondGap, "Ratio", XtRFloat, sizeof(float),
177 XtOffset(DclockWidget,dclock.sec_gap), XtRFloat, (caddr_t)&secgap},
178 { XtNspaceFactor, "Ratio", XtRFloat, sizeof(float),
179 XtOffset(DclockWidget,dclock.space_factor), XtRFloat,(caddr_t)&sspacer},
180 { XtNalarmFile, "alarmFile", XtRString, sizeof(String),
181 XtOffset(DclockWidget,dclock.alarmfile), XtRString, (String)NULL},
182 { XtNbellFile, "bellFile", XtRString, sizeof(String),
183 XtOffset(DclockWidget,dclock.bellfile), XtRString, (String)NULL},
184 { XtNaudioPlay, "audioPlay", XtRString, sizeof(String),
185 XtOffset(DclockWidget,dclock.audioplay), XtRString, "/usr/local/bin/play"},
186 };
187
188 /* Define the Dclock widget */
189
190 DclockClassRec dclockClassRec = {
191 { /* core fields */
192 /* superclass */ &widgetClassRec,
193 /* class_name */ "Dclock",
194 /* widget_size */ sizeof(DclockRec),
195 /* class_initialize */ NULL,
196 /* class_part_initialize */ NULL,
197 /* class_inited */ FALSE,
198 /* initialize */ Initialize,
199 /* initialize_hook */ NULL,
200 /* realize */ Realize,
201 /* actions */ actionsList,
202 /* num_actions */ XtNumber(actionsList),
203 /* resources */ resources,
204 /* resource_count */ XtNumber(resources),
205 /* xrm_class */ NULLQUARK,
206 /* compress_motion */ TRUE,
207 /* compress_exposure */ TRUE,
208 /* compress_enterleave */ TRUE,
209 /* visible_interest */ FALSE,
210 /* destroy */ Destroy,
211 /* resize */ Resize,
212 /* expose */ Redisplay,
213 /* set_values */ SetValues,
214 /* set_values_hook */ NULL,
215 /* set_values_almost */ XtInheritSetValuesAlmost,
216 /* get_values_hook */ NULL,
217 /* accept_focus */ NULL,
218 /* version */ XtVersion,
219 /* callback_private */ NULL,
220 /* tm_table */ defaultTranslations,
221 /* query_geometry */ NULL,
222 }
223 };
224 WidgetClass dclockWidgetClass = (WidgetClass) &dclockClassRec;
225
226
227 #ifdef NO_USLEEP
228 #define usleep(x) { struct timeval st_delay; \
229 st_delay.tv_usec = (x); st_delay.tv_sec = 0; \
230 select(32, NULL, NULL, NULL, &st_delay); }
231 #endif
232
233
234 /*
235 * These stipples give different densities for the
236 * different stages of fading.
237 */
238 static char stpl_1_8th[] =
239 {
240 0x80, 0x80, 0x08, 0x08, 0x80, 0x80, 0x08, 0x08,
241 0x80, 0x80, 0x08, 0x08, 0x80, 0x80, 0x08, 0x08,
242 0x80, 0x80, 0x08, 0x08, 0x80, 0x80, 0x08, 0x08,
243 0x80, 0x80, 0x08, 0x08, 0x80, 0x80, 0x08, 0x08
244 };
245
246 static char stpl_1_4th[] =
247 {
248 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
249 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
250 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
251 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22
252 };
253
254 static char stpl_3_8ths[] =
255 {
256 0xA2, 0xA2, 0x15, 0x15, 0xA8, 0xA8, 0x45, 0x45,
257 0x2A, 0x2A, 0x51, 0x51, 0x8A, 0x8A, 0x54, 0x54,
258 0xA2, 0xA2, 0x15, 0x15, 0xA8, 0xA8, 0x45, 0x45,
259 0x2A, 0x2A, 0x51, 0x51, 0x8A, 0x8A, 0x54, 0x54
260 };
261
262 static char stpl_one_half[] =
263 {
264 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
265 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
266 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
267 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA
268 };
269
270 /*
271 * fade_stip[] eventually contains the pixmaps used for each
272 * iteration of fading (initialized in Initialize()).
273 * If smooth color variations are used instead, then the
274 * colormap index values are held in fadevector[].
275 */
276 #define FADE_ITER 8
277 static Pixmap fade_stip[FADE_ITER];
278 int fadevector[FADE_ITER];
279 int bsave = 0;
280 Boolean use_stipple;
281
282 #define MAX_PTS 6 /* max # of pts per segment polygon */
283 #define NUM_SEGS 7 /* number of segments in a digit */
284
285 /*
286 * These constants give the bit positions for the segmask[]
287 * digit masks.
288 */
289 #define TOP 0
290 #define MIDDLE 1
291 #define BOTTOM 2
292 #define TOP_LEFT 3
293 #define BOT_LEFT 4
294 #define TOP_RIGHT 5
295 #define BOT_RIGHT 6
296
297 #define msk(i) (1 << (i))
298
299 /*
300 * segmask[n] contains a bitmask of the segments which
301 * should be lit for digit 'n'.
302 */
303 int segmask[11] =
304 {
305 /* 0 */ msk(TOP) | msk(TOP_RIGHT) | msk(BOT_RIGHT) | msk(BOTTOM)
306 | msk(BOT_LEFT) | msk(TOP_LEFT),
307 /* 1 */ msk(TOP_RIGHT) | msk(BOT_RIGHT),
308 /* 2 */ msk(TOP) | msk(TOP_RIGHT) | msk(MIDDLE) | msk(BOT_LEFT)
309 | msk(BOTTOM),
310 /* 3 */ msk(TOP) | msk(TOP_RIGHT) | msk(MIDDLE) | msk(BOT_RIGHT)
311 | msk(BOTTOM),
312 /* 4 */ msk(TOP_LEFT) | msk(MIDDLE) | msk(TOP_RIGHT) | msk(BOT_RIGHT),
313 /* 5 */ msk(TOP) | msk(TOP_LEFT) | msk(MIDDLE) | msk(BOT_RIGHT)
314 | msk(BOTTOM),
315 /* 6 */ msk(TOP_LEFT) | msk(BOT_LEFT) | msk(MIDDLE) | msk(BOTTOM)
316 | msk(BOT_RIGHT),
317 /* 7 */ msk(TOP) | msk(TOP_RIGHT) | msk(BOT_RIGHT),
318 /* 8 */ msk(TOP_LEFT) | msk(BOT_LEFT) | msk(MIDDLE) | msk(TOP)
319 | msk(BOTTOM) | msk(TOP_RIGHT) | msk(BOT_RIGHT),
320 /* 9 */ msk(TOP) | msk(TOP_LEFT) | msk(TOP_RIGHT) | msk(MIDDLE)
321 | msk(BOT_RIGHT),
322 /* blank */ 0
323 };
324
325 /*
326 * num_segment_pts indicates the number of vertices on the
327 * polygon that describes a segment
328 */
329 static int num_segment_pts = 6;
330 float slope_add, segxwidth, sxw;
331
332 /* Clipping masks to prevent angled digits from obscuring one another */
333
334 Region clip_norm, clip_small, clip_colon;
335 XPoint clip_pts[5];
336
337 typedef struct {
338 float x;
339 float y;
340 } XfPoint;
341
342 typedef XfPoint segment_pts[NUM_SEGS][MAX_PTS];
343
344 segment_pts tiny_segment_pts;
345 segment_pts norm_segment_pts;
346
347 #ifdef XFT_SUPPORT
348 #define XFT_COND(dclock) ((dclock).xftfontname)
349 #else
350 #define XFT_COND(dclock) (0)
351 #endif
352
353 /* ARGSUSED */
354 static void
Initialize(request,new)355 Initialize (request, new)
356 DclockWidget request;
357 DclockWidget new;
358 {
359 int i, n;
360 Display *dpy = XtDisplay(new);
361 Drawable root;
362 Colormap cmap = DefaultColormap (dpy, DefaultScreen(dpy));
363 XColor cvcolor, cvfg, cvbg;
364 float frac;
365
366 root = RootWindow(dpy, DefaultScreen(dpy));
367
368 if (new->dclock.alarm_time) {
369 if (sscanf(new->dclock.alarm_time, "%2d:%2d",
370 &Alarm.hrs, &Alarm.mins) != 2 || Alarm.hrs >= 24 ||
371 Alarm.mins >= 60) {
372 XtWarning("Alarm Time is in incorrect format.");
373 new->dclock.alarm_time = "00:00";
374 Alarm.hrs = Alarm.mins = 0;
375 }
376 new->dclock.alarm_time =
377 strcpy(XtMalloc(strlen(new->dclock.alarm_time)+1),
378 new->dclock.alarm_time);
379 }
380
381 /* control allowable features */
382
383 if (new->dclock.fade) new->dclock.scroll = False;
384 if (new->dclock.angle < 1.0) new->dclock.angle = 1.0;
385 if (new->dclock.angle > 2 * winheight) new->dclock.angle = 2 * winheight;
386 if (new->dclock.space_factor > 0.8) new->dclock.space_factor = 0.8;
387 if (new->dclock.space_factor < 0.0) new->dclock.space_factor = 0.0;
388 if (new->dclock.small_ratio < 0.1) new->dclock.small_ratio = 0.1;
389 if (new->dclock.small_ratio > 1.0) new->dclock.small_ratio = 1.0;
390 if (new->dclock.width_factor > 0.24) new->dclock.width_factor = 0.24;
391 if (new->dclock.width_factor < 0.01) new->dclock.width_factor = 0.01;
392 if (new->dclock.sec_gap < 0.0) new->dclock.sec_gap = 0.0;
393
394 #ifdef XFT_SUPPORT
395 if (XFT_COND(new->dclock)) {
396 new->dclock.xftfont = XftFontOpen(dpy, DefaultScreen(dpy),
397 XFT_FAMILY, XftTypeString, new->dclock.xftfontname,
398 XFT_SIZE, XftTypeDouble, ((float)new->core.height / 6.0) - 4.0,
399 NULL);
400 new->dclock.xftdraw = NULL;
401 }
402 #endif
403
404 GetGC(new);
405
406 /* Get RGB values for the foreground and background */
407
408 cvfg.pixel = new->dclock.foreground;
409 XQueryColor(dpy, cmap, &cvfg);
410 cvbg.pixel = new->dclock.led_off;
411 XQueryColor(dpy, cmap, &cvbg);
412
413 /* Attempt to initialize a color vector of FADE_ITER points between */
414 /* foreground and background */
415
416 use_stipple = False;
417 for (i = 0; i < FADE_ITER; i++) {
418 frac = (float)i / (float)(FADE_ITER - 1);
419 /* gamma correction */
420 frac = pow(frac, 0.5);
421
422 cvcolor.green = cvbg.green + (int)((float)(cvfg.green - cvbg.green) * frac);
423 cvcolor.blue = cvbg.blue + (int)((float)(cvfg.blue - cvbg.blue) * frac);
424 cvcolor.red = cvbg.red + (int)((float)(cvfg.red - cvbg.red) * frac);
425
426 if (!XAllocColor(dpy, cmap, &cvcolor)) {
427 use_stipple = True;
428 break;
429 }
430 fadevector[i] = cvcolor.pixel;
431 }
432
433 if (use_stipple) {
434 fade_stip[1] = XCreateBitmapFromData(dpy, root, stpl_1_8th, 16, 16);
435 fade_stip[2] = XCreateBitmapFromData(dpy, root, stpl_1_4th, 16, 16);
436 fade_stip[3] = XCreateBitmapFromData(dpy, root, stpl_3_8ths, 16, 16);
437 fade_stip[4] = XCreateBitmapFromData(dpy, root, stpl_one_half, 16, 16);
438 invert_bitmap(stpl_1_8th, 16, 16);
439 invert_bitmap(stpl_1_4th, 16, 16);
440 invert_bitmap(stpl_3_8ths, 16, 16);
441 fade_stip[5] = XCreateBitmapFromData(dpy, root, stpl_3_8ths, 16, 16);
442 fade_stip[6] = XCreateBitmapFromData(dpy, root, stpl_1_4th, 16, 16);
443 fade_stip[7] = XCreateBitmapFromData(dpy, root, stpl_1_8th, 16, 16);
444
445 for (n = 1; n != FADE_ITER; ++n)
446 if (!fade_stip[n])
447 {
448 fprintf(stderr, "dclock: Couldn't create stipple!\n");
449 exit(1);
450 }
451 }
452
453 if (!new->dclock.date_fmt || !*new->dclock.date_fmt)
454 saved_date = DATE_FMT;
455 if (new->dclock.date_fmt && !*new->dclock.date_fmt)
456 new->dclock.date_fmt = NULL;
457 if (new->dclock.dateup && new->dclock.date_fmt)
458 if (XFT_COND(new->dclock)) {
459 TopOffset = new->core.height / 6;
460 } else {
461 TopOffset = new->dclock.font->ascent + new->dclock.font->descent;
462 }
463
464 else
465 TopOffset = 0;
466
467 /* Shouldn't be necessary, but play it safe anyway */
468 for (n = 0; n < 10; n++)
469 {
470 new->dclock.digits[n] = 0;
471 new->dclock.tiny_digits[n] = 0;
472 }
473 new->dclock.digit_one[0] = new->dclock.digit_one[1] = 0;
474 new->dclock.colon[0] = new->dclock.colon[1] = 0;
475
476 new->dclock.interval_id = (XtIntervalId)NULL;
477 new->dclock.punt_resize = (XtIntervalId)NULL;
478
479 }
480
481 static void
GetGC(w)482 GetGC(w)
483 DclockWidget w;
484 { XGCValues xgcv;
485 XtGCMask gc_mask =
486 GCGraphicsExposures | GCFont | GCForeground | GCBackground;
487
488 xgcv.font = w->dclock.font->fid;
489 xgcv.graphics_exposures = FALSE;
490 xgcv.foreground = w->dclock.foreground;
491 xgcv.background = w->dclock.background;
492
493 w->dclock.foreGC = XtGetGC ((Widget) w, gc_mask, &xgcv);
494
495 xgcv.foreground = w->dclock.background;
496 xgcv.background = w->dclock.foreground;
497
498 w->dclock.backGC = XtGetGC ((Widget) w, gc_mask, &xgcv);
499 }
500
501
502 static void
invert_bitmap(bm,h,w)503 invert_bitmap(bm, h, w)
504 char *bm;
505 int h, w;
506 {
507 int i, *wp;
508
509 for (wp = (int *) bm, i = (h*w) / (8*sizeof(int)); i != 0; --i, ++wp)
510 *wp = ~(*wp);
511 }
512
513 static void
Realize(w,valueMask,attrs)514 Realize (w, valueMask, attrs)
515 Widget w;
516 XtValueMask *valueMask;
517 XSetWindowAttributes *attrs;
518 {
519 Display *dp = XtDisplay(w);
520 DclockWidget dw = (DclockWidget)w;
521 int sc = DefaultScreen(dp);
522 Visual *vs = DefaultVisual(dp, sc);;
523
524 *valueMask |= CWBitGravity;
525 attrs->bit_gravity = ForgetGravity;
526 vs = DefaultVisual(dp, sc);
527 XtCreateWindow(w, InputOutput, (Visual *)CopyFromParent, *valueMask, attrs);
528
529 #ifdef XFT_SUPPORT
530 if (XFT_COND(dw->dclock) && dw->dclock.xftdraw == NULL) {
531 XColor color;
532 Colormap cm = DefaultColormap(dp, sc);
533
534 dw->dclock.xftdraw = XftDrawCreate(dp, XtWindow(w), vs, cm);
535
536 color.pixel = dw->dclock.foreground;
537 XQueryColor(dp, cm, &color);
538 dw->dclock.xftfg.color.red = color.red;
539 dw->dclock.xftfg.color.green = color.green;
540 dw->dclock.xftfg.color.blue = color.blue;
541 dw->dclock.xftfg.color.alpha = 0xffff;
542 dw->dclock.xftfg.pixel = color.pixel;
543
544 color.pixel = dw->dclock.background;
545 XQueryColor(dp, cm, &color);
546 dw->dclock.xftbg.color.red = color.red;
547 dw->dclock.xftbg.color.green = color.green;
548 dw->dclock.xftbg.color.blue = color.blue;
549 dw->dclock.xftbg.color.alpha = 0xffff;
550 dw->dclock.xftbg.pixel = color.pixel;
551 }
552 #endif
553 ResizeNow(w);
554 }
555
556 static void
Destroy(w)557 Destroy (w)
558 DclockWidget w;
559 {
560 int n;
561
562 /* Be nice and free up memory before exiting. */
563
564 if (w->dclock.interval_id != (XtIntervalId)NULL)
565 XtRemoveTimeOut(w->dclock.interval_id);
566 if (w->dclock.punt_resize != (XtIntervalId)NULL)
567 XtRemoveTimeOut(w->dclock.punt_resize);
568 XtReleaseGC ((Widget)w, w->dclock.foreGC);
569 XtReleaseGC ((Widget)w, w->dclock.backGC);
570 for (n = 0; n < 10; n++) {
571 XFreePixmap(XtDisplay(w), w->dclock.digits[n]);
572 XFreePixmap(XtDisplay(w), w->dclock.tiny_digits[n]);
573 }
574 XFreePixmap(XtDisplay(w), w->dclock.digit_one[0]);
575 XFreePixmap(XtDisplay(w), w->dclock.digit_one[1]);
576 if (w->dclock.colon[0])
577 XFreePixmap(XtDisplay(w), w->dclock.colon[0]);
578 if (w->dclock.colon[1])
579 XFreePixmap(XtDisplay(w), w->dclock.colon[1]);
580 XDestroyRegion(clip_norm);
581 XDestroyRegion(clip_small);
582 XDestroyRegion(clip_colon);
583
584 #ifdef XFT_SUPPORT
585 if (XFT_COND(w->dclock))
586 XftFontClose(XtDisplay(w), w->dclock.xftfont);
587 #endif
588 }
589
590 /* ARGSUSED */
591 static void
ResizeNow(w)592 ResizeNow (w)
593 DclockWidget w;
594 {
595 int i, j;
596 float digit_w, digit_h, seg_width;
597 Pixmap pix;
598 GC gc = w->dclock.foreGC;
599 Display *dpy = XtDisplay(w);
600
601 if (!XtIsRealized((Widget)w))
602 return;
603
604 winwidth = w->core.width;
605 winheight = w->core.height;
606
607 #ifdef XFT_SUPPORT
608 if(XFT_COND(w->dclock)) {
609 XftFontClose(dpy, w->dclock.xftfont);
610 w->dclock.xftfont = XftFontOpen(dpy, DefaultScreen(dpy),
611 XFT_FAMILY, XftTypeString, w->dclock.xftfontname,
612 XFT_SIZE, XftTypeDouble, ((float)winheight / 6.0) - 4.0,
613 NULL);
614 }
615 #endif
616
617 y_ratio = (float)winheight / CLOCK_HEIGHT;
618
619 if (w->dclock.date_fmt || !w->dclock.display_time || w->dclock.alarm ||
620 w->dclock.bell)
621 /* make win temporarily shorter so digits will fit on top of date */
622 {
623 if(XFT_COND(w->dclock)) {
624 winheight -= w->core.height / 6;
625 } else {
626 winheight -= w->dclock.font->ascent + w->dclock.font->descent;
627 }
628 }
629
630 /*
631 * if the width of all segments are equal, then the width in x is the
632 * segment width times the cosine of the slope angle.
633 */
634 sxw = (w->dclock.width_factor *
635 sqrt(1 + (w->dclock.angle)*(w->dclock.angle)))/(w->dclock.angle);
636
637 /* height of the digit, leaving some room for a border */
638 digit_h = winheight - y_ratio * BORDER*2;
639
640 /* the amount of skew measured along x due to the slope of the digits */
641 slope_add = digit_h / (w->dclock.angle);
642
643 /* calculate the width of the digit based on everything. */
644 /* All attributes are calculated as ratios to the digit width. */
645 /* The colon is 1/2 the digit width; */
646 /* All other ratios are widget attributes. */
647
648 digit_w = (winwidth - slope_add * !(w->dclock.seconds) -
649 (slope_add * w->dclock.small_ratio) * w->dclock.seconds) / (3.5 +
650 w->dclock.miltime + w->dclock.seconds * (2 * w->dclock.small_ratio
651 + w->dclock.sec_gap) + (2 * w->dclock.space_factor + sxw) *
652 !(w->dclock.miltime));
653
654 /* segment widths as measured in x and y */
655
656 segxwidth = sxw * digit_w;
657 seg_width = w->dclock.width_factor * (digit_w + slope_add);
658
659 /* create the clipping masks */
660
661 clip_pts[0].x = clip_pts[4].x = 0;
662 clip_pts[1].x = (int)(slope_add);
663 clip_pts[2].x = (int)(slope_add + digit_w);
664 clip_pts[3].x = (int)digit_w;
665 clip_pts[1].y = clip_pts[2].y = 0;
666 clip_pts[0].y = clip_pts[4].y = clip_pts[3].y = digit_h;
667 clip_norm = XPolygonRegion(clip_pts, 5, WindingRule);
668
669 for(i = 0; i < 5; i++) {
670 clip_pts[i].x = (int)((float)clip_pts[i].x * w->dclock.small_ratio);
671 clip_pts[i].y = (int)((float)clip_pts[i].y * w->dclock.small_ratio);
672 }
673 clip_small = XPolygonRegion(clip_pts, 5, WindingRule);
674
675 clip_pts[1].x = (int)slope_add;
676 clip_pts[2].x = (int)(slope_add + 0.5 * digit_w);
677 clip_pts[3].x = (int)(0.5 * digit_w);
678 clip_pts[0].y = clip_pts[4].y = clip_pts[3].y = digit_h;
679 clip_colon = XPolygonRegion(clip_pts, 5, WindingRule);
680
681 /* printf("segxwidth = %f\n", segxwidth);
682 printf("winwidth = %d\n", winwidth);
683 printf("digit_w = %f\n", digit_w); */
684
685 w->dclock.digit_w = digit_w;
686 w->dclock.digit_h = digit_h;
687
688 if (w->dclock.tails)
689 {
690 segmask[6] |= msk(TOP);
691 segmask[9] |= msk(BOTTOM);
692 }
693 else
694 {
695 segmask[6] &= ~msk(TOP);
696 segmask[9] &= ~msk(BOTTOM);
697 }
698
699 build_segments(w, norm_segment_pts, slope_add + digit_w, digit_h);
700
701 /* printf("Tiny Segment Relative Points Dump:\n");
702 for (i = 0; i < 7; i++)
703 for (j = 0; j < 6; j++)
704 printf("digit[%d] point[%d]: x=%d, y=%d\n", i, j,
705 tiny_segment_pts[i][j].x, tiny_segment_pts[i][j].y);
706 */
707
708 if (w->dclock.seconds)
709 build_segments(w, tiny_segment_pts, w->dclock.small_ratio * (slope_add
710 + digit_w), w->dclock.small_ratio * digit_h);
711
712 for (i = 0; i < 10; i++) {
713
714 /* Make the big digit */
715
716 if (w->dclock.digits[i])
717 XFreePixmap(XtDisplay(w), w->dclock.digits[i]);
718 w->dclock.digits[i] =
719 XCreatePixmap(XtDisplay(w), XtWindow(w), (int)(slope_add +
720 digit_w), (int)digit_h, DefaultDepthOfScreen(XtScreen(w)));
721 XFillRectangle(XtDisplay(w), w->dclock.digits[i], w->dclock.backGC,
722 0, 0, (int)(slope_add + digit_w), (int)digit_h);
723 make_number(w, w->dclock.digits[i], gc, i, norm_segment_pts);
724
725 /* make smaller version of this digit for use by "seconds" */
726
727 if (w->dclock.tiny_digits[i])
728 XFreePixmap(XtDisplay(w), w->dclock.tiny_digits[i]);
729 if (w->dclock.seconds)
730 {
731 w->dclock.tiny_digits[i] =
732 XCreatePixmap(XtDisplay(w), XtWindow(w), (int)(
733 (slope_add + digit_w) *
734 w->dclock.small_ratio),
735 (int)(w->dclock.small_ratio * digit_h),
736 DefaultDepthOfScreen(XtScreen(w)));
737 XFillRectangle(XtDisplay(w), w->dclock.tiny_digits[i],
738 w->dclock.backGC, 0, 0, (int)((slope_add +
739 digit_w) * w->dclock.small_ratio),
740 (int)(w->dclock.small_ratio * digit_h));
741 make_number(w, w->dclock.tiny_digits[i], gc, i, tiny_segment_pts);
742 }
743 else
744 w->dclock.tiny_digits[i] = (Pixmap)NULL;
745 }
746
747 /* digit_one is either the number "1" or a blank */
748
749 if (w->dclock.digit_one[1])
750 XFreePixmap(XtDisplay(w), w->dclock.digit_one[1]);
751 w->dclock.digit_one[1] =
752 XCreatePixmap(XtDisplay(w), XtWindow(w), (int)(slope_add +
753 digit_w), (int)digit_h, DefaultDepthOfScreen(XtScreen(w)));
754 XFillRectangle(XtDisplay(w), w->dclock.digit_one[1], w->dclock.backGC,
755 0, 0, (int)(slope_add + digit_w), (int)digit_h);
756 make_number_one(w, w->dclock.digit_one[1], gc, 1, norm_segment_pts);
757
758 if (w->dclock.digit_one[0])
759 XFreePixmap(XtDisplay(w), w->dclock.digit_one[0]);
760 w->dclock.digit_one[0] =
761 XCreatePixmap(XtDisplay(w), XtWindow(w), (int)(slope_add +
762 digit_w), (int)digit_h, DefaultDepthOfScreen(XtScreen(w)));
763 XFillRectangle(XtDisplay(w), w->dclock.digit_one[0], w->dclock.backGC,
764 0, 0, (int)(slope_add + digit_w), (int)digit_h);
765 make_number_one(w, w->dclock.digit_one[0], gc, 10, norm_segment_pts);
766
767 /* The colon[0] area is blank (segment "off" color) */
768
769 if (w->dclock.colon[0])
770 XFreePixmap(XtDisplay(w), w->dclock.colon[0]);
771 w->dclock.colon[0] = XCreatePixmap(XtDisplay(w), XtWindow(w),
772 (int)(slope_add + 0.5 * digit_w), (int)digit_h,
773 DefaultDepthOfScreen(XtScreen(w)));
774
775 XFillRectangle(XtDisplay(w), w->dclock.colon[0],
776 w->dclock.backGC, 0, 0, (int)(slope_add + 0.5 * digit_w), (int)digit_h);
777
778 /* The additive 2 for the Arc diameter keeps the colon from looking too
779 small at small display sizes. */
780
781 XfFillArc(XtDisplay(w), w->dclock.colon[0], gc,
782 (slope_add + digit_w) * 0.25 - 0.5, 0.75 * digit_h - 0.5,
783 1 + seg_width/2, 0);
784 XfFillArc(XtDisplay(w), w->dclock.colon[0], gc,
785 slope_add * 0.75 + digit_w * 0.25 - 0.5, 0.25 * digit_h - 0.5,
786 1 + seg_width/2, 0);
787
788 /* colon[1] area has two circles */
789
790 if (w->dclock.colon[1])
791 XFreePixmap(XtDisplay(w), w->dclock.colon[1]);
792 w->dclock.colon[1] = XCreatePixmap(XtDisplay(w), XtWindow(w),
793 (int)(slope_add + 0.5 * digit_w), (int)digit_h,
794 DefaultDepthOfScreen(XtScreen(w)));
795
796 XFillRectangle(XtDisplay(w), w->dclock.colon[1],
797 w->dclock.backGC, 0, 0, (int)(slope_add + 0.5 * digit_w), (int)digit_h);
798
799 /* The additive 2 for the Arc diameter keeps the colon from looking too
800 small at small display sizes. */
801
802 XfFillArc(XtDisplay(w), w->dclock.colon[1], gc,
803 (slope_add + digit_w) * 0.25 - 0.5, 0.75 * digit_h - 0.5,
804 1 + seg_width/2, FADE_ITER);
805 XfFillArc(XtDisplay(w), w->dclock.colon[1], gc,
806 slope_add * 0.75 + digit_w * 0.25 - 0.5, 0.25 * digit_h - 0.5,
807 1 + seg_width/2, FADE_ITER);
808 XSetForeground(XtDisplay(w), gc, fadevector[FADE_ITER - 1]);
809
810 /* to optimize scrolling information (see scroll_time()) */
811
812 old_pix[0] = w->dclock.digits[0];
813 old_pix[1] = old_pix[2] = old_pix[3] = 0;
814 old_digs[0] = old_digs[1] = old_digs[2] = old_digs[3] = 0;
815
816 if (w->dclock.date_fmt || !w->dclock.display_time || w->dclock.alarm ||
817 w->dclock.bell)
818 /* restore size */
819 winheight = w->core.height;
820
821 w->dclock.punt_resize = (XtIntervalId)NULL;
822 Redisplay(w);
823 }
824
825 /* ARGSUSED */
826 static void
Resize(w)827 Resize (w)
828 DclockWidget w;
829 {
830 /* Punt for 1/10 second on the resize, so that multiple events */
831 /* created by an opaque resize do not overload the processor. */
832
833 XtAppContext app;
834
835 app = XtWidgetToApplicationContext((Widget)w);
836 if (w->dclock.punt_resize != (XtIntervalId)NULL)
837 XtRemoveTimeOut(w->dclock.punt_resize);
838 w->dclock.punt_resize = XtAppAddTimeOut(app,
839 (unsigned long)100, ResizeNow, (XtPointer)w);
840 }
841
842
843 static void
build_segments(wi,seg_pts,w,h)844 build_segments(wi, seg_pts, w, h)
845 DclockWidget wi;
846 segment_pts seg_pts;
847 float w, h;
848 {
849 XfPoint *pts;
850 float spacer, hskip, fslope, bslope, midpt, seg_width, segxw;
851 float invcosphi, invsinphi, invcospsi, invsinpsi, slope;
852 float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy5, dy6;
853 float xfactor, temp_xpts[4];
854
855 /* define various useful constants */
856
857 segxw = sxw * w;
858 slope = wi->dclock.angle;
859 seg_width = wi->dclock.width_factor * w;
860 spacer = (float)w * wi->dclock.space_factor;
861 hskip = (float)seg_width * 0.125;
862 fslope = 1 / (segxw/seg_width + 1/slope);
863 bslope = -1 / (segxw/seg_width - 1/slope);
864 midpt = h / 2;
865
866 /* define some trigonometric values */
867 /* phi is the forward angle separating two segments;
868 psi is the reverse angle separating two segments. */
869
870 invsinphi = sqrt(1 + fslope * fslope) / fslope;
871 invcosphi = sqrt(1 + 1/(fslope * fslope)) * fslope;
872 invsinpsi = sqrt(1 + bslope * bslope) / -bslope;
873 invcospsi = sqrt(1 + 1/(bslope * bslope)) * bslope;
874
875 /* define offsets from easily-calculated points for 6 situations */
876
877 dx1 = hskip * invsinphi / (slope/fslope - 1);
878 dy1 = hskip * invcosphi / (1 - fslope/slope);
879 dx2 = hskip * invsinpsi / (1 - slope/bslope);
880 dy2 = hskip * invcospsi / (bslope/slope - 1);
881 dx3 = hskip * invsinphi;
882 dx4 = hskip * invsinpsi;
883 dx5 = hskip * invsinpsi / (1 - fslope/bslope);
884 dy5 = hskip * invcospsi / (bslope/fslope - 1);
885 dx6 = dy5;
886 dy6 = dx5;
887
888 /* calculate some simple reference points */
889
890 temp_xpts[0] = spacer + (h - seg_width)/slope;
891 temp_xpts[1] = spacer + (h - seg_width/2)/slope + segxw/2;
892 temp_xpts[2] = spacer + h/slope + segxw;
893 temp_xpts[3] = temp_xpts[0] + segxw;
894
895 xfactor = w - 2 * spacer - h / slope - segxw;
896
897 /* calculate the digit positions */
898
899 pts = seg_pts[TOP];
900 pts[0].y = pts[1].y = 0;
901 pts[0].x = temp_xpts[2] - dx3;
902 pts[1].x = w - spacer - segxw + dx4;
903 pts[2].y = pts[5].y = (seg_width / 2) - dy5 - dy6;
904 pts[5].x = temp_xpts[1] + dx5 - dx6;
905 pts[2].x = pts[5].x + xfactor;
906 pts[3].y = pts[4].y = seg_width;
907 pts[4].x = temp_xpts[3] + dx4;
908 pts[3].x = temp_xpts[0] + xfactor - dx3;
909
910 pts = &(seg_pts[MIDDLE][0]);
911 pts[0].y = pts[1].y = midpt - seg_width/2;
912 pts[0].x = spacer + (h - pts[0].y)/slope + segxw;
913 pts[1].x = pts[0].x - segxw + xfactor;
914 pts[2].y = pts[5].y = midpt;
915 pts[3].y = pts[4].y = midpt + seg_width/2;
916 pts[5].x = spacer + (h - pts[5].y)/slope + segxw/2;
917 pts[2].x = pts[5].x + xfactor;
918 pts[4].x = pts[0].x - seg_width/slope;
919 pts[3].x = spacer + (h - pts[3].y)/slope + xfactor;
920
921 pts = &(seg_pts[BOTTOM][0]);
922 pts[3].y = pts[4].y = h;
923 pts[2].y = pts[5].y = h - (seg_width / 2) + dy5 + dy6;
924 pts[0].y = pts[1].y = h - seg_width;
925 pts[0].x = spacer + segxw + seg_width/slope + dx3;
926 pts[1].x = spacer + (h - pts[1].y)/slope + xfactor - dx4;
927 pts[4].x = spacer + segxw - dx4;
928 pts[5].x = spacer + segxw/2 + (h - pts[5].y)/slope + dx6 - dx5;
929 pts[2].x = pts[5].x + xfactor;
930 pts[3].x = spacer + xfactor + dx3;
931
932 pts = &(seg_pts[TOP_LEFT][0]);
933 pts[0].y = seg_width / 2 - dy6 + dy5;
934 pts[1].y = seg_width + dy2;
935 pts[2].y = seg_pts[MIDDLE][0].y - 2 * dy1;
936 pts[3].y = seg_pts[MIDDLE][5].y - 2 * dy6;
937 pts[4].y = seg_pts[MIDDLE][0].y;
938 pts[5].y = seg_width - dy1;
939 pts[0].x = temp_xpts[1] - dx5 - dx6;
940 pts[1].x = temp_xpts[3] - dx2;
941 pts[2].x = seg_pts[MIDDLE][0].x + 2 * dx1;
942 pts[3].x = seg_pts[MIDDLE][5].x - 2 * dx6;
943 pts[4].x = spacer + (h - pts[4].y)/slope;
944 pts[5].x = temp_xpts[0] + dx1;
945
946 pts = &(seg_pts[BOT_LEFT][0]);
947 pts[0].y = seg_pts[MIDDLE][5].y + 2 * dy5;
948 pts[1].y = seg_pts[MIDDLE][4].y + 2 * dy2;
949 pts[2].y = seg_pts[BOTTOM][0].y - dy1;
950 pts[3].y = seg_pts[BOTTOM][5].y - 2 * dy6;
951 pts[4].y = h - seg_width + dy2;
952 pts[5].y = midpt + seg_width/2;
953 pts[0].x = seg_pts[MIDDLE][5].x - 2 * dx5;
954 pts[1].x = seg_pts[MIDDLE][4].x - 2 * dx2;
955 pts[2].x = seg_pts[BOTTOM][0].x - dx3 + dx1;
956 pts[3].x = seg_pts[BOTTOM][5].x - 2 * dx6;
957 pts[4].x = spacer + seg_width / slope - dx2;
958 pts[5].x = spacer + (midpt - seg_width/2) / slope;
959
960 pts = &(seg_pts[TOP_RIGHT][0]);
961 pts[0].y = seg_width/2 - dy5 + dy6;
962 pts[1].y = seg_width - dy2;
963 pts[2].y = midpt - seg_width/2;
964 pts[3].y = midpt - 2 * dy5;
965 pts[4].y = pts[2].y - 2 * dy2;
966 pts[5].y = seg_width + dy1;
967 pts[0].x = temp_xpts[1] + xfactor + dx5 + dx6;
968 pts[1].x = temp_xpts[3] + xfactor + dx1;
969 pts[2].x = seg_pts[MIDDLE][0].x + xfactor;
970 pts[3].x = seg_pts[MIDDLE][5].x + xfactor + dx5 * 2;
971 pts[4].x = seg_pts[TOP_LEFT][4].x + xfactor + dx2 * 2;
972 pts[5].x = temp_xpts[0] + xfactor - dx1;
973
974 pts = &(seg_pts[BOT_RIGHT][0]);
975 pts[0].y = seg_pts[MIDDLE][2].y + 2 * dy6;
976 pts[1].y = midpt + seg_width / 2;
977 pts[2].y = h - seg_width + dy1;
978 pts[3].y = h - (seg_width / 2) + dy6 - dy5;
979 pts[4].y = h - seg_width - dy2;
980 pts[5].y = seg_pts[MIDDLE][3].y + 2 * dy1;
981 pts[0].x = seg_pts[MIDDLE][2].x + 2 * dx6;
982 pts[1].x = seg_pts[MIDDLE][3].x + segxw;
983 pts[2].x = seg_pts[BOTTOM][1].x + dx4 + segxw - dx1;
984 pts[3].x = seg_pts[BOTTOM][2].x + 2 * dx5;
985 pts[4].x = seg_pts[BOTTOM][1].x + dx4 + dx2;
986 pts[5].x = seg_pts[MIDDLE][3].x - 2 * dx1;
987
988 }
989
990 /*------------------------------------------------------------------------*/
991 /* find the squared length of a wire (or distance between two points in */
992 /* user space). */
993 /*------------------------------------------------------------------------*/
994
sqwirelen(userpt1,userpt2)995 float sqwirelen(userpt1, userpt2)
996 XfPoint *userpt1, *userpt2;
997 {
998 float xdist, ydist;
999
1000 xdist = userpt2->x - userpt1->x;
1001 ydist = userpt2->y - userpt1->y;
1002 return (xdist * xdist + ydist * ydist);
1003 }
1004
1005 /* Point-to-line segment distance measure */
1006
find_mindist(x,y,seglist,num_segs)1007 float find_mindist(x, y, seglist, num_segs)
1008 float x, y;
1009 XfPoint *seglist;
1010 int num_segs;
1011 {
1012 int i;
1013 double fd, mind = MAXFLOAT;
1014 float a, b, c, d, frac;
1015 float protod;
1016 XfPoint *pt1, *pt2, testpt;
1017
1018 testpt.x = x;
1019 testpt.y = y;
1020
1021 for (i = 0; i < num_segs; i++) {
1022 pt1 = seglist + i;
1023 pt2 = seglist + ((i + 1) % num_segs);
1024 c = sqwirelen(pt1, pt2);
1025 a = sqwirelen(pt1, &testpt);
1026 b = sqwirelen(pt2, &testpt);
1027 frac = a - b;
1028 if (frac >= c) d = b;
1029 else if (-frac >= c) d = a;
1030 else {
1031 protod = c + a - b;
1032 d = a - ((protod * protod) / (c * 4));
1033 }
1034 if (d < 0) /* due to roundoff error */
1035 fd = 0;
1036 else
1037 fd = sqrt((double)d);
1038 if (fd < mind) mind = fd;
1039 }
1040 return mind;
1041 }
1042
1043 /*------------------------*/
1044 /* Simple insideness test */
1045 /*------------------------*/
1046
test_insideness(x,y,seglist,num_segs)1047 int test_insideness(x, y, seglist, num_segs)
1048 float x, y;
1049 XfPoint *seglist;
1050 int num_segs;
1051 {
1052 int i, stval = 0;
1053 XfPoint *pt1, *pt2;
1054 float stdir;
1055 for (i = 0; i < num_segs; i++) {
1056 pt1 = seglist + i;
1057 pt2 = seglist + ((i + 1) % num_segs);
1058 stdir = (pt2->x - pt1->x) * (y - pt1->y)
1059 - (pt2->y - pt1->y) * (x - pt1->x);
1060 stval += sign(stdir);
1061 }
1062 return (abs(stval) == num_segs) ? 1 : 0;
1063 }
1064
1065 /*------------------------------------------------------*/
1066 /* Antialiasing circle-filling routine */
1067 /*------------------------------------------------------*/
1068
1069 static void
XfFillArc(dpy,pix,gc,center_x,center_y,radius,fgi)1070 XfFillArc(dpy, pix, gc, center_x, center_y, radius, fgi)
1071 Display *dpy;
1072 Drawable pix;
1073 GC gc;
1074 float center_x, center_y, radius;
1075 int fgi;
1076 {
1077 int i, j, color;
1078 float minx, miny, maxx, maxy, dist, xdist, ydist, rsq;
1079
1080 minx = center_x - radius;
1081 miny = center_y - radius;
1082 maxx = center_x + radius;
1083 maxy = center_y + radius;
1084 rsq = radius * radius;
1085
1086 /* scan through pixel centers in region */
1087
1088 for (j = (int)(miny - 1.0); j < (int)(maxy + 1.0); j++) {
1089 for (i = (int)(minx - 1.0); i < (int)(maxx + 1.0); i++) {
1090 xdist = center_x - ((float)i + 0.5);
1091 ydist = center_y - ((float)j + 0.5);
1092 dist = radius - (float)sqrt(xdist * xdist + ydist * ydist);
1093
1094 if (dist > 0) {
1095 if ((dist >= 1.0) && (fgi > 0)) color = fadevector[fgi - 1];
1096 else color = fadevector[(int)(dist * fgi)];
1097
1098 XSetForeground(dpy, gc, color);
1099 XDrawPoint(dpy, pix, gc, i, j);
1100 }
1101 }
1102 }
1103 }
1104
1105 /*------------------------------------------------------*/
1106 /* Wrapper for XFillPolygon, using floats for points */
1107 /* Antialiasing goes here, if you have a good fill */
1108 /* algorithm for it. . . */
1109 /*------------------------------------------------------*/
1110
1111 static void
XfFillPolygon(dpy,pix,gc,float_pts,num_pts,fgi)1112 XfFillPolygon(dpy, pix, gc, float_pts, num_pts, fgi)
1113 Display *dpy;
1114 Drawable pix;
1115 GC gc;
1116 XfPoint *float_pts;
1117 int num_pts, fgi;
1118 {
1119 int i, j, color;
1120 float minx, miny, maxx, maxy, dist;
1121 Boolean inside;
1122
1123 /* find boundaries */
1124
1125 minx = miny = 100000.0;
1126 maxx = maxy = -100000.0;
1127 for (i = 0; i < num_pts; i++) {
1128 if (float_pts[i].x < minx) minx = float_pts[i].x;
1129 if (float_pts[i].x > maxx) maxx = float_pts[i].x;
1130 if (float_pts[i].y < miny) miny = float_pts[i].y;
1131 if (float_pts[i].y > maxy) maxy = float_pts[i].y;
1132 }
1133
1134 /* scan through pixel centers in region */
1135
1136 for (j = (int)(miny - 1.0); j < (int)(maxy + 1.0); j++) {
1137 inside = False;
1138 for (i = (int)(minx - 1.0); i < (int)(maxx + 1.0); i++) {
1139 dist = find_mindist((float)i + 0.5, (float)j + 0.5,
1140 float_pts, num_pts);
1141 if (dist < 2.0)
1142 inside = test_insideness((float)i + 0.5, (float)j + 0.5,
1143 float_pts, num_pts);
1144 if (inside) {
1145 if ((dist >= 1.0) && (fgi > 0)) color = fadevector[fgi - 1];
1146 else {
1147 color = fadevector[(int)(dist * fgi)];
1148 }
1149 XSetForeground(dpy, gc, color);
1150 XDrawPoint(dpy, pix, gc, i, j);
1151 }
1152 }
1153 }
1154 }
1155
1156 /*------------------------------------------------------*/
1157
1158 static void
make_number(dw,pix,gc,n,seg_pts)1159 make_number(dw, pix, gc, n, seg_pts)
1160 DclockWidget dw;
1161 Pixmap pix;
1162 GC gc;
1163 int n;
1164 segment_pts seg_pts;
1165 {
1166 Display *dpy = XtDisplay(dw);
1167 int i;
1168
1169 for (i = 0; i != NUM_SEGS; ++i) {
1170 if (segmask[n] & msk(i))
1171 XfFillPolygon(dpy, pix, gc, seg_pts[i], num_segment_pts, FADE_ITER);
1172 else
1173 XfFillPolygon(dpy, pix, gc, seg_pts[i], num_segment_pts, 0);
1174 }
1175 XSetForeground(dpy, gc, fadevector[FADE_ITER - 1]);
1176 }
1177
1178 /*----------------------------------------------------------------------*/
1179 /* Variation of "make_number" to handle the first digit of "civilian */
1180 /* time", which may be the number "1" (2 segments only) or blank. */
1181 /*----------------------------------------------------------------------*/
1182
1183 static void
make_number_one(dw,pix,gc,n,seg_pts)1184 make_number_one(dw, pix, gc, n, seg_pts)
1185 DclockWidget dw;
1186 Pixmap pix;
1187 GC gc;
1188 int n;
1189 segment_pts seg_pts;
1190 {
1191 Display *dpy = XtDisplay(dw);
1192 int color;
1193
1194 color = (n == 1) ? FADE_ITER : 0;
1195 XfFillPolygon(dpy, pix, gc, seg_pts[TOP_RIGHT], num_segment_pts, color);
1196 XfFillPolygon(dpy, pix, gc, seg_pts[BOT_RIGHT], num_segment_pts, color);
1197 XSetForeground(dpy, gc, fadevector[FADE_ITER - 1]);
1198 }
1199
1200 /*------------------------------------------------------*/
1201 /*------------------------------------------------------*/
1202
1203 static void
make_fade_number(dw,pix,gc,on_msk,turn_on_msk,turn_off_msk,iter)1204 make_fade_number(dw, pix, gc, on_msk, turn_on_msk, turn_off_msk, iter)
1205 DclockWidget dw;
1206 Pixmap pix;
1207 GC gc;
1208 int on_msk, turn_on_msk, turn_off_msk;
1209 int iter;
1210 {
1211 Display *dpy = XtDisplay(dw);
1212 Pixmap on_stipple = fade_stip[iter],
1213 off_stipple = fade_stip[FADE_ITER - iter];
1214 int i;
1215
1216 if (!use_stipple) XSetFillStyle(dpy, gc, FillSolid);
1217
1218 for (i = 0; i != NUM_SEGS; ++i)
1219 {
1220 if (on_msk & msk(i))
1221 {
1222 if (use_stipple)
1223 XSetFillStyle(dpy, gc, FillSolid);
1224 XfFillPolygon(dpy, pix, gc, norm_segment_pts[i],
1225 num_segment_pts, FADE_ITER);
1226 }
1227 else if (turn_on_msk & msk(i))
1228 {
1229 if (use_stipple) {
1230 XSetStipple(dpy, gc, on_stipple);
1231 XSetFillStyle(dpy, gc, FillOpaqueStippled);
1232 }
1233 XfFillPolygon(dpy, pix, gc, norm_segment_pts[i],
1234 num_segment_pts, iter);
1235 }
1236 else if (turn_off_msk & msk(i))
1237 {
1238 if (use_stipple) {
1239 XSetStipple(dpy, gc, off_stipple);
1240 XSetFillStyle(dpy, gc, FillOpaqueStippled);
1241 }
1242 XfFillPolygon(dpy, pix, gc, norm_segment_pts[i],
1243 num_segment_pts, FADE_ITER - iter - 1);
1244 }
1245 }
1246
1247 if (use_stipple) XSetFillStyle(dpy, gc, FillSolid);
1248 XSetForeground(dpy, gc, fadevector[FADE_ITER - 1]);
1249 }
1250
1251 /*------------------------------------------------------*/
1252 /*------------------------------------------------------*/
1253
1254 static void
make_fade_number_one(dw,pix,gc,on_msk,turn_on_msk,turn_off_msk,iter)1255 make_fade_number_one(dw, pix, gc, on_msk, turn_on_msk, turn_off_msk, iter)
1256 DclockWidget dw;
1257 Pixmap pix;
1258 GC gc;
1259 int on_msk, turn_on_msk, turn_off_msk;
1260 int iter;
1261 {
1262 Display *dpy = XtDisplay(dw);
1263 Pixmap on_stipple = fade_stip[iter],
1264 off_stipple = fade_stip[FADE_ITER - iter];
1265 int i;
1266
1267 if (!use_stipple) XSetFillStyle(dpy, gc, FillSolid);
1268
1269 for (i = TOP_RIGHT; i < NUM_SEGS; ++i)
1270 {
1271 if (on_msk & msk(i))
1272 {
1273 if (use_stipple)
1274 XSetFillStyle(dpy, gc, FillSolid);
1275 XfFillPolygon(dpy, pix, gc, norm_segment_pts[i],
1276 num_segment_pts, FADE_ITER);
1277 }
1278 else if (turn_on_msk & msk(i))
1279 {
1280 if (use_stipple) {
1281 XSetStipple(dpy, gc, on_stipple);
1282 XSetFillStyle(dpy, gc, FillOpaqueStippled);
1283 }
1284 XfFillPolygon(dpy, pix, gc, norm_segment_pts[i],
1285 num_segment_pts, iter);
1286 }
1287 else if (turn_off_msk & msk(i))
1288 {
1289 if (use_stipple) {
1290 XSetStipple(dpy, gc, off_stipple);
1291 XSetFillStyle(dpy, gc, FillOpaqueStippled);
1292 }
1293 XfFillPolygon(dpy, pix, gc, norm_segment_pts[i],
1294 num_segment_pts, FADE_ITER - iter - 1);
1295 }
1296 }
1297
1298 if (use_stipple) XSetFillStyle(dpy, gc, FillSolid);
1299 XSetForeground(dpy, gc, fadevector[FADE_ITER - 1]);
1300 }
1301
1302 /* Play the bell---sophisticated version, with ability to configure */
1303 /* separate audio files for the alarm and hourly bell */
1304
playbell(DclockWidget w,int alarmtype)1305 static void playbell(DclockWidget w, int alarmtype)
1306 {
1307 /* Try using an audio process. If that fails, ring the bell */
1308 int status = 1;
1309
1310 if ((((alarmtype == 0) && (w->dclock.alarmfile != (String)NULL &&
1311 strcasecmp(w->dclock.alarmfile, "NULL"))) ||
1312 ((alarmtype == 1) && (w->dclock.bellfile != (String)NULL &&
1313 strcasecmp(w->dclock.bellfile, "NULL")))) &&
1314 w->dclock.audioplay)
1315 {
1316
1317 pid_t cpid, bell_proc;
1318 char *locargv[4];
1319 char *proot = strrchr(w->dclock.audioplay, '/');
1320
1321 if (proot == NULL) proot = w->dclock.audioplay;
1322 else proot++;
1323 locargv[0] = proot;
1324 if (alarmtype == 0)
1325 locargv[1] = w->dclock.alarmfile;
1326 else
1327 locargv[1] = w->dclock.bellfile;
1328 locargv[2] = NULL;
1329
1330 if ((cpid = fork()) < 0) {
1331 fprintf(stderr, "dclock: Fork error. Reverting to system bell.\n");
1332 w->dclock.audioplay = (String)NULL;
1333 }
1334 else if (cpid == 0) {
1335 execv(w->dclock.audioplay, locargv);
1336 fprintf(stderr, "dclock: Unable to exec \"%s\".\n", w->dclock.audioplay);
1337 exit(-1);
1338 }
1339 else {
1340 if (waitpid(cpid, &status, 0) != cpid) {
1341 fprintf(stderr, "dclock: Waitpid error (%d)\n", errno);
1342 exit(-1);
1343 }
1344 if (WEXITSTATUS(status) != 0) {
1345 fprintf(stderr, "dclock: \"%s\" returned an error. Reverting to"
1346 " system bell.\n", w->dclock.audioplay);
1347 w->dclock.audioplay = (String)NULL;
1348 }
1349 }
1350 }
1351
1352 if (WEXITSTATUS(status) != 0) {
1353 XBell(XtDisplay(w), 50);
1354 if (alarmtype == 0)
1355 XBell(XtDisplay(w), 50);
1356 }
1357 }
1358
1359 /* ARGSUSED */
1360 static void
Redisplay(w)1361 Redisplay (w)
1362 DclockWidget w;
1363 {
1364 XtAppContext app;
1365 Boolean save_scroll = w->dclock.scroll;
1366 Boolean save_fade = w->dclock.fade;
1367 time_t t;
1368
1369 if (!XtIsRealized((Widget)w))
1370 return;
1371
1372 if (w->dclock.punt_resize != (XtIntervalId)NULL) return;
1373
1374 if (w->dclock.interval_id != (XtIntervalId)NULL) {
1375 XtRemoveTimeOut(w->dclock.interval_id);
1376 w->dclock.interval_id = (XtIntervalId)NULL;
1377 }
1378 XFillRectangle(XtDisplay(w), XtWindow(w), w->dclock.backGC,
1379 0, 0, w->core.width, w->core.height);
1380 before.tm_min = before.tm_hour = before.tm_wday = -1;
1381 old_pix[0] = w->dclock.digits[0];
1382 old_pix[1] = old_pix[2] = old_pix[3] = 0;
1383 old_digs[0] = old_digs[1] = old_digs[2] = old_digs[3] = 0;
1384 w->dclock.scroll = FALSE;
1385 w->dclock.fade = FALSE;
1386 (void) show_time(w);
1387 w->dclock.scroll = save_scroll;
1388 w->dclock.fade = save_fade;
1389 app = XtWidgetToApplicationContext((Widget)w);
1390 if (w->dclock.display_time)
1391 if (w->dclock.seconds || w->dclock.blink)
1392 w->dclock.interval_id = XtAppAddTimeOut(app,
1393 (unsigned long)1000, timeout, (XtPointer)w);
1394 else {
1395 t = time(0);
1396 w->dclock.interval_id =
1397 XtAppAddTimeOut(app,
1398 (unsigned long)(60 - (t % 60)) * 1000,
1399 timeout, (XtPointer)w);
1400 }
1401 }
1402
1403 static void
draw_seconds(w,sec_val)1404 draw_seconds(w, sec_val)
1405 DclockWidget w;
1406 int sec_val;
1407 {
1408 int digitxpos, digitypos;
1409 float digit_w = w->dclock.digit_w;
1410 float digit_h = w->dclock.digit_h;
1411 Display *dpy = XtDisplay(w);
1412 Window win = XtWindow(w);
1413 GC gc = w->dclock.foreGC;
1414
1415 XSetRegion(dpy, gc, clip_small);
1416 digitxpos = (int)(winwidth - (2 * digit_w + slope_add) *
1417 w->dclock.small_ratio);
1418 digitypos = (int)(BORDER*y_ratio + (1.0 - w->dclock.small_ratio) *
1419 digit_h)+TopOffset;
1420 XSetClipOrigin(dpy, gc, digitxpos, digitypos);
1421 XCopyArea(dpy, w->dclock.tiny_digits[sec_val/10], win, gc,
1422 0, 0, (int)((digit_w + slope_add) * w->dclock.small_ratio),
1423 (int)(digit_h * w->dclock.small_ratio), digitxpos, digitypos);
1424 digitxpos += (int)(digit_w * w->dclock.small_ratio);
1425 XSetClipOrigin(dpy, gc, digitxpos, digitypos);
1426 XCopyArea(dpy, w->dclock.tiny_digits[sec_val%10], win, gc,
1427 0, 0, (int)((digit_w + slope_add) * w->dclock.small_ratio),
1428 (int)(digit_h * w->dclock.small_ratio), digitxpos, digitypos);
1429 }
1430
1431 static Boolean
show_time(w)1432 show_time(w)
1433 DclockWidget w;
1434 {
1435 char buf[11];
1436 Boolean alarm_went_off = False;
1437 time_t t = time(0);
1438 register struct tm *l_time = localtime(&t);
1439 float digit_w = w->dclock.digit_w;
1440 float digit_h = w->dclock.digit_h;
1441 int digitxpos, digitypos;
1442 Display *dpy = XtDisplay(w);
1443 Window win = XtWindow(w);
1444 GC gc = w->dclock.foreGC;
1445
1446 if (w->dclock.utc == True)
1447 l_time = gmtime(&t);
1448
1449 if (w->dclock.display_time == True) {
1450 if (w->dclock.miltime)
1451 (void) sprintf(buf, "%02d%02d", l_time->tm_hour, l_time->tm_min);
1452 else
1453 (void) sprintf(buf, "%02d%02d",
1454 (l_time->tm_hour) ? ((l_time->tm_hour <= 12) ?
1455 l_time->tm_hour : l_time->tm_hour - 12) : 12,
1456 l_time->tm_min);
1457 } else
1458 /* display the alarm time */
1459 (void) sprintf(buf, "%02d%02d", Alarm.hrs, Alarm.mins);
1460
1461 /* Copy the pre-defined pixmaps onto the viewing area.*/
1462 /* Start with the colon. */
1463
1464 XSetRegion(dpy, gc, clip_colon);
1465 digitxpos = cx;
1466 digitypos = (int)(BORDER*y_ratio)+TopOffset;
1467 XSetClipOrigin (dpy, gc, digitxpos, digitypos);
1468 XCopyArea(dpy,
1469 w->dclock.colon[!w->dclock.blink || l_time->tm_sec & 1],
1470 win, gc, 0, 0, (int)(slope_add + 0.5 * digit_w), (int)digit_h,
1471 digitxpos, digitypos);
1472
1473 /* Next the seconds. */
1474 if (w->dclock.seconds)
1475 draw_seconds(w, l_time->tm_sec);
1476
1477 /* The large digits are displayed by the scrolling/fading routine. */
1478 if (l_time->tm_min != before.tm_min || l_time->tm_hour != before.tm_hour)
1479 scroll_time(w, buf);
1480
1481 XSetClipMask(dpy,gc,None);
1482
1483 if ((w->dclock.date_fmt && before.tm_wday != l_time->tm_wday) ||
1484 (!w->dclock.display_time))
1485 show_date(w, l_time);
1486 if (w->dclock.alarm) show_alarm(w);
1487 if (w->dclock.bell) show_bell(w);
1488
1489 before = *l_time;
1490
1491 if (w->dclock.alarm && Alarm.hrs == l_time->tm_hour &&
1492 Alarm.mins == l_time->tm_min &&
1493 l_time->tm_sec < 5) {
1494 bsave |= 0x4; /* set alarm_active state */
1495 playbell(w, 0);
1496 toggle_reverse_video(w);
1497 alarm_went_off = True;
1498 } else {
1499 /* if alarm didn't go off, check for hour/half-hour bell */
1500 if (w->dclock.bell && (!w->dclock.seconds || l_time->tm_sec == 0) &&
1501 (l_time->tm_min == 0 || l_time->tm_min == 30)) {
1502 playbell(w, 1);
1503 if (l_time->tm_min == 0)
1504 playbell(w, 1);
1505 }
1506
1507 /* avoid leaving clock in reverse_video state after alarm, unless */
1508 /* alarmPersist is set, in which case we always leave dclock in */
1509 /* reverse video after an alarm. */
1510
1511 if (bsave & 0x4) {
1512 if (((bsave & 0x1) == ((bsave & 0x2) >> 1)) ^
1513 ((w->dclock.alarm_persist) ? 0 : 1))
1514 toggle_reverse_video(w);
1515
1516 bsave &= 0x3; /* clear alarm_active state */
1517 }
1518 }
1519 return alarm_went_off;
1520 }
1521
1522
1523 static void
scroll_time(w,p)1524 scroll_time(w, p)
1525 DclockWidget w;
1526 register char *p;
1527 {
1528 int chgd[4], J = winheight - BORDER*2*y_ratio + 1;
1529 register int i, j, incr;
1530 float digit_w = w->dclock.digit_w;
1531 float digit_h = w->dclock.digit_h;
1532 Display *dpy = XtDisplay(w);
1533 Window win = XtWindow(w);
1534 GC gc = w->dclock.foreGC;
1535 Pixmap new_pix[4];
1536 int new_digs[4], digitxpos, digitypos;
1537 int cur_sec = 0;
1538 time_t t;
1539 register struct tm *now;
1540
1541 /* definitions for the window x and y positions of each of the large digits. */
1542
1543 #define x (int)((i>1)*(digit_w / 2) + digit_w * i - (!w->dclock.miltime) \
1544 *((1 - 2 * w->dclock.space_factor) * digit_w - segxwidth))
1545 #define y (int)((BORDER * y_ratio)+TopOffset)
1546
1547 for (i = 0; i < 4; i++)
1548 new_digs[i] = *p++ - '0';
1549
1550 if (w->dclock.miltime)
1551 new_pix[0] = w->dclock.digits[new_digs[0]];
1552 else
1553 new_pix[0] = w->dclock.digit_one[new_digs[0]];
1554
1555 for (i = 1; i < 4; i++)
1556 new_pix[i] = w->dclock.digits[new_digs[i]];
1557
1558 digitypos = (int)(BORDER*y_ratio)+TopOffset;
1559
1560 /* digit scrolling routine. */
1561
1562 if (w->dclock.scroll)
1563 {
1564 for (i = 0; i < 4; i++) /* if pixmaps don't match, scroll it */
1565 chgd[i] = (new_pix[i] != old_pix[i]);
1566
1567 if (w->dclock.date_fmt || !w->dclock.display_time)
1568 J -= w->dclock.font->ascent + w->dclock.font->descent;
1569
1570 if ((incr = J / 30) < 1)
1571 incr = 1;
1572
1573 XSetRegion(dpy, gc, clip_norm);
1574 for (j = 0; j <= J; j += incr)
1575 for (i = 0; i < 4; i++)
1576 if (chgd[i]) {
1577 digitxpos = x;
1578 XSetClipOrigin(dpy, gc, digitxpos, digitypos);
1579 if (old_pix[i])
1580 XCopyArea(dpy, old_pix[i], win, gc,
1581 0, j, (int)(digit_w + slope_add),
1582 (int)(digit_h) - j, digitxpos, y);
1583
1584 XCopyArea(dpy, new_pix[i], win, gc,
1585 0, 0, (int)(digit_w + slope_add), j,
1586 digitxpos, y + (int)digit_h - j);
1587 }
1588 XSetClipMask(dpy, gc, None);
1589 }
1590
1591 /* digit fading routine */
1592
1593 else if (w->dclock.fade)
1594 {
1595 Pixmap tmp_pix[4];
1596 int oldmask, newmask;
1597 int stay_on[4], turn_on[4], turn_off[4];
1598 unsigned long fade_rate = w->dclock.fade_rate * 1000;
1599
1600 for (i = 0; i < 4; i++) /* if pixmaps don't match, fade it */
1601 if ((chgd[i] = (new_pix[i] != old_pix[i])))
1602 {
1603 tmp_pix[i] = XCreatePixmap(dpy, win, (int)(digit_w + slope_add),
1604 (int)digit_h, DefaultDepthOfScreen(XtScreen(w)));
1605 oldmask = segmask[old_digs[i]];
1606 newmask = segmask[new_digs[i]];
1607 stay_on[i] = oldmask & newmask;
1608 turn_on[i] = ~oldmask & newmask;
1609 turn_off[i] = oldmask & ~newmask;
1610 }
1611 else
1612 tmp_pix[i] = (Pixmap)NULL;
1613
1614 XSetClipMask(dpy, gc, None);
1615 for (j = 1; j != FADE_ITER; ++j)
1616 {
1617 for (i = 0; i < 4; i++)
1618 if (chgd[i])
1619 {
1620 XFillRectangle(dpy, tmp_pix[i], w->dclock.backGC,
1621 0, 0, (int)(digit_w + slope_add), (int)digit_h);
1622 if (i == 0 && !w->dclock.miltime)
1623 make_fade_number_one(w, tmp_pix[i], gc, stay_on[i],
1624 turn_on[i], turn_off[i], j);
1625 else
1626 make_fade_number(w, tmp_pix[i], gc, stay_on[i],
1627 turn_on[i], turn_off[i], j);
1628 digitxpos = x;
1629 XSetRegion(dpy, gc, clip_norm);
1630 XSetClipOrigin(dpy, gc, digitxpos, digitypos);
1631 XCopyArea(dpy, tmp_pix[i], win, gc, 0, 0, (int)(digit_w +
1632 slope_add), (int)digit_h, digitxpos, y);
1633 XSetClipMask(dpy, gc, None);
1634 }
1635
1636 XFlush(dpy);
1637 usleep(fade_rate);
1638
1639 /* Keep tabs on time; it's necessary to update the seconds */
1640 /* if fade_rate * FADE_ITER + (overhead) > 1 second */
1641
1642 t = time(0);
1643 now = localtime(&t);
1644 if (w->dclock.seconds && (now->tm_sec != cur_sec)) {
1645 cur_sec = now->tm_sec;
1646 draw_seconds(w, cur_sec);
1647 XSetClipMask(dpy,gc,None);
1648 }
1649 }
1650
1651 for (i = 0; i < 4; ++i)
1652 if (tmp_pix[i]) XFreePixmap(dpy, tmp_pix[i]);
1653 }
1654
1655 XSetRegion(dpy, gc, clip_norm);
1656 for (i = 0; i < 4; i++) {
1657 digitxpos = x;
1658 XSetClipOrigin(dpy, gc, digitxpos, digitypos);
1659 XCopyArea(dpy, new_pix[i], win, gc, 0,0, (int)(digit_w +
1660 slope_add), (int)digit_h, digitxpos, y);
1661 }
1662 XSetClipMask(dpy, gc, None);
1663 if (!w->dclock.display_time) outline_digit(w, cur_position, True);
1664
1665 for (i = 0; i < 4; i++) {
1666 old_pix[i] = new_pix[i];
1667 old_digs[i] = new_digs[i];
1668 }
1669
1670 #undef x
1671 #undef y
1672
1673 }
1674
1675 static void
show_date(w,now)1676 show_date(w, now)
1677 DclockWidget w;
1678 struct tm *now;
1679 {
1680 Display *dpy = XtDisplay(w);
1681 Window win = XtWindow(w);
1682 char datestr[128];
1683 register char *p;
1684 int x, datep;
1685 int tsize;
1686 #ifdef XFT_SUPPORT
1687 XGlyphInfo xftextents;
1688 #endif
1689
1690 if (XFT_COND(w->dclock)) {
1691 tsize = w->core.height / 6;
1692 } else {
1693 tsize = w->dclock.font->ascent + w->dclock.font->descent;
1694 }
1695
1696 if (!w->dclock.display_time)
1697 datep = strlen(strcpy(datestr, "Push HERE to Set/Unset Alarm"));
1698 else
1699 datep = strftime(datestr, 128, w->dclock.date_fmt, now);
1700
1701 if (XFT_COND(w->dclock)) {
1702 #ifdef XFT_SUPPORT
1703 XftTextExtents8(dpy, w->dclock.xftfont, (FcChar8*)datestr, datep, &xftextents);
1704 x = (w->core.width - xftextents.width) / 2;
1705 #endif
1706 } else {
1707 x = (w->core.width - XTextWidth(w->dclock.font, datestr, datep)) / 2;
1708 }
1709
1710 if (x < 2)
1711 x = 2;
1712 if (TopOffset) {
1713 if (XFT_COND(w->dclock)) {
1714 #ifdef XFT_SUPPORT
1715 XftDrawRect(w->dclock.xftdraw, &w->dclock.xftbg, 0, 0, winwidth, tsize);
1716 XftDrawString8(w->dclock.xftdraw, &w->dclock.xftfg, w->dclock.xftfont, x,
1717 (BORDER/2) + tsize - 2, (FcChar8*)datestr, datep);
1718 #endif
1719 } else {
1720 XFillRectangle(dpy, win, w->dclock.backGC,
1721 0, 0, winwidth, tsize);
1722 XDrawString(dpy, win, w->dclock.foreGC,
1723 x, ((BORDER/2)+tsize), datestr, datep);
1724 }
1725 } else {
1726 if (XFT_COND(w->dclock)) {
1727 #ifdef XFT_SUPPORT
1728 XftDrawRect(w->dclock.xftdraw, &w->dclock.xftbg, 0, winheight - tsize,
1729 winwidth, tsize);
1730 XftDrawString8(w->dclock.xftdraw, &w->dclock.xftfg, w->dclock.xftfont, x,
1731 winheight - BORDER - 2, (FcChar8*)datestr, datep);
1732 #endif
1733 } else {
1734 XFillRectangle(dpy, win, w->dclock.backGC,
1735 0, winheight - tsize, winwidth, tsize);
1736 XDrawString(dpy, win, w->dclock.foreGC,
1737 x, winheight - BORDER, datestr, datep);
1738 }
1739 }
1740 }
1741
1742 static void
show_alarm(w)1743 show_alarm(w)
1744 DclockWidget w;
1745 {
1746 Display *dpy = XtDisplay(w);
1747 Window win = XtWindow(w);
1748 int tsize = w->dclock.font->ascent + w->dclock.font->descent;
1749 int ccentx = BORDER;
1750 int hcsize = tsize >> 1;
1751 int ccenty;
1752
1753 if (TopOffset)
1754 ccenty = BORDER + (hcsize/2);
1755 else
1756 ccenty = winheight - BORDER - hcsize;
1757
1758 hcsize /= 2;
1759 XDrawArc(dpy, win, w->dclock.foreGC, ccentx - hcsize,
1760 ccenty - hcsize, hcsize << 1, hcsize << 1, -2800, 5600);
1761 hcsize *= 2;
1762 XDrawArc(dpy, win, w->dclock.foreGC, ccentx - hcsize,
1763 ccenty - hcsize, hcsize << 1, hcsize << 1, -2800, 5600);
1764 hcsize *= 1.5;
1765 XDrawArc(dpy, win, w->dclock.foreGC, ccentx - hcsize,
1766 ccenty - hcsize, hcsize << 1, hcsize << 1, -2800, 5600);
1767 }
1768
1769 static void
show_bell(w)1770 show_bell(w)
1771 DclockWidget w;
1772 {
1773 Display *dpy = XtDisplay(w);
1774 Window win = XtWindow(w);
1775 int tsize = w->dclock.font->ascent + w->dclock.font->descent;
1776 int hcsize = tsize >> 1;
1777 int ccentx = BORDER + ((w->dclock.alarm) ? (tsize * 3 / 2) : (tsize / 3));
1778 int ccenty;
1779
1780 if (TopOffset)
1781 ccenty = BORDER + (hcsize/2);
1782 else
1783 ccenty = winheight - BORDER - hcsize;
1784
1785 XDrawArc(dpy, win, w->dclock.foreGC, ccentx - tsize / 3,
1786 ccenty - tsize / 2, tsize * 2 / 3, tsize * 2 / 3, 0, 11520);
1787
1788 XDrawLine(dpy, win, w->dclock.foreGC, ccentx - tsize / 3,
1789 ccenty - tsize / 6, ccentx - tsize / 3, ccenty + tsize / 3);
1790 XDrawLine(dpy, win, w->dclock.foreGC, ccentx + tsize / 3,
1791 ccenty - tsize / 6, ccentx + tsize / 3, ccenty + tsize / 3);
1792
1793 XDrawArc(dpy, win, w->dclock.foreGC, ccentx - tsize * 2 / 3,
1794 ccenty + tsize / 6, tsize / 3, tsize / 3, 17280, 5760);
1795 XDrawArc(dpy, win, w->dclock.foreGC, ccentx + tsize / 3,
1796 ccenty + tsize / 6, tsize / 3, tsize / 3, 11520, 5760);
1797
1798 XDrawLine(dpy, win, w->dclock.foreGC, ccentx - tsize / 2,
1799 ccenty + tsize / 2, ccentx + tsize / 2, ccenty + tsize / 2);
1800
1801 XDrawArc(dpy, win, w->dclock.foreGC, ccentx - tsize / 12,
1802 ccenty + tsize / 2, tsize / 6, tsize / 6, 0, 23040);
1803 }
1804
1805
1806 static void
timeout(w,id)1807 timeout(w, id)
1808 DclockWidget w;
1809 XtIntervalId *id;
1810 {
1811 struct timeval now;
1812 int next_intr;
1813 Boolean alarm_went_off = show_time(w);
1814 XtAppContext app;
1815
1816 /* Try to catch the next second on the second boundary. */
1817
1818 /* NO! This is a BAD IDEA, and leads to catching the */
1819 /* time sometimes before the second, sometimes after, and */
1820 /* thereby skipping seconds all the time. Instead, we */
1821 /* should aim for a few milliseconds LATE (try 5ms). */
1822
1823 gettimeofday(&now, NULL);
1824 /* next_intr = (1e6 - now.tv_usec) / 1e3; */
1825 next_intr = (1e6 - now.tv_usec + 5000) / 1e3;
1826
1827 app = XtWidgetToApplicationContext((Widget)w);
1828 w->dclock.interval_id =
1829 XtAppAddTimeOut(app, (unsigned long)((alarm_went_off
1830 || w->dclock.seconds || w->dclock.blink) ?
1831 next_intr : 59000 + next_intr), timeout,
1832 (XtPointer)w);
1833 }
1834
1835 /* ARGSUSED */
1836 static Boolean
SetValues(current,request,new)1837 SetValues (current, request, new)
1838 DclockWidget current, request, new;
1839 {
1840 Boolean do_redraw = False;
1841
1842 if (current->dclock.display_time != new->dclock.display_time) {
1843 before.tm_min = before.tm_hour = before.tm_wday = -1;
1844 new->dclock.miltime = True; /* old state needs to be saved */
1845 do_redraw = True;
1846 }
1847 if (current->dclock.alarm_time != new->dclock.alarm_time) {
1848 if (sscanf(new->dclock.alarm_time, "%2d:%2d",
1849 &Alarm.hrs, &Alarm.mins) != 2 || Alarm.hrs >= 24 ||
1850 Alarm.mins >= 60) {
1851 XtWarning("Alarm Time is in incorrect format.");
1852 new->dclock.alarm_time = "00:00";
1853 Alarm.hrs = Alarm.mins = 0;
1854 }
1855 new->dclock.alarm_time =
1856 strcpy(XtMalloc(strlen(new->dclock.alarm_time)+1),
1857 new->dclock.alarm_time);
1858 do_redraw = True;
1859 }
1860
1861 if (new->dclock.foreground != current->dclock.foreground
1862 || new->dclock.background != current->dclock.background
1863 || new->dclock.tails != current->dclock.tails
1864 || new->dclock.fade != current->dclock.fade
1865 || new->dclock.miltime != current->dclock.miltime) {
1866 XtReleaseGC ((Widget)current, current->dclock.foreGC);
1867 XtReleaseGC ((Widget)current, current->dclock.backGC);
1868 GetGC(new);
1869 ResizeNow(new); /* pixmaps need to be redrawn */
1870 do_redraw = True;
1871 }
1872 if (new->dclock.seconds != current->dclock.seconds) {
1873 if (current->dclock.interval_id != (XtIntervalId)NULL) {
1874 XtRemoveTimeOut(current->dclock.interval_id);
1875 current->dclock.interval_id = (XtIntervalId)NULL;
1876 }
1877 ResizeNow(new);
1878 do_redraw = True;
1879 }
1880 if (new->dclock.date_fmt != current->dclock.date_fmt) {
1881 do_redraw = True;
1882 before.tm_wday = -1;
1883 }
1884 if (new->dclock.alarm != current->dclock.alarm)
1885 do_redraw = True;
1886
1887 return do_redraw;
1888 }
1889
1890 /* The following routines are called by the various keypress events. */
1891
1892 static void
decrease_space(w)1893 decrease_space(w)
1894 DclockWidget w;
1895 {
1896 w->dclock.space_factor *= 0.95;
1897 if (w->dclock.space_factor < 0.0) w->dclock.space_factor = 0;
1898 ResizeNow(w);
1899 Redisplay(w);
1900 }
1901
1902 static void
increase_space(w)1903 increase_space(w)
1904 DclockWidget w;
1905 {
1906 w->dclock.space_factor *= 1.05;
1907 if (w->dclock.space_factor > 0.25) w->dclock.space_factor = 0.25;
1908 ResizeNow(w);
1909 Redisplay(w);
1910 }
1911
1912 static void
decrease_slope(w)1913 decrease_slope(w)
1914 DclockWidget w;
1915 {
1916 w->dclock.angle *= 1.05;
1917 if (w->dclock.angle > 2 * winheight) w->dclock.angle = 2 * winheight;
1918 ResizeNow(w);
1919 Redisplay(w);
1920 }
1921
1922 static void
increase_slope(w)1923 increase_slope(w)
1924 DclockWidget w;
1925 {
1926 w->dclock.angle *= 0.95;
1927 if (w->dclock.angle < 1.0) w->dclock.angle = 1.0;
1928 ResizeNow(w);
1929 Redisplay(w);
1930 }
1931
1932 static void
widen_segment(w)1933 widen_segment(w)
1934 DclockWidget w;
1935 {
1936 w->dclock.width_factor *= 1.05;
1937 if (w->dclock.width_factor > 0.4) w->dclock.width_factor = 0.4;
1938 ResizeNow(w);
1939 Redisplay(w);
1940 }
1941
1942 static void
thin_segment(w)1943 thin_segment(w)
1944 DclockWidget w;
1945 {
1946 w->dclock.width_factor *= 0.95;
1947 if (w->dclock.width_factor < 0.01) w->dclock.width_factor = 0.01;
1948 ResizeNow(w);
1949 Redisplay(w);
1950 }
1951
1952 static void
print_help(w)1953 print_help(w)
1954 DclockWidget w;
1955 {
1956 fprintf(stderr, "In-Window single-character commands:\n\n");
1957 fprintf(stderr, " r Toggles Reverse Video.\n");
1958 fprintf(stderr, " s Toggles the seconds display.\n");
1959 fprintf(stderr, " b Toggles the bell attribute.\n");
1960 fprintf(stderr, " j Toggles the jump/scroll attribute.\n");
1961 fprintf(stderr, " f Toggles the fade attribute.\n");
1962 fprintf(stderr, " d Toggles the date format.\n");
1963 fprintf(stderr, " u Toggles the location of the date (top/bottom).\n");
1964 fprintf(stderr, " m Toggles the military time format.\n");
1965 fprintf(stderr, " a Toggles the alarm clock.\n");
1966 fprintf(stderr, " t Toggles the tails attribute.\n");
1967 fprintf(stderr, " v Ascii dump of the values of various attributes.\n");
1968 fprintf(stderr, " h Print this help screen.\n");
1969 fprintf(stderr, " ? Same as h.\n");
1970 fprintf(stderr, " : Toggles the blinking colon.\n");
1971 fprintf(stderr, " / Increases the slope of the digits.\n");
1972 fprintf(stderr, " \\ Decreases the slope of the digits.\n");
1973 fprintf(stderr, " + Increases the thickness of the digit segments.\n");
1974 fprintf(stderr, " - Decreases the thickness of the digit segments.\n");
1975 fprintf(stderr, " > Increases spacing between the digits.\n");
1976 fprintf(stderr, " < Decreases spacing between the digits.\n");
1977 fprintf(stderr, " q quit the program.\n");
1978 fprintf(stderr, " <Btn3> Toggle Alarm-time display\n");
1979 fprintf(stderr, " <Btn1> Increment number in alarm-time display\n");
1980 fprintf(stderr, " <Btn2> Decrement number in alarm-time display\n");
1981 fprintf(stderr, "\n\n");
1982 }
1983
1984 static void
print_dump(w)1985 print_dump(w)
1986 DclockWidget w;
1987 {
1988 fprintf(stderr, "Window attributes:\n\n");
1989 fprintf(stderr, " window width = %d\n", winwidth);
1990 fprintf(stderr, " window height = %d\n", winheight);
1991 fprintf(stderr, " slope = %f\n", w->dclock.angle);
1992 fprintf(stderr, " segment width ratio = %f\n", w->dclock.width_factor);
1993 fprintf(stderr, " seconds gap ratio = %f\n", w->dclock.sec_gap);
1994 fprintf(stderr, " small digit ratio = %f\n", w->dclock.small_ratio);
1995 fprintf(stderr, " digit spacing ratio = %f\n", w->dclock.space_factor);
1996 fprintf(stderr, " fade rate = %d\n", w->dclock.fade_rate);
1997 fprintf(stderr, " date format = %s\n", (w->dclock.date_fmt) ?
1998 (w->dclock.date_fmt) : "<null>");
1999 fprintf(stderr, " date location = %s\n", (w->dclock.dateup ? "top" : "bottom"));
2000 fprintf(stderr, "\n\n");
2001 }
2002
2003 static void
toggle_date(w)2004 toggle_date(w)
2005 DclockWidget w;
2006 {
2007 char *tmp;
2008
2009 if (!w->dclock.display_time) {
2010 XBell(XtDisplay(w), 50);
2011 return;
2012 }
2013 tmp = w->dclock.date_fmt;
2014 w->dclock.date_fmt = saved_date;
2015 saved_date = tmp;
2016
2017 if (w->dclock.dateup && w->dclock.date_fmt) {
2018 if (XFT_COND(w->dclock)) {
2019 TopOffset = w->core.height / 6;
2020 } else {
2021 TopOffset = w->dclock.font->ascent + w->dclock.font->descent;
2022 }
2023 } else
2024 TopOffset = 0;
2025
2026 before.tm_wday = -1;
2027 ResizeNow(w);
2028 Redisplay(w);
2029 }
2030
2031 static void
toggle_dateup(w)2032 toggle_dateup(w)
2033 DclockWidget w;
2034 {
2035 Arg arg;
2036 int tmp;
2037
2038 if (!w->dclock.display_time) {
2039 XBell(XtDisplay(w), 50);
2040 return;
2041 }
2042
2043 XtSetArg(arg, XtNdateUp, !w->dclock.dateup);
2044 XtSetValues((Widget)w, &arg, 1);
2045
2046 if (w->dclock.dateup && w->dclock.date_fmt) {
2047 if (XFT_COND(w->dclock)) {
2048 TopOffset = w->core.height / 6;
2049 } else {
2050 TopOffset = w->dclock.font->ascent + w->dclock.font->descent;
2051 }
2052 } else
2053 TopOffset = 0;
2054
2055 ResizeNow(w);
2056 Redisplay(w);
2057 }
2058
2059 static void
toggle_bell(w)2060 toggle_bell(w)
2061 DclockWidget w;
2062 {
2063 if ((w->dclock.bell = !w->dclock.bell)) {
2064 playbell(w, 1);
2065 }
2066 ResizeNow(w);
2067 Redisplay(w);
2068 }
2069
2070 static void
toggle_scroll(w)2071 toggle_scroll(w)
2072 DclockWidget w;
2073 {
2074 w->dclock.scroll = !w->dclock.scroll;
2075 }
2076
2077 static void
toggle_reverse_video(w)2078 toggle_reverse_video(w)
2079 DclockWidget w;
2080 {
2081 Display *dpy = XtDisplay(w);
2082 int i, itemp;
2083
2084 if (!(bsave & 0x1)) {
2085 XSetForeground(dpy, w->dclock.backGC, w->dclock.foreground);
2086 bsave |= 0x1; /* set reverse-video state */
2087 }
2088 else {
2089 XSetForeground(dpy, w->dclock.backGC, w->dclock.background);
2090 bsave &= 0x5; /* clear reverse-video state */
2091 }
2092
2093 if (!(bsave & 0x4))
2094 bsave = (bsave & 0x1) ? 0x3 : 0x0;
2095
2096 for (i = 0; i < (FADE_ITER >> 1); i++) {
2097 itemp = fadevector[i];
2098 fadevector[i] = fadevector[FADE_ITER - i - 1];
2099 fadevector[FADE_ITER - i - 1] = itemp;
2100 }
2101 ResizeNow(w);
2102 Redisplay(w);
2103 }
2104
2105 static void
toggle_military_time(w)2106 toggle_military_time(w)
2107 DclockWidget w;
2108 {
2109 Arg arg;
2110
2111 if (!w->dclock.display_time) {
2112 XBell(XtDisplay(w), 50);
2113 return;
2114 }
2115 XtSetArg(arg, XtNmilitaryTime, !w->dclock.miltime);
2116 XtSetValues((Widget)w, &arg, 1);
2117 }
2118
2119 static void
toggle_blink(w)2120 toggle_blink(w)
2121 DclockWidget w;
2122 {
2123 Arg arg;
2124
2125 if (!w->dclock.display_time) {
2126 XBell(XtDisplay(w), 50);
2127 return;
2128 }
2129 XtSetArg(arg, XtNblink, !w->dclock.blink);
2130 XtSetValues((Widget)w, &arg, 1);
2131 }
2132
2133 static void
toggle_seconds(w)2134 toggle_seconds(w)
2135 DclockWidget w;
2136 {
2137 Arg arg;
2138
2139 if (!w->dclock.display_time) {
2140 XBell(XtDisplay(w), 50);
2141 return;
2142 }
2143 XtSetArg(arg, XtNseconds, !w->dclock.seconds);
2144 XtSetValues((Widget)w, &arg, 1);
2145 }
2146
2147 static void
toggle_fade(w)2148 toggle_fade(w)
2149 DclockWidget w;
2150 {
2151 Arg arg;
2152
2153 XtSetArg(arg, XtNfade, !w->dclock.fade);
2154 XtSetValues((Widget)w, &arg, 1);
2155 if (w->dclock.fade && w->dclock.scroll)
2156 toggle_scroll(w);
2157 }
2158
2159 static void
toggle_tails(w)2160 toggle_tails(w)
2161 DclockWidget w;
2162 {
2163 Arg arg;
2164
2165 XtSetArg(arg, XtNtails, !w->dclock.tails);
2166 XtSetValues((Widget)w, &arg, 1);
2167 }
2168
2169 static void
toggle_alarm(w)2170 toggle_alarm(w)
2171 DclockWidget w;
2172 {
2173 Arg arg;
2174
2175 XtSetArg(arg, XtNalarm, !w->dclock.alarm);
2176 XtSetValues((Widget)w, &arg, 1);
2177 }
2178
2179 static void
set_alarm(w,event)2180 set_alarm(w, event)
2181 DclockWidget w;
2182 XButtonEvent *event;
2183 {
2184 static int saved_secs, saved_miltime, saved_fade, saved_blink;
2185
2186 if (event->button == 3) {
2187 if (!(w->dclock.display_time = !w->dclock.display_time)) {
2188 XtRemoveTimeOut(w->dclock.interval_id);
2189 saved_secs = w->dclock.seconds, w->dclock.seconds = False;
2190 saved_miltime = w->dclock.miltime, w->dclock.miltime = True;
2191 saved_fade = w->dclock.fade, w->dclock.fade = False;
2192 saved_blink = w->dclock.blink, w->dclock.blink = False;
2193 w->dclock.interval_id = (XtIntervalId)NULL;
2194 } else {
2195 w->dclock.seconds = saved_secs;
2196 w->dclock.miltime = saved_miltime;
2197 w->dclock.fade = saved_fade;
2198 w->dclock.blink = saved_blink;
2199 }
2200 ResizeNow(w);
2201 Redisplay(w);
2202 } else if (!w->dclock.display_time &&
2203 (event->button == 1 || event->button == 2)) {
2204 /* get the digit under the position (1-4) the mouse is over
2205 * and increment (possibly wrap around) to next digit.
2206 */
2207 int i, x, y = (int)((BORDER/2)*y_ratio) + TopOffset;
2208 /* first check to see if user toggles the alarm */
2209 if (XFT_COND(w->dclock)) {
2210 if (TopOffset)
2211 i = (BORDER + (w->core.height / 6))
2212 - event->y;
2213 else
2214 i = event->y - (winheight - (w->core.height / 6));
2215 } else {
2216 if (TopOffset)
2217 i = (BORDER + (w->dclock.font->ascent + w->dclock.font->descent))
2218 - event->y;
2219 else
2220 i = event->y - (winheight - (w->dclock.font->ascent
2221 + w->dclock.font->descent));
2222 }
2223 if (i >= 0)
2224 toggle_alarm(w);
2225 else for (i = 0; i < 4; i++) {
2226 x = i * w->dclock.digit_w + (i>1) * (w->dclock.digit_w / 2) +
2227 (w->dclock.digit_h - event->y) / w->dclock.angle;
2228 if (event->x < x + w->dclock.digit_w) {
2229 if (cur_position == i) {
2230 int digit = w->dclock.alarm_time[i>1?i+1:i] - '0';
2231 int mod;
2232 switch (i) {
2233 case 0:
2234 if (Alarm.hrs > 13 && digit == 1)
2235 digit++;
2236 mod = 3;
2237 before.tm_hour = -1;
2238 when 1 :
2239 mod = (Alarm.hrs < 20)? 10 : 4;
2240 before.tm_hour = -1;
2241 when 2:
2242 mod = 6;
2243 before.tm_min = -1;
2244 when 3 :
2245 mod = 10;
2246 before.tm_min = -1;
2247 }
2248 if (event->button == 1)
2249 digit = (digit + 1) % mod;
2250 else if (--digit < 0)
2251 digit = mod-1;
2252 w->dclock.alarm_time[i>1?i+1:i] = digit + '0';
2253 (void) sscanf(w->dclock.alarm_time, "%2d:%2d",
2254 &Alarm.hrs, &Alarm.mins);
2255 (void) show_time(w);
2256 outline_digit(w, cur_position, True);
2257 } else {
2258 outline_digit(w, cur_position, False);
2259 outline_digit(w, cur_position = i, True);
2260 }
2261 break;
2262 }
2263 }
2264 } else
2265 XBell(XtDisplay(w), 50);
2266 }
2267
2268 /* Draw a box around the digit, for use with the alarm-time setting function.*/
2269
2270 static void
outline_digit(w,i,draw_it)2271 outline_digit(w, i, draw_it)
2272 DclockWidget w;
2273 int i;
2274 Boolean draw_it;
2275 {
2276 int xbase, y, xadd;
2277 XPoint rectpts[5];
2278
2279 xbase = w->dclock.digit_w * i + (i>1) * (w->dclock.digit_w / 2);
2280 xadd = (int)(w->dclock.space_factor * w->dclock.digit_w / 4);
2281 y = 0.5 * BORDER * y_ratio;
2282
2283 rectpts[0].x = rectpts[4].x = xbase + xadd;
2284 rectpts[1].x = rectpts[0].x + ((w->dclock.digit_h + 2 * y) /
2285 w->dclock.angle);
2286 rectpts[2].x = rectpts[1].x + w->dclock.digit_w - xadd;
2287 rectpts[3].x = rectpts[0].x + w->dclock.digit_w - xadd;
2288 rectpts[0].y = rectpts[4].y = rectpts[3].y = (w->dclock.digit_h + 3 * y) + TopOffset;
2289 rectpts[1].y = rectpts[2].y = y + TopOffset;
2290
2291 XDrawLines(XtDisplay(w), XtWindow(w),
2292 draw_it? w->dclock.foreGC : w->dclock.backGC,
2293 rectpts, 5, CoordModeOrigin);
2294 }
2295