/*
 * astime is an analogue clock utility for X Windows.
 *
 * Copyright (c) 1998-2000  Albert Dorofeev <albert@tigr.net>
 * Copyright (c) 1999 William Kostis <kostis@ee.cornell.edu>
 *
 * For the updates see http://www.tigr.net/afterstep/as-apps/
 *
 * This software is distributed under GPL. For details see LICENSE file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/Xatom.h>

#include "x_color.h"
#include "state.h"
struct astime_state state;

#include "dof.xpm"
#include "months.xpm"
#include "digits.xpm"
#include "ampm.xpm"

/* nice idea from ascd */
typedef struct _XpmIcon {
	Pixmap pixmap;
	Pixmap mask;
	XpmAttributes attributes;
} XpmIcon;

XpmIcon dofXpm;
XpmIcon ampmXpm;
XpmIcon monthsXpm;
XpmIcon digitsXpm;

/* X windows related global variables */
Display *mainDisplay = 0;	/* The display we are working on */
Window Root;			/* The root window of X11 */
Pixmap backgroundWindow;	/* Window for drawing */
Pixmap drawWindow;		/* Window for drawing */
Window mainWindow;		/* Application window */
Window iconWindow;		/* Icon window */
XGCValues mainGCV;		/* graphics context values */
GC mainGC;			/* Graphics context */
Atom wm_delete_window;
Atom wm_protocols;
Font label_font;
XFontStruct * label_fnstruct;

/* the label position and color */
int label_x, label_y;
Pixel label_pix;

/* background, digits, months etc pixmap colors */
char bgpixmap_color[4][32];
char dof_pixmap_color[2][32];
char ampm_pixmap_color[2][32];
char digits_pixmap_color[2][32];
char months_pixmap_color[2][32];

Pixel back_pix, fore_pix;

/* The center of the clock */
struct coordinates center;
/* The half-size of the watch drawable area 
 * (2 more pixels are given to the frame */
struct coordinates watch_size;

/* calculated values in pixels */
int pixelLength[3];
struct line_struct {
	int x1, y1;
	int x2, y2;
} lines[3];

/* polygons for the hands */
XPoint hand[3][4];

/* pixels we need */
Pixel pix[9];

/* last time we updated */
time_t last_time = 0;
time_t curTime;

/* current time in nice format */
struct tm *loc_time;

/* requests for update */
int update_request = 0;

/* float pi = 3.14159265359; */

void draw_window(Window win)
{
	int i;

	XCopyArea(
			 mainDisplay,
			 backgroundWindow,
			 win,
			 mainGC,
			 0,
			 0,
			 state.total_size.x,
			 state.total_size.y,
			 0,
			 0
	    );
	if (state.draw_ampm)
		XCopyArea(
				 mainDisplay,
				 ampmXpm.pixmap,
				 win,
				 mainGC,
				 0,
				 (loc_time->tm_hour > 11 ? 6 : 0),
				 13,
				 7,
				 state.total_size.x - 14,
				 1
		    );
	if (state.draw_date) {
		XCopyArea(
				 mainDisplay,
				 dofXpm.pixmap,
				 win,
				 mainGC,
				 0,
				 loc_time->tm_wday * 6,
				 13,
				 7,
				 1,
				 1
		    );
		XCopyArea(
				 mainDisplay,
				 digitsXpm.pixmap,
				 win,
				 mainGC,
				 0,
				 (loc_time->tm_mday / 10) * 7,
				 7,
				 8,
				 1,
				 state.total_size.y - 9
		    );
		XCopyArea(
				 mainDisplay,
				 digitsXpm.pixmap,
				 win,
				 mainGC,
				 0,
				 (loc_time->tm_mday % 10) * 7,
				 7,
				 8,
				 8,
				 state.total_size.y - 9
		    );
		XCopyArea(
				 mainDisplay,
				 monthsXpm.pixmap,
				 win,
				 mainGC,
				 0,
				 loc_time->tm_mon * 6,
				 19,
				 7,
				 18,
				 state.total_size.y - 8
		    );
		/* The year won't fit in smaller displays */
		if (state.total_size.x > 51) {
			XCopyArea(
					 mainDisplay,
					 digitsXpm.pixmap,
					 win,
					 mainGC,
					 0,
				      (loc_time->tm_year % 100 / 10) * 7,
					 7,
					 8,
					 38,
					 state.total_size.y - 9
			    );
			XCopyArea(
					 mainDisplay,
					 digitsXpm.pixmap,
					 win,
					 mainGC,
					 0,
					 (loc_time->tm_year % 10) * 7,
					 7,
					 8,
					 45,
					 state.total_size.y - 9
			    );
		}
	}
#ifdef DEBUG
	mainGCV.foreground = pix[2];
	XChangeGC(
			 mainDisplay,
			 mainGC,
			 GCForeground,
			 &mainGCV
	    );
	XDrawLine(
			 mainDisplay,
			 win,
			 mainGC,
			 center.x - watch_size.x,
			 center.y - watch_size.y,
			 center.x + watch_size.x,
			 center.y - watch_size.y
	    );
	XDrawLine(
			 mainDisplay,
			 win,
			 mainGC,
			 center.x + watch_size.x,
			 center.y - watch_size.y,
			 center.x + watch_size.x,
			 center.y + watch_size.y
	    );
	XDrawLine(
			 mainDisplay,
			 win,
			 mainGC,
			 center.x + watch_size.x,
			 center.y + watch_size.y,
			 center.x - watch_size.x,
			 center.y + watch_size.y
	    );
	XDrawLine(
			 mainDisplay,
			 win,
			 mainGC,
			 center.x - watch_size.x,
			 center.y + watch_size.y,
			 center.x - watch_size.x,
			 center.y - watch_size.y
	    );
#endif


	/* draw filled polygonal hands */
	for (i = 2; i >= 0; i--) {
		if ((i == 0) && (state.draw_seconds == 0))
			break;

		if (state.hprop[i][HFILL]) {
			mainGCV.foreground = pix[i + 6];
			XChangeGC(
					 mainDisplay,
					 mainGC,
					 GCForeground | GCLineWidth,
					 &mainGCV
			    );

			XFillPolygon(mainDisplay, win, mainGC, hand[i], 3, Convex, CoordModeOrigin);
		}
		/* draw line-rendered hands */
		if (state.hprop[i][HCLINE]) {
			mainGCV.foreground = pix[i];

			if ((i == 0) && state.thin_seconds) {
				mainGCV.line_width = 1;
			} else {
				/* scale line thickness by window size */
				mainGCV.line_width = rint((1 + (0.5 * i)) * (((watch_size.x + watch_size.y) / 2.0) *
						(state.hthick / 100.0)));
			}

			XChangeGC(
					 mainDisplay,
					 mainGC,
					 GCForeground | GCLineWidth,
					 &mainGCV
			    );
			XDrawLine(
					 mainDisplay,
					 win,
					 mainGC,
					 lines[i].x1,
					 lines[i].y1,
					 lines[i].x2,
					 lines[i].y2
			    );
		}
		/* draw outlines of hands */
		if (state.hprop[i][HOLINE]) {
			mainGCV.foreground = pix[i + 3];
			mainGCV.line_width = rint((1 + (0.5 * i)) * (((watch_size.x + watch_size.y) / 2.0) *
					       (state.othick / 1000.0)));
			XChangeGC(
					 mainDisplay,
					 mainGC,
					 GCForeground | GCLineWidth,
					 &mainGCV
			    );
			XDrawLines(mainDisplay, win, mainGC, hand[i], 4, CoordModeOrigin);

		}
	}

	if ( state.show_label ) {
		mainGCV.foreground = label_pix;
		XChangeGC(
				 mainDisplay,
				 mainGC,
				 GCForeground,
				 &mainGCV
		    );
		XDrawString(
				mainDisplay,
				win,
				mainGC,
				label_x,
				label_y,
				state.label,
				strlen(state.label)
				);
	}
}

/* convert the position on 0-60 scale into xy coordinates */
inline float convert2x(int tim)
{
	return cos(M_PI * (((float) (tim - 15)) * 6) / 180);
}
inline float convert2y(int tim)
{
	return sin(M_PI * (((float) (tim - 15)) * 6) / 180);
}

float hwidth[3];
float hlength[3];
float hback[3];
float hscale;

/* calculate some rendering constants */
void constants()
{
	hscale = (state.hthick / 4.0);

	hwidth[0] = 0.02 * hscale;
	hwidth[1] = 0.05 * hscale;
	hwidth[2] = 0.07 * hscale;

	hlength[0] = 1.0;
	hlength[1] = 0.9;
	hlength[2] = 0.6;
	hback[0] = 0.02;
	hback[1] = 0.05;
	hback[2] = 0.07;

	if (state.float_seconds)
		hback[0] = -0.8;
}



/* calculate rendering parameters for hands */
void calculate_hands()
{
	int i;
	float xrate[3], yrate[3];

	loc_time = localtime(&curTime);
	if (state.shift) {
		loc_time->tm_hour += state.shift;
		while (loc_time->tm_hour > 23)
			loc_time->tm_hour %= 24;
		while (loc_time->tm_hour < 0)
			loc_time->tm_hour += 24;
	}
	/* seconds */
	xrate[0] = convert2x(loc_time->tm_sec);
	yrate[0] = convert2y(loc_time->tm_sec);

	/* minutes */
	xrate[1] = convert2x(loc_time->tm_min + (float) (loc_time->tm_sec) / 60.0);
	yrate[1] = convert2y(loc_time->tm_min + (float) (loc_time->tm_sec) / 60.0);

	/* hours */
	xrate[2] = convert2x(((loc_time->tm_hour % 12) * 5) + (loc_time->tm_min / 12.0));
	yrate[2] = convert2y(((loc_time->tm_hour % 12) * 5) + (loc_time->tm_min / 12.0));

	for (i = 0; i < 3; i++) {
		lines[i].x1 = center.x - rint(hback[i] * watch_size.x * xrate[i]);
		lines[i].y1 = center.y - rint(hback[i] * watch_size.y * yrate[i]);
		lines[i].x2 = rint(hlength[i] * watch_size.x * xrate[i]) + center.x;
		lines[i].y2 = rint(hlength[i] * watch_size.y * yrate[i]) + center.y;

#ifdef DEBUG
		if (i == 0)
			printf("%d sec = %d %d -> %d %d, xrate = %f, yrate = %f\n",
			       loc_time->tm_sec, lines[0].x1, lines[0].y1,
			   lines[0].x2, lines[0].y2, xrate[0], yrate[0]);
#endif

		/* generate polygons */
		hand[i][0].x = rint((hwidth[i] * watch_size.x) * yrate[i]) + center.x
		    - rint(hback[i] * watch_size.x * xrate[i]);
		hand[i][0].y = rint((hwidth[i] * watch_size.y) * (-xrate[i])) + center.y
		    - rint(hback[i] * watch_size.y * yrate[i]);
		hand[i][1].x = rint((hwidth[i] * watch_size.x) * (-yrate[i])) + center.x
		    - rint(hback[i] * watch_size.x * xrate[i]);
		hand[i][1].y = rint((hwidth[i] * watch_size.y) * xrate[i]) + center.y
		    - rint(hback[i] * watch_size.y * yrate[i]);
		hand[i][2].x = lines[i].x2;
		hand[i][2].y = lines[i].y2;
		hand[i][3].x = hand[i][0].x;
		hand[i][3].y = hand[i][0].y;
	}
}

/*
 * Calculate and redraw the lines corresponding to the
 * time.
 */
void refresh()
{
	constants();
	calculate_hands();
	draw_window(drawWindow);
	++update_request;
}

/*
 * This function clears up all X related
 * stuff and exits. It is called in case
 * of emergencies too.
 */
void x_cleanup()
{
	if (mainDisplay) {
		XCloseDisplay(mainDisplay);
	}
	exit(0);
}

/* 
 * This checks for X11 events. We distinguish the following:
 * - request to repaint the window
 * - request to quit (Close button)
 */
void CheckX11Events()
{
	XEvent Event;
	while (XPending(mainDisplay)) {
		XNextEvent(mainDisplay, &Event);
		switch (Event.type) {
		case Expose:
#ifdef DEBUG
			printf("Expose event caught: (%d %d) (%d x %d)\n",
			       ((XExposeEvent *) & Event)->x,
			       ((XExposeEvent *) & Event)->y,
			       ((XExposeEvent *) & Event)->width,
			       ((XExposeEvent *) & Event)->height);
#endif
			if (Event.xexpose.count == 0) {
				++update_request;
			}
			break;
		case ButtonPress:
			system(state.program_name);
			break;
		case ClientMessage:
			if ((Event.xclient.message_type == wm_protocols)
			    && (Event.xclient.data.l[0] == wm_delete_window)) {
#ifdef DEBUG
				printf("caught wm_delete_window, closing\n");
#endif
				x_cleanup();
			}
			break;
		}
	}
}

/*
 * Flush the changes that were done in the hidden window
 * onto the visible windows.
 */
void redraw()
{
	XCopyArea(mainDisplay, drawWindow, mainWindow, mainGC, 0, 0,
		  state.total_size.x, state.total_size.y, 0, 0);

	XCopyArea(mainDisplay, drawWindow, iconWindow, mainGC, 0, 0,
		  state.total_size.x, state.total_size.y, 0, 0);

	update_request = 0;
}

/*
 * This routine checks if the current time is equal to the time
 * specified in the configuration file and executes the given
 * command.
 */
void croncheck()
{
	int i;

	for (i = 0; i < state.nprogs; i++) {
		if (loc_time->tm_hour == state.PInf[i]->time[0]) {
			if (loc_time->tm_min == state.PInf[i]->time[1]) {
				if ((loc_time->tm_sec == state.PInf[i]->time[2])) {
					if (!state.PInf[i]->done) {
						state.PInf[i]->done = 1;
						system(state.PInf[i]->name);
					}
				} else {
					state.PInf[i]->done = 0;
				}
			}
		}
	}
}

/*
 * This function triggers checks for updates of the clock
 * every second and checks if any X11 events were passed 
 * to us. If one of the functions triggers the flag an
 * update is flushed to the screen.
 */
void update()
{
	curTime = time(0);

	if ( curTime != last_time ) {
		if ( abs(curTime - last_time) >= state.update_interval) {
			last_time = curTime;
			refresh();
		}
		if (state.cron_program)
			croncheck();
	}

	CheckX11Events();
	if (update_request) {
		redraw();
	}
}

/* 
 * Draw those little points every 5 secs on the background pixmap
 * that usually are marked 1,2,3,4,5,6,7 etc.
 * And those triangles that mark 3,6,9,12 o'clock 
 */
void draw_dial()
{
	int tmp, tinc;
	struct coordinates tmp_coord;
	float xrate, yrate;

	mainGCV.foreground = fore_pix;
	XChangeGC(mainDisplay, mainGC, GCForeground, &mainGCV);

	switch (state.tprop) {
	case 2:
		tinc = 1;
		break;
	case 1:
		tinc = 5;
		break;
	case 0:
		tinc = 60;
		break;
	default:
		tinc = 1;
		break;
	}

	if (state.tprop) {
		for (tmp = 0; tmp < 60; tmp += tinc) {
			xrate = convert2x(tmp);
			yrate = convert2y(tmp);
			tmp_coord.x = rint((watch_size.x + 1) * xrate) + center.x;
			tmp_coord.y = rint((watch_size.y + 1) * yrate) + center.y;
#ifdef DEBUG
			printf("dot at %d %d\n", tmp_coord.x, tmp_coord.y);
#endif
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x, tmp_coord.y);
			if (state.tprop == 2) {
				if (!(tmp % 5)) {
					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x, tmp_coord.y + 1);
					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x + 1, tmp_coord.y);
					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x, tmp_coord.y - 1);
					XDrawPoint(mainDisplay, backgroundWindow, mainGC, tmp_coord.x - 1, tmp_coord.y);
				}
			}
		}

		if (state.tprop == 1) {
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - watch_size.x - 1, center.y - 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - watch_size.x - 1, center.y + 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - watch_size.x, center.y);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + watch_size.x + 1, center.y - 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + watch_size.x + 1, center.y + 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + watch_size.x, center.y);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - 1, center.y - watch_size.y - 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + 1, center.y - watch_size.y - 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x, center.y - watch_size.y);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x - 1, center.y + watch_size.y + 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x + 1, center.y + watch_size.y + 1);
			XDrawPoint(mainDisplay, backgroundWindow, mainGC, center.x, center.y + watch_size.y);
		}
	}
}

void initialize(int argc, char **argv,
		char *window_name,
		char *display_name,
		char *mainGeometry,
		int withdrawn,
		int iconic,
		int pushed_in)
{
	int screen;
	Status status;
	XWindowAttributes winAttr;
	XSizeHints SizeHints;
	XTextProperty title;
	XClassHint classHint;
	int gravity;
	XWMHints WmHints;
	XEvent Event;
	int color_depth;
	Pixel tmp_pix;
	int result;
	int x_negative = 0;
	int y_negative = 0;
	int x_size_forced = 0;
	int y_size_forced = 0;
	int label_w = 0;
	int label_h = 0;

	mainDisplay = XOpenDisplay(display_name);
	if (!mainDisplay) {
		printf("astime : grrrr... can't open display %s. Sorry ...\n",
		       XDisplayName(display_name));
		exit(1);
	}
	screen = DefaultScreen(mainDisplay);
	Root = RootWindow(mainDisplay, screen);
	back_pix = GetColor(state.bgcolor, mainDisplay, Root);
	fore_pix = GetColor(state.fgcolor, mainDisplay, Root);
	color_depth = DefaultDepth(mainDisplay, screen);
#ifdef DEBUG
	printf("astime : detected color depth %d bpp, using %d bpp\n",
	       color_depth, color_depth);
#endif
	/* Set up the font for the label */
	if ( state.show_label ) {
		label_fnstruct = XLoadQueryFont( mainDisplay, state.font_name );
		if ( ! label_fnstruct ) {
			printf("astime : failed to load font %s\n",
					state.font_name);
			printf("astime : warning : the label will not be drawn\n");
			state.show_label = 0;
		} else {
			label_font = label_fnstruct->fid;
			label_w = XTextWidth(
					label_fnstruct,
					state.label,
					strlen(state.label)
					);
			label_h = label_fnstruct->max_bounds.ascent +
				label_fnstruct->max_bounds.descent;
#ifdef DEBUG
	printf("astime : loaded font %s\n", state.font_name);
	printf("astime : label width is %d, height is %d [label \"%s\"]\n",
			label_w, label_h, state.label);
#endif
		}
	}

	if (strlen(mainGeometry)) {
		/* Check the user-specified size */
		result = XParseGeometry(mainGeometry,
					&SizeHints.x,
					&SizeHints.y,
					&SizeHints.width,
					&SizeHints.height);
		if (result & WidthValue) {
			state.total_size.x = SizeHints.width;
			x_size_forced = 1;
		}
		if (result & HeightValue) {
			state.total_size.y = SizeHints.height;
			y_size_forced = 1;
		}
		if (result & XNegative)
			x_negative = 1;
		if (result & YNegative)
			y_negative = 1;
#ifdef DEBUG
		printf("User size: %d x %d position: %d %d (negative %d %d)\n",
		       state.total_size.x, state.total_size.y,
		       SizeHints.x, SizeHints.y, x_negative, y_negative);
#endif
	}
	/* adjust the coordinate/size for the date */
	if (state.draw_date) {
		center.x = state.total_size.x / 2;
		center.y = state.total_size.y / 2 - 4;
		watch_size.x = center.x - 3;
		watch_size.y = state.total_size.y / 2 - 7;
	} else {
		center.x = state.total_size.x / 2;
		center.y = state.total_size.y / 2;
		watch_size.x = center.x - 3;
		watch_size.y = center.y - 3;
	}
	/* adjust the coordinates/size for the label */
	if ( state.show_label ) {
		if ( state.label_top )
			center.y += label_h/2 + 1;
		else
			center.y -= label_h/2;
		watch_size.y -= label_h/2 + 1;
		label_x = state.total_size.x / 2 - label_w / 2;
		if ( state.label_top )
			label_y = label_h;
		else {
			if ( state.draw_date )
				label_y = state.total_size.y - 10;
			else
				label_y = state.total_size.y - 3;
		}
	}

	/* The day-of-week pixmap */
	sprintf(dof_pixmap_color[0], "c c %s", state.bgcolor);
	sprintf(dof_pixmap_color[1], "l c %s", state.daycolor);
	dof[1] = dof_pixmap_color[0];
	dof[2] = dof_pixmap_color[1];
	dofXpm.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	status = XpmCreatePixmapFromData(
			mainDisplay,		/* display */
			Root,			/* window */
			dof,			/* xpm */
			&dofXpm.pixmap,		/* resulting pixmap */
			&dofXpm.mask,
			&dofXpm.attributes);
	if (status != XpmSuccess) {
		printf("astime : (%d) not enough free color cells for day_of_week.\n", status);
		x_cleanup();
	}
	/* The AM/PM pixmap */
	sprintf(ampm_pixmap_color[0], "c c %s", state.bgcolor);
	sprintf(ampm_pixmap_color[1], "l c %s", state.ampmcolor);
	ampm[1] = ampm_pixmap_color[0];
	ampm[2] = ampm_pixmap_color[1];
	ampmXpm.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
	status = XpmCreatePixmapFromData(
			mainDisplay,		/* display */
			Root,			/* window */
			ampm,			/* xpm */
			&ampmXpm.pixmap,	/* resulting pixmap */
			&ampmXpm.mask,
			&ampmXpm.attributes);
	if (status != XpmSuccess) {
		printf("astime : (%d) not enough free color cells for AM/PM indicator.\n", status);
		x_cleanup();
	}
	/* The digits pixmap */
	sprintf(digits_pixmap_color[0], "c c %s", state.bgcolor);
	sprintf(digits_pixmap_color[1], ". c %s", state.datecolor);
	digits[1] = digits_pixmap_color[0];
	digits[2] = digits_pixmap_color[1];
	digitsXpm.attributes.valuemask |= 
		(XpmReturnPixels | XpmReturnExtensions);
	status = XpmCreatePixmapFromData(
			mainDisplay,		/* display */
			Root,			/* window */
			digits,			/* xpm */
			&digitsXpm.pixmap,	/* resulting pixmap */
			&digitsXpm.mask,
			&digitsXpm.attributes);
	if (status != XpmSuccess) {
		printf("astime : (%d) not enough free color cells for digits.\n", status);
		x_cleanup();
	}
	/* The months pixmap */
	sprintf(months_pixmap_color[0], ". c %s", state.bgcolor);
	sprintf(months_pixmap_color[1], "o c %s", state.datecolor);
	months[1] = months_pixmap_color[0];
	months[2] = months_pixmap_color[1];
	monthsXpm.attributes.valuemask |= 
		(XpmReturnPixels | XpmReturnExtensions);
	status = XpmCreatePixmapFromData(
			mainDisplay,		/* display */
			Root,			/* window */
			months,			/* xpm */
			&monthsXpm.pixmap,	/* resulting pixmap */
			&monthsXpm.mask,
			&monthsXpm.attributes);
	if (status != XpmSuccess) {
		printf("astime : (%d) not enough free color cells for months.\n", status);
		x_cleanup();
	}
	SizeHints.flags = USSize | USPosition;
	SizeHints.x = 0;
	SizeHints.y = 0;
	SizeHints.min_width =
	    SizeHints.max_width =
	    SizeHints.width = state.total_size.x;
	SizeHints.min_height =
	    SizeHints.max_height =
	    SizeHints.height = state.total_size.y;
	SizeHints.flags |= PMinSize | PMaxSize;
	/*
	 * The forth parameter is the window border width.
	 * Set to 0 it makes astime look really queer in
	 * AS when using -0-0 geometry. I have no idea why.
	 * When the border is 1, everything is fine, but
	 * the window is off by 1, of course.
	 * It is probably a bug in AS.
	 */
	XWMGeometry(mainDisplay, screen, mainGeometry, NULL, 0,
		    &SizeHints, &SizeHints.x, &SizeHints.y,
		    &SizeHints.width, &SizeHints.height, &gravity);
#ifdef DEBUG
	printf("SizeHints were: %d x %d, setting to %d x %d, position %d %d\n",
	       SizeHints.width, SizeHints.height,
	       state.total_size.x, state.total_size.y,
	       SizeHints.x, SizeHints.y);
#endif

	/* Correct the offsets if the X/Y are negative */
	SizeHints.win_gravity = NorthWestGravity;
	if (x_negative) {
		SizeHints.win_gravity = NorthEastGravity;
	}
	if (y_negative) {
		if (x_negative)
			SizeHints.win_gravity = SouthEastGravity;
		else
			SizeHints.win_gravity = SouthWestGravity;
	}
	SizeHints.flags |= PWinGravity;

	backgroundWindow = XCreatePixmap(
				mainDisplay,
				Root,
				state.total_size.x,
				state.total_size.y,
				color_depth
	    );

	drawWindow = XCreatePixmap(
				mainDisplay,		/* display */
				Root,			/* parent */
				state.total_size.x,	/* width */
				state.total_size.y,	/* height */
				color_depth		/* color depth */
	    );

	mainWindow = XCreateSimpleWindow(
				mainDisplay,		/* display */
				Root,			/* parent */
				SizeHints.x,		/* x */
				SizeHints.y,		/* y */
				SizeHints.width,	/* width */
				SizeHints.height,	/* height */
				0,			/* border_width */
				fore_pix,		/* border */
				back_pix		/* background */
	    );

	iconWindow = XCreateSimpleWindow(
				mainDisplay,		/* display */
				Root,			/* parent */
				SizeHints.x,		/* x */
				SizeHints.y,		/* y */
				SizeHints.width,	/* width */
				SizeHints.height,	/* height */
				0,			/* border_width */
				fore_pix,		/* border */
				back_pix		/* background */
	    );

	XSetWMNormalHints(mainDisplay, mainWindow, &SizeHints);
	XSetWMNormalHints(mainDisplay, iconWindow, &SizeHints);
	status = XClearWindow(mainDisplay, mainWindow);
	status = XClearWindow(mainDisplay, iconWindow);

	status = XGetWindowAttributes(
			     mainDisplay,	/* display */
			     mainWindow,	/* window */
			     &winAttr		/* window_attributes_return */
	    );
#ifdef DEBUG
	printf("Window Attributes: %d x %d at %d %d border %d\n",
	       winAttr.width, winAttr.height, winAttr.x, winAttr.y,
	       winAttr.border_width);
#endif

	status = XStringListToTextProperty(&window_name, 1, &title);
	XSetWMName(mainDisplay, mainWindow, &title);
	XSetWMName(mainDisplay, iconWindow, &title);

	classHint.res_name = "astime";
	classHint.res_class = "ASTIME";
	XSetClassHint(mainDisplay, mainWindow, &classHint);
	XStoreName(mainDisplay, mainWindow, window_name);
	XSetIconName(mainDisplay, mainWindow, window_name);

	/*
	 * If the user did not specify a program to run
	 * we do not intercept mouse clicks in our window.
	 */
	if (state.run_program)
		status = XSelectInput(
			mainDisplay,	/* display    */
			mainWindow,	/* window     */
			ExposureMask | ButtonPressMask	/* event_mask */
		    );
	else
		status = XSelectInput(
			mainDisplay,	/* display    */
			mainWindow,	/* window     */
			ExposureMask	/* event_mask */
		    );

	/* Make sure WindowMaker users can click happily away ... */
	if ( withdrawn )
		status = XSelectInput(
			mainDisplay,	/* display    */
			iconWindow,	/* window     */
			ExposureMask | ButtonPressMask	/* event_mask */
		    );
	else
		status = XSelectInput(
				mainDisplay,	/* display    */
				iconWindow,	/* window     */
				ExposureMask	/* event_mask */
		    );

	/* Creating Graphics context */
	mainGCV.foreground = fore_pix;
	mainGCV.background = back_pix;
	mainGCV.graphics_exposures = False;
	mainGCV.line_style = LineSolid;
	mainGCV.fill_style = FillSolid;
	mainGCV.line_width = 1;
	if ( state.show_label ) {
		mainGCV.font = label_font;
		mainGC = XCreateGC(mainDisplay,
				   mainWindow,
				   GCForeground | GCBackground | GCLineWidth |
				   GCLineStyle | GCFillStyle | GCFont,
				   &mainGCV
		    );
	} else {
		mainGC = XCreateGC(mainDisplay,
				   mainWindow,
				   GCForeground | GCBackground | GCLineWidth |
				   GCLineStyle | GCFillStyle,
				   &mainGCV
		    );
	}

	/* Draw the background */
	mainGCV.foreground = back_pix;
	XChangeGC(mainDisplay,
		  mainGC,
		  GCForeground,
		  &mainGCV
	    );
	XFillRectangle(mainDisplay,
		       backgroundWindow,
		       mainGC,
		       0,
		       0,
		       state.total_size.x,
		       state.total_size.y
	    );
	/* The shadow on the sides */
	if (pushed_in) {
		tmp_pix = DarkenColor(state.bgcolor, 1.6, mainDisplay, Root);
	} else {
		tmp_pix = LightenColor(state.bgcolor, 2.0, mainDisplay, Root);
	}
	mainGCV.foreground = tmp_pix;
	XChangeGC(mainDisplay,
		  mainGC,
		  GCForeground,
		  &mainGCV
	    );
	XDrawLine(mainDisplay,
		  backgroundWindow,
		  mainGC,
		  0,
		  0,
		  state.total_size.x - 1,
		  0
	    );
	XDrawLine(mainDisplay,
		  backgroundWindow,
		  mainGC,
		  0,
		  0,
		  0,
		  state.total_size.y - 1
	    );
	if (!pushed_in) {
		tmp_pix = DarkenColor(state.bgcolor, 1.6, mainDisplay, Root);
	} else {
		tmp_pix = LightenColor(state.bgcolor, 2.0, mainDisplay, Root);
	}
	mainGCV.foreground = tmp_pix;
	XChangeGC(mainDisplay,
		  mainGC,
		  GCForeground,
		  &mainGCV
	    );
	XDrawLine(mainDisplay,
		  backgroundWindow,
		  mainGC,
		  state.total_size.x - 1,
		  state.total_size.y,
		  state.total_size.x - 1,
		  0
	    );
	XDrawLine(mainDisplay,
		  backgroundWindow,
		  mainGC,
		  state.total_size.x,
		  state.total_size.y - 1,
		  0,
		  state.total_size.y - 1
	    );

	/* Finish up the background (the dial) */
	draw_dial();

	status = XSetCommand(mainDisplay, mainWindow, argv, argc);

	/* Set up the event for quitting the window */
	wm_delete_window = XInternAtom(mainDisplay,
				"WM_DELETE_WINDOW",	/* atom_name */
				False			/* only_if_exists */
	    );
	wm_protocols = XInternAtom(mainDisplay,
				"WM_PROTOCOLS",		/* atom_name */
				False			/* only_if_exists */
	    );
	status = XSetWMProtocols(mainDisplay,
				 mainWindow,
				 &wm_delete_window,
				 1
	    );
	status = XSetWMProtocols(mainDisplay,
				 iconWindow,
				 &wm_delete_window,
				 1
	    );

	WmHints.flags = StateHint | IconWindowHint;
	WmHints.initial_state =
	    withdrawn ? WithdrawnState :
	    iconic ? IconicState : NormalState;
	WmHints.icon_window = iconWindow;
	if (withdrawn) {
		WmHints.window_group = mainWindow;
		WmHints.flags |= WindowGroupHint;
	}
	if (iconic || withdrawn) {
		WmHints.icon_x = SizeHints.x;
		WmHints.icon_y = SizeHints.y;
		WmHints.flags |= IconPositionHint;
	}
	XSetWMHints(mainDisplay,
		    mainWindow,
		    &WmHints);

	/* Finally show the window */
	status = XMapWindow(mainDisplay, mainWindow);

	/* Get colors while waiting for Expose */
	if (state.neon) {
		pix[0] = DarkenColor(state.seccolor, 1.8, mainDisplay, Root);
		pix[1] = DarkenColor(state.mincolor, 1.8, mainDisplay, Root);
		pix[2] = DarkenColor(state.hourcolor, 1.8, mainDisplay, Root);
	} else {
		pix[0] = GetColor(state.seccolor, mainDisplay, Root);
		pix[1] = GetColor(state.mincolor, mainDisplay, Root);
		pix[2] = GetColor(state.hourcolor, mainDisplay, Root);
	}

	/* Get outline colors */
	if (state.neon) {
		pix[3] = GetColor(state.seccolor, mainDisplay, Root);
		pix[4] = GetColor(state.mincolor, mainDisplay, Root);
		pix[5] = GetColor(state.hourcolor, mainDisplay, Root);
	} else {
		pix[3] = LightenColor(state.seccolor, 1.8, mainDisplay, Root);
		pix[4] = LightenColor(state.mincolor, 1.8, mainDisplay, Root);
		pix[5] = LightenColor(state.hourcolor, 1.8, mainDisplay, Root);
	}

	/* allow user to override outline colors */
	if (strcmp(state.secocolor, "<>"))
		pix[3] = GetColor(state.secocolor, mainDisplay, Root);
	if (strcmp(state.minocolor, "<>"))
		pix[4] = GetColor(state.minocolor, mainDisplay, Root);
	if (strcmp(state.hourocolor, "<>"))
		pix[5] = GetColor(state.hourocolor, mainDisplay, Root);

	/* Get fill colors */
	pix[6] = pix[0];
	pix[7] = pix[1];
	pix[8] = pix[2];

	/* allow user to override fill colors */
	if (strcmp(state.secfillcolor, "<>"))
		pix[6] = GetColor(state.secfillcolor, mainDisplay, Root);
	if (strcmp(state.minfillcolor, "<>"))
		pix[7] = GetColor(state.minfillcolor, mainDisplay, Root);
	if (strcmp(state.hourfillcolor, "<>"))
		pix[8] = GetColor(state.hourfillcolor, mainDisplay, Root);

	/* Get the label color */
	label_pix = GetColor(state.label_color, mainDisplay, Root);

	/* try to read the time */
	curTime = time(0);
	last_time = curTime;
	refresh();

	/* wait for the Expose event now */
	XNextEvent(mainDisplay, &Event);
	/* We've got Expose -> draw the parts of the window. */
	redraw();
	XFlush(mainDisplay);
}
