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