1 /*
2  * astime is an analogue clock utility for X Windows.
3  *
4  * Copyright (c) 1998-2000  Albert Dorofeev <albert@tigr.net>
5  * Copyright (c) 1999 William Kostis <kostis@ee.cornell.edu>
6  *
7  * For the updates see http://www.tigr.net/afterstep/as-apps/
8  *
9  * This software is distributed under GPL. For details see LICENSE file.
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <math.h>
15 #include <time.h>
16 
17 #include <X11/Xlib.h>
18 #include <X11/xpm.h>
19 #include <X11/Xatom.h>
20 
21 #include "x_color.h"
22 #include "state.h"
23 struct astime_state state;
24 
25 #include "dof.xpm"
26 #include "months.xpm"
27 #include "digits.xpm"
28 #include "ampm.xpm"
29 
30 /* nice idea from ascd */
31 typedef struct _XpmIcon {
32 	Pixmap pixmap;
33 	Pixmap mask;
34 	XpmAttributes attributes;
35 } XpmIcon;
36 
37 XpmIcon dofXpm;
38 XpmIcon ampmXpm;
39 XpmIcon monthsXpm;
40 XpmIcon digitsXpm;
41 
42 /* X windows related global variables */
43 Display *mainDisplay = 0;	/* The display we are working on */
44 Window Root;			/* The root window of X11 */
45 Pixmap backgroundWindow;	/* Window for drawing */
46 Pixmap drawWindow;		/* Window for drawing */
47 Window mainWindow;		/* Application window */
48 Window iconWindow;		/* Icon window */
49 XGCValues mainGCV;		/* graphics context values */
50 GC mainGC;			/* Graphics context */
51 Atom wm_delete_window;
52 Atom wm_protocols;
53 Font label_font;
54 XFontStruct * label_fnstruct;
55 
56 /* the label position and color */
57 int label_x, label_y;
58 Pixel label_pix;
59 
60 /* background, digits, months etc pixmap colors */
61 char bgpixmap_color[4][32];
62 char dof_pixmap_color[2][32];
63 char ampm_pixmap_color[2][32];
64 char digits_pixmap_color[2][32];
65 char months_pixmap_color[2][32];
66 
67 Pixel back_pix, fore_pix;
68 
69 /* The center of the clock */
70 struct coordinates center;
71 /* The half-size of the watch drawable area
72  * (2 more pixels are given to the frame */
73 struct coordinates watch_size;
74 
75 /* calculated values in pixels */
76 int pixelLength[3];
77 struct line_struct {
78 	int x1, y1;
79 	int x2, y2;
80 } lines[3];
81 
82 /* polygons for the hands */
83 XPoint hand[3][4];
84 
85 /* pixels we need */
86 Pixel pix[9];
87 
88 /* last time we updated */
89 time_t last_time = 0;
90 time_t curTime;
91 
92 /* current time in nice format */
93 struct tm *loc_time;
94 
95 /* requests for update */
96 int update_request = 0;
97 
98 /* float pi = 3.14159265359; */
99 
draw_window(Window win)100 void draw_window(Window win)
101 {
102 	int i;
103 
104 	XCopyArea(
105 			 mainDisplay,
106 			 backgroundWindow,
107 			 win,
108 			 mainGC,
109 			 0,
110 			 0,
111 			 state.total_size.x,
112 			 state.total_size.y,
113 			 0,
114 			 0
115 	    );
116 	if (state.draw_ampm)
117 		XCopyArea(
118 				 mainDisplay,
119 				 ampmXpm.pixmap,
120 				 win,
121 				 mainGC,
122 				 0,
123 				 (loc_time->tm_hour > 11 ? 6 : 0),
124 				 13,
125 				 7,
126 				 state.total_size.x - 14,
127 				 1
128 		    );
129 	if (state.draw_date) {
130 		XCopyArea(
131 				 mainDisplay,
132 				 dofXpm.pixmap,
133 				 win,
134 				 mainGC,
135 				 0,
136 				 loc_time->tm_wday * 6,
137 				 13,
138 				 7,
139 				 1,
140 				 1
141 		    );
142 		XCopyArea(
143 				 mainDisplay,
144 				 digitsXpm.pixmap,
145 				 win,
146 				 mainGC,
147 				 0,
148 				 (loc_time->tm_mday / 10) * 7,
149 				 7,
150 				 8,
151 				 1,
152 				 state.total_size.y - 9
153 		    );
154 		XCopyArea(
155 				 mainDisplay,
156 				 digitsXpm.pixmap,
157 				 win,
158 				 mainGC,
159 				 0,
160 				 (loc_time->tm_mday % 10) * 7,
161 				 7,
162 				 8,
163 				 8,
164 				 state.total_size.y - 9
165 		    );
166 		XCopyArea(
167 				 mainDisplay,
168 				 monthsXpm.pixmap,
169 				 win,
170 				 mainGC,
171 				 0,
172 				 loc_time->tm_mon * 6,
173 				 19,
174 				 7,
175 				 18,
176 				 state.total_size.y - 8
177 		    );
178 		/* The year won't fit in smaller displays */
179 		if (state.total_size.x > 51) {
180 			XCopyArea(
181 					 mainDisplay,
182 					 digitsXpm.pixmap,
183 					 win,
184 					 mainGC,
185 					 0,
186 				      (loc_time->tm_year % 100 / 10) * 7,
187 					 7,
188 					 8,
189 					 38,
190 					 state.total_size.y - 9
191 			    );
192 			XCopyArea(
193 					 mainDisplay,
194 					 digitsXpm.pixmap,
195 					 win,
196 					 mainGC,
197 					 0,
198 					 (loc_time->tm_year % 10) * 7,
199 					 7,
200 					 8,
201 					 45,
202 					 state.total_size.y - 9
203 			    );
204 		}
205 	}
206 #ifdef DEBUG
207 	mainGCV.foreground = pix[2];
208 	XChangeGC(
209 			 mainDisplay,
210 			 mainGC,
211 			 GCForeground,
212 			 &mainGCV
213 	    );
214 	XDrawLine(
215 			 mainDisplay,
216 			 win,
217 			 mainGC,
218 			 center.x - watch_size.x,
219 			 center.y - watch_size.y,
220 			 center.x + watch_size.x,
221 			 center.y - watch_size.y
222 	    );
223 	XDrawLine(
224 			 mainDisplay,
225 			 win,
226 			 mainGC,
227 			 center.x + watch_size.x,
228 			 center.y - watch_size.y,
229 			 center.x + watch_size.x,
230 			 center.y + watch_size.y
231 	    );
232 	XDrawLine(
233 			 mainDisplay,
234 			 win,
235 			 mainGC,
236 			 center.x + watch_size.x,
237 			 center.y + watch_size.y,
238 			 center.x - watch_size.x,
239 			 center.y + watch_size.y
240 	    );
241 	XDrawLine(
242 			 mainDisplay,
243 			 win,
244 			 mainGC,
245 			 center.x - watch_size.x,
246 			 center.y + watch_size.y,
247 			 center.x - watch_size.x,
248 			 center.y - watch_size.y
249 	    );
250 #endif
251 
252 
253 	/* draw filled polygonal hands */
254 	for (i = 2; i >= 0; i--) {
255 		if ((i == 0) && (state.draw_seconds == 0))
256 			break;
257 
258 		if (state.hprop[i][HFILL]) {
259 			mainGCV.foreground = pix[i + 6];
260 			XChangeGC(
261 					 mainDisplay,
262 					 mainGC,
263 					 GCForeground | GCLineWidth,
264 					 &mainGCV
265 			    );
266 
267 			XFillPolygon(mainDisplay, win, mainGC, hand[i], 3, Convex, CoordModeOrigin);
268 		}
269 		/* draw line-rendered hands */
270 		if (state.hprop[i][HCLINE]) {
271 			mainGCV.foreground = pix[i];
272 
273 			if ((i == 0) && state.thin_seconds) {
274 				mainGCV.line_width = 1;
275 			} else {
276 				/* scale line thickness by window size */
277 				mainGCV.line_width = rint((1 + (0.5 * i)) * (((watch_size.x + watch_size.y) / 2.0) *
278 						(state.hthick / 100.0)));
279 			}
280 
281 			XChangeGC(
282 					 mainDisplay,
283 					 mainGC,
284 					 GCForeground | GCLineWidth,
285 					 &mainGCV
286 			    );
287 			XDrawLine(
288 					 mainDisplay,
289 					 win,
290 					 mainGC,
291 					 lines[i].x1,
292 					 lines[i].y1,
293 					 lines[i].x2,
294 					 lines[i].y2
295 			    );
296 		}
297 		/* draw outlines of hands */
298 		if (state.hprop[i][HOLINE]) {
299 			mainGCV.foreground = pix[i + 3];
300 			mainGCV.line_width = rint((1 + (0.5 * i)) * (((watch_size.x + watch_size.y) / 2.0) *
301 					       (state.othick / 1000.0)));
302 			XChangeGC(
303 					 mainDisplay,
304 					 mainGC,
305 					 GCForeground | GCLineWidth,
306 					 &mainGCV
307 			    );
308 			XDrawLines(mainDisplay, win, mainGC, hand[i], 4, CoordModeOrigin);
309 
310 		}
311 	}
312 
313 	if ( state.show_label ) {
314 		mainGCV.foreground = label_pix;
315 		XChangeGC(
316 				 mainDisplay,
317 				 mainGC,
318 				 GCForeground,
319 				 &mainGCV
320 		    );
321 		XDrawString(
322 				mainDisplay,
323 				win,
324 				mainGC,
325 				label_x,
326 				label_y,
327 				state.label,
328 				strlen(state.label)
329 				);
330 	}
331 }
332 
333 /* convert the position on 0-60 scale into xy coordinates */
convert2x(int tim)334 inline float convert2x(int tim)
335 {
336 	return cos(M_PI * (((float) (tim - 15)) * 6) / 180);
337 }
convert2y(int tim)338 inline float convert2y(int tim)
339 {
340 	return sin(M_PI * (((float) (tim - 15)) * 6) / 180);
341 }
342 
343 float hwidth[3];
344 float hlength[3];
345 float hback[3];
346 float hscale;
347 
348 /* calculate some rendering constants */
constants()349 void constants()
350 {
351 	hscale = (state.hthick / 4.0);
352 
353 	hwidth[0] = 0.02 * hscale;
354 	hwidth[1] = 0.05 * hscale;
355 	hwidth[2] = 0.07 * hscale;
356 
357 	hlength[0] = 1.0;
358 	hlength[1] = 0.9;
359 	hlength[2] = 0.6;
360 	hback[0] = 0.02;
361 	hback[1] = 0.05;
362 	hback[2] = 0.07;
363 
364 	if (state.float_seconds)
365 		hback[0] = -0.8;
366 }
367 
368 
369 
370 /* calculate rendering parameters for hands */
calculate_hands()371 void calculate_hands()
372 {
373 	int i;
374 	float xrate[3], yrate[3];
375 
376 	loc_time = localtime(&curTime);
377 	if (state.shift) {
378 		loc_time->tm_hour += state.shift;
379 		while (loc_time->tm_hour > 23)
380 			loc_time->tm_hour %= 24;
381 		while (loc_time->tm_hour < 0)
382 			loc_time->tm_hour += 24;
383 	}
384 	/* seconds */
385 	xrate[0] = convert2x(loc_time->tm_sec);
386 	yrate[0] = convert2y(loc_time->tm_sec);
387 
388 	/* minutes */
389 	xrate[1] = convert2x(loc_time->tm_min + (float) (loc_time->tm_sec) / 60.0);
390 	yrate[1] = convert2y(loc_time->tm_min + (float) (loc_time->tm_sec) / 60.0);
391 
392 	/* hours */
393 	xrate[2] = convert2x(((loc_time->tm_hour % 12) * 5) + (loc_time->tm_min / 12.0));
394 	yrate[2] = convert2y(((loc_time->tm_hour % 12) * 5) + (loc_time->tm_min / 12.0));
395 
396 	for (i = 0; i < 3; i++) {
397 		lines[i].x1 = center.x - rint(hback[i] * watch_size.x * xrate[i]);
398 		lines[i].y1 = center.y - rint(hback[i] * watch_size.y * yrate[i]);
399 		lines[i].x2 = rint(hlength[i] * watch_size.x * xrate[i]) + center.x;
400 		lines[i].y2 = rint(hlength[i] * watch_size.y * yrate[i]) + center.y;
401 
402 #ifdef DEBUG
403 		if (i == 0)
404 			printf("%d sec = %d %d -> %d %d, xrate = %f, yrate = %f\n",
405 			       loc_time->tm_sec, lines[0].x1, lines[0].y1,
406 			   lines[0].x2, lines[0].y2, xrate[0], yrate[0]);
407 #endif
408 
409 		/* generate polygons */
410 		hand[i][0].x = rint((hwidth[i] * watch_size.x) * yrate[i]) + center.x
411 		    - rint(hback[i] * watch_size.x * xrate[i]);
412 		hand[i][0].y = rint((hwidth[i] * watch_size.y) * (-xrate[i])) + center.y
413 		    - rint(hback[i] * watch_size.y * yrate[i]);
414 		hand[i][1].x = rint((hwidth[i] * watch_size.x) * (-yrate[i])) + center.x
415 		    - rint(hback[i] * watch_size.x * xrate[i]);
416 		hand[i][1].y = rint((hwidth[i] * watch_size.y) * xrate[i]) + center.y
417 		    - rint(hback[i] * watch_size.y * yrate[i]);
418 		hand[i][2].x = lines[i].x2;
419 		hand[i][2].y = lines[i].y2;
420 		hand[i][3].x = hand[i][0].x;
421 		hand[i][3].y = hand[i][0].y;
422 	}
423 }
424 
425 /*
426  * Calculate and redraw the lines corresponding to the
427  * time.
428  */
refresh()429 void refresh()
430 {
431 	constants();
432 	calculate_hands();
433 	draw_window(drawWindow);
434 	++update_request;
435 }
436 
437 /*
438  * This function clears up all X related
439  * stuff and exits. It is called in case
440  * of emergencies too.
441  */
x_cleanup()442 void x_cleanup()
443 {
444 	if (mainDisplay) {
445 		XCloseDisplay(mainDisplay);
446 	}
447 	exit(0);
448 }
449 
450 /*
451  * This checks for X11 events. We distinguish the following:
452  * - request to repaint the window
453  * - request to quit (Close button)
454  */
CheckX11Events()455 void CheckX11Events()
456 {
457 	XEvent Event;
458 	while (XPending(mainDisplay)) {
459 		XNextEvent(mainDisplay, &Event);
460 		switch (Event.type) {
461 		case Expose:
462 #ifdef DEBUG
463 			printf("Expose event caught: (%d %d) (%d x %d)\n",
464 			       ((XExposeEvent *) & Event)->x,
465 			       ((XExposeEvent *) & Event)->y,
466 			       ((XExposeEvent *) & Event)->width,
467 			       ((XExposeEvent *) & Event)->height);
468 #endif
469 			if (Event.xexpose.count == 0) {
470 				++update_request;
471 			}
472 			break;
473 		case ButtonPress:
474 			system(state.program_name);
475 			break;
476 		case ClientMessage:
477 			if ((Event.xclient.message_type == wm_protocols)
478 			    && (Event.xclient.data.l[0] == wm_delete_window)) {
479 #ifdef DEBUG
480 				printf("caught wm_delete_window, closing\n");
481 #endif
482 				x_cleanup();
483 			}
484 			break;
485 		}
486 	}
487 }
488 
489 /*
490  * Flush the changes that were done in the hidden window
491  * onto the visible windows.
492  */
redraw()493 void redraw()
494 {
495 	XCopyArea(mainDisplay, drawWindow, mainWindow, mainGC, 0, 0,
496 		  state.total_size.x, state.total_size.y, 0, 0);
497 
498 	XCopyArea(mainDisplay, drawWindow, iconWindow, mainGC, 0, 0,
499 		  state.total_size.x, state.total_size.y, 0, 0);
500 
501 	update_request = 0;
502 }
503 
504 /*
505  * This routine checks if the current time is equal to the time
506  * specified in the configuration file and executes the given
507  * command.
508  */
croncheck()509 void croncheck()
510 {
511 	int i;
512 
513 	for (i = 0; i < state.nprogs; i++) {
514 		if (loc_time->tm_hour == state.PInf[i]->time[0]) {
515 			if (loc_time->tm_min == state.PInf[i]->time[1]) {
516 				if ((loc_time->tm_sec == state.PInf[i]->time[2])) {
517 					if (!state.PInf[i]->done) {
518 						state.PInf[i]->done = 1;
519 						system(state.PInf[i]->name);
520 					}
521 				} else {
522 					state.PInf[i]->done = 0;
523 				}
524 			}
525 		}
526 	}
527 }
528 
529 /*
530  * This function triggers checks for updates of the clock
531  * every second and checks if any X11 events were passed
532  * to us. If one of the functions triggers the flag an
533  * update is flushed to the screen.
534  */
update()535 void update()
536 {
537 	curTime = time(0);
538 
539 	if ( curTime != last_time ) {
540 		if ( abs(curTime - last_time) >= state.update_interval) {
541 			last_time = curTime;
542 			refresh();
543 		}
544 		if (state.cron_program)
545 			croncheck();
546 	}
547 
548 	CheckX11Events();
549 	if (update_request) {
550 		redraw();
551 	}
552 }
553 
554 /*
555  * Draw those little points every 5 secs on the background pixmap
556  * that usually are marked 1,2,3,4,5,6,7 etc.
557  * And those triangles that mark 3,6,9,12 o'clock
558  */
draw_dial()559 void draw_dial()
560 {
561 	int tmp, tinc;
562 	struct coordinates tmp_coord;
563 	float xrate, yrate;
564 
565 	mainGCV.foreground = fore_pix;
566 	XChangeGC(mainDisplay, mainGC, GCForeground, &mainGCV);
567 
568 	switch (state.tprop) {
569 	case 2:
570 		tinc = 1;
571 		break;
572 	case 1:
573 		tinc = 5;
574 		break;
575 	case 0:
576 		tinc = 60;
577 		break;
578 	default:
579 		tinc = 1;
580 		break;
581 	}
582 
583 	if (state.tprop) {
584 		for (tmp = 0; tmp < 60; tmp += tinc) {
585 			xrate = convert2x(tmp);
586 			yrate = convert2y(tmp);
587 			tmp_coord.x = rint((watch_size.x + 1) * xrate) + center.x;
588 			tmp_coord.y = rint((watch_size.y + 1) * yrate) + center.y;
589 #ifdef DEBUG
590 			printf("dot at %d %d\n", tmp_coord.x, tmp_coord.y);
591 #endif
592 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x, tmp_coord.y);
593 			if (state.tprop == 2) {
594 				if (!(tmp % 5)) {
595 					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x, tmp_coord.y + 1);
596 					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x + 1, tmp_coord.y);
597 					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x, tmp_coord.y - 1);
598 					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x - 1, tmp_coord.y);
599 				}
600 			}
601 		}
602 
603 		if (state.tprop == 1) {
604 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - watch_size.x - 1, center.y - 1);
605 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - watch_size.x - 1, center.y + 1);
606 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - watch_size.x, center.y);
607 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + watch_size.x + 1, center.y - 1);
608 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + watch_size.x + 1, center.y + 1);
609 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + watch_size.x, center.y);
610 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - 1, center.y - watch_size.y - 1);
611 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + 1, center.y - watch_size.y - 1);
612 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x, center.y - watch_size.y);
613 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - 1, center.y + watch_size.y + 1);
614 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + 1, center.y + watch_size.y + 1);
615 			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x, center.y + watch_size.y);
616 		}
617 	}
618 }
619 
initialize(int argc,char ** argv,char * window_name,char * display_name,char * mainGeometry,int withdrawn,int iconic,int pushed_in,int no_border)620 void initialize(int argc, char **argv,
621 		char *window_name,
622 		char *display_name,
623 		char *mainGeometry,
624 		int withdrawn,
625 		int iconic,
626 		int pushed_in,
627 		int no_border)
628 {
629 	int screen;
630 	Status status;
631 	XWindowAttributes winAttr;
632 	XSizeHints SizeHints;
633 	XTextProperty title;
634 	XClassHint classHint;
635 	int gravity;
636 	XWMHints WmHints;
637 	XEvent Event;
638 	int color_depth;
639 	Pixel tmp_pix;
640 	int result;
641 	int x_negative = 0;
642 	int y_negative = 0;
643 	int x_size_forced = 0;
644 	int y_size_forced = 0;
645 	int label_w = 0;
646 	int label_h = 0;
647 
648 	mainDisplay = XOpenDisplay(display_name);
649 	if (!mainDisplay) {
650 		printf("astime : grrrr... can't open display %s. Sorry ...\n",
651 		       XDisplayName(display_name));
652 		exit(1);
653 	}
654 	screen = DefaultScreen(mainDisplay);
655 	Root = RootWindow(mainDisplay, screen);
656 	back_pix = GetColor(state.bgcolor, mainDisplay, Root);
657 	fore_pix = GetColor(state.fgcolor, mainDisplay, Root);
658 	color_depth = DefaultDepth(mainDisplay, screen);
659 #ifdef DEBUG
660 	printf("astime : detected color depth %d bpp, using %d bpp\n",
661 	       color_depth, color_depth);
662 #endif
663 	/* Set up the font for the label */
664 	if ( state.show_label ) {
665 		label_fnstruct = XLoadQueryFont( mainDisplay, state.font_name );
666 		if ( ! label_fnstruct ) {
667 			printf("astime : failed to load font %s\n",
668 					state.font_name);
669 			printf("astime : warning : the label will not be drawn\n");
670 			state.show_label = 0;
671 		} else {
672 			label_font = label_fnstruct->fid;
673 			label_w = XTextWidth(
674 					label_fnstruct,
675 					state.label,
676 					strlen(state.label)
677 					);
678 			label_h = label_fnstruct->max_bounds.ascent +
679 				label_fnstruct->max_bounds.descent;
680 #ifdef DEBUG
681 	printf("astime : loaded font %s\n", state.font_name);
682 	printf("astime : label width is %d, height is %d [label \"%s\"]\n",
683 			label_w, label_h, state.label);
684 #endif
685 		}
686 	}
687 
688 	if (strlen(mainGeometry)) {
689 		/* Check the user-specified size */
690 		result = XParseGeometry(mainGeometry,
691 					&SizeHints.x,
692 					&SizeHints.y,
693 					&SizeHints.width,
694 					&SizeHints.height);
695 		if (result & WidthValue) {
696 			state.total_size.x = SizeHints.width;
697 			x_size_forced = 1;
698 		}
699 		if (result & HeightValue) {
700 			state.total_size.y = SizeHints.height;
701 			y_size_forced = 1;
702 		}
703 		if (result & XNegative)
704 			x_negative = 1;
705 		if (result & YNegative)
706 			y_negative = 1;
707 #ifdef DEBUG
708 		printf("User size: %d x %d position: %d %d (negative %d %d)\n",
709 		       state.total_size.x, state.total_size.y,
710 		       SizeHints.x, SizeHints.y, x_negative, y_negative);
711 #endif
712 	}
713 	/* adjust the coordinate/size for the date */
714 	if (state.draw_date) {
715 		center.x = state.total_size.x / 2;
716 		center.y = state.total_size.y / 2 - 4;
717 		watch_size.x = center.x - 3;
718 		watch_size.y = state.total_size.y / 2 - 7;
719 	} else {
720 		center.x = state.total_size.x / 2;
721 		center.y = state.total_size.y / 2;
722 		watch_size.x = center.x - 3;
723 		watch_size.y = center.y - 3;
724 	}
725 	/* adjust the coordinates/size for the label */
726 	if ( state.show_label ) {
727 		if ( state.label_top )
728 			center.y += label_h/2 + 1;
729 		else
730 			center.y -= label_h/2;
731 		watch_size.y -= label_h/2 + 1;
732 		label_x = state.total_size.x / 2 - label_w / 2;
733 		if ( state.label_top )
734 			label_y = label_h;
735 		else {
736 			if ( state.draw_date )
737 				label_y = state.total_size.y - 10;
738 			else
739 				label_y = state.total_size.y - 3;
740 		}
741 	}
742 
743 	/* The day-of-week pixmap */
744 	sprintf(dof_pixmap_color[0], "c c %s", state.bgcolor);
745 	sprintf(dof_pixmap_color[1], "l c %s", state.daycolor);
746 	dof[1] = dof_pixmap_color[0];
747 	dof[2] = dof_pixmap_color[1];
748 	dofXpm.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
749 	status = XpmCreatePixmapFromData(
750 			mainDisplay,		/* display */
751 			Root,			/* window */
752 			dof,			/* xpm */
753 			&dofXpm.pixmap,		/* resulting pixmap */
754 			&dofXpm.mask,
755 			&dofXpm.attributes);
756 	if (status != XpmSuccess) {
757 		printf("astime : (%d) not enough free color cells for day_of_week.\n", status);
758 		x_cleanup();
759 	}
760 	/* The AM/PM pixmap */
761 	sprintf(ampm_pixmap_color[0], "c c %s", state.bgcolor);
762 	sprintf(ampm_pixmap_color[1], "l c %s", state.ampmcolor);
763 	ampm[1] = ampm_pixmap_color[0];
764 	ampm[2] = ampm_pixmap_color[1];
765 	ampmXpm.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
766 	status = XpmCreatePixmapFromData(
767 			mainDisplay,		/* display */
768 			Root,			/* window */
769 			ampm,			/* xpm */
770 			&ampmXpm.pixmap,	/* resulting pixmap */
771 			&ampmXpm.mask,
772 			&ampmXpm.attributes);
773 	if (status != XpmSuccess) {
774 		printf("astime : (%d) not enough free color cells for AM/PM indicator.\n", status);
775 		x_cleanup();
776 	}
777 	/* The digits pixmap */
778 	sprintf(digits_pixmap_color[0], "c c %s", state.bgcolor);
779 	sprintf(digits_pixmap_color[1], ". c %s", state.datecolor);
780 	digits[1] = digits_pixmap_color[0];
781 	digits[2] = digits_pixmap_color[1];
782 	digitsXpm.attributes.valuemask |=
783 		(XpmReturnPixels | XpmReturnExtensions);
784 	status = XpmCreatePixmapFromData(
785 			mainDisplay,		/* display */
786 			Root,			/* window */
787 			digits,			/* xpm */
788 			&digitsXpm.pixmap,	/* resulting pixmap */
789 			&digitsXpm.mask,
790 			&digitsXpm.attributes);
791 	if (status != XpmSuccess) {
792 		printf("astime : (%d) not enough free color cells for digits.\n", status);
793 		x_cleanup();
794 	}
795 	/* The months pixmap */
796 	sprintf(months_pixmap_color[0], ". c %s", state.bgcolor);
797 	sprintf(months_pixmap_color[1], "o c %s", state.datecolor);
798 	months[1] = months_pixmap_color[0];
799 	months[2] = months_pixmap_color[1];
800 	monthsXpm.attributes.valuemask |=
801 		(XpmReturnPixels | XpmReturnExtensions);
802 	status = XpmCreatePixmapFromData(
803 			mainDisplay,		/* display */
804 			Root,			/* window */
805 			months,			/* xpm */
806 			&monthsXpm.pixmap,	/* resulting pixmap */
807 			&monthsXpm.mask,
808 			&monthsXpm.attributes);
809 	if (status != XpmSuccess) {
810 		printf("astime : (%d) not enough free color cells for months.\n", status);
811 		x_cleanup();
812 	}
813 	SizeHints.flags = USSize | USPosition;
814 	SizeHints.x = 0;
815 	SizeHints.y = 0;
816 	SizeHints.min_width =
817 	    SizeHints.max_width =
818 	    SizeHints.width = state.total_size.x;
819 	SizeHints.min_height =
820 	    SizeHints.max_height =
821 	    SizeHints.height = state.total_size.y;
822 	SizeHints.flags |= PMinSize | PMaxSize;
823 	/*
824 	 * The forth parameter is the window border width.
825 	 * Set to 0 it makes astime look really queer in
826 	 * AS when using -0-0 geometry. I have no idea why.
827 	 * When the border is 1, everything is fine, but
828 	 * the window is off by 1, of course.
829 	 * It is probably a bug in AS.
830 	 */
831 	XWMGeometry(mainDisplay, screen, mainGeometry, NULL, 0,
832 		    &SizeHints, &SizeHints.x, &SizeHints.y,
833 		    &SizeHints.width, &SizeHints.height, &gravity);
834 #ifdef DEBUG
835 	printf("SizeHints were: %d x %d, setting to %d x %d, position %d %d\n",
836 	       SizeHints.width, SizeHints.height,
837 	       state.total_size.x, state.total_size.y,
838 	       SizeHints.x, SizeHints.y);
839 #endif
840 
841 	/* Correct the offsets if the X/Y are negative */
842 	SizeHints.win_gravity = NorthWestGravity;
843 	if (x_negative) {
844 		SizeHints.win_gravity = NorthEastGravity;
845 	}
846 	if (y_negative) {
847 		if (x_negative)
848 			SizeHints.win_gravity = SouthEastGravity;
849 		else
850 			SizeHints.win_gravity = SouthWestGravity;
851 	}
852 	SizeHints.flags |= PWinGravity;
853 
854 	backgroundWindow = XCreatePixmap(
855 				mainDisplay,
856 				Root,
857 				state.total_size.x,
858 				state.total_size.y,
859 				color_depth
860 	    );
861 
862 	drawWindow = XCreatePixmap(
863 				mainDisplay,		/* display */
864 				Root,			/* parent */
865 				state.total_size.x,	/* width */
866 				state.total_size.y,	/* height */
867 				color_depth		/* color depth */
868 	    );
869 
870 	mainWindow = XCreateSimpleWindow(
871 				mainDisplay,		/* display */
872 				Root,			/* parent */
873 				SizeHints.x,		/* x */
874 				SizeHints.y,		/* y */
875 				SizeHints.width,	/* width */
876 				SizeHints.height,	/* height */
877 				0,			/* border_width */
878 				fore_pix,		/* border */
879 				back_pix		/* background */
880 	    );
881 
882 	iconWindow = XCreateSimpleWindow(
883 				mainDisplay,		/* display */
884 				Root,			/* parent */
885 				SizeHints.x,		/* x */
886 				SizeHints.y,		/* y */
887 				SizeHints.width,	/* width */
888 				SizeHints.height,	/* height */
889 				0,			/* border_width */
890 				fore_pix,		/* border */
891 				back_pix		/* background */
892 	    );
893 
894 	XSetWMNormalHints(mainDisplay, mainWindow, &SizeHints);
895 	XSetWMNormalHints(mainDisplay, iconWindow, &SizeHints);
896 	status = XClearWindow(mainDisplay, mainWindow);
897 	status = XClearWindow(mainDisplay, iconWindow);
898 
899 	status = XGetWindowAttributes(
900 			     mainDisplay,	/* display */
901 			     mainWindow,	/* window */
902 			     &winAttr		/* window_attributes_return */
903 	    );
904 #ifdef DEBUG
905 	printf("Window Attributes: %d x %d at %d %d border %d\n",
906 	       winAttr.width, winAttr.height, winAttr.x, winAttr.y,
907 	       winAttr.border_width);
908 #endif
909 
910 	status = XStringListToTextProperty(&window_name, 1, &title);
911 	XSetWMName(mainDisplay, mainWindow, &title);
912 	XSetWMName(mainDisplay, iconWindow, &title);
913 
914 	classHint.res_name = "astime";
915 	classHint.res_class = "ASTIME";
916 	XSetClassHint(mainDisplay, mainWindow, &classHint);
917 	XStoreName(mainDisplay, mainWindow, window_name);
918 	XSetIconName(mainDisplay, mainWindow, window_name);
919 
920 	/*
921 	 * If the user did not specify a program to run
922 	 * we do not intercept mouse clicks in our window.
923 	 */
924 	if (state.run_program)
925 		status = XSelectInput(
926 			mainDisplay,	/* display    */
927 			mainWindow,	/* window     */
928 			ExposureMask | ButtonPressMask	/* event_mask */
929 		    );
930 	else
931 		status = XSelectInput(
932 			mainDisplay,	/* display    */
933 			mainWindow,	/* window     */
934 			ExposureMask	/* event_mask */
935 		    );
936 
937 	/* Make sure WindowMaker users can click happily away ... */
938 	if ( withdrawn )
939 		status = XSelectInput(
940 			mainDisplay,	/* display    */
941 			iconWindow,	/* window     */
942 			ExposureMask | ButtonPressMask	/* event_mask */
943 		    );
944 	else
945 		status = XSelectInput(
946 				mainDisplay,	/* display    */
947 				iconWindow,	/* window     */
948 				ExposureMask	/* event_mask */
949 		    );
950 
951 	/* Creating Graphics context */
952 	mainGCV.foreground = fore_pix;
953 	mainGCV.background = back_pix;
954 	mainGCV.graphics_exposures = False;
955 	mainGCV.line_style = LineSolid;
956 	mainGCV.fill_style = FillSolid;
957 	mainGCV.line_width = 1;
958 	if ( state.show_label ) {
959 		mainGCV.font = label_font;
960 		mainGC = XCreateGC(mainDisplay,
961 				   mainWindow,
962 				   GCForeground | GCBackground | GCLineWidth |
963 				   GCLineStyle | GCFillStyle | GCFont,
964 				   &mainGCV
965 		    );
966 	} else {
967 		mainGC = XCreateGC(mainDisplay,
968 				   mainWindow,
969 				   GCForeground | GCBackground | GCLineWidth |
970 				   GCLineStyle | GCFillStyle,
971 				   &mainGCV
972 		    );
973 	}
974 
975 	/* Draw the background */
976 	mainGCV.foreground = back_pix;
977 	XChangeGC(mainDisplay,
978 		  mainGC,
979 		  GCForeground,
980 		  &mainGCV
981 	    );
982 	XFillRectangle(mainDisplay,
983 		       backgroundWindow,
984 		       mainGC,
985 		       0,
986 		       0,
987 		       state.total_size.x,
988 		       state.total_size.y
989 	    );
990 
991       if (!no_border) {
992 
993 	/* The shadow on the sides */
994 	if (pushed_in) {
995 		tmp_pix = DarkenColor(state.bgcolor, 1.6, mainDisplay, Root);
996 	} else {
997 		tmp_pix = LightenColor(state.bgcolor, 2.0, mainDisplay, Root);
998 	}
999 	mainGCV.foreground = tmp_pix;
1000 	XChangeGC(mainDisplay,
1001 		  mainGC,
1002 		  GCForeground,
1003 		  &mainGCV
1004 	    );
1005 	XDrawLine(mainDisplay,
1006 		  backgroundWindow,
1007 		  mainGC,
1008 		  0,
1009 		  0,
1010 		  state.total_size.x - 1,
1011 		  0
1012 	    );
1013 	XDrawLine(mainDisplay,
1014 		  backgroundWindow,
1015 		  mainGC,
1016 		  0,
1017 		  0,
1018 		  0,
1019 		  state.total_size.y - 1
1020 	    );
1021 	if (!pushed_in) {
1022 		tmp_pix = DarkenColor(state.bgcolor, 1.6, mainDisplay, Root);
1023 	} else {
1024 		tmp_pix = LightenColor(state.bgcolor, 2.0, mainDisplay, Root);
1025 	}
1026 	mainGCV.foreground = tmp_pix;
1027 	XChangeGC(mainDisplay,
1028 		  mainGC,
1029 		  GCForeground,
1030 		  &mainGCV
1031 	    );
1032 	XDrawLine(mainDisplay,
1033 		  backgroundWindow,
1034 		  mainGC,
1035 		  state.total_size.x - 1,
1036 		  state.total_size.y,
1037 		  state.total_size.x - 1,
1038 		  0
1039 	    );
1040 	XDrawLine(mainDisplay,
1041 		  backgroundWindow,
1042 		  mainGC,
1043 		  state.total_size.x,
1044 		  state.total_size.y - 1,
1045 		  0,
1046 		  state.total_size.y - 1
1047 	    );
1048       }
1049 
1050 	/* Finish up the background (the dial) */
1051 	draw_dial();
1052 
1053 	status = XSetCommand(mainDisplay, mainWindow, argv, argc);
1054 
1055 	/* Set up the event for quitting the window */
1056 	wm_delete_window = XInternAtom(mainDisplay,
1057 				"WM_DELETE_WINDOW",	/* atom_name */
1058 				False			/* only_if_exists */
1059 	    );
1060 	wm_protocols = XInternAtom(mainDisplay,
1061 				"WM_PROTOCOLS",		/* atom_name */
1062 				False			/* only_if_exists */
1063 	    );
1064 	status = XSetWMProtocols(mainDisplay,
1065 				 mainWindow,
1066 				 &wm_delete_window,
1067 				 1
1068 	    );
1069 	status = XSetWMProtocols(mainDisplay,
1070 				 iconWindow,
1071 				 &wm_delete_window,
1072 				 1
1073 	    );
1074 
1075 	WmHints.flags = StateHint | IconWindowHint;
1076 	WmHints.initial_state =
1077 	    withdrawn ? WithdrawnState :
1078 	    iconic ? IconicState : NormalState;
1079 	WmHints.icon_window = iconWindow;
1080 	if (withdrawn) {
1081 		WmHints.window_group = mainWindow;
1082 		WmHints.flags |= WindowGroupHint;
1083 	}
1084 	if (iconic || withdrawn) {
1085 		WmHints.icon_x = SizeHints.x;
1086 		WmHints.icon_y = SizeHints.y;
1087 		WmHints.flags |= IconPositionHint;
1088 	}
1089 	XSetWMHints(mainDisplay,
1090 		    mainWindow,
1091 		    &WmHints);
1092 
1093 	/* Finally show the window */
1094 	status = XMapWindow(mainDisplay, mainWindow);
1095 
1096 	/* Get colors while waiting for Expose */
1097 	if (state.neon) {
1098 		pix[0] = DarkenColor(state.seccolor, 1.8, mainDisplay, Root);
1099 		pix[1] = DarkenColor(state.mincolor, 1.8, mainDisplay, Root);
1100 		pix[2] = DarkenColor(state.hourcolor, 1.8, mainDisplay, Root);
1101 	} else {
1102 		pix[0] = GetColor(state.seccolor, mainDisplay, Root);
1103 		pix[1] = GetColor(state.mincolor, mainDisplay, Root);
1104 		pix[2] = GetColor(state.hourcolor, mainDisplay, Root);
1105 	}
1106 
1107 	/* Get outline colors */
1108 	if (state.neon) {
1109 		pix[3] = GetColor(state.seccolor, mainDisplay, Root);
1110 		pix[4] = GetColor(state.mincolor, mainDisplay, Root);
1111 		pix[5] = GetColor(state.hourcolor, mainDisplay, Root);
1112 	} else {
1113 		pix[3] = LightenColor(state.seccolor, 1.8, mainDisplay, Root);
1114 		pix[4] = LightenColor(state.mincolor, 1.8, mainDisplay, Root);
1115 		pix[5] = LightenColor(state.hourcolor, 1.8, mainDisplay, Root);
1116 	}
1117 
1118 	/* allow user to override outline colors */
1119 	if (strcmp(state.secocolor, "<>"))
1120 		pix[3] = GetColor(state.secocolor, mainDisplay, Root);
1121 	if (strcmp(state.minocolor, "<>"))
1122 		pix[4] = GetColor(state.minocolor, mainDisplay, Root);
1123 	if (strcmp(state.hourocolor, "<>"))
1124 		pix[5] = GetColor(state.hourocolor, mainDisplay, Root);
1125 
1126 	/* Get fill colors */
1127 	pix[6] = pix[0];
1128 	pix[7] = pix[1];
1129 	pix[8] = pix[2];
1130 
1131 	/* allow user to override fill colors */
1132 	if (strcmp(state.secfillcolor, "<>"))
1133 		pix[6] = GetColor(state.secfillcolor, mainDisplay, Root);
1134 	if (strcmp(state.minfillcolor, "<>"))
1135 		pix[7] = GetColor(state.minfillcolor, mainDisplay, Root);
1136 	if (strcmp(state.hourfillcolor, "<>"))
1137 		pix[8] = GetColor(state.hourfillcolor, mainDisplay, Root);
1138 
1139 	/* Get the label color */
1140 	label_pix = GetColor(state.label_color, mainDisplay, Root);
1141 
1142 	/* try to read the time */
1143 	curTime = time(0);
1144 	last_time = curTime;
1145 	refresh();
1146 
1147 	/* wait for the Expose event now */
1148 	XNextEvent(mainDisplay, &Event);
1149 	/* We've got Expose -> draw the parts of the window. */
1150 	redraw();
1151 	XFlush(mainDisplay);
1152 }
1153